Skip to content

Commit

Permalink
The documentation editing tool with changes to support it.
Browse files Browse the repository at this point in the history
It is an action so it can be opened easily, should a document writer want to use it. It is also available in the Stored templates / User functions dialog.

It is possible that no one other than me will use it. :)
  • Loading branch information
cbhaley committed Nov 13, 2024
1 parent 58a418c commit 8204c80
Show file tree
Hide file tree
Showing 8 changed files with 315 additions and 15 deletions.
9 changes: 8 additions & 1 deletion src/calibre/customize/builtins.py
Original file line number Diff line number Diff line change
Expand Up @@ -1131,6 +1131,13 @@ class ActionBooklistContextMenu(InterfaceActionBase):
description = _('Open the context menu for the column')


class ActionFFDocAsst(InterfaceActionBase):
name = 'Template function document edit'
author = 'Charles Haley'
actual_plugin = 'calibre.gui2.actions.ff_doc_assistant:FFDocEditAssistantAction'
description = _('Open a template function documentation editor')


class ActionAllActions(InterfaceActionBase):
name = 'All GUI actions'
author = 'Charles Haley'
Expand Down Expand Up @@ -1179,7 +1186,7 @@ class ActionPluginUpdater(InterfaceActionBase):
ActionMarkBooks, ActionEmbed, ActionTemplateTester, ActionTagMapper, ActionAuthorMapper,
ActionVirtualLibrary, ActionBrowseAnnotations, ActionTemplateFunctions, ActionAutoscrollBooks,
ActionFullTextSearch, ActionManageCategories, ActionBooklistContextMenu, ActionSavedSearches,
ActionLayoutActions, ActionBrowseNotes,]
ActionLayoutActions, ActionBrowseNotes, ActionFFDocAsst,]

# }}}

Expand Down
27 changes: 27 additions & 0 deletions src/calibre/gui2/actions/ff_doc_assistant.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#!/usr/bin/env python
# License: GPLv3 Copyright: 2022, Charles Haley
#

from qt.core import QMenu, QToolButton

from calibre.gui2.actions import InterfaceAction
from calibre.gui2.dialogs.ff_doc_editor import FFDocEditor


class FFDocEditAssistantAction(InterfaceAction):

name = 'Edit template function documents'
action_spec = (_('Edit template function documents'), 'edit_input.png',
_('Template function documentation editing assistant'), ())
action_type = 'current'
# popup_type = QToolButton.ToolButtonPopupMode.InstantPopup
# action_add_menu = True
dont_add_to = frozenset(['context-menu-device', 'menubar-device'])

def genesis(self):
self.menu = m = self.qaction.menu()
self.qaction.triggered.connect(self.show_editor)

def show_editor(self):
d = FFDocEditor(can_copy_back=False)
d.exec()
187 changes: 187 additions & 0 deletions src/calibre/gui2/dialogs/ff_doc_editor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
#!/usr/bin/env python


__license__ = 'GPL v3'
__copyright__ = '2010, Kovid Goyal <[email protected]>'
__docformat__ = 'restructuredtext en'

'''
Created on 12 Nov 2024
@author: chaley
'''

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
from calibre.gui2.widgets2 import Dialog, HTMLDisplay
from calibre.utils.ffml_processor import FFMLProcessor
from calibre.utils.formatter_functions import formatter_functions


class FFDocEditor(Dialog):

def __init__(self, can_copy_back=False, parent=None):
self.ffml = FFMLProcessor()
self.can_copy_back = can_copy_back
self.last_operation = None
super().__init__(title=_('Template function documentation editor'),
name='template_function_doc_editor_dialog', parent=parent)

def sizeHint(self):
return QSize(800, 600)

def set_document_text(self, text):
self.editable_text_widget.setPlainText(text)

def document_text(self):
return self.editable_text_widget.toPlainText()

def copy_text(self):
QApplication.instance().clipboard().setText(self.document_text())

def html_widget(self, layout, row, column):
e = HTMLDisplay()
e.setFrameStyle(QFrame.Shape.Box)
if iswindows:
e.setDefaultStyleSheet('pre { font-family: "Segoe UI Mono", "Consolas", monospace; }')
layout.addWidget(e, row, column, 1, 1)
return e

def text_widget(self, read_only, layout, row, column):
e = QPlainTextEdit()
e.setReadOnly(read_only)
e.setFrameStyle(QFrame.Shape.Box)
layout.addWidget(e, row, column, 1, 1)
return e

def label_widget(self, text, layout, row, column, colspan=None):
e = QLabel(text)
layout.addWidget(e, row, column, 1, colspan if colspan is not None else 1)
return e

