コード例 #1
0
ファイル: pitch.py プロジェクト: shimpe/frescobaldi
def getTransposer(document, mainwindow):
    """Show a dialog and return the desired transposer.
    
    Returns None if the dialog was cancelled.
    
    """
    language = documentinfo.docinfo(document).language() or 'nederlands'

    def readpitches(text):
        """Reads pitches from text."""
        result = []
        for pitch, octave in re.findall(r"([a-z]+)([,']*)", text):
            r = ly.pitch.pitchReader(language)(pitch)
            if r:
                result.append(
                    ly.pitch.Pitch(*r, octave=ly.pitch.octaveToNum(octave)))
        return result

    def validate(text):
        """Returns whether the text contains exactly two pitches."""
        return len(readpitches(text)) == 2

    text = inputdialog.getText(
        mainwindow,
        _("Transpose"),
        _("Please enter two absolute pitches, separated by a space, "
          "using the pitch name language \"{language}\".").format(
              language=language),
        icon=icons.get('tools-transpose'),
        help="transpose",
        validate=validate)

    if text:
        return ly.pitch.transpose.Transposer(*readpitches(text))
コード例 #2
0
ファイル: pitch.py プロジェクト: jan-warchol/frescobaldi
def getTransposer(document, mainwindow):
    """Show a dialog and return the desired transposer.
    
    Returns None if the dialog was cancelled.
    
    """
    language = documentinfo.info(document).pitchLanguage() or 'nederlands'
    
    def readpitches(text):
        """Reads pitches from text."""
        result = []
        for pitch, octave in re.findall(r"([a-z]+)([,']*)", text):
            r = ly.pitch.pitchReader(language)(pitch)
            if r:
                result.append(ly.pitch.Pitch(*r, octave=ly.pitch.octaveToNum(octave)))
        return result
    
    def validate(text):
        """Returns whether the text contains exactly two pitches."""
        return len(readpitches(text)) == 2
    
    text = inputdialog.getText(mainwindow, _("Transpose"), _(
        "Please enter two absolute pitches, separated by a space, "
        "using the pitch name language \"{language}\"."
        ).format(language=language), icon = icons.get('tools-transpose'),
        help = transpose_help, validate = validate)
    
    if text:
        return ly.pitch.Transposer(*readpitches(text))
コード例 #3
0
ファイル: template.py プロジェクト: mbsrz1972/frescobaldi
def save(mainwindow):
    title = inputdialog.getText(mainwindow,
        _("Save as Template"),
        _("Please enter a template name:"), regexp=r"\w(.*\w)?")
    if not title:
        return
    
    # get the text and insert cursor position or selection
    cursor = mainwindow.textCursor()
    text = cursor.document().toPlainText()
    
    repls = [(cursor.position(), '${CURSOR}')]
    if cursor.hasSelection():
        repls.append((cursor.anchor(), '${ANCHOR}'))
        repls.sort()
        
    result = []
    prev = 0
    for pos, what in repls:
        result.append(text[prev:pos].replace('$', '$$'))
        result.append(what)
        prev = pos
    result.append(text[prev:].replace('$', '$$'))
    text = ''.join(result)
    
    # add header line, if it is lilypond, enable autorun
    headerline = '-*- template; indent: no;'
    if documentinfo.mode(cursor.document()) == 'lilypond':
        headerline += ' template-run;'
    text = headerline + '\n' + text
    
    # save the new snippet
    model.model().saveSnippet(None, text, title)
