Example #1
0
 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
Example #2
0
 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
Example #3
0
 def _cursor_moved(self):
     tc = QTextCursor(self.Editor.textCursor())  # copy of cursor
     self.ColNumber.setText(str(tc.positionInBlock()))
     tb = tc.block()
     if tb == self.last_text_block:
         return  # still on same line, nothing more to do
     # Fill in line-number widget, line #s are origin-1
     self.LineNumber.setText(str(tb.blockNumber() + 1))
     # Fill in the image name and folio widgets
     pn = self.page_model.page_index(tc.position())
     if pn is not None:  # the page model has info on this position
         self.ImageFilename.setText(self.page_model.filename(pn))
         self.Folio.setText(self.page_model.folio_string(pn))
     else:  # no image data, or cursor is above page 1
         self.ImageFilename.setText('')
         self.Folio.setText('')
     # Change the current-line "extra selection" to the new current line.
     # Note: the cursor member of the extra selection may not have a
     # selection. If it does, the current line highlight disappears. The
     # cursor "tc" may or may not have a selection; to make sure, we clone
     # it and remove any selection from it.
     cltc = QTextCursor(tc)
     cltc.setPosition(tc.position(), QTextCursor.MoveAnchor)
     # Set the cloned cursor into the current line extra selection object.
     self.current_line_sel.cursor = cltc
     # Re-assign the list of extra selections to force update of display.
     self.Editor.setExtraSelections(self.extra_sel_list)
Example #4
0
 def paintEvent(self, event):
     if not globalSettings.lineNumbersEnabled:
         return QWidget.paintEvent(self, event)
     painter = QPainter(self)
     painter.fillRect(event.rect(), colorValues['lineNumberArea'])
     cursor = QTextCursor(self.editor.document())
     cursor.movePosition(QTextCursor.Start)
     atEnd = False
     if globalSettings.relativeLineNumbers:
         relativeTo = self.editor.textCursor().blockNumber()
     else:
         relativeTo = -1
     while not atEnd:
         rect = self.editor.cursorRect(cursor)
         block = cursor.block()
         if block.isVisible():
             number = str(cursor.blockNumber() - relativeTo).replace(
                 '-', '−')
             painter.setPen(colorValues['lineNumberAreaText'])
             painter.drawText(0, rect.top(),
                              self.width() - 2,
                              self.fontMetrics().height(), Qt.AlignRight,
                              number)
         cursor.movePosition(QTextCursor.EndOfBlock)
         atEnd = cursor.atEnd()
         if not atEnd:
             cursor.movePosition(QTextCursor.NextBlock)
Example #5
0
 def _cursor_moved(self):
     tc = QTextCursor(self.Editor.textCursor()) # copy of cursor
     self.ColNumber.setText( str( tc.positionInBlock() ) )
     tb = tc.block()
     if tb == self.last_text_block :
         return # still on same line, nothing more to do
     # Fill in line-number widget, line #s are origin-1
     self.LineNumber.setText( str( tb.blockNumber()+1 ) )
     # Fill in the image name and folio widgets
     pn = self.page_model.page_index(tc.position())
     if pn is not None : # the page model has info on this position
         self.ImageFilename.setText(self.page_model.filename(pn))
         self.Folio.setText(self.page_model.folio_string(pn))
     else: # no image data, or cursor is above page 1
         self.ImageFilename.setText('')
         self.Folio.setText('')
     # Change the current-line "extra selection" to the new current line.
     # Note: the cursor member of the extra selection may not have a
     # selection. If it does, the current line highlight disappears. The
     # cursor "tc" may or may not have a selection; to make sure, we clone
     # it and remove any selection from it.
     cltc = QTextCursor(tc)
     cltc.setPosition(tc.position(),QTextCursor.MoveAnchor)
     # Set the cloned cursor into the current line extra selection object.
     self.current_line_sel.cursor = cltc
     # Re-assign the list of extra selections to force update of display.
     self.Editor.setExtraSelections(self.extra_sel_list)
