Example #1
0
 def loadCompletions(self):
     """ Loads the completion data from the config. """
     conf = config("scorewiz completion")
     for w in self.completableWidgets:
         compObj, name = w.completionObject(), str(w.name())
         compObj.setOrder(KCompletion.Sorted)
         compObj.setItems(py2qstringlist(conf.get(name, '').splitlines()))
Example #2
0
def install():
    """
    Main install functions, called always if this file is imported
    """
    conf = config()
    if "version" not in conf:
        install_katefiletyperc()
Example #3
0
 def loadSettings(self):
     """ Load the settings """
     conf = config("rumor")
     if 'oss:1' in self.ilist:
         idefault = odefault = 'oss:1'
     else:
         idefault = 'kbd'
         odefault = self.olist[max(1, len(self.olist)-1)]
     i = conf.get("midi in", idefault)
     o = conf.get("midi out", odefault)
     if i in self.ilist:
         self.ibut.setCurrentItem(self.ilist.index(i))
     if o in self.olist:
         self.obut.setCurrentItem(self.olist.index(o))
     self.lang.setCurrentText(unautofy(conf.get("language", "auto")))
     self.absPitches.setChecked(conf["absolute pitches"] == "1")
     self.explDur.setChecked(conf["explicit durations"] == "1")
     self.noBar.setChecked(conf["no barlines"] == "1")
     self.noDots.setChecked(conf["no dots"] == "1")
     self.legato.setChecked(conf["legato"] == "1")
     self.stripRests.setChecked(conf["strip rests"] == "1")
     # Guile scripts
     self.scripts.clear()
     scripts = conf.get("scripts", "").splitlines()
     for name, path in getRumorFiles().iteritems():
         try:
             d = open(path).readline().strip()
             c = QCheckListItem(self.scripts, name, QCheckListItem.CheckBox)
             c.setOn(name in scripts)
             if d.startswith(';'):
                 c.setText(1, d.strip(";"))
         except IOError:
             pass
Example #4
0
def actionsConfig():
    """
    Returns a dictionary with action names mapping to a value 0 or 1,
    (whether they are enabled by the user or by default).
    """
    conf = config("actions")
    return dict((name, int(conf[name] or default))
        for name, default, title, tooltip in actions)
Example #5
0
 def saveCompletions(self):
     """ Saves completion items for all lineedits. """
     conf = config("scorewiz completion")
     for w in self.completableWidgets:
         name, text = str(w.name()), unicode(w.text())
         items = qstringlist2py(w.completionObject().items())
         if len(text) > 1 and text not in items:
             items.append(text)
         conf[name] = '\n'.join(items)
Example #6
0
def init():
    global version
    lilypond = config("commands").get("lilypond", "lilypond")
    try:
        match = re.search(r"(\d+)\.(\d+)(?:\.(\d+))?", Popen((lilypond, "-v"),
            stdout=PIPE).communicate()[0].splitlines()[0])
    except OSError, e:
        match = None
        error(_("Could not start LilyPond: %s") % e)
Example #7
0
 def _run(self, args, mode=None):
     if config("preferences")["delete intermediate files"] == "1":
         args.insert(0, "-ddelete-intermediate-files")
     args = [a.encode(sys.getfilesystemencoding() or "utf-8") for a in args]
     self.setArguments(args)
     a = dict(filename=self.f.ly, mode=mode)
     if mode:
         self.log.msg(_("LilyPond [$filename] starting ($mode)...").args(a))
     else:
         self.log.msg(_("LilyPond [$filename] starting...").args(a))
     self.start() or self.log.fail(_("Could not start LilyPond."))
Example #8
0
    def __init__(self, f, log):
        super(LyJob, self).__init__()
        self.f = f
        self.setExecutable(config("commands").get("lilypond", "lilypond"))
        self.setWorkingDirectory(f.directory)
        self.log = log
        self.stdout = Outputter(log, f)
        self.stderr = Outputter(log, f)

        QObject.connect(self, SIGNAL("receivedStdout(KProcess*, char*, int)"), self.stdout.receive)
        QObject.connect(self, SIGNAL("receivedStderr(KProcess*, char*, int)"), self.stderr.receive)
