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
|
2021-01-25 14:45:47 +01:00
|
|
|
|
2022-02-22 12:51:23 +01:00
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
from typing import Callable
|
2021-02-01 13:08:56 +01:00
|
|
|
|
2012-12-21 08:51:59 +01:00
|
|
|
import aqt
|
2022-02-13 04:40:47 +01:00
|
|
|
import aqt.forms
|
|
|
|
import aqt.operations
|
2021-03-27 12:38:20 +01:00
|
|
|
from anki.collection import OpChangesWithId
|
|
|
|
from anki.decks import DeckId
|
2020-01-15 04:49:26 +01:00
|
|
|
from aqt import gui_hooks
|
2021-04-03 08:26:10 +02:00
|
|
|
from aqt.operations.deck import add_deck_dialog
|
2019-12-20 10:19:03 +01:00
|
|
|
from aqt.qt import *
|
2020-11-17 08:42:43 +01:00
|
|
|
from aqt.utils import (
|
2021-01-25 14:45:47 +01:00
|
|
|
HelpPage,
|
|
|
|
HelpPageArgument,
|
2021-01-07 05:24:49 +01:00
|
|
|
disable_help_button,
|
2020-11-17 08:42:43 +01:00
|
|
|
openHelp,
|
|
|
|
restoreGeom,
|
|
|
|
saveGeom,
|
|
|
|
shortcut,
|
|
|
|
showInfo,
|
|
|
|
tr,
|
|
|
|
)
|
2019-12-20 10:19:03 +01:00
|
|
|
|
2012-12-21 08:51:59 +01:00
|
|
|
|
|
|
|
class StudyDeck(QDialog):
|
2019-12-23 01:34:10 +01:00
|
|
|
def __init__(
|
|
|
|
self,
|
2021-02-02 14:30:53 +01:00
|
|
|
mw: aqt.AnkiQt,
|
2022-02-22 12:51:23 +01:00
|
|
|
names: Callable[[], list[str]] | None = None,
|
|
|
|
accept: str | None = None,
|
|
|
|
title: str | None = None,
|
2021-01-25 14:45:47 +01:00
|
|
|
help: HelpPageArgument = HelpPage.KEYBOARD_SHORTCUTS,
|
2022-02-22 12:51:23 +01:00
|
|
|
current: str | None = None,
|
2021-02-02 14:30:53 +01:00
|
|
|
cancel: bool = True,
|
2022-02-22 12:51:23 +01:00
|
|
|
parent: QWidget | None = None,
|
2021-02-02 14:30:53 +01:00
|
|
|
dyn: bool = False,
|
2022-02-22 12:51:23 +01:00
|
|
|
buttons: list[str | QPushButton] | None = None,
|
2021-02-02 14:30:53 +01:00
|
|
|
geomKey: str = "default",
|
2022-02-22 12:51:23 +01:00
|
|
|
callback: Callable[[StudyDeck], None] | None = None,
|
2020-01-15 22:41:23 +01:00
|
|
|
) -> None:
|
2022-02-22 12:51:23 +01:00
|
|
|
super().__init__(parent)
|
|
|
|
if not parent:
|
|
|
|
mw.garbage_collect_on_dialog_finish(self)
|
2012-12-21 08:51:59 +01:00
|
|
|
self.mw = mw
|
|
|
|
self.form = aqt.forms.studydeck.Ui_Dialog()
|
|
|
|
self.form.setupUi(self)
|
|
|
|
self.form.filter.installEventFilter(self)
|
2020-01-15 07:53:24 +01:00
|
|
|
gui_hooks.state_did_reset.append(self.onReset)
|
2021-02-11 01:09:06 +01:00
|
|
|
self.geomKey = f"studyDeck-{geomKey}"
|
2013-09-20 07:51:12 +02:00
|
|
|
restoreGeom(self, self.geomKey)
|
2021-01-07 05:24:49 +01:00
|
|
|
disable_help_button(self)
|
2012-12-21 08:51:59 +01:00
|
|
|
if not cancel:
|
|
|
|
self.form.buttonBox.removeButton(
|
2021-10-05 05:53:01 +02:00
|
|
|
self.form.buttonBox.button(QDialogButtonBox.StandardButton.Cancel)
|
2019-12-23 01:34:10 +01:00
|
|
|
)
|
2021-02-24 23:34:01 +01:00
|
|
|
if buttons is not None:
|
2021-03-17 05:51:59 +01:00
|
|
|
for button_or_label in buttons:
|
|
|
|
self.form.buttonBox.addButton(
|
2021-10-05 05:53:01 +02:00
|
|
|
button_or_label, QDialogButtonBox.ButtonRole.ActionRole
|
2021-03-17 05:51:59 +01:00
|
|
|
)
|
2012-12-21 08:51:59 +01:00
|
|
|
else:
|
2021-03-26 04:48:26 +01:00
|
|
|
b = QPushButton(tr.actions_add())
|
2012-12-21 08:51:59 +01:00
|
|
|
b.setShortcut(QKeySequence("Ctrl+N"))
|
2021-03-26 04:48:26 +01:00
|
|
|
b.setToolTip(shortcut(tr.decks_add_new_deck_ctrlandn()))
|
2021-10-05 05:53:01 +02:00
|
|
|
self.form.buttonBox.addButton(b, QDialogButtonBox.ButtonRole.ActionRole)
|
2020-05-04 05:23:08 +02:00
|
|
|
qconnect(b.clicked, self.onAddDeck)
|
2012-12-21 08:51:59 +01:00
|
|
|
if title:
|
|
|
|
self.setWindowTitle(title)
|
|
|
|
if not names:
|
2021-02-02 14:30:53 +01:00
|
|
|
names_ = [
|
2020-05-15 13:21:10 +02:00
|
|
|
d.name
|
|
|
|
for d in self.mw.col.decks.all_names_and_ids(
|
|
|
|
include_filtered=dyn, skip_empty_default=True
|
|
|
|
)
|
|
|
|
]
|
2012-12-21 08:51:59 +01:00
|
|
|
self.nameFunc = None
|
2021-02-02 14:30:53 +01:00
|
|
|
self.origNames = names_
|
2012-12-21 08:51:59 +01:00
|
|
|
else:
|
|
|
|
self.nameFunc = names
|
|
|
|
self.origNames = names()
|
2022-02-22 12:51:23 +01:00
|
|
|
self.name: str | None = None
|
|
|
|
self.form.buttonBox.addButton(
|
2021-10-05 05:53:01 +02:00
|
|
|
accept or tr.decks_study(), QDialogButtonBox.ButtonRole.AcceptRole
|
2019-12-23 01:34:10 +01:00
|
|
|
)
|
2022-02-10 00:53:13 +01:00
|
|
|
self.setModal(True)
|
2020-05-04 05:23:08 +02:00
|
|
|
qconnect(self.form.buttonBox.helpRequested, lambda: openHelp(help))
|
|
|
|
qconnect(self.form.filter.textEdited, self.redraw)
|
|
|
|
qconnect(self.form.list.itemDoubleClicked, self.accept)
|
2022-02-22 12:51:23 +01:00
|
|
|
qconnect(self.finished, self.on_finished)
|
2012-12-21 08:51:59 +01:00
|
|
|
self.show()
|
|
|
|
# redraw after show so position at center correct
|
|
|
|
self.redraw("", current)
|
2022-02-07 13:05:59 +01:00
|
|
|
self.callback = callback
|
|
|
|
if callback:
|
2022-02-10 00:53:13 +01:00
|
|
|
self.show()
|
2022-02-07 13:05:59 +01:00
|
|
|
else:
|
|
|
|
self.exec()
|
2012-12-21 08:51:59 +01:00
|
|
|
|
2020-05-26 10:08:55 +02:00
|
|
|
def eventFilter(self, obj: QObject, evt: QEvent) -> bool:
|
2021-10-05 05:53:01 +02:00
|
|
|
if isinstance(evt, QKeyEvent) and evt.type() == QEvent.Type.KeyPress:
|
2020-07-12 21:15:58 +02:00
|
|
|
new_row = current_row = self.form.list.currentRow()
|
|
|
|
rows_count = self.form.list.count()
|
|
|
|
key = evt.key()
|
|
|
|
|
2021-10-05 05:53:01 +02:00
|
|
|
if key == Qt.Key.Key_Up:
|
2020-07-12 21:15:58 +02:00
|
|
|
new_row = current_row - 1
|
2021-10-05 05:53:01 +02:00
|
|
|
elif key == Qt.Key.Key_Down:
|
2020-07-12 21:15:58 +02:00
|
|
|
new_row = current_row + 1
|
2021-03-17 05:51:59 +01:00
|
|
|
elif (
|
2021-10-05 05:53:01 +02:00
|
|
|
evt.modifiers() & Qt.KeyboardModifier.ControlModifier
|
|
|
|
and Qt.Key.Key_1 <= key <= Qt.Key.Key_9
|
2021-03-17 05:51:59 +01:00
|
|
|
):
|
2021-10-05 05:53:01 +02:00
|
|
|
row_index = key - Qt.Key.Key_1
|
2020-07-12 21:15:58 +02:00
|
|
|
if row_index < rows_count:
|
|
|
|
new_row = row_index
|
|
|
|
|
|
|
|
if rows_count:
|
|
|
|
new_row %= rows_count # don't let row index overflow/underflow
|
2020-07-13 11:33:36 +02:00
|
|
|
if new_row != current_row:
|
|
|
|
self.form.list.setCurrentRow(new_row)
|
|
|
|
return True
|
2012-12-21 08:51:59 +01:00
|
|
|
return False
|
|
|
|
|
2022-02-22 12:51:23 +01:00
|
|
|
def redraw(self, filt: str, focus: str | None = None) -> None:
|
2012-12-21 08:51:59 +01:00
|
|
|
self.filt = filt
|
|
|
|
self.focus = focus
|
|
|
|
self.names = [n for n in self.origNames if self._matches(n, filt)]
|
|
|
|
l = self.form.list
|
|
|
|
l.clear()
|
|
|
|
l.addItems(self.names)
|
|
|
|
if focus in self.names:
|
|
|
|
idx = self.names.index(focus)
|
|
|
|
else:
|
|
|
|
idx = 0
|
|
|
|
l.setCurrentRow(idx)
|
2021-10-05 05:53:01 +02:00
|
|
|
l.scrollToItem(l.item(idx), QAbstractItemView.ScrollHint.PositionAtCenter)
|
2012-12-21 08:51:59 +01:00
|
|
|
|
2021-02-01 13:08:56 +01:00
|
|
|
def _matches(self, name: str, filt: str) -> bool:
|
2012-12-21 08:51:59 +01:00
|
|
|
name = name.lower()
|
|
|
|
filt = filt.lower()
|
|
|
|
if not filt:
|
|
|
|
return True
|
2013-05-23 07:38:47 +02:00
|
|
|
for word in filt.split(" "):
|
|
|
|
if word not in name:
|
2012-12-21 08:51:59 +01:00
|
|
|
return False
|
|
|
|
return True
|
|
|
|
|
2021-02-01 14:28:21 +01:00
|
|
|
def onReset(self) -> None:
|
2012-12-21 08:51:59 +01:00
|
|
|
# model updated?
|
|
|
|
if self.nameFunc:
|
|
|
|
self.origNames = self.nameFunc()
|
|
|
|
self.redraw(self.filt, self.focus)
|
|
|
|
|
2020-01-15 22:41:23 +01:00
|
|
|
def accept(self) -> None:
|
2012-12-21 08:51:59 +01:00
|
|
|
row = self.form.list.currentRow()
|
|
|
|
if row < 0:
|
2021-03-26 04:48:26 +01:00
|
|
|
showInfo(tr.decks_please_select_something())
|
2012-12-21 08:51:59 +01:00
|
|
|
return
|
|
|
|
self.name = self.names[self.form.list.currentRow()]
|
2022-02-22 12:51:23 +01:00
|
|
|
self.accept_with_callback()
|
|
|
|
|
|
|
|
def accept_with_callback(self) -> None:
|
2022-02-07 13:05:59 +01:00
|
|
|
if self.callback:
|
|
|
|
self.callback(self)
|
2022-02-22 12:51:23 +01:00
|
|
|
super().accept()
|
2012-12-21 08:51:59 +01:00
|
|
|
|
2020-01-15 22:41:23 +01:00
|
|
|
def onAddDeck(self) -> None:
|
2012-12-21 08:51:59 +01:00
|
|
|
row = self.form.list.currentRow()
|
|
|
|
if row < 0:
|
|
|
|
default = self.form.filter.text()
|
|
|
|
else:
|
|
|
|
default = self.names[self.form.list.currentRow()]
|
2021-03-22 14:17:07 +01:00
|
|
|
|
2021-03-27 12:38:20 +01:00
|
|
|
def success(out: OpChangesWithId) -> None:
|
|
|
|
deck = self.mw.col.decks.get(DeckId(out.id))
|
2021-03-22 14:17:07 +01:00
|
|
|
self.name = deck["name"]
|
2022-02-22 12:51:23 +01:00
|
|
|
self.accept_with_callback()
|
2021-03-22 14:17:07 +01:00
|
|
|
|
2021-07-30 03:35:55 +02:00
|
|
|
if diag := add_deck_dialog(parent=self, default_text=default):
|
|
|
|
diag.success(success).run_in_background()
|
2022-02-22 12:51:23 +01:00
|
|
|
|
|
|
|
def on_finished(self) -> None:
|
|
|
|
saveGeom(self, self.geomKey)
|
|
|
|
gui_hooks.state_did_reset.remove(self.onReset)
|