diff --git a/qt/aqt/browser/__init__.py b/qt/aqt/browser/__init__.py index af3113e13..15e42606f 100644 --- a/qt/aqt/browser/__init__.py +++ b/qt/aqt/browser/__init__.py @@ -1,10 +1,13 @@ # Copyright: Ankitects Pty Ltd and contributors # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html - from __future__ import annotations +import sys + +import aqt + from .browser import Browser -from .dialogs import CardInfoDialog, ChangeModel, FindDupesDialog +from .dialogs import CardInfoDialog, ChangeModel, FindAndReplaceDialog, FindDupesDialog from .sidebar import ( SidebarItem, SidebarItemType, @@ -30,3 +33,7 @@ from .table import ( StatusDelegate, Table, ) + +# aliases for legacy pathnames +sys.modules["aqt.find_and_replace"] = sys.modules["aqt.browser.dialogs"] +aqt.find_and_replace = sys.modules["aqt.browser.dialogs"] # type: ignore diff --git a/qt/aqt/browser/browser.py b/qt/aqt/browser/browser.py index d489345d0..71623f12b 100644 --- a/qt/aqt/browser/browser.py +++ b/qt/aqt/browser/browser.py @@ -18,12 +18,16 @@ from anki.stats import CardStats from anki.tags import MARKED_TAG from anki.utils import ids2str, isMac from aqt import AnkiQt, gui_hooks -from aqt.browser.dialogs import CardInfoDialog, ChangeModel, FindDupesDialog +from aqt.browser.dialogs import ( + CardInfoDialog, + ChangeModel, + FindAndReplaceDialog, + FindDupesDialog, +) from aqt.browser.sidebar import SidebarTreeView from aqt.browser.table import Table from aqt.editor import Editor from aqt.exporting import ExportDialog -from aqt.find_and_replace import FindAndReplaceDialog from aqt.main import ResetReason from aqt.operations.card import set_card_deck, set_card_flag from aqt.operations.collection import undo diff --git a/qt/aqt/browser/dialogs.py b/qt/aqt/browser/dialogs.py index f98df777a..9f3671d1b 100644 --- a/qt/aqt/browser/dialogs.py +++ b/qt/aqt/browser/dialogs.py @@ -10,14 +10,23 @@ import aqt from anki.consts import * from anki.models import NotetypeDict from anki.notes import NoteId -from aqt import gui_hooks +from aqt import AnkiQt, QWidget, gui_hooks +from aqt.operations.note import find_and_replace +from aqt.operations.tag import find_and_replace_tag from aqt.qt import * from aqt.utils import ( HelpPage, askUser, disable_help_button, openHelp, + qconnect, + restore_combo_history, + restore_combo_index_for_session, + restore_is_checked, restoreGeom, + save_combo_history, + save_combo_index_for_session, + save_is_checked, saveGeom, tr, ) @@ -224,3 +233,103 @@ class CardInfoDialog(QDialog): def reject(self) -> None: saveGeom(self, "revlog") return QDialog.reject(self) + + +class FindAndReplaceDialog(QDialog): + COMBO_NAME = "BrowserFindAndReplace" + + def __init__( + self, parent: QWidget, *, mw: AnkiQt, note_ids: Sequence[NoteId] + ) -> None: + super().__init__(parent) + self.mw = mw + self.note_ids = note_ids + self.field_names: List[str] = [] + + # fetch field names and then show + mw.query_op( + lambda: mw.col.field_names_for_note_ids(note_ids), + success=self._show, + ) + + def _show(self, field_names: Sequence[str]) -> None: + # add "all fields" and "tags" to the top of the list + self.field_names = [ + tr.browsing_all_fields(), + tr.editing_tags(), + ] + list(field_names) + + disable_help_button(self) + self.form = aqt.forms.findreplace.Ui_Dialog() + self.form.setupUi(self) + self.setWindowModality(Qt.WindowModal) + + self._find_history = restore_combo_history( + self.form.find, self.COMBO_NAME + "Find" + ) + self.form.find.completer().setCaseSensitivity(Qt.CaseSensitive) + self._replace_history = restore_combo_history( + self.form.replace, self.COMBO_NAME + "Replace" + ) + self.form.replace.completer().setCaseSensitivity(Qt.CaseSensitive) + + restore_is_checked(self.form.re, self.COMBO_NAME + "Regex") + restore_is_checked(self.form.ignoreCase, self.COMBO_NAME + "ignoreCase") + + self.form.field.addItems(self.field_names) + restore_combo_index_for_session( + self.form.field, self.field_names, self.COMBO_NAME + "Field" + ) + + qconnect(self.form.buttonBox.helpRequested, self.show_help) + + restoreGeom(self, "findreplace") + self.show() + self.form.find.setFocus() + + def accept(self) -> None: + saveGeom(self, "findreplace") + save_combo_index_for_session(self.form.field, self.COMBO_NAME + "Field") + + search = save_combo_history( + self.form.find, self._find_history, self.COMBO_NAME + "Find" + ) + replace = save_combo_history( + self.form.replace, self._replace_history, self.COMBO_NAME + "Replace" + ) + regex = self.form.re.isChecked() + match_case = not self.form.ignoreCase.isChecked() + save_is_checked(self.form.re, self.COMBO_NAME + "Regex") + save_is_checked(self.form.ignoreCase, self.COMBO_NAME + "ignoreCase") + + # tags? + if self.form.field.currentIndex() == 1: + find_and_replace_tag( + parent=self.parentWidget(), + note_ids=self.note_ids, + search=search, + replacement=replace, + regex=regex, + match_case=match_case, + ) + else: + # fields + if self.form.field.currentIndex() == 0: + field = None + else: + field = self.field_names[self.form.field.currentIndex() - 2] + + find_and_replace( + parent=self.parentWidget(), + note_ids=self.note_ids, + search=search, + replacement=replace, + regex=regex, + field_name=field, + match_case=match_case, + ) + + super().accept() + + def show_help(self) -> None: + openHelp(HelpPage.BROWSING_FIND_AND_REPLACE) diff --git a/qt/aqt/find_and_replace.py b/qt/aqt/find_and_replace.py deleted file mode 100644 index 4f6b14e3f..000000000 --- a/qt/aqt/find_and_replace.py +++ /dev/null @@ -1,128 +0,0 @@ -# Copyright: Ankitects Pty Ltd and contributors -# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html - -from __future__ import annotations - -from typing import List, Sequence - -import aqt -from anki.notes import NoteId -from aqt import AnkiQt, QWidget -from aqt.operations.note import find_and_replace -from aqt.operations.tag import find_and_replace_tag -from aqt.qt import QDialog, Qt -from aqt.utils import ( - HelpPage, - disable_help_button, - openHelp, - qconnect, - restore_combo_history, - restore_combo_index_for_session, - restore_is_checked, - restoreGeom, - save_combo_history, - save_combo_index_for_session, - save_is_checked, - saveGeom, - tr, -) - - -class FindAndReplaceDialog(QDialog): - COMBO_NAME = "BrowserFindAndReplace" - - def __init__( - self, parent: QWidget, *, mw: AnkiQt, note_ids: Sequence[NoteId] - ) -> None: - super().__init__(parent) - self.mw = mw - self.note_ids = note_ids - self.field_names: List[str] = [] - - # fetch field names and then show - mw.query_op( - lambda: mw.col.field_names_for_note_ids(note_ids), - success=self._show, - ) - - def _show(self, field_names: Sequence[str]) -> None: - # add "all fields" and "tags" to the top of the list - self.field_names = [ - tr.browsing_all_fields(), - tr.editing_tags(), - ] + list(field_names) - - disable_help_button(self) - self.form = aqt.forms.findreplace.Ui_Dialog() - self.form.setupUi(self) - self.setWindowModality(Qt.WindowModal) - - self._find_history = restore_combo_history( - self.form.find, self.COMBO_NAME + "Find" - ) - self.form.find.completer().setCaseSensitivity(Qt.CaseSensitive) - self._replace_history = restore_combo_history( - self.form.replace, self.COMBO_NAME + "Replace" - ) - self.form.replace.completer().setCaseSensitivity(Qt.CaseSensitive) - - restore_is_checked(self.form.re, self.COMBO_NAME + "Regex") - restore_is_checked(self.form.ignoreCase, self.COMBO_NAME + "ignoreCase") - - self.form.field.addItems(self.field_names) - restore_combo_index_for_session( - self.form.field, self.field_names, self.COMBO_NAME + "Field" - ) - - qconnect(self.form.buttonBox.helpRequested, self.show_help) - - restoreGeom(self, "findreplace") - self.show() - self.form.find.setFocus() - - def accept(self) -> None: - saveGeom(self, "findreplace") - save_combo_index_for_session(self.form.field, self.COMBO_NAME + "Field") - - search = save_combo_history( - self.form.find, self._find_history, self.COMBO_NAME + "Find" - ) - replace = save_combo_history( - self.form.replace, self._replace_history, self.COMBO_NAME + "Replace" - ) - regex = self.form.re.isChecked() - match_case = not self.form.ignoreCase.isChecked() - save_is_checked(self.form.re, self.COMBO_NAME + "Regex") - save_is_checked(self.form.ignoreCase, self.COMBO_NAME + "ignoreCase") - - # tags? - if self.form.field.currentIndex() == 1: - find_and_replace_tag( - parent=self.parentWidget(), - note_ids=self.note_ids, - search=search, - replacement=replace, - regex=regex, - match_case=match_case, - ) - else: - # fields - if self.form.field.currentIndex() == 0: - field = None - else: - field = self.field_names[self.form.field.currentIndex() - 2] - - find_and_replace( - parent=self.parentWidget(), - note_ids=self.note_ids, - search=search, - replacement=replace, - regex=regex, - field_name=field, - match_case=match_case, - ) - - super().accept() - - def show_help(self) -> None: - openHelp(HelpPage.BROWSING_FIND_AND_REPLACE)