コード例 #4
0
ファイル: cut_assign.py プロジェクト: AlexSchr/frescobaldi
def cut_assign(cursor):
    """Cuts selected text and assigns it to a LilyPond variable."""
    # ask the variable name
    name = inputdialog.getText(None, _("Cut and Assign"), _(
        "Please enter the name for the variable to assign the selected "
        "text to:"), regexp="[A-Za-z]+")
    if not name:
        return
    
    cursortools.strip_selection(cursor)
    
    # determine state at cursor
    block = cursortools.block(cursor)
    state = tokeniter.state(block)
    for t in tokeniter.partition(cursor).left:
        state.follow(t)
    
    mode = ""
    for p in state.parsers():
        if isinstance(p, ly.lex.lilypond.ParseInputMode):
            if isinstance(p, ly.lex.lilypond.ParseLyricMode):
                mode = " \\lyricmode"
            elif isinstance(p, ly.lex.lilypond.ParseChordMode):
                mode = " \\chordmode"
            elif isinstance(p, ly.lex.lilypond.ParseFigureMode):
                mode = " \\figuremode"
            elif isinstance(p, ly.lex.lilypond.ParseDrumMode):
                mode = " \\drummode"
            break

    # find insertion place:
    found = False
    while block.previous().isValid():
        block = block.previous()
        state = tokeniter.state(block)
        if isinstance(state.parser(), ly.lex.lilypond.ParseGlobal):
            found = True
            break
        tokens = tokeniter.tokens(block)
        for t in tokens:
            if isinstance(t, ly.lex.lilypond.Name):
                found = True
                break
            elif not isinstance(t, (ly.lex.Space, ly.lex.Comment)):
                break
        if found:
            break
    insert = QTextCursor(block)
    text = cursor.selection().toPlainText()
    space = '\n' if '\n' in text else ' '
    text = ''.join((name, ' =', mode, ' {', space, text, space, '}\n\n'))
    with cursortools.compress_undo(cursor):
        cursor.insertText('\\' + name)
        pos = insert.selectionStart()
        insert.insertText(text)
    if metainfo.info(cursor.document()).auto_indent:
        insert.setPosition(pos, QTextCursor.KeepAnchor)
        with cursortools.compress_undo(insert, True):
            indent.re_indent(insert)
コード例 #5
0
ファイル: rhythm.py プロジェクト: ryanakca/frescobaldi
def rhythm_apply(cursor, mainwindow):
    durs = inputdialog.getText(mainwindow,
                               _("Apply Rhythm"),
                               _("Enter a rhythm:"),
                               complete=sorted(_history),
                               regexp=r'([0-9./* ]|\\breve|\\longa|\\maxima)+',
                               help="rhythm",
                               icon=icons.get('tools-rhythm'))
    durations = durs.split()
    if durations:
        _history.add(durs.strip())
    ly.rhythm.rhythm_overwrite(lydocument.cursor(cursor), durations)
コード例 #6
0
ファイル: rhythm.py プロジェクト: EdwardBetts/frescobaldi
def rhythm_apply(cursor, mainwindow):
    durs = inputdialog.getText(mainwindow,
        _("Apply Rhythm"), _("Enter a rhythm:"),
        complete = sorted(_history),
        regexp = r'([0-9./* ]|\\breve|\\longa|\\maxima)+',
        help = "rhythm", icon = icons.get('tools-rhythm'))
    if not durs:
        return # user cancelled dialog
    durations = durs.split()
    if durations:
        _history.add(durs.strip())
        ly.rhythm.rhythm_overwrite(lydocument.cursor(cursor), durations)
コード例 #7
0
ファイル: rhythm.py プロジェクト: mbsrz1972/frescobaldi
def rhythm_apply(cursor, mainwindow):
    durs = inputdialog.getText(mainwindow,
        _("Apply Rhythm"), _("Enter a rhythm:"),
        complete = sorted(_history),
        regexp = r'([0-9./* ]|\\breve|\\longa|\\maxima)+',
        help = rhythm_help, icon = icons.get('tools-rhythm'))
    if durs and durs.split():
        _history.add(durs.strip())
        duration_source = remove_dups(itertools.cycle(durs.split()))
        with cursortools.Editor() as e:
            for c, d in duration_cursor_items(cursor):
                e.insertText(c, next(duration_source))