def setup_ui(self):
gl = QGridLayout(self)
hl = QHBoxLayout()

so = self.show_original_cb = QCheckBox(_('Show documentation for function'))
so.setChecked(gprefs.get('template_function_doc_editor_show_original', False))
so.stateChanged.connect(self.first_row_checkbox_changed)
hl.addWidget(so)

f = self.functions_box = QComboBox()
self.builtins = formatter_functions().get_builtins()
f.addItem('')
f.addItems(self.builtins.keys())
hl.addWidget(f)
f.currentIndexChanged.connect(self.functions_box_index_changed)

so = self.show_in_english_cb = QCheckBox(_('Show original English'))
so.stateChanged.connect(self.first_row_checkbox_changed)
hl.addWidget(so)

so = self.show_formatted_cb = QCheckBox(_('Show with placeholders replaced'))
so.stateChanged.connect(self.first_row_checkbox_changed)
hl.addWidget(so)

hl.addStretch()
gl.addLayout(hl, 0, 0, 1, 2)

self.original_doc_label = self.label_widget(
_('Raw documentation for the selected function'), gl, 1, 0)
w = self.original_doc_html_label = self.label_widget(
_('Documentation for the selected function in HTML'), gl, 1, 1)
w.setVisible(so.isChecked())
w = self.original_text_widget = self.text_widget(True, gl, 2, 0)
w.setVisible(so.isChecked())
w = self.original_text_result = self.html_widget(gl, 2, 1)
w.setVisible(so.isChecked())

self.label_widget(_('Document being edited'), gl, 3, 0)
l = QHBoxLayout()
l.addWidget(QLabel(_('Document in HTML')))
cb = self.doc_show_formatted_cb = QCheckBox(_('Show with placeholders replaced'))
cb.setToolTip(_('This requires the original function documentation to be visible above'))
cb.stateChanged.connect(self._editable_box_changed)
l.addWidget(cb)
l.addStretch()
gl.addLayout(l, 3, 1)

w = self.editable_text_widget = self.text_widget(False, gl, 4, 0)
w.textChanged.connect(self.editable_box_changed)
self.editable_text_result = self.html_widget(gl, 4, 1)
if self.can_copy_back:
self.label_widget(_('Text will be stored with the saved template/function'), gl, 5, 0)
else:
self.label_widget(_('You must copy the text then paste it where it is needed'), gl, 5, 0, colspan=2)

l = QHBoxLayout()
b = QPushButton(_('&Copy text'))
b.clicked.connect(self.copy_text)
l.addWidget(b)
l.addStretch()
gl.addLayout(l, 6, 0)
gl.addWidget(self.bb, 6, 1)

self.changed_timer = QTimer()
self.fill_in_top_row()

def editable_box_changed(self):
self.changed_timer.stop()
t = self.changed_timer = QTimer()
t.timeout.connect(self._editable_box_changed)
t.setSingleShot(True)
t.setInterval(250)
t.start()

def _editable_box_changed(self):
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'))
else:
self.editable_text_result.setHtml(
self.ffml.document_to_html(self.editable_text_widget.toPlainText(), 'edited text'))

def fill_in_top_row(self):
to_show = self.show_original_cb.isChecked()
self.original_doc_label.setVisible(to_show)
self.original_doc_html_label.setVisible(to_show)
self.show_in_english_cb.setVisible(to_show)
self.show_formatted_cb.setVisible(to_show)
self.original_text_widget.setVisible(to_show)
self.original_text_result.setVisible(to_show)
if not to_show:
self.doc_show_formatted_cb.setVisible(False)
self._editable_box_changed()
return
name = self.functions_box.currentText()
if name in self.builtins:
doc = self.builtins[name].doc
if not self.can_copy_back:
self.doc_show_formatted_cb.setVisible(True)
if self.show_in_english_cb.isChecked():
html = doc.formatted_english if self.show_formatted_cb.isChecked() else doc.raw_english
self.original_text_widget.setPlainText(doc.raw_english.lstrip())
self.original_text_result.setHtml(self.ffml.document_to_html(html, name))
else:
html = doc.formatted_other if self.show_formatted_cb.isChecked() else doc.raw_other
self.original_text_widget.setPlainText(doc.raw_other.lstrip())
self.original_text_result.setHtml(self.ffml.document_to_html(html, name))
else:
self.original_text_widget.setPlainText('')
self.original_text_result.setHtml(self.ffml.document_to_html('', name))
self.doc_show_formatted_cb.setVisible(False)
self._editable_box_changed()

