diff --git a/src/calibre/customize/builtins.py b/src/calibre/customize/builtins.py index 5e5d4fff35..ea83688621 100644 --- a/src/calibre/customize/builtins.py +++ b/src/calibre/customize/builtins.py @@ -855,6 +855,12 @@ class ActionBrowseAnnotations(InterfaceActionBase): description = _('Browse highlights and bookmarks from all books in the library') +class ActionFullTextSearch(InterfaceActionBase): + name = 'Full Text Search' + actual_plugin = 'calibre.gui2.actions.fts:FullTextSearchAction' + description = _('Search the full text of all books in the calibre library') + + class ActionEditToC(InterfaceActionBase): name = 'Edit ToC' actual_plugin = 'calibre.gui2.actions.toc_edit:ToCEditAction' @@ -1097,7 +1103,8 @@ plugins += [ActionAdd, ActionFetchAnnotations, ActionGenerateCatalog, ActionCopyToLibrary, ActionTweakEpub, ActionUnpackBook, ActionNextMatch, ActionStore, ActionPluginUpdater, ActionPickRandom, ActionEditToC, ActionSortBy, ActionMarkBooks, ActionEmbed, ActionTemplateTester, ActionTagMapper, ActionAuthorMapper, - ActionVirtualLibrary, ActionBrowseAnnotations, ActionTemplateFunctions, ActionAutoscrollBooks] + ActionVirtualLibrary, ActionBrowseAnnotations, ActionTemplateFunctions, ActionAutoscrollBooks, + ActionFullTextSearch,] # }}} diff --git a/src/calibre/gui2/actions/fts.py b/src/calibre/gui2/actions/fts.py new file mode 100644 index 0000000000..bdfd2f0ed0 --- /dev/null +++ b/src/calibre/gui2/actions/fts.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python +# License: GPL v3 Copyright: 2020, Kovid Goyal + + +from calibre.gui2.actions import InterfaceAction + + +class FullTextSearchAction(InterfaceAction): + + name = 'Full Text Search' + action_spec = (_('Search full text of books'), 'search.png', + _('Search the full text of all books in the calibre library'), _('Z')) + dont_add_to = frozenset(('context-menu-device',)) + action_type = 'current' + + def genesis(self): + self.qaction.triggered.connect(self.show_fts) + self._dialog = None + + @property + def dialog(self): + if self._dialog is None: + from calibre.gui2.fts.dialog import FTSDialog + self._dialog = FTSDialog(self.gui) + return self._dialog + + def show_fts(self): + self.dialog.show() + self.dialog.raise_() + + def library_about_to_change(self, olddb, db): + if self._dialog is not None: + self._dialog.library_changed() diff --git a/src/calibre/gui2/fts/dialog.py b/src/calibre/gui2/fts/dialog.py new file mode 100644 index 0000000000..a39c63d097 --- /dev/null +++ b/src/calibre/gui2/fts/dialog.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python +# vim:fileencoding=utf-8 +# License: GPL v3 Copyright: 2022, Kovid Goyal + + +import os +from qt.core import QDialogButtonBox, QHBoxLayout, QSize, QStackedWidget, QVBoxLayout + +from calibre.gui2.fts.utils import get_db +from calibre.gui2.widgets2 import Dialog + +from calibre.gui2.fts.scan import ScanStatus +from calibre.gui2.fts.search import ResultsPanel + + +class FTSDialog(Dialog): + + def __init__(self, parent=None): + super().__init__(_('Search the text of all books in the library'), 'library-fts-dialog-2', + default_buttons=QDialogButtonBox.StandardButton.Close) + + def setup_ui(self): + l = QVBoxLayout(self) + self.stack = s = QStackedWidget(self) + l.addWidget(s) + h = QHBoxLayout() + h.setContentsMargins(0, 0, 0, 0) + l.addLayout(h) + h.addWidget(self.bb) + self.scan_status = ss = ScanStatus(self) + self.results_panel = rp = ResultsPanel(self) + s.addWidget(ss), s.addWidget(rp) + self.show_scan_status() + + def show_scan_status(self): + self.stack.setCurrentWidget(self.scan_status) + self.scan_status.specialize_button_box(self.bb) + + def show_results_panel(self): + self.stack.setCurrentWidget(self.results_panel) + self.results_panel.specialize_button_box(self.bb) + + def library_changed(self): + self.results_panel.clear_results() + + def sizeHint(self): + return QSize(1000, 680) + + +if __name__ == '__main__': + from calibre.gui2 import Application + from calibre.library import db + get_db.db = db(os.path.expanduser('~/test library')) + app = Application([]) + d = FTSDialog() + d.exec() diff --git a/src/calibre/gui2/fts/search.py b/src/calibre/gui2/fts/search.py index 028a68cb39..bf60e15744 100644 --- a/src/calibre/gui2/fts/search.py +++ b/src/calibre/gui2/fts/search.py @@ -600,6 +600,8 @@ class ResultsPanel(QWidget): def __init__(self, parent=None): super().__init__(parent) + if isinstance(parent, QDialog): + parent.finished.connect(self.shutdown) self.l = l = QVBoxLayout(self) l.setContentsMargins(0, 0, 0, 0) self.splitter = s = QSplitter(self) @@ -618,13 +620,23 @@ class ResultsPanel(QWidget): rv.matches_found.connect(self.sip.matches_found) rv.search_complete.connect(self.sip.stop) sip.search_signal.connect(self.search) - sip.clear_search.connect(rv.model().clear_results) + sip.clear_search.connect(self.clear_results) self.details = d = DetailsPanel(self) rv.current_changed.connect(d.show_result) rv.search_started.connect(d.clear) s.addWidget(d) + def specialize_button_box(self, bb): + bb.clear() + bb.addButton(QDialogButtonBox.StandardButton.Close) + + def clear_results(self): + self.results_view.m.clear_results() + + def shutdown(self): + self.clear_results() + if __name__ == '__main__': from calibre.gui2 import Application