コード例 #8
0
ファイル: template.py プロジェクト: ryanakca/frescobaldi
def save(mainwindow):

    titles = dict((snippets.title(name), name)
                  for name in model.model().names()
                  if 'template' in snippets.get(name).variables)
    title = inputdialog.getText(mainwindow,
                                _("Save as Template"),
                                _("Please enter a template name:"),
                                regexp=r"\w(.*\w)?",
                                complete=sorted(titles))
    if not title:
        return

    if title in titles:
        if QMessageBox.critical(
                mainwindow, _("Overwrite Template?"),
                _("A template named \"{name}\" already exists.\n\n"
                  "Do you want to overwrite it?").format(name=title),
                QMessageBox.Yes | QMessageBox.Cancel) != QMessageBox.Yes:
            return
        name = titles[title]
    else:
        name = None

    # get the text and insert cursor position or selection
    cursor = mainwindow.textCursor()
    text = cursor.document().toPlainText()

    repls = [(cursor.position(), '${CURSOR}')]
    if cursor.hasSelection():
        repls.append((cursor.anchor(), '${ANCHOR}'))
        repls.sort()

    result = []
    prev = 0
    for pos, what in repls:
        result.append(text[prev:pos].replace('$', '$$'))
        result.append(what)
        prev = pos
    result.append(text[prev:].replace('$', '$$'))
    text = ''.join(result)

    # add header line, if it is lilypond, enable autorun
    headerline = '-*- template; indent: no;'
    if documentinfo.mode(cursor.document()) == 'lilypond':
        dinfo = documentinfo.docinfo(cursor.document())
        if dinfo.complete() and dinfo.has_output():
            headerline += ' template-run;'
    text = headerline + '\n' + text

    # save the new snippet
    model.model().saveSnippet(name, text, title)
コード例 #9
0
def save(mainwindow):
    
    titles = dict((snippets.title(name), name)
                  for name in model.model().names()
                  if 'template' in snippets.get(name).variables)
    title = inputdialog.getText(mainwindow,
        _("Save as Template"),
        _("Please enter a template name:"),
        regexp=r"\w(.*\w)?", complete=sorted(titles))
    if not title:
        return
    
    if title in titles:
        if QMessageBox.critical(mainwindow,
            _("Overwrite Template?"),
            _("A template named \"{name}\" already exists.\n\n"
              "Do you want to overwrite it?").format(name=title),
            QMessageBox.Yes | QMessageBox.Cancel) != QMessageBox.Yes:
            return
        name = titles[title]
    else:
        name = None
    
    # get the text and insert cursor position or selection
    cursor = mainwindow.textCursor()
    text = cursor.document().toPlainText()
    
    repls = [(cursor.position(), '${CURSOR}')]
    if cursor.hasSelection():
        repls.append((cursor.anchor(), '${ANCHOR}'))
        repls.sort()
        
    result = []
    prev = 0
    for pos, what in repls:
        result.append(text[prev:pos].replace('$', '$$'))
        result.append(what)
        prev = pos
    result.append(text[prev:].replace('$', '$$'))
    text = ''.join(result)
    
    # add header line, if it is lilypond, enable autorun
    headerline = '-*- template; indent: no;'
    if documentinfo.mode(cursor.document()) == 'lilypond':
        headerline += ' template-run;'
    text = headerline + '\n' + text
    
    # save the new snippet
    model.model().saveSnippet(name, text, title)
コード例 #10
0
ファイル: pitch.py プロジェクト: rdoursenaud/frescobaldi
def getModeShifter(document, mainwindow):
    """Show a dialog and return the desired mode shifter.
    
    Returns None if the dialog was cancelled.
    
    TODO: 
    1. Create a dialog where you can choose the mode from a dropdown list.
    2. Define more modes/scales.
    
    """
    from fractions import Fraction
    # Mode definitions
    modes = {
    'Major': (0, 1, 2, Fraction(5, 2), Fraction(7, 2), Fraction(9, 2), Fraction(11, 2)),
    'Minor': (0, 1, Fraction(3, 2), Fraction(5, 2), Fraction(7, 2), 4, Fraction(11, 2)),
    'Natminor': (0, 1, Fraction(3, 2), Fraction(5, 2), Fraction(7, 2), 4, 5),
    'Dorian': (0, 1, Fraction(3, 2), Fraction(5, 2), Fraction(7, 2), Fraction(9, 2), 5)
    }
    language = documentinfo.docinfo(document).language() or 'nederlands'
    
    def readpitches(text):
        """Reads pitches from text."""
        result = []
        for pitch, octave in re.findall(r"([a-z]+)([,']*)", text):
            r = ly.pitch.pitchReader(language)(pitch)
            if r:
                result.append(ly.pitch.Pitch(*r, octave=ly.pitch.octaveToNum(octave)))
        return result
        
    def validate(text):
        """Validates text by checking if it contains a defined mode."""
        words = text.split()
        one = bool(words) and len(readpitches(words[0])) == 1
        sec = len(words) > 1 and words[1].capitalize() in modes
        return one and sec
    
    text = inputdialog.getText(mainwindow, _("Shift mode"), _(
        "Please enter the mode to shift to. (i.e. \"D major\")"
        ), icon = icons.get('tools-transpose'),
        help = "mode_shift", validate = validate)
    if text:
        words = text.split()
        key = readpitches(words[0])[0]
        scale = modes[words[1].capitalize()]
        return ly.pitch.transpose.ModeShifter(key, scale)
