Example #1
0
 def setLine(self, line):
     cursor = QTextCursor(self.document())
     cursor.movePosition(QTextCursor.End)
     cursor.setPosition(self.newPromptPos, QTextCursor.KeepAnchor)
     cursor.removeSelectedText()
     cursor.insertText(line)
     self.setTextCursor(cursor)
Example #2
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 #3
0
    def add_debug_message(self, message):
        self.text_debug.append(message)

        while self.text_debug.document().blockCount() > 1000:
            cursor = QTextCursor(self.text_debug.document().begin())
            cursor.select(QTextCursor.BlockUnderCursor)
            cursor.movePosition(QTextCursor.Right, QTextCursor.KeepAnchor)
            cursor.removeSelectedText()

        if self.checkbox_debug_auto_scroll.isChecked():
            self.text_debug.verticalScrollBar().setValue(self.text_debug.verticalScrollBar().maximum())
Example #4
0
 def _removeBlock(blockIndex):
     block = self._doc.findBlockByNumber(blockIndex)
     if block.next().isValid():  # not the last
         cursor = QTextCursor(block)
         cursor.movePosition(QTextCursor.NextBlock, QTextCursor.KeepAnchor)
     elif block.previous().isValid():  # the last, not the first
         cursor = QTextCursor(block.previous())
         cursor.movePosition(QTextCursor.EndOfBlock)
         cursor.movePosition(QTextCursor.NextBlock, QTextCursor.KeepAnchor)
         cursor.movePosition(QTextCursor.EndOfBlock, QTextCursor.KeepAnchor)
     else:  # only one block
         cursor = QTextCursor(block)
         cursor.movePosition(QTextCursor.EndOfBlock, QTextCursor.KeepAnchor)
     cursor.removeSelectedText()
Example #5
0
def html_copy(cursor, scheme='editor', number_lines=False):
    """Return a new QTextDocument with highlighting set as HTML textcharformats.

    The cursor is a cursor of a document.Document instance. If the cursor
    has a selection, only the selection is put in the new document.

    If number_lines is True, line numbers are added.

    """
    data = textformats.formatData(scheme)
    doc = QTextDocument()
    doc.setDefaultFont(data.font)
    doc.setPlainText(cursor.document().toPlainText())
    if metainfo.info(cursor.document()).highlighting:
        highlight(doc, mapping(data),
                  ly.lex.state(documentinfo.mode(cursor.document())))
    if cursor.hasSelection():
        # cut out not selected text
        start, end = cursor.selectionStart(), cursor.selectionEnd()
        cur1 = QTextCursor(doc)
        cur1.setPosition(start, QTextCursor.KeepAnchor)
        cur2 = QTextCursor(doc)
        cur2.setPosition(end)
        cur2.movePosition(QTextCursor.End, QTextCursor.KeepAnchor)
        cur2.removeSelectedText()
        cur1.removeSelectedText()
    if number_lines:
        c = QTextCursor(doc)
        f = QTextCharFormat()
        f.setBackground(QColor('#eeeeee'))
        if cursor.hasSelection():
            num = cursor.document().findBlock(
                cursor.selectionStart()).blockNumber() + 1
            last = cursor.document().findBlock(cursor.selectionEnd())
        else:
            num = 1
            last = cursor.document().lastBlock()
        lastnum = last.blockNumber() + 1
        padding = len(format(lastnum))
        block = doc.firstBlock()
        while block.isValid():
            c.setPosition(block.position())
            c.setCharFormat(f)
            c.insertText('{0:>{1}d} '.format(num, padding))
            block = block.next()
            num += 1
    return doc
