Anki/qt/aqt/browser/find_and_replace.py
Ben Nguyen 931e1d80f2
Enable strict_optional in aqt/. and aqt/browser (#3486)
* Boolean naming convention

* Rename no_strict_optional -> strict_optional

* Update CONTRIBUTORS

* Enable strict optional for aqt module

* Fix errors

* Enable strict optional for aqt browser

* Fix layout.py errors

* Fix find_duplicates.py errors

* Fix browser.py errors

* Revert a0 a1 names

* Fix tree.py errors

* Fix previewer.py errors

* Fix model.py errors

* Fix find_and_replace.py errors

* Fix item.py errors

* Fix toolbar.py errors

* Fix table/__init__.py errors

* Fix model.py errors

* Fix state.py errors

* Fix table.py errors

* Fix errors in card_info.py

* Fix searchbar.py errors

* Fix name

* Fix assert in browser.py

* Formatting

* Fix assert vh

* assert is not None instead of truthy

* Split _move_current() up to correct type signature (dae)

We want either index or direction, but not both.
2024-10-11 23:12:48 +10:00

179 lines
5.6 KiB
Python

# 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 collections.abc import Sequence
import aqt
import aqt.forms
import aqt.operations
from anki.notes import NoteId
from aqt import AnkiQt
from aqt.operations import QueryOp
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,
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,
tooltip,
tr,
)
class FindAndReplaceDialog(QDialog):
COMBO_NAME = "BrowserFindAndReplace"
def __init__(
self,
parent: QWidget,
*,
mw: AnkiQt,
note_ids: Sequence[NoteId],
field: str | None = None,
) -> None:
"""
If 'field' is passed, only this is added to the field selector.
Otherwise, the fields belonging to the 'note_ids' are added.
"""
super().__init__(parent)
self.mw = mw
self.note_ids = note_ids
self.field_names: list[str] = []
self._field = field
if field:
self._show([field])
elif note_ids:
# fetch field names and then show
QueryOp(
parent=mw,
op=lambda col: col.field_names_for_note_ids(note_ids),
success=self._show,
).run_in_background()
else:
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.WindowModality.WindowModal)
self._find_history = restore_combo_history(
self.form.find, self.COMBO_NAME + "Find"
)
find_completer = self.form.find.completer()
assert find_completer is not None
find_completer.setCaseSensitivity(Qt.CaseSensitivity.CaseSensitive)
self._replace_history = restore_combo_history(
self.form.replace, self.COMBO_NAME + "Replace"
)
replace_completer = self.form.replace.completer()
assert replace_completer is not None
replace_completer.setCaseSensitivity(Qt.CaseSensitivity.CaseSensitive)
if not self.note_ids:
# no selected notes to affect
self.form.selected_notes.setChecked(False)
self.form.selected_notes.setEnabled(False)
elif self._field:
self.form.selected_notes.setChecked(False)
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)
if self._field:
self.form.field.setCurrentIndex(self.field_names.index(self._field))
else:
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")
if not self.form.selected_notes.isChecked():
# an empty list means *all* notes
self.note_ids = []
parent_widget = self.parentWidget()
assert parent_widget is not None
# tags?
if self.form.field.currentIndex() == 1:
op = find_and_replace_tag(
parent=parent_widget,
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()]
op = find_and_replace(
parent=parent_widget,
note_ids=self.note_ids,
search=search,
replacement=replace,
regex=regex,
field_name=field,
match_case=match_case,
)
if not self.note_ids:
op.success(
lambda out: tooltip(
tr.browsing_notes_updated(count=out.count),
parent=self.parentWidget(),
)
)
op.run_in_background()
super().accept()
def show_help(self) -> None:
openHelp(HelpPage.BROWSING_FIND_AND_REPLACE)