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 c = lydocument.cursor(cursor) c.end = None for item in ly.rhythm.music_items(c, partial=ly.document.OUTSIDE): cursor.setPosition(item.end) break cursor.insertText(direction + dynamic) self.mainwindow().currentView().setTextCursor(cursor) else: c = lydocument.cursor(cursor) cursors = [] for item in ly.rhythm.music_items(c): csr = QTextCursor(cursor.document()) csr.setPosition(item.end) cursors.append(csr) 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.compress_undo(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)
def articulation_positions(cursor): """Returns a list of positions where an articulation can be added. Every position is given as a QTextCursor instance. If the cursor has a selection, all positions in the selection are returned. """ c = lydocument.cursor(cursor) if not cursor.hasSelection(): # just select til the end of the current line c.select_end_of_block() rests = True partial = ly.document.OUTSIDE else: rests = False partial = ly.document.INSIDE source = lydocument.Source(c, True, partial, True) positions = [] for p in ly.rhythm.music_tokens(source): if not rests and isinstance(p[0], ly.lex.lilypond.Rest): continue positions.append(source.cursor(p[-1], start=len(p[-1]))) if not cursor.hasSelection(): break # leave if first found, that's enough return positions
def changeLanguage(cursor, language): """Changes the language of the pitch names.""" c = lydocument.cursor(cursor, select_all=True) try: with qutil.busyCursor(): changed = ly.pitch.translate.translate(c, language) 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 cursor.hasSelection(): # there was no selection and no language command, so insert one version = (documentinfo.docinfo(cursor.document()).version() or lilypondinfo.preferred().version()) ly.pitch.translate.insert_language(c.document, language, version) 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.)")))
def changeLanguage(cursor, language): """Changes the language of the pitch names.""" c = lydocument.cursor(cursor, select_all=True) try: with qutil.busyCursor(): changed = ly.pitch.translate.translate(c, language) 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 cursor.hasSelection(): # there was no selection and no language command, so insert one version = (documentinfo.docinfo(cursor.document()).version() or lilypondinfo.preferred().version()) ly.pitch.translate.insert_language(c.document, language, version) 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.)")))
def abs2rel(cursor, startpitch, first_pitch_absolute): """Converts pitches from absolute to relative.""" with qutil.busyCursor(): c = lydocument.cursor(cursor, select_all=True) ly.pitch.abs2rel.abs2rel(c, startpitch=startpitch, first_pitch_absolute=first_pitch_absolute)
def spanner_positions(cursor): """Return a list with 0 to 2 QTextCursor instances. At the first cursor a starting spanner item can be inserted, at the second an ending item. """ c = lydocument.cursor(cursor) if cursor.hasSelection(): partial = ly.document.INSIDE else: # just select until the end of the current line c.select_end_of_block() partial = ly.document.OUTSIDE items = list(ly.rhythm.music_items(c, partial=partial)) if cursor.hasSelection(): del items[1:-1] else: del items[2:] positions = [] for i in items: c = QTextCursor(cursor.document()) c.setPosition(i.end) positions.append(c) return positions
def articulation_positions(cursor): """Returns a list of positions where an articulation can be added. Every position is given as a QTextCursor instance. If the cursor has a selection, all positions in the selection are returned. """ c = lydocument.cursor(cursor) if not cursor.hasSelection(): # just select until the end of the current line c.select_end_of_block() rests = True partial = ly.document.OUTSIDE else: rests = False partial = ly.document.INSIDE positions = [] for item in ly.rhythm.music_items(c, partial): if not rests and item.tokens and isinstance(item.tokens[0], ly.lex.lilypond.Rest): continue csr = QTextCursor(cursor.document()) csr.setPosition(item.end) positions.append(csr) if not cursor.hasSelection(): break # leave if first found, that's enough return positions
def articulation_positions(cursor): """Returns a list of positions where an articulation can be added. Every position is given as a QTextCursor instance. If the cursor has a selection, all positions in the selection are returned. """ c = lydocument.cursor(cursor) if not cursor.hasSelection(): # just select until the end of the current line c.select_end_of_block() rests = True partial = ly.document.OUTSIDE else: rests = False partial = ly.document.INSIDE source = lydocument.Source(c, True, partial, True) positions = [] for p in ly.rhythm.music_tokens(source): if not rests and isinstance(p[0], ly.lex.lilypond.Rest): continue positions.append(source.cursor(p[-1], start=len(p[-1]))) if not cursor.hasSelection(): break # leave if first found, that's enough return positions
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 c = lydocument.cursor(cursor) c.select_end_of_block() with cursortools.compress_undo(cursor): for item in ly.rhythm.music_items(c, partial=ly.document.OUTSIDE): c = QTextCursor(cursor.document()) c.setPosition(item.end) c.insertText('\\arpeggio') if name != lastused: cursortools.strip_indent(c) indent = c.block().text()[:c.position() - c.block().position()] c.insertText(name + '\n' + indent) # just pick the first place return
def remove_trailing_whitespace(cursor): """Remove trailing whitespace from all lines in the selection. If there is no selection, the whole document is used. """ ly.reformat.remove_trailing_whitespace(lydocument.cursor(cursor, select_all=True))
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 c = lydocument.cursor(cursor) c.select_end_of_block() source = lydocument.Source(c, True, ly.document.OUTSIDE, True) with cursortools.compress_undo(cursor): for p in ly.rhythm.music_tokens(source): c = source.cursor(p[-1], start=len(p[-1])) c.insertText('\\arpeggio') if name != lastused: cursortools.strip_indent(c) indent = c.block().text()[:c.position()-c.block().position()] c.insertText(name + '\n' + indent) # just pick the first place return
def html_inline(cursor, scheme='editor', inline=True, number_lines=False, full_html=True): """Return an (by default) inline-styled HTML document for the cursor's selection.""" c = lydocument.cursor(cursor) return html(c, scheme, inline, number_lines, full_html)
def increase_indent(cursor): """Increases the indent of the line the cursor is at (or the selected lines). If there is no selection or the cursor is not in the first indent space, just inserts a Tab (or spaces). """ c = lydocument.cursor(cursor) indenter(cursor.document()).increase_indent(c)
def re_indent(cursor, indent_blank_lines=False): """Re-indents the selected region or the whole document. If indent_blank_lines is True, the indent of blank lines is made larger if necessary. If False (the default), the indent of blank lines if not changed if it is shorter than it should be. """ c = lydocument.cursor(cursor, select_all=True) indenter(cursor.document()).indent(c, indent_blank_lines)
def actionTriggered(self, name): d = ['_', '', '^'][self.direction() + 1] single = '' if name == "grace_grace": inner = '' outer = '\\grace { ', ' }' single = '\\grace ' elif name == "grace_beam": inner = d + '[', ']' outer = '\\grace { ', ' }' elif name == "grace_accia": inner = '' outer = '\\acciaccatura { ', ' }' single = '\\acciaccatura ' elif name == "grace_appog": inner = '' outer = '\\appoggiatura { ', ' }' single = '\\appoggiatura ' elif name == "grace_slash": inner = d + '[', ']' outer = '\\slashedGrace { ', ' }' elif name == "grace_after": inner = d + '{ ' outer = '\\afterGrace ', ' }' cursor = self.mainwindow().textCursor() with cursortools.compress_undo(cursor): if inner: for i, ci in zip(inner, spanner_positions(cursor)): ci.insertText(i) if cursor.hasSelection(): ins = self.mainwindow().textCursor() ins.setPosition(cursor.selectionStart()) ins.insertText(outer[0]) ins.setPosition(cursor.selectionEnd()) ins.insertText(outer[1]) else: if single: cursor.insertText(single) else: c = lydocument.cursor(cursor) c.end = None items = list( ly.rhythm.music_items(c, partial=ly.document.OUTSIDE)) after = self.mainwindow().textCursor() try: i = items[2] pos = i.pos + 1 end = (i.tokens or i.dur_tokens)[0].end after.setPosition(pos) after.setPosition(end, QTextCursor.KeepAnchor) except IndexError: after.movePosition(cursor.EndOfLine) after.insertText(outer[1]) cursor.insertText(outer[0])
def positions(cursor): """Return a list of QTextCursors describing the grob the cursor points at. When the cursor point at e.g. a slur, the returned cursors describe both ends of the slur. The returned list may contain zero to two cursors. """ c = lydocument.cursor(cursor) c.end = None source = lydocument.Source(c, True) for token in source.tokens: break else: return [] cur = source.cursor(token, end=0) cursors = [cur] # some heuristic to find the relevant range(s) the linked grob represents if isinstance(token, ly.lex.lilypond.Direction): # a _, - or ^ is found; find the next token for token in source: if not isinstance(token, (ly.lex.Space, ly.lex.Comment)): break end = token.end + source.block.position() if token == '\\markup': # find the end of the markup expression depth = source.state.depth() for token in source: if source.state.depth() < depth: end = token.end + source.block.position() break elif token == '"': # find the end of the string for token in source: if isinstance(token, ly.lex.StringEnd): end = token.end + source.block.position() break elif isinstance(token, ly.lex.MatchStart): # find the end of slur, beam. ligature, phrasing slur, etc. name = token.matchname nest = 1 for token in source: if isinstance(token, ly.lex.MatchEnd) and token.matchname == name: nest -= 1 if nest == 0: cursors.append(source.cursor(token)) break elif isinstance(token, ly.lex.MatchStart) and token.matchname == name: nest += 1 cur.setPosition(end, QTextCursor.KeepAnchor) return cursors
def actionTriggered(self, name): d = ['_', '', '^'][self.direction()+1] single = '' if name == "grace_grace": inner = '' outer = '\\grace { ', ' }' single = '\\grace ' elif name == "grace_beam": inner = d + '[', ']' outer = '\\grace { ', ' }' elif name == "grace_accia": inner = '' outer = '\\acciaccatura { ', ' }' single = '\\acciaccatura ' elif name == "grace_appog": inner = '' outer = '\\appoggiatura { ', ' }' single = '\\appoggiatura ' elif name == "grace_slash": inner = d + '[', ']' outer = '\\slashedGrace { ', ' }' elif name == "grace_after": inner = d + '{ ' outer = '\\afterGrace ', ' }' cursor = self.mainwindow().textCursor() with cursortools.compress_undo(cursor): if inner: for i, ci in zip(inner, spanner_positions(cursor)): ci.insertText(i) if cursor.hasSelection(): ins = self.mainwindow().textCursor() ins.setPosition(cursor.selectionStart()) ins.insertText(outer[0]) ins.setPosition(cursor.selectionEnd()) ins.insertText(outer[1]) else: if single: cursor.insertText(single) else: c = lydocument.cursor(cursor) c.end = None items = list(ly.rhythm.music_items(c, partial=ly.document.OUTSIDE)) after = self.mainwindow().textCursor() try: i = items[2] pos = i.pos + 1 end = (i.tokens or i.dur_tokens)[0].end after.setPosition(pos) after.setPosition(end, QTextCursor.KeepAnchor) except IndexError: after.movePosition(cursor.EndOfLine) after.insertText(outer[1]) cursor.insertText(outer[0])
def getCurrentLilyObject(self, cursor): """ Use cursor from textedit link to get type of object being edited.""" lycursor = lydocument.cursor(cursor) self.pos = lycursor.start node = self.docinfo.node(self.pos) self.node = node child = self.docinfo.iter_music(node) for c in child: name = c.__class__.__name__ #get instance name return self.item2object(name) name = node.__class__.__name__ return self.item2object(name)
def transpose(cursor, transposer, mainwindow=None): """Transpose pitches using the specified transposer.""" c = lydocument.cursor(cursor, select_all=True) try: with qutil.busyCursor(): ly.pitch.transpose.transpose(c, transposer) except ly.pitch.PitchNameNotAvailable as e: 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 = e.language))
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)
def html_inline(cursor, scheme='editor', inline=True, number_lines=False, full_html=True, wrap_tag="pre", wrap_attrib="id", wrap_attrib_name="document"): """Return an (by default) inline-styled HTML document for the cursor's selection.""" c = lydocument.cursor(cursor) return html(c, scheme, inline, number_lines, full_html, wrap_tag, wrap_attrib, wrap_attrib_name)
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)
def actionTriggered(self, name): d = ['_', '', '^'][self.direction()+1] single = '' if name == "grace_grace": inner = '' outer = '\\grace { ', ' }' single = '\\grace ' elif name == "grace_beam": inner = d + '[', ']' outer = '\\grace { ', ' }' elif name == "grace_accia": inner = '' outer = '\\acciaccatura { ', ' }' single = '\\acciaccatura ' elif name == "grace_appog": inner = '' outer = '\\appoggiatura { ', ' }' single = '\\appoggiatura ' elif name == "grace_slash": inner = d + '[', ']' outer = '\\slashedGrace { ', ' }' elif name == "grace_after": inner = d + '{ ' outer = '\\afterGrace ', ' }' cursor = self.mainwindow().textCursor() with cursortools.compress_undo(cursor): if inner: for i, ci in zip(inner, spanner_positions(cursor)): ci.insertText(i) if cursor.hasSelection(): ins = self.mainwindow().textCursor() ins.setPosition(cursor.selectionStart()) ins.insertText(outer[0]) ins.setPosition(cursor.selectionEnd()) ins.insertText(outer[1]) else: if single: cursor.insertText(single) else: c = lydocument.cursor(cursor) c.end = None source = lydocument.Source(c, True, ly.document.OUTSIDE, True) music_list = list(ly.rhythm.music_tokens(source)) try: m = music_list[2][0] after = source.cursor(m, 1) except IndexError: after = self.mainwindow().textCursor() after.movePosition(cursor.EndOfLine) after.insertText(outer[1]) cursor.insertText(outer[0])
def transpose(cursor, transposer, mainwindow=None): """Transpose pitches using the specified transposer.""" c = lydocument.cursor(cursor, select_all=True) try: with qutil.busyCursor(): ly.pitch.transpose.transpose(c, transposer) except ly.pitch.PitchNameNotAvailable as e: 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=e.language))
def actionTriggered(self, name): cursor = self.mainwindow().textCursor() style = _glissandoStyles[name] c = lydocument.cursor(cursor) c.select_end_of_block() for item in ly.rhythm.music_items(c, partial=ly.document.OUTSIDE): c = QTextCursor(cursor.document()) c.setPosition(item.end) if style: text = "-\\tweak #'style #'{0} \\glissando".format(style) else: text = '\\glissando' c.insertText(text) return
def actionTriggered(self, name): cursor = self.mainwindow().textCursor() style = _glissandoStyles[name] c = lydocument.cursor(cursor) c.select_end_of_block() source = lydocument.Source(c, True, ly.document.OUTSIDE, True) for p in ly.rhythm.music_tokens(source): c = source.cursor(p[-1], start=len(p[-1])) if style: text = "-\\tweak #'style #'{0} \\glissando".format(style) else: text = '\\glissando' c.insertText(text) return
def force_directions(cursor, direction): """Force the directions to be all the same, neutral, up or down.""" from PyQt5.QtGui import QTextCursor import re reg = re.compile('(.*)(Up|Down|Neutral)') c = lydocument.cursor(cursor) source = ly.document.Source(c, None, ly.document.PARTIAL, True) with c.document as d: for t in source: if isinstance(t, ly.lex.lilypond.Direction): d[t.pos] = dir_operators[direction] elif isinstance(t, ly.lex.lilypond.Command): match = reg.match(t) if match: updated = match.groups()[0] + dir_commands[direction] del d[t.pos:t.end-1] d[t.pos] = updated
def force_directions(cursor, direction): """Force the directions to be all the same, neutral, up or down.""" from PyQt5.QtGui import QTextCursor import re reg = re.compile('(.*)(Up|Down|Neutral)') c = lydocument.cursor(cursor) source = ly.document.Source(c, None, ly.document.PARTIAL, True) with c.document as d: for t in source: if isinstance(t, ly.lex.lilypond.Direction): d[t.pos] = dir_operators[direction] elif isinstance(t, ly.lex.lilypond.Command): match = reg.match(t) if match: updated = match.groups()[0] + dir_commands[direction] del d[t.pos:t.end - 1] d[t.pos] = updated
def hyphenate(self): """Hyphenates selected Lyrics text.""" view = self.mainwindow().currentView() cursor = view.textCursor() found = [] c = lydocument.cursor(cursor, select_all=True) # find text to hyphenate source = lydocument.Source(c) for token in source: if isinstance(token, ly.lex.lilypond.LyricText): # a word found pos = source.position(token) for m in _word_re.finditer(token): found.append((pos + m.start(), pos + 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 pos = start + token.pos for m in _word_re.finditer(token): found.append( (pos + m.start(), pos + m.end(), m.group())) if not found and cursor.hasSelection(): # still not succeeded, then try flat text for m in _word_re.finditer(cursor.selection().toPlainText()): found.append((start + m.start(), start + m.end(), m.group())) if found: import hyphendialog h = hyphendialog.HyphenDialog(self.mainwindow()).hyphenator() if h: with c.document as d: for start, end, word in found: hyph_word = h.inserted(word, ' -- ') if word != hyph_word: d[start:end] = hyph_word
def hyphenate(self): """Hyphenates selected Lyrics text.""" view = self.mainwindow().currentView() cursor = view.textCursor() found = [] c = lydocument.cursor(cursor, select_all=True) # find text to hyphenate source = lydocument.Source(c) for token in source: if isinstance(token, ly.lex.lilypond.LyricText): # a word found pos = source.position(token) for m in _word_re.finditer(token): found.append((pos + m.start(), pos + 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 pos = start + token.pos for m in _word_re.finditer(token): found.append((pos + m.start(), pos + m.end(), m.group())) if not found and cursor.hasSelection(): # still not succeeded, then try flat text for m in _word_re.finditer(cursor.selection().toPlainText()): found.append((start + m.start(), start + m.end(), m.group())) if found: import hyphendialog h = hyphendialog.HyphenDialog(self.mainwindow()).hyphenator() if h: with c.document as d: for start, end, word in found: hyph_word = h.inserted(word, ' -- ') if word != hyph_word: d[start:end] = hyph_word
def spanner_positions(cursor): """Return a list with 0 to 2 QTextCursor instances. At the first cursor a starting spanner item can be inserted, at the second an ending item. """ c = lydocument.cursor(cursor) if cursor.hasSelection(): partial = ly.document.INSIDE else: # just select until the end of the current line c.select_end_of_block() partial = ly.document.OUTSIDE source = lydocument.Source(c, True, partial, True) positions = [source.cursor(p[-1], start=len(p[-1])) for p in ly.rhythm.music_tokens(source)] if cursor.hasSelection(): del positions[1:-1] else: del positions[2:] return positions
def rhythm_copy(cursor): _clipboard[:] = ly.rhythm.rhythm_extract(lydocument.cursor(cursor))
def rhythm_explicit(cursor): ly.rhythm.rhythm_explicit(lydocument.cursor(cursor))
def rhythm_implicit_per_line(cursor): ly.rhythm.rhythm_implicit_per_line(lydocument.cursor(cursor))
def rhythm_remove(cursor): ly.rhythm.rhythm_remove(lydocument.cursor(cursor))
def rhythm_remove_fraction_scaling(cursor): ly.rhythm.rhythm_remove_fraction_scaling(lydocument.cursor(cursor))
def rhythm_undot(cursor): ly.rhythm.rhythm_undot(lydocument.cursor(cursor))
def rhythm_halve(cursor): ly.rhythm.rhythm_halve(lydocument.cursor(cursor))
def decrease_indent(cursor): """Decreases the indent of the line the cursor is at (or the selected lines).""" c = lydocument.cursor(cursor) indenter(cursor.document()).decrease_indent(c)
def rhythm_paste(cursor): ly.rhythm.rhythm_overwrite(lydocument.cursor(cursor), _clipboard)
def rel2abs(cursor, first_pitch_absolute): """Converts pitches from relative to absolute.""" with qutil.busyCursor(): c = lydocument.cursor(cursor, select_all=True) ly.pitch.rel2abs.rel2abs(c, first_pitch_absolute=first_pitch_absolute)
def decorator(cursor, *args): c = lydocument.cursor(cursor) remove = func(c, *args) with c.document as d: for start, end in remove: del d[start:end]
def rhythm_double(cursor): ly.rhythm.rhythm_double(lydocument.cursor(cursor))