Example #9
0
 def saveSettings(self):
     """ Saves the settings to lilykderc """
     conf = config("rumor")
     conf["tempo"] = self.tempo.tempo()
     conf["quantize"] = self.quantize.currentText()
     conf["step"] = int(self.step.isChecked())
     conf["mono"] = int(self.mono.isChecked())
     conf["meter"] = autofy(self.meter.currentText())
     conf["keysig"] = autofy(self.keysig.currentText())
     conf["timidity"] = int(self.timidity.isRunning())
     self.status.message(_("Settings have been saved."), 2000)
Example #10
0
 def loadSettings(self):
     """ Loads the settings from lilykderc """
     conf = config("rumor")
     self.tempo.setTempo(int(conf.get("tempo", 100)))
     self.quantize.setCurrentText(conf.get("quantize", "16"))
     self.step.setChecked(conf["step"] == "1")
     self.mono.setChecked(conf["mono"] == "1")
     self.meter.setCurrentText(unautofy(conf.get("meter", "auto")))
     self.keysig.setCurrentText(unautofy(conf.get("meter", "auto")))
     if conf["timidity"] == "1":
         self.timidity.start()
Example #11
0
def findDicts():
    conf = config("hyphenation")

    def paths():
        """ build a list of paths based on config """
        # in which prefixes to look for relative paths
        if 'KDEDIRS' in os.environ:
            prefixes = os.environ['KDEDIRS'].split(':')
        else:
            prefixes = ['/usr', '/usr/local']
            if 'KDEDIR' in os.environ:
                prefixes.append(os.environ['KDEDIR'])
        # if the path is not absolute, add it to all prefixes.
        for path in conf["paths"].splitlines() or defaultpaths:
            if os.path.isabs(path):
                yield path
            else:
                for pref in prefixes:
                    yield os.path.join(pref, path)

    # now find the hyph_xx_XX.dic files
    dicfiles = (f
        for p in paths() if os.path.isdir(p)
            for f in glob(os.path.join(p, 'hyph_*.dic')) if os.path.isfile(f))

    # present the user with human readable language names
    all_languages = kconfig("all_languages", True, False, "locale")

    # default to the users current locale if not used before
    defaultlang = None

    global hyphdicts
    # empty it, because we might be called again when the user changes
    # the settings.
    hyphdicts = {}
    for dic in dicfiles:
        lang = os.path.basename(dic)[5:-4]
        # find a human readable name belonging to the language code
        for i in lang, lang.split('_')[0]:
            name = all_languages.group(i).get("Name")
            if name:
                name = '%s  (%s)' % (name, lang)
                hyphdicts[name] = dic
                # set current locale as default
                if lang == language:
                    defaultlang = name
                break
        else:
            hyphdicts[lang] = dic

    # if not used before, write the current locale (if existing) as default
    if defaultlang and conf["lastused"] not in hyphdicts:
        conf["lastused"] = defaultlang
Example #12
0
def runLilyPond(doc, preview=False):
    """
    Run Lilypond on the specified kate.document() and produce a PDF in the same
    directory if that is writable. If param `preview` is True, the PDF will
    contain clickable notes and other objects (point-and-click). If false, the
    PDF will not contain those textedit:// links.
    """
    if doc.url == "":
        sorry(_("Your document currently has no filename, please save first."))
        return

    if doc.modified:
        if config("preferences")["save on run"] == "1":
            doc.save()
        else:
            sorry(_("Your document has been modified, please save first."))
            return

    f = LyFile(doc)
    if not f.isLocalFile():
        # TODO: implement remote file support
        sorry(
            _("Sorry, support for remote files is not yet implemented.\n" "Please save your document to a local file.")
        )
        return

    # the document may contain specially formatted variables to specify e.g.
    # another lilypond file to build instead of the current document.
    lvars = variables(doc)
    ly = lvars.get(preview and "master-preview" or "master-publish") or lvars.get("master")
    if ly:
        f.setPath(os.path.join(f.directory, ly))

    from lilykde import log

    if config("preferences")["clear log"] == "1":
        log.logWidget().clear()
    Ly2PDF(f, log.logWidget()).run(preview)
Example #13
0
    def completed(self, success):
        if success and self.f.pdf:
            if self.f.hasUpdatedPDF():
                self.f.previewPDF()
                # should we embed the LilyPond source files in the PDF?
                from lilykde import pdftk

                if not self.preview and config("preferences")["embed source files"] == "1" and pdftk.installed():
                    pdftk.attach_files(self.f.path, 3)
            else:
                self.log.msg(_("LilyPond did not write a PDF. " "You probably forgot <b>\layout</b>?"))
            from lilykde import actions

            self.log.actions(actions.listActions(self.f, self.preview))
