a7812dedc0
The enum changes should work on PyQt 5.x, and are required in PyQt 6.x. They are not supported by the PyQt5 typings however, so we need to run our tests with PyQt6.
281 lines
9.1 KiB
Python
281 lines
9.1 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 concurrent.futures import Future
|
|
from operator import itemgetter
|
|
from typing import Any, Optional, Sequence
|
|
|
|
import aqt.clayout
|
|
from anki import stdmodels
|
|
from anki.collection import Collection
|
|
from anki.lang import without_unicode_isolation
|
|
from anki.models import NotetypeDict, NotetypeId, NotetypeNameIdUseCount
|
|
from anki.notes import Note
|
|
from aqt import AnkiQt, gui_hooks
|
|
from aqt.operations import QueryOp
|
|
from aqt.operations.notetype import (
|
|
add_notetype_legacy,
|
|
remove_notetype,
|
|
update_notetype_legacy,
|
|
)
|
|
from aqt.qt import *
|
|
from aqt.schema_change_tracker import ChangeTracker
|
|
from aqt.utils import (
|
|
HelpPage,
|
|
askUser,
|
|
disable_help_button,
|
|
getText,
|
|
maybeHideClose,
|
|
openHelp,
|
|
restoreGeom,
|
|
saveGeom,
|
|
showInfo,
|
|
tr,
|
|
)
|
|
|
|
|
|
class Models(QDialog):
|
|
def __init__(
|
|
self,
|
|
mw: AnkiQt,
|
|
parent: Optional[QWidget] = None,
|
|
fromMain: bool = False,
|
|
selected_notetype_id: Optional[NotetypeId] = None,
|
|
):
|
|
self.mw = mw
|
|
parent = parent or mw
|
|
self.fromMain = fromMain
|
|
self.selected_notetype_id = selected_notetype_id
|
|
QDialog.__init__(self, parent, Qt.WindowType.Window)
|
|
self.col = mw.col.weakref()
|
|
assert self.col
|
|
self.mm = self.col.models
|
|
self.form = aqt.forms.models.Ui_Dialog()
|
|
self.form.setupUi(self)
|
|
qconnect(
|
|
self.form.buttonBox.helpRequested,
|
|
lambda: openHelp(HelpPage.ADDING_A_NOTE_TYPE),
|
|
)
|
|
self.models: Sequence[NotetypeNameIdUseCount] = []
|
|
self.setupModels()
|
|
restoreGeom(self, "models")
|
|
self.exec()
|
|
|
|
# Models
|
|
##########################################################################
|
|
|
|
def maybe_select_provided_notetype(self) -> None:
|
|
if not self.selected_notetype_id:
|
|
self.form.modelsList.setCurrentRow(0)
|
|
return
|
|
for i, m in enumerate(self.models):
|
|
if m.id == self.selected_notetype_id:
|
|
self.form.modelsList.setCurrentRow(i)
|
|
break
|
|
|
|
def setupModels(self) -> None:
|
|
self.model = None
|
|
f = self.form
|
|
box = f.buttonBox
|
|
|
|
default_buttons = [
|
|
(tr.actions_add(), self.onAdd),
|
|
(tr.actions_rename(), self.onRename),
|
|
(tr.actions_delete(), self.onDelete),
|
|
]
|
|
|
|
if self.fromMain:
|
|
default_buttons.extend(
|
|
[
|
|
(tr.notetypes_fields(), self.onFields),
|
|
(tr.notetypes_cards(), self.onCards),
|
|
]
|
|
)
|
|
|
|
default_buttons.append((tr.notetypes_options(), self.onAdvanced))
|
|
|
|
for label, func in gui_hooks.models_did_init_buttons(default_buttons, self):
|
|
button = box.addButton(label, QDialogButtonBox.ButtonRole.ActionRole)
|
|
qconnect(button.clicked, func)
|
|
|
|
qconnect(f.modelsList.itemDoubleClicked, self.onRename)
|
|
|
|
def on_done(fut: Future) -> None:
|
|
self.updateModelsList(fut.result())
|
|
self.maybe_select_provided_notetype()
|
|
|
|
self.mw.taskman.with_progress(self.col.models.all_use_counts, on_done, self)
|
|
maybeHideClose(box)
|
|
|
|
def refresh_list(self, *ignored_args: Any) -> None:
|
|
QueryOp(
|
|
parent=self,
|
|
op=lambda col: col.models.all_use_counts(),
|
|
success=self.updateModelsList,
|
|
).run_in_background()
|
|
|
|
def onRename(self) -> None:
|
|
nt = self.current_notetype()
|
|
text, ok = getText(tr.actions_new_name(), default=nt["name"])
|
|
if ok and text.strip():
|
|
nt["name"] = text
|
|
|
|
update_notetype_legacy(parent=self, notetype=nt).success(
|
|
self.refresh_list
|
|
).run_in_background()
|
|
|
|
def updateModelsList(self, notetypes: Sequence[NotetypeNameIdUseCount]) -> None:
|
|
row = self.form.modelsList.currentRow()
|
|
if row == -1:
|
|
row = 0
|
|
self.form.modelsList.clear()
|
|
|
|
self.models = notetypes
|
|
for m in self.models:
|
|
mUse = tr.browsing_note_count(count=m.use_count)
|
|
item = QListWidgetItem(f"{m.name} [{mUse}]")
|
|
self.form.modelsList.addItem(item)
|
|
self.form.modelsList.setCurrentRow(row)
|
|
|
|
def current_notetype(self) -> NotetypeDict:
|
|
row = self.form.modelsList.currentRow()
|
|
return self.mm.get(NotetypeId(self.models[row].id))
|
|
|
|
def onAdd(self) -> None:
|
|
m = AddModel(self.mw, self).get()
|
|
if m:
|
|
# if legacy add-ons already added the notetype, skip adding
|
|
if m["id"]:
|
|
return
|
|
|
|
# prompt for name
|
|
text, ok = getText(tr.actions_name(), default=m["name"])
|
|
if not ok or not text.strip():
|
|
return
|
|
m["name"] = text
|
|
|
|
add_notetype_legacy(parent=self, notetype=m).success(
|
|
self.refresh_list
|
|
).run_in_background()
|
|
|
|
def onDelete(self) -> None:
|
|
if len(self.models) < 2:
|
|
showInfo(tr.notetypes_please_add_another_note_type_first(), parent=self)
|
|
return
|
|
idx = self.form.modelsList.currentRow()
|
|
if self.models[idx].use_count:
|
|
msg = tr.notetypes_delete_this_note_type_and_all()
|
|
else:
|
|
msg = tr.notetypes_delete_this_unused_note_type()
|
|
if not askUser(msg, parent=self):
|
|
return
|
|
|
|
tracker = ChangeTracker(self.mw)
|
|
if not tracker.mark_schema():
|
|
return
|
|
|
|
nt = self.current_notetype()
|
|
remove_notetype(parent=self, notetype_id=nt["id"]).success(
|
|
lambda _: self.refresh_list()
|
|
).run_in_background()
|
|
|
|
def onAdvanced(self) -> None:
|
|
nt = self.current_notetype()
|
|
d = QDialog(self)
|
|
disable_help_button(d)
|
|
frm = aqt.forms.modelopts.Ui_Dialog()
|
|
frm.setupUi(d)
|
|
frm.latexsvg.setChecked(nt.get("latexsvg", False))
|
|
frm.latexHeader.setText(nt["latexPre"])
|
|
frm.latexFooter.setText(nt["latexPost"])
|
|
d.setWindowTitle(
|
|
without_unicode_isolation(tr.actions_options_for(val=nt["name"]))
|
|
)
|
|
qconnect(frm.buttonBox.helpRequested, lambda: openHelp(HelpPage.LATEX))
|
|
restoreGeom(d, "modelopts")
|
|
gui_hooks.models_advanced_will_show(d)
|
|
d.exec()
|
|
saveGeom(d, "modelopts")
|
|
nt["latexsvg"] = frm.latexsvg.isChecked()
|
|
nt["latexPre"] = str(frm.latexHeader.toPlainText())
|
|
nt["latexPost"] = str(frm.latexFooter.toPlainText())
|
|
update_notetype_legacy(parent=self, notetype=nt).success(
|
|
self.refresh_list
|
|
).run_in_background()
|
|
|
|
def _tmpNote(self) -> Note:
|
|
nt = self.current_notetype()
|
|
return Note(self.col, nt)
|
|
|
|
def onFields(self) -> None:
|
|
from aqt.fields import FieldDialog
|
|
|
|
FieldDialog(self.mw, self.current_notetype(), parent=self)
|
|
|
|
def onCards(self) -> None:
|
|
from aqt.clayout import CardLayout
|
|
|
|
n = self._tmpNote()
|
|
CardLayout(self.mw, n, ord=0, parent=self, fill_empty=True)
|
|
|
|
# Cleanup
|
|
##########################################################################
|
|
|
|
def reject(self) -> None:
|
|
saveGeom(self, "models")
|
|
QDialog.reject(self)
|
|
|
|
|
|
class AddModel(QDialog):
|
|
model: Optional[NotetypeDict]
|
|
|
|
def __init__(self, mw: AnkiQt, parent: Optional[QWidget] = None) -> None:
|
|
self.parent_ = parent or mw
|
|
self.mw = mw
|
|
self.col = mw.col
|
|
QDialog.__init__(self, self.parent_, Qt.WindowType.Window)
|
|
self.model = None
|
|
self.dialog = aqt.forms.addmodel.Ui_Dialog()
|
|
self.dialog.setupUi(self)
|
|
disable_help_button(self)
|
|
# standard models
|
|
self.notetypes: list[
|
|
Union[NotetypeDict, Callable[[Collection], NotetypeDict]]
|
|
] = []
|
|
for (name, func) in stdmodels.get_stock_notetypes(self.col):
|
|
item = QListWidgetItem(tr.notetypes_add(val=name))
|
|
self.dialog.models.addItem(item)
|
|
self.notetypes.append(func)
|
|
# add copies
|
|
for m in sorted(self.col.models.all(), key=itemgetter("name")):
|
|
item = QListWidgetItem(tr.notetypes_clone(val=m["name"]))
|
|
self.dialog.models.addItem(item)
|
|
self.notetypes.append(m)
|
|
self.dialog.models.setCurrentRow(0)
|
|
# the list widget will swallow the enter key
|
|
s = QShortcut(QKeySequence("Return"), self)
|
|
qconnect(s.activated, self.accept)
|
|
# help
|
|
qconnect(self.dialog.buttonBox.helpRequested, self.onHelp)
|
|
|
|
def get(self) -> Optional[NotetypeDict]:
|
|
self.exec()
|
|
return self.model
|
|
|
|
def reject(self) -> None:
|
|
QDialog.reject(self)
|
|
|
|
def accept(self) -> None:
|
|
model = self.notetypes[self.dialog.models.currentRow()]
|
|
if isinstance(model, dict):
|
|
# clone existing
|
|
self.model = self.mw.col.models.copy(model, add=False)
|
|
else:
|
|
self.model = model(self.col)
|
|
QDialog.accept(self)
|
|
|
|
def onHelp(self) -> None:
|
|
openHelp(HelpPage.ADDING_A_NOTE_TYPE)
|