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
77d4d2a370
@ -15,6 +15,7 @@ from functools import partial
|
|||||||
from qt.core import (
|
from qt.core import (
|
||||||
QAbstractItemView,
|
QAbstractItemView,
|
||||||
QApplication,
|
QApplication,
|
||||||
|
QCheckBox,
|
||||||
QColor,
|
QColor,
|
||||||
QComboBox,
|
QComboBox,
|
||||||
QCursor,
|
QCursor,
|
||||||
@ -24,6 +25,7 @@ from qt.core import (
|
|||||||
QFontDatabase,
|
QFontDatabase,
|
||||||
QFontInfo,
|
QFontInfo,
|
||||||
QFontMetrics,
|
QFontMetrics,
|
||||||
|
QHBoxLayout,
|
||||||
QIcon,
|
QIcon,
|
||||||
QLineEdit,
|
QLineEdit,
|
||||||
QPalette,
|
QPalette,
|
||||||
@ -60,8 +62,11 @@ from calibre.utils.resources import get_path as P
|
|||||||
|
|
||||||
class DocViewer(Dialog):
|
class DocViewer(Dialog):
|
||||||
|
|
||||||
def __init__(self, docs_dsl, parent=None):
|
def __init__(self, ffml, builtins, function_type_string_method, parent=None):
|
||||||
self.docs_dsl = docs_dsl
|
self.ffml = ffml
|
||||||
|
self.builtins = builtins
|
||||||
|
self.function_type_string = function_type_string_method
|
||||||
|
self.last_operation = None
|
||||||
super().__init__(title=_('Template function documentation'), name='template_editor_doc_viewer_dialog',
|
super().__init__(title=_('Template function documentation'), name='template_editor_doc_viewer_dialog',
|
||||||
default_buttons=QDialogButtonBox.StandardButton.Close, parent=parent)
|
default_buttons=QDialogButtonBox.StandardButton.Close, parent=parent)
|
||||||
|
|
||||||
@ -69,7 +74,6 @@ class DocViewer(Dialog):
|
|||||||
return QSize(800, 600)
|
return QSize(800, 600)
|
||||||
|
|
||||||
def set_html(self, html):
|
def set_html(self, html):
|
||||||
print(html)
|
|
||||||
self.doc_viewer_widget.setHtml(html)
|
self.doc_viewer_widget.setHtml(html)
|
||||||
|
|
||||||
def setup_ui(self):
|
def setup_ui(self):
|
||||||
@ -79,23 +83,50 @@ class DocViewer(Dialog):
|
|||||||
e.setDefaultStyleSheet('pre { font-family: "Segoe UI Mono", "Consolas", monospace; }')
|
e.setDefaultStyleSheet('pre { font-family: "Segoe UI Mono", "Consolas", monospace; }')
|
||||||
e.anchor_clicked.connect(safe_open_url)
|
e.anchor_clicked.connect(safe_open_url)
|
||||||
l.addWidget(e)
|
l.addWidget(e)
|
||||||
l.addWidget(self.bb)
|
bl = QHBoxLayout()
|
||||||
|
l.addLayout(bl)
|
||||||
|
self.english_cb = cb = QCheckBox(_('Show documentation in original &English'))
|
||||||
|
cb.setChecked(gprefs.get('template_editor_docs_in_english', False))
|
||||||
|
cb.stateChanged.connect(self.english_cb_state_changed)
|
||||||
|
bl.addWidget(cb)
|
||||||
|
bl.addWidget(self.bb)
|
||||||
b = self.bb.addButton(_('Show &all functions'), QDialogButtonBox.ButtonRole.ActionRole)
|
b = self.bb.addButton(_('Show &all functions'), QDialogButtonBox.ButtonRole.ActionRole)
|
||||||
b.clicked.connect(self.show_all_functions)
|
b.clicked.connect(self.show_all_functions)
|
||||||
b.setToolTip((_('Shows a list of all built-in functions in alphabetic order')))
|
b.setToolTip((_('Shows a list of all built-in functions in alphabetic order')))
|
||||||
|
|
||||||
def show_function(self):
|
def english_cb_state_changed(self):
|
||||||
self.set_html(
|
if self.last_operation is not None:
|
||||||
self.docs_dsl.document_to_html(self.all_functions[self.current_function_name].doc,
|
self.last_operation()
|
||||||
self.current_function_name))
|
gprefs['template_editor_docs_in_english'] = self.english_cb.isChecked()
|
||||||
|
|
||||||
|
def header_line(self, name):
|
||||||
|
return f'\n<h3>{name} ({self.function_type_string(name, longform=False)})</h3>\n'
|
||||||
|
|
||||||
|
def get_doc(self, func):
|
||||||
|
doc = func.doc if hasattr(func, 'doc') else ''
|
||||||
|
return doc.raw_text if self.english_cb.isChecked() and hasattr(doc, 'raw_text') else doc
|
||||||
|
|
||||||
|
def show_function(self, fname):
|
||||||
|
self.last_operation = partial(self.show_function, fname)
|
||||||
|
bif = self.builtins[fname]
|
||||||
|
if fname not in self.builtins or not bif.doc:
|
||||||
|
self.set_html(self.header_line(fname) + ('No documentation provided'))
|
||||||
|
else:
|
||||||
|
self.set_html(self.header_line(fname) +
|
||||||
|
self.ffml.document_to_html(self.get_doc(bif), fname))
|
||||||
|
|
||||||
def show_all_functions(self):
|
def show_all_functions(self):
|
||||||
funcs = formatter_functions().get_builtins()
|
self.last_operation = self.show_all_functions
|
||||||
result = []
|
result = []
|
||||||
a = result.append
|
a = result.append
|
||||||
for name in sorted(funcs):
|
for name in sorted(self.builtins):
|
||||||
a(f'\n<h2>{name}</h2>\n')
|
a(self.header_line(name))
|
||||||
try:
|
try:
|
||||||
a(self.docs_dsl.document_to_html(funcs[name].doc.strip(), name))
|
doc = self.get_doc(self.builtins[name])
|
||||||
|
if not doc:
|
||||||
|
a(_('No documentation provided'))
|
||||||
|
else:
|
||||||
|
a(self.ffml.document_to_html(doc.strip(), name))
|
||||||
except Exception:
|
except Exception:
|
||||||
print('Exception in', name)
|
print('Exception in', name)
|
||||||
raise
|
raise
|
||||||
@ -410,7 +441,7 @@ class TemplateDialog(QDialog, Ui_TemplateDialog):
|
|||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
self.setWindowIcon(self.windowIcon())
|
self.setWindowIcon(self.windowIcon())
|
||||||
|
|
||||||
self.docs_dsl = FFMLProcessor()
|
self.ffml = FFMLProcessor()
|
||||||
self.dialog_number = dialog_number
|
self.dialog_number = dialog_number
|
||||||
self.coloring = color_field is not None
|
self.coloring = color_field is not None
|
||||||
self.iconing = icon_field_key is not None
|
self.iconing = icon_field_key is not None
|
||||||
@ -572,7 +603,8 @@ class TemplateDialog(QDialog, Ui_TemplateDialog):
|
|||||||
|
|
||||||
def open_documentation_viewer(self):
|
def open_documentation_viewer(self):
|
||||||
if self.doc_viewer is None:
|
if self.doc_viewer is None:
|
||||||
dv = self.doc_viewer = DocViewer(self.docs_dsl, self)
|
dv = self.doc_viewer = DocViewer(self.ffml, self.all_functions,
|
||||||
|
self.function_type_string, parent=self)
|
||||||
dv.finished.connect(self.doc_viewer_finished)
|
dv.finished.connect(self.doc_viewer_finished)
|
||||||
dv.show()
|
dv.show()
|
||||||
if self.current_function_name is not None:
|
if self.current_function_name is not None:
|
||||||
@ -1016,9 +1048,9 @@ def evaluate(book, context):
|
|||||||
self.func_type.clear()
|
self.func_type.clear()
|
||||||
if name in self.all_functions:
|
if name in self.all_functions:
|
||||||
doc = self.all_functions[name].doc.strip()
|
doc = self.all_functions[name].doc.strip()
|
||||||
self.documentation.setHtml(self.docs_dsl.document_to_html(doc, name))
|
self.documentation.setHtml(self.ffml.document_to_html(doc, name))
|
||||||
if self.doc_viewer is not None:
|
if self.doc_viewer is not None:
|
||||||
self.doc_viewer_widget.setHtml(self.docs_dsl.document_to_html(self.all_functions[name].doc, name))
|
self.doc_viewer.show_function(name)
|
||||||
if name in self.builtins and name in self.builtin_source_dict:
|
if name in self.builtins and name in self.builtin_source_dict:
|
||||||
self.source_code.setPlainText(self.builtin_source_dict[name])
|
self.source_code.setPlainText(self.builtin_source_dict[name])
|
||||||
else:
|
else:
|
||||||
|
@ -747,7 +747,9 @@ you the value as well as all the local variables</p></string>
|
|||||||
<string>&Documentation</string>
|
<string>&Documentation</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>Click this button to open the documentation in a separate dialog</string>
|
<string>Click this button to open the documentation in a separate dialog.
|
||||||
|
If no function is selected above then show all functions.
|
||||||
|
Selecting a function will show only that function's documentation</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -762,8 +764,8 @@ you the value as well as all the local variables</p></string>
|
|||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string><p>When using functions in a Single Function Mode template,
|
<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
|
for example {title:uppercase()}, the first parameter 'value' is omitted. It is automatically replaced
|
||||||
by the value of the specified field.</p> In all the other modes the value parameter
|
by the value of the specified field.</p> <p>In all the other modes the value parameter
|
||||||
must be supplied.</string>
|
must be supplied.</p></string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -43,7 +43,7 @@ class Node:
|
|||||||
return self._text
|
return self._text
|
||||||
|
|
||||||
def escaped_text(self):
|
def escaped_text(self):
|
||||||
return prepare_string_for_xml(self._text) #.replace('<', 'LESS_THAN') #.replace('>', '>')
|
return prepare_string_for_xml(self._text)
|
||||||
|
|
||||||
|
|
||||||
class DocumentNode(Node):
|
class DocumentNode(Node):
|
||||||
@ -90,9 +90,15 @@ class UrlNode(Node):
|
|||||||
def label(self):
|
def label(self):
|
||||||
return self._label
|
return self._label
|
||||||
|
|
||||||
|
def escaped_label(self):
|
||||||
|
return prepare_string_for_xml(self._label)
|
||||||
|
|
||||||
def url(self):
|
def url(self):
|
||||||
return self._url
|
return self._url
|
||||||
|
|
||||||
|
def escaped_url(self):
|
||||||
|
return prepare_string_for_xml(self._url)
|
||||||
|
|
||||||
|
|
||||||
class ListNode(Node):
|
class ListNode(Node):
|
||||||
|
|
||||||
@ -258,7 +264,7 @@ class FFMLProcessor:
|
|||||||
elif tree.node_kind() == NodeKinds.BLANK_LINE:
|
elif tree.node_kind() == NodeKinds.BLANK_LINE:
|
||||||
result += '\n<br>\n<br>\n'
|
result += '\n<br>\n<br>\n'
|
||||||
elif tree.node_kind() == NodeKinds.URL:
|
elif tree.node_kind() == NodeKinds.URL:
|
||||||
result += f'<a href="{tree.url()}">{tree.label()}</a>'
|
result += f'<a href="{tree.escaped_url()}">{tree.escaped_label()}</a>'
|
||||||
elif tree.node_kind() == NodeKinds.LIST:
|
elif tree.node_kind() == NodeKinds.LIST:
|
||||||
result += '\n<ul>\n'
|
result += '\n<ul>\n'
|
||||||
for child in tree.children():
|
for child in tree.children():
|
||||||
@ -335,19 +341,28 @@ class FFMLProcessor:
|
|||||||
result = self.tree_to_rst(child, indent, result)
|
result = self.tree_to_rst(child, indent, result)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def document_to_rst(self, document, name):
|
def document_to_rst(self, document, name, indent=0, prefix=None):
|
||||||
"""
|
"""
|
||||||
Given a document in the Formatter Function Markup Language (FFML), return
|
Given a document in the Formatter Function Markup Language (FFML), return
|
||||||
that document in RST (sphinx reStructuredText) format.
|
that document in RST (sphinx reStructuredText) format.
|
||||||
|
|
||||||
:param document: the text in FFML.
|
:param document: the text in FFML.
|
||||||
:param name: the name of the document, used during error
|
:param name: the name of the document, used during error
|
||||||
processing. It is usually the name of the function.
|
processing. It is usually the name of the function.
|
||||||
|
:param indent: the indenting level of the items in the tree. This is
|
||||||
|
usually zero, but can be greater than zero if you want
|
||||||
|
the RST output indented.
|
||||||
|
:param prefix: string. if supplied, this string replaces the indent
|
||||||
|
on the first line of the output. This permits specifying
|
||||||
|
an RST block, for example a bullet list
|
||||||
|
|
||||||
:return: a string containing the RST text
|
:return: a string containing the RST text
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return self.tree_to_rst(self.parse_document(document, name), 0)
|
doc = self.tree_to_rst(self.parse_document(document, name), indent)
|
||||||
|
if prefix is not None:
|
||||||
|
doc = prefix + doc.lstrip(' ' * indent)
|
||||||
|
return doc
|
||||||
|
|
||||||
# ============== Internal methods =================
|
# ============== Internal methods =================
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user