diff --git a/ankiqt/config.py b/ankiqt/config.py
index 652b324e0..81bfbce6a 100644
--- a/ankiqt/config.py
+++ b/ankiqt/config.py
@@ -96,6 +96,7 @@ class Config(dict):
'proxyUser': '',
'proxyPass': '',
'loadLastDeck': False,
+ 'deckBrowserRefreshPeriod': 3600,
}
for (k,v) in fields.items():
if not self.has_key(k):
diff --git a/ankiqt/ui/main.py b/ankiqt/ui/main.py
index ae42f43b6..07322ad65 100644
--- a/ankiqt/ui/main.py
+++ b/ankiqt/ui/main.py
@@ -240,7 +240,6 @@ Please do not file a bug report with Anki.
""")
self.currentCard = None
self.lastCard = None
self.editor.deck = self.deck
- self.updateRecentFilesMenu()
if self.deck:
self.enableDeckMenuItems()
self.updateViews(state)
@@ -270,7 +269,6 @@ Please do not file a bug report with Anki.
""")
self.help.hide()
self.currentCard = None
self.lastCard = None
- self.updateRecentFilesMenu()
self.disableDeckMenuItems()
# hide all deck-associated dialogs
self.closeAllDeckWindows()
@@ -709,33 +707,35 @@ To upgrade an old deck, download Anki 0.9.8.7."""))
self.config['recentDeckPaths'].insert(0, path)
del self.config['recentDeckPaths'][20:]
self.config.save()
- self.updateRecentFilesMenu()
- def updateRecentFilesMenu(self):
- self.config['recentDeckPaths'] = [
- p for p in self.config['recentDeckPaths']
- if os.path.exists(p)]
- if not self.config['recentDeckPaths']:
- self.mainWin.menuOpenRecent.setEnabled(False)
- return
- self.mainWin.menuOpenRecent.setEnabled(True)
- self.mainWin.menuOpenRecent.clear()
- n = 1
- for file in self.config['recentDeckPaths']:
- a = QAction(self)
- if sys.platform.startswith("darwin"):
- a.setShortcut(_("Ctrl+Alt+%d" % n))
- else:
- a.setShortcut(_("Alt+%d" % n))
- a.setText(os.path.basename(file))
- a.setStatusTip(os.path.abspath(file))
- self.connect(a, SIGNAL("triggered()"),
- lambda n=n: self.loadRecent(n-1))
- self.mainWin.menuOpenRecent.addAction(a)
- n += 1
+ def onSwitchToDeck(self):
+ diag = QDialog(self)
+ diag.setWindowTitle(_("Open Recent Deck"))
+ vbox = QVBoxLayout()
+ combo = QComboBox()
+ self.switchDecks = (
+ [(os.path.basename(x).replace(".anki", ""), x)
+ for x in self.config['recentDeckPaths']
+ if not self.deck or self.deck.path != x])
+ self.switchDecks.sort()
+ combo.addItems(QStringList([x[0] for x in self.switchDecks]))
+ self.connect(combo, SIGNAL("activated(int)"),
+ self.onSwitchActivated)
+ vbox.addWidget(combo)
+ bbox = QDialogButtonBox(QDialogButtonBox.Cancel)
+ self.connect(bbox, SIGNAL("rejected()"),
+ lambda: self.switchDeckDiag.close())
+ vbox.addWidget(bbox)
+ diag.setLayout(vbox)
+ diag.show()
+ combo.showPopup()
+ combo.setFocus()
+ self.switchDeckDiag = diag
+ diag.exec_()
- def loadRecent(self, n):
- self.loadDeck(self.config['recentDeckPaths'][n])
+ def onSwitchActivated(self, idx):
+ self.switchDeckDiag.close()
+ self.loadDeck(self.switchDecks[idx][1])
# New files, loading & saving
##########################################################################
@@ -758,6 +758,12 @@ To upgrade an old deck, download Anki 0.9.8.7."""))
if self.deck is not None:
if self.deck.reviewEarly:
self.deck.resetAfterReviewEarly()
+ # update counts
+ for d in self.browserDecks:
+ if d['path'] == self.deck.path:
+ d['due'] = self.deck.failedSoonCount + self.deck.revCount
+ d['new'] = self.deck.newCountToday
+ d['mod'] = self.deck.modified
if self.deck.modifiedSinceSave():
if (self.deck.path is None or
(not self.config['saveOnClose'] and
@@ -807,6 +813,7 @@ To upgrade an old deck, download Anki 0.9.8.7."""))
self.deck.initUndo()
self.deck.addModel(BasicModel())
self.deck.save()
+ self.browserLastRefreshed = 0
self.moveToState("initial")
def ensureSyncParams(self):
@@ -858,11 +865,13 @@ To upgrade an old deck, download Anki 0.9.8.7."""))
if self.syncDeck(onlyMerge=True, reload=2, interactive=False):
return
self.deck = None
+ self.browserLastRefreshed = 0
self.moveToState("initial")
def onGetSharedDeck(self):
if not self.inMainWindow(): return
ui.getshared.GetShared(self, 0)
+ self.browserLastRefreshed = 0
def onGetSharedPlugin(self):
if not self.inMainWindow(): return
@@ -885,6 +894,7 @@ To upgrade an old deck, download Anki 0.9.8.7."""))
return False
else:
self.updateRecentFiles(file)
+ self.browserLastRefreshed = 0
return True
def showToolTip(self, msg):
@@ -955,6 +965,7 @@ your deck."""))
self.deck.initUndo()
self.updateTitleBar()
self.updateRecentFiles(self.deck.path)
+ self.browserLastRefreshed = 0
self.moveToState("initial")
return file
@@ -971,11 +982,63 @@ your deck."""))
self.connect(self.mainWin.importDeckButton,
SIGNAL("clicked()"),
self.onImport)
+ self.browserLastRefreshed = 0
+ self.browserDecks = []
+
+ def refreshBrowserDecks(self):
+ self.browserDecks = []
+ if not self.config['recentDeckPaths']:
+ return
+ toRemove = []
+ if ui.splash.finished:
+ self.startProgress(max=len(self.config['recentDeckPaths']))
+ for c, d in enumerate(self.config['recentDeckPaths']):
+ if ui.splash.finished:
+ self.updateProgress(_("Checking deck %(x)d of %(y)d...") % {
+ 'x': c, 'y': len(self.config['recentDeckPaths'])})
+ if not os.path.exists(d):
+ toRemove.append(d)
+ continue
+ try:
+ deck = DeckStorage.Deck(d, backup=False)
+ except:
+ toRemove.append(d)
+ continue
+ self.browserDecks.append({
+ 'path': d,
+ 'name': deck.name(),
+ 'due': deck.failedSoonCount + deck.revCount,
+ 'new': deck.newCountToday,
+ 'mod': deck.modified,
+ })
+ deck.close()
+ for d in toRemove:
+ self.config['recentDeckPaths'].remove(d)
+ self.config.save()
+ if ui.splash.finished:
+ self.finishProgress()
+ self.browserLastRefreshed = time.time()
+
+ def reorderBrowserDecks(self):
+ h = {}
+ for d in self.browserDecks:
+ h[d['path']] = d
+ self.browserDecks = []
+ for path in self.config['recentDeckPaths']:
+ try:
+ self.browserDecks.append(h[path])
+ except:
+ pass
+
+ def forceBrowserRefresh(self):
+ self.browserLastRefreshed = 0
+ self.showDeckBrowser()
def showDeckBrowser(self):
import sip
focusButton = None
self.switchToDecksScreen()
+ # remove all widgets from layout & layout itself
if self.mainWin.decksFrame.layout():
while 1:
obj = self.mainWin.decksFrame.layout().takeAt(0)
@@ -987,60 +1050,58 @@ your deck."""))
obj.widget().deleteLater()
sip.delete(obj)
sip.delete(self.mainWin.decksFrame.layout())
+ # build new layout
layout = QGridLayout()
- self.deckBrowserDecks = []
- if self.config['recentDeckPaths']:
+ if (time.time() - self.browserLastRefreshed >
+ self.config['deckBrowserRefreshPeriod']):
+ self.refreshBrowserDecks()
+ else:
+ self.reorderBrowserDecks()
+ if self.browserDecks:
layout.addWidget(QLabel(_("Deck")), 0, 0)
- l = QLabel(_("Due"))
+ l = QLabel(_("Due
Today"))
l.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
layout.addWidget(l, 0, 1)
- l = QLabel(_("New"))
+ l = QLabel(_("New
Today"))
l.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
layout.addWidget(l, 0, 2)
- toRemove = []
- if ui.splash.finished:
- self.startProgress(max=len(self.config['recentDeckPaths']))
- for c, d in enumerate(self.config['recentDeckPaths']):
- if ui.splash.finished:
- self.updateProgress(_("Checking deck %(x)d of %(y)d...") % {
- 'x': c, 'y': len(self.config['recentDeckPaths'])})
- if not os.path.exists(d):
- toRemove.append(d)
- continue
- try:
- deck = DeckStorage.Deck(d, backup=False)
- except:
- toRemove.append(d)
- continue
- self.deckBrowserDecks.append(d)
- # name & stats
- n = deck.name()
+ # space
+ layout.addWidget(QLabel(" " * 4), 0, 3)
+ for c, deck in enumerate(self.browserDecks):
+ # name
+ n = deck['name']
if len(n) > 30:
n = n[:30] + "..."
- layout.addWidget(QLabel(n), c+1, 0)
- l = QLabel("" + str(deck.failedSoonCount + deck.revCount) +
- "")
+ mod = _("%s ago") % anki.utils.fmtTimeSpan(
+ time.time() - deck['mod'])
+ layout.addWidget(QLabel(
+ "%d. %s
%s" %
+ (c+1, n, mod)), c+1, 0)
+ # due
+ col = '%s'
+ if deck['due'] > 0:
+ s = col % str(deck['due'])
+ else:
+ s = ""
+ l = QLabel(s)
l.setMinimumWidth(50)
l.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
layout.addWidget(l, c+1, 1)
- l = QLabel(str(deck.newCount))
+ # new
+ if deck['new']:
+ s = str(deck['new'])
+ else:
+ s = ""
+ l = QLabel(s)
l.setMinimumWidth(50)
l.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
layout.addWidget(l, c+1, 2)
# open
openButton = QPushButton(_("Open"))
- extra = ""
- if c < 9:
- extra = " (Alt+%d)" % (c+1)
- openButton.setToolTip(_("Open this deck%s") % extra)
- if c < 9:
- if sys.platform.startswith("darwin"):
- openButton.setShortcut(_("Ctrl+Alt+%d" % (c+1)))
- else:
- openButton.setShortcut(_("Alt+%d" % (c+1)))
+ openButton.setToolTip(_("Open this deck"))
self.connect(openButton, SIGNAL("clicked()"),
- lambda d=d: self.loadDeck(d))
- layout.addWidget(openButton, c+1, 3)
+ lambda d=deck['path']: self.loadDeck(d))
+ layout.addWidget(openButton, c+1, 4)
if c == 0:
focusButton = openButton
# more
@@ -1053,19 +1114,24 @@ your deck."""))
_("Forget removes the deck from the list without deleting."))
self.connect(moreButton, SIGNAL("currentIndexChanged(int)"),
lambda idx, c=c: self.onDeckBrowserMore(idx, c))
- layout.addWidget(moreButton, c+1, 4)
- deck.close()
+ layout.addWidget(moreButton, c+1, 5)
refresh = QPushButton(_("Refresh"))
+ refresh.setToolTip(_("Check due counts again (Ctrl+r)"))
+ refresh.setShortcut(_("Ctrl+r"))
self.connect(refresh, SIGNAL("clicked()"),
- self.showDeckBrowser)
- layout.addWidget(refresh, c+2, 3)
- for d in toRemove:
- self.config['recentDeckPaths'].remove(d)
- if ui.splash.finished:
- self.finishProgress()
+ self.forceBrowserRefresh)
+ layout.addWidget(refresh, c+2, 4)
else:
- layout.addWidget(QLabel(_(
- "Welcome to Anki! Click 'Download Deck' to get started.")), 0, 0)
+ l = QLabel(_("""\
+
+
+Welcome to Anki! Click download deck to get started. You can return here
+later by clicking on the left-pointing arrow on the toolbar.
+
+
+"""))
+ l.setWordWrap(True)
+ layout.addWidget(l, 0, 0)
self.mainWin.decksFrame.setLayout(layout)
self.app.processEvents()
if focusButton:
@@ -1076,11 +1142,13 @@ your deck."""))
return
elif idx == 1:
# forget
- self.config['recentDeckPaths'].remove(self.deckBrowserDecks[c])
+ self.config['recentDeckPaths'].remove(self.browserDecks[c]['path'])
+ del self.browserDecks[c]
self.showDeckBrowser()
elif idx == 2:
# delete
- deck = self.deckBrowserDecks[c]
+ deck = self.browserDecks[c]['path']
+ del self.browserDecks[c]
if ui.utils.askUser(_("Delete %s?") % os.path.basename(deck)):
os.unlink(deck)
self.config['recentDeckPaths'].remove(deck)
@@ -2065,6 +2133,7 @@ Couldn't contact Anki Online. Please check your internet connection.""")
self.connect(m.actionOpenOnline, s, self.onOpenOnline)
self.connect(m.actionDownloadSharedDeck, s, self.onGetSharedDeck)
self.connect(m.actionDownloadSharedPlugin, s, self.onGetSharedPlugin)
+ self.connect(m.actionOpenRecent, s, self.onSwitchToDeck)
self.connect(m.actionOpen, s, self.onOpen)
self.connect(m.actionSave, s, self.onSave)
self.connect(m.actionSaveAs, s, self.onSaveAs)
diff --git a/designer/main.ui b/designer/main.ui
index 04694d424..c57d5685c 100644
--- a/designer/main.ui
+++ b/designer/main.ui
@@ -1501,14 +1501,9 @@
-
-
+
@@ -2315,6 +2310,18 @@
123
+
+
+
+ :/icons/document-open-recent.png:/icons/document-open-recent.png
+
+
+ Open Recent...
+
+
+ Ctrl+Shift+R
+
+
newPerDay