Example #14
0
def parseAconnect(channel):
    """
    Returns a list of tuples ('0:0', 'Port name') of the
    available MIDI ports for either reading (channel = 'i')
    or writing (channel = 'o')
    """
    option = channel == 'i' and '--input' or '--output'
    cmd = config("commands").get("aconnect", "aconnect")
    res = []
    for line in Popen([cmd, option], stdout=PIPE).communicate()[0].splitlines():
        m = re.match(r"client\s*(\d+)|\s*(\d+)\s+'([^']+)'", line)
        if m.group(1):
            client = m.group(1)
        elif client != "0":
            port, name = m.group(2,3)
            res.append(("%s:%s" % (client, port), name.strip()))
    return res
Example #15
0
def openFile(pdf):
    """ Open the specified PDF document """

    global _file

    c = KApplication.dcopClient()
    kpdf = DCOPApp(c.appId(), c).kpdf

    # When LilyPond writes a PDF, it first deletes the old one.
    # So the new PDF gets a different inode number, which causes
    # KPDF to sometimes loose the 'watch' on the file.
    # So we call KPDF to open the file, and remember the page number
    # displayed ourselves, because KPDF also seems to forget the scroll
    # position due to LilyPond deleting the old PDF first.
    # It would be best that either KPDF is fixed to just look for a
    # named file, even if it has a different inode number, or that
    # LilyPond is fixed to not delete the old PDF first, but just
    # truncate it and write the new data into it.

    # Update June 17, 2008: LilyPond >= 2.11.49 does not delete the PDF
    # anymore on unix platforms!

    # Document already shown?
    if _file == pdf:
        # Yes this is the same PDF, see if we need to force KPDF to
        # reload the document. If not, we trust that KPDF automatically
        # updates its view.
        from lilykde.version import version
        if (
            # User can force reload of PDF with config option
            config('preferences')['force reload pdf'] == '1'
            # LilyPond >= 2.11.49 does not delete the PDF anymore on unix
            or version < (2, 11, 49) or os.name not in ('posix', 'mac')
            # read KPDF's 'watch file' option (default is true)
            or kconfig('kpdfpartrc', True, False).group('General')['WatchFile']
                in ('false', '0', 'off', 'no')):
            # Reopen same document, remembering page number
            page = kpdf.currentPage()[1]
            kpdf.openDocument(KURL(pdf))
            QTimer.singleShot(100, lambda: kpdf.goToPage(page))
    else:
        # This is another PDF, just open it.
        kpdf.openDocument(KURL(pdf))
        _file = pdf
Example #16
0
def askLanguage():
    """
    Ask the user which language to use.
    Returns None if the user cancels the dialog.
    """
    conf = config("hyphenation")
    lang = conf["lastused"] or ""
    langs = list(sorted(hyphdicts.keys()))
    index = lang in langs and langs.index(lang) or 0
    lang, ok = KInputDialog.getItem(
        _("Language selection"),
        _("Please select a language:"),
        py2qstringlist(langs), index, False,
        editor.topLevelWidget()
    )
    if ok:
        lang = unicode(lang)
        conf["lastused"] = lang
        return lang
Example #17
0
 def saveConfig(self):
     """
     Save some preferences.
     """
     conf = config("scorewiz")
     conf['language'] = self.getLanguage() or 'default'
     conf['typographical'] = self.typq.isChecked() and '1' or '0'
     conf['remove tagline'] = self.tagl.isChecked() and '1' or '0'
     conf['remove barnumbers'] = self.barnum.isChecked() and '1' or '0'
     conf['midi'] = self.midi.isChecked() and '1' or '0'
     conf['metronome mark'] = self.metro.isChecked() and '1' or '0'
     conf['paper size'] = self.paper.currentItem() and \
         str(self.paper.currentText()) or 'default'
     conf['paper landscape'] = self.paperLandscape.isChecked() and '1' or '0'
     conf['instrument names'] = self.instr.isChecked() and '1' or '0'
     conf['instrument names italian'] = self.instrIt.isChecked() and '1' or '0'
     conf['instrument names first system'] = \
         ('short', 'long')[self.instrFirst.currentItem()]
     conf['instrument names other systems'] = \
         ('none', 'short', 'long')[self.instrOther.currentItem()]
