diff --git a/src/calibre/gui2/dialogs/template_dialog.py b/src/calibre/gui2/dialogs/template_dialog.py index fa1bdf92fe..bf004aebaf 100644 --- a/src/calibre/gui2/dialogs/template_dialog.py +++ b/src/calibre/gui2/dialogs/template_dialog.py @@ -430,6 +430,7 @@ class TemplateDialog(QDialog, Ui_TemplateDialog): s = gprefs.get('template_editor_break_on_print', False) self.go_button.setEnabled(s) self.remove_all_button.setEnabled(s) + self.set_all_button.setEnabled(s) self.toggle_button.setEnabled(s) self.breakpoint_line_box.setEnabled(s) self.breakpoint_line_box_label.setEnabled(s) @@ -440,6 +441,7 @@ class TemplateDialog(QDialog, Ui_TemplateDialog): self.set_up_font_boxes() self.toggle_button.clicked.connect(self.toggle_button_pressed) self.remove_all_button.clicked.connect(self.remove_all_button_pressed) + self.set_all_button.clicked.connect(self.set_all_button_pressed) self.load_button.clicked.connect(self.load_template) self.save_button.clicked.connect(self.save_template) @@ -511,6 +513,7 @@ class TemplateDialog(QDialog, Ui_TemplateDialog): gprefs['template_editor_break_on_print'] = new_state != 0 self.go_button.setEnabled(new_state != 0) self.remove_all_button.setEnabled(new_state != 0) + self.set_all_button.setEnabled(new_state != 0) self.toggle_button.setEnabled(new_state != 0) self.breakpoint_line_box.setEnabled(new_state != 0) self.breakpoint_line_box_label.setEnabled(new_state != 0) @@ -521,6 +524,9 @@ class TemplateDialog(QDialog, Ui_TemplateDialog): def remove_all_button_pressed(self): self.textbox.set_clicked_line_numbers(set()) + def set_all_button_pressed(self): + self.textbox.set_clicked_line_numbers({i for i in range(1, self.textbox.blockCount()+1)}) + def toggle_button_pressed(self): ln = self.breakpoint_line_box.value() if ln > self.textbox.blockCount(): diff --git a/src/calibre/gui2/dialogs/template_dialog.ui b/src/calibre/gui2/dialogs/template_dialog.ui index 974c823343..e90f17065e 100644 --- a/src/calibre/gui2/dialogs/template_dialog.ui +++ b/src/calibre/gui2/dialogs/template_dialog.ui @@ -170,21 +170,21 @@ - - - - T&emplate: - - - textbox - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - + + + + + T&emplate: + + + textbox + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + @@ -232,6 +232,9 @@ you the value as well as all the local variables</p> :/images/sync-right.png:/images/sync-right.png + + padding: 5; padding-left: 1; + If 'Enable breakpoints' is checked then click this button to run your template @@ -287,6 +290,9 @@ you the value as well as all the local variables</p> &Toggle + + padding: 5; padding-left: 1; + :/images/swap.png:/images/swap.png @@ -314,6 +320,9 @@ you the value as well as all the local variables</p> &Remove all + + padding: 5; padding-left: 1; + :/images/list_remove.png:/images/list_remove.png @@ -323,6 +332,23 @@ you the value as well as all the local variables</p> + + + + &Set all + + + padding: 5; padding-left: 1; + + + + :/images/plus.png:/images/plus.png + + + Set breakpoint on every line + + + @@ -590,13 +616,26 @@ you the value as well as all the local variables</p> - &Save + Sa&ve Save the template in a file + + + + QFrame::VLine + + + QFrame::Raised + + + 3 + + + diff --git a/src/calibre/gui2/dialogs/template_dialog_code_widget.py b/src/calibre/gui2/dialogs/template_dialog_code_widget.py index 0686dc53e2..dd9eac207a 100644 --- a/src/calibre/gui2/dialogs/template_dialog_code_widget.py +++ b/src/calibre/gui2/dialogs/template_dialog_code_widget.py @@ -8,7 +8,8 @@ License: GPLv3 Copyright: 2021, Kovid Goyal ''' from qt.core import ( - QFont, QPainter, QPalette, QPlainTextEdit, QRect, Qt, QTextEdit, QTextFormat + QFont, QPainter, QPalette, QPlainTextEdit, QRect, Qt, QTextEdit, + QTextFormat, QTextCursor ) from calibre.gui2.tweak_book.editor.text import LineNumbers @@ -91,6 +92,9 @@ class CodeEditor(QPlainTextEdit): self.clicked_line_numbers.add(line) self.update(self.line_number_area.geometry()) + def number_of_lines(self): + return self.blockCount() + def set_clicked_line_numbers(self, new_set): self.clicked_line_numbers = new_set self.update(self.line_number_area.geometry()) @@ -131,3 +135,86 @@ class CodeEditor(QPlainTextEdit): top = bottom bottom = top + int(self.blockBoundingRect(block).height()) num += 1 + + def keyPressEvent(self, ev): + if ev.key() == Qt.Key.Key_Insert: + self.setOverwriteMode(self.overwriteMode() ^ True) + ev.accept() + return + key = ev.key() + if key == Qt.Key_Tab or key == Qt.Key_Backtab: + ''' + Handle indenting usingTab and Shift Tab. This is remarkably + difficult because of the way Qt represents the edit buffer. + + Selections represent the start and end as character positions in the + buffer. To convert a position into a line number we must get the + block number containing that position. You so this by setting a + cursor to that position. + + To get the text of a line we must convert the line number (the + block number) to a block and then fetch the text from that. + + To change text we must create a cursor that selects all the text on + the line. Because cursors use document positions, not block numbers + or blocks, we must convert line numbers to blocks then get the + position of the first character of the block. We then "extend" the + selection to the end by computing the end position: the start + the + length of the text on the line. We then uses that cursor to + "insert" the new text, which magically replaces the selected text. + ''' + # Get the position of the start and end of the selection. + cursor = self.textCursor() + start_position = cursor.selectionStart() + end_position = cursor.selectionEnd() + + # Now convert positions into block (line) numbers + cursor.setPosition(start_position) + start_block = cursor.block().blockNumber() + cursor.setPosition(end_position) + end_block = cursor.block().blockNumber() + + def select_block(block_number, curs): + # Note the side effect: 'curs' is changed to select the line + blk = self.document().findBlockByNumber(block_number) + txt = blk.text() + pos = blk.position() + curs.setPosition(pos) + curs.setPosition(pos+len(txt), QTextCursor.KeepAnchor) + return txt + + # Check if there is a selection. If not then only Shift-Tab is valid + if start_position == end_position: + if key == Qt.Key_Backtab: + txt = select_block(start_block, cursor) + if txt.startswith('\t'): + # This works because of the side effect in select_block() + cursor.insertText(txt[1:]) + cursor.setPosition(start_position-1) + self.setTextCursor(cursor) + ev.accept() + else: + QPlainTextEdit.keyPressEvent(self, ev) + return + # There is a selection so both Tab and Shift-Tab do indenting operations + for bn in range(start_block, end_block+1): + txt = select_block(bn, cursor) + if key == Qt.Key_Backtab: + if txt.startswith('\t'): + cursor.insertText(txt[1:]) + if bn == start_block: + start_position -= 1 + end_position -= 1 + else: + cursor.insertText('\t' + txt) + if bn == start_block: + start_position += 1 + end_position += 1 + # Restore the selection, adjusted for the added or deleted tabs + cursor.setPosition(start_position) + cursor.setPosition(end_position, QTextCursor.KeepAnchor) + self.setTextCursor(cursor) + ev.accept() + return + QPlainTextEdit.keyPressEvent(self, ev) +