Example #1
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 #2
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 #3
0
    def unindent(self):
        cursor = self._neditor.textCursor()
        start_block = self._neditor.document().findBlock(
            cursor.selectionStart())
        end_block = self._neditor.document().findBlock(cursor.selectionEnd())

        position = cursor.position()
        if position == 0:
            return

        if start_block != end_block:
            # Unindent multiple lines
            block = start_block
            stop_block = end_block.next()
            while block != stop_block:
                indentation = self.__block_indentation(block)
                if indentation.endswith(self.text()):
                    cursor = QTextCursor(block)
                    cursor.setPosition(block.position() + len(indentation))
                    cursor.setPosition(cursor.position() - self.WIDTH,
                                       QTextCursor.KeepAnchor)
                    cursor.removeSelectedText()
                block = block.next()
        else:
            # Unindent one line
            indentation = self.__block_indentation(start_block)
            cursor = QTextCursor(start_block)
            cursor.setPosition(start_block.position() + len(indentation))
            cursor.setPosition(cursor.position() - self.WIDTH,
                               QTextCursor.KeepAnchor)
            cursor.removeSelectedText()
Example #4
0
    def unindent(self):
        cursor = self._neditor.textCursor()
        start_block = self._neditor.document().findBlock(
            cursor.selectionStart())
        end_block = self._neditor.document().findBlock(
            cursor.selectionEnd())

        position = cursor.position()
        if position == 0:
            return

        if start_block != end_block:
            # Unindent multiple lines
            block = start_block
            stop_block = end_block.next()
            while block != stop_block:
                indentation = self.__block_indentation(block)
                if indentation.endswith(self.text()):
                    cursor = QTextCursor(block)
                    cursor.setPosition(block.position() + len(indentation))
                    cursor.setPosition(cursor.position() - self.WIDTH,
                                       QTextCursor.KeepAnchor)
                    cursor.removeSelectedText()
                block = block.next()
        else:
            # Unindent one line
            indentation = self.__block_indentation(start_block)
            cursor = QTextCursor(start_block)
            cursor.setPosition(start_block.position() + len(indentation))
            cursor.setPosition(cursor.position() - self.WIDTH,
                               QTextCursor.KeepAnchor)
            cursor.removeSelectedText()
Example #5
0
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
Example #6
0
 def comentar_linha(self):
     """
     comenta a linha
     :return:
         None
     """
     editor = self.widget_abas.widget(self.widget_abas.currentIndex())
     caminho = editor.get_caminho()
     # Testa se a aba eh a de boas vindas
     if caminho == 0:
         return
     cursor_atual = editor.textCursor()
     posicao = cursor_atual.position()
     bloco_atual = cursor_atual.block()
     cursor = QTextCursor(bloco_atual)
     editor.setTextCursor(cursor)
     texto = bloco_atual.text()
     if texto.strip().startswith("//"):
         cursor = self.selecionar_texto(cursor, '/', cursor.position(), 2)
         cursor.removeSelectedText()
         cursor.setPosition(posicao - 2)
         editor.setTextCursor(cursor)
     else:
         editor.insertPlainText('//')
         cursor.setPosition(posicao + 2)
         editor.setTextCursor(cursor)
Example #7
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 #8
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 #9
0
    def _append_log(self, record):
        """Internal method to insert a displayed record into the underlying
        :class:`QPlainTextEdit`."""
        self.infobar.add_record(record)
        self._rec_lines.append(record.text.count('\n') + 1)

        # NOTE: For some reason, we must use `setPosition` in order to
        # guarantee a absolute, fixed selection (at least on linux). It seems
        # almost if `movePosition(End)` will be re-evaluated at any time the
        # cursor/selection is used and therefore always point to the end of
        # the document.

        cursor = QTextCursor(self.textctrl.document())
        cursor.movePosition(QTextCursor.End)
        pos0 = cursor.position()
        cursor.insertText(record.text + '\n')
        pos1 = cursor.position()

        cursor = QTextCursor(self.textctrl.document())
        cursor.setPosition(pos0)
        cursor.setPosition(pos1, QTextCursor.KeepAnchor)

        selection = QTextEdit.ExtraSelection()
        selection.format = self.formats.get(record.domain, self.default_format)
        selection.cursor = cursor

        selections = self.textctrl.extraSelections()
        if selections:
            # Force the previous selection to end at the current block.
            # Without this, all previous selections are be updated to span
            # over the rest of the document, which dramatically impacts
            # performance because it means that all selections need to be
            # considered even if showing only the end of the document.
            selections[-1].cursor.setPosition(pos0, QTextCursor.KeepAnchor)
        selections.append(selection)
        self.textctrl.setExtraSelections(selections[-self.maxlen:])
        self.textctrl.ensureCursorVisible()

        if self.maxlen:
            # setMaximumBlockCount() must *not* be in effect while inserting
            # the text, because it will mess with the cursor positions and
            # make it nearly impossible to create a proper ExtraSelection!
            num_lines = sum(self._rec_lines)
            self.textctrl.setMaximumBlockCount(num_lines + 1)
            self.textctrl.setMaximumBlockCount(0)