Example #18
0
 def saveSettings(self):
     """ Save the settings """
     conf = config("rumor")
     conf["midi in"] = self.ilist[self.ibut.currentItem()]
     conf["midi out"] = self.olist[self.obut.currentItem()]
     conf["language"] = autofy(self.lang.currentText())
     conf["absolute pitches"] = int(self.absPitches.isChecked())
     conf["explicit durations"] = int(self.explDur.isChecked())
     conf["no barlines"] = int(self.noBar.isChecked())
     conf["no dots"] = int(self.noDots.isChecked())
     conf["legato"] = int(self.legato.isChecked())
     conf["strip rests"] = int(self.stripRests.isChecked())
     # Read script listview
     c = self.scripts.firstChild()
     names = []
     while c is not None:
         if c.isOn():
             names.append(unicode(c.text()))
         c = c.nextSibling()
     conf["scripts"] = '\n'.join(names)
Example #19
0
 def undock(self):
     """
     Undocks the widget and place it in a QDialog
     """
     if not self.dialog:
         parent = None
         if config("preferences")["keep undocked on top"] != '0':
             parent = kate.mainWidget()
         d = DockDialog(self, parent)
         d.setCaption("%s - %s" % (self.title, PROGRAMNAME))
         if self.dialogSize:
             d.resize(*self.dialogSize)
         if self.dialogPos:
             d.move(*self.dialogPos)
         QVBoxLayout(d).setAutoAdd(True)
         self.widget.reparent(d, QPoint(0, 0))
         if not self.focus:
             self.widget.setFocusPolicy(QWidget.WheelFocus)
         self.dialog = d
         if self.tool:
             sip.delete(self.tool.widget)
             self.tool = None
         self.show()
Example #20
0
def runAction(url):
    """
    Runs an URL with KRun. If url starts with "email=" or "emailpreview=",
    it is converted to a mailto: link with the url attached, and opened in
    the default KDE mailer.
    If url starts with "print=", the file is directly printed with lpr.
    If url starts with "embed=", a subroutine in pdftk is called to embed
    LilyPond documents in the output PDF.
    """
    # hack: prevent QTextView recognizing mailto: urls cos it can't handle
    # query string
    url = unicode(url)        # url could be a QString
    m = re.match("([a-z]+)=(.*)", url)
    if not m:
        return krun(url)
    command, url = m.groups()
    if command == 'print':
        path = unicode(KURL(url).path())
        cmd = splitcommandline(config("commands").get("lpr", "lpr"))
        cmd.append(path)
        p = Popen(cmd, stderr=PIPE)
        if p.wait() != 0:
            error(_("Printing failed: %s") % p.stderr.read())
        else:
            info(_("The document has been sent to the printer."))
    elif command in ('email', 'emailpreview'):
        if command == "email" or warncontinue(_(
            "This PDF has been created with point-and-click urls (preview "
            "mode), which increases the file size dramatically. It's better "
            "to email documents without point-and-click urls (publish mode), "
            "because they are much smaller. Continue anyway?")):
            KApplication.kApplication().invokeMailer(
                KURL(u"mailto:?attach=%s" % url), "", True)
    elif command == 'embed':
        ly = unicode(KURL(url).path())
        from lilykde import pdftk
        pdftk.attach_files(ly)
Example #21
0
def listActions(lyfile, preview):
    """
    returns a actions list that LogWidget (see widgets.py) can display,
    based on the given LyFile object (see runlily.py) and the preview
    mode (True or False).
    """
    act = actionsConfig()
    actions = []
    if act["open_folder"]:
        actions.append(("file://%s" % lyfile.directory, _("Open folder")))
    if lyfile.hasUpdatedPDF():
        if act["open_pdf"]:
            actions.append(("file://%s" % lyfile.pdf, _("Open PDF")))
        if act["print_pdf"]:
            actions.append(("print=file://%s" % lyfile.pdf, _("Print")))
        if act["email_pdf"]:
            # hack: prevent QTextView from recognizing mailto urls, as
            # it then uses the mailClick signal, which does not give us
            # the query string. Later on, we prepend the "mailto:?" :)
            if preview:
                actions.append(("emailpreview=file://%s" % lyfile.pdf,
                    _("Email PDF (preview)")))
            else:
                actions.append(("email=file://%s" % lyfile.pdf,
                    _("Email PDF")))
        # should we embed the LilyPond source files in the PDF?
        from lilykde import pdftk
        if act["embed_source"] and pdftk.installed() and (preview or
                config("preferences")['embed source files'] != '1'):
            actions.append(("embed=file://%s" % lyfile.path, _("Embed source")))
    midis = lyfile.getUpdated(".midi")
    if act["play_midi"] and midis:
        actions.append(("file://%s" % midis[0], _("Play MIDI")))
        actions.extend([("file://%s" % m, str(n+1))
            for n, m in enumerate(midis[1:])])
    return actions
