improve filtered decks

- add a custom study option to the deck overview. it combines the
  ability to increase the daily limits with the ability to create filtered
  decks based on presets
- removed the presets from the filtered deck dialog.
- moved the filter/cram button on the decks/overview screen to the tools menu
- filtered decks no longer show their search terms (easily findable by
  clicking options), and instead show a brief explanation of how they work.
- the filter by tags preset now presents a dialog like anki 1.2's active tags
  dialog. decks will remember their previously selected tags.
- the custom study option creates a deck called "Custom Study Session", which
  will automatically get reused if you custom study in a different deck. you
  can rename it if you want to keep it.
- filtered decks now show in the deck list as blue
This commit is contained in:
Damien Elmes 2012-10-26 01:58:46 +09:00
parent efa1b54c2e
commit 3e45c56f3a
11 changed files with 618 additions and 260 deletions

159
aqt/customstudy.py Normal file
View file

@ -0,0 +1,159 @@
# Copyright: Damien Elmes <anki@ichi2.net>
# -*- coding: utf-8 -*-
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
from aqt.qt import *
import aqt
from anki.utils import ids2str, isWin, isMac
from aqt.utils import showInfo, showWarning, openHelp, getOnlyText, askUser
from operator import itemgetter
from anki.consts import *
RADIO_NEW = 1
RADIO_REV = 2
RADIO_FORGOT = 3
RADIO_AHEAD = 4
RADIO_RANDOM = 5
RADIO_PREVIEW = 6
RADIO_TAGS = 7
class CustomStudy(QDialog):
def __init__(self, mw):
QDialog.__init__(self, mw)
self.mw = mw
self.deck = self.mw.col.decks.current()
self.form = f = aqt.forms.customstudy.Ui_Dialog()
f.setupUi(self)
self.setWindowModality(Qt.WindowModal)
self.setupSignals()
f.radio1.click()
self.exec_()
def setupSignals(self):
f = self.form; c = self.connect; s = SIGNAL("clicked()")
c(f.radio1, s, lambda: self.onRadioChange(1))
c(f.radio2, s, lambda: self.onRadioChange(2))
c(f.radio3, s, lambda: self.onRadioChange(3))
c(f.radio4, s, lambda: self.onRadioChange(4))
c(f.radio5, s, lambda: self.onRadioChange(5))
c(f.radio6, s, lambda: self.onRadioChange(6))
c(f.radio7, s, lambda: self.onRadioChange(7))
def onRadioChange(self, idx):
f = self.form; sp = f.spin
smin = 1; smax = 9999; sval = 1
post = _("cards")
tit = ""
spShow = True
def plus(num):
if num == 1000:
num = "1000+"
return "<b>"+str(num)+"</b>"
if idx == RADIO_NEW:
new = self.mw.col.sched.totalNewForCurrentDeck()
self.deck['newToday']
tit = _("New cards in deck: %s") % plus(new)
pre = _("Increase today's new card limit by")
sval = min(new, self.deck.get('extendNew', 10))
smax = new
elif idx == RADIO_REV:
rev = self.mw.col.sched.totalRevForCurrentDeck()
tit = _("Reviews due in deck: %s") % plus(rev)
pre = _("Increase today's review limit by")
sval = min(rev, self.deck.get('extendRev', 10))
elif idx == RADIO_FORGOT:
pre = _("Review cards forgotten in last")
post = _("days")
smax = 30
elif idx == RADIO_AHEAD:
pre = _("Review ahead by")
post = _("days")
elif idx == RADIO_RANDOM:
pre = _("Select")
post = _("cards randomly from the deck")
sval = 100
elif idx == RADIO_PREVIEW:
pre = _("Preview new cards added in the last")
post = _("days")
sval = 1
elif idx == RADIO_TAGS:
tit = _("Press OK to choose tags.")
sval = 100
spShow = False
pre = post = ""
sp.setShown(spShow)
f.title.setText(tit)
f.title.setShown(not not tit)
f.spin.setMinimum(smin)
f.spin.setMaximum(smax)
f.spin.setValue(sval)
f.preSpin.setText(pre)
f.postSpin.setText(post)
self.radioIdx = idx
def accept(self):
f = self.form; i = self.radioIdx; spin = f.spin.value()
if i == RADIO_NEW:
self.deck['extendNew'] = spin
self.mw.col.decks.save(self.deck)
self.mw.col.sched.extendLimits(spin, 0)
self.mw.reset()
return QDialog.accept(self)
elif i == RADIO_REV:
self.deck['extendRev'] = spin
self.mw.col.decks.save(self.deck)
self.mw.col.sched.extendLimits(0, spin)
self.mw.reset()
return QDialog.accept(self)
elif i == RADIO_TAGS:
tags = self._getTags()
if not tags:
return
# the rest create a filtered deck
cur = self.mw.col.decks.byName(_("Custom Study Session"))
if cur:
if not cur['dyn']:
showInfo("Please rename the existing Custom Study deck first.")
return QDialog.accept(self)
else:
# safe to empty
self.mw.col.sched.emptyDyn(cur['id'])
# reuse; don't delete as it may have children
dyn = cur
self.mw.col.decks.select(cur['id'])
else:
did = self.mw.col.decks.newDyn(_("Custom Study Session"))
dyn = self.mw.col.decks.get(did)
# and then set various options
if i == RADIO_FORGOT:
dyn['delays'] = [1]
dyn['terms'][0] = ['rated:1:%d' % spin, 9999, DYN_RANDOM]
dyn['resched'] = False
elif i == RADIO_AHEAD:
dyn['delays'] = None
dyn['terms'][0] = ['prop:due<=%d' % spin, 9999, DYN_DUE]
dyn['resched'] = True
elif i == RADIO_RANDOM:
dyn['delays'] = None
dyn['terms'][0] = ['', spin, DYN_RANDOM]
dyn['resched'] = True
elif i == RADIO_PREVIEW:
dyn['delays'] = None
dyn['terms'][0] = ['is:new added:%s'%spin, 9999, DYN_OLDEST]
dyn['resched'] = True
elif i == RADIO_TAGS:
dyn['delays'] = None
dyn['terms'][0] = ["(is:new or is:due) "+tags, 9999, DYN_RANDOM]
dyn['resched'] = True
# add deck limit
dyn['terms'][0][0] = "deck:'%s' %s " % (self.deck['name'], dyn['terms'][0][0])
# generate cards
if not self.mw.col.sched.rebuildDyn():
return showWarning(_("No cards matched the criteria you provided."))
self.mw.moveToState("overview")
QDialog.accept(self)
def _getTags(self):
from aqt.taglimit import TagLimit
t = TagLimit(self.mw, self)
return t.tags