Example #10
0
 def change_cursor_position(self, value):  # изменение положения курсора в текстовом поле
     if pd.notnull(value):  # если указано перемещение курсора
         place = QTextCursor(self.text.textCursor())  # запоминаем копию текущего курсора
         place.setPosition(place.position() + int(value))  # изменяем позицию курсора на value
         self.text.setTextCursor(place)  # устанавливаем новый курсор
         if value < 0:  # запоминаем переход внутрь скобок
             self.inside = -value
         else:
             self.inside = 0
Example #11
0
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)
Example #12
0
 def start_complete_process(self, event: QtGui.QKeyEvent,
                            tc: QtGui.QTextCursor):
     if (event.text().isalnum() and
             not self.document().characterAt(tc.position() + 1).isalnum()):
         tc.movePosition(QtGui.QTextCursor.Left,
                         QtGui.QTextCursor.MoveAnchor, 1)
         tc.select(QtGui.QTextCursor.WordUnderCursor)
         if len(tc.selectedText()) <= 1:
             return
         self.completer.setCompletionPrefix(tc.selectedText())
         popup = self.completer.popup()
         popup.setCurrentIndex(self.completer.completionModel().index(0, 0))
         cr = self.cursorRect()
         cr.setWidth(
             self.completer.popup().sizeHintForColumn(0) +
             self.completer.popup().verticalScrollBar().sizeHint().width())
         self.completer.complete(cr)
     else:
         self.completer.popup().hide()
Example #13
0
 def read_pages(self, section, value, version):
     valid_rule = {C.FolioRuleAdd1,C.FolioRuleSet,C.FolioRuleSkip}
     valid_fmt = {C.FolioFormatArabic,C.FolioFormatLCRom,C.FolioFormatUCRom,C.FolioFormatSame}
     last_fmt = C.FolioFormatArabic
     last_pos = -1 # ensure monotonically increasing positions
     self.clear()
     if not isinstance(value, list) :
         pagedata_logger.error('{} metadata must be a list of lists, ignoring it'.format(C.MD_PT))
         return
     for row in value:
         try:
             # throws exception if not exactly 6 items
             [P, fn, pfrs, rule, fmt, nbr] = row
             P = int(P) # exception if not valid numeric
             tc = QTextCursor(self.document)
             tc.setPosition(P) # no effect if P negative or too big
             if (tc.position() != P) or (P < last_pos) :
                 raise ValueError("Invalid document position")
             last_pos = P
             rule = int(rule) # exceptions if not numeric
             fmt = int(fmt)
             nbr = int(nbr)
             if not ( (rule in valid_rule) and (fmt in valid_fmt) and (nbr >= 0) ) :
                 raise ValueError("Invalid folio info")
             # All looks good, do permanent things
             self.cursor_list.append(tc)
             self.filename_list.append(fn)
             self.folio_list.append( [rule, fmt, nbr] )
             if fmt != C.FolioFormatSame :
                 self.explicit_formats.add(len(self.folio_list)-1)
             # get list of proofer strings, dropping opening null string
             # due to leading backslash. If it is only '\\' the result
             # is the list [''].
             plist = pfrs.split('\\')[1:]
             self.proofers_list.append(plist)
         except Exception as thing:
             pagedata_logger.error('Invalid row of page metadata: '+thing.args[0])
             pagedata_logger.error('  ignoring {}'.format(row))
     if 0 < len(self.filename_list) :
         self._active = True
         self._add_stopper()
         self.PagesUpdated.emit()