Example #6
0
def html_copy(cursor, scheme='editor', number_lines=False):
    """Return a new QTextDocument with highlighting set as HTML textcharformats.

    The cursor is a cursor of a document.Document instance. If the cursor
    has a selection, only the selection is put in the new document.

    If number_lines is True, line numbers are added.

    """
    data = textformats.formatData(scheme)
    doc = QTextDocument()
    doc.setDefaultFont(data.font)
    doc.setPlainText(cursor.document().toPlainText())
    if metainfo.info(cursor.document()).highlighting:
        highlight(doc, mapping(data), ly.lex.state(documentinfo.mode(cursor.document())))
    if cursor.hasSelection():
        # cut out not selected text
        start, end = cursor.selectionStart(), cursor.selectionEnd()
        cur1 = QTextCursor(doc)
        cur1.setPosition(start, QTextCursor.KeepAnchor)
        cur2 = QTextCursor(doc)
        cur2.setPosition(end)
        cur2.movePosition(QTextCursor.End, QTextCursor.KeepAnchor)
        cur2.removeSelectedText()
        cur1.removeSelectedText()
    if number_lines:
        c = QTextCursor(doc)
        f = QTextCharFormat()
        f.setBackground(QColor('#eeeeee'))
        if cursor.hasSelection():
            num = cursor.document().findBlock(cursor.selectionStart()).blockNumber() + 1
            last = cursor.document().findBlock(cursor.selectionEnd())
        else:
            num = 1
            last = cursor.document().lastBlock()
        lastnum = last.blockNumber() + 1
        padding = len(format(lastnum))
        block = doc.firstBlock()
        while block.isValid():
            c.setPosition(block.position())
            c.setCharFormat(f)
            c.insertText('{0:>{1}d} '.format(num, padding))
            block = block.next()
            num += 1
    return doc
Example #7
0
 def _removeBlock(blockIndex):
     block = self._doc.findBlockByNumber(blockIndex)
     if block.next().isValid():  # not the last
         cursor = QTextCursor(block)
         cursor.movePosition(QTextCursor.NextBlock,
                             QTextCursor.KeepAnchor)
     elif block.previous().isValid():  # the last, not the first
         cursor = QTextCursor(block.previous())
         cursor.movePosition(QTextCursor.EndOfBlock)
         cursor.movePosition(QTextCursor.NextBlock,
                             QTextCursor.KeepAnchor)
         cursor.movePosition(QTextCursor.EndOfBlock,
                             QTextCursor.KeepAnchor)
     else:  # only one block
         cursor = QTextCursor(block)
         cursor.movePosition(QTextCursor.EndOfBlock,
                             QTextCursor.KeepAnchor)
     cursor.removeSelectedText()
Example #8
0
    def _get_textdoc(self, index):
        """Create the QTextDocument of an item.

        Args:
            index: The QModelIndex of the item to draw.
        """
        # FIXME we probably should do eliding here. See
        # qcommonstyle.cpp:viewItemDrawText
        text_option = QTextOption()
        if self._opt.features & QStyleOptionViewItem.WrapText:
            text_option.setWrapMode(QTextOption.WordWrap)
        else:
            text_option.setWrapMode(QTextOption.ManualWrap)
        text_option.setTextDirection(self._opt.direction)
        text_option.setAlignment(QStyle.visualAlignment(
            self._opt.direction, self._opt.displayAlignment))

        self._doc = QTextDocument(self)
        if index.parent().isValid():
            self._doc.setPlainText(self._opt.text)
        else:
            self._doc.setHtml('<b>{}</b>'.format(html.escape(self._opt.text)))
        self._doc.setDefaultFont(self._opt.font)
        self._doc.setDefaultTextOption(text_option)
        self._doc.setDefaultStyleSheet(style.get_stylesheet("""
            .highlight {
                {{ color['completion.match.fg'] }}
            }
        """))
        self._doc.setDocumentMargin(2)

        if index.column() == 0:
            marks = index.data(basecompletion.Role.marks)
            if marks is None:
                return
            for mark in marks:
                cur = QTextCursor(self._doc)
                cur.setPosition(mark[0])
                cur.setPosition(mark[1], QTextCursor.KeepAnchor)
                txt = cur.selectedText()
                cur.removeSelectedText()
                cur.insertHtml('<span class="highlight">{}</span>'.format(
                    html.escape(txt)))