Example #6
0
    def cmdJoinLines(self, cmd, repeatLineCount=None):
        if repeatLineCount is not None:
            self._selectRangeForRepeat(repeatLineCount)

        start, end = self._selectedLinesRange()
        count = end - start

        if not count:  # nothing to join
            return

        self._saveLastEditLinesCmd(cmd, end - start + 1)

        cursor = QTextCursor(self._qpart.document().findBlockByNumber(start))
        with self._qpart:
            for _ in range(count):
                cursor.movePosition(QTextCursor.EndOfBlock)
                cursor.movePosition(QTextCursor.NextCharacter,
                                    QTextCursor.KeepAnchor)
                self.moveToFirstNonSpace(cursor, QTextCursor.KeepAnchor)
                nonEmptyBlock = cursor.block().length() > 1
                cursor.removeSelectedText()
                if nonEmptyBlock:
                    cursor.insertText(' ')

        self._qpart.setTextCursor(cursor)
    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
Example #8
0
 def cursor_position(self, position):
     line, column = position
     line = min(line, self.line_count() - 1)
     column = min(column, len(self.line_text(line)))
     cursor = QTextCursor(self.document().findBlockByNumber(line))
     cursor.setPosition(cursor.block().position() + column,
                        QTextCursor.MoveAnchor)
     self.setTextCursor(cursor)
Example #9
0
 def cursor_position(self, position):
     line, column = position
     line = min(line, self.line_count() - 1)
     column = min(column, len(self.line_text(line)))
     cursor = QTextCursor(self.document().findBlockByNumber(line))
     cursor.setPosition(cursor.block().position() + column,
                        QTextCursor.MoveAnchor)
     self.setTextCursor(cursor)
Example #10
0
    def set_new_cursor_position(self, cursor: QTextCursor) -> None:
        """
        Set new cursor position in main status bar

        Args:
            cursor(QTextCursor): active cursor of code editor in active tab
        """
        self.statusbar_widget.set_line_and_column(
            cursor.block().blockNumber() + 1,
            cursor.positionInBlock() + 1)
    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
Example #12
0
 def select_between(tc: QTextCursor,
                    start_char: str,
                    end_char: str,
                    select_inside: bool = True) -> None:
     pos = tc.positionInBlock()
     text = tc.block().text()
     start_pos = text.rfind(start_char, 0, pos)
     if start_pos == -1:
         return
     end_pos = text.find(end_char, pos)
     if end_pos == -1:
         return
     if select_inside:
         start_pos += len(start_char)
     else:
         end_pos += len(end_char)
         while end_pos < len(text) and text[end_pos].isspace():
             end_pos += 1
     tc.setPosition(tc.block().position() + start_pos)
     tc.setPosition(tc.block().position() + end_pos, QTC.KeepAnchor)
Example #13
0
    def highlightHorizontalLine(self, text, cursor, bf, strt):
        found = False
        for mo in re.finditer(self.MARKDOWN_KEYS_REGEX['HR'],text):
            prevBlock = self.currentBlock().previous()
            prevCursor = QTextCursor(prevBlock)
            prev = prevBlock.text()
            prevAscii = str(prev.replace(u'\u2029','\n'))
            if prevAscii.strip():
                #print "Its a header"
                prevCursor.select(QTextCursor.LineUnderCursor)
                #prevCursor.setCharFormat(self.MARKDOWN_KWS_FORMAT['Header'])
                formatRange = QTextLayout.FormatRange()
                formatRange.format = self.MARKDOWN_KWS_FORMAT['Header']
                formatRange.length = prevCursor.block().length()
                formatRange.start = 0
                prevCursor.block().layout().setAdditionalFormats([formatRange])
            self.setFormat(mo.start()+strt, mo.end() - mo.start(), self.MARKDOWN_KWS_FORMAT['HR'])

        for mo in re.finditer(self.MARKDOWN_KEYS_REGEX['eHR'],text):
            prevBlock = self.currentBlock().previous()
            prevCursor = QTextCursor(prevBlock)
            prev = prevBlock.text()
            prevAscii = str(prev.replace(u'\u2029','\n'))
            if prevAscii.strip():
                #print "Its a header"
                prevCursor.select(QTextCursor.LineUnderCursor)
                #prevCursor.setCharFormat(self.MARKDOWN_KWS_FORMAT['Header'])
                formatRange = QTextLayout.FormatRange()
                formatRange.format = self.MARKDOWN_KWS_FORMAT['Header']
                formatRange.length = prevCursor.block().length()
                formatRange.start = 0
                prevCursor.block().layout().setAdditionalFormats([formatRange])
            self.setFormat(mo.start()+strt, mo.end() - mo.start(), self.MARKDOWN_KWS_FORMAT['HR'])
        return found