View file

@ -44,8 +44,6 @@ class DeckBrowser(object):
self._onShared()
elif cmd == "import":
self.mw.onImport()
elif cmd == "cram":
self.mw.onCram()
elif cmd == "create":
deck = getOnlyText(_("New deck name:"))
if deck:
@ -58,9 +56,8 @@ class DeckBrowser(object):
self._collapse(arg)
def _keyHandler(self, evt):
# currently does nothing
key = unicode(evt.text())
if key == "f":
self.mw.onCram()
def _selDeck(self, did):
self.mw.col.decks.select(did)
@ -85,6 +82,7 @@ body { margin: 1em; -webkit-user-select: none; }
.count { width: 6em; text-align: right; }
.collapse { color: #000; text-decoration:none; display:inline-block;
width: 1em; }
.filtered { color: #00a !important; }
""" % dict(width=_dragIndicatorBorderWidth)
_body = """
@ -179,6 +177,7 @@ where id > ?""", (self.mw.col.sched.dayCutoff-86400)*1000)
def _deckRow(self, node, depth, cnt):
name, did, due, lrn, new, children = node
deck = self.mw.col.decks.get(did)
if did == 1 and cnt > 1 and not children:
# if the default deck is empty, hide it
if not self.mw.col.db.scalar("select 1 from cards where did = 1"):
@ -204,9 +203,14 @@ where id > ?""", (self.mw.col.sched.dayCutoff-86400)*1000)
collapse = "<a class=collapse href='collapse:%d'>%s</a>" % (did, prefix)
else:
collapse = "<span class=collapse></span>"
if deck['dyn']:
extraclass = "filtered"
else:
extraclass = ""
buf += """
<td class=decktd colspan=5>%s%s<a class=deck href='open:%d'>%s</a></td>"""% (
indent(), collapse, did, name)
<td class=decktd colspan=5>%s%s<a class="deck %s" href='open:%d'>%s</a></td>"""% (
indent(), collapse, extraclass, did, name)
# due counts
def nonzeroColour(cnt, colour):
if not cnt:
@ -311,7 +315,6 @@ where id > ?""", (self.mw.col.sched.dayCutoff-86400)*1000)
["", "shared", _("Get Shared")],
["", "create", _("Create Deck")],
["Ctrl+I", "import", _("Import File")],
["F", "cram", _("Filter/Cram")],
]
buf = ""
for b in links:

