diff --git a/anki/cards.py b/anki/cards.py index 90f80fa59..a0c5c5d54 100644 --- a/anki/cards.py +++ b/anki/cards.py @@ -187,3 +187,10 @@ lapses=?, left=?, odue=?, odid=?, did=? where id = ?""", del d['col'] del d['timerStarted'] return pprint.pformat(d, width=300) + + def userFlag(self): + return self.flags & 0b111 + + def setUserFlag(self, flag): + assert 0 <= flag <= 7 + self.flags = (self.flags & ~0b111) | flag diff --git a/anki/collection.py b/anki/collection.py index 7d644b6e6..6cec228db 100644 --- a/anki/collection.py +++ b/anki/collection.py @@ -850,3 +850,11 @@ and queue = 0""", intTime(), self.usn()) def _closeLog(self): self._logHnd = None + + # Card Flags + ########################################################################## + + def setUserFlag(self, flag, cids): + assert 0 <= flag <= 7 + self.db.execute("update cards set flags = (flags & ~?) | ? where id in %s" % + ids2str(cids), 0b111, flag) diff --git a/anki/find.py b/anki/find.py index b86319840..214f11644 100644 --- a/anki/find.py +++ b/anki/find.py @@ -29,6 +29,7 @@ class Finder: rated=self._findRated, tag=self._findTag, dupe=self._findDupes, + flag=self._findFlag, ) self.search['is'] = self._findCardState runHook("search", self.search) @@ -271,6 +272,14 @@ select distinct(n.id) from cards c, notes n where c.nid=n.id and """+preds (c.queue = 1 and c.due <= %d)""" % ( self.col.sched.today, self.col.sched.dayCutoff) + def _findFlag(self, args): + (val, args) = args + if not val or val not in "01234": + return + val = int(val) + mask = 2**3 - 1 + return "(c.flags & %d) == %d" % (mask, val) + def _findRated(self, args): # days(:optional_ease) (val, args) = args diff --git a/aqt/browser.py b/aqt/browser.py index f875873d4..f8f306b50 100644 --- a/aqt/browser.py +++ b/aqt/browser.py @@ -24,11 +24,6 @@ from aqt.webview import AnkiWebView from anki.consts import * from anki.sound import playFromText, clearAudioQueue -COLOUR_SUSPENDED = "#FFFFB2" -COLOUR_MARKED = "#D9B2E9" - -# fixme: need to refresh after undo - # Data model ########################################################################## @@ -318,6 +313,15 @@ class DataModel(QAbstractTableModel): # Line painter ###################################################################### +COLOUR_SUSPENDED = "#FFFFB2" + +flagColours = { + 1: "#F5B7B1", + 2: "#BB8FCE", + 3: "#82E0AA", + 4: "#85C1E9", +} + class StatusDelegate(QItemDelegate): def __init__(self, browser, model): @@ -335,16 +339,19 @@ class StatusDelegate(QItemDelegate): return finally: self.browser.mw.progress.blockUpdates = True + col = None - if c.note().hasTag("Marked"): - col = COLOUR_MARKED - elif c.queue == -1: + if c.queue == -1: col = COLOUR_SUSPENDED + elif c.userFlag() > 0: + col = flagColours[c.userFlag()] + if col: brush = QBrush(QColor(col)) painter.save() painter.fillRect(option.rect, brush) painter.restore() + return QItemDelegate.paint(self, painter, option, index) # Browser window @@ -403,7 +410,6 @@ class Browser(QMainWindow): f.actionChangeModel.triggered.connect(self.onChangeModel) f.actionFindDuplicates.triggered.connect(self.onFindDupes) f.actionFindReplace.triggered.connect(self.onFindReplace) - f.actionToggle_Mark.triggered.connect(lambda: self.onMark()) f.actionDelete.triggered.connect(self.deleteNotes) # cards f.actionChange_Deck.triggered.connect(self.setDeck) @@ -411,6 +417,11 @@ class Browser(QMainWindow): f.actionReposition.triggered.connect(self.reposition) f.actionReschedule.triggered.connect(self.reschedule) f.actionToggle_Suspend.triggered.connect(self.onSuspend) + f.actionRed_Flag.triggered.connect(lambda: self.onSetFlag(1)) + f.actionPurple_Flag.triggered.connect(lambda: self.onSetFlag(2)) + f.actionGreen_Flag.triggered.connect(lambda: self.onSetFlag(3)) + f.actionBlue_Flag.triggered.connect(lambda: self.onSetFlag(4)) + f.actionClear_Flag.triggered.connect(lambda: self.onSetFlag(0)) # jumps f.actionPreviousCard.triggered.connect(self.onPreviousCard) f.actionNextCard.triggered.connect(self.onNextCard) @@ -743,11 +754,10 @@ by clicking on one on the left.""")) self._addTodayFilters(m) self._addCardStateFilters(m) - m.addSeparator() - self._addDeckFilters(m) self._addNoteTypeFilters(m) self._addTagFilters(m) + self._addSavedSearches(m) m.exec_(self.form.filter.mapToGlobal(QPoint(0,0))) @@ -793,10 +803,7 @@ by clicking on one on the left.""")) def _addCommonFilters(self, m): items = ( (_("Whole Collection"), ""), - (_("Current Deck"), "deck:current"), - None, - (_("Marked"), "tag:marked"), - (_("Leech"), "tag:leech")) + (_("Current Deck"), "deck:current")) self._addSimpleFilters(m, items) def _addTodayFilters(self, m): @@ -816,7 +823,15 @@ by clicking on one on the left.""")) (_("Due"), "is:due"), None, (_("Suspended"), "is:suspended"), - (_("Buried"), "is:buried")) + (_("Buried"), "is:buried"), + None, + (_("Red Flag"), "flag:1"), + (_("Purple Flag"), "flag:2"), + (_("Green Flag"), "flag:3"), + (_("Blue Flag"), "flag:4"), + (_("No Flag"), "flag:0"), + (_("Any Flag"), "-flag:0"), + ) self._addSimpleFilters(m, items) _tagsMenuSize = 30 @@ -847,8 +862,6 @@ by clicking on one on the left.""")) def _addTagFilterBlock(self, m, tags): for t in tags: - if t.lower() == "marked" or t.lower() == "leech": - continue a = m.addAction(t) a.triggered.connect(lambda *, tag=t: self.setFilter("tag", tag)) @@ -1338,7 +1351,7 @@ update cards set usn=?, mod=?, did=? where id in """ + scids, def _clearUnusedTags(self): self.col.tags.registerNotes() - # Suspending and marking + # Suspending ###################################################################### def isSuspended(self): @@ -1357,16 +1370,12 @@ update cards set usn=?, mod=?, did=? where id in """ + scids, self.model.reset() self.mw.requireReset() - def isMarked(self): - return not not (self.card and self.card.note().hasTag("Marked")) + # Flags + ###################################################################### - def onMark(self, mark=None): - if mark is None: - mark = not self.isMarked() - if mark: - self.addTags(tags="marked", label=False) - else: - self.deleteTags(tags="marked", label=False) + def onSetFlag(self, n): + self.col.setUserFlag(n, self.selectedCards()) + self.model.reset() # Repositioning ###################################################################### diff --git a/aqt/reviewer.py b/aqt/reviewer.py index 79e64def8..452da6972 100644 --- a/aqt/reviewer.py +++ b/aqt/reviewer.py @@ -120,7 +120,7 @@ class Reviewer: def revHtml(self): extra = self.mw.col.conf.get("reviewExtra", "") return f""" - +