Ejemplo n.º 1
0
    def resized(self):
        ' Resize images to fit in new view size and adjust all line number references accordingly '
        for v in (self.left, self.right):
            changes = []
            for i, (top, bot, kind) in enumerate(v.changes):
                if top in v.images:
                    img, oldw, oldlines = v.images[top]
                    lines, w = self.get_lines_for_image(img, v)
                    if lines != oldlines:
                        changes.append((i, lines, lines - oldlines, img, w))

            for i, lines, delta, img, w in changes:
                top, bot, kind = v.changes[i]
                c = QTextCursor(v.document().findBlockByNumber(top+1))
                c.beginEditBlock()
                c.movePosition(c.StartOfBlock)
                if delta > 0:
                    for _ in xrange(delta):
                        c.insertBlock()
                else:
                    c.movePosition(c.NextBlock, c.KeepAnchor, -delta)
                    c.removeSelectedText()
                c.endEditBlock()
                v.images[top] = (img, w, lines)
                def mapnum(x):
                    return x if x <= top else x + delta
                lnm = LineNumberMap()
                lnm.max_width = v.line_number_map.max_width
                for x, val in v.line_number_map.iteritems():
                    dict.__setitem__(lnm, mapnum(x), val)
                v.line_number_map = lnm
                v.changes = [(mapnum(t), mapnum(b), k) for t, b, k in v.changes]
                v.headers = [(mapnum(x), name) for x, name in v.headers]
                v.images = OrderedDict((mapnum(x), v) for x, v in v.images.iteritems())
            v.viewport().update()
Ejemplo n.º 2
0
 def set_document(self, doc, doc_name=None):
     old_doc = self.doc
     if old_doc is not None:
         old_doc.contentsChange.disconnect(self.reformat_blocks)
         c = QTextCursor(old_doc)
         c.beginEditBlock()
         blk = old_doc.begin()
         while blk.isValid():
             blk.layout().clearAdditionalFormats()
             blk = blk.next()
         c.endEditBlock()
     self.doc = self.doc_name = None
     if doc is not None:
         self.doc = doc
         self.doc_name = doc_name
         doc.contentsChange.connect(self.reformat_blocks)
         self.rehighlight()
Ejemplo n.º 3
0
 def set_document(self, doc, doc_name=None):
     old_doc = self.doc
     if old_doc is not None:
         old_doc.contentsChange.disconnect(self.reformat_blocks)
         c = QTextCursor(old_doc)
         c.beginEditBlock()
         blk = old_doc.begin()
         while blk.isValid():
             blk.layout().clearAdditionalFormats()
             blk = blk.next()
         c.endEditBlock()
     self.doc = self.doc_name = None
     if doc is not None:
         self.doc = doc
         self.doc_name = doc_name
         doc.contentsChange.connect(self.reformat_blocks)
         self.rehighlight()
Ejemplo n.º 4
0
def apply_smart_comment(editor, opening='/*', closing='*/', line_comment=None):
    doc = editor.document()
    c = QTextCursor(editor.textCursor())
    c.clearSelection()
    before_opening = doc.find(opening, c, QTextDocument.FindFlag.FindBackward | QTextDocument.FindFlag.FindCaseSensitively)
    before_closing = doc.find(closing, c, QTextDocument.FindFlag.FindBackward | QTextDocument.FindFlag.FindCaseSensitively)
    after_opening = doc.find(opening, c, QTextDocument.FindFlag.FindCaseSensitively)
    after_closing = doc.find(closing, c, QTextDocument.FindFlag.FindCaseSensitively)
    in_block_comment = (not before_opening.isNull() and (before_closing.isNull() or before_opening.position() >= before_closing.position())) and \
        (not after_closing.isNull() and (after_opening.isNull() or after_closing.position() <= after_opening.position()))
    if in_block_comment:
        before_opening.removeSelectedText(), after_closing.removeSelectedText()
        return
    c = QTextCursor(editor.textCursor())
    left, right = min(c.position(), c.anchor()), max(c.position(), c.anchor())
    c.beginEditBlock()
    c.setPosition(right), c.insertText(closing)
    c.setPosition(left), c.insertText(opening)
    c.endEditBlock()