Example #14
0
 def indent_line(self, line_no, indent_length):
     block = self._get_block(line_no)
     cursor = QTextCursor(block)
     cursor.joinPreviousEditBlock()
     cursor.movePosition(QTextCursor.StartOfBlock, QTextCursor.MoveAnchor)
     if indent_length < 0:
         for i in range(-indent_length):
             cursor.deleteChar()
     else:
         cursor.insertText(" " * indent_length)
     if indent_length:
         cursor.movePosition(QTextCursor.StartOfBlock, QTextCursor.MoveAnchor)
         line = unicode(cursor.block().text())
         if len(line) and line[0] == " ":
             cursor.movePosition(QTextCursor.NextWord, QTextCursor.MoveAnchor)
         self.editview.setTextCursor(cursor)
     cursor.endEditBlock()
Example #15
0
	def lineNumberAreaPaintEvent(self, event):
		painter = QPainter(self.lineNumberArea)
		painter.fillRect(event.rect(), colorValues['lineNumberArea'])
		cursor = QTextCursor(self.document())
		cursor.movePosition(QTextCursor.Start)
		atEnd = False
		while not atEnd:
			rect = self.cursorRect(cursor)
			block = cursor.block()
			if block.isVisible():
				number = str(cursor.blockNumber() + 1)
				painter.setPen(colorValues['lineNumberAreaText'])
				painter.drawText(0, rect.top(), self.lineNumberArea.width()-2,
					self.fontMetrics().height(), Qt.AlignRight, number)
			cursor.movePosition(QTextCursor.EndOfBlock)
			atEnd = cursor.atEnd()
			if not atEnd:
				cursor.movePosition(QTextCursor.NextBlock)
Example #16
0
 def indent_line(self, line_no, indent_length):
     block = self._get_block(line_no)
     cursor = QTextCursor(block)
     cursor.joinPreviousEditBlock()
     cursor.movePosition(QTextCursor.StartOfBlock, QTextCursor.MoveAnchor)
     if indent_length < 0:
         for i in range(-indent_length):
             cursor.deleteChar()
     else:
         cursor.insertText(" " * indent_length)
     if indent_length:
         cursor.movePosition(QTextCursor.StartOfBlock,
                             QTextCursor.MoveAnchor)
         line = unicode(cursor.block().text())
         if len(line) and line[0] == " ":
             cursor.movePosition(QTextCursor.NextWord,
                                 QTextCursor.MoveAnchor)
         self.editview.setTextCursor(cursor)
     cursor.endEditBlock()
Example #17
0
 def lineNumberAreaPaintEvent(self, event):
     painter = QPainter(self.lineNumberArea)
     painter.fillRect(event.rect(), Qt.cyan)
     cursor = QTextCursor(self.document())
     cursor.movePosition(QTextCursor.Start)
     atEnd = False
     while not atEnd:
         rect = self.cursorRect(cursor)
         block = cursor.block()
         if block.isVisible():
             number = str(cursor.blockNumber() + 1)
             painter.setPen(Qt.darkCyan)
             painter.drawText(0, rect.top(),
                              self.lineNumberArea.width() - 2,
                              self.fontMetrics().height(), Qt.AlignRight,
                              number)
         cursor.movePosition(QTextCursor.EndOfBlock)
         atEnd = cursor.atEnd()
         if not atEnd:
             cursor.movePosition(QTextCursor.NextBlock)
