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)
+