From 30fe553c2810f2c504c4e9b7853c7a458f781ffb Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 19 Jun 2022 11:35:24 +0530 Subject: [PATCH] Viewer: Allow using search expressions for --open-at --- src/calibre/gui2/viewer/main.py | 7 +++++-- src/calibre/gui2/viewer/search.py | 18 +++++++++++++----- src/calibre/gui2/viewer/ui.py | 18 ++++++++++++++++-- src/calibre/gui2/viewer/web_view.py | 2 ++ 4 files changed, 36 insertions(+), 9 deletions(-) diff --git a/src/calibre/gui2/viewer/main.py b/src/calibre/gui2/viewer/main.py index 2c05574416..2506deaf8e 100644 --- a/src/calibre/gui2/viewer/main.py +++ b/src/calibre/gui2/viewer/main.py @@ -131,7 +131,10 @@ View an e-book. 'the string "something". The form toc-href:something will match the ' 'href (internal link destination) of toc nodes. The matching is exact. ' 'If you want to match a substring, use the form toc-href-contains:something. ' - 'The form ref:something will use Reference mode references.')) + 'The form ref:something will use Reference mode references. The form search:something will' + ' search for something after opening the book. The form regex:something will search' + ' for the regular expression something after opening the book.' + )) a('--continue', default=False, action='store_true', dest='continue_reading', help=_('Continue reading the last opened book')) @@ -197,7 +200,7 @@ def main(args=sys.argv): oat = opts.open_at if oat and not ( oat.startswith('toc:') or oat.startswith('toc-href:') or oat.startswith('toc-href-contains:') or - oat.startswith('epubcfi(/') or is_float(oat) or oat.startswith('ref:')): + oat.startswith('epubcfi(/') or is_float(oat) or oat.startswith('ref:') or oat.startswith('search:') or oat.startswith('regex:')): raise SystemExit(f'Not a valid --open-at value: {opts.open_at}') if get_session_pref('singleinstance', False): diff --git a/src/calibre/gui2/viewer/search.py b/src/calibre/gui2/viewer/search.py index 42f2f0a9bb..2c770cfb43 100644 --- a/src/calibre/gui2/viewer/search.py +++ b/src/calibre/gui2/viewer/search.py @@ -319,9 +319,10 @@ def search_in_name(name, search_query, ctx_size=75): yield match.span() else: spans = [] - a = lambda s, l: spans.append((s, s + l)) - primary_collator_without_punctuation().find_all(search_query.text, raw, a, search_query.mode == 'word') miter = lambda: spans + if raw: + a = lambda s, l: spans.append((s, s + l)) + primary_collator_without_punctuation().find_all(search_query.text, raw, a, search_query.mode == 'word') for (start, end) in miter(): before = raw[max(0, start-ctx_size):start] @@ -454,9 +455,16 @@ class SearchInput(QWidget): # {{{ def find_previous(self): self.emit_search(backwards=True) - def focus_input(self, text=None): + def focus_input(self, text=None, search_type=None, case_sensitive=None): if text and hasattr(text, 'rstrip'): self.search_box.setText(text) + if search_type is not None: + idx = self.query_type.findData(search_type) + if idx < 0: + idx = self.query_type.findData('normal') + self.query_type.setCurrentIndex(idx) + if case_sensitive is not None: + self.case_sensitive.setChecked(bool(case_sensitive)) self.search_box.setFocus(Qt.FocusReason.OtherFocusReason) le = self.search_box.lineEdit() le.end(False) @@ -668,8 +676,8 @@ class SearchPanel(QWidget): # {{{ def update_hidden_message(self): self.hidden_message.setVisible(self.results.current_result_is_hidden) - def focus_input(self, text=None): - self.search_input.focus_input(text) + def focus_input(self, text=None, search_type=None, case_sensitive=None): + self.search_input.focus_input(text, search_type, case_sensitive) def search_cleared(self): self.results.clear_all_results() diff --git a/src/calibre/gui2/viewer/ui.py b/src/calibre/gui2/viewer/ui.py index 8c494bb415..68a883522d 100644 --- a/src/calibre/gui2/viewer/ui.py +++ b/src/calibre/gui2/viewer/ui.py @@ -99,6 +99,7 @@ class EbookViewer(MainWindow): t.setSingleShot(True), t.setInterval(3000), t.setTimerType(Qt.TimerType.VeryCoarseTimer) connect_lambda(t.timeout, self, lambda self: self.save_annotations(in_book_file=False)) self.pending_open_at = open_at + self.pending_search = None self.base_window_title = _('E-book viewer') self.setDockOptions(QMainWindow.DockOption.AnimatedDocks | QMainWindow.DockOption.AllowTabbedDocks | QMainWindow.DockOption.AllowNestedDocks) self.setWindowTitle(self.base_window_title) @@ -195,6 +196,7 @@ class EbookViewer(MainWindow): self.web_view.highlights_changed.connect(self.highlights_changed) self.web_view.update_reading_rates.connect(self.update_reading_rates) self.web_view.edit_book.connect(self.edit_book) + self.web_view.content_file_changed.connect(self.content_file_changed) self.actions_toolbar.initialize(self.web_view, self.search_dock.toggleViewAction()) at.update_action_state(False) self.setCentralWidget(self.web_view) @@ -310,11 +312,11 @@ class EbookViewer(MainWindow): if not is_visible: self.toc.scroll_to_current_toc_node() - def show_search(self, text, trigger=False): + def show_search(self, text, trigger=False, search_type=None, case_sensitive=None): self.search_dock.setVisible(True) self.search_dock.activateWindow() self.search_dock.raise_() - self.search_widget.focus_input(text) + self.search_widget.focus_input(text, search_type, case_sensitive) if trigger: self.search_widget.trigger() @@ -425,6 +427,11 @@ class EbookViewer(MainWindow): self.loading_overlay.hide() self.actions_toolbar.update_action_state(True) + def content_file_changed(self, fname): + if self.pending_search: + search, self.pending_search = self.pending_search, None + self.show_search(text=search['query'], trigger=True, search_type=search['type'], case_sensitive=search['case_sensitive']) + def show_error(self, title, msg, details): self.loading_overlay.hide() error_dialog(self, title, msg, det_msg=details or None, show=True) @@ -526,6 +533,7 @@ class EbookViewer(MainWindow): if self.shutting_down: return open_at, self.pending_open_at = self.pending_open_at, None + self.pending_search = None self.web_view.clear_caches() if not ok: self.actions_toolbar.update_action_state(False) @@ -577,6 +585,12 @@ class EbookViewer(MainWindow): initial_position = {'type': 'cfi', 'data': open_at} elif open_at.startswith('ref:'): initial_position = {'type': 'ref', 'data': open_at[len('ref:'):]} + elif open_at.startswith('search:'): + self.pending_search = {'type': 'normal', 'query': open_at[len('search:'):], 'case_sensitive': False} + initial_position = {'type': 'bookpos', 'data': 0} + elif open_at.startswith('regex:'): + self.pending_search = {'type': 'regex', 'query': open_at[len('regex:'):], 'case_sensitive': True} + initial_position = {'type': 'bookpos', 'data': 0} elif is_float(open_at): initial_position = {'type': 'bookpos', 'data': float(open_at)} highlights = self.current_book_data['annotations_map']['highlight'] diff --git a/src/calibre/gui2/viewer/web_view.py b/src/calibre/gui2/viewer/web_view.py index 3d0bd3258c..c2c009ea28 100644 --- a/src/calibre/gui2/viewer/web_view.py +++ b/src/calibre/gui2/viewer/web_view.py @@ -462,6 +462,7 @@ class WebView(RestartingWebEngineView): paged_mode_changed = pyqtSignal() standalone_misc_settings_changed = pyqtSignal(object) view_created = pyqtSignal(object) + content_file_changed = pyqtSignal(str) def __init__(self, parent=None): self._host_widget = None @@ -623,6 +624,7 @@ class WebView(RestartingWebEngineView): def on_content_file_changed(self, data): self.current_content_file = data + self.content_file_changed.emit(self.current_content_file) def start_book_load(self, initial_position=None, highlights=None, current_book_data=None, reading_rates=None): key = (set_book_path.path,)