436f257e36
- keep track of size and position for a number of dialogs - make sure addons dialog opens at an appropriate size for the system font size - add optional argument to showText to restore geom on creation and save on reject (other buttons, if used, need to be programmed to save individually)
301 lines
10 KiB
Python
301 lines
10 KiB
Python
# Copyright: Damien Elmes <anki@ichi2.net>
|
|
# -*- coding: utf-8 -*-
|
|
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|
from operator import itemgetter
|
|
|
|
from anki.consts import NEW_CARDS_RANDOM
|
|
from aqt.qt import *
|
|
import aqt
|
|
from aqt.utils import showInfo, showWarning, openHelp, getOnlyText, askUser, \
|
|
tooltip, saveGeom, restoreGeom
|
|
|
|
class DeckConf(QDialog):
|
|
def __init__(self, mw, deck):
|
|
QDialog.__init__(self, mw)
|
|
self.mw = mw
|
|
self.deck = deck
|
|
self.childDids = [
|
|
d[1] for d in self.mw.col.decks.children(self.deck['id'])]
|
|
self._origNewOrder = None
|
|
self.form = aqt.forms.dconf.Ui_Dialog()
|
|
self.form.setupUi(self)
|
|
self.mw.checkpoint(_("Options"))
|
|
self.setupCombos()
|
|
self.setupConfs()
|
|
self.setWindowModality(Qt.WindowModal)
|
|
self.connect(self.form.buttonBox,
|
|
SIGNAL("helpRequested()"),
|
|
lambda: openHelp("deckoptions"))
|
|
self.connect(self.form.confOpts, SIGNAL("clicked()"), self.confOpts)
|
|
self.form.confOpts.setText(u"▾")
|
|
self.connect(self.form.buttonBox.button(QDialogButtonBox.RestoreDefaults),
|
|
SIGNAL("clicked()"),
|
|
self.onRestore)
|
|
self.setWindowTitle(_("Options for %s") % self.deck['name'])
|
|
# qt doesn't size properly with altered fonts otherwise
|
|
restoreGeom(self, "deckconf", adjustSize=True)
|
|
self.show()
|
|
self.exec_()
|
|
saveGeom(self, "deckconf")
|
|
|
|
def setupCombos(self):
|
|
import anki.consts as cs
|
|
f = self.form
|
|
f.newOrder.addItems(cs.newCardOrderLabels().values())
|
|
self.connect(f.newOrder, SIGNAL("currentIndexChanged(int)"),
|
|
self.onNewOrderChanged)
|
|
|
|
# Conf list
|
|
######################################################################
|
|
|
|
def setupConfs(self):
|
|
self.connect(self.form.dconf, SIGNAL("currentIndexChanged(int)"),
|
|
self.onConfChange)
|
|
self.conf = None
|
|
self.loadConfs()
|
|
|
|
def loadConfs(self):
|
|
current = self.deck['conf']
|
|
self.confList = self.mw.col.decks.allConf()
|
|
self.confList.sort(key=itemgetter('name'))
|
|
startOn = 0
|
|
self.ignoreConfChange = True
|
|
self.form.dconf.clear()
|
|
for idx, conf in enumerate(self.confList):
|
|
self.form.dconf.addItem(conf['name'])
|
|
if str(conf['id']) == str(current):
|
|
startOn = idx
|
|
self.ignoreConfChange = False
|
|
self.form.dconf.setCurrentIndex(startOn)
|
|
if self._origNewOrder is None:
|
|
self._origNewOrder = self.confList[startOn]['new']['order']
|
|
self.onConfChange(startOn)
|
|
|
|
def confOpts(self):
|
|
m = QMenu(self.mw)
|
|
a = m.addAction(_("Add"))
|
|
a.connect(a, SIGNAL("triggered()"), self.addGroup)
|
|
a = m.addAction(_("Delete"))
|
|
a.connect(a, SIGNAL("triggered()"), self.remGroup)
|
|
a = m.addAction(_("Rename"))
|
|
a.connect(a, SIGNAL("triggered()"), self.renameGroup)
|
|
a = m.addAction(_("Set for all subdecks"))
|
|
a.connect(a, SIGNAL("triggered()"), self.setChildren)
|
|
if not self.childDids:
|
|
a.setEnabled(False)
|
|
m.exec_(QCursor.pos())
|
|
|
|
def onConfChange(self, idx):
|
|
if self.ignoreConfChange:
|
|
return
|
|
if self.conf:
|
|
self.saveConf()
|
|
conf = self.confList[idx]
|
|
self.deck['conf'] = conf['id']
|
|
self.loadConf()
|
|
cnt = 0
|
|
for d in self.mw.col.decks.all():
|
|
if d['dyn']:
|
|
continue
|
|
if d['conf'] == conf['id']:
|
|
cnt += 1
|
|
if cnt > 1:
|
|
txt = _("Your changes will affect multiple decks. If you wish to "
|
|
"change only the current deck, please add a new options group first.")
|
|
else:
|
|
txt = ""
|
|
self.form.count.setText(txt)
|
|
|
|
def addGroup(self):
|
|
name = getOnlyText(_("New options group name:"))
|
|
if not name:
|
|
return
|
|
# first, save currently entered data to current conf
|
|
self.saveConf()
|
|
# then clone the conf
|
|
id = self.mw.col.decks.confId(name, cloneFrom=self.conf)
|
|
# set the deck to the new conf
|
|
self.deck['conf'] = id
|
|
# then reload the conf list
|
|
self.loadConfs()
|
|
|
|
def remGroup(self):
|
|
if self.conf['id'] == 1:
|
|
showInfo(_("The default configuration can't be removed."), self)
|
|
else:
|
|
self.mw.col.decks.remConf(self.conf['id'])
|
|
self.deck['conf'] = 1
|
|
self.loadConfs()
|
|
|
|
def renameGroup(self):
|
|
old = self.conf['name']
|
|
name = getOnlyText(_("New name:"), default=old)
|
|
if not name or name == old:
|
|
return
|
|
self.conf['name'] = name
|
|
self.loadConfs()
|
|
|
|
def setChildren(self):
|
|
if not askUser(
|
|
_("Set all decks below %s to this option group?") %
|
|
self.deck['name']):
|
|
return
|
|
for did in self.childDids:
|
|
deck = self.mw.col.decks.get(did)
|
|
if deck['dyn']:
|
|
continue
|
|
deck['conf'] = self.deck['conf']
|
|
self.mw.col.decks.save(deck)
|
|
tooltip(ngettext("%d deck updated.", "%d decks updated.", \
|
|
len(self.childDids)) % len(self.childDids))
|
|
|
|
# Loading
|
|
##################################################
|
|
|
|
def listToUser(self, l):
|
|
return " ".join([str(x) for x in l])
|
|
|
|
def parentLimText(self, type="new"):
|
|
# top level?
|
|
if "::" not in self.deck['name']:
|
|
return ""
|
|
lim = -1
|
|
for d in self.mw.col.decks.parents(self.deck['id']):
|
|
c = self.mw.col.decks.confForDid(d['id'])
|
|
x = c[type]['perDay']
|
|
if lim == -1:
|
|
lim = x
|
|
else:
|
|
lim = min(x, lim)
|
|
return _("(parent limit: %d)") % lim
|
|
|
|
def loadConf(self):
|
|
self.conf = self.mw.col.decks.confForDid(self.deck['id'])
|
|
# new
|
|
c = self.conf['new']
|
|
f = self.form
|
|
f.lrnSteps.setText(self.listToUser(c['delays']))
|
|
f.lrnGradInt.setValue(c['ints'][0])
|
|
f.lrnEasyInt.setValue(c['ints'][1])
|
|
f.lrnEasyInt.setValue(c['ints'][1])
|
|
f.lrnFactor.setValue(c['initialFactor']/10.0)
|
|
f.newOrder.setCurrentIndex(c['order'])
|
|
f.newPerDay.setValue(c['perDay'])
|
|
f.bury.setChecked(c.get("bury", True))
|
|
f.newplim.setText(self.parentLimText('new'))
|
|
# rev
|
|
c = self.conf['rev']
|
|
f.revPerDay.setValue(c['perDay'])
|
|
f.easyBonus.setValue(c['ease4']*100)
|
|
f.fi1.setValue(c['ivlFct']*100)
|
|
f.maxIvl.setValue(c['maxIvl'])
|
|
f.revplim.setText(self.parentLimText('rev'))
|
|
f.buryRev.setChecked(c.get("bury", True))
|
|
# lapse
|
|
c = self.conf['lapse']
|
|
f.lapSteps.setText(self.listToUser(c['delays']))
|
|
f.lapMult.setValue(c['mult']*100)
|
|
f.lapMinInt.setValue(c['minInt'])
|
|
f.leechThreshold.setValue(c['leechFails'])
|
|
f.leechAction.setCurrentIndex(c['leechAction'])
|
|
# general
|
|
c = self.conf
|
|
f.maxTaken.setValue(c['maxTaken'])
|
|
f.showTimer.setChecked(c.get('timer', 0))
|
|
f.autoplaySounds.setChecked(c['autoplay'])
|
|
f.replayQuestion.setChecked(c.get('replayq', True))
|
|
# description
|
|
f.desc.setPlainText(self.deck['desc'])
|
|
|
|
def onRestore(self):
|
|
self.mw.progress.start()
|
|
self.mw.col.decks.restoreToDefault(self.conf)
|
|
self.mw.progress.finish()
|
|
self.loadConf()
|
|
|
|
# New order
|
|
##################################################
|
|
|
|
def onNewOrderChanged(self, new):
|
|
old = self.conf['new']['order']
|
|
if old == new:
|
|
return
|
|
self.conf['new']['order'] = new
|
|
self.mw.progress.start()
|
|
self.mw.col.sched.resortConf(self.conf)
|
|
self.mw.progress.finish()
|
|
|
|
# Saving
|
|
##################################################
|
|
|
|
def updateList(self, conf, key, w, minSize=1):
|
|
items = unicode(w.text()).split(" ")
|
|
ret = []
|
|
for i in items:
|
|
if not i:
|
|
continue
|
|
try:
|
|
i = float(i)
|
|
assert i > 0
|
|
if i == int(i):
|
|
i = int(i)
|
|
ret.append(i)
|
|
except:
|
|
# invalid, don't update
|
|
showWarning(_("Steps must be numbers."))
|
|
return
|
|
if len(ret) < minSize:
|
|
showWarning(_("At least one step is required."))
|
|
return
|
|
conf[key] = ret
|
|
|
|
def saveConf(self):
|
|
# new
|
|
c = self.conf['new']
|
|
f = self.form
|
|
self.updateList(c, 'delays', f.lrnSteps)
|
|
c['ints'][0] = f.lrnGradInt.value()
|
|
c['ints'][1] = f.lrnEasyInt.value()
|
|
c['initialFactor'] = f.lrnFactor.value()*10
|
|
c['order'] = f.newOrder.currentIndex()
|
|
c['perDay'] = f.newPerDay.value()
|
|
c['bury'] = f.bury.isChecked()
|
|
if self._origNewOrder != c['order']:
|
|
# order of current deck has changed, so have to resort
|
|
if c['order'] == NEW_CARDS_RANDOM:
|
|
self.mw.col.sched.randomizeCards(self.deck['id'])
|
|
else:
|
|
self.mw.col.sched.orderCards(self.deck['id'])
|
|
# rev
|
|
c = self.conf['rev']
|
|
c['perDay'] = f.revPerDay.value()
|
|
c['ease4'] = f.easyBonus.value()/100.0
|
|
c['ivlFct'] = f.fi1.value()/100.0
|
|
c['maxIvl'] = f.maxIvl.value()
|
|
c['bury'] = f.buryRev.isChecked()
|
|
# lapse
|
|
c = self.conf['lapse']
|
|
self.updateList(c, 'delays', f.lapSteps, minSize=0)
|
|
c['mult'] = f.lapMult.value()/100.0
|
|
c['minInt'] = f.lapMinInt.value()
|
|
c['leechFails'] = f.leechThreshold.value()
|
|
c['leechAction'] = f.leechAction.currentIndex()
|
|
# general
|
|
c = self.conf
|
|
c['maxTaken'] = f.maxTaken.value()
|
|
c['timer'] = f.showTimer.isChecked() and 1 or 0
|
|
c['autoplay'] = f.autoplaySounds.isChecked()
|
|
c['replayq'] = f.replayQuestion.isChecked()
|
|
# description
|
|
self.deck['desc'] = f.desc.toPlainText()
|
|
self.mw.col.decks.save(self.deck)
|
|
self.mw.col.decks.save(self.conf)
|
|
|
|
def reject(self):
|
|
self.accept()
|
|
|
|
def accept(self):
|
|
self.saveConf()
|
|
self.mw.reset()
|
|
QDialog.accept(self)
|