Example #14
0
    def insert_snippet(self):
        cursor = self._editor.word_under_cursor()
        prefix = cursor.selectedText()

        # pos = cursor.position()
        # cursor.movePosition(QTextCursor.StartOfWord)
        # start_pos = cursor.position()
        # cursor.setPosition(pos, QTextCursor.KeepAnchor)
        copy = QTextCursor(cursor)
        copy.movePosition(QTextCursor.StartOfWord)
        start = copy.position()

        self._current_snippet = self.snippets.get(prefix)
        if self._current_snippet is not None:
            self.active = True
            cursor.removeSelectedText()
            cursor.insertText(self._current_snippet.body)
            self._highlight(start)
        else:
            self.active = False
Example #15
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 #16
0
 def read_pages(self, stream, sentinel, vers, parm):
     valid_rule = {C.FolioRuleAdd1,C.FolioRuleSet,C.FolioRuleSkip}
     valid_fmt = {C.FolioFormatArabic,C.FolioFormatLCRom,C.FolioFormatUCRom,C.FolioFormatSame}
     last_fmt = C.FolioFormatArabic
     last_pos = -1 # ensure monotonically increasing positions
     for line in metadata.read_to(stream, sentinel):
         try:
             # throws exception if not exactly 6 items
             (P, fn, pfrs, rule, fmt, nbr) = line.split(' ')
             P = int(P) # throws exception if not valid int string
             tc = QTextCursor(self.document)
             tc.setPosition(P) # no effect if P negative or too big
             if (tc.position() != P) or (P < last_pos) :
                 raise ValueError("Invalid document position")
             last_pos = P
             rule = int(rule)
             fmt = int(fmt)
             nbr = int(nbr)
             if not ( (rule in valid_rule) and (fmt in valid_fmt) and (nbr >= 0) ) :
                 raise ValueError("Invalid folio info")
             # All looks good, do permanent things
             self.cursor_list.append(tc)
             self.filename_list.append(fn)
             self.folio_list.append( [rule, fmt, nbr] )
             if fmt != C.FolioFormatSame :
                 self.explicit_formats.add(len(self.folio_list)-1)
             # get list of proofer strings, dropping opening null string
             # due to leading backslash. If it is only '\\' the result
             # is the list [''].
             plist = pfrs.replace(C.UNICODE_EN_SPACE,' ').split('\\')[1:]
             self.proofers_list.append(plist)
         except Exception as thing:
             pagedata_logger.error('invalid line of page metadata: '+thing.args[0])
             pagedata_logger.error('  ignoring "'+line+'"')
     if 0 < len(self.filename_list) :
         self._active = True
         self._add_stopper()
