예제 #1
0
 def actionTriggered(self, name):
     # convert arpeggio_normal to arpeggioNormal, etc.
     name = _arpeggioTypes[name]
     cursor = self.mainwindow().textCursor()
     # which arpeggio type is last used?
     lastused = '\\arpeggioNormal'
     types = set(_arpeggioTypes.values())
     block = cursor.block()
     while block.isValid():
         s = types.intersection(tokeniter.tokens(block))
         if s:
             lastused = s.pop()
             break
         block = block.previous()
     # where to insert
     source = tokeniter.Source.fromCursor(cursor, True, -1)
     with cursortools.editBlock(cursor):
         for p in music.music_items(source, tokens=source.tokens):
             c = source.cursor(p[-1], start=len(p[-1]))
             c.insertText('\\arpeggio')
             if name != lastused:
                 cursortools.stripIndent(c)
                 import indent
                 indent.insertText(c, name + '\n')
             # just pick the first place
             return
예제 #2
0
def changeIndent(cursor, direction):
    """Changes the indent in the desired direction (-1 for left and +1 for right).
    
    Returns True if the indent operation was applied.
    The cursor may contain a selection.
    
    """
    # get some variables from the document
    indent_vars = indentVariables(cursor.document())
    
    blocks = list(cursortools.blocks(cursor))
    block = blocks[0]
    pos = cursor.selectionStart() - block.position()
    token = tokeniter.tokens(block)[0] if tokeniter.tokens(block) else None
    if cursor.hasSelection() or pos == 0 or (token and isinstance(token, ly.lex.Space) and token.end >= pos):
        # decrease the indent
        state = tokeniter.state(block)
        current_indent = getIndent(block)
        new_indent = current_indent + direction * indent_vars['indent-width']
        if state.mode() in ('lilypond', 'scheme'):
            computed_indent = computeIndent(block)
            if cmp(computed_indent, new_indent) == direction:
                new_indent = computed_indent
        diff = new_indent - current_indent
        with cursortools.editBlock(cursor):
            for block in blocks:
                setIndent(block, getIndent(block) + diff)
        return True
예제 #3
0
def changeLanguage(cursor, language):
    """Changes the language of the pitch names."""
    selection = cursor.hasSelection()
    if selection:
        start = cursor.selectionStart()
        cursor.setPosition(cursor.selectionEnd())
        cursor.setPosition(0, QTextCursor.KeepAnchor)
        source = tokeniter.Source.selection(cursor)
    else:
        source = tokeniter.Source.document(cursor)
    
    pitches = PitchIterator(source)
    tokens = pitches.tokens()
    writer = ly.pitch.pitchWriter(language)
    
    if selection:
        # consume tokens before the selection, following the language
        source.consume(tokens, start)
    
    changed = False # track change of \language or \include language command
    with cursortools.editBlock(cursor):
        try:
            with util.busyCursor():
                with cursortools.Editor() as e:
                    for t in tokens:
                        if isinstance(t, ly.lex.lilypond.Note):
                            # translate the pitch name
                            p = pitches.read(t)
                            if p:
                                n = writer(*p)
                                if n != t:
                                    e.insertText(source.cursor(t), n)
                        elif isinstance(t, LanguageName) and t != language:
                            # change the language name in a command
                            e.insertText(source.cursor(t), language)
                            changed = True
        except ly.pitch.PitchNameNotAvailable:
            QMessageBox.critical(None, app.caption(_("Pitch Name Language")), _(
                "Can't perform the requested translation.\n\n"
                "The music contains quarter-tone alterations, but "
                "those are not available in the pitch language \"{name}\"."
                ).format(name=language))
            return
        if changed:
            return
        if not selection:
            # there was no selection and no language command, so insert one
            insertLanguage(cursor.document(), language)
            return
    # there was a selection but no command, user must insert manually.
    QMessageBox.information(None, app.caption(_("Pitch Name Language")),
        '<p>{0}</p>'
        '<p><code>\\include "{1}.ly"</code> {2}</p>'
        '<p><code>\\language "{1}"</code> {3}</p>'.format(
            _("The pitch language of the selected text has been "
                "updated, but you need to manually add the following "
                "command to your document:"),
            language,
            _("(for LilyPond below 2.14), or"),
            _("(for LilyPond 2.14 and higher.)")))