Example #18
0
 def _cursor_moved(self):
     tc = QTextCursor(self.Editor.textCursor()) # copy of cursor
     self.ColNumber.setText( str( tc.positionInBlock() ) )
     tb = tc.block()
     if tb == self.last_text_block :
         return # still on same line, nothing more to do
     # Fill in line-number widget, line #s are origin-1
     self.LineNumber.setText( str( tb.blockNumber()+1 ) )
     # Fill in the image name and folio widgets
     pn = self.page_model.page_index(tc.position())
     if pn is not None : # the page model has info on this position
         self.ImageFilename.setText(self.page_model.filename(pn))
         self.Folio.setText(self.page_model.folio_string(pn))
     else: # no image data, or cursor is above page 1
         self.ImageFilename.setText('')
         self.Folio.setText('')
     # Change the extra selection to the current line. The cursor needs
     # to have no selection. Yes, we are here because the cursor moved,
     # but that doesn't mean no-selection; it might have moved because
     # of a shift-click extending the selection.
     #xtc.clearSelection()
     self.current_line_sel.cursor = tc
     self.Editor.setExtraSelections(self.extra_sel_list)
Example #19
0
	def paintEvent(self, event):
		if not globalSettings.lineNumbersEnabled:
			return QWidget.paintEvent(self, event)
		painter = QPainter(self)
		painter.fillRect(event.rect(), colorValues['lineNumberArea'])
		cursor = QTextCursor(self.editor.document())
		cursor.movePosition(QTextCursor.Start)
		atEnd = False
		if globalSettings.relativeLineNumbers:
			relativeTo = self.editor.textCursor().blockNumber()
		else:
			relativeTo = -1
		while not atEnd:
			rect = self.editor.cursorRect(cursor)
			block = cursor.block()
			if block.isVisible():
				number = str(cursor.blockNumber() - relativeTo).replace('-', '−')
				painter.setPen(colorValues['lineNumberAreaText'])
				painter.drawText(0, rect.top(), self.width() - 2,
					self.fontMetrics().height(), Qt.AlignRight, number)
			cursor.movePosition(QTextCursor.EndOfBlock)
			atEnd = cursor.atEnd()
			if not atEnd:
				cursor.movePosition(QTextCursor.NextBlock)
Example #20
0
    def cmdJoinLines(self, cmd, repeatLineCount=None):
        if repeatLineCount is not None:
            self._selectRangeForRepeat(repeatLineCount)

        start, end = self._selectedLinesRange()
        count = end - start

        if not count:  # nothing to join
            return

        self._saveLastEditLinesCmd(cmd, end - start + 1)

        cursor = QTextCursor(self._qpart.document().findBlockByNumber(start))
        with self._qpart:
            for _ in range(count):
                cursor.movePosition(QTextCursor.EndOfBlock)
                cursor.movePosition(QTextCursor.NextCharacter, QTextCursor.KeepAnchor)
                self.moveToFirstNonSpace(cursor, QTextCursor.KeepAnchor)
                nonEmptyBlock = cursor.block().length() > 1
                cursor.removeSelectedText()
                if nonEmptyBlock:
                    cursor.insertText(' ')

        self._qpart.setTextCursor(cursor)
