def postImport(self, settings, doc): """Adaptations of the source after running musicxml2ly Present settings: Reformat source Remove superfluous durations Remove duration scaling Engrave directly """ cursor = QTextCursor(doc) if settings[0]: import reformat reformat.reformat(cursor) if settings[1]: cursor.select(QTextCursor.Document) from rhythm import rhythm rhythm.rhythm_implicit_per_line(cursor) if settings[2]: cursor.select(QTextCursor.Document) from rhythm import rhythm rhythm.rhythm_remove_fraction_scaling(cursor) if settings[3]: import engrave engrave.engraver(self.mainwindow()).engrave("preview", doc, False)
def load(self, keepUndo=False): """Loads the current url. Returns True if loading succeeded, False if an error occurred, and None when the current url is empty or non-local. Currently only local files are supported. If keepUndo is True, the loading can be undone (with Ctrl-Z). """ fileName = self.url().toLocalFile() if fileName: try: with open(fileName) as f: data = f.read() except (IOError, OSError): return False # errors are caught in MainWindow.openUrl() text = util.decode(data) if keepUndo: c = QTextCursor(self) c.select(QTextCursor.Document) c.insertText(text) else: self.setPlainText(text) self.setModified(False) self.loaded() app.documentLoaded(self) return True
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 cursors(self): """Cursors for rectangular selection. 1 cursor for every line """ cursors = [] if self._start is not None: startLine, startVisibleCol = self._start currentLine, currentCol = self._qpart.cursorPosition if abs(startLine - currentLine) > self._MAX_SIZE or \ abs(startVisibleCol - currentCol) > self._MAX_SIZE: # Too big rectangular selection freezes the GUI self._qpart.userWarning.emit('Rectangular selection area is too big') self._start = None return [] currentBlockText = self._qpart.textCursor().block().text() currentVisibleCol = self._realToVisibleColumn(currentBlockText, currentCol) for lineNumber in range(min(startLine, currentLine), max(startLine, currentLine) + 1): block = self._qpart.document().findBlockByNumber(lineNumber) cursor = QTextCursor(block) realStartCol = self._visibleToRealColumn(block.text(), startVisibleCol) realCurrentCol = self._visibleToRealColumn(block.text(), currentVisibleCol) if realStartCol is None: realStartCol = block.length() # out of range value if realCurrentCol is None: realCurrentCol = block.length() # out of range value cursor.setPositionInBlock(min(realStartCol, block.length() - 1)) cursor.setPositionInBlock(min(realCurrentCol, block.length() - 1), QTextCursor.KeepAnchor) cursors.append(cursor) return cursors
def drawContents(self, painter): """ Reimplementation of drawContents to limit the drawing inside `textRext`. """ painter.setPen(self.__color) painter.setFont(self.font()) if self.__textRect: rect = self.__textRect else: rect = self.rect().adjusted(5, 5, -5, -5) if Qt.mightBeRichText(self.__message): doc = QTextDocument() doc.setHtml(self.__message) doc.setTextWidth(rect.width()) cursor = QTextCursor(doc) cursor.select(QTextCursor.Document) fmt = QTextBlockFormat() fmt.setAlignment(self.__alignment) cursor.mergeBlockFormat(fmt) painter.save() painter.translate(rect.topLeft()) doc.drawContents(painter) painter.restore() else: painter.drawText(rect, self.__alignment, self.__message)
def cursorAt(self, position, in_active_area=True): c = QTextCursor(self.block) p = min(position, self.length-1) if in_active_area: p = max(p, self.active_area_start) c.movePosition(QTextCursor.Right, QTextCursor.MoveAnchor, p) return c
def onMatchNumberChange(self, matchNumber): # Set default format on the whole text before highlighting the selected # match. document = self.matchText.document() cursor = QTextCursor(document) cursor.movePosition(QTextCursor.End, QTextCursor.KeepAnchor) cursor.setCharFormat(QTextCharFormat()) search = self.getSearchText() for i, match in enumerate(self.regex.finditer(search)): if i + 1 == matchNumber: break else: assert False, ("We didn't find a match?! (RE=%r, text=%r" % (self.regex.pattern, search)) self.formatMatchedText(document, match) model = self.groupsView.model() model.clear() # Create a reversed self.regex.groupindex dictionnary groupsIndexes = dict((v, k) for (k, v) in self.regex.groupindex.iteritems()) for i in range(1, self.regex.groups + 1): groupName = groupsIndexes.get(i, "") groupValue = match.group(i) model.append((groupName, groupValue))
def _find(self, textEdit=None, backward=False): if textEdit is None: textEdit = self.textEdit found = False if textEdit is not None: cursor = textEdit.textCursor() text, flags = self._textAndFindFlags(backward=backward) position = cursor.position() cursor = textEdit.document().find(text, cursor, flags) if not cursor.isNull(): textEdit.setTextCursor(cursor) found = True elif self.wrapAroundCheckBox.isChecked(): cursor = QTextCursor(textEdit.textCursor()) cursor.movePosition(backward and QTextCursor.End or QTextCursor.Start) cursor = textEdit.document().find(text, cursor, flags) if not cursor.isNull(): if position == cursor.position(): pass #todo textEdit.setTextCursor(cursor) found = True self.writeSettings() if not found: QApplication.beep() self.feedbackLabel.setText(self.tr("Not found")) else: self.clearFeedback()
def load(self, url=None, encoding=None, keepUndo=False): """Load the specified or current url (if None was specified). Currently only local files are supported. An IOError is raised when trying to load a nonlocal URL. If loading succeeds and an url was specified, the url is make the current url (by calling setUrl() internally). If keepUndo is True, the loading can be undone (with Ctrl-Z). """ if url is None: url = QUrl() u = url if not url.isEmpty() else self.url() text = self.load_data(u, encoding or self._encoding) if keepUndo: c = QTextCursor(self) c.select(QTextCursor.Document) c.insertText(text) else: self.setPlainText(text) self.setModified(False) if not url.isEmpty(): self.setUrl(url) self.loaded() app.documentLoaded(self)
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 highlightMarkdown(self, text, strt): cursor = QTextCursor(self.document()) bf = cursor.blockFormat() self.setFormat(0, len(text), QColor(self.theme['color'])) #bf.clearBackground() #cursor.movePosition(QTextCursor.End) #cursor.setBlockFormat(bf) #Block quotes can contain all elements so process it first self.highlightBlockQuote(text, cursor, bf, strt) #If empty line no need to check for below elements just return if self.highlightEmptyLine(text, cursor, bf, strt): return #If horizontal line, look at pevious line to see if its a header, process and return if self.highlightHorizontalLine(text, cursor, bf, strt): return if self.highlightAtxHeader(text, cursor, bf, strt): return self.highlightList(text, cursor, bf, strt) self.highlightLink(text, cursor, bf, strt) self.highlightImage(text, cursor, bf, strt) self.highlightCodeSpan(text, cursor, bf, strt) self.highlightEmphasis(text, cursor, bf, strt) self.highlightBold(text, cursor, bf, strt) self.highlightCodeBlock(text, cursor, bf, strt)
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 goto_target(mainwindow, target): """Switch to the document and location where the node target is.""" filename = target.document.filename doc = app.openUrl(QUrl.fromLocalFile(filename)) cursor = QTextCursor(doc) cursor.setPosition(target.position) browseriface.get(mainwindow).setTextCursor(cursor) mainwindow.currentView().centerCursor()
def _makeQtExtraSelection(startAbsolutePosition, length): selection = QTextEdit.ExtraSelection() cursor = QTextCursor(self.document()) cursor.setPosition(startAbsolutePosition) cursor.setPosition(startAbsolutePosition + length, QTextCursor.KeepAnchor) selection.cursor = cursor selection.format = self._userExtraSelectionFormat return selection
def main(): """Main function.""" options, files = parse_commandline() urls = list(map(url, files)) if not app.qApp.isSessionRestored(): if not options.new and remote.enabled(): api = remote.get() if api: api.command_line(options, urls) api.close() sys.exit(0) if QSettings().value("splash_screen", True) not in ("false", False): import splashscreen splashscreen.show() QTimer.singleShot(0, remote.setup) # Start listening for IPC import mainwindow # contains MainWindow class import session # Initialize QSessionManager support import sessions # Initialize our own named session support import document # contains Document class # boot Frescobaldi-specific stuff that should be running on startup import viewhighlighter # highlight arbitrary ranges in text import matcher # matches braces etc in active text window import progress # creates progress bar in view space import autocomplete # auto-complete input if app.qApp.isSessionRestored(): # Restore session, we are started by the session manager session.restoreSession() return # load specified session doc = None if options.session and options.session != "none": doc = sessions.loadSession(options.session) # Just create one MainWindow win = mainwindow.MainWindow() win.show() if urls: for u in urls: doc = win.openUrl(u, options.encoding) elif not options.session: # no docs, load default session doc = sessions.loadDefaultSession() win.setCurrentDocument(doc or document.Document()) if urls and options.line is not None: # set the last loaded document active and apply navigation if requested pos = doc.findBlockByNumber(options.line - 1).position() + options.column cursor = QTextCursor(doc) cursor.setPosition(pos) win.currentView().setTextCursor(cursor) win.currentView().centerCursor()
def isBlankBefore(cursor): """Returns True if there's no text on the current line before the cursor.""" if cursor.hasSelection(): return False if cursor.atBlockStart(): return True c = QTextCursor(cursor) c.movePosition(QTextCursor.StartOfBlock, QTextCursor.KeepAnchor) return c.selection().toPlainText().isspace()
def _selectLines(self, startBlockNumber, endBlockNumber): """Select whole lines """ startBlock = self.document().findBlockByNumber(startBlockNumber) endBlock = self.document().findBlockByNumber(endBlockNumber) cursor = QTextCursor(startBlock) cursor.setPosition(endBlock.position(), QTextCursor.KeepAnchor) cursor.movePosition(QTextCursor.EndOfBlock, QTextCursor.KeepAnchor) self.setTextCursor(cursor)
def isBlankAfter(cursor): """Returns True if there's no text on the current line after the cursor.""" if cursor.hasSelection(): return False if cursor.atBlockEnd(): return True c = QTextCursor(cursor) c.movePosition(QTextCursor.EndOfBlock, QTextCursor.KeepAnchor) return c.selection().toPlainText().isspace()
def show_msg(self, message): """Show message in textBrowser """ self.textEdit.append(message) # Scroll to end of the last message cursor = QTextCursor(self.textEdit.textCursor()) cursor.movePosition(QTextCursor.End) self.textEdit.setTextCursor(cursor) QApplication.processEvents()
def select_range(self, s, e): cursor = QTextCursor(self) spos = self.findBlockByLineNumber(s[0] - 1).position() + s[1] epos = self.findBlockByLineNumber(e[0] - 1).position() + e[1] cursor.setPosition(spos, QTextCursor.MoveAnchor) cursor.setPosition(epos, QTextCursor.KeepAnchor) return cursor
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)
def startmain(): import optparse optparse._ = _ # let optparse use our translations parser = optparse.OptionParser( usage = _("{appname} [options] file ...").format(appname=info.name), version = "{0} {1}".format(info.appname, info.version), description = _("A LilyPond Music Editor")) parser.add_option('-e', '--encoding', metavar=_("ENC"), help=_("Encoding to use")) parser.add_option('-l', '--line', type="int", metavar=_("NUM"), help=_("Line number to go to, starting at 1")) parser.add_option('-c', '--column', type="int", metavar=_("NUM"), help=_("Column to go to, starting at 0"), default=0) parser.add_option('--start', metavar=_("NAME"), help=_("Session to start ('{none}' for empty session)").format(none="none"), dest="session") args = QApplication.arguments() if os.name == 'nt' and args and 'python' in os.path.basename(args[0]).lower(): args = args[2:] else: args = args[1:] options, files = parser.parse_args(args) # load specified session doc = None if options.session and options.session != "none": doc = sessions.loadSession(options.session) # Just create one MainWindow win = mainwindow.MainWindow() win.show() if files: # make urls for arg in files: if re.match(r'^(https?|s?ftp)://', arg): url = QUrl(arg) elif arg.startswith('file://'): url = QUrl.fromLocalFile(arg[7:]) elif arg.startswith('file:'): url = QUrl.fromLocalFile(os.path.abspath(arg[5:])) else: url = QUrl.fromLocalFile(os.path.abspath(arg)) doc = win.openUrl(url, options.encoding) elif not options.session: # no docs, load default session doc = sessions.loadDefaultSession() win.setCurrentDocument(doc or document.Document()) if files and options.line is not None: # set the last loaded document active and apply navigation if requested pos = doc.findBlockByNumber(options.line - 1).position() + options.column cursor = QTextCursor(doc) cursor.setPosition(pos) win.currentView().setTextCursor(cursor) win.currentView().centerCursor()
def is_cell_separator(self, cursor=None, block=None): """Return True if cursor (or text block) is on a block separator""" assert cursor is not None or block is not None if cursor is not None: cursor0 = QTextCursor(cursor) cursor0.select(QTextCursor.BlockUnderCursor) text = to_text_string(cursor0.selectedText()) else: text = to_text_string(block.text()) return text.lstrip().startswith(self.CELL_SEPARATORS)
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 setLine(self, line): cursor = QTextCursor(self.document()) cursor.movePosition(QTextCursor.End) cursor.setPosition(self.newPromptPos, QTextCursor.KeepAnchor) cursor.removeSelectedText() cursor.insertText(line) self.setTextCursor(cursor)
def focus_line(self, line_no): """ highlight the line""" line_no -= 1 cursor = QTextCursor(self.document().findBlockByLineNumber(line_no)) cursor.clearSelection() highlight = QTextEdit.ExtraSelection() highlight.cursor = cursor highlight.format.setProperty(QTextFormat.FullWidthSelection, True) highlight.format.setBackground(QBrush(QColor("#657b83"))) self.setExtraSelections([highlight]) self.setTextCursor(cursor)
def insert_python(text, cursor, name, view): """Regards the text as Python code, and exec it. name and view are given in case an exception occurs. The following variables are available: - text: contains selection or '', set it to insert new text - state: contains simplestate for the cursor position - cursor: the QTextCursor After the insert, the cursor points to the end of the inserted snippet. """ namespace = { 'cursor': QTextCursor(cursor), 'state': state(cursor), 'text': cursor.selection().toPlainText(), 'view': view, 'ANCHOR': 1, 'CURSOR': 2, } try: code = compile(text, "<snippet>", "exec") if sys.version_info < (3, 0): exec("exec code in namespace") else: exec(code, namespace) if 'main' in namespace: return namespace['main']() except Exception: handle_exception(name, view) else: text = namespace.get('text', '') if isinstance(text, (tuple, list)): ANCHOR = namespace.get('ANCHOR', 1) CURSOR = namespace.get('CURSOR', 2) a, c = -1, -1 for t in text: if t == ANCHOR: a = cursor.selectionStart() elif t == CURSOR: c = cursor.selectionStart() else: cursor.insertText(t) if (a, c) != (-1, -1): new = QTextCursor(cursor) if a != -1: new.setPosition(a) if c != -1: new.setPosition(c, QTextCursor.KeepAnchor if a != -1 else QTextCursor.MoveAnchor) return new else: cursor.insertText(namespace['text'])
def cursorForItem(self, item): """Returns a cursor for the specified item. This method (as all others) assume that the item refers to the current Document. """ doc = self.parent().mainwindow().currentDocument() cursor = QTextCursor(doc) cursor.setPosition(item.position) return cursor
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 on_log_filter(self): log_lvl_name = str(self.sender().iconText()).upper() self.log_lvl = log_levels[log_lvl_name] cursor = QTextCursor(self.document()) current_block = cursor.block() while current_block.isValid() and current_block.userData(): block_log_lvl = current_block.userData().log_lvl if block_log_lvl <= self.log_lvl: current_block.setVisible(True) else: current_block.setVisible(False) current_block = current_block.next() self.viewport().update()
def on_log_received(self, data): time_info = datetime.fromtimestamp((data['time']/1000)).isoformat() log_message = '%s: %s : %s' % ( time_info, data['level'], data['message']) message_document = self.document() cursor_to_add = QTextCursor(message_document) cursor_to_add.movePosition(cursor_to_add.End) cursor_to_add.insertText(log_message + '\n') if data['level'] in COLORS: fmt = QTextCharFormat() fmt.setForeground(COLORS[data['level']]) cursor_to_add.movePosition(cursor_to_add.PreviousBlock) cursor_to_add_fmt = message_document.find(data['level'], cursor_to_add.position()) cursor_to_add_fmt.mergeCharFormat(fmt) self.ensureCursorVisible()
def insert_python(text, cursor, name, view): """Regards the text as Python code, and exec it. name and view are given in case an exception occurs. The following variables are available: - text: contains selection or '', set it to insert new text - state: contains simplestate for the cursor position - cursor: the QTextCursor After the insert, the cursor points to the end of the inserted snippet. """ namespace = { 'cursor': QTextCursor(cursor), 'state': state(cursor), 'text': cursor.selection().toPlainText(), 'view': view, 'ANCHOR': 1, 'CURSOR': 2, } try: code = compile(text, "<snippet>", "exec") if sys.version_info < (3, 0): exec("exec code in namespace") else: exec(code, namespace) if 'main' in namespace: return namespace['main']() except Exception: handle_exception(name, view) else: text = namespace.get('text', '') if isinstance(text, (tuple, list)): ANCHOR = namespace.get('ANCHOR', 1) CURSOR = namespace.get('CURSOR', 2) a, c = -1, -1 for t in text: if t == ANCHOR: a = cursor.selectionStart() elif t == CURSOR: c = cursor.selectionStart() else: cursor.insertText(t) if (a, c) != (-1, -1): new = QTextCursor(cursor) if a != -1: new.setPosition(a) if c != -1: new.setPosition( c, QTextCursor.KeepAnchor if a != -1 else QTextCursor.MoveAnchor) return new else: cursor.insertText(namespace['text'])
def toggleMark(self, linenum, type): """Toggles the mark of the given type on the given line.""" nums = [mark.blockNumber() for mark in self._marks[type]] index = bisect.bisect_left(nums, linenum) if linenum in nums: # remove double occurrences while True: del self._marks[type][index] del nums[index] if linenum not in nums: break index = bisect.bisect_left(nums, linenum) else: mark = QTextCursor(self.document().findBlockByNumber(linenum)) try: # only available in very recent PyQt4 versions mark.setKeepPositionOnInsert(True) except AttributeError: pass self._marks[type].insert(index, mark) self.marksChanged()
def goto(self, line_no): cursor = self.textCursor() block = cursor.block() row = cursor.blockNumber() while row > line_no: block = block.previous() row -= 1 while row < line_no: block = block.next() row += 1 cursor = QTextCursor(block) self.setTextCursor(cursor)
def onMatchNumberChange(self, matchNumber): # Set default format on the whole text before highlighting the selected # match. document = self.matchText.document() cursor = QTextCursor(document) cursor.movePosition(QTextCursor.End, QTextCursor.KeepAnchor) cursor.setCharFormat(QTextCharFormat()) search = self.getSearchText() for i, match in enumerate(self.regex.finditer(search)): if i + 1 == matchNumber: break else: assert False, ("We didn't find a match?! (RE=%r, text=%r" % (self.regex.pattern, search)) self.formatMatchedText(document, match) model = self.groupsView.model() model.clear() # Create a reversed self.regex.groupindex dictionnary groupsIndexes = dict( (v, k) for (k, v) in self.regex.groupindex.iteritems()) for i in range(1, self.regex.groups + 1): groupName = groupsIndexes.get(i, "") groupValue = match.group(i) model.append((groupName, groupValue))
def postImport(self, settings, doc): """Adaptations of the source after running musicxml2ly Present settings: Reformat source Remove superfluous durations Remove duration scaling Engrave directly """ cursor = QTextCursor(doc) if settings[0]: import reformat reformat.reformat(cursor) if settings[1]: cursor.select(QTextCursor.Document) from rhythm import rhythm rhythm.rhythm_implicit_per_line(cursor) if settings[2]: cursor.select(QTextCursor.Document) from rhythm import rhythm rhythm.rhythm_remove_fraction_scaling(cursor) if settings[3]: import engrave engrave.engraver(self.mainwindow()).engrave('preview', doc, False)
def _makeBraceExtraSelection(self, block, pos, matched): """Make QTextEdit.ExtraSelection for highlighted brace """ sel = QTextEdit.ExtraSelection() sel.cursor = QTextCursor(block) sel.cursor.setPosition(block.position() + pos, QTextCursor.MoveAnchor) sel.cursor.setPosition(block.position() + pos + 1, QTextCursor.KeepAnchor) if matched: sel.format = DEFAULT_STYLE['matchedBrace'] else: sel.format = DEFAULT_STYLE['unMatchedBrace'] return sel
def insertMarkers(self): # Copy the text and if it is empty, complain and exit. qi = QString(self.insertText.text()) if qi.isEmpty(): pqMsgs.warningMsg("No insert text specified") return # See how many pages are involved: all the ones that aren't marked skip n = 0 for i in range(IMC.pageTable.size()): if IMC.pageTable.getAction(i) != IMC.FolioRuleSkip: n += 1 if n == 0: # page table empty or all rows marked skip pqMsgs.warningMsg("No pages to give folios to") return m = "Insert this string at the top of {0} pages?".format(n) b = pqMsgs.okCancelMsg(QString(m), pqMsgs.trunc(qi, 35)) if b: # Convert any '\n' in the text to the QT line delimiter char # we do this in the copy so the lineEdit text doesn't change qi.replace(QString(u'\\n'), QString(IMC.QtLineDelim)) # get a cursor on the edit document tc = QTextCursor(IMC.editWidget.textCursor()) tc.beginEditBlock() # start single undoable operation # Working from the end of the document backward, go to the # top of each page and insert the string for i in reversed(range(IMC.pageTable.size())): if IMC.pageTable.getAction(i) != IMC.FolioRuleSkip: # Note the page's start position and set our work cursor to it pos = IMC.pageTable.getCursor(i).position() tc.setPosition(pos) # Make a copy of the insert string replacing %f with this folio f = IMC.pageTable.getDisplay(i) qf = QString(qi) qf.replace(QString(u'%f'), f, Qt.CaseInsensitive) tc.insertText(qf) # The insertion goes in ahead of the saved cursor position so now # it points after the inserted string. Put it back where it was. IMC.pageTable.setPosition(i, pos) tc.endEditBlock() # wrap up the undo op
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_selection(cursor) pos = cursor.selectionStart() with cursortools.compress_undo(cursor): # insert the snippet, might return a new cursor if 'python' in variables: new = insert_python(text, cursor, name, view) elif 'macro' in variables: new = insert_macro(text, view) else: new = insert_snippet(text, cursor, variables) # QTextBlocks the snippet starts and ends block = cursor.document().findBlock(pos) last = cursor.block() # re-indent if not explicitly suppressed by a 'indent: no' variable if last != block and 'no' not in variables.get('indent', ''): c = QTextCursor(last) c.setPosition(block.position(), QTextCursor.KeepAnchor) with cursortools.compress_undo(c, True): indent.re_indent(c, True) if not new and 'keep' in selection: end = cursor.position() cursor.setPosition(pos) cursor.setPosition(end, QTextCursor.KeepAnchor) view.setTextCursor(new or cursor)
def previousMark(self, cursor, type=None): """Finds the first mark before the cursor (of the type if specified).""" if type is None: marks = [] for type in types: marks += self._marks[type] # sort the marks on line number marks.sort(key=lambda mark: mark.blockNumber()) else: marks = self._marks[type] nums = [mark.blockNumber() for mark in marks] index = bisect.bisect_left(nums, cursor.blockNumber()) if index > 0: return QTextCursor(marks[index - 1].block())
def load(self): """Loads the marks from the metainfo.""" self._marks = dict((type, []) for type in types) marks = metainfo.info(self.document()).bookmarks try: d = json.loads(marks) or {} except ValueError: return # No JSON object could be decoded for type in types: self._marks[type] = [ QTextCursor(self.document().findBlockByNumber(num)) for num in d.get(type, []) ] self.marksChanged()
def cursorPosition(self, pos): line, col = pos line = min(line, len(self.lines) - 1) lineText = self.lines[line] if col is not None: col = min(col, len(lineText)) else: col = len(lineText) - len(lineText.lstrip()) cursor = QTextCursor(self.document().findBlockByNumber(line)) setPositionInBlock(cursor, col) self.setTextCursor(cursor)
def metaStringIn(self, qline): parts = unicode(qline).split(' ') tc = QTextCursor(IMC.editWidget.document()) tc.setPosition(int(parts[0])) # see comments on proofer string in loadPsep above. qs_proofers = QString(parts[2]) qs_proofers = qs_proofers.remove(0, 1) qs_proofers = qs_proofers.replace(QChar(u'\\'), QChar(u',')) proofers = unicode(qs_proofers).split(',') row = [tc, QString(parts[1]), proofers, 0, 0, 0, 0] index = self.size() # index of row to be row[self._Foact] = int(parts[3]) fcode = int(parts[4]) if fcode != self._last_format: # either row 0 and self._last_format is None, or a change of format self._last_format = fcode # put index of row we are about to add, into set of explicit formats self._explict_formats.add(self.size()) else: fcode = IMC.FolioFormatSame row[self._Fofor] = fcode self._TheDB.append(row) self.setValue(index, int(parts[5])) # also updates display
def nextMark(self, cursor, type=None): """Finds the first mark after the cursor (of the type if specified).""" if type is None: marks = [] for type in types: marks += self._marks[type] # sort the marks on line number marks.sort(key=lambda mark: mark.blockNumber()) else: marks = self._marks[type] nums = [mark.blockNumber() for mark in marks] index = bisect.bisect_right(nums, cursor.blockNumber()) if index < len(nums): return QTextCursor(marks[index].block())
def saveDocument(self, doc, save_as=False): """ Saves the document, asking for a name if necessary. If save_as is True, a name is always asked. Returns True if saving succeeded. """ if save_as or doc.url().isEmpty(): filename = doc.url().toLocalFile() if filename: filetypes = app.filetypes(os.path.splitext(filename)[1]) else: directory = app.basedir() # default directory to save to import documentinfo import ly.lex filename = os.path.join(directory, documentinfo.defaultfilename(doc)) filetypes = app.filetypes( ly.lex.extensions[documentinfo.mode(doc)]) caption = app.caption(_("dialog title", "Save File")) filename = QFileDialog.getSaveFileName(self, caption, filename, filetypes) if not filename: return False # cancelled url = QUrl.fromLocalFile(filename) else: url = doc.url() if QSettings().value("strip_trailing_whitespace", False, bool): import reformat reformat.remove_trailing_whitespace(QTextCursor(doc)) # we only support local files for now filename = url.toLocalFile() b = backup.backup(filename) try: doc.save(url) except IOError as e: msg = _("{message}\n\n{strerror} ({errno})").format( message=_("Could not write to: {url}").format(url=filename), strerror=e.strerror, errno=e.errno) QMessageBox.critical(self, app.caption(_("Error")), msg) return False else: if b: backup.removeBackup(filename) recentfiles.add(doc.url()) return True
def __init__(self, doc, links): """Creates QTextCursor instances for every link, keeps a reference to the document.""" self.document = doc # make a sorted list of cursors with their [destination, ...] destinations list self._cursor_dict = d = {} # mapping from (line, col) to QTextCursor self._cursors = cursors = [] # sorted list of the cursors self._destinations = destinations = [] # corresponding list of destinations for pos, dest in sorted(links.items()): line, column = pos b = doc.findBlockByNumber(line - 1) if b.isValid(): c = d[pos] = QTextCursor(doc) c.setPosition(b.position() + column) cursors.append(c) destinations.append(dest)
def bind(self, document): """Called when a document is loaded this Reference points to. Creates a QTextCursor so the position is maintained even if the document changes. """ b = document.findBlockByNumber(max(0, self._line - 1)) if b.isValid(): self._cursor = c = QTextCursor(document) c.setPosition(b.position() + self._column) document.closed.connect(self.unbind) if self._line > 0: bookmarks.bookmarks(document).setMark(self._line - 1, "error") else: self._cursor = None
def notify(self, text, textHighlight=None): # highlightRange is range in passed text. length = len(self.notifyBox.toPlainText()) self.notifyBox.append(text) self.notifyBox.verticalScrollBar().setValue( self.notifyBox.verticalScrollBar().maximum()) if textHighlight != None: begin = length + text.find(textHighlight) + 1 end = begin + len(textHighlight) fmt = QTextCharFormat() col = QColor(Qt.red) col.setAlpha(130) fmt.setBackground(col) cursor = QTextCursor(self.notifyBox.document()) cursor.setPosition(begin) cursor.setPosition(end, QTextCursor.KeepAnchor) cursor.setCharFormat(fmt)
def _makeMatchSelection(self, block, columnIndex, matched): """Make matched or unmatched QTextEdit.ExtraSelection """ selection = QTextEdit.ExtraSelection() if matched: bgColor = Qt.green else: bgColor = Qt.red selection.format.setBackground(bgColor) selection.cursor = QTextCursor(block) selection.cursor.setPosition(block.position() + columnIndex) selection.cursor.movePosition(QTextCursor.Right, QTextCursor.KeepAnchor) return selection
def selectedPosition(self, pos): anchorPos, cursorPos = pos anchorLine, anchorCol = anchorPos cursorLine, cursorCol = cursorPos anchorCursor = QTextCursor( self.document().findBlockByNumber(anchorLine)) setPositionInBlock(anchorCursor, anchorCol) # just get absolute position cursor = QTextCursor(self.document().findBlockByNumber(cursorLine)) setPositionInBlock(cursor, cursorCol) anchorCursor.setPosition(cursor.position(), QTextCursor.KeepAnchor) self.setTextCursor(anchorCursor)
def updateDescAndQL(self): # get the name of the selected dataset dataset_name, dataset_serviceType = self.getSelectedNameAndType() #custom web service object dataset = self.selectdataSets(dataset_name, dataset_serviceType) quicklook = os.path.join(self.quicklooks_dir, dataset.QLname + ".jpg") desc = dataset.getDescription(self.language) name = dataset.getName(self.language) #update decription self.dlg.textEdit.clear() #creation and last update if self.language == "EN": crDate = "Creation date : " + dataset.creationDate update = "Last update : " + dataset.lastUpdate elif self.language == "GR": crDate = unicode( "Ημερομηνια δημιουργιας : " + dataset.creationDate, 'utf-8') update = unicode("Τελευταία ενημέρωση : " + dataset.lastUpdate, 'utf-8') cursor = QTextCursor(self.dlg.textEdit.document()) cursor.insertHtml("<h3> " + name + " <br><br></h3>") cursor.insertHtml("<p> " + desc + " <br><br><br></p>") cursor.insertHtml("<p><i> " + crDate + " <br></i></p>") #cursor.insertHtml("<p><i> "+update+" <br></i></p>") self.dlg.textEdit.setReadOnly(True) #update quicklook #GET DIMENSIONS OF THE IMAGE img = Image.open(quicklook) w, h = img.size scene = QGraphicsScene() pic = QPixmap(quicklook) scene.addItem(QGraphicsPixmapItem(pic)) self.dlg.graphicsView.setScene(scene) self.dlg.graphicsView.fitInView(QRectF(0, 0, w, h), Qt.KeepAspectRatio) self.dlg.graphicsView.show()
def cursors(self): """Cursors for rectangular selection. 1 cursor for every line """ cursors = [] if self._start is not None: startLine, startVisibleCol = self._start currentLine, currentCol = self._qpart.cursorPosition if abs(startLine - currentLine) > self._MAX_SIZE or \ abs(startVisibleCol - currentCol) > self._MAX_SIZE: # Too big rectangular selection freezes the GUI self._qpart.userWarning.emit( 'Rectangular selection area is too big') self._start = None return [] currentBlockText = self._qpart.textCursor().block().text() currentVisibleCol = self._realToVisibleColumn( currentBlockText, currentCol) for lineNumber in range(min(startLine, currentLine), max(startLine, currentLine) + 1): block = self._qpart.document().findBlockByNumber(lineNumber) cursor = QTextCursor(block) realStartCol = self._visibleToRealColumn( block.text(), startVisibleCol) realCurrentCol = self._visibleToRealColumn( block.text(), currentVisibleCol) if realStartCol is None: realStartCol = block.length() # out of range value if realCurrentCol is None: realCurrentCol = block.length() # out of range value cursor.setPosition(cursor.block().position() + min(realStartCol, block.length() - 1)) cursor.setPosition( cursor.block().position() + min(realCurrentCol, block.length() - 1), QTextCursor.KeepAnchor) cursors.append(cursor) return cursors
def centerCursor(self): tc = QTextCursor( self.textCursor()) # copy the working cursor with its selection top_point = tc.position() # one end of selection, in character units bot_point = tc.anchor() # ..and the other end if top_point > bot_point: # often the position is > the anchor (top_point, bot_point) = (bot_point, top_point) tc.setPosition(top_point) # cursor for the top of the selection selection_top = self.cursorRect(tc).top() # ..get its top pixel line_height = self.cursorRect( tc).height() # and save height of one line tc.setPosition(bot_point) # cursor for the end of the selection selection_bot = self.cursorRect( tc).bottom() # ..selection's bottom pixel selection_height = selection_bot - selection_top + 1 # selection height in pixels view_height = self.viewport().geometry().height( ) # scrolled area's height in px view_half = view_height >> 1 # int(view_height/2) pixel_adjustment = 0 if selection_height < view_half: # selected text is less than half the window height: center the top of the # selection, i.e., make the cursor_top equal to view_half. pixel_adjustment = selection_top - view_half # may be negative else: # selected text is taller than half the window, can we show it all? if selection_height < (view_height - line_height): # all selected text fits in the viewport (with a little free): center it. pixel_adjustment = (selection_top + (selection_height / 2)) - view_half else: # not all selected text fits the window, put text top near window top pixel_adjustment = selection_top - line_height # OK, convert the pixel adjustment to a line-adjustment based on the assumption # that a scrollbar pageStep is the height of the viewport in lines. adjust_fraction = pixel_adjustment / view_height vscroller = self.verticalScrollBar() page_step = vscroller.pageStep( ) # lines in a viewport page, actually less 1 adjust_lines = int(page_step * adjust_fraction) target = vscroller.value() + adjust_lines if (target >= 0) and (target <= vscroller.maximum()): vscroller.setValue(target)
def handle_exception(name, view): """Called when a snippet raises a Python exception. Shows the error message and offers the option to edit the offending snippet. """ import sys, traceback exc_type, exc_value, exc_traceback = sys.exc_info() tb = traceback.extract_tb(exc_traceback) while tb and tb[0][0] != "<snippet>": del tb[0] msg = ''.join( traceback.format_list(tb) + traceback.format_exception_only(exc_type, exc_value)) dlg = QMessageBox(QMessageBox.Critical, _("Snippet error"), msg, QMessageBox.Ok | QMessageBox.Cancel) dlg.button(QMessageBox.Ok).setText(_("Edit Snippet")) dlg.setDefaultButton(QMessageBox.Cancel) dlg.setEscapeButton(QMessageBox.Cancel) if dlg.exec_() != QMessageBox.Ok: return # determine line number if exc_type is SyntaxError: lineno = exc_value.lineno elif tb: lineno = tb[0][1] else: lineno = None import panelmanager from . import edit widget = panelmanager.manager(view.window()).snippettool.widget() textedit = edit.Edit(widget, name).text if lineno is not None: # convert to line number in full snippet text for block in cursortools.all_blocks(textedit.document()): if block.text().startswith('-*- '): lineno += 1 else: break block = textedit.document().findBlockByNumber(lineno - 1) if block.isValid(): textedit.setTextCursor(QTextCursor(block))