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)
def save(self): """Called to perform the edits in the document.""" cursor = QTextCursor(self._range) start = cursor.selectionStart() # use cursordiff; don't destroy point and click positions cursordiff.insert_text(cursor, self.view.toPlainText()) cursor.setPosition(start, QTextCursor.KeepAnchor) with cursortools.compress_undo(cursor, True): # re-indent the inserted line(s) indent.re_indent(cursor)
def change_cursor_info(self, data: QTextCursor): ss = data.selectionStart() se = data.selectionEnd() selected_info = "" if se - ss: selected_info = "{0} {1} ".format( se - ss, "char" if (se - ss) == 1 else "chars") self.showMessage("{0}{1}:{2}".format(selected_info, data.blockNumber(), data.columnNumber()))
class ParsingInput(QWidget): edit_complete = pyqtSignal(Task) def __init__(self): super().__init__(flags=Qt.WindowTitleHint | Qt.WindowCloseButtonHint | Qt.WindowMinimizeButtonHint | Qt.WindowMaximizeButtonHint) self.setFocusPolicy(Qt.StrongFocus) self.setMinimumSize(28, 28) self.setCursor(Qt.IBeamCursor) self.text = [] self.character_offsets = [5] self.parsed_blocks = [] self.qt_text = QTextDocument("") self.cursor = QTextCursor(self.qt_text) self.cursor.setPosition(0) self.cursor_visible = True self.cursor_timer = QTimer() self.cursor_timer.timeout.connect(self.blink_cursor) self.set_cursor_visible() def paintEvent(self, e): qp = QPainter() qp.begin(self) qp.setRenderHint(QPainter.Antialiasing) qp.setRenderHint(QPainter.HighQualityAntialiasing) self.draw(qp) qp.end() def draw(self, qp): size = self.size() w = size.width() h = size.height() font = QFont('Sans-Serif', 12, QFont.Normal) qp.setFont(font) qp.setPen(QColor(128, 128, 128)) qp.setBrush(QColor(255, 255, 255)) qp.drawRoundedRect(0, 0, w, h, 5, 5) qp.setPen(QColor(0, 0, 0)) font_metrics = qp.fontMetrics() c_start_position = 5 cursor_pixel_position = c_start_position self.character_offsets = [cursor_pixel_position] for i, c in enumerate(self.text): start_of_parsed_block = False end_of_parsed_block = False inside_parsed_block = False for start, end in self.parsed_blocks: if start == i: block_width = 4 for char in self.text[start:end]: block_width += font_metrics.width(char["char"]) qp.setPen(QColor(0, 0, 0)) qp.setBrush(QColor(0, 0, 0)) qp.drawRoundedRect(c_start_position + 2, 4, block_width, 20, 2, 2) start_of_parsed_block = True if end == i: end_of_parsed_block = True if start <= i < end: inside_parsed_block = True if end_of_parsed_block: c_start_position += 4 if start_of_parsed_block: c_start_position += 4 c_width = font_metrics.width(c["char"]) if self.cursor.hasSelection() and self.cursor.selectionStart( ) <= i < self.cursor.selectionEnd(): if start_of_parsed_block or end_of_parsed_block: sel_rect_start = c_start_position - 2 sel_rect_width = font_metrics.width(c["char"]) + 4 else: sel_rect_start = c_start_position sel_rect_width = font_metrics.width(c["char"]) if i != self.cursor.selectionStart(): sel_rect_start += 2 if i + 1 != self.cursor.selectionEnd(): sel_rect_width += 2 else: sel_rect_width -= 2 if not inside_parsed_block: selection_color = QColor(100, 100, 255) else: selection_color = QColor(60, 60, 168) qp.setBrush(selection_color) qp.setPen(selection_color) qp.drawRect(sel_rect_start, 6, sel_rect_width, 16) qp.setPen( QColor(inside_parsed_block * 255, inside_parsed_block * 255, inside_parsed_block * 255)) qp.setBrush( QColor(255 - inside_parsed_block * 255, 255 - inside_parsed_block * 255, 255 - inside_parsed_block * 255)) qp.drawText(c_start_position, 20, c["char"]) c_start_position += c_width if i == self.cursor.position() - 1: cursor_pixel_position = c_start_position self.character_offsets.append(c_start_position) if self.hasFocus() and self.cursor_visible: qp.setPen(QColor(0, 0, 0)) for start, end in self.parsed_blocks: if start < self.cursor.position() <= end: qp.setPen(QColor(255, 255, 255)) qp.drawLine(cursor_pixel_position, 4, cursor_pixel_position, 28 - 4) def keyPressEvent(self, event): key = event.key() if key == Qt.Key_Left: if event.modifiers() & Qt.CTRL: new_pos = max( 0, self.get_text_str().rfind( " ", 0, max(0, self.cursor.position() - 1))) else: new_pos = self.cursor.position() - 1 if event.modifiers() & Qt.SHIFT: if self.cursor.hasSelection(): self.cursor.setPosition(max(0, new_pos), mode=QTextCursor.KeepAnchor) else: self.cursor.setPosition(self.cursor.position(), mode=QTextCursor.MoveAnchor) self.cursor.setPosition(max(0, new_pos), mode=QTextCursor.KeepAnchor) else: self.cursor.setPosition(max(0, new_pos)) self.set_cursor_visible() self.update() elif key == Qt.Key_Right: if event.modifiers() & Qt.CTRL: next_space = self.get_text_str().find( " ", self.cursor.position() + 1, len(self.text)) if next_space < 0: new_pos = len(self.text) else: new_pos = next_space else: new_pos = self.cursor.position() + 1 if event.modifiers() & Qt.SHIFT: if self.cursor.hasSelection(): self.cursor.setPosition(max(0, new_pos), mode=QTextCursor.KeepAnchor) else: self.cursor.setPosition(self.cursor.position(), mode=QTextCursor.MoveAnchor) self.cursor.setPosition(max(0, new_pos), mode=QTextCursor.KeepAnchor) else: self.cursor.setPosition(min(len(self.text), new_pos)) self.set_cursor_visible() self.update() elif key == Qt.Key_Home: if event.modifiers() & Qt.SHIFT: if self.cursor.hasSelection(): self.cursor.setPosition(0, mode=QTextCursor.KeepAnchor) else: self.cursor.setPosition(self.cursor.position(), mode=QTextCursor.MoveAnchor) self.cursor.setPosition(0, mode=QTextCursor.KeepAnchor) else: self.cursor.setPosition(0) self.set_cursor_visible() self.update() elif key == Qt.Key_End: if event.modifiers() & Qt.SHIFT: if self.cursor.hasSelection(): self.cursor.setPosition(len(self.text), mode=QTextCursor.KeepAnchor) else: self.cursor.setPosition(self.cursor.position(), mode=QTextCursor.MoveAnchor) self.cursor.setPosition(len(self.text), mode=QTextCursor.KeepAnchor) else: self.cursor.setPosition(len(self.text)) self.set_cursor_visible() self.update() elif key == Qt.Key_Escape: self.cursor.clearSelection() self.set_cursor_visible() self.update() elif key == Qt.Key_Backspace: something_changed = False if self.cursor.hasSelection(): self.delete_selected_text() something_changed = True else: if self.cursor.position() > 0: self.text.pop(self.cursor.position() - 1) self.cursor.setPosition(self.cursor.position() - 1) something_changed = True if something_changed: self.parse_text() self.set_cursor_visible() self.update() elif key == Qt.Key_Delete: something_changed = False if self.cursor.hasSelection(): self.delete_selected_text() something_changed = True else: if self.cursor.position() < len(self.text): self.text.pop(self.cursor.position()) something_changed = True if something_changed: self.parse_text() self.set_cursor_visible() self.update() elif key == Qt.Key_C and event.modifiers() & Qt.CTRL: selected_text = self.get_selected_text() print("Selected Text: {}".format(selected_text)) if len(selected_text) > 0: QApplication.clipboard().setText(selected_text) elif key == Qt.Key_V and event.modifiers() & Qt.CTRL: self.delete_selected_text() clipboard_text = QApplication.clipboard().text() self.text = self.text[:self.cursor.position()] + \ [{"char": c, "parse": True} for c in clipboard_text] + \ self.text[self.cursor.position():] self.parse_text() self.cursor.setPosition(self.cursor.position() + len(clipboard_text)) self.set_cursor_visible() self.update() elif key == Qt.Key_Enter or key == Qt.Key_Return: self.edit_complete.emit(self.get_task()) self.text = [] self.cursor.setPosition(0) self.parse_text() self.set_cursor_visible() self.update() else: if len(event.text()) > 0: typed_text = "" for c in event.text(): if c in ALL_CHARACTERS: typed_text += c if len(typed_text): self.delete_selected_text() self.text = self.text[:self.cursor.position()] + \ [{"char": c, "parse": True} for c in typed_text] + \ self.text[self.cursor.position():] self.parse_text() self.cursor.setPosition(self.cursor.position() + len(typed_text)) self.set_cursor_visible() self.update() def get_selected_text(self): if self.cursor.hasSelection(): return "".join([ c["char"] for c in self.text[self.cursor.selectionStart():self. cursor.selectionEnd()] ]) else: return "" def delete_selected_text(self): if self.cursor.hasSelection(): start_pos = self.cursor.selectionStart() self.text = self.text[:start_pos] + self.text[self.cursor. selectionEnd():] self.cursor.clearSelection() self.cursor.setPosition(start_pos) def get_min_dist_pos(self, click_x): min_dist_pos = 0 min_dist = abs(self.character_offsets[0] - click_x) for i, offset in enumerate(self.character_offsets[1:]): d = abs(offset - click_x) if d < min_dist: min_dist = d min_dist_pos = i + 1 return min_dist_pos def mousePressEvent(self, event): self.cursor.select(QTextCursor.BlockUnderCursor) self.cursor.setPosition(self.get_min_dist_pos(event.pos().x()), mode=QTextCursor.MoveAnchor) self.update() def mouseMoveEvent(self, event): self.cursor.setPosition(self.get_min_dist_pos(event.pos().x()), mode=QTextCursor.KeepAnchor) self.update() def mouseReleaseEvent(self, event): self.cursor.setPosition(self.get_min_dist_pos(event.pos().x()), mode=QTextCursor.KeepAnchor) if self.cursor.selectionStart() == self.cursor.selectionEnd(): self.cursor.clearSelection() self.update() def blink_cursor(self): self.cursor_visible = not self.cursor_visible self.update() def set_cursor_visible(self): self.cursor_visible = True self.cursor_timer.stop() self.cursor_timer.start(500) def get_text_str(self): return "".join([c["char"] for c in self.text]) @staticmethod def generate_date(day, hour=None, minute=None): fuzziness = timedelta(days=1) date = datetime.now() if day in ["Morgen", "Tomorrow"]: date = datetime.now() + timedelta(days=1) try: if hour is not None and 0 <= int(hour) <= 24: if minute is not None and 0 <= int(minute) < 60: date = datetime(year=date.year, month=date.month, day=date.day, hour=int(hour), minute=int(minute)) else: date = datetime(year=date.year, month=date.month, day=date.day, hour=int(hour), minute=0) fuzziness = 0 except ValueError: print("Unexpected Values for hour ({}) and minute ({}). Ignoring.". format(hour, minute)) return date, fuzziness def parse_text(self): regexes = [ (re.compile(r"(Morgen|Tomorrow) (?:at |um )?(\d\d?):(\d\d?)"), self.generate_date), (re.compile(r"(Morgen|Tomorrow)"), self.generate_date) ] text = self.get_text_str() saved_cursor_pos = self.cursor.position() self.qt_text.setPlainText(text) self.cursor.setPosition(saved_cursor_pos) self.parsed_blocks = [] for regex, generator in regexes: matches = regex.finditer(text) for match in matches: if match.end() > 0 and self.text[match.end() - 1]["parse"]: print(match.groups()) print(generator(*match.groups())) text = text[:match.start()] + " " * ( match.end() - match.start()) + text[match.end():] self.parsed_blocks.append((match.start(), match.end())) def get_task(self): new_task = Task() new_task.text = self.get_text_str() return new_task
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)