mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Merge branch 'master' of https://github.com/cbhaley/calibre
This commit is contained in:
commit
e035674bdb
@ -11,7 +11,8 @@ Created on 12 Nov 2024
|
||||
@author: chaley
|
||||
'''
|
||||
|
||||
from qt.core import QApplication, QCheckBox, QComboBox, QFrame, QGridLayout, QHBoxLayout, QLabel, QPlainTextEdit, QPushButton, QSize, QTimer
|
||||
from qt.core import (QApplication, QCheckBox, QComboBox, QFrame, QLabel, QGridLayout,
|
||||
QHBoxLayout, QPlainTextEdit, QPushButton, QSize, QTimer)
|
||||
|
||||
from calibre.constants import iswindows
|
||||
from calibre.gui2 import gprefs
|
||||
@ -139,12 +140,18 @@ class FFDocEditor(Dialog):
|
||||
name = self.functions_box.currentText()
|
||||
if name and self.doc_show_formatted_cb.isVisible() and self.doc_show_formatted_cb.isChecked():
|
||||
doc = self.builtins[name].doc
|
||||
self.editable_text_result.setHtml(
|
||||
self.ffml.document_to_html(doc.format_again(
|
||||
self.editable_text_widget.toPlainText()), 'edited text'))
|
||||
try:
|
||||
self.editable_text_result.setHtml(
|
||||
self.ffml.document_to_html(doc.format_again(
|
||||
self.editable_text_widget.toPlainText()), 'edited text'))
|
||||
except Exception as e:
|
||||
self.editable_text_result.setHtml(str(e))
|
||||
else:
|
||||
self.editable_text_result.setHtml(
|
||||
self.ffml.document_to_html(self.editable_text_widget.toPlainText(), 'edited text'))
|
||||
try:
|
||||
self.editable_text_result.setHtml(
|
||||
self.ffml.document_to_html(self.editable_text_widget.toPlainText(), 'edited text'))
|
||||
except Exception as e:
|
||||
self.editable_text_result.setHtml(str(e))
|
||||
|
||||
def fill_in_top_row(self):
|
||||
to_show = self.show_original_cb.isChecked()
|
||||
|
@ -47,6 +47,7 @@ from calibre.ebooks.metadata.book.base import Metadata
|
||||
from calibre.ebooks.metadata.book.formatter import SafeFormat
|
||||
from calibre.gui2 import choose_files, choose_save_file, error_dialog, gprefs, pixmap_to_data, question_dialog, safe_open_url
|
||||
from calibre.gui2.dialogs.template_dialog_ui import Ui_TemplateDialog
|
||||
from calibre.gui2.dialogs.template_general_info import GeneralInformationDialog
|
||||
from calibre.gui2.widgets2 import Dialog, HTMLDisplay
|
||||
from calibre.library.coloring import color_row_key, displayable_columns
|
||||
from calibre.utils.config_base import tweaks
|
||||
@ -542,6 +543,7 @@ class TemplateDialog(QDialog, Ui_TemplateDialog):
|
||||
self.documentation.setReadOnly(True)
|
||||
self.source_code.setReadOnly(True)
|
||||
self.doc_button.clicked.connect(self.open_documentation_viewer)
|
||||
self.general_info_button.clicked.connect(self.open_general_info_dialog)
|
||||
|
||||
if text is not None:
|
||||
if text_is_placeholder:
|
||||
@ -615,6 +617,9 @@ class TemplateDialog(QDialog, Ui_TemplateDialog):
|
||||
def doc_viewer_finished(self):
|
||||
self.doc_viewer = None
|
||||
|
||||
def open_general_info_dialog(self):
|
||||
GeneralInformationDialog().exec()
|
||||
|
||||
def geometry_string(self, txt):
|
||||
if self.dialog_number is None or self.dialog_number == 0:
|
||||
return txt
|
||||
|
@ -754,18 +754,13 @@ Selecting a function will show only that function's documentation</string>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="some_label">
|
||||
<widget class="QPushButton" name="general_info_button">
|
||||
<property name="text">
|
||||
<string>See tooltip for general information</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
<string>General &Information</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><p>When using functions in a Single function mode template,
|
||||
for example {title:uppercase()}, the first parameter 'value' is omitted. It is automatically replaced
|
||||
by the value of the specified field.</p> <p>In all the other modes the value parameter
|
||||
must be supplied.</p></string>
|
||||
<string>Click this button to see general help about using template functions
|
||||
and how the are documented.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
114
src/calibre/gui2/dialogs/template_general_info.py
Normal file
114
src/calibre/gui2/dialogs/template_general_info.py
Normal file
@ -0,0 +1,114 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
|
||||
__copyright__ = '2024, Kovid Goyal kovid@kovidgoyal.net'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
__license__ = 'GPL v3'
|
||||
|
||||
'''
|
||||
@author: Charles Haley
|
||||
'''
|
||||
|
||||
from qt.core import (QDialogButtonBox, QVBoxLayout)
|
||||
|
||||
from calibre.constants import iswindows
|
||||
from calibre.gui2.widgets2 import Dialog, HTMLDisplay
|
||||
from calibre.utils.ffml_processor import FFMLProcessor
|
||||
|
||||
class GeneralInformationDialog(Dialog):
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(title=_('Template function general information'), name='template_editor_gen_info_dialog',
|
||||
default_buttons=QDialogButtonBox.StandardButton.Close, parent=parent)
|
||||
|
||||
def setup_ui(self):
|
||||
l = QVBoxLayout(self)
|
||||
e = HTMLDisplay(self)
|
||||
l.addWidget(e)
|
||||
if iswindows:
|
||||
e.setDefaultStyleSheet('pre { font-family: "Segoe UI Mono", "Consolas", monospace; }')
|
||||
l.addWidget(self.bb)
|
||||
e.setHtml(FFMLProcessor().document_to_html(information, 'Template Information'))
|
||||
|
||||
|
||||
information = '''
|
||||
[LIST]
|
||||
[*]`Functions in Single Function Mode templates`
|
||||
|
||||
When using functions in a Single function mode template,
|
||||
for example ``{title:uppercase()}``, the first parameter ``value`` is omitted.
|
||||
It is automatically replaced by the value of the specified field.
|
||||
|
||||
In all the other modes the value parameter must be supplied.
|
||||
[*]`Editor for asssting with template function documentation`
|
||||
|
||||
An editor is available for helping write template function documentation. Given a document
|
||||
in the Formatter Function Markup Language, it show the resulting HTML. The HTML is updated as you edit.
|
||||
|
||||
This editor is available in two ways:
|
||||
[LIST]
|
||||
[*]Using the command
|
||||
[CODE]
|
||||
calibre-debug -c "from calibre.gui2.dialogs.ff_doc_editor import main; main()"[/CODE]
|
||||
all on one line.
|
||||
[*]By defining a keyboard shortcut in calibre for the action `Open the template
|
||||
documenation editor` in the `Miscellaneous` section. There is no default shortcut.
|
||||
[/LIST]
|
||||
[*]The `Template Function Markup Language`
|
||||
|
||||
Format Function Markup Language (FFML) is a basic markup language used to
|
||||
document formatter functions. It is based on a combination of RST used by sphinx
|
||||
and BBCODE used by many bulletin board systems such as MobileRead. It provides a
|
||||
way to specify:
|
||||
[LIST]
|
||||
[*]Inline program code text: surround this text with \`\` as in \`\`foo\`\`. Tags inside the text are ignored.
|
||||
[*]Italic text: surround this text with \`. Example \`foo\` produces `foo`.
|
||||
[*]Text intended to reference a calibre GUI action. This uses RST syntax. Example: ``:guilabel:`Preferences->Advanced->Template functions``. For HTML the produced text is in a different font. as in :guilabel:`Some text`
|
||||
[*]Empty lines, indicated by two newlines in a row. A visible empty line in the FFMC will become an empty line in the output.
|
||||
[*]URLs. The syntax is similar to BBCODE: ``[URL href="http..."]Link text[/URL]``. Example: ``[URL href="https://en.wikipedia.org/wiki/ISO_8601"]ISO[/URL]`` produces [URL href="https://en.wikipedia.org/wiki/ISO_8601"]ISO[/URL]
|
||||
[*]Internal function reference links. These are links to formatter function
|
||||
documentation. The syntax is the same as guilabel. Example: ``:ref:`get_note` ``.
|
||||
The characters '()' are automatically added to the function name when
|
||||
displayed. For HTML it generates the same as the inline program code text
|
||||
operator (\`\`) with no link. Example: ``:ref:`add` `` produces ``add()``.
|
||||
For RST it generates a ``:ref:`` reference that works only in an RST document
|
||||
containing formatter function documentation. Example: ``:ref:`get_note` ``
|
||||
generates ``:ref:`get_note() <ff_get_note>``
|
||||
[*]Example program code text blocks. Surround the code block with ``[CODE]``
|
||||
and ``[/CODE]`` tags. These tags must be first on a line. Example:
|
||||
|
||||
[CODE]
|
||||
\[CODE]program:
|
||||
get_note('authors', 'Isaac Asimov', 1)
|
||||
\[/CODE]
|
||||
[/CODE]
|
||||
produces
|
||||
[CODE]
|
||||
program:
|
||||
get_note('authors', 'Isaac Asimov', 1)[/CODE]
|
||||
|
||||
[*]Bulleted lists, using BBCODE tags. Surround the list with ``[LIST]`` and
|
||||
``[/LIST]``. List items are indicated with ``[*]``. All of the tags must be
|
||||
first on a line. Bulleted lists can be nested and can contain other FFML
|
||||
elements.
|
||||
|
||||
Example: a two bullet list containing CODE blocks
|
||||
[CODE]
|
||||
\[LIST]
|
||||
[*]Return the HTML of the note attached to the tag `Fiction`:
|
||||
\[CODE]
|
||||
program:
|
||||
get_note('tags', 'Fiction', '')
|
||||
\[/CODE]
|
||||
[*]Return the plain text of the note attached to the author `Isaac Asimov`:
|
||||
\[CODE]
|
||||
program:
|
||||
get_note('authors', 'Isaac Asimov', 1)
|
||||
\[/CODE]
|
||||
\[/LIST]
|
||||
[/CODE]
|
||||
|
||||
[*]HTML output contains no CSS and does not start with a tag such as <DIV> or <P>.
|
||||
[/LIST]
|
||||
[/LIST]
|
||||
'''
|
@ -43,6 +43,7 @@ from calibre.gui2.auto_add import AutoAdder
|
||||
from calibre.gui2.changes import handle_changes
|
||||
from calibre.gui2.cover_flow import CoverFlowMixin
|
||||
from calibre.gui2.device import DeviceMixin
|
||||
from calibre.gui2.dialogs.ff_doc_editor import FFDocEditor
|
||||
from calibre.gui2.dialogs.message_box import JobError
|
||||
from calibre.gui2.ebook_download import EbookDownloadMixin
|
||||
from calibre.gui2.email import EmailMixin
|
||||
@ -329,6 +330,13 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
|
||||
action=self.alt_esc_action)
|
||||
self.alt_esc_action.triggered.connect(self.clear_additional_restriction)
|
||||
|
||||
self.ff_doc_editor_action = QAction(self)
|
||||
self.addAction(self.ff_doc_editor_action)
|
||||
self.keyboard.register_shortcut('open ff document editor',
|
||||
_('Open the template documentation editor'), default_keys=(''),
|
||||
action=self.ff_doc_editor_action)
|
||||
self.ff_doc_editor_action.triggered.connect(self.open_ff_doc_editor)
|
||||
|
||||
# ###################### Start spare job server ########################
|
||||
QTimer.singleShot(1000, self.create_spare_pool)
|
||||
|
||||
@ -462,6 +470,9 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
|
||||
def esc(self, *args):
|
||||
self.search.clear()
|
||||
|
||||
def open_ff_doc_editor(self):
|
||||
FFDocEditor(False).exec()
|
||||
|
||||
def focus_current_view(self):
|
||||
view = self.current_view()
|
||||
if view is self.library_view:
|
||||
|
@ -41,10 +41,10 @@ class Node:
|
||||
return self._children
|
||||
|
||||
def text(self):
|
||||
return self._text
|
||||
return self._text.replace('\\', '')
|
||||
|
||||
def escaped_text(self):
|
||||
return prepare_string_for_xml(self._text)
|
||||
return prepare_string_for_xml(self.text())
|
||||
|
||||
|
||||
class BlankLineNode(Node):
|
||||
@ -248,6 +248,7 @@ class FFMLProcessor:
|
||||
|
||||
:return: a parse tree for the document
|
||||
"""
|
||||
self.input_line = 1
|
||||
self.input = doc
|
||||
self.input_pos = 0
|
||||
self.document_name = name
|
||||
@ -404,13 +405,16 @@ class FFMLProcessor:
|
||||
self.document = DocumentNode()
|
||||
self.input = None
|
||||
self.input_pos = 0
|
||||
self.input_line = 1
|
||||
|
||||
def error(self, message):
|
||||
raise ValueError(f'{message} on line {self.input_line} in "{self.document_name}"')
|
||||
|
||||
def find(self, for_what):
|
||||
p = self.input.find(for_what, self.input_pos)
|
||||
if p < 0:
|
||||
return -1
|
||||
while p > 0 and self.input[p-1] == '\\':
|
||||
p = self.input.find(for_what, p+1)
|
||||
return -1 if p < 0 else p - self.input_pos
|
||||
|
||||
def move_pos(self, to_where):
|
||||
@ -450,7 +454,9 @@ class FFMLProcessor:
|
||||
return len(self.input)
|
||||
|
||||
def get_code_block(self):
|
||||
self.move_pos(len('[CODE]\n'))
|
||||
self.move_pos(len('[CODE]'))
|
||||
if self.text_to(1) == '\n':
|
||||
self.move_pos(1)
|
||||
end = self.find('[/CODE]')
|
||||
if end < 0:
|
||||
self.error('Missing [/CODE] for block')
|
||||
@ -469,6 +475,11 @@ class FFMLProcessor:
|
||||
self.move_pos(end + len('``'))
|
||||
return node
|
||||
|
||||
def get_escaped_char(self):
|
||||
node = EscapedCharNode(self.text_to(1))
|
||||
self.move_pos(1)
|
||||
return node
|
||||
|
||||
def get_gui_label(self):
|
||||
self.move_pos(len(':guilabel:`'))
|
||||
end = self.find('`')
|
||||
|
Loading…
x
Reference in New Issue
Block a user