mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-11-01 11:07:02 -04:00
Several related changes:
* Add a "with" statement to the template language that for the duration of the code block changes the "current book" to the one specified by the book id.
* A new formatter function selected_books() that returns the book ids of the currently selected books
* A new formatter function selected_column() that returns the lookup name of the column containing the selected cell.
* A new formatter function sort_book_ids() that sorts the books specified by book_ids.
* A new formatter function show_dialog() that opens a dialog to display plain text or html.
* Add check boxes to the template tester to control "run as you type" and to restrict test runs to the first selected book.
Here is an example using several of the new features:
program:
ids = sort_book_ids(selected_books(), 'series', 1, 'title', 1);
res = '<style> th, td {padding: 2px;}</style> <h2>Book Size Report</h2><p><table>';
total = 0;
def table_row(title, series, size):
return strcat('<tr><td>', title, '</td>',
'<td>', series, '</td>',
'<td>', if size !=# 0 then human_readable(size) else '0' fi, '</td>',
'</tr>', character('newline'))
fed;
for id in ids:
with id:
s = booksize();
total = total + s;
res = strcat(res, table_row($title, $series, s))
htiw
rof;
res = strcat(res, table_row('TOTAL', '', total));
res = strcat(res, '</table>');
show_dialog(res)
This commit is contained in:
parent
91216de5f3
commit
ed83d9eeb8
@ -488,6 +488,8 @@ def create_defs():
|
||||
defs['book_details_note_link_icon_width'] = 1.0
|
||||
defs['tag_browser_show_category_icons'] = True
|
||||
defs['tag_browser_show_value_icons'] = True
|
||||
defs['template_editor_run_as_you_type'] = True
|
||||
defs['template_editor_show_all_selected_books'] = True
|
||||
|
||||
def migrate_tweak(tweak_name, pref_name):
|
||||
# If the tweak has been changed then leave the tweak in the file so
|
||||
|
||||
@ -212,7 +212,7 @@ class TemplateHighlighter(QSyntaxHighlighter):
|
||||
|
||||
KEYWORDS_GPM = ['if', 'then', 'else', 'elif', 'fi', 'for', 'rof',
|
||||
'separator', 'break', 'continue', 'return', 'in', 'inlist',
|
||||
'inlist_field', 'def', 'fed', 'limit']
|
||||
'inlist_field', 'def', 'fed', 'limit', 'with', 'htiw']
|
||||
|
||||
KEYWORDS_PYTHON = ['and', 'as', 'assert', 'break', 'class', 'continue', 'def',
|
||||
'del', 'elif', 'else', 'except', 'exec', 'finally', 'for', 'from',
|
||||
@ -581,8 +581,11 @@ class TemplateDialog(QDialog, Ui_TemplateDialog):
|
||||
formatter_functions().get_builtins_and_aliases())
|
||||
|
||||
# Set up the breakpoint bar
|
||||
s = gprefs.get('template_editor_break_on_print', False)
|
||||
self.go_button.setEnabled(s)
|
||||
run_as_you_type = gprefs.get('template_editor_run_as_you_type')
|
||||
self.run_as_you_type_box.setChecked(run_as_you_type)
|
||||
self.go_button.setEnabled(not run_as_you_type)
|
||||
self.break_box.setEnabled(not run_as_you_type)
|
||||
s = gprefs.get('template_editor_enable_breakpoints', False)
|
||||
self.remove_all_button.setEnabled(s)
|
||||
self.set_all_button.setEnabled(s)
|
||||
self.toggle_button.setEnabled(s)
|
||||
@ -591,6 +594,10 @@ class TemplateDialog(QDialog, Ui_TemplateDialog):
|
||||
self.break_box.setChecked(s)
|
||||
self.break_box.stateChanged.connect(self.break_box_changed)
|
||||
self.go_button.clicked.connect(self.go_button_pressed)
|
||||
self.show_all_selected_books.clicked.connect(self.show_all_selected_books_changed)
|
||||
self.run_as_you_type_box.stateChanged.connect(self.run_as_you_type_box_changed)
|
||||
self.show_all_selected_books.setChecked(gprefs.get('template_editor_show_all_selected_books'))
|
||||
self.show_all_selected_books.clicked.connect(self.show_all_selected_books_changed)
|
||||
|
||||
# Set up the display table
|
||||
self.table_column_widths = None
|
||||
@ -725,14 +732,20 @@ class TemplateDialog(QDialog, Ui_TemplateDialog):
|
||||
else:
|
||||
mi = (get_model_metadata_instance(), )
|
||||
self.mi = mi
|
||||
self.setup_result_display_table()
|
||||
|
||||
def setup_result_display_table(self):
|
||||
tv = self.template_value
|
||||
mi = self.mi
|
||||
row_count = len(mi) if gprefs.get('template_editor_show_all_selected_books') else 1
|
||||
tv.clear()
|
||||
tv.setColumnCount(3)
|
||||
tv.setHorizontalHeaderLabels((_('Book title'), '', _('Template value')))
|
||||
tv.horizontalHeader().setStretchLastSection(True)
|
||||
tv.horizontalHeader().sectionResized.connect(self.table_column_resized)
|
||||
tv.setRowCount(len(mi))
|
||||
tv.setRowCount(len(mi) )
|
||||
# Set the height of the table
|
||||
h = tv.rowHeight(0) * min(len(mi), 5)
|
||||
h = tv.rowHeight(0) * min(row_count, 5)
|
||||
h += 2 * tv.frameWidth() + tv.horizontalHeader().height()
|
||||
tv.setMinimumHeight(h)
|
||||
tv.setMaximumHeight(h)
|
||||
@ -742,9 +755,9 @@ class TemplateDialog(QDialog, Ui_TemplateDialog):
|
||||
else:
|
||||
tv.setColumnWidth(0, tv.fontMetrics().averageCharWidth() * 10)
|
||||
tv.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
|
||||
tv.setRowCount(len(mi))
|
||||
tv.setRowCount(row_count)
|
||||
# Use our own widget to get rid of elision. setTextElideMode() doesn't work
|
||||
for r in range(len(mi)):
|
||||
for r in range(row_count):
|
||||
w = QLineEdit(tv)
|
||||
w.setReadOnly(True)
|
||||
w.setText(mi[r].get('title', _('No title provided')))
|
||||
@ -786,15 +799,15 @@ class TemplateDialog(QDialog, Ui_TemplateDialog):
|
||||
pmi = None
|
||||
new_mi.append(pmi)
|
||||
self.set_mi(new_mi, self.fm)
|
||||
if not self.break_box.isChecked():
|
||||
if not self.run_as_you_type_box.isChecked():
|
||||
self.display_values(str(self.textbox.toPlainText()))
|
||||
|
||||
def set_waiting_message(self):
|
||||
if self.break_box.isChecked():
|
||||
for i in range(len(self.mi)):
|
||||
if not self.run_as_you_type_box.isChecked():
|
||||
for i in range(self.template_value.rowCount()):
|
||||
self.template_value.cellWidget(i, 2).setText('')
|
||||
self.template_value.cellWidget(0, 2).setText(
|
||||
_("*** Breakpoints are enabled. Waiting for the 'Go' button to be pressed"))
|
||||
_("*** Waiting for the 'Go' button to be pressed"))
|
||||
|
||||
def show_code_context_menu(self, point):
|
||||
m = self.source_code.createStandardContextMenu()
|
||||
@ -930,15 +943,40 @@ def evaluate(book, context):
|
||||
gprefs['gpm_template_editor_font_size'] = toWhat
|
||||
self.set_editor_font()
|
||||
|
||||
def run_as_you_type_box_changed(self, new_state):
|
||||
gprefs['template_editor_run_as_you_type'] = new_state != 0
|
||||
self.go_button.setEnabled(new_state == 0)
|
||||
if new_state == 0:
|
||||
self.set_waiting_message()
|
||||
self.break_box.setEnabled(True)
|
||||
enable_break_boxes = self.break_box.isChecked()
|
||||
else:
|
||||
self.break_box.setEnabled(False)
|
||||
enable_break_boxes = False
|
||||
self.display_values(str(self.textbox.toPlainText()))
|
||||
|
||||
self.remove_all_button.setEnabled(enable_break_boxes)
|
||||
self.set_all_button.setEnabled(enable_break_boxes)
|
||||
self.toggle_button.setEnabled(enable_break_boxes)
|
||||
self.breakpoint_line_box.setEnabled(enable_break_boxes)
|
||||
self.breakpoint_line_box_label.setEnabled(enable_break_boxes)
|
||||
|
||||
def break_box_changed(self, new_state):
|
||||
gprefs['template_editor_break_on_print'] = new_state != 0
|
||||
self.go_button.setEnabled(new_state != 0)
|
||||
gprefs['template_editor_enable_breakpoints'] = 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)
|
||||
if new_state == 0:
|
||||
if gprefs['template_editor_run_as_you_type']:
|
||||
self.display_values(str(self.textbox.toPlainText()))
|
||||
else:
|
||||
self.set_waiting_message()
|
||||
|
||||
def show_all_selected_books_changed(self, new_state):
|
||||
gprefs['template_editor_show_all_selected_books'] = new_state != 0
|
||||
self.setup_result_display_table()
|
||||
if gprefs['template_editor_run_as_you_type']:
|
||||
self.display_values(str(self.textbox.toPlainText()))
|
||||
else:
|
||||
self.set_waiting_message()
|
||||
@ -1042,7 +1080,7 @@ def evaluate(book, context):
|
||||
self.last_text = cur_text
|
||||
self.highlighter.regenerate_paren_positions()
|
||||
self.text_cursor_changed()
|
||||
if not self.break_box.isChecked():
|
||||
if self.run_as_you_type_box.isChecked():
|
||||
self.display_values(cur_text)
|
||||
else:
|
||||
self.set_waiting_message()
|
||||
@ -1095,6 +1133,8 @@ def evaluate(book, context):
|
||||
w.setCursorPosition(0)
|
||||
finally:
|
||||
sys.settrace(None)
|
||||
if not gprefs.get('template_editor_show_all_selected_books', True):
|
||||
break
|
||||
|
||||
def text_cursor_changed(self):
|
||||
cursor = self.textbox.textCursor()
|
||||
|
||||
@ -206,14 +206,13 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="break_box">
|
||||
<widget class="QCheckBox" name="run_as_you_type_box">
|
||||
<property name="text">
|
||||
<string>Enable &breakpoints</string>
|
||||
<string>R&un as you type</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><p>If checked, the template evaluator will stop when it
|
||||
evaluates an expression on a double-clicked line number, opening a dialog showing
|
||||
you the value as well as all the local variables</p></string>
|
||||
<string><p>If checked then the template will be run (tested) after every
|
||||
keystroke. If unchecked then the template will be run when the "Go" button is pushed.</p></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -237,7 +236,7 @@ you the value as well as all the local variables</p></string>
|
||||
<set>Qt::ToolButtonTextBesideIcon</set>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>If 'Enable breakpoints' is checked then click this button to run your template</string>
|
||||
<string>If 'Run as you type' is not checked then click this button to run your template</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -248,6 +247,18 @@ you the value as well as all the local variables</p></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="break_box">
|
||||
<property name="text">
|
||||
<string>Enable &breakpoints</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><p>If checked, the template evaluator will stop when it
|
||||
evaluates an expression on a double-clicked line number, opening a dialog showing
|
||||
you the value as well as all the local variables</p></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="breakpoint_line_box_label">
|
||||
<property name="text">
|
||||
@ -378,17 +389,34 @@ you the value as well as all the local variables</p></string>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<widget class="QLabel">
|
||||
<property name="text">
|
||||
<string>Template value:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>template_value</cstring>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>The value of the template using the current book in the library view</string>
|
||||
</property>
|
||||
</widget>
|
||||
<layout class="QHBoxLayout">
|
||||
<item>
|
||||
<widget class="QLabel">
|
||||
<property name="text">
|
||||
<string>Template value:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>template_value</cstring>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>The value of the template using the currently selected book(s)
|
||||
in the library view</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="show_all_selected_books">
|
||||
<property name="text">
|
||||
<string>Show template result for all selected books</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><p>If checked then the template will be evaluated for all selected
|
||||
books. If not checked then the template will be evaluated for the first selected book. Unchecking this
|
||||
box is useful if the template uses the selected books to generate a report.</p></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="8" column="0" colspan="3">
|
||||
<widget class="QTableWidget" name="template_value">
|
||||
|
||||
@ -60,6 +60,7 @@ class Node:
|
||||
NODE_SWITCH = 31
|
||||
NODE_SWITCH_IF = 32
|
||||
NODE_LIST_COUNT_FIELD = 33
|
||||
NODE_WITH = 34
|
||||
|
||||
def __init__(self, line_number, name):
|
||||
self.my_line_number = line_number
|
||||
@ -74,6 +75,14 @@ class Node:
|
||||
return self.my_line_number
|
||||
|
||||
|
||||
class WithNode(Node):
|
||||
def __init__(self, line_number, book_id, block):
|
||||
Node.__init__(self, line_number, 'if ...')
|
||||
self.node_type = self.NODE_WITH
|
||||
self.book_id = book_id
|
||||
self.block = block
|
||||
|
||||
|
||||
class IfNode(Node):
|
||||
def __init__(self, line_number, condition, then_part, else_part):
|
||||
Node.__init__(self, line_number, 'if ...')
|
||||
@ -600,6 +609,20 @@ class _Parser:
|
||||
def local_call_expression(self, name, arguments):
|
||||
return LocalFunctionCallNode(self.line_number, name, arguments)
|
||||
|
||||
def with_expression(self):
|
||||
self.consume()
|
||||
line_number = self.line_number
|
||||
book_id = self.top_expr()
|
||||
if not self.token_op_is(':'):
|
||||
self.error(_("{0} statement: expected '{1}', "
|
||||
"found '{2}'").format('with', ':', self.token_text()))
|
||||
self.consume()
|
||||
block = self.expression_list()
|
||||
if not self.token_is('htiw'):
|
||||
self.error(_("'{0}' statement: missing the closing '{1}'").format('def', 'fed'))
|
||||
self.consume()
|
||||
return WithNode(line_number, book_id, block)
|
||||
|
||||
def call_expression(self, name, arguments):
|
||||
compiled_func = self.funcs[name].cached_compiled_text
|
||||
if compiled_func is None:
|
||||
@ -692,6 +715,7 @@ class _Parser:
|
||||
'continue': (lambda self: self.consume(), lambda self: ContinueNode(self.line_number)),
|
||||
'return': (lambda self: self.consume(), lambda self: ReturnNode(self.line_number, self.top_expr())),
|
||||
'def': (lambda self: None, define_function_expression),
|
||||
'with': (lambda self: None, with_expression)
|
||||
}
|
||||
|
||||
# {inlined_function_name: tuple(constraint on number of length, node builder) }
|
||||
@ -1017,6 +1041,27 @@ class _Interpreter:
|
||||
raise e
|
||||
return val
|
||||
|
||||
def do_node_with(self, prog):
|
||||
line_number = prog.line_number
|
||||
parent_book = self.parent_book
|
||||
v = None
|
||||
try:
|
||||
book_id = int(self.expr(prog.book_id))
|
||||
if self.break_reporter:
|
||||
self.break_reporter("'with': book id ", str(book_id), line_number)
|
||||
self.parent_book = self.parent.book = get_database(
|
||||
self.parent_book, 'with statement').new_api.get_proxy_metadata(book_id)
|
||||
v = self.expression_list(prog.block)
|
||||
if self.break_reporter:
|
||||
self.break_reporter("'with': block value", v, line_number)
|
||||
return v
|
||||
except (StopException, ValueError, ReturnExecuted) as e:
|
||||
raise e
|
||||
except Exception as e:
|
||||
self.error(_("Unhandled exception '{0}'").format(e), line_number)
|
||||
finally:
|
||||
self.parent_book = self.parent.book = parent_book
|
||||
|
||||
def do_node_if(self, prog):
|
||||
line_number = prog.line_number
|
||||
test_part = self.expr(prog.condition)
|
||||
@ -1608,7 +1653,8 @@ class _Interpreter:
|
||||
Node.NODE_LOCAL_FUNCTION_DEFINE: do_node_local_function_define,
|
||||
Node.NODE_LOCAL_FUNCTION_CALL: do_node_local_function_call,
|
||||
Node.NODE_LIST_COUNT_FIELD: do_node_list_count_field,
|
||||
}
|
||||
Node.NODE_WITH: do_node_with,
|
||||
}
|
||||
|
||||
def expr(self, prog):
|
||||
try:
|
||||
@ -1707,6 +1753,7 @@ class TemplateFormatter(string.Formatter):
|
||||
(r'(def|fed|continue)\b', lambda x,t: (_Parser.LEX_KEYWORD, t)),
|
||||
(r'(return|inlist|break)\b', lambda x,t: (_Parser.LEX_KEYWORD, t)),
|
||||
(r'(inlist_field)\b', lambda x,t: (_Parser.LEX_KEYWORD, t)),
|
||||
(r'(with|htiw)\b', lambda x,t: (_Parser.LEX_KEYWORD, t)),
|
||||
(r'(\|\||&&|!|{|})', lambda x,t: (_Parser.LEX_OP, t)),
|
||||
(r'[(),=;:\+\-*/&]', lambda x,t: (_Parser.LEX_OP, t)),
|
||||
(r'-?[\d\.]+', lambda x,t: (_Parser.LEX_CONST, t)),
|
||||
|
||||
@ -55,6 +55,7 @@ CASE_CHANGES = _('Case changes')
|
||||
DATE_FUNCTIONS = _('Date functions')
|
||||
DB_FUNCS = _('Database functions')
|
||||
URL_FUNCTIONS = _('URL functions')
|
||||
GUI_FUNCTIONS = __('GUI functions')
|
||||
|
||||
|
||||
# Class and method to save an untranslated copy of translated strings
|
||||
@ -1249,7 +1250,7 @@ string.
|
||||
class BuiltinApproximateFormats(BuiltinFormatterFunction):
|
||||
name = 'approximate_formats'
|
||||
arg_count = 0
|
||||
category = GET_FROM_METADATA
|
||||
category = DB_FUNCS
|
||||
def __doc__getter__(self): return translate_ffml(
|
||||
r'''
|
||||
``approximate_formats()`` -- return a comma-separated list of formats associated
|
||||
@ -1278,7 +1279,7 @@ column's value in your save/send templates.
|
||||
class BuiltinFormatsModtimes(BuiltinFormatterFunction):
|
||||
name = 'formats_modtimes'
|
||||
arg_count = 1
|
||||
category = GET_FROM_METADATA
|
||||
category = DB_FUNCS
|
||||
def __doc__getter__(self): return translate_ffml(
|
||||
r'''
|
||||
``formats_modtimes(date_format_string)`` -- return a comma-separated list of
|
||||
@ -1302,7 +1303,7 @@ that format names are always uppercase, as in EPUB.
|
||||
class BuiltinFormatsSizes(BuiltinFormatterFunction):
|
||||
name = 'formats_sizes'
|
||||
arg_count = 0
|
||||
category = GET_FROM_METADATA
|
||||
category = DB_FUNCS
|
||||
def __doc__getter__(self): return translate_ffml(
|
||||
r'''
|
||||
|
||||
@ -1323,7 +1324,7 @@ format names are always uppercase, as in EPUB.
|
||||
class BuiltinFormatsPaths(BuiltinFormatterFunction):
|
||||
name = 'formats_paths'
|
||||
arg_count = -1
|
||||
category = GET_FROM_METADATA
|
||||
category = DB_FUNCS
|
||||
def __doc__getter__(self): return translate_ffml(
|
||||
r'''
|
||||
``formats_paths([separator])`` -- return a ``separator``-separated list of
|
||||
@ -1345,7 +1346,7 @@ format names are always uppercase, as in EPUB.
|
||||
class BuiltinFormatsPathSegments(BuiltinFormatterFunction):
|
||||
name = 'formats_path_segments'
|
||||
arg_count = 5
|
||||
category = GET_FROM_METADATA
|
||||
category = DB_FUNCS
|
||||
def __doc__getter__(self): return translate_ffml(
|
||||
r'''
|
||||
``formats_path_segments(with_author, with_title, with_format, with_ext, sep)``
|
||||
@ -1775,7 +1776,7 @@ template, and use that column\'s value in your save/send templates.
|
||||
class BuiltinAnnotationCount(BuiltinFormatterFunction):
|
||||
name = 'annotation_count'
|
||||
arg_count = 0
|
||||
category = GET_FROM_METADATA
|
||||
category = DB_FUNCS
|
||||
def __doc__getter__(self): return translate_ffml(
|
||||
r'''
|
||||
``annotation_count()`` -- return the total number of annotations of all types
|
||||
@ -3649,6 +3650,116 @@ This can be useful to truncate a value.
|
||||
return pat.sub(repl, template)
|
||||
|
||||
|
||||
class BuiltinSelectedBooks(BuiltinFormatterFunction):
|
||||
name = 'selected_books'
|
||||
arg_count = 0
|
||||
category = GUI_FUNCTIONS
|
||||
def __doc__getter__(self): return translate_ffml(
|
||||
r'''
|
||||
``selected_books([sorted_by, ascending])`` -- returns a list of book ids in
|
||||
selection order for the currently selected books.
|
||||
|
||||
This function can be used only in the GUI.
|
||||
''')
|
||||
|
||||
def evaluate(self, formatter, kwargs, mi, locals, *args):
|
||||
from calibre.gui2.ui import get_gui
|
||||
g = get_gui()
|
||||
book_ids = g.current_view().get_selected_ids()
|
||||
return ', '.join([str(book_id) for book_id in book_ids])
|
||||
|
||||
|
||||
class BuiltinSortBookIds(BuiltinFormatterFunction):
|
||||
name = 'sort_book_ids'
|
||||
arg_count = -1
|
||||
category = GUI_FUNCTIONS
|
||||
def __doc__getter__(self): return translate_ffml(
|
||||
r'''
|
||||
``sort_book_ids(book_ids, sorted_by, ascending [, sorted_by, ascending]*)`` --
|
||||
returns the list of book ids sorted by the column specified by the lookup name
|
||||
in ``sorted_by`` in the order specified by ``ascending``. If ``ascending`` is
|
||||
``'1'`` then the books are sorted by the value in the 'sorted_by' column in
|
||||
ascending order, otherwise in descending order. You can have multiple pairs of
|
||||
``sorted_by, ascending``. The first pair specifies the major order.
|
||||
|
||||
This function can be used only in the GUI.
|
||||
''')
|
||||
|
||||
def evaluate(self, formatter, kwargs, mi, locals, book_ids, *args):
|
||||
from calibre.gui2.ui import get_gui
|
||||
g = get_gui()
|
||||
bids = [int(b.strip()) for b in book_ids.split(',')]
|
||||
if len(args) < 2:
|
||||
raise ValueError(_('The sort_book_ids function requires at least 3 arguments'))
|
||||
if len(args) % 2 != 0:
|
||||
raise ValueError(_('The id and direction arguments must be in pairs'))
|
||||
sort_spec = []
|
||||
for i in range(0, len(args), 2):
|
||||
sort_by = args[i]
|
||||
asc = True if args[i+1] == '1' else False
|
||||
sort_spec.append((sort_by, asc))
|
||||
bids = g.current_db.new_api.multisort(sort_spec, bids)
|
||||
return ', '.join([str(b) for b in bids])
|
||||
|
||||
|
||||
class BuiltinSelectedColumn(BuiltinFormatterFunction):
|
||||
name = 'selected_column'
|
||||
arg_count = 0
|
||||
category = GUI_FUNCTIONS
|
||||
def __doc__getter__(self): return translate_ffml(
|
||||
r'''
|
||||
``selected_column()`` -- returns the lookup name of the column containing the currently
|
||||
selected cell. It returns ``''`` if no cell is selected.
|
||||
|
||||
This function can be used only in the GUI.
|
||||
''')
|
||||
|
||||
def evaluate(self, formatter, kwargs, mi, locals):
|
||||
from calibre.gui2.ui import get_gui
|
||||
v = get_gui().current_view()
|
||||
idx = v.currentIndex()
|
||||
if idx.isValid():
|
||||
key = v.column_map[idx.column()]
|
||||
return key
|
||||
return ''
|
||||
|
||||
|
||||
class BuiltinShowDialog(BuiltinFormatterFunction):
|
||||
name = 'show_dialog'
|
||||
arg_count = 1
|
||||
category = GUI_FUNCTIONS
|
||||
def __doc__getter__(self): return translate_ffml(
|
||||
r'''
|
||||
``show_dialog(html_or_text)`` -- show a dialog containing the html or text. The
|
||||
function returns ``'1'`` if the user presses OK, ``''`` if Cancel.
|
||||
|
||||
This function can be used only in the GUI.
|
||||
''')
|
||||
|
||||
def evaluate(self, formatter, kwargs, mi, locals, html):
|
||||
from calibre.gui2.widgets2 import Dialog, HTMLDisplay
|
||||
from qt.core import QDialog, QVBoxLayout
|
||||
|
||||
class HTMLDialog(Dialog):
|
||||
|
||||
def __init__(self, title, prefs):
|
||||
super().__init__(title, 'formatter_html_dialog', prefs=prefs)
|
||||
|
||||
def setup_ui(self):
|
||||
l = QVBoxLayout(self)
|
||||
d = self.display = HTMLDisplay()
|
||||
l.addWidget(d)
|
||||
l.addWidget(self.bb)
|
||||
|
||||
def set_html(self, tt_text):
|
||||
self.display.setHtml(tt_text)
|
||||
|
||||
db = get_database(mi, 'show_dialog')
|
||||
d = HTMLDialog(_('Template output'), db.new_api.backend.prefs)
|
||||
d.set_html(html)
|
||||
return '1' if d.exec() == QDialog.DialogCode.Accepted else ''
|
||||
|
||||
|
||||
_formatter_builtins = [
|
||||
BuiltinAdd(), BuiltinAnd(), BuiltinApproximateFormats(), BuiltinArguments(),
|
||||
BuiltinAssign(),
|
||||
@ -3677,8 +3788,10 @@ _formatter_builtins = [
|
||||
BuiltinMultiply(), BuiltinNot(), BuiltinOndevice(),
|
||||
BuiltinOr(), BuiltinPrint(), BuiltinQueryString(), BuiltinRatingToStars(),
|
||||
BuiltinRange(), BuiltinRawField(), BuiltinRawList(),
|
||||
BuiltinRe(), BuiltinReGroup(), BuiltinRound(), BuiltinSelect(), BuiltinSeriesSort(),
|
||||
BuiltinSetGlobals(), BuiltinShorten(), BuiltinStrcat(), BuiltinStrcatMax(),
|
||||
BuiltinRe(), BuiltinReGroup(), BuiltinRound(), BuiltinSelect(),
|
||||
BuiltinSelectedBooks(), BuiltinSelectedColumn(), BuiltinSeriesSort(),
|
||||
BuiltinSetGlobals(), BuiltinShorten(), BuiltinShowDialog(), BuiltinSortBookIds(),
|
||||
BuiltinStrcat(), BuiltinStrcatMax(),
|
||||
BuiltinStrcmp(), BuiltinStrcmpcase(), BuiltinStrInList(), BuiltinStrlen(), BuiltinSubitems(),
|
||||
BuiltinSublist(),BuiltinSubstr(), BuiltinSubtract(), BuiltinSwapAroundArticles(),
|
||||
BuiltinSwapAroundComma(), BuiltinSwitch(), BuiltinSwitchIf(),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user