46890fbbaa
* refresh model list when legacy addon adds notetype When legacy addons add note types, they automatically add the note type to the collection models. When this was detected, the dialog box presenting the list of models was not being updated due to an early return in the code. This commit adds a list refresh along this path of execution to ensure the gui is updated with the newly added model. * add trailing space to name in CONTRIBUTORS
290 lines
9.5 KiB
Python
290 lines
9.5 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 or mw)
|
|
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.show()
|
|
|
|
# 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:
|
|
def on_success(notetype: NotetypeDict) -> None:
|
|
# if legacy add-ons already added the notetype, skip adding
|
|
if notetype["id"]:
|
|
self.refresh_list()
|
|
return
|
|
|
|
# prompt for name
|
|
text, ok = getText(tr.actions_name(), default=notetype["name"], parent=self)
|
|
if not ok or not text.strip():
|
|
return
|
|
notetype["name"] = text
|
|
|
|
add_notetype_legacy(parent=self, notetype=notetype).success(
|
|
self.refresh_list
|
|
).run_in_background()
|
|
|
|
AddModel(self.mw, on_success, self)
|
|
|
|
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,
|
|
on_success: Callable[[NotetypeDict], None],
|
|
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)
|
|
self.setWindowModality(Qt.WindowModality.ApplicationModal)
|
|
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)
|
|
self.on_success = on_success
|
|
self.show()
|
|
|
|
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)
|
|
# On mac, we need to allow time for the existing modal to close or
|
|
# Qt gets confused.
|
|
self.mw.progress.single_shot(100, lambda: self.on_success(self.model), True)
|
|
|
|
def onHelp(self) -> None:
|
|
openHelp(HelpPage.ADDING_A_NOTE_TYPE)
|