def first_row_checkbox_changed(self):
gprefs['template_function_doc_editor_show_original'] = self.show_original_cb.isChecked()
self.fill_in_top_row()

def functions_box_index_changed(self, idx):
self.show_original_cb.setChecked(True)
self.fill_in_top_row()
2 changes: 1 addition & 1 deletion src/calibre/gui2/dialogs/template_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ def show_all_functions(self):
self.last_operation = self.show_all_functions
result = []
a = result.append
for name in sorted(self.builtins):
for name in sorted(self.builtins, key=sort_key):
a(self.header_line(name))
try:
doc = self.get_doc(self.builtins[name])
Expand Down
19 changes: 19 additions & 0 deletions src/calibre/gui2/preferences/template_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from qt.core import QDialog, QDialogButtonBox

from calibre.gui2 import error_dialog, gprefs, question_dialog, warning_dialog
from calibre.gui2.dialogs.ff_doc_editor import FFDocEditor
from calibre.gui2.dialogs.template_dialog import TemplateDialog
from calibre.gui2.preferences import AbortInitialize, ConfigWidgetBase, test_widget
from calibre.gui2.preferences.template_functions_ui import Ui_Form
Expand Down Expand Up @@ -187,6 +188,7 @@ def initialize(self):
self.program.textChanged.connect(self.enable_replace_button)
self.create_button.clicked.connect(self.create_button_clicked)
self.delete_button.clicked.connect(self.delete_button_clicked)
self.doc_edit_button.clicked.connect(self.doc_edit_button_clicked)
self.create_button.setEnabled(False)
self.delete_button.setEnabled(False)
self.replace_button.setEnabled(False)
Expand All @@ -206,9 +208,11 @@ def initialize(self):
self.st_delete_button.setEnabled(False)
self.st_replace_button.setEnabled(False)
self.st_test_template_button.setEnabled(False)
self.st_doc_edit_button.setEnabled(False)
self.st_clear_button.clicked.connect(self.st_clear_button_clicked)
self.st_test_template_button.clicked.connect(self.st_test_template)
self.st_replace_button.clicked.connect(self.st_replace_button_clicked)
self.st_doc_edit_button.clicked.connect(self.st_doc_edit_button_clicked)

self.st_current_program_name = ''
self.st_current_program_text = ''
Expand Down Expand Up @@ -239,6 +243,12 @@ def show_only_user_defined_changed(self, state):
def enable_replace_button(self):
self.replace_button.setEnabled(self.delete_button.isEnabled())

def doc_edit_button_clicked(self):
d = FFDocEditor(can_copy_back=True, parent=self)
d.set_document_text(self.documentation.toPlainText())
if d.exec() == QDialog.DialogCode.Accepted:
self.documentation.setPlainText(d.document_text())

def clear_button_clicked(self):
self.build_function_names_box()
self.program.clear()
Expand Down Expand Up @@ -425,6 +435,7 @@ def st_clear_button_clicked(self):
self.template_editor.new_doc.clear()
self.st_create_button.setEnabled(False)
self.st_delete_button.setEnabled(False)
self.st_doc_edit_button.setEnabled(False)

def st_build_function_names_box(self, scroll_to=''):
self.te_name.blockSignals(True)
Expand All @@ -447,6 +458,7 @@ def st_delete_button_clicked(self):
self.changed_signal.emit()
self.st_create_button.setEnabled(True)
self.st_delete_button.setEnabled(False)
self.st_doc_edit_button.setEnabled(False)
self.st_build_function_names_box()
self.te_textbox.setReadOnly(False)
self.st_current_program_name = ''
Expand Down Expand Up @@ -483,6 +495,7 @@ def st_template_name_edited(self, txt):
self.st_delete_button.setEnabled(b)
self.st_test_template_button.setEnabled(b)
self.te_textbox.setReadOnly(False)
self.st_doc_edit_button.setEnabled(True)

def st_function_index_changed(self, idx):
txt = self.te_name.currentText()
Expand Down Expand Up @@ -518,6 +531,12 @@ def st_replace_button_clicked(self):
self.st_delete_button_clicked()
self.st_create_button_clicked(use_name=name)

def st_doc_edit_button_clicked(self):
d = FFDocEditor(can_copy_back=True, parent=self)
d.set_document_text(self.template_editor.new_doc.toPlainText())
if d.exec() == QDialog.DialogCode.Accepted:
self.template_editor.new_doc.setPlainText(d.document_text())

def commit(self):
pref_value = []
for name, cls in iteritems(self.funcs):
Expand Down
Loading

0 comments on commit 8204c80

Please sign in to comment.