Ejemplo n.º 5
0
def apply_smart_comment(editor, opening='/*', closing='*/', line_comment=None):
    doc = editor.document()
    c = QTextCursor(editor.textCursor())
    c.clearSelection()
    before_opening = doc.find(opening, c, doc.FindBackward | doc.FindCaseSensitively)
    before_closing = doc.find(closing, c, doc.FindBackward | doc.FindCaseSensitively)
    after_opening = doc.find(opening, c, doc.FindCaseSensitively)
    after_closing = doc.find(closing, c, doc.FindCaseSensitively)
    in_block_comment = (not before_opening.isNull() and (before_closing.isNull() or before_opening.position() >= before_closing.position())) and \
        (not after_closing.isNull() and (after_opening.isNull() or after_closing.position() <= after_opening.position()))
    if in_block_comment:
        before_opening.removeSelectedText(), after_closing.removeSelectedText()
        return
    c = QTextCursor(editor.textCursor())
    left, right = min(c.position(), c.anchor()), max(c.position(), c.anchor())
    c.beginEditBlock()
    c.setPosition(right), c.insertText(closing)
    c.setPosition(left), c.insertText(opening)
    c.endEditBlock()
Ejemplo n.º 6
0
    def resized(self):
        ' Resize images to fit in new view size and adjust all line number references accordingly '
        for v in (self.left, self.right):
            changes = []
            for i, (top, bot, kind) in enumerate(v.changes):
                if top in v.images:
                    img, oldw, oldlines = v.images[top]
                    lines, w = self.get_lines_for_image(img, v)
                    if lines != oldlines:
                        changes.append((i, lines, lines - oldlines, img, w))

            for i, lines, delta, img, w in changes:
                top, bot, kind = v.changes[i]
                c = QTextCursor(v.document().findBlockByNumber(top + 1))
                c.beginEditBlock()
                c.movePosition(c.StartOfBlock)
                if delta > 0:
                    for _ in xrange(delta):
                        c.insertBlock()
                else:
                    c.movePosition(c.NextBlock, c.KeepAnchor, -delta)
                    c.removeSelectedText()
                c.endEditBlock()
                v.images[top] = (img, w, lines)

                def mapnum(x):
                    return x if x <= top else x + delta

                lnm = LineNumberMap()
                lnm.max_width = v.line_number_map.max_width
                for x, val in v.line_number_map.iteritems():
                    dict.__setitem__(lnm, mapnum(x), val)
                v.line_number_map = lnm
                v.changes = [(mapnum(t), mapnum(b), k)
                             for t, b, k in v.changes]
                v.headers = [(mapnum(x), name) for x, name in v.headers]
                v.images = OrderedDict(
                    (mapnum(x), v) for x, v in v.images.iteritems())
            v.viewport().update()