Example #9
0
 def check_expected(self):  # проверка текущего слова на соответствие ожидаемым
     part_match = False  # частичное совпадение
     self.mem += self.queue[self.pos]  # на случай, если ожидается строка из нескольких слов
     match = self.exp.query('following in @self.mem')  # проверяем на точное совпадение наличие слова в словаре
     if not match.empty:  # найдено точное совпадение
         # ожидаемое слово изменяет предыдущую команду tex, удаляем ее("плюс минус", "больше равно" и т.д.)
         if pd.notnull(match.iloc[0][5]):
             # "сумма по модулю", "корень степени" - перед удалением нужно выйти за пределы напечатанных скобок
             # так как курсор уже находится внутри скобок и правая скобка иначе не будет удалена
             if self.inside:
                 self.change_cursor_position(self.inside)
             place = QTextCursor(self.text.textCursor())  # получаем копию текущего курсора
             # выделяем справа налево кол-во символов, которое необходимо удалить
             place.movePosition(QTextCursor.Left,
                                QTextCursor.KeepAnchor,
                                int(match.iloc[0][5]))
             place.removeSelectedText()  # удаляем выделенное количество символов
             self.text.setTextCursor(place)  # меняем позицию курсора
         if match.iloc[0][4]:  # есть команда tex с учетом ожидаемых слов, выводим ее
             self.text.insertPlainText(match.iloc[0][4])
         if pd.notnull(match.iloc[0][7]):  # нужно изменить позицию курсора
             self.change_cursor_position(match.iloc[0][7])
         if match.iloc[0][6]:  # есть слово-ограничитель
             self.constraint.append(match.iloc[0][6])  # запоминаем его
             self.constraint_kind.append(self.kind)  # и его категорию
         # удаляем это ожидаемое слово(группу слов) из словаря, так как они больше не нужны
         self.exp = self.exp[self.exp.following != self.mem]
     # проверяем на частичное совпадение, возможно, дальше могут ожидаться другие слова
     # ("сумма по", "сумма по модулю")
     if not (self.exp.empty or self.exp[self.exp.following.str.contains(self.mem)].empty):
         part_match = True  # частичное совпадение
         # добавляем пробел в строку поиска, так как в нее будут добавляться слова на след. итерации
         self.mem += ' '
     if not match.empty or part_match:  # было частичное или полное совпадение
         del self.queue[:self.pos + 1]  # удаляем обработанные слова из очереди, они больше не нужны
         self.pos = 0  # устанавливаем указатель очереди на начало
     # слово не соответствует ожидаемым, позиция при этом не изменяется
     # слово будет проверяться дальше по таблице ключевых слов
     if not part_match:
         self.mem = ''  # очищаем строку проверяемых слов
         self.exp = self.exp[0:0]  # очищаем словарь
Example #10
0
 def __update_text_status_messages(self, severity, msg):  # yet another PyQt5 thread workaround
     prepend = ''
     append = '<br>'
     if severity < 6:
         prepend = '<span style="color:'
         append = '</span><br>'
     if severity == 5:
         prepend = f'{prepend} #53a0ed;">'
     elif severity == 4:
         prepend = f'{prepend} yellow;">'
     elif severity == 3 or severity == 0:
         prepend = f'{prepend} red;">'
     elif 1 <= severity <= 2:
         prepend = f'{prepend} orange;">'
     cursor = QTextCursor(self._window.text_status.document())
     cursor.setPosition(0)
     self._window.text_status.setTextCursor(cursor)
     self._window.text_status.insertHtml(f'{prepend}{msg}{append}\n')
     if self._window.text_status.toPlainText().count('\n') >= _MAX_STATUSTEXT_MESSAGES:
         cursor.movePosition(QTextCursor.End)
         cursor.select(QTextCursor.LineUnderCursor)
         cursor.removeSelectedText()
         cursor.deletePreviousChar()
