Example #1
0
 def test_overlap(self):
     test_str = 'This is something, string, string.\nparagraph, paragraph!\n    method?'
     test_fmt = [(0, 2, 1), (0, 2, 2), (0, 10, 3), (5, 15, 4), (34, 3, 5)]
     # output formats may be duplicated, because Qt store format this way
     true_result = [
         (0, 2, 1), (0, 2, 2),
         (0, 2, 3), (2, 3, 3), (5, 5, 3),  # from (0, 10, 3), broken into three parts
         (5, 5, 4), (10, 10, 4),  # from (5, 15, 4)
         (35, 2, 5)  # 34 is \n
     ]
     doc = NTextDocument()
     doc.setText(test_str, test_fmt)
     result = NTextDocument.getFormats(doc)
     self.assertEqual(true_result, result)
Example #2
0
 def test_overlap(self):
     test_str = 'This is something, string, string.\nparagraph, paragraph!\n    method?'
     test_fmt = [(0, 2, 1), (0, 2, 2), (0, 10, 3), (5, 15, 4), (34, 3, 5)]
     # output formats may be duplicated, because Qt store format this way
     true_result = [
         (0, 2, 1),
         (0, 2, 2),
         (0, 2, 3),
         (2, 3, 3),
         (5, 5, 3),  # from (0, 10, 3), broken into three parts
         (5, 5, 4),
         (10, 10, 4),  # from (5, 15, 4)
         (35, 2, 5)  # 34 is \n
     ]
     doc = NTextDocument()
     doc.setText(test_str, test_fmt)
     result = NTextDocument.getFormats(doc)
     self.assertEqual(true_result, result)