コード例 #11
0
ファイル: pitch.py プロジェクト: shimpe/frescobaldi
def getModalTransposer(document, mainwindow):
    """Show a dialog and return the desired modal transposer.
    
    Returns None if the dialog was cancelled.
    
    """
    language = documentinfo.docinfo(document).language() or 'nederlands'

    def readpitches(text):
        """Reads pitches from text."""
        result = []
        for pitch, octave in re.findall(r"([a-z]+)([,']*)", text):
            r = ly.pitch.pitchReader(language)(pitch)
            if r:
                result.append(
                    ly.pitch.Pitch(*r, octave=ly.pitch.octaveToNum(octave)))
        return result

    def validate(text):
        """Returns whether the text is an integer followed by the name of a key."""
        words = text.split()
        if len(words) != 2:
            return False
        try:
            steps = int(words[0])
            keyIndex = ly.pitch.transpose.ModalTransposer.getKeyIndex(words[1])
            return True
        except ValueError:
            return False

    text = inputdialog.getText(
        mainwindow,
        _("Transpose"),
        _("Please enter the number of steps to alter by, followed by a key signature. (i.e. \"5 F\")"
          ),
        icon=icons.get('tools-transpose'),
        help="modal_transpose",
        validate=validate)
    if text:
        words = text.split()
        return ly.pitch.transpose.ModalTransposer(
            int(words[0]),
            ly.pitch.transpose.ModalTransposer.getKeyIndex(words[1]))
コード例 #12
0
ファイル: pitch.py プロジェクト: jan-warchol/frescobaldi
def getModalTransposer(document, mainwindow):
    """Show a dialog and return the desired modal transposer.
    
    Returns None if the dialog was cancelled.
    
    """
    language = documentinfo.info(document).pitchLanguage() or 'nederlands'
    
    def readpitches(text):
        """Reads pitches from text."""
        result = []
        for pitch, octave in re.findall(r"([a-z]+)([,']*)", text):
            r = ly.pitch.pitchReader(language)(pitch)
            if r:
                result.append(ly.pitch.Pitch(*r, octave=ly.pitch.octaveToNum(octave)))
        return result
    
    def validate(text):
        """Returns whether the text is an integer followed by the name of a key."""
        words = text.split()
        if len(words) != 2:
            return False
        try:
            steps = int(words[0])
            keyIndex = ly.pitch.ModalTransposer.getKeyIndex(words[1])
            return True
        except ValueError:
            return False
    
    text = inputdialog.getText(mainwindow, _("Transpose"), _(
        "Please enter the number of steps to alter by, followed by a key signature. (i.e. \"5 F\")"
        ), icon = icons.get('tools-transpose'),
        help = transpose_help, validate = validate)
    if text:
        words = text.split()
        return ly.pitch.ModalTransposer(int(words[0]), ly.pitch.ModalTransposer.getKeyIndex(words[1]))