View file

@ -29,67 +29,15 @@ class DeckConf(QDialog):
SIGNAL("helpRequested()"),
lambda: openHelp("filtered"))
self.setWindowTitle(_("Options for %s") % self.deck['name'])
self.setupExamples()
self.setupOrder()
self.loadConf()
self.show()
if first:
if isMac or isWin:
self.form.examples.showPopup()
else:
mw.progress.timer(200, self.form.examples.showPopup, False)
self.exec_()
def setupOrder(self):
import anki.consts as cs
self.form.order.addItems(cs.dynOrderLabels().values())
def setupExamples(self):
import anki.consts as cs
f = self.form
d = self.dynExamples = cs.dynExamples()
for c, row in enumerate(d):
if not row:
f.examples.insertSeparator(c)
else:
f.examples.addItem(row[0])
self.connect(f.examples, SIGNAL("activated(int)"),
self.onExample)
# we'll need to reset whenever something is changed
self.ignoreChange = False
def onChange(*args):
if self.ignoreChange:
return
f.examples.setCurrentIndex(0)
c = self.connect
c(f.steps, SIGNAL("textEdited(QString)"), onChange)
c(f.search, SIGNAL("textEdited(QString)"), onChange)
c(f.order, SIGNAL("activated(int)"), onChange)
c(f.limit, SIGNAL("valueChanged(int)"), onChange)
c(f.stepsOn, SIGNAL("stateChanged(int)"), onChange)
c(f.resched, SIGNAL("stateChanged(int)"), onChange)
def onExample(self, idx):
if idx == 0:
return
p = self.dynExamples[idx][1]
f = self.form
self.ignoreChange = True
search = [p['search']]
if self.search:
search.append(self.search)
f.search.setText(" ".join(search))
f.order.setCurrentIndex(p['order'])
f.resched.setChecked(p.get("resched", True))
if p.get("steps"):
f.steps.setText(p['steps'])
f.stepsOn.setChecked(True)
else:
f.steps.setText("1 10")
f.stepsOn.setChecked(False)
f.limit.setValue(1000)
self.ignoreChange = False
def loadConf(self):
f = self.form
d = self.deck

View file

