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 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 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 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 show(cursor, pos=None, num_lines=6): """Displays a tooltip showing part of the cursor's Document. If the cursor has a selection, those blocks are displayed. Otherwise, num_lines lines are displayed. If pos is not given, the global mouse position is used. """ block = cursor.document().findBlock(cursor.selectionStart()) c2 = QTextCursor(block) if cursor.hasSelection(): c2.setPosition(cursor.selectionEnd(), QTextCursor.KeepAnchor) c2.movePosition(QTextCursor.EndOfBlock, QTextCursor.KeepAnchor) else: c2.movePosition(QTextCursor.NextBlock, QTextCursor.KeepAnchor, num_lines) data = textformats.formatData('editor') doc = QTextDocument() font = QFont(data.font) font.setPointSizeF(font.pointSizeF() * .8) doc.setDefaultFont(font) doc.setPlainText(c2.selection().toPlainText()) if metainfo.info(cursor.document()).highlighting: highlighter.highlight(doc, state=tokeniter.state(block)) size = doc.size().toSize() + QSize(8, -4) pix = QPixmap(size) pix.fill(data.baseColors['background']) doc.drawContents(QPainter(pix)) label = QLabel() label.setPixmap(pix) label.setStyleSheet("QLabel { border: 1px solid #777; }") label.resize(size) widgets.customtooltip.show(label, pos)
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 cursor(self): """Return a QTextCursor with the same selection.""" c = QTextCursor(self.document.document) c.movePosition(QTextCursor.End) if self.end is None else c.setPosition( self.end) c.setPosition(self.start, QTextCursor.KeepAnchor) return c
def get_tooltip(self, pos): cursor_pos = self.document().documentLayout().hitTest(QtCore.QPointF(pos), Qt.Qt.ExactHit) if cursor_pos == -1: return "", QRect() cursor = QTextCursor(self.document()) cursor.setPosition(cursor_pos) col = cursor.positionInBlock() line = cursor.blockNumber() tooltip = "" rect = QRect() if line >= len(self.highlighter.matches): return "", QRect() match_start = 0 match_len = 0 matches = 0 #for word in self.highlighter.matches[line]: #if col >= word[1] and col < word[2]: for word in self.words_at_pos(line, col): keyword = word[0] # Go for the smallest region possible so we can update # as soon as the mouse falls out of range of one of the words. if word[1] > match_start: match_start = word[1] if word[2] - word[1] < match_len: match_len = word[2] - word[1] if matches > 0: tooltip += u"\n" if not keyword.section == "": tooltip += u"【%s】 " % keyword.section tooltip += u"%s ― %s" % (keyword.word, keyword.meaning) matches += 1 # Highlight our word, so we can get the rect. cursor.movePosition(QTextCursor.Start) for i in range(line): cursor.movePosition(QTextCursor.NextBlock) for i in range(match_start): cursor.movePosition(QTextCursor.NextCharacter) for i in range(match_len): cursor.movePosition(QTextCursor.NextCharacter, QTextCursor.KeepAnchor) rect = self.cursorRect(cursor) return tooltip, rect
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 _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 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 formatMatchedText(self, document, match): """Format the matched text in a document""" cursor = QTextCursor(document) cursor.setPosition(match.start()) cursor.movePosition(QTextCursor.NextCharacter, QTextCursor.KeepAnchor, match.end() - match.start()) cursor.setCharFormat(self.matchFormat)
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 _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 decorator(cursor): remove = list(func(cursor)) if remove: c = QTextCursor(cursor) with cursortools.compress_undo(c): for start, end in sorted(remove, reverse=True): c.setPosition(start) c.setPosition(end, QTextCursor.KeepAnchor) c.removeSelectedText()
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 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 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 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 formatMatchedText(self, document, match): """Format the matched text in a document""" cursor = QTextCursor(document) cursor.setPosition(match.start()) cursor.movePosition( QTextCursor.NextCharacter, QTextCursor.KeepAnchor, match.end() - match.start()) cursor.setCharFormat(self.matchFormat)
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 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 __highlight(self, positions, color=None, cancel=False): cursor = QTextCursor(self.document()) for position in positions: if position > self.get_position('eof'): return cursor.setPosition(position) cursor.movePosition(QTextCursor.NextCharacter, QTextCursor.KeepAnchor) charformat = cursor.charFormat() pen = QPen(Qt.NoPen) if cancel else QPen(color) charformat.setTextOutline(pen) cursor.setCharFormat(charformat)
def setTextCursor(self, cursor): """Reimplemented to show n surrounding lines.""" numlines = QSettings().value("view_preferences/context_lines", 3, int) if numlines > 0: c = QTextCursor(cursor) c.setPosition(cursor.selectionEnd()) c.movePosition(QTextCursor.Down, QTextCursor.MoveAnchor, numlines) super(View, self).setTextCursor(c) c.setPosition(cursor.selectionStart()) c.movePosition(QTextCursor.Up, QTextCursor.MoveAnchor, numlines) super(View, self).setTextCursor(c) super(View, self).setTextCursor(cursor)
def goto_target(mainwindow, target): """Switch to the document and location where the node target is.""" lydoc = target.document try: # this succeeds if this is a document that is currently open doc = lydoc.document except AttributeError: # it is an included file, just load it 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 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 selectedPosition(self, pos): anchorPos, cursorPos = pos anchorLine, anchorCol = anchorPos cursorLine, cursorCol = cursorPos anchorCursor = QTextCursor(self.document().findBlockByNumber(anchorLine)) anchorCursor.setPositionInBlock(anchorCol) # just get absolute position cursor = QTextCursor(self.document().findBlockByNumber(cursorLine)) cursor.setPositionInBlock(cursorCol) anchorCursor.setPosition(cursor.position(), QTextCursor.KeepAnchor) self.setTextCursor(anchorCursor)
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 cursor(self, start=0, end=None): """Returns a QTextCursor for the last token. If start is given the cursor will start at position start in the token (from the beginning of the token). Start defaults to 0. If end is given, the cursor will end at that position in the token (from the beginning of the token). End defaults to the length of the token. """ if end is None: end = len(self.token()) c = QTextCursor(self.document.document) c.setPosition(self.position() + start) c.setPosition(self.position() + end, QTextCursor.KeepAnchor) return c
def apply_changes(self): """Apply the changes and update the tokens.""" c = QTextCursor(self._d) # record a sensible position for undo c.setPosition(self._changes_list[-1][0]) c.joinPreviousEditBlock() if self.combine_undo else c.beginEditBlock() try: for start, end, text in self._changes_list: c.movePosition(QTextCursor.End) if end is None else c.setPosition(end) c.setPosition(start, QTextCursor.KeepAnchor) c.insertText(text) finally: c.endEditBlock() if self.combine_undo is None: self.combine_undo = True
def cursor(block, token, start=0, end=None): """Returns a QTextCursor for the given token in the given block. If start is given the cursor will start at position start in the token (from the beginning of the token). Start defaults to 0. If end is given, the cursor will end at that position in the token (from the beginning of the token). End defaults to the length of the token. """ if end is None: end = len(token) cursor = QTextCursor(block) cursor.setPosition(block.position() + token.pos + start) cursor.setPosition(block.position() + token.pos + end, QTextCursor.KeepAnchor) return cursor
def preceding(cursor): """Returns a preceding duration before the cursor, or an empty list.""" c = QTextCursor(cursor) c.setPosition(cursor.selectionStart()) for tokens in back(c): for t in tokens: if isinstance(t, ly.lex.lilypond.Duration): l = [t] for t in tokens: if isinstance(t, ly.lex.lilypond.Duration): l.append(t) elif not isinstance(t, ly.lex.Space): break l.reverse() return l return []
def apply_changes(self): """Apply the changes and update the tokens.""" c = QTextCursor(self._d) # record a sensible position for undo c.setPosition(self._changes_list[-1][0]) c.joinPreviousEditBlock() if self.combine_undo else c.beginEditBlock() try: for start, end, text in self._changes_list: c.movePosition( QTextCursor.End) if end is None else c.setPosition(end) c.setPosition(start, QTextCursor.KeepAnchor) c.insertText(text) finally: c.endEditBlock() if self.combine_undo is None: self.combine_undo = True
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 insert_snippet(text, cursor, variables): """Inserts a normal text snippet. After the insert, the cursor points to the end of the inserted snippet. If this function returns a cursor it must be set as the cursor for the view after the snippet has been inserted. """ exp_base = expand.Expander(cursor) evs = [] # make a list of events, either text or a constant for text, key in snippets.expand(text): if text: evs.append(text) if key == '$': evs.append('$') elif key: # basic variables func = getattr(exp_base, key, None) if func: evs.append(func()) selectionUsed = expand.SELECTION in evs # do the padding if 'selection: strip;' is used if selectionUsed and 'strip' in variables.get('selection', ''): space = '\n' if '\n' in cursor.selection().toPlainText() else ' ' # change whitespace in previous and next piece of text i = evs.index(expand.SELECTION) for j in range(i-1, -i, -1): if evs[j] not in expand.constants: evs[j] = evs[j].rstrip() + space break for j in range(i+1, len(evs)): if evs[j] not in expand.constants: evs[j] = space + evs[j].lstrip() break # now insert the text ins = QTextCursor(cursor) selectionUsed and ins.setPosition(cursor.selectionStart()) a, c = -1, -1 for e in evs: if e == expand.ANCHOR: a = ins.position() elif e == expand.CURSOR: c = ins.position() elif e == expand.SELECTION: ins.setPosition(cursor.selectionEnd()) else: ins.insertText(e) cursor.setPosition(ins.position()) # return a new cursor if requested 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
def gotoTextCursor(self, cursor, numlines=3): """Go to the specified cursor. If possible, at least numlines (default: 3) number of surrounding lines is shown. The number of surrounding lines can also be set in the preferences, under the key "view_preferences/context_lines". This setting takes precedence. """ numlines = QSettings().value("view_preferences/context_lines", numlines, int) if numlines > 0: c = QTextCursor(cursor) c.setPosition(cursor.selectionEnd()) c.movePosition(QTextCursor.Down, QTextCursor.MoveAnchor, numlines) self.setTextCursor(c) c.setPosition(cursor.selectionStart()) c.movePosition(QTextCursor.Up, QTextCursor.MoveAnchor, numlines) self.setTextCursor(c) self.setTextCursor(cursor)
def keyPressEvent(self, event): #pqMsgs.printKeyEvent(event) kkey = int(int(event.modifiers()) & IMC.keypadDeModifier) | int( event.key()) # add as little overhead as possible: if it isn't ours, pass it on. if kkey in IMC.keysOfInterest: # we trust python to do this quickly event.accept() # we handle this one if kkey in IMC.findKeys: # ^f, ^g, etc. -- just pass them straight to the Find panel self.emit(SIGNAL("editKeyPress"), kkey) elif kkey in IMC.zoomKeys: # n.b. the self.font and setFont methods inherit from QWidget # Point increment by which to change. n = (-1) if (kkey == IMC.ctl_minus) else 1 # Actual point size currently in use, plus increment p = self.fontInfo().pointSize() + n if (p > 3) and (p < 65): # don't let's get ridiculous, hmm? # Simply calling self.font().setPointSize() had no effect, # we have to actually call setFont() to make change happen. f = self.font() # so get our font, f.setPointSize(p) # change its point size +/- self.setFont(f) # and put the font back IMC.fontSize = p # and remember the size for shutdown time elif kkey in IMC.markKeys: # ^1-9, jump to bookmark bkn = kkey - IMC.ctl_1 # make it 0-8 if self.bookMarkList[ bkn] is not None: # if that bookmark is set, self.setTextCursor(self.bookMarkList[bkn]) # jump to it elif kkey in IMC.markShiftKeys: # shift-ctl-1/9, select to mark # Make our document cursor's selection go from our current ANCHOR # to the POSITION from the bookmark cursor. mark_tc = self.bookMarkList[kkey - IMC.ctl_shft_1] if mark_tc is not None: tc = QTextCursor(self.textCursor()) tc.setPosition(mark_tc.position(), QTextCursor.KeepAnchor) self.setTextCursor(tc) elif kkey in IMC.markSetKeys: # ctl-alt-1-9, set a bookmark bkn = kkey - IMC.ctl_alt_1 # make it 0-8 self.bookMarkList[bkn] = QTextCursor(self.textCursor()) IMC.needMetadataSave |= IMC.bookmarksChanged else: # not in keysOfInterest, so pass it up to parent event.ignore() super(PPTextEditor, self).keyPressEvent(event)
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 replaceText(self, pos, length, text): """Replace length symbols from ``pos`` with new text. If ``pos`` is an integer, it is interpreted as absolute position, if a tuple - as ``(line, column)`` """ if isinstance(pos, tuple): pos = self.mapToAbsPosition(*pos) endPos = pos + length if not self.document().findBlock(pos).isValid(): raise IndexError('Invalid start position %d' % pos) if not self.document().findBlock(endPos).isValid(): raise IndexError('Invalid end position %d' % endPos) cursor = QTextCursor(self.document()) cursor.setPosition(pos) cursor.setPosition(endPos, QTextCursor.KeepAnchor) cursor.insertText(text)
def toLowerCase(self): global reWord # the regex \b\w+\b tc = QTextCursor(self.textCursor()) if not tc.hasSelection(): return # no selection, nothing to do startpos = tc.selectionStart() endpos = tc.selectionEnd() qs = QString(tc.selectedText()) # copy of selected text i = reWord.indexIn(qs, 0) # index of first word if any if i < 0: return # no words in selection, exit while i >= 0: w = reWord.cap(0) # found word as QString n = w.size() # its length qs.replace(i, n, w.toLower()) # replace it with UC version i = reWord.indexIn(qs, i + n) # find next word if any # we have changed at least one word, replace selection with altered text tc.insertText(qs) # that wiped the selection, so restore it by "dragging" left to right tc.setPosition(startpos, QTextCursor.MoveAnchor) # click tc.setPosition(endpos, QTextCursor.KeepAnchor) # drag self.setTextCursor(tc)
def keep_selection(cursor, edit=None): """Performs operations inside the selection and restore the selection afterwards. If edit is given, call setTextCursor(cursor) on the Q(Plain)TextEdit afterwards. """ start, end, pos = cursor.selectionStart(), cursor.selectionEnd( ), cursor.position() cur2 = QTextCursor(cursor) cur2.setPosition(end) try: yield finally: if pos == start: cursor.setPosition(cur2.position()) cursor.setPosition(start, QTextCursor.KeepAnchor) else: cursor.setPosition(start) cursor.setPosition(cur2.position(), QTextCursor.KeepAnchor) if edit: edit.setTextCursor(cursor)
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)