Example #11
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 #12
0
class MainGui(QWidget):
    """The main GUI window containing all the widgets and primary methods."""
    def __init__(self):
        super().__init__()
        self.setWindowIcon(QIcon('./images/icon2.png'))
        self.initializeUI()

    def initializeUI(self):
        """Create and set the widgets for the GUI."""
        self.setWindowTitle('Clipboard Collector')
        self.setGeometry(100, 100, 100, 400)

        append_layout = QVBoxLayout()
        #append_layout.setSizeConstraint(10)
        right_side_layout = QVBoxLayout()
        g_layout = QGridLayout()
        h_layout = QHBoxLayout()

        self.text_window = QTextEdit()
        self.text_window.setPlaceholderText(
            'Text copied to clipboard will be automatically pasted here.')
        self.text_window.setAcceptDrops(True)
        self.text_window_cursor = self.text_window.textCursor()

        self.cursor = QTextCursor(self.text_window_cursor)

        self.clipboard = app.clipboard()

        self.start_button = QPushButton('Start')
        self.start_button.clicked.connect(self.startWorker)

        self.stop_button = QPushButton('Stop')
        self.stop_button.setEnabled(False)
        self.stop_button.clicked.connect(self.stopWorker)

        self.copy_all = QPushButton('Copy All')
        self.copy_all.setEnabled(False)
        self.copy_all.clicked.connect(self.copyAll)

        self.clear_all = QPushButton('Clear Text Field')
        self.clear_all.clicked.connect(self.clearText)

        self.cb_bullet = QCheckBox('Bullet Points', self)
        self.cb_bullet.setEnabled(False)
        self.cb_bullet.stateChanged.connect(self.bulletPoints)

        self.cb_custom_prefix = QCheckBox('Custom Prefix', self)
        self.cb_custom_prefix.setEnabled(False)
        self.cb_custom_prefix.stateChanged.connect(self.customPrefix)

        self.cb_custom_suffix = QCheckBox('Custom Suffix', self)
        self.cb_custom_suffix.setEnabled(False)
        self.cb_custom_suffix.stateChanged.connect(self.customSuffix)

        self.prefix = QLineEdit()
        self.prefix.setPlaceholderText('Enter prefix here')

        self.suffix = QLineEdit()
        self.suffix.setPlaceholderText('Enter suffix here')

        self.append_label = QLabel('Append Options')
        self.append_label.setToolTip("Press 'Start' to enable Append Options.")

        g_layout.addWidget(self.start_button, 0, 0)
        g_layout.addWidget(self.stop_button, 0, 1)
        g_layout.addWidget(self.copy_all, 0, 2)
        g_layout.addWidget(self.clear_all, 0, 3)

        self.left_side_widget = QWidget()
        self.left_side_widget.setObjectName('AppendBar')
        self.left_side_widget.setLayout(append_layout)

        append_layout.addWidget(self.append_label)
        append_layout.addWidget(self.cb_bullet)
        append_layout.addWidget(self.cb_custom_prefix)
        append_layout.addWidget(self.prefix)
        append_layout.addWidget(self.cb_custom_suffix)
        append_layout.addWidget(self.suffix)
        append_layout.addStretch()

        right_side_layout.addWidget(self.text_window)
        right_side_layout.addLayout(g_layout)

        h_layout.addWidget(self.left_side_widget)
        h_layout.addLayout(right_side_layout, 1)

        self.setLayout(h_layout)
        self.show()

    def stopWorker(self):
        """Calls kill method of ClipBoardExtractor class and
        exits the thread."""
        self.copy_all.setEnabled(True)
        self.worker.kill()
        self.start_button.setEnabled(True)
        self.stop_button.setEnabled(False)
        self.thread.exit()

    def startWorker(self):
        """Clears the clipboard of any text that may
        otherwise be unintentionally captured. Begins
        run method of ClipboardExtractor class in a
        separate thread."""
        self.cb_bullet.setEnabled(True)
        self.cb_custom_prefix.setEnabled(True)
        self.cb_custom_suffix.setEnabled(True)
        self.clipboard.clear()
        self.thread = QThread()
        self.worker = ClipboardExtractor(self.cursor)
        self.worker.moveToThread(self.thread)
        self.thread.started.connect(self.worker.run)
        self.thread.start()

        self.start_button.setEnabled(False)
        self.stop_button.setEnabled(True)

    def copyAll(self):
        """Copy all the text QTextEdit"""
        self.clipboard.setText(self.text_window.toPlainText(),
                               mode=self.clipboard.Clipboard)

    def clearText(self):
        """Remove all text on QTextEdit"""
        self.cursor.movePosition(self.cursor.Start, QTextCursor.KeepAnchor)
        self.cursor.removeSelectedText()

    def bulletPoints(self):
        self.worker.bp()

    def customPrefix(self):
        self.worker.prefix()

    def customSuffix(self):
        self.worker.suffix()

    def getPrefix(self):
        return self.prefix.text()

    def getSuffix(self):
        return self.suffix.text()
