Anki/ankiqt/ui/deckproperties.py
2011-04-28 09:26:15 +09:00

241 lines
9.4 KiB
Python

# Copyright: Damien Elmes <anki@ichi2.net>
# License: GNU GPL, version 3 or later; http://www.gnu.org/copyleft/gpl.html
from PyQt4.QtGui import *
from PyQt4.QtCore import *
import sys, re, time
import ankiqt.forms
import anki
from ankiqt import ui
from anki.utils import parseTags
from anki.deck import newCardOrderLabels, newCardSchedulingLabels
from anki.deck import revCardOrderLabels
from anki.utils import hexifyID, dehexifyID
import ankiqt
class DeckProperties(QDialog):
def __init__(self, parent, deck, onFinish=None):
QDialog.__init__(self, parent, Qt.Window)
self.parent = parent
self.d = deck
self.onFinish = onFinish
self.origMod = self.d.modified
self.dialog = ankiqt.forms.deckproperties.Ui_DeckProperties()
self.dialog.setupUi(self)
self.dialog.buttonBox.button(QDialogButtonBox.Help).setAutoDefault(False)
self.dialog.buttonBox.button(QDialogButtonBox.Close).setAutoDefault(False)
self.readData()
self.connect(self.dialog.modelsAdd, SIGNAL("clicked()"), self.onAdd)
self.connect(self.dialog.modelsEdit, SIGNAL("clicked()"), self.onEdit)
self.connect(self.dialog.modelsDelete, SIGNAL("clicked()"), self.onDelete)
self.connect(self.dialog.buttonBox, SIGNAL("helpRequested()"), self.helpRequested)
self.show()
def readData(self):
# syncing
if self.d.syncName:
self.dialog.doSync.setCheckState(Qt.Checked)
else:
self.dialog.doSync.setCheckState(Qt.Unchecked)
self.dialog.mediaURL.setText(self.d.getVar("mediaURL") or "")
# latex
self.dialog.latexHeader.setText(self.d.getVar("latexPre"))
self.dialog.latexFooter.setText(self.d.getVar("latexPost"))
# scheduling
for type in ("hard", "mid", "easy"):
v = getattr(self.d, type + "IntervalMin")
getattr(self.dialog, type + "Min").setText(str(v))
v = getattr(self.d, type + "IntervalMax")
getattr(self.dialog, type + "Max").setText(str(v))
self.dialog.delay0.setText(unicode(self.d.delay0/60.0))
self.dialog.delay1.setText(unicode(self.d.delay1))
self.dialog.delay2.setText(unicode(int(self.d.delay2*100)))
self.dialog.collapse.setCheckState(self.d.collapseTime
and Qt.Checked or Qt.Unchecked)
self.dialog.perDay.setCheckState(self.d.getBool("perDay")
and Qt.Checked or Qt.Unchecked)
# models
self.updateModelsList()
# hour shift
self.dialog.timeOffset.setText(str(
(self.d.utcOffset - time.timezone) / 60.0 / 60.0))
# leeches
self.dialog.suspendLeeches.setChecked(self.d.getBool("suspendLeeches"))
self.dialog.leechFails.setValue(self.d.getInt("leechFails"))
# spacing
self.dialog.newSpacing.setText(unicode(self.d.getFloat("newSpacing")/60.0))
self.dialog.revSpacing.setText(unicode(self.d.getFloat("revSpacing")*100))
def updateModelsList(self):
idx = self.dialog.modelsList.currentRow()
self.dialog.modelsList.clear()
self.models = []
for model in self.d.models:
name = ngettext("%(name)s [%(facts)d fact]",
"%(name)s [%(facts)d facts]", self.d.modelUseCount(model)) % {
'name': model.name,
'facts': self.d.modelUseCount(model),
}
self.models.append((name, model))
self.models.sort()
for (name, model) in self.models:
item = QListWidgetItem(name)
self.dialog.modelsList.addItem(item)
cm = self.d.currentModel
try:
if ankiqt.mw.currentCard:
cm = ankiqt.mw.currentCard.fact.model
except:
# model has been deleted
pass
if model == cm:
self.dialog.modelsList.setCurrentItem(item)
def onAdd(self):
m = ui.modelchooser.AddModel(self, self.parent, self.d).getModel()
if m:
self.d.addModel(m)
self.updateModelsList()
def onEdit(self):
model = self.selectedModel()
if not model:
return
# set to current
self.d.currentModel = model
ui.modelproperties.ModelProperties(self, self.d, model, self.parent,
onFinish=self.updateModelsList)
def onDelete(self):
model = self.selectedModel()
row = self.dialog.modelsList.currentRow()
if not model:
return
if len(self.d.models) < 2:
ui.utils.showWarning(_("Please add another model first."),
parent=self)
return
if self.d.s.scalar("select 1 from sources where id=:id",
id=model.source):
ui.utils.showWarning(_("This model is used by deck source:\n"
"%s\nYou will need to remove the source "
"first.") % hexifyID(model.source))
return
count = self.d.modelUseCount(model)
if count:
if not ui.utils.askUser(
_("This model is used by %d facts.\n"
"Are you sure you want to delete it?\n"
"If you delete it, these cards will be lost.")
% count, parent=self):
return
self.d.deleteModel(model)
self.updateModelsList()
self.dialog.modelsList.setCurrentRow(row)
ankiqt.mw.reset()
def selectedModel(self):
row = self.dialog.modelsList.currentRow()
if row == -1:
return None
return self.models[self.dialog.modelsList.currentRow()][1]
def updateField(self, obj, field, value):
if getattr(obj, field) != value:
setattr(obj, field, value)
self.d.setModified()
def helpRequested(self):
QDesktopServices.openUrl(QUrl(ankiqt.appWiki +
"DeckProperties"))
def reject(self):
n = _("Deck Properties")
self.d.startProgress()
self.d.setUndoStart(n)
needSync = False
# syncing
if self.dialog.doSync.checkState() == Qt.Checked:
old = self.d.syncName
oldSync = self.d.lastSync
self.d.enableSyncing()
if self.d.syncName != old:
needSync = True
else:
# put it back
self.d.lastSync = oldSync
else:
self.d.disableSyncing()
url = unicode(self.dialog.mediaURL.text())
if url:
if not re.match("^(http|https|ftp)://", url, re.I):
url = "http://" + url
if not url.endswith("/"):
url += "/"
old = self.d.getVar("mediaURL") or ""
if old != url:
self.d.setVar("mediaURL", url)
# latex
self.d.setVar('latexPre', unicode(self.dialog.latexHeader.toPlainText()))
self.d.setVar('latexPost', unicode(self.dialog.latexFooter.toPlainText()))
# scheduling
minmax = ("Min", "Max")
for type in ("hard", "mid", "easy"):
v = getattr(self.dialog, type + "Min").text()
try:
v = float(v)
except ValueError:
continue
self.updateField(self.d, type + "IntervalMin", v)
v = getattr(self.dialog, type + "Max").text()
try:
v = float(v)
except ValueError:
continue
self.updateField(self.d, type + "IntervalMax", v)
try:
v = float(self.dialog.delay0.text()) * 60.0
self.updateField(self.d, 'delay0', v)
v2 = int(self.dialog.delay1.text())
v2 = max(0, v2)
self.updateField(self.d, 'delay1', v2)
v = float(self.dialog.delay2.text()) / 100.0
self.updateField(self.d, 'delay2', max(0, min(100, v)))
except ValueError:
pass
try:
self.d.setVar("suspendLeeches",
not not self.dialog.suspendLeeches.isChecked())
self.d.setVar("leechFails",
int(self.dialog.leechFails.value()))
except ValueError:
pass
try:
self.d.setVar("newSpacing", float(self.dialog.newSpacing.text()) * 60)
self.d.setVar("revSpacing", float(self.dialog.revSpacing.text()) / 100.0)
except ValueError:
pass
# hour shift
try:
offset = float(str(self.dialog.timeOffset.text()))
offset = max(min(offset, 24), -24)
self.updateField(self.d, 'utcOffset', offset*60*60+time.timezone)
except:
pass
was = self.d.modified
self.updateField(self.d, 'collapseTime',
self.dialog.collapse.isChecked() and 1 or 0)
if self.dialog.perDay.isChecked() != self.d.getBool("perDay"):
self.d.setVar('perDay', self.dialog.perDay.isChecked())
# mark deck dirty and close
if self.origMod != self.d.modified:
ankiqt.mw.deck.updateCutoff()
ankiqt.mw.reset()
self.d.setUndoEnd(n)
self.d.finishProgress()
if self.onFinish:
self.onFinish()
QDialog.reject(self)
if needSync:
ankiqt.mw.syncDeck(interactive=-1)