@ -745,6 +745,7 @@ and check the statistics for a home deck instead."""))
self.connect(m.actionDonate, s, self.onDonate)
self.connect(m.actionFullSync, s, self.onFullSync)
self.connect(m.actionStudyDeck, s, self.onStudyDeck)
self.connect(m.actionCreateFiltered, s, self.onCram)
self.connect(m.actionEmptyCards, s, self.onEmptyCards)
def updateTitleBar(self):

View file

@ -55,8 +55,8 @@ class Overview(object):
self.mw.moveToState("deckBrowser")
elif url == "review":
openLink(aqt.appShared+"info/%s?v=%s"%(self.sid, self.sidVer))
elif url == "limits":
self.onLimits()
elif url == "studymore":
self.onStudyMore()
else:
openLink(url)
@ -65,17 +65,14 @@ class Overview(object):
key = unicode(evt.text())
if key == "o":
self.mw.onDeckConf()
if key == "f" and not cram:
deck = self.mw.col.decks.current()
self.mw.onCram("'deck:%s'" % deck['name'])
if key == "r" and cram:
self.mw.col.sched.rebuildDyn()
self.mw.reset()
if key == "e" and cram:
self.mw.col.sched.emptyDyn(self.mw.col.decks.selected())
self.mw.reset()
if key == "l":
self.onLimits()
if key == "c" and not cram:
self.onStudyMore()
# HTML
############################################################
@ -98,10 +95,14 @@ class Overview(object):
def _desc(self, deck):
if deck['dyn']:
search, limit, order = deck['terms'][0]
desc = "%s<br>%s" % (
_("Search: %s") % search,
_("Order: %s") % dynOrderLabels()[order].lower())
desc = _("""\
This is a special deck for studying outside of the normal schedule.""")
desc += " " + _("""\
Cards will be automatically returned to their original decks after you review \
them.""")
desc += " " + _("""\
Deleting this deck from the deck list will return all remaining cards \
to their original deck.""")
else:
desc = deck.get("desc", "")
if not desc:
@ -190,11 +191,8 @@ text-align: center;
links.append(["R", "refresh", _("Rebuild")])
links.append(["E", "empty", _("Empty")])
else:
if not sum(self.mw.col.sched.counts()):
if self.mw.col.sched.newDue() or \
self.mw.col.sched.revDue():
links.append(["L", "limits", _("Study More")])
links.append(["F", "cram", _("Filter/Cram")])
links.append(["C", "studymore", _("Custom Study")])
#links.append(["F", "cram", _("Filter/Cram")])
buf = ""
for b in links:
if b[0]:
@ -209,22 +207,9 @@ text-align: center;
self.bottom.web.setFixedHeight(size)
self.bottom.web.setLinkHandler(self._linkHandler)
# Today's limits
# Studying more
######################################################################
def onLimits(self):
d = QDialog(self.mw)
frm = aqt.forms.limits.Ui_Dialog()
frm.setupUi(d)
deck = self.mw.col.decks.current()
frm.newToday.setValue(deck.get('extendNew', 10))
frm.revToday.setValue(deck.get('extendRev', 50))
def accept():
n = deck['extendNew'] = frm.newToday.value()
r = deck['extendRev'] = frm.revToday.value()
self.mw.col.decks.save(deck)
self.mw.col.sched.extendLimits(n, r)
self.mw.reset()
d.connect(frm.buttonBox, SIGNAL("accepted()"), accept)
d.exec_()
def onStudyMore(self):
import aqt.customstudy
aqt.customstudy.CustomStudy(self.mw)

97
aqt/taglimit.py Normal file
View file

@ -0,0 +1,97 @@
# Copyright: Damien Elmes <anki@ichi2.net>
# License: GNU GPL, version 3 or later; http://www.gnu.org/copyleft/gpl.html
import aqt
from aqt.qt import *
from aqt.utils import saveGeom, restoreGeom
class TagLimit(QDialog):
def __init__(self, mw, parent):
QDialog.__init__(self, parent, Qt.Window)
self.mw = mw
self.parent = parent
self.deck = self.parent.deck
self.dialog = aqt.forms.taglimit.Ui_Dialog()
self.dialog.setupUi(self)
self.rebuildTagList()
restoreGeom(self, "tagLimit")
self.exec_()
def rebuildTagList(self):
usertags = self.mw.col.tags.all()
yes = self.deck.get("activeTags", [])
no = self.deck.get("inactiveTags", [])
yesHash = {}
noHash = {}
for y in yes:
yesHash[y] = True
for n in no:
noHash[n] = True
groupedTags = []
usertags.sort()
icon = QIcon(":/icons/Anki_Fact.png")
groupedTags.append([icon, usertags])
self.tags = []
for (icon, tags) in groupedTags:
for t in tags:
self.tags.append(t)
item = QListWidgetItem(icon, t.replace("_", " "))
self.dialog.activeList.addItem(item)
if t in yesHash:
mode = QItemSelectionModel.Select
self.dialog.activeCheck.setChecked(True)
else:
mode = QItemSelectionModel.Deselect
idx = self.dialog.activeList.indexFromItem(item)
self.dialog.activeList.selectionModel().select(idx, mode)
# inactive
item = QListWidgetItem(icon, t.replace("_", " "))
self.dialog.inactiveList.addItem(item)
if t in noHash:
mode = QItemSelectionModel.Select
else:
mode = QItemSelectionModel.Deselect
idx = self.dialog.inactiveList.indexFromItem(item)
self.dialog.inactiveList.selectionModel().select(idx, mode)
def reject(self):
self.tags = ""
QDialog.reject(self)
def accept(self):
self.hide()
n = 0
# gather yes/no tags
yes = []
no = []
for c in range(self.dialog.activeList.count()):
# active
if self.dialog.activeCheck.isChecked():
item = self.dialog.activeList.item(c)
idx = self.dialog.activeList.indexFromItem(item)
if self.dialog.activeList.selectionModel().isSelected(idx):
yes.append(self.tags[c])
# inactive
item = self.dialog.inactiveList.item(c)
idx = self.dialog.inactiveList.indexFromItem(item)
if self.dialog.inactiveList.selectionModel().isSelected(idx):
no.append(self.tags[c])
# save in the deck for future invocations
self.deck['activeTags'] = yes
self.deck['inactiveTags'] = no
self.mw.col.decks.save(self.deck)
# build query string
self.tags = ""
if yes:
arr = []
for req in yes:
arr.append("tag:'%s'" % req)
self.tags += "(" + " or ".join(arr) + ")"
if no:
arr = []
for req in no:
arr.append("-tag:'%s'" % req)
self.tags += " " + " ".join(arr)
saveGeom(self, "tagLimit")
QDialog.accept(self)

191
designer/customstudy.ui Normal file
View file

@ -0,0 +1,191 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>290</width>
<height>338</height>
</rect>
</property>
<property name="windowTitle">
<string>Custom Study</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="5" column="0">
<widget class="QRadioButton" name="radio6">
<property name="text">
<string>Preview new cards</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QRadioButton" name="radio5">
<property name="text">
<string>Study a random selection of cards</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QRadioButton" name="radio2">
<property name="text">
<string>Increase today's review card limit</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QRadioButton" name="radio1">
<property name="text">
<string>Increase today's new card limit</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QRadioButton" name="radio3">
<property name="text">
<string>Review forgotten cards</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QRadioButton" name="radio4">
<property name="text">
<string>Review ahead</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QRadioButton" name="radio7">
<property name="text">
<string>Limit to particular tags</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string/>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="title">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="preSpin">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spin"/>
</item>
<item>
<widget class="QLabel" name="postSpin">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>radio1</tabstop>
<tabstop>radio2</tabstop>
<tabstop>radio3</tabstop>
<tabstop>radio4</tabstop>
<tabstop>radio5</tabstop>
<tabstop>radio6</tabstop>
<tabstop>radio7</tabstop>
<tabstop>spin</tabstop>
<tabstop>buttonBox</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>Dialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>Dialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View file

@ -20,21 +20,21 @@
<string>Filter</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="2" column="0">
<item row="1" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Limit to</string>
</property>
</widget>
</item>
<item row="1" column="0">
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Search</string>
</property>
</widget>
</item>
<item row="2" column="1">
<item row="1" column="1">
<widget class="QSpinBox" name="limit">
<property name="maximumSize">
<size>
@ -50,29 +50,19 @@
</property>
</widget>
</item>
<item row="2" column="2">
<item row="1" column="2">
<widget class="QLabel" name="label">
<property name="text">
<string>cards selected by</string>
</property>
</widget>
</item>
<item row="1" column="1" colspan="6">
<item row="0" column="1" colspan="4">
<widget class="QLineEdit" name="search"/>
</item>
<item row="2" column="3" colspan="4">
<item row="1" column="3" colspan="2">
<widget class="QComboBox" name="order"/>
</item>
<item row="0" column="1" colspan="6">
<widget class="QComboBox" name="examples"/>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Preset</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>

View file

@ -1,151 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>428</width>
<height>183</height>
</rect>
</property>
<property name="windowTitle">
<string>Extend Limits</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="3">
<widget class="QSpinBox" name="revToday">
<property name="maximum">
<number>1000</number>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QSpinBox" name="newToday">
<property name="maximum">
<number>1000</number>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="label_4">
<property name="text">
<string>+</string>
</property>
</widget>
</item>
<item row="2" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Today's review limit</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="label_5">
<property name="text">
<string>+</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Today's new card limit</string>
</property>
</widget>
</item>
<item row="0" column="1">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>To review cards before they are due, please use filter/cram.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>newToday</tabstop>
<tabstop>revToday</tabstop>
<tabstop>buttonBox</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>Dialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>Dialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View file

@ -104,6 +104,7 @@
<addaction name="actionFullSync"/>
</widget>
<addaction name="actionStudyDeck"/>
<addaction name="actionCreateFiltered"/>
<addaction name="separator"/>
<addaction name="menuMaintenance"/>
<addaction name="separator"/>
@ -190,7 +191,7 @@
<string>Browse &amp;&amp; Install...</string>
</property>
<property name="statusTip">
<string></string>
<string/>
</property>
</action>
<action name="actionFullDatabaseCheck">
@ -248,6 +249,14 @@
<string>Empty Cards...</string>
</property>
</action>
<action name="actionCreateFiltered">
<property name="text">
<string>Create Filtered Deck...</string>
</property>
<property name="shortcut">
<string>F</string>
</property>
</action>
</widget>
<resources>
<include location="icons.qrc"/>

126
designer/taglimit.ui Normal file
View file

@ -0,0 +1,126 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>361</width>
<height>394</height>
</rect>
</property>
<property name="windowTitle">
<string>Selective Study</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QCheckBox" name="activeCheck">
<property name="text">
<string>Require one or more of these tags:</string>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="activeList">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>2</verstretch>
</sizepolicy>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::MultiSelection</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Select tags to exclude:</string>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="inactiveList">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>2</verstretch>
</sizepolicy>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::MultiSelection</enum>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>Dialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>358</x>
<y>264</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>Dialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>activeCheck</sender>
<signal>toggled(bool)</signal>
<receiver>activeList</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>133</x>
<y>18</y>
</hint>
<hint type="destinationlabel">
<x>133</x>
<y>85</y>
</hint>
</hints>
</connection>
</connections>
</ui>