2019-02-05 04:59:03 +01:00
|
|
|
# Copyright: Ankitects Pty Ltd and contributors
|
2012-12-21 08:51:59 +01:00
|
|
|
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
2020-05-14 12:58:45 +02:00
|
|
|
|
2021-02-02 14:30:53 +01:00
|
|
|
from concurrent.futures import Future
|
2012-12-21 08:51:59 +01:00
|
|
|
from operator import itemgetter
|
2021-04-30 08:09:44 +02:00
|
|
|
from typing import Any, List, Optional, Sequence
|
2019-12-20 10:19:03 +01:00
|
|
|
|
2018-12-15 03:45:38 +01:00
|
|
|
import aqt.clayout
|
2021-03-26 07:06:02 +01:00
|
|
|
from anki import Collection, stdmodels
|
2020-11-18 16:03:04 +01:00
|
|
|
from anki.lang import without_unicode_isolation
|
2021-03-27 13:03:19 +01:00
|
|
|
from anki.models import NotetypeDict, NotetypeId, NotetypeNameIdUseCount
|
2020-05-14 12:58:45 +02:00
|
|
|
from anki.notes import Note
|
2020-03-19 12:03:09 +01:00
|
|
|
from aqt import AnkiQt, gui_hooks
|
2021-05-08 08:20:10 +02:00
|
|
|
from aqt.operations import QueryOp
|
2021-04-30 08:09:44 +02:00
|
|
|
from aqt.operations.notetype import (
|
|
|
|
add_notetype_legacy,
|
|
|
|
remove_notetype,
|
|
|
|
update_notetype_legacy,
|
|
|
|
)
|
2019-12-20 10:19:03 +01:00
|
|
|
from aqt.qt import *
|
2021-04-30 08:01:47 +02:00
|
|
|
from aqt.schema_change_tracker import ChangeTracker
|
2019-12-23 01:34:10 +01:00
|
|
|
from aqt.utils import (
|
2021-01-25 14:45:47 +01:00
|
|
|
HelpPage,
|
2019-12-23 01:34:10 +01:00
|
|
|
askUser,
|
2021-01-07 05:24:49 +01:00
|
|
|
disable_help_button,
|
2019-12-23 01:34:10 +01:00
|
|
|
getText,
|
|
|
|
maybeHideClose,
|
|
|
|
openHelp,
|
|
|
|
restoreGeom,
|
|
|
|
saveGeom,
|
|
|
|
showInfo,
|
2020-11-17 08:42:43 +01:00
|
|
|
tr,
|
2019-12-23 01:34:10 +01:00
|
|
|
)
|
2019-12-20 10:19:03 +01:00
|
|
|
|
2012-12-21 08:51:59 +01:00
|
|
|
|
|
|
|
class Models(QDialog):
|
2021-01-22 03:56:39 +01:00
|
|
|
def __init__(
|
|
|
|
self,
|
|
|
|
mw: AnkiQt,
|
2021-03-17 05:51:59 +01:00
|
|
|
parent: Optional[QWidget] = None,
|
2021-02-02 15:00:29 +01:00
|
|
|
fromMain: bool = False,
|
2021-03-27 13:03:19 +01:00
|
|
|
selected_notetype_id: Optional[NotetypeId] = None,
|
2021-01-22 03:56:39 +01:00
|
|
|
):
|
2020-05-20 05:53:34 +02:00
|
|
|
self.mw = mw
|
2019-12-16 12:17:32 +01:00
|
|
|
parent = parent or mw
|
2012-12-22 00:21:24 +01:00
|
|
|
self.fromMain = fromMain
|
2021-01-22 03:56:39 +01:00
|
|
|
self.selected_notetype_id = selected_notetype_id
|
2019-12-16 12:17:32 +01:00
|
|
|
QDialog.__init__(self, parent, Qt.Window)
|
2020-05-04 13:52:48 +02:00
|
|
|
self.col = mw.col.weakref()
|
2019-12-23 01:34:10 +01:00
|
|
|
assert self.col
|
2012-12-21 08:51:59 +01:00
|
|
|
self.mm = self.col.models
|
|
|
|
self.form = aqt.forms.models.Ui_Dialog()
|
|
|
|
self.form.setupUi(self)
|
2020-10-12 04:17:02 +02:00
|
|
|
qconnect(
|
|
|
|
self.form.buttonBox.helpRequested,
|
2021-01-25 14:45:47 +01:00
|
|
|
lambda: openHelp(HelpPage.ADDING_A_NOTE_TYPE),
|
2020-10-12 04:17:02 +02:00
|
|
|
)
|
2021-04-30 07:16:44 +02:00
|
|
|
self.models: Sequence[NotetypeNameIdUseCount] = []
|
2012-12-21 08:51:59 +01:00
|
|
|
self.setupModels()
|
|
|
|
restoreGeom(self, "models")
|
|
|
|
self.exec_()
|
|
|
|
|
|
|
|
# Models
|
|
|
|
##########################################################################
|
|
|
|
|
2021-02-01 13:08:56 +01:00
|
|
|
def maybe_select_provided_notetype(self) -> None:
|
2021-01-22 03:56:39 +01:00
|
|
|
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
|
|
|
|
|
2020-07-30 05:28:52 +02:00
|
|
|
def setupModels(self) -> None:
|
2012-12-21 08:51:59 +01:00
|
|
|
self.model = None
|
2019-12-23 01:34:10 +01:00
|
|
|
f = self.form
|
|
|
|
box = f.buttonBox
|
2020-08-23 15:52:02 +02:00
|
|
|
|
|
|
|
default_buttons = [
|
2021-03-26 04:48:26 +01:00
|
|
|
(tr.actions_add(), self.onAdd),
|
|
|
|
(tr.actions_rename(), self.onRename),
|
|
|
|
(tr.actions_delete(), self.onDelete),
|
2020-08-23 15:52:02 +02:00
|
|
|
]
|
|
|
|
|
2012-12-22 00:21:24 +01:00
|
|
|
if self.fromMain:
|
2020-08-31 13:50:38 +02:00
|
|
|
default_buttons.extend(
|
|
|
|
[
|
2021-03-26 04:48:26 +01:00
|
|
|
(tr.notetypes_fields(), self.onFields),
|
|
|
|
(tr.notetypes_cards(), self.onCards),
|
2020-08-31 13:50:38 +02:00
|
|
|
]
|
|
|
|
)
|
2020-08-23 15:52:02 +02:00
|
|
|
|
2021-03-26 04:48:26 +01:00
|
|
|
default_buttons.append((tr.notetypes_options(), self.onAdvanced))
|
2020-08-23 15:52:02 +02:00
|
|
|
|
2020-08-23 17:10:56 +02:00
|
|
|
for label, func in gui_hooks.models_did_init_buttons(default_buttons, self):
|
2020-08-31 13:40:42 +02:00
|
|
|
button = box.addButton(label, QDialogButtonBox.ActionRole)
|
2020-08-23 15:52:02 +02:00
|
|
|
qconnect(button.clicked, func)
|
|
|
|
|
2020-05-04 05:23:08 +02:00
|
|
|
qconnect(f.modelsList.itemDoubleClicked, self.onRename)
|
2020-05-04 13:52:48 +02:00
|
|
|
|
2021-02-02 14:30:53 +01:00
|
|
|
def on_done(fut: Future) -> None:
|
2020-05-04 13:52:48 +02:00
|
|
|
self.updateModelsList(fut.result())
|
2021-01-22 03:56:39 +01:00
|
|
|
self.maybe_select_provided_notetype()
|
2020-05-04 13:52:48 +02:00
|
|
|
|
|
|
|
self.mw.taskman.with_progress(self.col.models.all_use_counts, on_done, self)
|
2012-12-21 08:51:59 +01:00
|
|
|
maybeHideClose(box)
|
|
|
|
|
2021-04-30 08:09:44 +02:00
|
|
|
def refresh_list(self, *ignored_args: Any) -> None:
|
2021-05-08 08:20:10 +02:00
|
|
|
QueryOp(
|
|
|
|
parent=self,
|
|
|
|
op=lambda col: col.models.all_use_counts(),
|
2021-04-30 07:16:44 +02:00
|
|
|
success=self.updateModelsList,
|
2021-05-08 08:20:10 +02:00
|
|
|
).run_in_background()
|
2021-04-30 07:16:44 +02:00
|
|
|
|
2020-07-30 05:28:52 +02:00
|
|
|
def onRename(self) -> None:
|
2020-05-04 13:52:48 +02:00
|
|
|
nt = self.current_notetype()
|
2021-04-30 08:09:44 +02:00
|
|
|
text, ok = getText(tr.actions_new_name(), default=nt["name"])
|
|
|
|
if ok and text.strip():
|
|
|
|
nt["name"] = text
|
2012-12-21 08:51:59 +01:00
|
|
|
|
2021-04-30 08:09:44 +02:00
|
|
|
update_notetype_legacy(parent=self, notetype=nt).success(
|
|
|
|
self.refresh_list
|
|
|
|
).run_in_background()
|
2020-05-04 13:52:48 +02:00
|
|
|
|
2021-04-30 07:16:44 +02:00
|
|
|
def updateModelsList(self, notetypes: Sequence[NotetypeNameIdUseCount]) -> None:
|
2012-12-21 08:51:59 +01:00
|
|
|
row = self.form.modelsList.currentRow()
|
|
|
|
if row == -1:
|
|
|
|
row = 0
|
|
|
|
self.form.modelsList.clear()
|
2020-05-04 13:52:48 +02:00
|
|
|
|
|
|
|
self.models = notetypes
|
2012-12-21 08:51:59 +01:00
|
|
|
for m in self.models:
|
2021-03-26 05:21:04 +01:00
|
|
|
mUse = tr.browsing_note_count(count=m.use_count)
|
2021-02-11 01:09:06 +01:00
|
|
|
item = QListWidgetItem(f"{m.name} [{mUse}]")
|
2012-12-21 08:51:59 +01:00
|
|
|
self.form.modelsList.addItem(item)
|
|
|
|
self.form.modelsList.setCurrentRow(row)
|
|
|
|
|
2021-03-27 12:46:49 +01:00
|
|
|
def current_notetype(self) -> NotetypeDict:
|
2020-05-04 13:52:48 +02:00
|
|
|
row = self.form.modelsList.currentRow()
|
2021-03-27 13:03:19 +01:00
|
|
|
return self.mm.get(NotetypeId(self.models[row].id))
|
2012-12-21 08:51:59 +01:00
|
|
|
|
2020-07-30 05:28:52 +02:00
|
|
|
def onAdd(self) -> None:
|
2012-12-21 08:51:59 +01:00
|
|
|
m = AddModel(self.mw, self).get()
|
|
|
|
if m:
|
2021-04-30 07:16:44 +02:00
|
|
|
# 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(
|
2021-04-30 08:09:44 +02:00
|
|
|
self.refresh_list
|
2021-04-30 07:16:44 +02:00
|
|
|
).run_in_background()
|
2012-12-21 08:51:59 +01:00
|
|
|
|
2020-07-30 05:28:52 +02:00
|
|
|
def onDelete(self) -> None:
|
2012-12-21 08:51:59 +01:00
|
|
|
if len(self.models) < 2:
|
2021-03-26 04:48:26 +01:00
|
|
|
showInfo(tr.notetypes_please_add_another_note_type_first(), parent=self)
|
2012-12-21 08:51:59 +01:00
|
|
|
return
|
2020-05-04 07:46:10 +02:00
|
|
|
idx = self.form.modelsList.currentRow()
|
|
|
|
if self.models[idx].use_count:
|
2021-03-26 04:48:26 +01:00
|
|
|
msg = tr.notetypes_delete_this_note_type_and_all()
|
2012-12-21 08:51:59 +01:00
|
|
|
else:
|
2021-03-26 04:48:26 +01:00
|
|
|
msg = tr.notetypes_delete_this_unused_note_type()
|
2012-12-21 08:51:59 +01:00
|
|
|
if not askUser(msg, parent=self):
|
|
|
|
return
|
2020-05-04 13:52:48 +02:00
|
|
|
|
2021-04-30 08:01:47 +02:00
|
|
|
tracker = ChangeTracker(self.mw)
|
|
|
|
if not tracker.mark_schema():
|
|
|
|
return
|
2020-05-04 13:52:48 +02:00
|
|
|
|
|
|
|
nt = self.current_notetype()
|
2021-04-30 08:01:47 +02:00
|
|
|
remove_notetype(parent=self, notetype_id=nt["id"]).success(
|
|
|
|
lambda _: self.refresh_list()
|
|
|
|
).run_in_background()
|
2012-12-21 08:51:59 +01:00
|
|
|
|
2020-07-30 05:28:52 +02:00
|
|
|
def onAdvanced(self) -> None:
|
2020-05-04 13:52:48 +02:00
|
|
|
nt = self.current_notetype()
|
2012-12-21 08:51:59 +01:00
|
|
|
d = QDialog(self)
|
2021-01-07 05:24:49 +01:00
|
|
|
disable_help_button(d)
|
2012-12-21 08:51:59 +01:00
|
|
|
frm = aqt.forms.modelopts.Ui_Dialog()
|
|
|
|
frm.setupUi(d)
|
2020-05-04 13:52:48 +02:00
|
|
|
frm.latexsvg.setChecked(nt.get("latexsvg", False))
|
|
|
|
frm.latexHeader.setText(nt["latexPre"])
|
|
|
|
frm.latexFooter.setText(nt["latexPost"])
|
2020-11-18 16:03:04 +01:00
|
|
|
d.setWindowTitle(
|
2021-03-26 05:21:04 +01:00
|
|
|
without_unicode_isolation(tr.actions_options_for(val=nt["name"]))
|
2020-11-18 16:03:04 +01:00
|
|
|
)
|
2021-01-25 14:45:47 +01:00
|
|
|
qconnect(frm.buttonBox.helpRequested, lambda: openHelp(HelpPage.LATEX))
|
2014-06-18 20:47:45 +02:00
|
|
|
restoreGeom(d, "modelopts")
|
2020-03-19 12:03:09 +01:00
|
|
|
gui_hooks.models_advanced_will_show(d)
|
2012-12-21 08:51:59 +01:00
|
|
|
d.exec_()
|
2014-06-18 20:47:45 +02:00
|
|
|
saveGeom(d, "modelopts")
|
2020-05-04 13:52:48 +02:00
|
|
|
nt["latexsvg"] = frm.latexsvg.isChecked()
|
|
|
|
nt["latexPre"] = str(frm.latexHeader.toPlainText())
|
|
|
|
nt["latexPost"] = str(frm.latexFooter.toPlainText())
|
2021-04-30 08:09:44 +02:00
|
|
|
update_notetype_legacy(parent=self, notetype=nt).success(
|
|
|
|
self.refresh_list
|
|
|
|
).run_in_background()
|
2012-12-21 08:51:59 +01:00
|
|
|
|
2020-07-30 05:28:52 +02:00
|
|
|
def _tmpNote(self) -> Note:
|
2020-05-04 13:52:48 +02:00
|
|
|
nt = self.current_notetype()
|
2020-05-14 12:58:45 +02:00
|
|
|
return Note(self.col, nt)
|
2012-12-22 00:21:24 +01:00
|
|
|
|
2020-07-30 05:28:52 +02:00
|
|
|
def onFields(self) -> None:
|
2012-12-22 00:21:24 +01:00
|
|
|
from aqt.fields import FieldDialog
|
2019-12-23 01:34:10 +01:00
|
|
|
|
2020-05-04 13:52:48 +02:00
|
|
|
FieldDialog(self.mw, self.current_notetype(), parent=self)
|
2012-12-22 00:21:24 +01:00
|
|
|
|
2020-07-30 05:28:52 +02:00
|
|
|
def onCards(self) -> None:
|
2012-12-22 00:21:24 +01:00
|
|
|
from aqt.clayout import CardLayout
|
2019-12-23 01:34:10 +01:00
|
|
|
|
2012-12-22 00:21:24 +01:00
|
|
|
n = self._tmpNote()
|
2020-05-14 12:58:45 +02:00
|
|
|
CardLayout(self.mw, n, ord=0, parent=self, fill_empty=True)
|
2012-12-22 00:21:24 +01:00
|
|
|
|
2012-12-21 08:51:59 +01:00
|
|
|
# Cleanup
|
|
|
|
##########################################################################
|
|
|
|
|
2020-07-30 05:28:52 +02:00
|
|
|
def reject(self) -> None:
|
2012-12-21 08:51:59 +01:00
|
|
|
saveGeom(self, "models")
|
|
|
|
QDialog.reject(self)
|
|
|
|
|
|
|
|
|
2019-12-23 01:34:10 +01:00
|
|
|
class AddModel(QDialog):
|
2021-03-27 12:46:49 +01:00
|
|
|
model: Optional[NotetypeDict]
|
2020-08-11 22:56:58 +02:00
|
|
|
|
2021-02-01 14:28:21 +01:00
|
|
|
def __init__(self, mw: AnkiQt, parent: Optional[QWidget] = None) -> None:
|
2020-05-04 05:23:08 +02:00
|
|
|
self.parent_ = parent or mw
|
2012-12-21 08:51:59 +01:00
|
|
|
self.mw = mw
|
|
|
|
self.col = mw.col
|
2020-05-04 05:23:08 +02:00
|
|
|
QDialog.__init__(self, self.parent_, Qt.Window)
|
2012-12-21 08:51:59 +01:00
|
|
|
self.model = None
|
|
|
|
self.dialog = aqt.forms.addmodel.Ui_Dialog()
|
|
|
|
self.dialog.setupUi(self)
|
2021-01-07 05:24:49 +01:00
|
|
|
disable_help_button(self)
|
2012-12-21 08:51:59 +01:00
|
|
|
# standard models
|
2021-03-27 12:46:49 +01:00
|
|
|
self.notetypes: List[
|
|
|
|
Union[NotetypeDict, Callable[[Collection], NotetypeDict]]
|
|
|
|
] = []
|
2020-05-15 08:05:58 +02:00
|
|
|
for (name, func) in stdmodels.get_stock_notetypes(self.col):
|
2021-03-26 05:21:04 +01:00
|
|
|
item = QListWidgetItem(tr.notetypes_add(val=name))
|
2012-12-21 08:51:59 +01:00
|
|
|
self.dialog.models.addItem(item)
|
2021-03-26 07:06:02 +01:00
|
|
|
self.notetypes.append(func)
|
2012-12-21 08:51:59 +01:00
|
|
|
# add copies
|
2013-01-08 02:39:51 +01:00
|
|
|
for m in sorted(self.col.models.all(), key=itemgetter("name")):
|
2021-03-26 05:21:04 +01:00
|
|
|
item = QListWidgetItem(tr.notetypes_clone(val=m["name"]))
|
2012-12-21 08:51:59 +01:00
|
|
|
self.dialog.models.addItem(item)
|
2021-03-26 07:06:02 +01:00
|
|
|
self.notetypes.append(m)
|
2012-12-21 08:51:59 +01:00
|
|
|
self.dialog.models.setCurrentRow(0)
|
|
|
|
# the list widget will swallow the enter key
|
|
|
|
s = QShortcut(QKeySequence("Return"), self)
|
2020-05-04 05:23:08 +02:00
|
|
|
qconnect(s.activated, self.accept)
|
2012-12-21 08:51:59 +01:00
|
|
|
# help
|
2020-05-04 05:23:08 +02:00
|
|
|
qconnect(self.dialog.buttonBox.helpRequested, self.onHelp)
|
2012-12-21 08:51:59 +01:00
|
|
|
|
2021-03-27 12:46:49 +01:00
|
|
|
def get(self) -> Optional[NotetypeDict]:
|
2012-12-21 08:51:59 +01:00
|
|
|
self.exec_()
|
|
|
|
return self.model
|
|
|
|
|
2020-07-30 05:28:52 +02:00
|
|
|
def reject(self) -> None:
|
2012-12-21 08:51:59 +01:00
|
|
|
QDialog.reject(self)
|
|
|
|
|
2020-07-30 05:28:52 +02:00
|
|
|
def accept(self) -> None:
|
2021-03-26 07:06:02 +01:00
|
|
|
model = self.notetypes[self.dialog.models.currentRow()]
|
|
|
|
if isinstance(model, dict):
|
2021-04-30 07:16:44 +02:00
|
|
|
# clone existing
|
|
|
|
self.model = self.mw.col.models.copy(model, add=False)
|
2021-03-26 07:06:02 +01:00
|
|
|
else:
|
|
|
|
self.model = model(self.col)
|
2012-12-21 08:51:59 +01:00
|
|
|
QDialog.accept(self)
|
|
|
|
|
2020-07-30 05:28:52 +02:00
|
|
|
def onHelp(self) -> None:
|
2021-01-25 14:45:47 +01:00
|
|
|
openHelp(HelpPage.ADDING_A_NOTE_TYPE)
|