Example #21
0
class TextSectionEditor(QTextDocument):
    def __init__(self,
                 sectionId: str,
                 content="",
                 pos=0,
                 selectionStart=0,
                 selectionEnd=0):
        super().__init__()
        self.dtb = DTB()
        self.sectionId = sectionId
        self.setDefaultStyleSheet(CSS)
        self.setHtml(content)
        self.s_start = selectionStart
        self.s_end = selectionEnd

        self.cur = QTextCursor(self)
        self.cur.setPosition(pos)
        self.result = {
            "text": "",
            "cursorPosition": self.pos,
            "eventAccepted": False
        }
        self.pending = False

    @property
    def len(self):
        return self.characterCount()

    @property
    def s_len(self):
        return abs(self.s_end - self.s_start)

    @property
    def pos(self):
        return self.cur.position()

    def onChange(self):

        self._update_ddb()
        self.setResponse(True)
        return self.result

    def onLoad(self):
        item = self.dtb.getDB("Section", self.sectionId)
        self.setHtml(item["text"])
        self.setResponse(True, cur=self.len)
        return self.result

    def onMenu(self, style={}, **kwargs):
        backup = [self.pos, self.s_start, self.s_end]

        for k, v in style.items():
            # un peut répétition mais uniquement sur des if alors ...
            if k == "fgColor":
                self._set_fg_color(v)
            elif k == "underline":  # pragma: no branch
                self._set_underline(style["underline"])

            self.cur.setPosition(backup[0])
            self.s_start = backup[1]
            self.s_end = backup[2]

        if self.pending:
            # else:
            self._update_ddb()
        else:
            self.setResponse(False)
        return self.result

    def onKey(self, event):
        # on met en premier ceux à qui il faut passer l'event
        if event["key"] == Qt.Key_Return:
            self.do_key_return(event)

        elif event["modifiers"] == Qt.ControlModifier:
            self.do_control_modifier(event)

        else:
            self.setResponse(False)

        if self.pending:
            self._update_ddb()
        return self.result

    def do_control_modifier(self, event):

        if event == KeyW.KEY_1:
            self.do_key_1()

        elif event == KeyW.KEY_2:
            self.do_key_2()

        elif event == KeyW.KEY_3:
            self.do_key_3()

        elif event == KeyW.KEY_4:
            self.do_key_4()

        elif event["key"] == Qt.Key_U:  # pragma: no branch
            self.do_key_u()

    def do_key_1(self):
        self._set_fg_color(BLACK)

    def do_key_2(self):
        self._set_fg_color(BLUE)

    def do_key_3(self):
        self._set_fg_color(GREEN)

    def do_key_4(self):
        self._set_fg_color(RED)

    def do_key_return(self, event):
        block = self.findBlock(self.pos)
        if event["modifiers"] == Qt.ControlModifier:
            self._appendEmptyBlock()
        elif event["modifiers"] == Qt.ShiftModifier:
            self._insertEmptyBlock()
        elif block.blockFormat().headingLevel():
            self._appendEmptyBlock()
        else:
            if self._headerAutoFormat():
                return
            else:
                self.setResponse(False)

    def do_key_u(self):
        self._set_underline("toggle")

    def setResponse(self, accepted, text=None, cur=None):
        self.result["text"] = text or self.toHtml()
        self.result["cursorPosition"] = cur or self.cur.position()
        self.result["eventAccepted"] = accepted

    def _appendEmptyBlock(self,
                          section="p",
                          pre_move=QTextCursor.EndOfBlock,
                          set_response=True):
        self.cur.movePosition(pre_move)
        self.cur.insertBlock(blockFormat[section], blockCharFormat[section])
        self.cur.insertFragment(QTextDocumentFragment.fromPlainText(""))
        self.pending = True
        if set_response:
            self.setResponse(True)

    def _insertEmptyBlock(self,
                          section="p",
                          pre_move=QTextCursor.StartOfBlock,
                          set_response=True):
        old = self.cur.blockFormat().headingLevel()
        self._appendEmptyBlock(pre_move=pre_move, set_response=False)
        self._set_block_style(old)
        self.cur.movePosition(QTextCursor.PreviousBlock)
        self._set_block_style(section)
        self.pending = True
        if set_response:  # pragma: no branch
            self.setResponse(True)

    def _headerAutoFormat(self):

        # on check les expressions régulières suivantes:
        #   #, ##, ###, ####, #####
        line = self.cur.block().text()
        matched = RE_AUTOPARAGRAPH_DEBUT.search(line)
        matched_at_start = False
        if not matched:
            matched = RE_AUTOPARAGRAPH_FIN.search(line)
            if not matched:
                return False
        else:
            matched_at_start = True

        # strip les # et applique les styles par défault
        level = len(matched.groups()[0])
        if matched_at_start:
            text = self.cur.block().text()[level + 1:]
        else:
            text = self.cur.block().text()[:-(level + 1)]

        self.cur.beginEditBlock()
        self.cur.select(QTextCursor.LineUnderCursor)
        self.cur.setCharFormat(blockCharFormat[level])
        self.cur.insertText(text)
        self.cur.setBlockFormat(blockFormat[level])
        self.cur.endEditBlock()

        self._appendEmptyBlock()
        self.pending = True
        self.setResponse(True)
        return True

    @contextmanager
    def _merge_char_format(self):
        self._select_word_or_selection()
        f: QTextCharFormat = QTextCharFormat()
        yield f
        self.cur.mergeCharFormat(f)
        self.pending = True
        self.setResponse(True, cur=max(self.pos, self.s_start, self.s_end))

    def _select_word_or_selection(self):
        if self.s_start < self.pos:
            self.cur.movePosition(QTextCursor.Left, QTextCursor.KeepAnchor,
                                  self.s_len)
        elif self.s_end > self.pos:
            self.cur.movePosition(QTextCursor.Right, QTextCursor.KeepAnchor,
                                  self.s_len)
        else:
            self.cur.select(QTextCursor.WordUnderCursor)

    def _set_block_style(self, level):
        self.cur.setBlockFormat(blockFormat[level])
        self.cur.setBlockCharFormat(blockCharFormat[level])

    def _set_fg_color(self, color):
        with self._merge_char_format() as f:
            f.setForeground(QBrush(QColor(color)))

    def _set_underline(self, value):
        with self._merge_char_format() as f:
            if value == "toggle":
                value = not self.cur.charFormat().fontUnderline()
            f.setFontUnderline(value)

    def _update_ddb(self):
        new_body = TextSectionFormatter(self.toHtml()).build_body()
        self.dtb.setDB("Section", self.sectionId, {"text": new_body})
        return new_body
