diff --git a/aqt/deckopts.py b/aqt/deckopts.py new file mode 100644 index 000000000..4529890b6 --- /dev/null +++ b/aqt/deckopts.py @@ -0,0 +1,131 @@ +# Copyright: Damien Elmes +# 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 +import aqt + +class DeckOptions(QDialog): + + def __init__(self, mw): + QDialog.__init__(self, mw, Qt.Window) + self.mw = mw + self.d = mw.deck + self.form = aqt.forms.deckopts.Ui_Dialog() + self.form.setupUi(self) + self.setup() + self.exec_() + + def setup(self): + self.form.buttonBox.button(QDialogButtonBox.Help).setAutoDefault(False) + self.form.buttonBox.button(QDialogButtonBox.Close).setAutoDefault(False) + self.connect(self.form.modelsAdd, SIGNAL("clicked()"), self.onAdd) + self.connect(self.form.modelsEdit, SIGNAL("clicked()"), self.onEdit) + self.connect(self.form.modelsDelete, SIGNAL("clicked()"), self.onDelete) + self.connect(self.form.buttonBox, SIGNAL("helpRequested()"), + self.helpRequested) + # syncing + self.form.doSync.setChecked(self.d.syncingEnabled()) + self.form.mediaURL.setText(self.d.conf['mediaURL']) + # latex + self.form.latexHeader.setText(self.d.conf['latexPre']) + self.form.latexFooter.setText(self.d.conf['latexPost']) + # models + self.updateModelsList() + + def updateModelsList(self): + idx = self.form.modelsList.currentRow() + self.form.modelsList.clear() + self.models = [] + for model in self.d.models().values(): + self.models.append((model.name, model)) + self.models.sort() + for (name, model) in self.models: + item = QListWidgetItem(name) + self.form.modelsList.addItem(item) + cm = self.d.currentModel + try: + if aqt.mw.currentCard: + cm = aqt.mw.currentCard.fact.model + except: + # model has been deleted + pass + if model == cm: + self.form.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.form.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.form.modelsList.setCurrentRow(row) + aqt.mw.reset() + + def selectedModel(self): + row = self.form.modelsList.currentRow() + if row == -1: + return None + return self.models[self.form.modelsList.currentRow()][1] + + def helpRequested(self): + aqt.openHelp("DeckOptions") + + def reject(self): + needSync = False + # syncing + if self.form.doSync.isChecked(): + old = self.d.syncName + self.d.enableSyncing() + if self.d.syncName != old: + needSync = True + else: + self.d.disableSyncing() + url = unicode(self.form.mediaURL.text()) + if url: + if not re.match("^(http|https|ftp)://", url, re.I): + url = "http://" + url + if not url.endswith("/"): + url += "/" + self.d.conf['mediaURL'] = url + # latex + self.d.conf['latexPre'] = unicode(self.form.latexHeader.toPlainText()) + self.d.conf['latexPost'] = unicode(self.form.latexFooter.toPlainText()) + QDialog.reject(self) + if needSync: + aqt.mw.syncDeck(interactive=-1) diff --git a/aqt/deckproperties.py b/aqt/deckproperties.py deleted file mode 100644 index 4263a6df9..000000000 --- a/aqt/deckproperties.py +++ /dev/null @@ -1,240 +0,0 @@ -# Copyright: Damien Elmes -# 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 aqt.forms -import anki -from aqt 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 aqt - -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 = aqt.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 aqt.mw.currentCard: - cm = aqt.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) - aqt.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): - aqt.openHelp("DeckProperties") - - def reject(self): - n = _("Deck Properties") - self.mw.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: - aqt.mw.deck.updateCutoff() - aqt.mw.reset() - self.d.setUndoEnd(n) - self.d.finishProgress() - if self.onFinish: - self.onFinish() - QDialog.reject(self) - if needSync: - aqt.mw.syncDeck(interactive=-1) diff --git a/aqt/main.py b/aqt/main.py index 0607d6782..3e7b41302 100755 --- a/aqt/main.py +++ b/aqt/main.py @@ -662,7 +662,8 @@ Debug info:\n%s""") % traceback.format_exc(), help="DeckErrors") card=self.currentCard) def onDeckProperties(self): - self.deckProperties = aqt.deckproperties.DeckProperties(self, self.deck) + import aqt.deckopts + aqt.deckopts.DeckOptions(self) def onPrefs(self): import aqt.preferences diff --git a/aqt/preferences.py b/aqt/preferences.py index 3efc9e4d1..c07129b86 100644 --- a/aqt/preferences.py +++ b/aqt/preferences.py @@ -15,7 +15,6 @@ class Preferences(QDialog): QDialog.__init__(self, mw, Qt.Window) self.mw = mw self.config = mw.config - self.origInterfaceLang = self.config['interfaceLang'] self.form = aqt.forms.preferences.Ui_Preferences() self.form.setupUi(self) self.needDeckClose = False @@ -23,15 +22,15 @@ class Preferences(QDialog): lambda: aqt.openHelp("Preferences")) self.setupLang() self.setupNetwork() - self.setupSave() - self.setupAdvanced() + self.setupBackup() + self.setupOptions() self.setupMedia() self.show() def accept(self): self.updateNetwork() - self.updateSave() - self.updateAdvanced() + self.updateBackup() + self.updateOptions() self.updateMedia() self.config.save() self.mw.setupLang() @@ -111,7 +110,7 @@ class Preferences(QDialog): self.config['proxyUser'] = unicode(self.form.proxyUser.text()) self.config['proxyPass'] = unicode(self.form.proxyPass.text()) - def setupSave(self): + def setupBackup(self): self.form.numBackups.setValue(self.config['numBackups']) self.connect(self.form.openBackupFolder, SIGNAL("linkActivated(QString)"), @@ -137,10 +136,10 @@ class Preferences(QDialog): self.config['mediaLocation'] = p self.needDeckClose = True - def updateSave(self): + def updateBackup(self): self.config['numBackups'] = self.form.numBackups.value() - def setupAdvanced(self): + def setupOptions(self): self.form.showEstimates.setChecked(not self.config['suppressEstimates']) self.form.centerQA.setChecked(self.config['centerQA']) self.form.showProgress.setChecked(self.config['showProgress']) @@ -149,7 +148,7 @@ class Preferences(QDialog): self.form.stripHTML.setChecked(self.config['stripHTML']) self.form.autoplaySounds.setChecked(self.config['autoplaySounds']) - def updateAdvanced(self): + def updateOptions(self): self.config['suppressEstimates'] = not self.form.showEstimates.isChecked() self.config['centerQA'] = self.form.centerQA.isChecked() self.config['showProgress'] = self.form.showProgress.isChecked() diff --git a/designer/deckopts.ui b/designer/deckopts.ui new file mode 100644 index 000000000..b348e3dc2 --- /dev/null +++ b/designer/deckopts.ui @@ -0,0 +1,239 @@ + + + Dialog + + + Qt::ApplicationModal + + + + 0 + 0 + 353 + 363 + + + + Deck Options + + + + + + 0 + + + + Basic + + + + + + <b>Models</b> + + + true + + + + + + + + + + 0 + 0 + + + + + + + + + + &Add + + + false + + + + + + + &Edit + + + false + + + + + + + &Delete + + + false + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + Synchronize this deck with AnkiWeb + + + + + + + label + + + <b>Synchronization</b> + + + false + + + true + + + + + + + 4 + + + + + Media URL + + + + + + + + + + + + + LaTeX + + + + + + Header + + + + + + + true + + + + + + + Footer + + + + + + + true + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close|QDialogButtonBox::Help + + + + + + + qtabwidget + doSync + mediaURL + modelsList + modelsAdd + modelsEdit + modelsDelete + buttonBox + latexHeader + latexFooter + + + + + buttonBox + accepted() + Dialog + accept() + + + 275 + 442 + + + 157 + 274 + + + + + buttonBox + rejected() + Dialog + reject() + + + 343 + 442 + + + 286 + 274 + + + + + diff --git a/designer/deckproperties.ui b/designer/deckproperties.ui deleted file mode 100644 index 8096cc6e3..000000000 --- a/designer/deckproperties.ui +++ /dev/null @@ -1,777 +0,0 @@ - - - DeckProperties - - - Qt::ApplicationModal - - - - 0 - 0 - 404 - 540 - - - - Deck Properties - - - - - - 0 - - - - Basic - - - - - - <b>Models</b> - - - true - - - - - - - - - - 0 - 0 - - - - - - - - - - &Add - - - false - - - - - - - &Edit - - - false - - - - - - - &Delete - - - false - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - Synchronize this deck with AnkiWeb - - - - - - - label - - - <b>Synchronization</b> - - - false - - - true - - - - - - - 4 - - - - - Media URL - - - - - - - - - - - - - LaTeX - - - - - - Header - - - - - - - true - - - - - - - Footer - - - - - - - true - - - - - - - - Advanced - - - - 6 - - - - - - - - 0 - 0 - - - - - 50 - 16777215 - - - - - - - - - 0 - 0 - - - - - 50 - 16777215 - - - - - - - - Initial button 4 interval - - - - - - - Initial button 3 interval - - - - - - - ~ - - - Qt::AlignCenter - - - - - - - - 0 - 0 - - - - - 50 - 0 - - - - - 50 - 16777215 - - - - - - - - - 0 - 0 - - - - - 50 - 16777215 - - - - - - - - ~ - - - Qt::AlignCenter - - - - - - - - 0 - 0 - - - - - 50 - 16777215 - - - - - - - - - 0 - 0 - - - - - 50 - 0 - - - - - 50 - 16777215 - - - - - - - - ~ - - - Qt::AlignCenter - - - - - - - Initial button 2 interval - - - - - - - Button 1 delay - - - - - - - days - - - - - - - days - - - - - - - days - - - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 20 - 20 - - - - - - - - Button 1 multiplier - - - true - - - - - - - Mature bonus - - - true - - - - - - - Qt::Horizontal - - - - - - - - 0 - 0 - - - - - 50 - 0 - - - - - 50 - 16777215 - - - - - - - - minutes - - - - - - - - 0 - 0 - - - - - 50 - 16777215 - - - - - - - - - 0 - 0 - - - - - 50 - 16777215 - - - - - - - - days - - - - - - - Qt::Horizontal - - - - - - - Qt::Horizontal - - - - - - - Show failed cards early - - - true - - - - - - - - 0 - 0 - - - - - - - - - - - Per-day scheduling - - - - - - - - 0 - 0 - - - - - - - - - - - Leech threshold - - - - - - - - 0 - 0 - - - - - 50 - 16777215 - - - - - - - - Shift midnight by - - - - - - - - 0 - 0 - - - - - 50 - 16777215 - - - - - - - - Sibling delay (new cards) - - - - - - - - 0 - 0 - - - - - 50 - 16777215 - - - - - - - - minutes - - - - - - - Suspend leeches - - - - - - - - 0 - 0 - - - - - - - - - - - % - - - - - - - - 0 - 0 - - - - hours - - - - - - - failures - - - - - - - Sibling delay (reviews) - - - - - - - - - - % - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Close|QDialogButtonBox::Help - - - - - - - qtabwidget - doSync - mediaURL - modelsList - modelsAdd - modelsEdit - modelsDelete - buttonBox - latexHeader - latexFooter - delay0 - delay2 - delay1 - hardMin - hardMax - midMin - midMax - easyMin - easyMax - leechFails - timeOffset - newSpacing - revSpacing - collapse - suspendLeeches - perDay - - - - - buttonBox - accepted() - DeckProperties - accept() - - - 275 - 442 - - - 157 - 274 - - - - - buttonBox - rejected() - DeckProperties - reject() - - - 343 - 442 - - - 286 - 274 - - - - - diff --git a/designer/groupman.ui b/designer/groupman.ui index 928439f42..adde91c61 100644 --- a/designer/groupman.ui +++ b/designer/groupman.ui @@ -38,7 +38,7 @@ - Settings + Options @@ -96,7 +96,7 @@ - &Settings... + &Options... diff --git a/designer/main.ui b/designer/main.ui index df069839d..75fbb8b2b 100644 --- a/designer/main.ui +++ b/designer/main.ui @@ -366,7 +366,7 @@ :/icons/contents.png:/icons/contents.png - &Deck Properties... + &Deck Options... Customize models, syncing and scheduling