Example #3
0
class NTextEdit(QTextEdit, TextFormatter):
    """The widget used to edit diary contents in Editor window."""
    # spaces that auto-indent can recognize
    SPACE_KINDS = (' ', '\u3000')  # full width space U+3000

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._doc = NTextDocument(self)
        self.setDocument(self._doc)
        # remove highlight color's alpha to avoid alpha loss in copy&paste.
        # NTextDocument should use this color too.
        hl, bg = self.HlColor, self.palette().base().color()
        fac = hl.alpha() / 255
        self.HlColor = QColor(round(hl.red()*fac + bg.red()*(1-fac)),
                              round(hl.green()*fac + bg.green()*(1-fac)),
                              round(hl.blue()*fac + bg.blue()*(1-fac)))

        self.autoIndent = False
        # used by tab indent shortcut
        if QLocale().language() in (QLocale.Chinese, QLocale.Japanese):
            self._indent = '  '   # 2 full width spaces
        else:
            self._indent = '    '  # 4 spaces
        # setup format menu
        onHLAct = lambda: super(NTextEdit, self).setHL(self.hlAct.isChecked())
        onBDAct = lambda: super(NTextEdit, self).setBD(self.bdAct.isChecked())
        onSOAct = lambda: super(NTextEdit, self).setSO(self.soAct.isChecked())
        onULAct = lambda: super(NTextEdit, self).setUL(self.ulAct.isChecked())
        onItaAct = lambda: super(NTextEdit, self).setIta(self.itaAct.isChecked())

        self.fmtMenu = QMenu(self.tr('Format'), self)
        # shortcuts of format actions only used to display shortcut-hint in menu
        self.hlAct = QAction(makeQIcon(':/menu/highlight.png'), self.tr('Highlight'),
                             self, triggered=onHLAct,
                             shortcut=QKeySequence('Ctrl+H'))
        self.bdAct = QAction(makeQIcon(':/menu/bold.png'), self.tr('Bold'),
                             self, triggered=onBDAct,
                             shortcut=QKeySequence.Bold)
        self.soAct = QAction(makeQIcon(':/menu/strikeout.png'), self.tr('Strike out'),
                             self, triggered=onSOAct,
                             shortcut=QKeySequence('Ctrl+T'))
        self.ulAct = QAction(makeQIcon(':/menu/underline.png'), self.tr('Underline'),
                             self, triggered=onULAct,
                             shortcut=QKeySequence.Underline)
        self.itaAct = QAction(makeQIcon(':/menu/italic.png'), self.tr('Italic'),
                              self, triggered=onItaAct,
                              shortcut=QKeySequence.Italic)
        self.clrAct = QAction(self.tr('Clear format'), self,
                              shortcut=QKeySequence('Ctrl+D'),
                              triggered=self.clearFormat)
        self.acts = (self.hlAct, self.bdAct, self.soAct, self.ulAct,
                     self.itaAct)  # excluding uncheckable clrAct
        for a in self.acts:
            self.fmtMenu.addAction(a)
            a.setCheckable(True)
        self.fmtMenu.addSeparator()
        self.addAction(self.clrAct)
        self.fmtMenu.addAction(self.clrAct)
        self.key2act = {
            Qt.Key_H: self.hlAct, Qt.Key_B: self.bdAct, Qt.Key_T: self.soAct,
            Qt.Key_U: self.ulAct, Qt.Key_I: self.itaAct}

    def contextMenuEvent(self, event):
        menu = self.createStandardContextMenu()
        setStdEditMenuIcons(menu)

        if not self.isReadOnly():
            if self.textCursor().hasSelection():
                self._setFmtActs()
                self.fmtMenu.setEnabled(True)
            else:
                self.fmtMenu.setEnabled(False)
            before = menu.actions()[2]
            menu.insertSeparator(before)
            menu.insertMenu(before, self.fmtMenu)

        menu.exec_(event.globalPos())
        menu.deleteLater()

    def keyPressEvent(self, event):
        if self.isReadOnly():
            return super().keyPressEvent(event)

        if event.modifiers() == Qt.ControlModifier and event.key() in self.key2act:
            # set actions before calling format methods
            self._setFmtActs()
            self.key2act[event.key()].trigger()
        elif event.key() == Qt.Key_Tab:
            # will not receive event if tabChangesFocus is True
            self.textCursor().insertText(self._indent)
        elif event.key() == Qt.Key_Return and self.autoIndent:
            # auto-indent support
            para = self.textCursor().block().text()
            if len(para) > 0 and para[0] in NTextEdit.SPACE_KINDS:
                space, spaceCount = para[0], 1
                for c in para[1:]:
                    if c != space: break
                    spaceCount += 1
                super().keyPressEvent(event)
                self.textCursor().insertText(space * spaceCount)
            else:
                super().keyPressEvent(event)
        else:
            super().keyPressEvent(event)

    def insertFromMimeData(self, source):
        """Disable some unsupported types"""
        self.insertHtml(source.html() or source.text())

    def setRichText(self, text, formats):
        self._doc.setHlColor(self.HlColor)
        self._doc.setText(text, formats)

    def setAutoIndent(self, enabled):
        assert isinstance(enabled, (bool, int))
        self.autoIndent = enabled

    def getRichText(self):
        # self.document() will return QTextDocument, not NTextDocument
        return self.toPlainText(), self._doc.getFormats()

    def _setFmtActs(self):
        """Check formats in current selection and check or uncheck actions"""
        fmts = [QTextFormat.BackgroundBrush, QTextFormat.FontWeight,
                QTextFormat.FontStrikeOut,
                QTextFormat.TextUnderlineStyle, QTextFormat.FontItalic]

        cur = self.textCursor()
        start, end = cur.anchor(), cur.position()
        if start > end:
            start, end = end, start
        results = [True] * 5
        for pos in range(end, start, -1):
            cur.setPosition(pos)
            charFmt = cur.charFormat()
            for i, f in enumerate(fmts):
                if results[i] and not charFmt.hasProperty(f):
                    results[i] = False
            if not any(results): break
        for i, c in enumerate(results):
            self.acts[i].setChecked(c)

    def clearFormat(self):
        fmt = QTextCharFormat()
        self.textCursor().setCharFormat(fmt)