Ejemplo n.º 7
0
class Doc(QObject):

    changed_status = pyqtSignal(dict)  # for update the status bar
    changed_bold = pyqtSignal(bool)  # for update button "Bold"
    enabled_save = pyqtSignal(bool)  # for update button "Save"

    # -------------------------------------------------------------------------
    def __init__(self, config):
        super(Doc, self).__init__()
        self._text = QTextDocument()
        self._cfg = config
        self._text_edit_cursor = QTextCursor(self._text)
        self._text.setIndentWidth(self._cfg.get("TextEditor/IndentWidth", 24))

        self.set_default_font()

    # -------------------------------------------------------------------------
    @property
    def text(self):
        return self._text

    # -------------------------------------------------------------------------
    def is_modified(self):
        return self._text.isModified()

    # -------------------------------------------------------------------------
    def in_table(self):
        return bool(self._text_edit_cursor.currentTable())

    # -------------------------------------------------------------------------
    def set_default_font(self, set_modified=False):
        font_name = self._cfg.get("TextEditor/Font", "Mono", system=True)
        sz = self._cfg.get("TextEditor/FontSize", 10, system=True)
        font = QFont(font_name, sz)
        self._text.setDefaultFont(font)
        if set_modified:
            self._text.setModified(True)
            self.change(self._text_edit_cursor)

    # -------------------------------------------------------------------------
    def change(self, text_cursor: QTextCursor = None):
        """
        Called (from QTextEdit event) with changed text or position of cursor
        """
        if text_cursor:
            self._text_edit_cursor = text_cursor

        # refresh data on statusbar
        y = self._text_edit_cursor.blockNumber() + 1
        cnt = self._text.lineCount()
        x = self._text_edit_cursor.columnNumber() + 1
        chg = (self.tr("The document is not saved")
               if self._text.isModified() else "")
        xy = f"{y} : {x} [{cnt}]"
        self.changed_status.emit({"left": chg, "right": xy})
        self.enabled_save.emit(self.is_modified())

    # -------------------------------------------------------------------------
    def bold(self):
        """
        Set/unset bold font
        """
        fmt = self._text_edit_cursor.charFormat()
        typ = QFont.Normal if fmt.fontWeight() == QFont.Bold else QFont.Bold
        fmt.setFontWeight(typ)
        self._text_edit_cursor.setCharFormat(fmt)
        self.change(self._text_edit_cursor)
        self.changed_bold.emit(typ == QFont.Bold)

    # -------------------------------------------------------------------------
    def color(self, color):
        fmt = self._text_edit_cursor.charFormat()
        fmt.setForeground(QColor(color))
        self._text_edit_cursor.setCharFormat(fmt)

    # -------------------------------------------------------------------------
    def background_color(self, color):
        fmt = self._text_edit_cursor.charFormat()
        if self.in_table():
            table = self._text_edit_cursor.currentTable()
            cell = table.cellAt(self._text_edit_cursor)
            cell_format = cell.format()
            cell_format.setBackground(QColor(color))
            cell.setFormat(cell_format)
        else:
            fmt.setBackground(QColor(color))
        self._text_edit_cursor.setCharFormat(fmt)

    # -------------------------------------------------------------------------
    def font(self, font):
        fmt = self._text_edit_cursor.charFormat()
        fmt.setFont(font)
        self._text_edit_cursor.setCharFormat(fmt)

    # -------------------------------------------------------------------------
    def font_size(self, font_size):
        fmt = self._text_edit_cursor.charFormat()
        fmt.setFontPointSize(font_size)
        self._text_edit_cursor.setCharFormat(fmt)

    # -------------------------------------------------------------------------
    def hline(self):
        """
        Insert horizontal line
        """
        # Tag HR is not correctly displayed  in QTextview
        cur_char_fmt = self._text_edit_cursor.charFormat()
        cur_block_fmt = self._text_edit_cursor.blockFormat()
        if bool(self._text_edit_cursor.currentTable()):
            self._text_edit_cursor.insertBlock(cur_block_fmt, cur_char_fmt)

        block_fmt = QTextBlockFormat()
        block_fmt.setTopMargin(5)
        block_fmt.setBottomMargin(5)
        block_fmt.setAlignment(Qt.AlignLeft)
        block_fmt.setBackground(QBrush(QColor("#C1C1C1")))

        char_format = QTextCharFormat()
        char_format.setFont(QFont("Arial", 1))

        self._text_edit_cursor.insertBlock(block_fmt, char_format)
        self._text_edit_cursor.insertText(" ")

        self._text_edit_cursor.insertBlock(cur_block_fmt, cur_char_fmt)

        self.change(self._text_edit_cursor)

    # -------------------------------------------------------------------------
    def show_hide_hidden_char(self, show: bool):
        if show:
            # show hidden char
            option = self._text.defaultTextOption()
            option.setFlags(
                option.flags() | QTextOption.ShowTabsAndSpaces
                | QTextOption.ShowLineAndParagraphSeparators
                | QTextOption.AddSpaceForLineAndParagraphSeparators)
            self._text.setDefaultTextOption(option)
        else:
            # remove hidden char
            option = self._text.defaultTextOption()
            stas = ~QTextOption.ShowTabsAndSpaces
            slaps = ~QTextOption.ShowLineAndParagraphSeparators
            asflaps = ~QTextOption.AddSpaceForLineAndParagraphSeparators
            option.setFlags(option.flags() & stas & slaps & asflaps)
            self._text.setDefaultTextOption(option)

    # -------------------------------------------------------------------------
    def blist(self):
        self._text_edit_cursor.insertList(QTextListFormat.ListDisc)

    # -------------------------------------------------------------------------
    def nlist(self):
        self._text_edit_cursor.insertList(QTextListFormat.ListDecimal)

    # -------------------------------------------------------------------------
    def copy_format(self, fmt):
        self._text_edit_cursor.setCharFormat(fmt)

    # -------------------------------------------------------------------------
    def clear_format(self):
        txt = self._text.toPlainText()
        self._text_edit_cursor.beginEditBlock()  # ----  begin -------
        self._text_edit_cursor.select(QTextCursor.Document)
        self._text_edit_cursor.removeSelectedText()
        fmt = QTextBlockFormat()
        self._text_edit_cursor.setBlockFormat(fmt)
        self._text_edit_cursor.insertText(txt)
        self._text_edit_cursor.endEditBlock()  # ----  end ---------

    # -------------------------------------------------------------------------
    def text_align(self, horiz, vert):

        fmt = QTextBlockFormat()
        fmt.setAlignment(horiz)
        self._text_edit_cursor.mergeBlockFormat(fmt)

        if self.in_table():
            table = self._text_edit_cursor.currentTable()
            cell = table.cellAt(self._text_edit_cursor)
            cell_format = cell.format()
            cell_format.setVerticalAlignment(vert)
            cell.setFormat(cell_format)

    # -------------------------------------------------------------------------
    def insert_table(self, table_params):
        fmt = QTextTableFormat()
        fmt.setCellPadding(table_params["padding"])
        fmt.setCellSpacing(table_params["space"])
        return self._text_edit_cursor.insertTable(table_params["rows"],
                                                  table_params["cols"], fmt)

    # -------------------------------------------------------------------------
    def table(self):
        """
        Current table if cursor in table
        """
        return self._text_edit_cursor.currentTable()

    # -------------------------------------------------------------------------
    def cell(self):
        """
        Cell in current table
        """
        return self.table().cellAt(self._text_edit_cursor)

    # -------------------------------------------------------------------------
    def add_row(self):
        self.table().appendRows(1)

    # -------------------------------------------------------------------------
    def add_col(self):
        self.table().appendColumns(1)

    # -------------------------------------------------------------------------
    def del_row(self):
        self.table().removeRows(self.cell().row(), 1)

    # -------------------------------------------------------------------------
    def del_col(self):
        self.table().removeColumns(self.cell().column(), 1)

    # -------------------------------------------------------------------------
    def ins_row(self):
        self.table().insertRows(self.cell().row(), 1)

    # -------------------------------------------------------------------------
    def ins_col(self):
        self.table().insertColumns(self.cell().column(), 1)

    # -------------------------------------------------------------------------
    def merge_cells(self):
        self.table().mergeCells(self._text_edit_cursor)

    # -------------------------------------------------------------------------
    def split_cells(self):
        self.table().splitCell(self.cell().row(), self.cell().column(), 1, 1)

    # -------------------------------------------------------------------------
    def replace(self, replace_text):
        self._text_edit_cursor.insertText(replace_text)

    # -------------------------------------------------------------------------
    def ins_date(self):
        self._text_edit_cursor.insertText(
            datetime.datetime.now().strftime("%d.%m.%Y "))

    # -------------------------------------------------------------------------
    def ins_time(self):
        self._text_edit_cursor.insertText(
            datetime.datetime.now().strftime("%d.%m.%Y %H:%M:%S "))

    # -------------------------------------------------------------------------
    def in_image(self):
        return self._text_edit_cursor.charFormat().isImageFormat()

    # -------------------------------------------------------------------------
    def ins_image(self, image, fmt, width, height, insert_space=True):
        bytes_ = QByteArray()
        buffer = QBuffer(bytes_)
        buffer.open(QIODevice.WriteOnly)
        image.save(buffer, fmt)
        buffer.close()

        base64 = bytes_.toBase64().data().decode(encoding="utf-8")

        s = (f'<img width="{width}" height="{height}" '
             f'src="data:image/{fmt};base64,{base64}"')

        self._text_edit_cursor.insertHtml(s)

        if insert_space:
            self._text_edit_cursor.insertText(" ")

    # -------------------------------------------------------------------------
    @staticmethod
    def get_image(html):
        image, width, height, fmt = None, -1, -1, "png"
        if "<img" in html:
            raw = html[html.index("<img"):].split(">")[0].split('"')
            for i, r in enumerate(raw):
                if "base64" in r:
                    img_txt = r.split(",")[-1].encode(encoding="utf-8")
                    image = QPixmap()
                    image.width()
                    image.height()
                    image.loadFromData(QByteArray.fromBase64(img_txt))
                    fmt = r.split("image/")[1].split(";")[0]
                if "width" in r and i + 1 < len(raw):
                    width = int(raw[i + 1])
                if "height" in r and i + 1 < len(raw):
                    height = int(raw[i + 1])
        return image, width, height, fmt

    # -------------------------------------------------------------------------
    def save(self, save_proc):
        if self._text.isModified():
            self._text.setModified(False)
            if self._cfg.get("TextEditor/PlainText", 0):
                txt = str(self._text.toPlainText())
                if self._cfg.get("TextEditor/ReplaceTabWithSpace", 0):
                    cnt = self._cfg.get("TextEditor/CountSpaceInTab", 1)
                    txt = txt.replace("\t", " " * cnt)
                res = save_proc(txt)
            else:
                res = save_proc(str(self._text.toHtml(encoding=QByteArray())))
            if res is not None:
                self._text.setModified(True)

        self.change(self._text_edit_cursor)

    # -------------------------------------------------------------------------
    def load(self, load_proc):
        self._text.clear()
        if self._cfg.get("TextEditor/PlainText", 0):
            self._text.setPlainText(load_proc())
        else:
            self._text.setHtml(load_proc())
        self._text.setModified(False)

    # -------------------------------------------------------------------------
    def get_text(self):
        if self._cfg.get("TextEditor/PlainText", 0):
            return str(self._text.toPlainText())
        else:
            return str(self._text.toHtml(encoding=QByteArray()))

    # -------------------------------------------------------------------------
    def is_empty(self):
        return not bool(len(self._text.toPlainText().strip()))