Example #22
0
def convertLy():
    """ Run convert-ly on the current document """
    global version
    docVersion = getVersion()
    if not docVersion:
        sorry(_("Can't determine the LilyPond version of the current document."
                " Please add a \\version statement with the correct version."))
    elif not version:
        sorry(_("Can't determine the version of LilyPond. "
                "Please check your LilyPond installation."))
    elif docVersion >= version:
        sorry(_("This LilyPond document is already up-to-date."))
    else:
        # Ok, let's run convert-ly.
        # We add the from-version. Only in that case convert-ly wants to
        # read from stdin.
        convert_ly = config("commands").get("convert-ly", "convert-ly")
        try:
            out, err = Popen((convert_ly, "-f", "%d.%d.%d" % docVersion, "-"),
                            stdin=PIPE, stdout=PIPE, stderr=PIPE
                            ).communicate(editor.text().encode('utf8'))
            if out:
                editor.setText(u"%s\n\n%%{\n%s\n%%}\n" %
                    (out.decode('utf8'), err.decode('utf8')))
                info(_(
                 "The document has been processed with convert-ly. You'll find "
                 "the messages of convert-ly in a comment block at the end. "
                 "You still may have to edit some parts manually."), timeout=10)
            else:
                msg = err.decode('utf8').replace('\n', '<br>')
                info(_(
                 "The document has been processed with convert-ly, but "
                 "remained unchanged. This is the message given by "
                 "convert-ly: %s") % "<br><br>%s" % msg, timeout=10)
        except OSError, e:
            error(_("Could not start convert-ly: %s") % e)
Example #23
0
 def load(self):
     conf = config("preferences")
     for w, c, d in self.checks:
         w.setChecked(bool(int(conf.get(c, d))))
Example #24
0
 def save(self):
     conf = config("actions")
     for a, w, d in self.actions:
         conf[a] = int(w.isChecked())
Example #25
0
def _saveTab(index):
    config('lqi')['current tab'] = index
Example #26
0
    def applyRhythm(self):
        """ Adds the entered rhythm to the selected music."""
        durs = [m.group() for m in Res.finddurs.finditer(
            unicode(self.rhythm.text()))]
        def durgen():
            old = ''
            while True:
                for i in durs:
                    yield i != old and i or ''
                    old = i
        nextdur = durgen().next
        def repl(m):
            if m.group('chord'):
                return m.group('chord') + nextdur()
        Res.edit(repl)





Articulations()
Rhythm()


# Remember the currently selected tab.
def _saveTab(index):
    config('lqi')['current tab'] = index
toolbox.setCurrentIndex(int(config('lqi')['current tab'] or '0'))
QObject.connect(toolbox, SIGNAL("currentChanged(int)"), _saveTab)

Example #27
0
 def save(self):
     conf = config("preferences")
     for w, c, d in self.checks:
         conf[c] = int(w.isChecked())
Example #28
0
 def onStart(self):
     self.command = config("commands").get("timidity",
         'timidity -iA -B2,8 -Os -EFreverb=0')
