diff --git a/qt/aqt/gui_hooks.py b/qt/aqt/gui_hooks.py index de0ec671d..5a001251e 100644 --- a/qt/aqt/gui_hooks.py +++ b/qt/aqt/gui_hooks.py @@ -862,6 +862,51 @@ class _EditorWillUseFontForFieldFilter: editor_will_use_font_for_field = _EditorWillUseFontForFieldFilter() +class _EmptyCardsWillBeDeletedFilter: + """Allow to change the list of cards to delete. + + For example, an add-on creating a method to delete only empty + new cards would be done as follow: +``` +from anki.consts import CARD_TYPE_NEW +from anki.utils import ids2str +from aqt import mw +from aqt import gui_hooks + +def filter(cids, col): + return col.db.list( + f"select id from cards where (type={CARD_TYPE_NEW} and (id in {ids2str(cids)))") + +def emptyNewCard(): + gui_hooks.append(filter) + mw.onEmptyCards() + gui_hooks.remove(filter) +```""" + + _hooks: List[Callable[[List[int]], List[int]]] = [] + + def append(self, cb: Callable[[List[int]], List[int]]) -> None: + """(cids: List[int])""" + self._hooks.append(cb) + + def remove(self, cb: Callable[[List[int]], List[int]]) -> None: + if cb in self._hooks: + self._hooks.remove(cb) + + def __call__(self, cids: List[int]) -> List[int]: + for filter in self._hooks: + try: + cids = filter(cids) + except: + # if the hook fails, remove it + self._hooks.remove(filter) + raise + return cids + + +empty_cards_will_be_deleted = _EmptyCardsWillBeDeletedFilter() + + class _MediaSyncDidProgressHook: _hooks: List[Callable[["aqt.mediasync.LogEntryWithTime"], None]] = [] diff --git a/qt/aqt/main.py b/qt/aqt/main.py index 9ca7e2acb..182c5de8a 100644 --- a/qt/aqt/main.py +++ b/qt/aqt/main.py @@ -1303,6 +1303,7 @@ will be lost. Continue?""" def onEmptyCards(self): self.progress.start(immediate=True) cids = self.col.emptyCids() + cids = gui_hooks.empty_cards_will_be_deleted(cids) if not cids: self.progress.finish() tooltip(_("No empty cards.")) diff --git a/qt/tools/genhooks_gui.py b/qt/tools/genhooks_gui.py index b92d0a969..54b131f51 100644 --- a/qt/tools/genhooks_gui.py +++ b/qt/tools/genhooks_gui.py @@ -346,6 +346,30 @@ hooks = [ name="media_sync_did_progress", args=["entry: aqt.mediasync.LogEntryWithTime"], ), Hook(name="media_sync_did_start_or_stop", args=["running: bool"]), + Hook( + name="empty_cards_will_be_deleted", + args=["cids: List[int]"], + return_type="List[int]", + doc="""Allow to change the list of cards to delete. + + For example, an add-on creating a method to delete only empty + new cards would be done as follow: +``` +from anki.consts import CARD_TYPE_NEW +from anki.utils import ids2str +from aqt import mw +from aqt import gui_hooks + +def filter(cids, col): + return col.db.list( + f"select id from cards where (type={CARD_TYPE_NEW} and (id in {ids2str(cids)))") + +def emptyNewCard(): + gui_hooks.append(filter) + mw.onEmptyCards() + gui_hooks.remove(filter) +```""", + ), # Adding cards ################### Hook(