mirror of
https://github.com/ankitects/anki.git
synced 2025-09-25 17:26:36 -04:00
remove sidebar in browser
use a pop-up menu instead, which saves a lot of screen real estate and should be more intuitive for new users also: - add options to manage note types and clear unused tags in the relevant submenus - shuffle a few shortcut keys - remove the old favourites code - saving and removing now done via the menu - individual card templates now searchable
This commit is contained in:
parent
0c3565228d
commit
2415611450
2 changed files with 360 additions and 402 deletions
370
aqt/browser.py
370
aqt/browser.py
|
@ -17,7 +17,8 @@ from anki.utils import fmtTimeSpan, ids2str, stripHTMLMedia, htmlToTextLine, isW
|
||||||
isMac, isLin
|
isMac, isLin
|
||||||
from aqt.utils import saveGeom, restoreGeom, saveSplitter, restoreSplitter, \
|
from aqt.utils import saveGeom, restoreGeom, saveSplitter, restoreSplitter, \
|
||||||
saveHeader, restoreHeader, saveState, restoreState, applyStyles, getTag, \
|
saveHeader, restoreHeader, saveState, restoreState, applyStyles, getTag, \
|
||||||
showInfo, askUser, tooltip, openHelp, showWarning, shortcut, mungeQA
|
showInfo, askUser, tooltip, openHelp, showWarning, shortcut, mungeQA, \
|
||||||
|
getOnlyText
|
||||||
from anki.hooks import runHook, addHook, remHook
|
from anki.hooks import runHook, addHook, remHook
|
||||||
from aqt.webview import AnkiWebView
|
from aqt.webview import AnkiWebView
|
||||||
from anki.consts import *
|
from anki.consts import *
|
||||||
|
@ -367,15 +368,12 @@ class Browser(QMainWindow):
|
||||||
self.form.setupUi(self)
|
self.form.setupUi(self)
|
||||||
restoreGeom(self, "editor", 0)
|
restoreGeom(self, "editor", 0)
|
||||||
restoreState(self, "editor")
|
restoreState(self, "editor")
|
||||||
restoreSplitter(self.form.splitter_2, "editor2")
|
|
||||||
restoreSplitter(self.form.splitter, "editor3")
|
restoreSplitter(self.form.splitter, "editor3")
|
||||||
self.form.splitter_2.setChildrenCollapsible(False)
|
|
||||||
self.form.splitter.setChildrenCollapsible(False)
|
self.form.splitter.setChildrenCollapsible(False)
|
||||||
self.card = None
|
self.card = None
|
||||||
self.setupColumns()
|
self.setupColumns()
|
||||||
self.setupTable()
|
self.setupTable()
|
||||||
self.setupMenus()
|
self.setupMenus()
|
||||||
self.setupTree()
|
|
||||||
self.setupHeaders()
|
self.setupHeaders()
|
||||||
self.setupHooks()
|
self.setupHooks()
|
||||||
self.setupEditor()
|
self.setupEditor()
|
||||||
|
@ -390,6 +388,8 @@ class Browser(QMainWindow):
|
||||||
f.previewButton.clicked.connect(self.onTogglePreview)
|
f.previewButton.clicked.connect(self.onTogglePreview)
|
||||||
f.previewButton.setToolTip(_("Preview Selected Card (%s)") %
|
f.previewButton.setToolTip(_("Preview Selected Card (%s)") %
|
||||||
shortcut(_("Ctrl+Shift+P")))
|
shortcut(_("Ctrl+Shift+P")))
|
||||||
|
|
||||||
|
f.filter.clicked.connect(self.onFilterButton)
|
||||||
# edit
|
# edit
|
||||||
f.actionUndo.triggered.connect(self.mw.onUndo)
|
f.actionUndo.triggered.connect(self.mw.onUndo)
|
||||||
f.actionInvertSelection.triggered.connect(self.invertSelection)
|
f.actionInvertSelection.triggered.connect(self.invertSelection)
|
||||||
|
@ -400,7 +400,6 @@ class Browser(QMainWindow):
|
||||||
f.actionAdd.triggered.connect(self.mw.onAddCard)
|
f.actionAdd.triggered.connect(self.mw.onAddCard)
|
||||||
f.actionAdd_Tags.triggered.connect(lambda: self.addTags())
|
f.actionAdd_Tags.triggered.connect(lambda: self.addTags())
|
||||||
f.actionRemove_Tags.triggered.connect(lambda: self.deleteTags())
|
f.actionRemove_Tags.triggered.connect(lambda: self.deleteTags())
|
||||||
f.actionClear_Unused_Tags.triggered.connect(self.clearUnusedTags)
|
|
||||||
f.actionChangeModel.triggered.connect(self.onChangeModel)
|
f.actionChangeModel.triggered.connect(self.onChangeModel)
|
||||||
f.actionFindDuplicates.triggered.connect(self.onFindDupes)
|
f.actionFindDuplicates.triggered.connect(self.onFindDupes)
|
||||||
f.actionFindReplace.triggered.connect(self.onFindReplace)
|
f.actionFindReplace.triggered.connect(self.onFindReplace)
|
||||||
|
@ -419,7 +418,7 @@ class Browser(QMainWindow):
|
||||||
f.actionLastCard.triggered.connect(self.onLastCard)
|
f.actionLastCard.triggered.connect(self.onLastCard)
|
||||||
f.actionFind.triggered.connect(self.onFind)
|
f.actionFind.triggered.connect(self.onFind)
|
||||||
f.actionNote.triggered.connect(self.onNote)
|
f.actionNote.triggered.connect(self.onNote)
|
||||||
f.actionTags.triggered.connect(self.onTags)
|
f.actionTags.triggered.connect(self.onFilterButton)
|
||||||
f.actionCardList.triggered.connect(self.onCardList)
|
f.actionCardList.triggered.connect(self.onCardList)
|
||||||
# help
|
# help
|
||||||
f.actionGuide.triggered.connect(self.onHelp)
|
f.actionGuide.triggered.connect(self.onHelp)
|
||||||
|
@ -465,7 +464,6 @@ class Browser(QMainWindow):
|
||||||
def _closeEventCleanup(self):
|
def _closeEventCleanup(self):
|
||||||
self._cancelPreviewTimer()
|
self._cancelPreviewTimer()
|
||||||
self.editor.setNote(None)
|
self.editor.setNote(None)
|
||||||
saveSplitter(self.form.splitter_2, "editor2")
|
|
||||||
saveSplitter(self.form.splitter, "editor3")
|
saveSplitter(self.form.splitter, "editor3")
|
||||||
saveGeom(self, "editor")
|
saveGeom(self, "editor")
|
||||||
saveState(self, "editor")
|
saveState(self, "editor")
|
||||||
|
@ -484,10 +482,6 @@ class Browser(QMainWindow):
|
||||||
"Show answer on RET or register answer."
|
"Show answer on RET or register answer."
|
||||||
if evt.key() == Qt.Key_Escape:
|
if evt.key() == Qt.Key_Escape:
|
||||||
self.close()
|
self.close()
|
||||||
elif self.mw.app.focusWidget() == self.form.tree:
|
|
||||||
if evt.key() in (Qt.Key_Return, Qt.Key_Enter):
|
|
||||||
item = self.form.tree.currentItem()
|
|
||||||
self.onTreeClick(item, 0)
|
|
||||||
|
|
||||||
def setupColumns(self):
|
def setupColumns(self):
|
||||||
self.columns = [
|
self.columns = [
|
||||||
|
@ -513,7 +507,6 @@ class Browser(QMainWindow):
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
||||||
def setupSearch(self):
|
def setupSearch(self):
|
||||||
self.form.searchEdit.setLineEdit(FavouritesLineEdit(self.mw, self))
|
|
||||||
self.form.searchButton.clicked.connect(self.onSearchActivated)
|
self.form.searchButton.clicked.connect(self.onSearchActivated)
|
||||||
self.form.searchEdit.lineEdit().returnPressed.connect(self.onSearchActivated)
|
self.form.searchEdit.lineEdit().returnPressed.connect(self.onSearchActivated)
|
||||||
self.form.searchEdit.setCompleter(None)
|
self.form.searchEdit.setCompleter(None)
|
||||||
|
@ -742,39 +735,22 @@ by clicking on one on the left."""))
|
||||||
# Filter tree
|
# Filter tree
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
||||||
class CallbackItem(QTreeWidgetItem):
|
def onFilterButton(self):
|
||||||
def __init__(self, root, name, onclick, oncollapse=None, expanded=False):
|
m = QMenu()
|
||||||
QTreeWidgetItem.__init__(self, root, [name])
|
|
||||||
self.setExpanded(expanded)
|
|
||||||
self.onclick = onclick
|
|
||||||
self.oncollapse = oncollapse
|
|
||||||
|
|
||||||
def setupTree(self):
|
self._addCommonFilters(m)
|
||||||
self.form.tree.itemClicked.connect(self.onTreeClick)
|
m.addSeparator()
|
||||||
p = QPalette()
|
|
||||||
p.setColor(QPalette.Base, QColor("#d6dde0"))
|
|
||||||
self.form.tree.setPalette(p)
|
|
||||||
self.buildTree()
|
|
||||||
self.form.tree.itemExpanded.connect(lambda item: self.onTreeCollapse(item))
|
|
||||||
self.form.tree.itemCollapsed.connect(lambda item: self.onTreeCollapse(item))
|
|
||||||
|
|
||||||
def buildTree(self):
|
self._addTodayFilters(m)
|
||||||
self.form.tree.clear()
|
self._addCardStateFilters(m)
|
||||||
root = self.form.tree
|
m.addSeparator()
|
||||||
self._systemTagTree(root)
|
|
||||||
self._favTree(root)
|
|
||||||
self._decksTree(root)
|
|
||||||
self._modelTree(root)
|
|
||||||
self._userTagTree(root)
|
|
||||||
self.form.tree.setIndentation(15)
|
|
||||||
|
|
||||||
def onTreeClick(self, item, col):
|
self._addDeckFilters(m)
|
||||||
if getattr(item, 'onclick', None):
|
self._addNoteTypeFilters(m)
|
||||||
item.onclick()
|
self._addTagFilters(m)
|
||||||
|
self._addSavedSearches(m)
|
||||||
|
|
||||||
def onTreeCollapse(self, item):
|
m.exec_(self.form.filter.mapToGlobal(QPoint(0,0)))
|
||||||
if getattr(item, 'oncollapse', None):
|
|
||||||
item.oncollapse()
|
|
||||||
|
|
||||||
def setFilter(self, *args):
|
def setFilter(self, *args):
|
||||||
if len(args) == 1:
|
if len(args) == 1:
|
||||||
|
@ -805,71 +781,172 @@ by clicking on one on the left."""))
|
||||||
self.form.searchEdit.lineEdit().setText(txt)
|
self.form.searchEdit.lineEdit().setText(txt)
|
||||||
self.onSearchActivated()
|
self.onSearchActivated()
|
||||||
|
|
||||||
def _systemTagTree(self, root):
|
def _addSimpleFilters(self, m, items):
|
||||||
tags = (
|
for row in items:
|
||||||
(_("Whole Collection"), "ankibw", ""),
|
if row is None:
|
||||||
(_("Current Deck"), "deck16", "deck:current"),
|
m.addSeparator()
|
||||||
(_("Added Today"), "view-pim-calendar.png", "added:1"),
|
else:
|
||||||
(_("Studied Today"), "view-pim-calendar.png", "rated:1"),
|
label, search = row
|
||||||
(_("Again Today"), "view-pim-calendar.png", "rated:1:1"),
|
a = m.addAction(label)
|
||||||
(_("New"), "plus16.png", "is:new"),
|
a.triggered.connect(lambda *, f=search: self.setFilter(f))
|
||||||
(_("Learning"), "stock_new_template_red.png", "is:learn"),
|
|
||||||
(_("Review"), "clock16.png", "is:review"),
|
|
||||||
(_("Due"), "clock16.png", "is:due"),
|
|
||||||
(_("Marked"), "star16.png", "tag:marked"),
|
|
||||||
(_("Suspended"), "media-playback-pause.png", "is:suspended"),
|
|
||||||
(_("Leech"), "emblem-important.png", "tag:leech"))
|
|
||||||
for name, icon, cmd in tags:
|
|
||||||
item = self.CallbackItem(
|
|
||||||
root, name, lambda c=cmd: self.setFilter(c))
|
|
||||||
item.setIcon(0, QIcon(":/icons/" + icon))
|
|
||||||
return root
|
|
||||||
|
|
||||||
def _favTree(self, root):
|
def _addCommonFilters(self, m):
|
||||||
saved = self.col.conf.get('savedFilters', [])
|
items = (
|
||||||
if not saved:
|
(_("Whole Collection"), ""),
|
||||||
# Don't add favourites to tree if none saved
|
(_("Current Deck"), "deck:current"),
|
||||||
return
|
None,
|
||||||
root = self.CallbackItem(root, _("My Searches"), None)
|
(_("Marked"), "tag:marked"),
|
||||||
root.setExpanded(True)
|
(_("Leech"), "tag:leech"))
|
||||||
root.setIcon(0, QIcon(":/icons/emblem-favorite-dark.png"))
|
self._addSimpleFilters(m, items)
|
||||||
for name, filt in sorted(saved.items()):
|
|
||||||
item = self.CallbackItem(root, name, lambda s=filt: self.setFilter(s))
|
|
||||||
item.setIcon(0, QIcon(":/icons/emblem-favorite-dark.png"))
|
|
||||||
|
|
||||||
def _userTagTree(self, root):
|
def _addTodayFilters(self, m):
|
||||||
for t in sorted(self.col.tags.all()):
|
m = m.addMenu(_("Today"))
|
||||||
|
items = (
|
||||||
|
(_("Added Today"), "added:1"),
|
||||||
|
(_("Studied Today"), "rated:1"),
|
||||||
|
(_("Again Today"), "rated:1:1"))
|
||||||
|
self._addSimpleFilters(m, items)
|
||||||
|
|
||||||
|
def _addCardStateFilters(self, m):
|
||||||
|
m = m.addMenu(_("Card State"))
|
||||||
|
items = (
|
||||||
|
(_("New"), "is:new"),
|
||||||
|
(_("Learning"), "is:learn"),
|
||||||
|
(_("Review"), "is:review"),
|
||||||
|
(_("Due"), "is:due"),
|
||||||
|
None,
|
||||||
|
(_("Suspended"), "is:suspended"),
|
||||||
|
(_("Buried"), "is:buried"))
|
||||||
|
self._addSimpleFilters(m, items)
|
||||||
|
|
||||||
|
_tagsMenuSize = 30
|
||||||
|
|
||||||
|
def _addTagFilters(self, m):
|
||||||
|
m = m.addMenu(_("Tags"))
|
||||||
|
|
||||||
|
a = m.addAction(_("Clear Unused"))
|
||||||
|
a.triggered.connect(self.clearUnusedTags)
|
||||||
|
m.addSeparator()
|
||||||
|
|
||||||
|
tags = sorted(self.col.tags.all(), key=lambda s: s.lower())
|
||||||
|
|
||||||
|
if len(tags) < self._tagsMenuSize:
|
||||||
|
self._addTagFilterBlock(m, tags)
|
||||||
|
else:
|
||||||
|
# split the list into a more manageable size
|
||||||
|
chunks = []
|
||||||
|
while tags:
|
||||||
|
chunk = tags[:self._tagsMenuSize]
|
||||||
|
chunks.append(chunk)
|
||||||
|
del tags[:self._tagsMenuSize]
|
||||||
|
# use separate menu for each chunk
|
||||||
|
for chunk in chunks:
|
||||||
|
name = chunk[0]+"..."
|
||||||
|
child = m.addMenu(name)
|
||||||
|
self._addTagFilterBlock(child, chunk)
|
||||||
|
|
||||||
|
def _addTagFilterBlock(self, m, tags):
|
||||||
|
for t in tags:
|
||||||
if t.lower() == "marked" or t.lower() == "leech":
|
if t.lower() == "marked" or t.lower() == "leech":
|
||||||
continue
|
continue
|
||||||
item = self.CallbackItem(
|
a = m.addAction(t)
|
||||||
root, t, lambda t=t: self.setFilter("tag", t))
|
a.triggered.connect(lambda *, tag=t: self.setFilter("tag", tag))
|
||||||
item.setIcon(0, QIcon(":/icons/anki-tag.png"))
|
|
||||||
|
|
||||||
def _decksTree(self, root):
|
def _addDeckFilters(self, m):
|
||||||
grps = self.col.sched.deckDueTree()
|
def addDecks(parent, decks):
|
||||||
def fillGroups(root, grps, head=""):
|
for head, did, rev, lrn, new, children in decks:
|
||||||
for g in grps:
|
name = self.mw.col.decks.get(did)['name']
|
||||||
item = self.CallbackItem(
|
shortname = name.split("::")[-1]
|
||||||
root, g[0],
|
if children:
|
||||||
lambda g=g: self.setFilter("deck", head+g[0]),
|
newparent = parent.addMenu(shortname)
|
||||||
lambda g=g: self.mw.col.decks.collapseBrowser(g[1]),
|
a = newparent.addAction(_("Filter"))
|
||||||
not self.mw.col.decks.get(g[1]).get('browserCollapsed', False))
|
a.triggered.connect(
|
||||||
item.setIcon(0, QIcon(":/icons/deck16.png"))
|
lambda *, name=name: self.setFilter("deck", name))
|
||||||
newhead = head + g[0]+"::"
|
newparent.addSeparator()
|
||||||
fillGroups(item, g[5], newhead)
|
addDecks(newparent, children)
|
||||||
fillGroups(root, grps)
|
else:
|
||||||
|
a = parent.addAction(shortname)
|
||||||
|
a.triggered.connect(
|
||||||
|
lambda *, name=name: self.setFilter("deck", name))
|
||||||
|
|
||||||
def _modelTree(self, root):
|
# fixme: could rewrite to avoid calculating due # in the future
|
||||||
for m in sorted(self.col.models.all(), key=itemgetter("name")):
|
alldecks = self.col.sched.deckDueTree()
|
||||||
mitem = self.CallbackItem(
|
root = m.addMenu(_("Decks"))
|
||||||
root, m['name'], lambda m=m: self.setFilter("mid", str(m['id'])))
|
|
||||||
mitem.setIcon(0, QIcon(":/icons/product_design.png"))
|
addDecks(root, alldecks)
|
||||||
# for t in m['tmpls']:
|
|
||||||
# titem = self.CallbackItem(
|
def _addNoteTypeFilters(self, m):
|
||||||
# t['name'], lambda m=m, t=t: self.setFilter(
|
m = m.addMenu(_("Note Types"))
|
||||||
# "model", m['name'], "card", t['name']))
|
a = m.addAction(_("Manage..."))
|
||||||
# titem.setIcon(0, QIcon(":/icons/stock_new_template.png"))
|
a.triggered.connect(self.mw.onNoteTypes)
|
||||||
# mitem.addChild(titem)
|
m.addSeparator()
|
||||||
|
for nt in sorted(self.col.models.all(), key=itemgetter("name")):
|
||||||
|
# no sub menu if it's a single template
|
||||||
|
if len(nt['tmpls']) == 1:
|
||||||
|
a = m.addAction(nt['name'])
|
||||||
|
a.triggered.connect(lambda *, nt=nt: self.setFilter("mid", str(nt['id'])))
|
||||||
|
else:
|
||||||
|
subm = m.addMenu(nt['name'])
|
||||||
|
a = subm.addAction(_("All Card Types"))
|
||||||
|
a.triggered.connect(lambda *, nt=nt: self.setFilter("mid", str(nt['id'])))
|
||||||
|
|
||||||
|
# add templates
|
||||||
|
subm.addSeparator()
|
||||||
|
for c, tmpl in enumerate(nt['tmpls']):
|
||||||
|
a = subm.addAction(_("%(n)d: %(name)s") % dict(
|
||||||
|
n=c+1, name=tmpl['name']))
|
||||||
|
a.triggered.connect(lambda *, nt=nt, c=c: self.setFilter(
|
||||||
|
"mid", str(nt['id']), "card", str(c+1)
|
||||||
|
))
|
||||||
|
|
||||||
|
# Favourites
|
||||||
|
######################################################################
|
||||||
|
|
||||||
|
def _addSavedSearches(self, m):
|
||||||
|
# make sure exists
|
||||||
|
if "savedFilters" not in self.col.conf:
|
||||||
|
self.col.conf['savedFilters'] = {}
|
||||||
|
|
||||||
|
m.addSeparator()
|
||||||
|
|
||||||
|
if self._currentFilterIsSaved():
|
||||||
|
a = m.addAction(_("Remove Current Filter..."))
|
||||||
|
a.triggered.connect(self._onRemoveFilter)
|
||||||
|
else:
|
||||||
|
a = m.addAction(_("Save Current Filter..."))
|
||||||
|
a.triggered.connect(self._onSaveFilter)
|
||||||
|
|
||||||
|
saved = self.col.conf['savedFilters']
|
||||||
|
if not saved:
|
||||||
|
return
|
||||||
|
|
||||||
|
m.addSeparator()
|
||||||
|
for name, filt in sorted(saved.items()):
|
||||||
|
a = m.addAction(name)
|
||||||
|
a.triggered.connect(lambda *, f=filt: self.setFilter(f))
|
||||||
|
|
||||||
|
def _onSaveFilter(self):
|
||||||
|
name = getOnlyText(_("Please give your filter a name:"))
|
||||||
|
if not name:
|
||||||
|
return
|
||||||
|
filt = self.form.searchEdit.lineEdit().text()
|
||||||
|
self.col.conf['savedFilters'][name] = filt
|
||||||
|
self.col.setMod()
|
||||||
|
|
||||||
|
def _onRemoveFilter(self):
|
||||||
|
name = self._currentFilterIsSaved()
|
||||||
|
if not askUser(_("Remove %s from your saved searches?") % name):
|
||||||
|
return
|
||||||
|
del self.col.conf['savedFilters'][name]
|
||||||
|
self.col.setMod()
|
||||||
|
|
||||||
|
# returns name if found
|
||||||
|
def _currentFilterIsSaved(self):
|
||||||
|
filt = self.form.searchEdit.lineEdit().text()
|
||||||
|
for k,v in self.col.conf['savedFilters'].items():
|
||||||
|
if filt == v:
|
||||||
|
return k
|
||||||
|
return None
|
||||||
|
|
||||||
# Info
|
# Info
|
||||||
######################################################################
|
######################################################################
|
||||||
|
@ -1261,7 +1338,6 @@ update cards set usn=?, mod=?, did=? where id in """ + scids,
|
||||||
|
|
||||||
def _clearUnusedTags(self):
|
def _clearUnusedTags(self):
|
||||||
self.col.tags.registerNotes()
|
self.col.tags.registerNotes()
|
||||||
self.buildTree()
|
|
||||||
|
|
||||||
# Suspending and marking
|
# Suspending and marking
|
||||||
######################################################################
|
######################################################################
|
||||||
|
@ -1385,16 +1461,12 @@ update cards set usn=?, mod=?, did=? where id in """ + scids,
|
||||||
addHook("reset", self.onReset)
|
addHook("reset", self.onReset)
|
||||||
addHook("editTimer", self.refreshCurrentCard)
|
addHook("editTimer", self.refreshCurrentCard)
|
||||||
addHook("editFocusLost", self.refreshCurrentCardFilter)
|
addHook("editFocusLost", self.refreshCurrentCardFilter)
|
||||||
for t in "newTag", "newModel", "newDeck":
|
|
||||||
addHook(t, self.buildTree)
|
|
||||||
|
|
||||||
def teardownHooks(self):
|
def teardownHooks(self):
|
||||||
remHook("reset", self.onReset)
|
remHook("reset", self.onReset)
|
||||||
remHook("editTimer", self.refreshCurrentCard)
|
remHook("editTimer", self.refreshCurrentCard)
|
||||||
remHook("editFocusLost", self.refreshCurrentCardFilter)
|
remHook("editFocusLost", self.refreshCurrentCardFilter)
|
||||||
remHook("undoState", self.onUndoState)
|
remHook("undoState", self.onUndoState)
|
||||||
for t in "newTag", "newModel", "newDeck":
|
|
||||||
remHook(t, self.buildTree)
|
|
||||||
|
|
||||||
def onUndoState(self, on):
|
def onUndoState(self, on):
|
||||||
self.form.actionUndo.setEnabled(on)
|
self.form.actionUndo.setEnabled(on)
|
||||||
|
@ -1590,9 +1662,6 @@ update cards set usn=?, mod=?, did=? where id in """ + scids,
|
||||||
self.editor.web.setFocus()
|
self.editor.web.setFocus()
|
||||||
self.editor.web.eval("focusField(0);")
|
self.editor.web.eval("focusField(0);")
|
||||||
|
|
||||||
def onTags(self):
|
|
||||||
self.form.tree.setFocus()
|
|
||||||
|
|
||||||
def onCardList(self):
|
def onCardList(self):
|
||||||
self.form.tableView.setFocus()
|
self.form.tableView.setFocus()
|
||||||
|
|
||||||
|
@ -1774,88 +1843,3 @@ Are you sure you want to continue?""")):
|
||||||
def onHelp(self):
|
def onHelp(self):
|
||||||
openHelp("browsermisc")
|
openHelp("browsermisc")
|
||||||
|
|
||||||
# Favourites button
|
|
||||||
######################################################################
|
|
||||||
class FavouritesLineEdit(QLineEdit):
|
|
||||||
buttonClicked = pyqtSignal(bool)
|
|
||||||
|
|
||||||
def __init__(self, mw, browser, parent=None):
|
|
||||||
super().__init__(parent)
|
|
||||||
self.mw = mw
|
|
||||||
self.browser = browser
|
|
||||||
# add conf if missing
|
|
||||||
if 'savedFilters' not in self.mw.col.conf:
|
|
||||||
self.mw.col.conf['savedFilters'] = {}
|
|
||||||
self.button = QToolButton(self)
|
|
||||||
self.button.setStyleSheet('border: 0px;')
|
|
||||||
self.button.setCursor(Qt.ArrowCursor)
|
|
||||||
self.button.clicked.connect(self.buttonClicked.emit)
|
|
||||||
self.setIcon(':/icons/emblem-favorite-off.png')
|
|
||||||
# flag to raise save or delete dialog on button click
|
|
||||||
self.doSave = True
|
|
||||||
# name of current saved filter (if query matches)
|
|
||||||
self.name = None
|
|
||||||
self.buttonClicked.connect(self.onClicked)
|
|
||||||
self.textChanged.connect(self.updateButton)
|
|
||||||
|
|
||||||
def resizeEvent(self, event):
|
|
||||||
buttonSize = self.button.sizeHint()
|
|
||||||
frameWidth = self.style().pixelMetric(QStyle.PM_DefaultFrameWidth)
|
|
||||||
self.button.move(self.rect().right() - frameWidth - buttonSize.width(),
|
|
||||||
(self.rect().bottom() - buttonSize.height() + 1) / 2)
|
|
||||||
self.setTextMargins(0, 0, buttonSize.width() * 1.5, 0)
|
|
||||||
super().resizeEvent(event)
|
|
||||||
|
|
||||||
def setIcon(self, path):
|
|
||||||
self.button.setIcon(QIcon(path))
|
|
||||||
|
|
||||||
def setText(self, txt):
|
|
||||||
super().setText(txt)
|
|
||||||
self.updateButton()
|
|
||||||
|
|
||||||
def updateButton(self, reset=True):
|
|
||||||
# If search text is a saved query, switch to the delete button.
|
|
||||||
# Otherwise show save button.
|
|
||||||
txt = str(self.text()).strip()
|
|
||||||
for key, value in list(self.mw.col.conf['savedFilters'].items()):
|
|
||||||
if txt == value:
|
|
||||||
self.doSave = False
|
|
||||||
self.name = key
|
|
||||||
self.setIcon(QIcon(":/icons/emblem-favorite.png"))
|
|
||||||
return
|
|
||||||
self.doSave = True
|
|
||||||
self.setIcon(QIcon(":/icons/emblem-favorite-off.png"))
|
|
||||||
|
|
||||||
def onClicked(self):
|
|
||||||
if self.doSave:
|
|
||||||
self.saveClicked()
|
|
||||||
else:
|
|
||||||
self.deleteClicked()
|
|
||||||
|
|
||||||
def saveClicked(self):
|
|
||||||
txt = str(self.text()).strip()
|
|
||||||
dlg = QInputDialog(self)
|
|
||||||
dlg.setInputMode(QInputDialog.TextInput)
|
|
||||||
dlg.setLabelText(_("The current search terms will be added as a new "
|
|
||||||
"item in the sidebar.\n"
|
|
||||||
"Search name:"))
|
|
||||||
dlg.setWindowTitle(_("Save search"))
|
|
||||||
ok = dlg.exec_()
|
|
||||||
name = dlg.textValue()
|
|
||||||
if ok:
|
|
||||||
self.mw.col.conf['savedFilters'][name] = txt
|
|
||||||
self.mw.col.setMod()
|
|
||||||
|
|
||||||
self.updateButton()
|
|
||||||
self.browser.buildTree()
|
|
||||||
|
|
||||||
def deleteClicked(self):
|
|
||||||
msg = _('Remove "%s" from your saved searches?') % self.name
|
|
||||||
ok = QMessageBox.question(self, _('Remove search'),
|
|
||||||
msg, QMessageBox.Yes, QMessageBox.No)
|
|
||||||
|
|
||||||
if ok == QMessageBox.Yes:
|
|
||||||
self.mw.col.conf['savedFilters'].pop(self.name, None)
|
|
||||||
self.mw.col.setMod()
|
|
||||||
self.updateButton()
|
|
||||||
self.browser.buildTree()
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>750</width>
|
<width>750</width>
|
||||||
<height>400</height>
|
<height>493</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="minimumSize">
|
<property name="minimumSize">
|
||||||
|
@ -26,35 +26,12 @@
|
||||||
<widget class="QWidget" name="centralwidget">
|
<widget class="QWidget" name="centralwidget">
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||||
<property name="spacing">
|
<property name="spacing">
|
||||||
<number>0</number>
|
<number>3</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="margin">
|
<property name="margin">
|
||||||
<number>0</number>
|
<number>3</number>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QSplitter" name="splitter_2">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
<widget class="QTreeWidget" name="tree">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
|
||||||
<horstretch>1</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="frameShape">
|
|
||||||
<enum>QFrame::NoFrame</enum>
|
|
||||||
</property>
|
|
||||||
<attribute name="headerVisible">
|
|
||||||
<bool>false</bool>
|
|
||||||
</attribute>
|
|
||||||
<column>
|
|
||||||
<property name="text">
|
|
||||||
<string notr="true">1</string>
|
|
||||||
</property>
|
|
||||||
</column>
|
|
||||||
</widget>
|
|
||||||
<widget class="QSplitter" name="splitter">
|
<widget class="QSplitter" name="splitter">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||||
|
@ -96,7 +73,7 @@
|
||||||
<property name="rightMargin">
|
<property name="rightMargin">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<item row="0" column="0">
|
<item row="0" column="1">
|
||||||
<widget class="QComboBox" name="searchEdit">
|
<widget class="QComboBox" name="searchEdit">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||||
|
@ -112,14 +89,14 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="1">
|
<item row="0" column="2">
|
||||||
<widget class="QPushButton" name="searchButton">
|
<widget class="QPushButton" name="searchButton">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Search</string>
|
<string>Search</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="2">
|
<item row="0" column="3">
|
||||||
<widget class="QPushButton" name="previewButton">
|
<widget class="QPushButton" name="previewButton">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Preview</string>
|
<string>Preview</string>
|
||||||
|
@ -132,6 +109,13 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QPushButton" name="filter">
|
||||||
|
<property name="text">
|
||||||
|
<string>Filter...</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
|
@ -231,7 +215,6 @@
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
</widget>
|
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
@ -297,7 +280,6 @@
|
||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
<addaction name="actionAdd_Tags"/>
|
<addaction name="actionAdd_Tags"/>
|
||||||
<addaction name="actionRemove_Tags"/>
|
<addaction name="actionRemove_Tags"/>
|
||||||
<addaction name="actionClear_Unused_Tags"/>
|
|
||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
<addaction name="actionChangeModel"/>
|
<addaction name="actionChangeModel"/>
|
||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
|
@ -324,7 +306,7 @@
|
||||||
<string>Select &All</string>
|
<string>Select &All</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="shortcut">
|
<property name="shortcut">
|
||||||
<string>Ctrl+A</string>
|
<string>Ctrl+Alt+A</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
<action name="actionUndo">
|
<action name="actionUndo">
|
||||||
|
@ -353,7 +335,7 @@
|
||||||
<string>N&ote</string>
|
<string>N&ote</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="shortcut">
|
<property name="shortcut">
|
||||||
<string>Ctrl+Shift+F</string>
|
<string>Ctrl+Shift+N</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
<action name="actionNextCard">
|
<action name="actionNextCard">
|
||||||
|
@ -392,9 +374,6 @@
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Select &Notes</string>
|
<string>Select &Notes</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="shortcut">
|
|
||||||
<string>Ctrl+Shift+N</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
</action>
|
||||||
<action name="actionFindReplace">
|
<action name="actionFindReplace">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
@ -411,10 +390,10 @@
|
||||||
</action>
|
</action>
|
||||||
<action name="actionTags">
|
<action name="actionTags">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Fil&ters</string>
|
<string>Fil&ter</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="shortcut">
|
<property name="shortcut">
|
||||||
<string>Ctrl+Shift+R</string>
|
<string>Ctrl+Shift+F</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
<action name="actionCardList">
|
<action name="actionCardList">
|
||||||
|
@ -486,11 +465,6 @@
|
||||||
<string>Ctrl+Shift+D</string>
|
<string>Ctrl+Shift+D</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
<action name="actionClear_Unused_Tags">
|
|
||||||
<property name="text">
|
|
||||||
<string>Clear Unused Tags</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
<action name="actionToggle_Suspend">
|
<action name="actionToggle_Suspend">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Toggle Suspend</string>
|
<string>Toggle Suspend</string>
|
||||||
|
|
Loading…
Reference in a new issue