Example #29
0
    def onStart(self):
        """ Here we construct the command etc. """
        p = self.parent()
        conf = config("rumor")
        # indent of current line
        self.indent = re.match(r'\s*',
            editor.currentLine()[:editor.pos()[1]]).group()
        # text from start to cursor
        text = editor.fragment((0, 0), editor.pos())
        cmd = [config("commands").get("rumor", "rumor")]
        # Language
        lang = conf.get("language", "auto")
        if lang not in (
                'ne', 'en', 'en-short', 'de', 'no', 'sv', 'it', 'ca', 'es'):
            # determine lily language from document
            m = re.compile(r'.*\\include\s*"('
                "nederlands|english|deutsch|norsk|svenska|suomi|"
                "italiano|catalan|espanol|portuges|vlaams"
                r')\.ly"', re.DOTALL).match(text)
            if m:
                lang = m.group(1)[:2]
                if lang == "po": lang = "es"
                elif lang == "su": lang = "de"
                elif lang == "en" and not re.match(
                        r'\b[a-g](flat|sharp)\b', text):
                    lang == "en-short"
                elif lang == "vl":
                    # "vlaams" is not supported by Rumor
                    # TODO: rewrite the pitches output by Rumor :-)
                    lang == "it"
            else:
                lang = "ne" # the default
        cmd.append("--lang=%s" % lang)

        # Step recording?
        if p.step.isChecked():
            cmd.append("--flat")
        else:
            # No, set tempo, quantization and meter
            cmd.append("--tempo=%d" % p.tempo.tempo())
            cmd.append("--grain=%s" % p.quantize.currentText())
            meter = autofy(p.meter.currentText())
            if meter == "auto":
                # determine from document - find the latest \time command:
                m = re.compile(r'.*\\time\s*(\d+/(1|2|4|8|16|32|64|128))(?!\d)',
                    re.DOTALL).match(text)
                if m:
                    meter = m.group(1)
                else:
                    meter = '4/4'
            cmd.append("--meter=%s" % meter)

        # Key signature
        acc = autofy(p.keysig.currentText())
        if acc == "auto":
            # Determine key signature from document.
            m = re.compile(
                r'.*\\key\s+(' + '|'.join(pitches[lang].keys()) + r')\s*\\'
                r'(major|minor|(ion|dor|phryg|(mixo)?lyd|aeol|locr)ian)\b',
                re.DOTALL).match(text)
            if m:
                pitch, mode = m.group(1,2)
                acc = pitches[lang][pitch] + modes[mode]
            else:
                acc = 0
        else:
            acc == int(acc)
        acc += 2    # use sharps for half tones leading to second, fifth, sixth
        cmd.append("--key=%s" % revpitches[lang][bound(acc, -8, 12)])

        # Monophonic input?
        if p.mono.isChecked():
            cmd.append("--no-chords")

        # Absolute pitches?
        if int(conf.get("absolute pitches", "0")):
            cmd.append("--absolute-pitches")

        # Explicit durations?
        if int(conf.get("explicit durations", "0")):
            cmd.append("--explicit-durations")

        # No barlines?
        self.noBarlines = int(conf.get("no barlines", "0"))

        # No dots?
        if int(conf.get("no dots", "0")):
            cmd.append("--no-dots")

        # Legato?
        if int(conf.get("legato", "0")):
            cmd.append("--legato")

        # Strip rests?
        if int(conf.get("strip rests", "0")):
            cmd.append("--strip")

        # Guile scripts?
        scripts = conf.get("scripts", "").splitlines()
        if scripts:
            paths = getRumorFiles()
            for s in scripts:
                if s in paths:
                    cmd.append("--script=%s" % paths[s])

        # input/output
        i = conf.get("midi in", "oss:1")
        o = conf.get("midi out", "oss:1")
        if o.startswith('oss:'):
            cmd.append("--oss=%s" % o.split(":")[1])
        elif re.match(r"\d", o) and re.match(r"\d", i):
            cmd.append("--alsa=%s,%s" % (i, o))
        elif re.match(r"\d", o):
            cmd.append("--alsa=%s" % o)
        self.keyboardEmu = i == "keyboard"

        if self.keyboardEmu:
            cmd.append("--kbd")
            p.setFocus()
            self.comm = KProcess.Communication(KProcess.Stdin | KProcess.Stdout)
            self.pty = True
        else:
            self.comm = KProcess.Stdout
            self.pty = False
        self.command = cmd
