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.from_cursor(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.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 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.from_cursor(cursor, True, -1) with cursortools.compress_undo(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.strip_indent(c) import indent indent.insert_text(c, name + '\n') # just pick the first place return
def duration_cursor_items(cursor): """Yields two-tuples (cursor, list of duration tokens). The list of duration tokens may be empty. This can be used to find the places to insert or overwrite durations in the selected music. """ source = tokeniter.Source.selection(cursor, True) for m in music.music_items(source): i = iter(m) c = QTextCursor(source.block) for t in i: if isinstance(t, ly.lex.lilypond.Duration): l = [t] c.setPosition(source.block.position() + t.pos) for t in i: if isinstance(t, ly.lex.lilypond.Duration): l.append(t) elif not isinstance(t, ly.lex.Space): break c.setPosition(source.block.position() + l[-1].end, c.KeepAnchor) break else: c.setPosition(source.block.position() + t.end) l = [] yield c, l
def actionTriggered(self, name): cursor = self.mainwindow().textCursor() style = _glissandoStyles[name] source = tokeniter.Source.from_cursor(cursor, True, -1) for p in music.music_items(source, tokens=source.tokens): 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 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: source = tokeniter.Source.from_cursor(cursor, True, -1) music_list = list(music.music_items(source, tokens=source.tokens)) 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 spanner_positions(cursor): if cursor.hasSelection(): source = tokeniter.Source.selection(cursor, True) tokens = None else: source = tokeniter.Source.fromCursor(cursor, True, -1) tokens = source.tokens # only current line positions = [source.cursor(p[-1], start=len(p[-1])) for p in music.music_items(source, tokens=tokens)] if cursor.hasSelection(): del positions[1:-1] else: del positions[2:] return positions
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. """ if cursor.hasSelection(): source = tokeniter.Source.selection(cursor, True) tokens = None else: source = tokeniter.Source.from_cursor(cursor, True, -1) tokens = source.tokens # only current line positions = [source.cursor(p[-1], start=len(p[-1])) for p in music.music_items(source, tokens=tokens)] if cursor.hasSelection(): del positions[1:-1] else: del positions[2:] 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. """ if cursor.hasSelection(): source = tokeniter.Source.selection(cursor, True) tokens = None rests = False else: source = tokeniter.Source.fromCursor(cursor, True, -1) tokens = source.tokens # only current line rests = True positions = [] for p in music.music_items(source, tokens=tokens): 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 duration_items(cursor, *classes): """Yields block, list where tokens in list are instance of *classes.""" source = tokeniter.Source.selection(cursor, True) for m in music.music_items(source): yield source.block, [token for token in m if isinstance(token, classes)]