Example #13
0
class Widget_Script(QtWidgets.QPlainTextEdit):
    '''
    Text edit box holding the current script.

    Attributes:
    * modified
      - Bool, True if this has been modified since Clear_Modified
        was called.
      - Handled manually since the widget isModified is missing
        from pyqt.
    * cursor
      - QTextCursor used to edit or analyze the document.
    '''
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.modified = False
        # Set up the cursor, attached to the document.
        self.cursor = QTextCursor(self.document())

        # Set up dragging. (TODO: maybe remove if set up in qt designer.)
        self.setAcceptDrops(True)

        self.modificationChanged.connect(self.Handle_modificationChanged)
        # Hook up a handler to the document's content change signal.
        self.document().contentsChange.connect(self.Handle_contentsChange)

        # Set up the QSyntaxHighlighter.
        # Note: this also connect to contentsChange, and qt will handle
        # connected handlers in their connection order; since the custom
        # handler edits the text, it needs to run before the highlighter
        # otherwise the highlight position can get thrown off (pointing
        # at changed positions). So, add this after setting up
        # the above connections to avoid the problem.
        self.highlighter = Script_Syntax_Highlighter(self.document())

        return

    def Clear_Modified(self):
        'Clears the modified flag.'
        # Clear the local flag and the hidden widget flag,
        # so that the widget triggers modificationChanged later.
        self.modified = False
        self.setWindowModified(False)
        return

    def Is_Modified(self):
        'Returns True if the text has been modified since Clear_Modified.'
        return self.modified

    def Handle_modificationChanged(self, arg=None):
        '''
        The text was modified since setModified(False) was called
        on it (from the main window).
        '''
        self.modified = True
        return

    def Handle_contentsChange(self, position, chars_removed, chars_added):
        '''
        Detect text changes, and maybe do some edits to clean them up.
        '''
        self.modified = True

        # Note: position is the start of the edited section, after
        # which chars are added (or from after which chars were removed,
        # or both).
        # Quirk: when a file is opened, there is a hidden EoL character
        # which is included in chars_added but cannot be positioned to.
        # As a workaround, check against the total document size, -1
        # for the eof.
        total_chars = self.document().characterCount() - 1

        #-Removed; de-indent is handled by keyPressEvent with shift-tab,
        # instead of with space deletion (which is clumsy in practice).
        ## If a space is deleted, can try to detect if it is a multiple
        ##  of 4 from the start of the line, and if so, delete another 3.
        ## Only do this on a single character removal, with at least
        ##  3 characters prior to it, for backspaces; deletes will need
        ##  to check for 3 following spaces instead.
        ## It is unclear on how to detect what was removed, so this will
        ##  be blind to if it was a space or something else.
        #if chars_removed == 1 and chars_added == 0:
        #
        #    # Look back to check alignment with the start of the line.
        #    # TODO
        #
        #    # Loop over this, trying to look back, then forward.
        #    for start, end in [(position -3, position),
        #                       (position, position + 3)]:
        #        # Don't search past doc boundaries.
        #        # (Note: cursor can move to before or after a character,
        #        #  so the position range is char count +1.)
        #        if start < 0:
        #            continue
        #        if end > total_chars:
        #            continue
        #        # TODO: how to know when near the end of the doc.
        #
        #        # Set the anchor and position for implicit selection.
        #        self.cursor.setPosition(start, QTextCursor.MoveAnchor)
        #        self.cursor.setPosition(end, QTextCursor.KeepAnchor)
        #
        #        # Pull the chars.
        #        text = self.cursor.selectedText()
        #
        #        # If 3 spaces, remove the selection.
        #        if text == '   ':
        #            self.cursor.removeSelectedText()
        #            # Limit to one direction deletion
        #            break

        # For char additions, look for tabs and replace them.
        # Note: this can happen when text is copy/pasted in, with
        # tabs included, so this logic should try to catch any tab
        # within the new text body.
        if chars_added:
            # Limit the end point to the last non-eof character.
            start = position
            end = position + chars_added
            if end > total_chars:
                end = total_chars

            # Save the cursor position.
            old_position = self.textCursor().position()
            old_anchor = self.textCursor().anchor()

            # Use the cursor to select what was added.
            # This involves setting the anchor to the selection start,
            # and cursor position to selection end.
            self.cursor.setPosition(start, QTextCursor.MoveAnchor)
            self.cursor.setPosition(end, QTextCursor.KeepAnchor)
            text = self.cursor.selectedText()
            if '\t' in text:
                # Replace the selected text with a copy that has
                # tabs swapped to spaces.
                # TODO: for some reason this clears the undo history,
                # though docs don't mention this behavior; look into why.
                self.cursor.insertText(text.replace('\t', '    '))

            # TODO: fix this bit of code; it is giving a '-1' index
            # error for some reason; also, it may not be necessary.
            # Reset the cursor selection back to the original.
            #self.cursor.setPosition(old_anchor, QTextCursor.MoveAnchor)
            #self.cursor.setPosition(old_position, QTextCursor.KeepAnchor)

        return

    # TODO: the above could be better done by intercepting key pressed,
    # which would also allow adding shift-tab support.
    # https://stackoverflow.com/questions/13579116/qtextedit-shift-tab-wrong-behaviour

    def keyPressEvent(self, event):
        '''
        This function is called automatically when a key is pressed.
        The original version handles up/down/left/right/pageup/pagedown
        and ignores other keys.
        This wrapper will also catch shift-tab.
        This is triggered prior to contentsChange.
        '''
        if event.key() == QtCore.Qt.Key_Backtab:
            # The text cursor will need to be updated appropriately.
            # self.textCursor()
            # Get the place the edit is occurring.
            # The anchor may indicate a section of text is highlighted.
            # Proper handling of highlights in VS/notepad style would
            # be a little complicated...
            # TODO: think about this.

            # Get the current text cursor position/anchor.
            # Note: anchor can be before or after the position.
            position = self.textCursor().position()
            anchor = self.textCursor().anchor()

            # Standardize the locations so 'start' is first, 'end' is second.
            if position > anchor:
                start = anchor
                end = position
            else:
                start = position
                end = anchor

            # If these differ, then a chunk of text is highlighted,
            # in which case all involved lines can have their
            # indentation changed. TODO.

            # Move the cursor to select 4 characters prior to
            # the original selection, to find out if they are
            # a group of spaces.
            self.cursor.setPosition(start, QTextCursor.MoveAnchor)
            self.cursor.setPosition(start - 4, QTextCursor.KeepAnchor)
            text = self.cursor.selectedText()
            if text == '    ':
                # If there, shift-tab was pressed with 4 spaces prior
                # to the selected text. In this case, remove the
                # 4 spaces but do not modify the text selection.
                self.cursor.removeSelectedText()
                # Offset the original position/anchor back by 4.
                position -= 4
                anchor -= 4

            # Reset the cursor selection back to the original, accounting
            # for the deleted text.
            self.cursor.setPosition(anchor, QTextCursor.MoveAnchor)
            self.cursor.setPosition(position, QTextCursor.KeepAnchor)

        else:
            # Just pass the call upward for now.
            super().keyPressEvent(event)
        return

    def New_Script(self):
        '''
        Create a new script.
        '''
        # Set up a default header, the function to apply hand edits,
        # and a function to write results.
        lines = [
            '# X4 Customizer input script.',
            'from Plugins import *',
            '',
            '# Apply hand edits from the GUI tables.',
            'Apply_Live_Editor_Patches()',
            '',
            '# Write modified files to the output extension.',
            'Write_To_Extension()',
        ]
        self.setPlainText('\n'.join(lines))
        self.Clear_Modified()
        return