예제 #4
0
 def slotAccepted(self):
     """Makes the score and puts it in the editor."""
     from . import build
     builder = build.Builder(self)
     cursor = self.parent().currentView().textCursor()
     with cursortools.editBlock(cursor):
         cursortools.insertText(cursor, builder.text())
         indent.reIndent(cursor)
예제 #5
0
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(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.editBlock(cursor):
        cursor.insertText('\\' + name)
        if metainfo.info(cursor.document()).autoindent:
            indent.insertText(insert, text)
        else:
            insert.insertText(text)
예제 #6
0
def reformat(cursor):
    """Reformats the selection or the whole document, adjusting the whitespace."""
    def newlinebefore(t):
        editor.insertText(tokeniter.cursor(block, t, end=0), '\n')
    
    def newlineafter(t):
        editor.insertText(tokeniter.cursor(block, t, start=len(t)), '\n')
    
    indent_vars = indent.indentVariables(cursor.document())
    
    with cursortools.editBlock(cursor):
        indent.reIndent(cursor)
        with cursortools.Editor() as editor:
            for block in get_blocks(cursor):
                
                denters = []
                tokens = tokeniter.tokens(block)
                
                nonspace_index = -1
                for i, t in enumerate(tokens):
                    if isinstance(t, ly.lex.Indent) and t in ('{', '<<'):
                        denters.append(i)
                    elif isinstance(t, ly.lex.Dedent) and t in ('}', '>>'):
                        if denters:
                            denters.pop()
                        elif nonspace_index != -1:
                            newlinebefore(t)
                    elif not isinstance(t, ly.lex.Space):
                        nonspace_index = i
                for i in denters:
                    if i < nonspace_index:
                        newlineafter(tokens[i])
                    
                # TODO: wrap long lines
        
        indent.reIndent(cursor)
        
        with cursortools.Editor() as editor:
            for block in get_blocks(cursor):
                tokens = tokeniter.tokens(block)
                if (len(tokens) == 2
                    and isinstance(tokens[0], ly.lex.Space)
                    and isinstance(tokens[1], (
                        ly.lex.lilypond.LineComment,
                        ly.lex.scheme.LineComment))
                    and len(tokens[1]) > 2
                    and len(set(tokens[1][:3])) == 1):
                    # move commented lines with more than 2 comment characters
                    # to column 0
                    editor.removeSelectedText(tokeniter.cursor(block, tokens[0]))
                else:
                    # remove trialing whitespace
                    for t in tokens[::-1]:
                        if isinstance(t, ly.lex.Space):
                            editor.removeSelectedText(tokeniter.cursor(block, t))
                        else:
                            break
예제 #7
0
 def actionTriggered(self, name):
     name = name[8:]
     direction = ['_', '', '^'][self.direction() + 1]
     isSpanner = name not in dynamic_marks
     if isSpanner:
         dynamic = dynamic_spanners[name]
     else:
         dynamic = '\\' + name
     cursor = self.mainwindow().textCursor()
     if not cursor.hasSelection():
         # dynamic right before the cursor?
         left = tokeniter.partition(cursor).left
         if not left or not isinstance(left[-1], ly.lex.lilypond.Dynamic):
             # no, find the first pitch
             source = tokeniter.Source.fromCursor(cursor, True, -1)
             for p in music.music_items(source):
                 cursor = source.cursor(p[-1], start=len(p[-1]))
                 break
         cursor.insertText(direction + dynamic)
         self.mainwindow().currentView().setTextCursor(cursor)
     else:
         source = tokeniter.Source.selection(cursor, True)
         cursors = [source.cursor(p[-1], start=len(p[-1]))
             for p in music.music_items(source)]
         if not cursors:
             return
         c1, c2 = cursors[0], cursors[-1]
         # are there dynamics at the cursor? then skip them
         d1 = dynamics(c1)
         if d1:
             c1 = tokeniter.cursor(c1.block(), d1[-1], start=len(d1[-1]))
         with cursortools.editBlock(cursor):
             if len(cursors) > 1:
                 # dynamics after the end cursor?
                 d2 = dynamics(c2)
                 if isSpanner and not d2:
                     # don't terminate the spanner if there's a dynamic there
                     c2.insertText('\\!')
                 elif set(d1).intersection(dynamic_spanners.values()):
                     # write the dynamic at the end if there's a spanner at start
                     # remove ending \! if there
                     terminator = tokeniter.find("\\!", d2)
                     if terminator:
                         c2 = tokeniter.cursor(c2.block(), terminator)
                     if direction in d1:
                         c2.insertText(dynamic)
                     else:
                         c2.insertText(direction + dynamic)
                     return
             c1.insertText(direction + dynamic)
예제 #8
0
    def actionTriggered(self, name):
        d = ['_', '', '^'][self.direction()+1]
        if name == "spanner_slur":
            spanner = d + '(', ')'
        elif name == "spanner_phrasingslur":
            spanner = d + '\\(', '\\)'
        elif name == "spanner_beam16":
            spanner = d + '[', ']'
        elif name == "spanner_trill":
            spanner = '\\startTrillSpan', '\\stopTrillSpan'

        cursor = self.mainwindow().textCursor()
        with cursortools.editBlock(cursor):
            for s, c in zip(spanner, spanner_positions(cursor)):
                c.insertText(s)
예제 #9
0
def insertText(cursor, text):
    """Inserts text and indents it if there are newlines in it."""
    if '\n' not in text:
        cursor.insertText(text)
        return
    line = cursor.document().findBlock(cursor.selectionStart()).blockNumber()
    with cursortools.editBlock(cursor):
        cursor.insertText(text)
        block = cursor.document().findBlockByNumber(line)
        last = cursor.block()
        tokeniter.update(block) # tokenize inserted lines
        while last != block:
            block = block.next()
            if setIndent(block, computeIndent(block)):
                tokeniter.update(block)
예제 #10
0
def reIndent(cursor):
    """Re-indents the selected region or the whole document."""
    if cursor.hasSelection():
        blocks = cursortools.blocks(cursor)
    else:
        blocks = cursortools.allBlocks(cursor.document())
    with cursortools.editBlock(cursor):
        for block in blocks:
            tokeniter.update(block)
            if tokeniter.state(block).mode() in ('lilypond', 'scheme'):
                indent = computeIndent(block)
            else:
                indent = getIndent(block)
            if setIndent(block, indent):
                tokeniter.update(block) # force token update if changed
예제 #11
0
 def actionTriggered(self, name):
     if self.tool().shorthands.isChecked() and name in shorthands:
         text = '_-^'[self.direction()+1] + shorthands[name]
     else:
         text = ('_', '', '^')[self.direction()+1] + '\\' + name
     cursor = self.mainwindow().textCursor()
     selection = cursor.hasSelection()
     cursors = articulation_positions(cursor)
     if cursors:
         with cursortools.editBlock(cursor):
             for c in cursors:
                 c.insertText(text)
         if not selection:
             self.mainwindow().currentView().setTextCursor(c)
     elif not selection:
         cursor.insertText(text)
예제 #12
0
 def keyPressEvent(self, ev):
     if homekey.handle(self, ev):
         return
     super(View, self).keyPressEvent(ev)
     
     if metainfo.info(self.document()).autoindent:
         # run the indenter on Return or when the user entered a dedent token.
         import indent
         cursor = self.textCursor()
         if ev.text() == '\r' or (ev.text() in ('}', '#', '>') and indent.indentable(cursor)):
             with cursortools.editBlock(cursor, True):
                 indent.autoIndentBlock(cursor.block())
             # keep the cursor at the indent position on vertical move
             cursor = self.textCursor()
             pos = cursor.position()
             cursor.setPosition(cursor.block().position()) # move horizontal
             cursor.setPosition(pos) # move back to position
             self.setTextCursor(cursor)
예제 #13
0
 def hyphenate(self):
     """Hyphenates selected Lyrics text."""
     view = self.mainwindow().currentView()
     cursor = view.textCursor()
     found = []
     # find text to hyphenate
     if cursor.hasSelection():
         source = tokeniter.Source.selection(cursor)
     else:
         source = tokeniter.Source.document(cursor)
     for token in source:
         if isinstance(token, ly.lex.lilypond.LyricText):
             # a word found
             for m in _word_re.finditer(token):
                 found.append((source.cursor(token, m.start(), m.end()), m.group()))
     if not found and cursor.hasSelection():
         # no tokens were found, then tokenize the text again as if in lyricmode
         start = cursor.selectionStart()
         state = ly.lex.State(ly.lex.lilypond.ParseLyricMode)
         for token in state.tokens(cursor.selection().toPlainText()):
             if isinstance(token, ly.lex.lilypond.LyricText):
                 # a word found
                 for m in _word_re.finditer(token):
                     cur = QTextCursor(cursor)
                     cur.setPosition(start + token.pos + m.start())
                     cur.setPosition(start + token.pos + m.end(), QTextCursor.KeepAnchor)
                     found.append((cur, m.group()))
     if not found and cursor.hasSelection():
         # still not succeeded, then try flat text
         for m in _word_re.finditer(cursor.selection().toPlainText()):
             cur = QTextCursor(cursor)
             cur.setPosition(start + m.start())
             cur.setPosition(start + m.end(), QTextCursor.KeepAnchor)
             found.append((cur, m.group()))
     if found:
         import hyphendialog
         h = hyphendialog.HyphenDialog(self.mainwindow()).hyphenator()
         if h:
             with cursortools.editBlock(cursor):
                 for cur, word in found:
                     hyph_word = h.inserted(word, ' -- ')
                     if word != hyph_word:
                         cur.insertText(hyph_word)
예제 #14
0
def insert(name, view):
    """Insert named snippet into the view."""
    text, variables = snippets.get(name)
    cursor = view.textCursor()
    
    selection = variables.get('selection', '')
    if 'yes' in selection and not cursor.hasSelection():
        return
    if 'strip' in selection:
        cursortools.strip(cursor)
    
    pos = cursor.selectionStart()
    line = cursor.document().findBlock(pos).blockNumber()
    with cursortools.editBlock(cursor):
        
        # insert the snippet, might return a new cursor
        if 'python' in variables:
            new = insert_python(text, cursor, name, view)
        else:
            new = insert_snippet(text, cursor, variables)
        
        # QTextBlocks the snippet starts and ends
        block = cursor.document().findBlockByNumber(line)
        last = cursor.block()
        
        # re-indent if not explicitly suppressed by a 'indent: no' variable
        if last != block and 'no' not in variables.get('indent', ''):
            tokeniter.update(block) # tokenize inserted lines
            while True:
                block = block.next()
                if indent.setIndent(block, indent.computeIndent(block)):
                    tokeniter.update(block)
                if block == last:
                    break
    
    if not new and 'keep' in selection:
        end = cursor.position()
        cursor.setPosition(pos)
        cursor.setPosition(end, QTextCursor.KeepAnchor)
    view.setTextCursor(new or cursor)
예제 #15
0
def rhythm_remove(cursor):
    with cursortools.editBlock(cursor):
        for c in cursors(cursor, ly.lex.lilypond.Duration):
            c.removeSelectedText()
예제 #16
0
def rhythm_remove_scaling(cursor):
    with cursortools.editBlock(cursor):
        for c in cursors(cursor, ly.lex.lilypond.Scaling):
            c.removeSelectedText()