2021-04-12 09:50:44 +02:00
|
|
|
# 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 abc import ABC, abstractmethod, abstractproperty
|
2021-10-03 10:59:42 +02:00
|
|
|
from typing import Sequence, cast
|
2021-04-12 09:50:44 +02:00
|
|
|
|
2021-07-05 12:44:48 +02:00
|
|
|
from anki.browser import BrowserConfig
|
2021-04-12 09:50:44 +02:00
|
|
|
from anki.cards import Card, CardId
|
2021-07-05 12:44:48 +02:00
|
|
|
from anki.collection import Collection
|
2022-06-24 05:57:42 +02:00
|
|
|
from anki.errors import NotFoundError
|
2021-04-12 09:50:44 +02:00
|
|
|
from anki.notes import Note, NoteId
|
|
|
|
from anki.utils import ids2str
|
|
|
|
from aqt.browser.table import Column, ItemId, ItemList
|
|
|
|
|
|
|
|
|
|
|
|
class ItemState(ABC):
|
2021-07-05 12:44:48 +02:00
|
|
|
GEOMETRY_KEY_PREFIX: str
|
|
|
|
SORT_COLUMN_KEY: str
|
|
|
|
SORT_BACKWARDS_KEY: str
|
2021-10-03 10:59:42 +02:00
|
|
|
_active_columns: list[str]
|
2021-04-12 09:50:44 +02:00
|
|
|
|
|
|
|
def __init__(self, col: Collection) -> None:
|
|
|
|
self.col = col
|
2021-07-05 12:44:48 +02:00
|
|
|
self._sort_column = self.col.get_config(self.SORT_COLUMN_KEY)
|
|
|
|
self._sort_backwards = self.col.get_config(self.SORT_BACKWARDS_KEY, False)
|
2021-04-12 09:50:44 +02:00
|
|
|
|
|
|
|
def is_notes_mode(self) -> bool:
|
|
|
|
"""Return True if the state is a NoteState."""
|
|
|
|
return isinstance(self, NoteState)
|
|
|
|
|
|
|
|
# Stateless Helpers
|
|
|
|
|
|
|
|
def note_ids_from_card_ids(self, items: Sequence[ItemId]) -> Sequence[NoteId]:
|
|
|
|
return self.col.db.list(
|
|
|
|
f"select distinct nid from cards where id in {ids2str(items)}"
|
|
|
|
)
|
|
|
|
|
|
|
|
def card_ids_from_note_ids(self, items: Sequence[ItemId]) -> Sequence[CardId]:
|
|
|
|
return self.col.db.list(f"select id from cards where nid in {ids2str(items)}")
|
|
|
|
|
|
|
|
def column_key_at(self, index: int) -> str:
|
|
|
|
return self._active_columns[index]
|
|
|
|
|
|
|
|
def column_label(self, column: Column) -> str:
|
|
|
|
return (
|
|
|
|
column.notes_mode_label if self.is_notes_mode() else column.cards_mode_label
|
|
|
|
)
|
|
|
|
|
2021-09-30 12:45:05 +02:00
|
|
|
def column_tooltip(self, column: Column) -> str:
|
|
|
|
if self.is_notes_mode():
|
|
|
|
return column.notes_mode_tooltip
|
|
|
|
return column.cards_mode_tooltip
|
|
|
|
|
2021-04-12 09:50:44 +02:00
|
|
|
# Columns and sorting
|
|
|
|
|
|
|
|
# abstractproperty is deprecated but used due to mypy limitations
|
|
|
|
# (https://github.com/python/mypy/issues/1362)
|
2021-10-02 15:13:24 +02:00
|
|
|
@abstractproperty # pylint: disable=deprecated-decorator
|
2021-10-03 10:59:42 +02:00
|
|
|
def active_columns(self) -> list[str]:
|
2021-04-12 09:50:44 +02:00
|
|
|
"""Return the saved or default columns for the state."""
|
|
|
|
|
|
|
|
@abstractmethod
|
|
|
|
def toggle_active_column(self, column: str) -> None:
|
|
|
|
"""Add or remove an active column."""
|
|
|
|
|
2021-07-05 12:44:48 +02:00
|
|
|
@property
|
2021-04-12 09:50:44 +02:00
|
|
|
def sort_column(self) -> str:
|
2021-07-05 12:44:48 +02:00
|
|
|
return self._sort_column
|
2021-04-12 09:50:44 +02:00
|
|
|
|
|
|
|
@sort_column.setter
|
|
|
|
def sort_column(self, column: str) -> None:
|
2021-07-05 12:44:48 +02:00
|
|
|
self.col.set_config(self.SORT_COLUMN_KEY, column)
|
|
|
|
self._sort_column = column
|
2021-04-12 09:50:44 +02:00
|
|
|
|
2021-07-05 12:44:48 +02:00
|
|
|
@property
|
2021-04-12 09:50:44 +02:00
|
|
|
def sort_backwards(self) -> bool:
|
2021-10-30 01:08:07 +02:00
|
|
|
"If true, descending order."
|
2021-07-05 12:44:48 +02:00
|
|
|
return self._sort_backwards
|
2021-04-12 09:50:44 +02:00
|
|
|
|
|
|
|
@sort_backwards.setter
|
|
|
|
def sort_backwards(self, order: bool) -> None:
|
2021-07-05 12:44:48 +02:00
|
|
|
self.col.set_config(self.SORT_BACKWARDS_KEY, order)
|
|
|
|
self._sort_backwards = order
|
2021-04-12 09:50:44 +02:00
|
|
|
|
|
|
|
# Get objects
|
|
|
|
|
|
|
|
@abstractmethod
|
|
|
|
def get_card(self, item: ItemId) -> Card:
|
|
|
|
"""Return the item if it's a card or its first card if it's a note."""
|
|
|
|
|
|
|
|
@abstractmethod
|
|
|
|
def get_note(self, item: ItemId) -> Note:
|
|
|
|
"""Return the item if it's a note or its note if it's a card."""
|
|
|
|
|
|
|
|
# Get ids
|
|
|
|
|
|
|
|
@abstractmethod
|
|
|
|
def find_items(
|
2021-10-03 10:59:42 +02:00
|
|
|
self, search: str, order: bool | str | Column, reverse: bool
|
2021-04-12 09:50:44 +02:00
|
|
|
) -> Sequence[ItemId]:
|
|
|
|
"""Return the item ids fitting the given search and order."""
|
|
|
|
|
|
|
|
@abstractmethod
|
|
|
|
def get_item_from_card_id(self, card: CardId) -> ItemId:
|
|
|
|
"""Return the appropriate item id for a card id."""
|
|
|
|
|
|
|
|
@abstractmethod
|
|
|
|
def get_card_ids(self, items: Sequence[ItemId]) -> Sequence[CardId]:
|
|
|
|
"""Return the card ids for the given item ids."""
|
|
|
|
|
|
|
|
@abstractmethod
|
|
|
|
def get_note_ids(self, items: Sequence[ItemId]) -> Sequence[NoteId]:
|
|
|
|
"""Return the note ids for the given item ids."""
|
|
|
|
|
|
|
|
# Toggle
|
|
|
|
|
|
|
|
@abstractmethod
|
|
|
|
def toggle_state(self) -> ItemState:
|
|
|
|
"""Return an instance of the other state."""
|
|
|
|
|
|
|
|
@abstractmethod
|
|
|
|
def get_new_items(self, old_items: Sequence[ItemId]) -> ItemList:
|
|
|
|
"""Given a list of ids from the other state, return the corresponding ids for this state."""
|
|
|
|
|
|
|
|
|
|
|
|
class CardState(ItemState):
|
2021-07-05 12:44:48 +02:00
|
|
|
GEOMETRY_KEY_PREFIX = "editor"
|
|
|
|
SORT_COLUMN_KEY = BrowserConfig.CARDS_SORT_COLUMN_KEY
|
|
|
|
SORT_BACKWARDS_KEY = BrowserConfig.CARDS_SORT_BACKWARDS_KEY
|
|
|
|
|
2021-04-12 09:50:44 +02:00
|
|
|
def __init__(self, col: Collection) -> None:
|
|
|
|
super().__init__(col)
|
|
|
|
self._active_columns = self.col.load_browser_card_columns()
|
|
|
|
|
|
|
|
@property
|
2021-10-03 10:59:42 +02:00
|
|
|
def active_columns(self) -> list[str]:
|
2021-04-12 09:50:44 +02:00
|
|
|
return self._active_columns
|
|
|
|
|
|
|
|
def toggle_active_column(self, column: str) -> None:
|
|
|
|
if column in self._active_columns:
|
|
|
|
self._active_columns.remove(column)
|
|
|
|
else:
|
|
|
|
self._active_columns.append(column)
|
|
|
|
self.col.set_browser_card_columns(self._active_columns)
|
|
|
|
|
|
|
|
def get_card(self, item: ItemId) -> Card:
|
|
|
|
return self.col.get_card(CardId(item))
|
|
|
|
|
|
|
|
def get_note(self, item: ItemId) -> Note:
|
|
|
|
return self.get_card(item).note()
|
|
|
|
|
|
|
|
def find_items(
|
2021-10-03 10:59:42 +02:00
|
|
|
self, search: str, order: bool | str | Column, reverse: bool
|
2021-04-12 09:50:44 +02:00
|
|
|
) -> Sequence[ItemId]:
|
|
|
|
return self.col.find_cards(search, order, reverse)
|
|
|
|
|
|
|
|
def get_item_from_card_id(self, card: CardId) -> ItemId:
|
|
|
|
return card
|
|
|
|
|
|
|
|
def get_card_ids(self, items: Sequence[ItemId]) -> Sequence[CardId]:
|
|
|
|
return cast(Sequence[CardId], items)
|
|
|
|
|
|
|
|
def get_note_ids(self, items: Sequence[ItemId]) -> Sequence[NoteId]:
|
|
|
|
return super().note_ids_from_card_ids(items)
|
|
|
|
|
|
|
|
def toggle_state(self) -> NoteState:
|
|
|
|
return NoteState(self.col)
|
|
|
|
|
|
|
|
def get_new_items(self, old_items: Sequence[ItemId]) -> Sequence[CardId]:
|
|
|
|
return super().card_ids_from_note_ids(old_items)
|
|
|
|
|
|
|
|
|
|
|
|
class NoteState(ItemState):
|
2021-07-05 12:44:48 +02:00
|
|
|
GEOMETRY_KEY_PREFIX = "editorNotesMode"
|
|
|
|
SORT_COLUMN_KEY = BrowserConfig.NOTES_SORT_COLUMN_KEY
|
|
|
|
SORT_BACKWARDS_KEY = BrowserConfig.NOTES_SORT_BACKWARDS_KEY
|
|
|
|
|
2021-04-12 09:50:44 +02:00
|
|
|
def __init__(self, col: Collection) -> None:
|
|
|
|
super().__init__(col)
|
|
|
|
self._active_columns = self.col.load_browser_note_columns()
|
|
|
|
|
|
|
|
@property
|
2021-10-03 10:59:42 +02:00
|
|
|
def active_columns(self) -> list[str]:
|
2021-04-12 09:50:44 +02:00
|
|
|
return self._active_columns
|
|
|
|
|
|
|
|
def toggle_active_column(self, column: str) -> None:
|
|
|
|
if column in self._active_columns:
|
|
|
|
self._active_columns.remove(column)
|
|
|
|
else:
|
|
|
|
self._active_columns.append(column)
|
|
|
|
self.col.set_browser_note_columns(self._active_columns)
|
|
|
|
|
|
|
|
def get_card(self, item: ItemId) -> Card:
|
2022-06-24 05:57:42 +02:00
|
|
|
if cards := self.get_note(item).cards():
|
|
|
|
return cards[0]
|
2022-10-21 10:02:12 +02:00
|
|
|
raise NotFoundError("card not found", None, None, None)
|
2021-04-12 09:50:44 +02:00
|
|
|
|
|
|
|
def get_note(self, item: ItemId) -> Note:
|
|
|
|
return self.col.get_note(NoteId(item))
|
|
|
|
|
|
|
|
def find_items(
|
2021-10-03 10:59:42 +02:00
|
|
|
self, search: str, order: bool | str | Column, reverse: bool
|
2021-04-12 09:50:44 +02:00
|
|
|
) -> Sequence[ItemId]:
|
|
|
|
return self.col.find_notes(search, order, reverse)
|
|
|
|
|
|
|
|
def get_item_from_card_id(self, card: CardId) -> ItemId:
|
|
|
|
return self.col.get_card(card).note().id
|
|
|
|
|
|
|
|
def get_card_ids(self, items: Sequence[ItemId]) -> Sequence[CardId]:
|
|
|
|
return super().card_ids_from_note_ids(items)
|
|
|
|
|
|
|
|
def get_note_ids(self, items: Sequence[ItemId]) -> Sequence[NoteId]:
|
|
|
|
return cast(Sequence[NoteId], items)
|
|
|
|
|
|
|
|
def toggle_state(self) -> CardState:
|
|
|
|
return CardState(self.col)
|
|
|
|
|
|
|
|
def get_new_items(self, old_items: Sequence[ItemId]) -> Sequence[NoteId]:
|
|
|
|
return super().note_ids_from_card_ids(old_items)
|