コード例 #13
0
ファイル: cut_assign.py プロジェクト: zsalch/frescobaldi
def cut_assign(cursor):
    """Cuts selected text and assigns it to a LilyPond variable."""
    # ask the variable name
    name = inputdialog.getText(
        None,
        _("Cut and Assign"),
        _("Please enter the name for the variable to assign the selected "
          "text to:"),
        regexp="[A-Za-z]+")
    if not name:
        return

    cursortools.strip_selection(cursor)

    # determine state at cursor
    block = cursortools.block(cursor)
    state = tokeniter.state(block)
    for t in tokeniter.partition(cursor).left:
        state.follow(t)

    mode = ""
    for p in state.parsers():
        if isinstance(p, ly.lex.lilypond.ParseInputMode):
            if isinstance(p, ly.lex.lilypond.ParseLyricMode):
                mode = " \\lyricmode"
            elif isinstance(p, ly.lex.lilypond.ParseChordMode):
                mode = " \\chordmode"
            elif isinstance(p, ly.lex.lilypond.ParseFigureMode):
                mode = " \\figuremode"
            elif isinstance(p, ly.lex.lilypond.ParseDrumMode):
                mode = " \\drummode"
            break

    # find insertion place:
    found = False
    while block.previous().isValid():
        block = block.previous()
        state = tokeniter.state(block)
        if isinstance(state.parser(), ly.lex.lilypond.ParseGlobal):
            found = True
            break
        tokens = tokeniter.tokens(block)
        for t in tokens:
            if isinstance(t, ly.lex.lilypond.Name):
                found = True
                break
            elif not isinstance(t, (ly.lex.Space, ly.lex.Comment)):
                break
        if found:
            break
    insert = QTextCursor(block)
    text = cursor.selection().toPlainText()
    space = '\n' if '\n' in text else ' '
    text = ''.join((name, ' =', mode, ' {', space, text, space, '}\n\n'))
    with cursortools.compress_undo(cursor):
        cursor.insertText('\\' + name)
        pos = insert.selectionStart()
        insert.insertText(text)
    if metainfo.info(cursor.document()).auto_indent:
        insert.setPosition(pos, QTextCursor.KeepAnchor)
        with cursortools.compress_undo(insert, True):
            indent.re_indent(insert)
