diff --git a/src/calibre/gui2/custom_column_widgets.py b/src/calibre/gui2/custom_column_widgets.py index b328d7b6ee..98f0957bfa 100644 --- a/src/calibre/gui2/custom_column_widgets.py +++ b/src/calibre/gui2/custom_column_widgets.py @@ -18,6 +18,7 @@ from qt.core import ( from calibre.ebooks.metadata import title_sort from calibre.gui2 import UNDEFINED_QDATETIME, elided_text, error_dialog, gprefs from calibre.gui2.comments_editor import Editor as CommentsEditor +from calibre.gui2.markdown_editor import Editor as MarkdownEditor from calibre.gui2.complete2 import EditWithComplete as EWC from calibre.gui2.dialogs.tag_editor import TagEditor from calibre.gui2.library.delegates import ClearingDoubleSpinBox, ClearingSpinBox @@ -467,6 +468,41 @@ class Comments(Base): self.signals_to_disconnect.append(self._tb.data_changed) +class Markdown(Base): + + def setup_ui(self, parent): + self._box = QGroupBox(parent) + self._box.setTitle(label_string(self.col_metadata['name'])) + self._layout = QVBoxLayout() + self._tb = MarkdownEditor(self._box) + self._tb.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) + # self._tb.setTabChangesFocus(True) + self._layout.addWidget(self._tb) + self._box.setLayout(self._layout) + self.widgets = [self._box] + + def setter(self, val): + self._tb.markdown = str(val or '').strip() + + def getter(self): + val = self._tb.markdown.strip() + if not val: + val = None + return val + + @property + def tab(self): + return self._tb.tab + + @tab.setter + def tab(self, val): + self._tb.tab = val + + def connect_data_changed(self, slot): + self._tb.editor.textChanged.connect(slot) + self.signals_to_disconnect.append(self._tb.editor.textChanged) + + class MultipleWidget(QWidget): def __init__(self, parent, only_manage_items=False, widget=EditWithComplete, name=None): @@ -780,8 +816,10 @@ def comments_factory(db, key, parent): ctype = fm.get('display', {}).get('interpret_as', 'html') if ctype == 'short-text': return SimpleText(db, key, parent) - if ctype in ('long-text', 'markdown'): + if ctype == 'long-text': return LongText(db, key, parent) + if ctype == 'markdown': + return Markdown(db, key, parent) return Comments(db, key, parent) diff --git a/src/calibre/gui2/markdown_editor.py b/src/calibre/gui2/markdown_editor.py new file mode 100644 index 0000000000..f709ad2675 --- /dev/null +++ b/src/calibre/gui2/markdown_editor.py @@ -0,0 +1,122 @@ +#!/usr/bin/env python + + +__license__ = 'GPL v3' +__copyright__ = '2023, un_pogaz ' +__docformat__ = 'restructuredtext en' + +from qt.core import ( + QPlainTextEdit, Qt, QTabWidget, QVBoxLayout, QWidget, +) + +from calibre.gui2.book_details import css +from calibre.gui2.widgets2 import HTMLDisplay +from calibre.library.comments import markdown + + +class Editor(QWidget): # {{{ + + def __init__(self, parent=None): + QWidget.__init__(self, parent) + self._layout = QVBoxLayout(self) + self.setLayout(self._layout) + self.tabs = QTabWidget(self) + self.tabs.setTabPosition(QTabWidget.TabPosition.South) + self._layout.addWidget(self.tabs) + + self.editor = QPlainTextEdit(self.tabs) + + self.preview = HTMLDisplay(self.tabs) + self.preview.setDefaultStyleSheet(css()) + self.preview.setTabChangesFocus(True) + + self.tabs.addTab(self.editor, _('&Markdown source')) + self.tabs.addTab(self.preview, _('&Preview')) + + self.tabs.currentChanged[int].connect(self.change_tab) + self.layout().setContentsMargins(0, 0, 0, 0) + + def set_minimum_height_for_editor(self, val): + self.editor.setMinimumHeight(val) + + @property + def markdown(self): + self.tabs.setCurrentIndex(0) + return self.editor.toPlainText().strip() + + @markdown.setter + def markdown(self, v): + self.editor.setPlainText(str(v or '')) + + def change_tab(self, index): + if index == 0: # changing to source + pass + if index == 1: # changing to preview + html = markdown(self.editor.toPlainText().strip()) + val = f'''\ + + + +
{html}
+ + ''' + self.preview.setHtml(val) + + @property + def tab(self): + return 'code' if self.tabs.currentWidget() is self.editor else 'preview' + + @tab.setter + def tab(self, val): + self.tabs.setCurrentWidget(self.preview if val == 'preview' else self.editor) + + def set_readonly(self, val): + self.editor.setReadOnly(bool(val)) + + def hide_tabs(self): + self.tabs.tabBar().setVisible(False) + + def smarten_punctuation(self): + from calibre.ebooks.conversion.preprocess import smarten_punctuation + markdown = self.markdown + newmarkdown = smarten_punctuation(markdown) + if markdown != newmarkdown: + self.markdown = newmarkdown + +# }}} + + +if __name__ == '__main__': + from calibre.gui2 import Application + app = Application([]) + w = Editor() + w.resize(800, 600) + w.setWindowFlag(Qt.WindowType.Dialog) + w.show() + w.markdown = '''\ +test *italic* **bold** ***bold-italic*** `code` [link](https://calibre-ebook.com) span + +> Blockquotes + + if '>'+' '*4 in string: + pre_block() + +1. list 1 + 1. list 1.1 + 2. list 1.2 +2. list 2 + +*** + +* list +- list + +# Headers 1 +## Headers 2 +### Headers 3 +#### Headers 4 +##### Headers 5 +###### Headers 6 +''' + app.exec() + # print(w.markdown)