Example #30
0
    def __init__(self, *args):
        QFrame.__init__(self, *args)
        # Accept keyboard focus
        self.setFocusPolicy(QWidget.ClickFocus)
        layout = QGridLayout(self, 4, 5, 4)
        layout.setColStretch(4, 1)

        # Big Start/stop toggle button
        self.r = RumorButton(self)
        layout.addMultiCellWidget(self.r, 0, 3, 0, 0)

        # labels for other controls:
        layout.addWidget(QLabel(_("Tempo:"), self), 0, 1)
        layout.addWidget(QLabel(_("Meter:"), self), 1, 1)
        layout.addWidget(QLabel(_("Key:"), self), 2, 1)

        # Status line
        self.status = QStatusBar(self)
        self.status.setSizeGripEnabled(False)
        layout.addMultiCellWidget(self.status, 3, 3, 1, 4)

        # Tempo adjustment (spinbox + long slider)
        self.tempo = TempoControl(self)
        layout.addWidget(self.tempo.spinbox, 0, 2)
        hb = QHBoxLayout()
        layout.addLayout(hb, 0, 3)
        hb.addWidget(self.tempo.tapButton)
        hb.addWidget(self.tempo.slider)

        # Meter select (editable qcombobox defaulting to document)
        self.meter = QComboBox(self)
        self.meter.setEditable(True)
        self.meter.insertStringList(py2qstringlist([
            AUTO,
            '1/4', '2/4', '3/4', '4/4', '5/4', '6/4',
            '2/2', '3/2',
            '3/8', '6/8', '9/8', '12/8',
            '3/16',
            ]))
        self.meter.setValidator(QRegExpValidator(QRegExp(
            re.escape(AUTO) + "|[1-9][0-9]*/(1|2|4|8|16|32|64|128)"),
            self.meter))
        QToolTip.add(self.meter, _(
            "The meter to use. Leave 'Auto' to let LilyKDE determine "
            "the meter from the LilyPond document."))
        layout.addWidget(self.meter, 1, 2)

        # Quantize (1,2,4,8,16,32,64 or 128, default to 16)
        hb = QHBoxLayout()
        layout.addLayout(hb, 1, 3)
        hb.addWidget(QLabel(_("Quantize:"), self))
        self.quantize = QComboBox(self)
        self.quantize.insertStringList(py2qstringlist(
            str(2**i) for i in range(8)))
        QToolTip.add(self.quantize, _(
            "The shortest note duration to use."))
        hb.addWidget(self.quantize)

        # Step recording: (checkbox, disables the three controls above)
        self.step = QCheckBox(_("Step"), self)
        QToolTip.add(self.step, _(
            "Record LilyPond input note by note, without durations."))
        hb.addWidget(self.step)

        # Monophonic input (no chords)
        self.mono = QCheckBox(_("Mono"), self)
        QToolTip.add(self.mono, _(
            "Record monophonic input, without chords."))
        hb.addWidget(self.mono)

        # Key signature select (any lilypond pitch, defaulting to document)
        self.keysig = QComboBox(self)
        self.keysig.insertItem(AUTO)
        self.keysig.insertStringList(py2qstringlist(
            "%d" % i for i in range(-7, 1)))
        self.keysig.insertStringList(py2qstringlist(
            "%+d" % i for i in range(1, 8)))
        QToolTip.add(self.keysig, _(
            "The number of accidentals. A negative number designates flats. "
            "Leave 'Auto' to let LilyKDE determine the key signature from "
            "the LilyPond document."))
        layout.addWidget(self.keysig, 2, 2)

        hb = QHBoxLayout()
        layout.addLayout(hb, 2, 3)

        # Timidity button
        self.timidity = TimidityButton(self)
        hb.addWidget(self.timidity)

        # Button 'More Settings'
        sb = QPushButton(_("Configure..."), self)
        QToolTip.add(sb, _("Adjust more settings, like MIDI input and output."))
        QObject.connect(sb, SIGNAL("clicked()"), lambda: RumorSettings(self))
        hb.addWidget(sb)

        # Save Button
        sb = QPushButton(_("Save"), self)
        QToolTip.add(sb, _("Set these settings as default."))
        QObject.connect(sb, SIGNAL("clicked()"), self.saveSettings)
        hb.addWidget(sb)

        self.loadSettings()

        # display Rumor version on first start.
        cmd = config("commands").get("rumor", "rumor")
        try:
            v = Popen([cmd, '--version'], stdout=PIPE).communicate()[0].strip()
            self.status.message(_("Found rumor version $version.").args(
                version = v), 5000)
        except OSError, e:
            self.status.message(_("Could not find Rumor: %s") % e, 10000)