Example #22
0
class Log(QTextBrowser):
    """Widget displaying output from a Job."""
    def __init__(self, parent=None):
        super(Log, self).__init__(parent)
        self.setOpenLinks(False)
        self.cursor = QTextCursor(self.document())
        self._types = job.ALL
        self._lasttype = None
        self._formats = self.logformats()

    def setMessageTypes(self, types):
        """Set the types of Job output to display.

        types is a constant bitmask from job, like job.STDOUT etc.
        By default job.ALL is used.

        """
        self._types = types

    def messageTypes(self):
        """Return the set message types (job.ALL by default)."""
        return self._types

    def connectJob(self, job):
        """Gives us the output from the Job (past and upcoming)."""
        for msg, type in job.history():
            self.write(msg, type)
        job.output.connect(self.write)

    def textFormat(self, type):
        """Returns a QTextFormat() for the given type."""
        return self._formats[type]

    def write(self, message, type):
        """Writes the given message with the given type to the log.

        The keepScrolledDown context manager is used to scroll the log further
        down if it was scrolled down at that moment.

        If two messages of a different type are written after each other a newline
        is inserted if otherwise the message would continue on the same line.

        """
        if type & self._types:
            with self.keepScrolledDown():
                changed = type != self._lasttype
                self._lasttype = type
                if changed and self.cursor.block().text() and not message.startswith('\n'):
                    self.cursor.insertText('\n')
                self.writeMessage(message, type)

    def writeMessage(self, message, type):
        """Inserts the given message in the text with the textformat belonging to type."""
        self.cursor.insertText(message, self.textFormat(type))

    @contextlib.contextmanager
    def keepScrolledDown(self):
        """Performs a function, ensuring the log stays scrolled down if it was scrolled down on start."""
        vbar = self.verticalScrollBar()
        scrolleddown = vbar.value() == vbar.maximum()
        try:
            yield
        finally:
            if scrolleddown:
                vbar.setValue(vbar.maximum())

    def logformats(self):
        """Returns a dictionary with QTextCharFormats for the different types of messages.

        Besides the STDOUT, STDERR, NEUTRAL, FAILURE and SUCCESS formats there is also
        a "link" format, that looks basically the same as the output formats, but blueish
        and underlined, to make parts of the output (e.g. filenames) look clickable.

        """
        textColor = QApplication.palette().color(QPalette.WindowText)
        successColor = qutil.addcolor(textColor, 0, 128, 0) # more green
        failureColor = qutil.addcolor(textColor, 128, 0, 0) # more red
        linkColor    = qutil.addcolor(textColor, 0, 0, 128) # more blue
        stdoutColor  = qutil.addcolor(textColor, 64, 64, 0) # more purple

        s = QSettings()
        s.beginGroup("log")
        outputFont = QFont(s.value("fontfamily", "monospace", str))
        outputFont.setPointSizeF(s.value("fontsize", 9.0, float))

        output = QTextCharFormat()
        output.setFont(outputFont)
        # enable zooming the log font size
        output.setProperty(QTextFormat.FontSizeAdjustment, 0)

        stdout = QTextCharFormat(output)
        stdout.setForeground(stdoutColor)

        stderr = QTextCharFormat(output)
        link   = QTextCharFormat(output)
        link.setForeground(linkColor)
        link.setFontUnderline(True)

        status = QTextCharFormat()
        status.setFontWeight(QFont.Bold)

        neutral = QTextCharFormat(status)

        success = QTextCharFormat(status)
        success.setForeground(successColor)

        failure = QTextCharFormat(status)
        failure.setForeground(failureColor)

        return {
            job.STDOUT: stdout,
            job.STDERR: stderr,
            job.NEUTRAL: neutral,
            job.SUCCESS: success,
            job.FAILURE: failure,
            'link': link,
        }