コード例 #14
0
ファイル: pitch.py プロジェクト: WedgeLeft/frescobaldi
def transpose(cursor, mainwindow):
    """Transposes pitches."""
    language = documentinfo.info(cursor.document()).pitchLanguage() or 'nederlands'
    
    def readpitches(text):
        """Reads pitches from text."""
        result = []
        for pitch, octave in re.findall(r"([a-z]+)([,']*)", text):
            r = ly.pitch.pitchReader(language)(pitch)
            if r:
                result.append(ly.pitch.Pitch(*r, octave=ly.pitch.octaveToNum(octave)))
        return result
    
    def validate(text):
        """Returns whether the text contains exactly two pitches."""
        return len(readpitches(text)) == 2
    
    text = inputdialog.getText(mainwindow, _("Transpose"), _(
        "Please enter two absolute pitches, separated by a space, "
        "using the pitch name language \"{language}\"."
        ).format(language=language), icon = icons.get('tools-transpose'),
        help = transpose_help, validate = validate)
    if text == None:
        return
    
    transposer = ly.pitch.Transposer(*readpitches(text))
    
    selection = cursor.hasSelection()
    if selection:
        start = cursor.selectionStart()
        cursor.setPosition(cursor.selectionEnd())
        cursor.setPosition(0, QTextCursor.KeepAnchor)
        source = tokeniter.Source.selection(cursor, True)
    else:
        source = tokeniter.Source.document(cursor, True)
    
    pitches = PitchIterator(source)
    psource = pitches.pitches()
    
    class gen(object):
        def __init__(self):
            self.inSelection = not selection
        
        def __iter__(self):
            return self
        
        def __next__(self):
            while True:
                t = next(psource)
                if isinstance(t, (ly.lex.Space, ly.lex.Comment)):
                    continue
                elif not self.inSelection and pitches.position(t) >= start:
                    self.inSelection = True
                # Handle stuff that's the same in relative and absolute here
                if t == "\\relative":
                    relative()
                elif isinstance(t, ly.lex.lilypond.MarkupScore):
                    absolute(context())
                elif isinstance(t, ly.lex.lilypond.ChordMode):
                    chordmode()
                elif isinstance(t, ly.lex.lilypond.PitchCommand):
                    if t == "\\transposition":
                        next(psource) # skip pitch
                    elif t == "\\transpose":
                        for p in getpitches(context()):
                            transpose(p)
                    elif t == "\\key":
                        for p in getpitches(context()):
                            transpose(p, 0)
                    else:
                        return t
                else:
                    return t
        
        next = __next__
    
    tsource = gen()
    
    def context():
        """Consume tokens till the level drops (we exit a construct)."""
        depth = source.state.depth()
        for t in tsource:
            yield t
            if source.state.depth() < depth:
                return
    
    def consume():
        """Consume tokens from context() returning the last token, if any."""
        t = None
        for t in context():
            pass
        return t
        
    def transpose(p, resetOctave = None):
        """Transpose absolute pitch, using octave if given."""
        transposer.transpose(p)
        if resetOctave is not None:
            p.octave = resetOctave
        if tsource.inSelection:
            pitches.write(p, editor)

    def chordmode():
        """Called inside \\chordmode or \\chords."""
        for p in getpitches(context()):
            transpose(p, 0)
            
    def absolute(tokens):
        """Called when outside a possible \\relative environment."""
        for p in getpitches(tokens):
            transpose(p)
    
    def relative():
        """Called when \\relative is encountered."""
        def transposeRelative(p, lastPitch):
            """Transposes a relative pitch; returns the pitch in absolute form."""
            # absolute pitch determined from untransposed pitch of lastPitch
            p.makeAbsolute(lastPitch)
            if not tsource.inSelection:
                return p
            # we may change this pitch. Make it relative against the
            # transposed lastPitch.
            try:
                last = lastPitch.transposed
            except AttributeError:
                last = lastPitch
            # transpose a copy and store that in the transposed
            # attribute of lastPitch. Next time that is used for
            # making the next pitch relative correctly.
            newLastPitch = p.copy()
            transposer.transpose(p)
            newLastPitch.transposed = p.copy()
            if p.octaveCheck is not None:
                p.octaveCheck = p.octave
            p.makeRelative(last)
            if relPitch:
                # we are allowed to change the pitch after the
                # \relative command. lastPitch contains this pitch.
                lastPitch.octave += p.octave
                p.octave = 0
                pitches.write(lastPitch, editor)
                del relPitch[:]
            pitches.write(p, editor)
            return newLastPitch

        lastPitch = None
        relPitch = [] # we use a list so it can be changed from inside functions
        
        # find the pitch after the \relative command
        t = next(tsource)
        if isinstance(t, Pitch):
            lastPitch = t
            if tsource.inSelection:
                relPitch.append(lastPitch)
            t = next(tsource)
        else:
            lastPitch = Pitch.c1()
        
        while True:
            # eat stuff like \new Staff == "bla" \new Voice \notes etc.
            if isinstance(source.state.parser(), ly.lex.lilypond.ParseTranslator):
                t = consume()
            elif isinstance(t, ly.lex.lilypond.NoteMode):
                t = next(tsource)
            else:
                break
        
        # now transpose the relative expression
        if t in ('{', '<<'):
            # Handle full music expression { ... } or << ... >>
            for t in context():
                if t == '\\octaveCheck':
                    for p in getpitches(context()):
                        lastPitch = p.copy()
                        del relPitch[:]
                        if tsource.inSelection:
                            transposer.transpose(p)
                            lastPitch.transposed = p
                            pitches.write(p, editor)
                elif isinstance(t, ly.lex.lilypond.ChordStart):
                    chord = [lastPitch]
                    for p in getpitches(context()):
                        chord.append(transposeRelative(p, chord[-1]))
                    lastPitch = chord[:2][-1] # same or first
                elif isinstance(t, Pitch):
                    lastPitch = transposeRelative(t, lastPitch)
        elif isinstance(t, ly.lex.lilypond.ChordStart):
            # Handle just one chord
            for p in getpitches(context()):
                lastPitch = transposeRelative(p, lastPitch)
        elif isinstance(t, Pitch):
            # Handle just one pitch
            transposeRelative(token, lastPitch)

    # Do it!
    try:
        with qutil.busyCursor():
            with cursortools.Editor() as editor:
                absolute(tsource)
    except ly.pitch.PitchNameNotAvailable:
        QMessageBox.critical(mainwindow, app.caption(_("Transpose")), _(
            "Can't perform the requested transposition.\n\n"
            "The transposed music would contain quarter-tone alterations "
            "that are not available in the pitch language \"{language}\"."
            ).format(language = pitches.language))