Example #17
0
class ParsingInput(QWidget):
    edit_complete = pyqtSignal(Task)

    def __init__(self):
        super().__init__(flags=Qt.WindowTitleHint | Qt.WindowCloseButtonHint
                         | Qt.WindowMinimizeButtonHint
                         | Qt.WindowMaximizeButtonHint)
        self.setFocusPolicy(Qt.StrongFocus)
        self.setMinimumSize(28, 28)
        self.setCursor(Qt.IBeamCursor)
        self.text = []
        self.character_offsets = [5]
        self.parsed_blocks = []
        self.qt_text = QTextDocument("")
        self.cursor = QTextCursor(self.qt_text)
        self.cursor.setPosition(0)
        self.cursor_visible = True
        self.cursor_timer = QTimer()
        self.cursor_timer.timeout.connect(self.blink_cursor)
        self.set_cursor_visible()

    def paintEvent(self, e):
        qp = QPainter()
        qp.begin(self)
        qp.setRenderHint(QPainter.Antialiasing)
        qp.setRenderHint(QPainter.HighQualityAntialiasing)
        self.draw(qp)
        qp.end()

    def draw(self, qp):
        size = self.size()
        w = size.width()
        h = size.height()
        font = QFont('Sans-Serif', 12, QFont.Normal)
        qp.setFont(font)
        qp.setPen(QColor(128, 128, 128))
        qp.setBrush(QColor(255, 255, 255))
        qp.drawRoundedRect(0, 0, w, h, 5, 5)
        qp.setPen(QColor(0, 0, 0))
        font_metrics = qp.fontMetrics()
        c_start_position = 5
        cursor_pixel_position = c_start_position
        self.character_offsets = [cursor_pixel_position]
        for i, c in enumerate(self.text):
            start_of_parsed_block = False
            end_of_parsed_block = False
            inside_parsed_block = False
            for start, end in self.parsed_blocks:
                if start == i:
                    block_width = 4
                    for char in self.text[start:end]:
                        block_width += font_metrics.width(char["char"])
                    qp.setPen(QColor(0, 0, 0))
                    qp.setBrush(QColor(0, 0, 0))
                    qp.drawRoundedRect(c_start_position + 2, 4, block_width,
                                       20, 2, 2)
                    start_of_parsed_block = True
                if end == i:
                    end_of_parsed_block = True
                if start <= i < end:
                    inside_parsed_block = True
            if end_of_parsed_block:
                c_start_position += 4
            if start_of_parsed_block:
                c_start_position += 4
            c_width = font_metrics.width(c["char"])
            if self.cursor.hasSelection() and self.cursor.selectionStart(
            ) <= i < self.cursor.selectionEnd():
                if start_of_parsed_block or end_of_parsed_block:
                    sel_rect_start = c_start_position - 2
                    sel_rect_width = font_metrics.width(c["char"]) + 4
                else:
                    sel_rect_start = c_start_position
                    sel_rect_width = font_metrics.width(c["char"])
                if i != self.cursor.selectionStart():
                    sel_rect_start += 2
                if i + 1 != self.cursor.selectionEnd():
                    sel_rect_width += 2
                else:
                    sel_rect_width -= 2
                if not inside_parsed_block:
                    selection_color = QColor(100, 100, 255)
                else:
                    selection_color = QColor(60, 60, 168)
                qp.setBrush(selection_color)
                qp.setPen(selection_color)
                qp.drawRect(sel_rect_start, 6, sel_rect_width, 16)
            qp.setPen(
                QColor(inside_parsed_block * 255, inside_parsed_block * 255,
                       inside_parsed_block * 255))
            qp.setBrush(
                QColor(255 - inside_parsed_block * 255,
                       255 - inside_parsed_block * 255,
                       255 - inside_parsed_block * 255))
            qp.drawText(c_start_position, 20, c["char"])
            c_start_position += c_width
            if i == self.cursor.position() - 1:
                cursor_pixel_position = c_start_position
            self.character_offsets.append(c_start_position)
        if self.hasFocus() and self.cursor_visible:
            qp.setPen(QColor(0, 0, 0))
            for start, end in self.parsed_blocks:
                if start < self.cursor.position() <= end:
                    qp.setPen(QColor(255, 255, 255))
            qp.drawLine(cursor_pixel_position, 4, cursor_pixel_position,
                        28 - 4)

    def keyPressEvent(self, event):
        key = event.key()
        if key == Qt.Key_Left:
            if event.modifiers() & Qt.CTRL:
                new_pos = max(
                    0,
                    self.get_text_str().rfind(
                        " ", 0, max(0,
                                    self.cursor.position() - 1)))
            else:
                new_pos = self.cursor.position() - 1
            if event.modifiers() & Qt.SHIFT:
                if self.cursor.hasSelection():
                    self.cursor.setPosition(max(0, new_pos),
                                            mode=QTextCursor.KeepAnchor)
                else:
                    self.cursor.setPosition(self.cursor.position(),
                                            mode=QTextCursor.MoveAnchor)
                    self.cursor.setPosition(max(0, new_pos),
                                            mode=QTextCursor.KeepAnchor)
            else:
                self.cursor.setPosition(max(0, new_pos))
            self.set_cursor_visible()
            self.update()
        elif key == Qt.Key_Right:
            if event.modifiers() & Qt.CTRL:
                next_space = self.get_text_str().find(
                    " ",
                    self.cursor.position() + 1, len(self.text))
                if next_space < 0:
                    new_pos = len(self.text)
                else:
                    new_pos = next_space
            else:
                new_pos = self.cursor.position() + 1
            if event.modifiers() & Qt.SHIFT:
                if self.cursor.hasSelection():
                    self.cursor.setPosition(max(0, new_pos),
                                            mode=QTextCursor.KeepAnchor)
                else:
                    self.cursor.setPosition(self.cursor.position(),
                                            mode=QTextCursor.MoveAnchor)
                    self.cursor.setPosition(max(0, new_pos),
                                            mode=QTextCursor.KeepAnchor)
            else:
                self.cursor.setPosition(min(len(self.text), new_pos))
            self.set_cursor_visible()
            self.update()
        elif key == Qt.Key_Home:
            if event.modifiers() & Qt.SHIFT:
                if self.cursor.hasSelection():
                    self.cursor.setPosition(0, mode=QTextCursor.KeepAnchor)
                else:
                    self.cursor.setPosition(self.cursor.position(),
                                            mode=QTextCursor.MoveAnchor)
                    self.cursor.setPosition(0, mode=QTextCursor.KeepAnchor)
            else:
                self.cursor.setPosition(0)
            self.set_cursor_visible()
            self.update()
        elif key == Qt.Key_End:
            if event.modifiers() & Qt.SHIFT:
                if self.cursor.hasSelection():
                    self.cursor.setPosition(len(self.text),
                                            mode=QTextCursor.KeepAnchor)
                else:
                    self.cursor.setPosition(self.cursor.position(),
                                            mode=QTextCursor.MoveAnchor)
                    self.cursor.setPosition(len(self.text),
                                            mode=QTextCursor.KeepAnchor)
            else:
                self.cursor.setPosition(len(self.text))
            self.set_cursor_visible()
            self.update()
        elif key == Qt.Key_Escape:
            self.cursor.clearSelection()
            self.set_cursor_visible()
            self.update()
        elif key == Qt.Key_Backspace:
            something_changed = False
            if self.cursor.hasSelection():
                self.delete_selected_text()
                something_changed = True
            else:
                if self.cursor.position() > 0:
                    self.text.pop(self.cursor.position() - 1)
                    self.cursor.setPosition(self.cursor.position() - 1)
                    something_changed = True
            if something_changed:
                self.parse_text()
                self.set_cursor_visible()
                self.update()
        elif key == Qt.Key_Delete:
            something_changed = False
            if self.cursor.hasSelection():
                self.delete_selected_text()
                something_changed = True
            else:
                if self.cursor.position() < len(self.text):
                    self.text.pop(self.cursor.position())
                    something_changed = True
            if something_changed:
                self.parse_text()
                self.set_cursor_visible()
                self.update()
        elif key == Qt.Key_C and event.modifiers() & Qt.CTRL:
            selected_text = self.get_selected_text()
            print("Selected Text: {}".format(selected_text))
            if len(selected_text) > 0:
                QApplication.clipboard().setText(selected_text)
        elif key == Qt.Key_V and event.modifiers() & Qt.CTRL:
            self.delete_selected_text()
            clipboard_text = QApplication.clipboard().text()
            self.text = self.text[:self.cursor.position()] + \
                [{"char": c, "parse": True} for c in clipboard_text] + \
                self.text[self.cursor.position():]
            self.parse_text()
            self.cursor.setPosition(self.cursor.position() +
                                    len(clipboard_text))
            self.set_cursor_visible()
            self.update()
        elif key == Qt.Key_Enter or key == Qt.Key_Return:
            self.edit_complete.emit(self.get_task())
            self.text = []
            self.cursor.setPosition(0)
            self.parse_text()
            self.set_cursor_visible()
            self.update()
        else:
            if len(event.text()) > 0:
                typed_text = ""
                for c in event.text():
                    if c in ALL_CHARACTERS:
                        typed_text += c
                if len(typed_text):
                    self.delete_selected_text()
                    self.text = self.text[:self.cursor.position()] + \
                        [{"char": c, "parse": True} for c in typed_text] + \
                        self.text[self.cursor.position():]
                    self.parse_text()
                    self.cursor.setPosition(self.cursor.position() +
                                            len(typed_text))
                    self.set_cursor_visible()
                    self.update()

    def get_selected_text(self):
        if self.cursor.hasSelection():
            return "".join([
                c["char"] for c in self.text[self.cursor.selectionStart():self.
                                             cursor.selectionEnd()]
            ])
        else:
            return ""

    def delete_selected_text(self):
        if self.cursor.hasSelection():
            start_pos = self.cursor.selectionStart()
            self.text = self.text[:start_pos] + self.text[self.cursor.
                                                          selectionEnd():]
            self.cursor.clearSelection()
            self.cursor.setPosition(start_pos)

    def get_min_dist_pos(self, click_x):
        min_dist_pos = 0
        min_dist = abs(self.character_offsets[0] - click_x)
        for i, offset in enumerate(self.character_offsets[1:]):
            d = abs(offset - click_x)
            if d < min_dist:
                min_dist = d
                min_dist_pos = i + 1
        return min_dist_pos

    def mousePressEvent(self, event):
        self.cursor.select(QTextCursor.BlockUnderCursor)
        self.cursor.setPosition(self.get_min_dist_pos(event.pos().x()),
                                mode=QTextCursor.MoveAnchor)
        self.update()

    def mouseMoveEvent(self, event):
        self.cursor.setPosition(self.get_min_dist_pos(event.pos().x()),
                                mode=QTextCursor.KeepAnchor)
        self.update()

    def mouseReleaseEvent(self, event):
        self.cursor.setPosition(self.get_min_dist_pos(event.pos().x()),
                                mode=QTextCursor.KeepAnchor)
        if self.cursor.selectionStart() == self.cursor.selectionEnd():
            self.cursor.clearSelection()
        self.update()

    def blink_cursor(self):
        self.cursor_visible = not self.cursor_visible
        self.update()

    def set_cursor_visible(self):
        self.cursor_visible = True
        self.cursor_timer.stop()
        self.cursor_timer.start(500)

    def get_text_str(self):
        return "".join([c["char"] for c in self.text])

    @staticmethod
    def generate_date(day, hour=None, minute=None):
        fuzziness = timedelta(days=1)
        date = datetime.now()
        if day in ["Morgen", "Tomorrow"]:
            date = datetime.now() + timedelta(days=1)
        try:
            if hour is not None and 0 <= int(hour) <= 24:
                if minute is not None and 0 <= int(minute) < 60:
                    date = datetime(year=date.year,
                                    month=date.month,
                                    day=date.day,
                                    hour=int(hour),
                                    minute=int(minute))
                else:
                    date = datetime(year=date.year,
                                    month=date.month,
                                    day=date.day,
                                    hour=int(hour),
                                    minute=0)
                fuzziness = 0
        except ValueError:
            print("Unexpected Values for hour ({}) and minute ({}). Ignoring.".
                  format(hour, minute))
        return date, fuzziness

    def parse_text(self):
        regexes = [
            (re.compile(r"(Morgen|Tomorrow) (?:at |um )?(\d\d?):(\d\d?)"),
             self.generate_date),
            (re.compile(r"(Morgen|Tomorrow)"), self.generate_date)
        ]
        text = self.get_text_str()
        saved_cursor_pos = self.cursor.position()
        self.qt_text.setPlainText(text)
        self.cursor.setPosition(saved_cursor_pos)
        self.parsed_blocks = []
        for regex, generator in regexes:
            matches = regex.finditer(text)
            for match in matches:
                if match.end() > 0 and self.text[match.end() - 1]["parse"]:
                    print(match.groups())
                    print(generator(*match.groups()))
                    text = text[:match.start()] + " " * (
                        match.end() - match.start()) + text[match.end():]
                    self.parsed_blocks.append((match.start(), match.end()))

    def get_task(self):
        new_task = Task()
        new_task.text = self.get_text_str()
        return new_task
Example #18
0
def character_at(cursor: QtGui.QTextCursor) -> str:
    return cursor.document().characterAt(cursor.position())
Example #19
0
 def move_to_next_word(self, doc: QTextDocument, cursor: QTextCursor):
     """Moves cursor to next word"""
     while not cursor.isNull() and not cursor.atEnd():
         if doc.characterAt(cursor.position()) not in self.ignored_chars:
             return
         cursor.movePosition(QTextCursor.NextCharacter)
Example #20
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 #21
0
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