Example #23
0
    def display_tooltips_for_cursor(self, cursor: QTextCursor,
                                    display_point: QPoint) -> None:
        """Displays a tooltip at current cursor position if a citation tag or an image tag is under cursor. Hides
        the tooltip if no tags are under cursor"""
        def is_inside_tag(tag, cursor_pos) -> bool:
            """Returns True if given cursor position is inside tag boundaries"""
            return tag[0] <= cursor_pos < (tag[1] + tag[0])

        show_citation_tooltips = self.SettingsManager.get_setting_value(
            "Editor/Show citation tooltips")
        show_image_tooltips = self.SettingsManager.get_setting_value(
            "Editor/Show image tooltips")
        hide_tooltip = True
        current_line_text = cursor.block().text()
        current_line_tags = self.highlighter.get_tags(current_line_text)
        cursor_pos = cursor.positionInBlock()

        for tag in current_line_tags:
            tag_text = current_line_text[tag[0]:tag[0] + tag[1]]

            if show_citation_tooltips and tag[
                    2] == "citation" and is_inside_tag(tag, cursor_pos):
                # Remove brackets and @ symbol from the tag text
                citation_identifier = components.extractors.citation_extractor(
                    tag_text)[0]

                # If the citekey is not in self.document_info["citations"] or doesn't have a citation text yet, show
                # placeholder
                if citation_identifier not in self.document_structure[
                        "citations"]:
                    QToolTip.showText(display_point,
                                      "Fetching citation info...", self,
                                      QRect(), 5000)
                    hide_tooltip = False

                elif citation_identifier in self.document_structure[
                        "citations"] and self.document_structure["citations"][
                            citation_identifier]["citation"] == "":
                    QToolTip.showText(display_point,
                                      "Fetching citation info...", self,
                                      QRect(), 5000)
                    hide_tooltip = False

                # If the citekey is in self.document_info["citations"] and citation show citation info
                else:
                    QToolTip.showText(
                        display_point, self.document_structure["citations"]
                        [citation_identifier]["citation"], self, QRect(), 5000)
                    hide_tooltip = False

            elif show_image_tooltips and tag[2] == "image" and is_inside_tag(
                    tag, cursor_pos):
                # Do nothing
                if not self.SettingsManager.get_setting_value(
                        "Editor/Show citation tooltips"):
                    return

                path = tag_text[tag_text.find("(") + 1:tag_text.find(")")]
                width = self.SettingsManager.get_setting_value(
                    "Editor/Image tooltip width")
                height = self.SettingsManager.get_setting_value(
                    "Editor/Image tooltip height")
                QToolTip.showText(
                    display_point,
                    f"<img src='{path}' width='{width}' height='{height}'>",
                    self, QRect(), 5000)
                hide_tooltip = False

        # Hide tooltip if no image or citation is under cursor
        if hide_tooltip:
            QToolTip.hideText()
Example #24
0
def is_empty_line(cursor: QtGui.QTextCursor) -> bool:
    return cursor.block().text() in {"\N{PARAGRAPH SEPARATOR}", "\n", ""}
Example #25
0
class Log(QTextBrowser):
    """Widget displaying output from a Job."""
    def __init__(self, parent=None):
        super(Log, self).__init__(parent)
        self.setOpenLinks(False)
        self.cursor = QTextCursor(self.document())
        self._types = job.ALL
        self._lasttype = None
        self._formats = self.logformats()

    def setMessageTypes(self, types):
        """Set the types of Job output to display.

        types is a constant bitmask from job, like job.STDOUT etc.
        By default job.ALL is used.

        """
        self._types = types

    def messageTypes(self):
        """Return the set message types (job.ALL by default)."""
        return self._types

    def connectJob(self, job):
        """Gives us the output from the Job (past and upcoming)."""
        for msg, type in job.history():
            self.write(msg, type)
        job.output.connect(self.write)

    def textFormat(self, type):
        """Returns a QTextFormat() for the given type."""
        return self._formats[type]

    def write(self, message, type):
        """Writes the given message with the given type to the log.

        The keepScrolledDown context manager is used to scroll the log further
        down if it was scrolled down at that moment.

        If two messages of a different type are written after each other a newline
        is inserted if otherwise the message would continue on the same line.

        """
        if type & self._types:
            with self.keepScrolledDown():
                changed = type != self._lasttype
                self._lasttype = type
                if changed and self.cursor.block().text(
                ) and not message.startswith('\n'):
                    self.cursor.insertText('\n')
                self.writeMessage(message, type)

    def writeMessage(self, message, type):
        """Inserts the given message in the text with the textformat belonging to type."""
        self.cursor.insertText(message, self.textFormat(type))

    @contextlib.contextmanager
    def keepScrolledDown(self):
        """Performs a function, ensuring the log stays scrolled down if it was scrolled down on start."""
        vbar = self.verticalScrollBar()
        scrolleddown = vbar.value() == vbar.maximum()
        try:
            yield
        finally:
            if scrolleddown:
                vbar.setValue(vbar.maximum())

    def logformats(self):
        """Returns a dictionary with QTextCharFormats for the different types of messages.

        Besides the STDOUT, STDERR, NEUTRAL, FAILURE and SUCCESS formats there is also
        a "link" format, that looks basically the same as the output formats, but blueish
        and underlined, to make parts of the output (e.g. filenames) look clickable.

        """
        textColor = QApplication.palette().color(QPalette.WindowText)
        successColor = qutil.addcolor(textColor, 0, 128, 0)  # more green
        failureColor = qutil.addcolor(textColor, 128, 0, 0)  # more red
        linkColor = qutil.addcolor(textColor, 0, 0, 128)  # more blue
        stdoutColor = qutil.addcolor(textColor, 64, 64, 0)  # more purple

        s = QSettings()
        s.beginGroup("log")
        outputFont = QFont(s.value("fontfamily", "monospace", str))
        outputFont.setPointSizeF(s.value("fontsize", 9.0, float))

        output = QTextCharFormat()
        output.setFont(outputFont)
        # enable zooming the log font size
        output.setProperty(QTextFormat.FontSizeAdjustment, 0)

        stdout = QTextCharFormat(output)
        stdout.setForeground(stdoutColor)

        stderr = QTextCharFormat(output)
        link = QTextCharFormat(output)
        link.setForeground(linkColor)
        link.setFontUnderline(True)

        status = QTextCharFormat()
        status.setFontWeight(QFont.Bold)

        neutral = QTextCharFormat(status)

        success = QTextCharFormat(status)
        success.setForeground(successColor)

        failure = QTextCharFormat(status)
        failure.setForeground(failureColor)

        return {
            job.STDOUT: stdout,
            job.STDERR: stderr,
            job.NEUTRAL: neutral,
            job.SUCCESS: success,
            job.FAILURE: failure,
            'link': link,
        }