diff --git a/aqt/addcards.py b/aqt/addcards.py index 4928ea34c..d12f271af 100644 --- a/aqt/addcards.py +++ b/aqt/addcards.py @@ -9,16 +9,16 @@ import anki from anki.facts import Fact from anki.errors import * from anki.utils import stripHTML, parseTags -from aqt.utils import saveGeom, restoreGeom +from aqt.utils import saveGeom, restoreGeom, showWarning, askUser from anki.sound import clearAudioQueue from anki.hooks import addHook, removeHook import aqt.editor, aqt.modelchooser -class FocusButton(QPushButton): - def focusInEvent(self, evt): - if evt.reason() == Qt.TabFocusReason: - self.emit(SIGNAL("tabIn")) - QPushButton.focusInEvent(self, evt) +# todo: + # if field.fieldModel.features: + # w.setLayoutDirection(Qt.RightToLeft) + # else: + # w.setLayoutDirection(Qt.LeftToRight) class AddCards(QDialog): @@ -26,39 +26,37 @@ class AddCards(QDialog): windParent = None QDialog.__init__(self, windParent, Qt.Window) self.mw = mw - self.dialog = aqt.forms.addcards.Ui_Dialog() - self.dialog.setupUi(self) + self.form = aqt.forms.addcards.Ui_Dialog() + self.form.setupUi(self) self.setWindowTitle(_("Add")) self.setupEditor() - self.addChooser() - self.addButtons() - self.modelChanged() + self.setupChooser() + self.setupButtons() + self.onReset() self.addedItems = 0 self.forceClose = False restoreGeom(self, "add") + addHook('reset', self.onReset) + self.setupNewFact() self.show() - addHook('guiReset', self.modelChanged) def setupEditor(self): - self.editor = aqt.editor.Editor(self.mw, self.dialog.fieldsArea) + self.editor = aqt.editor.Editor(self.mw, self.form.fieldsArea) # get a fact for testing - fact = self.mw.deck.getFact(3951) - self.editor.setFact(fact) + #fact = self.mw.deck.getFact(3951) + #self.editor.setFact(fact) - def addChooser(self): - return - self.modelChooser = aqt.modelchooser.ModelChooser(self, - self.mw, - self.mw.deck, - self.modelChanged) - self.dialog.modelArea.setLayout(self.modelChooser) + def setupChooser(self): + self.modelChooser = aqt.modelchooser.ModelChooser( + self.mw, self.form.modelArea) + # modelChanged func def helpRequested(self): aqt.openHelp("AddItems") - def addButtons(self): + def setupButtons(self): self.addButton = QPushButton(_("Add")) - self.dialog.buttonBox.addButton(self.addButton, + self.form.buttonBox.addButton(self.addButton, QDialogButtonBox.ActionRole) self.addButton.setShortcut(_("Ctrl+Return")) if sys.platform.startswith("darwin"): @@ -70,11 +68,11 @@ class AddCards(QDialog): self.connect(self.addButton, SIGNAL("clicked()"), self.addCards) self.closeButton = QPushButton(_("Close")) self.closeButton.setAutoDefault(False) - self.dialog.buttonBox.addButton(self.closeButton, + self.form.buttonBox.addButton(self.closeButton, QDialogButtonBox.RejectRole) self.helpButton = QPushButton(_("Help")) self.helpButton.setAutoDefault(False) - self.dialog.buttonBox.addButton(self.helpButton, + self.form.buttonBox.addButton(self.helpButton, QDialogButtonBox.HelpRole) self.connect(self.helpButton, SIGNAL("clicked()"), self.helpRequested) @@ -84,31 +82,41 @@ class AddCards(QDialog): browser.updateSearch() browser.onFact() - def modelChanged(self, model=None): - return + # FIXME: need to make sure to clean up fact on exit + def setupNewFact(self, set=True): + f = self.mw.deck.newFact() + if self.editor.fact: + # keep tags? + f.tags = self.editor.fact.tags + if set: + self.editor.setFact(f) + return f + + def onReset(self, model=None): oldFact = self.editor.fact - # create a new fact - fact = self.mw.deck.newFact() + fact = self.setupNewFact(set=False) # copy fields from old fact if oldFact: - n = 0 - for field in fact.fields: + self.removeTempFact(oldFact) + for n in range(len(fact._fields)): try: - field.value = oldFact.fields[n].value + fact._fields[n] = oldFact._fields[n] except IndexError: break - n += 1 - fact.tags = oldFact.tags - else: - fact.tags = "last" #self.mw.deck.lastTags - # set the new fact - self.editor.setFact(fact, check=True, forceRedraw=True) - self.setTabOrder(self.editor.tags, self.addButton) - self.setTabOrder(self.addButton, self.closeButton) - self.setTabOrder(self.closeButton, self.helpButton) + self.editor.setFact(fact) + #self.setTabOrder(self.editor.tags, self.addButton) + #self.setTabOrder(self.addButton, self.closeButton) + #self.setTabOrder(self.closeButton, self.helpButton) + + def removeTempFact(self, fact): + if not fact: + return + # we don't have to worry about cards; just the fact + self.mw.deck._delFacts([fact.id]) def reportAddedFact(self, fact): - self.dialog.status.append( + return + self.form.status.append( _("Added %(num)d card(s) for " "%(str)s.") % { "num": len(fact.cards), @@ -118,57 +126,31 @@ class AddCards(QDialog): }) def addFact(self, fact): - try: - fact = self.mw.deck.addFact(fact, False) - except FactInvalidError: - ui.utils.showInfo(_( + if any(fact.problems()): + showWarning(_( "Some fields are missing or not unique."), - parent=self, help="AddItems#AddError") + help="AddItems#AddError") return - if not fact: - ui.utils.showWarning(_("""\ + cards = self.mw.deck.addFact(fact) + if not cards: + showWarning(_("""\ The input you have provided would make an empty -question or answer on all cards."""), parent=self) +question or answer on all cards."""), help="AddItems") return - self.reportAddedFact(fact) - # we don't reset() until the add cards dialog is closed + # FIXME: return to overview on add? return fact - def initializeNewFact(self, old_fact): - f = self.mw.deck.newFact() - f.tags = self.mw.deck.lastTags - return f - - def clearOldFact(self, old_fact): - f = self.initializeNewFact(old_fact) - self.editor.setFact(f, check=True, scroll=True) - # let completer know our extra tags - self.editor.tags.addTags(parseTags(self.mw.deck.lastTags)) - return f - def addCards(self): - # make sure updated - self.editor.saveFieldsNow() + self.editor.saveNow() fact = self.editor.fact - n = _("Add") - self.mw.deck.setUndoStart(n) - fact = self.addFact(fact) if not fact: return - # stop anything playing clearAudioQueue() - - self.mw.deck.setUndoEnd(n) - self.mw.deck.rebuildCounts() - self.mw.updateTitleBar() - - # start a new fact - self.clearOldFact(fact) - - self.mw.autosave() + self.setupNewFact() + self.mw.deck.autosave() def keyPressEvent(self, evt): "Show answer on RET or register answer." @@ -178,37 +160,20 @@ question or answer on all cards."""), parent=self) return return QDialog.keyPressEvent(self, evt) - def closeEvent(self, evt): - if self.onClose(): - evt.accept() - else: - evt.ignore() - def reject(self): - # remove dupe + if not self.canClose(): + return + clearAudioQueue() + self.removeTempFact(self.editor.fact) + self.editor.setFact(None) + self.modelChooser.cleanup() + removeHook('reset', self.onReset) + saveGeom(self, "add") aqt.dialogs.close("AddCards") QDialog.reject(self) - - if self.onClose(): - self.modelChooser.deinit() - QDialog.reject(self) - - def onClose(self): - return - removeHook('guiReset', self.modelChanged) - # stop anything playing - clearAudioQueue() + def canClose(self): if (self.forceClose or self.editor.fieldsAreBlank() or - ui.utils.askUser(_("Close and lose current input?"), - self)): - self.modelChooser.deinit() - self.editor.close() - ui.dialogs.close("AddCards") - self.mw.deck.db.flush() - self.mw.deck.rebuildCSS() - self.mw.reset() - saveGeom(self, "add") + askUser(_("Close and lose current input?"))): return True - else: - return False + return False diff --git a/aqt/clayout.py b/aqt/clayout.py index 69fc18630..246c04192 100644 --- a/aqt/clayout.py +++ b/aqt/clayout.py @@ -13,6 +13,8 @@ from aqt.utils import saveGeom, restoreGeom, getBase, mungeQA, \ getText import aqt.templates +# fixme: replace font substitutions with native comma list + class ResizingTextEdit(QTextEdit): def sizeHint(self): return QSize(200, 800) diff --git a/aqt/editor.py b/aqt/editor.py index b9ac9a760..8e5125aa7 100644 --- a/aqt/editor.py +++ b/aqt/editor.py @@ -15,13 +15,6 @@ from aqt.utils import shortcut, showInfo, showWarning, getBase, getFile import aqt import anki.js -# todo: - # if field.fieldModel.features: - # w.setLayoutDirection(Qt.RightToLeft) - # else: - # w.setLayoutDirection(Qt.LeftToRight) - - pics = ("jpg", "jpeg", "png", "tif", "tiff", "gif") audio = ("wav", "mp3", "ogg", "flac") @@ -175,6 +168,7 @@ $(function () { """ +# caller is responsible for resetting fact on reset class Editor(object): def __init__(self, mw, widget): self.widget = widget @@ -184,20 +178,12 @@ class Editor(object): self._keepButtons = False # current card, for card layout self.card = None - addHook("deckClosed", self.deckClosedHook) - addHook("guiReset", self.refresh) - addHook("colourChanged", self.colourChanged) self.setupOuter() self.setupButtons() self.setupWeb() self.setupTagsAndGroup() self.setupKeyboard() - def close(self): - removeHook("deckClosed", self.deckClosedHook) - removeHook("guiReset", self.refresh) - removeHook("colourChanged", self.colourChanged) - # Initial setup ############################################################ @@ -380,15 +366,6 @@ class Editor(object): return [(f['font'], f['esize']) for f in self.fact.model().fields] - def refresh(self): - if self.fact: - self.fact.load() - # fixme: what if fact is deleted? - self.setFact(self.fact) - - def deckClosedHook(self): - self.setFact(None) - def saveNow(self): "Must call this before adding cards, closing dialog, etc." if not self.fact: @@ -409,6 +386,14 @@ class Editor(object): cols.append("#ffc") self.web.eval("setBackgrounds(%s);" % simplejson.dumps(cols)) + def fieldsAreBlank(self): + if not self.fact: + return True + for f in self.fact._fields: + if f: + return False + return True + # HTML editing ###################################################################### @@ -592,7 +577,7 @@ class Editor(object): recent.append("#000000") self.colourDiag.close() self.onForeground() - runHook("colourChanged") + self.colourChanged() def onNextColour(self): self.colourDiag.focusWidget().nextInFocusChain().setFocus() @@ -610,7 +595,7 @@ class Editor(object): recent.append(colour) self.web.eval("setFormat('forecolor', '%s')" % colour) self.colourDiag.close() - runHook("colourChanged") + self.colourChanged() def onNewColour(self): new = QColorDialog.getColor(Qt.white, self.widget) @@ -620,7 +605,7 @@ class Editor(object): txtcol = unicode(new.name()) if txtcol not in recent: recent.append(txtcol) - runHook("colourChanged") + self.colourChanged() self.onChooseColour(txtcol) # Audio/video/images diff --git a/aqt/main.py b/aqt/main.py index 658c9bb02..d020192c7 100755 --- a/aqt/main.py +++ b/aqt/main.py @@ -113,7 +113,12 @@ class AnkiQt(QMainWindow): def _editCurrentState(self, oldState): pass - def reset(self): + def factChanged(self, fid): + "Called when a card or fact is edited (but not deleted)." + runHook("factChanged", fid) + + def reset(self, type="all", *args): + "Called for non-trivial edits. Rebuilds queue." self.deck.reset() runHook("reset") diff --git a/aqt/modelchooser.py b/aqt/modelchooser.py index 0aafacad7..38e34890e 100644 --- a/aqt/modelchooser.py +++ b/aqt/modelchooser.py @@ -4,38 +4,42 @@ from PyQt4.QtGui import * from PyQt4.QtCore import * from operator import attrgetter -import anki, sys from anki import stdmodels -from anki.models import * -import aqt.forms +from anki.lang import ngettext from anki.hooks import addHook, removeHook +from aqt.utils import isMac class ModelChooser(QHBoxLayout): - def __init__(self, parent, main, deck, onChangeFunc=None, cards=True, label=True): + def __init__(self, mw, widget, cards=True, label=True): QHBoxLayout.__init__(self) - self.parent = parent - self.main = main - self.deck = deck - self.onChangeFunc = onChangeFunc + self.widget = widget + self.mw = mw + self.deck = mw.deck + self.handleCards = cards + self.label = label + self._ignoreReset = False self.setMargin(0) self.setSpacing(4) - self.shortcuts = [] - if label: + self.setupModels() + self.setupTemplates() + addHook('reset', self.onReset) + self.widget.setLayout(self) + + def setupModels(self): + if self.label: self.modelLabel = QLabel(_("Model:")) self.addWidget(self.modelLabel) + # models dropdown self.models = QComboBox() - s = QShortcut(QKeySequence(_("Shift+Alt+m")), self.parent) + s = QShortcut(QKeySequence(_("Shift+Alt+m")), self.widget) s.connect(s, SIGNAL("activated()"), lambda: self.models.showPopup()) - self.drawModels() - sizePolicy = QSizePolicy( - QSizePolicy.Policy(7), - QSizePolicy.Policy(0)) - self.models.setSizePolicy(sizePolicy) self.addWidget(self.models) + self.connect(self.models, SIGNAL("activated(int)"), self.onModelChange) + # edit button self.edit = QPushButton() - if not sys.platform.startswith("darwin"): + if not isMac: self.edit.setFixedWidth(32) self.edit.setIcon(QIcon(":/icons/configure.png")) self.edit.setShortcut(_("Shift+Alt+e")) @@ -43,120 +47,105 @@ class ModelChooser(QHBoxLayout): self.edit.setAutoDefault(False) self.addWidget(self.edit) self.connect(self.edit, SIGNAL("clicked()"), self.onEdit) - self.connect(self.models, SIGNAL("activated(int)"), self.onChange) - self.handleCards = False - if cards: - self.handleCards = True - label = QLabel(_("Cards:")) - self.addWidget(label) + # layout + sizePolicy = QSizePolicy( + QSizePolicy.Policy(7), + QSizePolicy.Policy(0)) + self.models.setSizePolicy(sizePolicy) + self.updateModels() + + def setupTemplates(self): + self.cardShortcuts = [] + if self.handleCards: self.cards = QPushButton() self.cards.setAutoDefault(False) self.connect(self.cards, SIGNAL("clicked()"), self.onCardChange) self.addWidget(self.cards) - self.drawCardModels() - addHook('guiReset', self.onModelEdited) + self.updateTemplates() - def deinit(self): - removeHook('guiReset', self.onModelEdited) + def cleanup(self): + removeHook('reset', self.onReset) + + def onReset(self): + if not self._ignoreReset: + self.updateModels() + self.updateTemplates() def show(self): - for i in range(self.count()): - self.itemAt(i).widget().show() + self.widget.show() def hide(self): - for i in range(self.count()): - self.itemAt(i).widget().hide() + self.widget.hide() def onEdit(self): - ui.deckproperties.DeckProperties(self.parent, self.deck, - onFinish=self.onModelEdited) + import aqt.models + aqt.models.Models(self.mw, self.widget) - def onModelEdited(self): - # hack - from aqt import mw - self.deck = mw.deck - self.drawModels() - self.changed(self.deck.currentModel) - - def onChange(self, idx): + def onModelChange(self, idx): model = self._models[idx] - self.deck.currentModel = model - self.changed(self.deck.currentModel) - self.deck.setModified() + self.deck.conf['currentModelId'] = model.id + self.updateTemplates() + self._ignoreReset = True + self.mw.reset() + self._ignoreReset = False - def changed(self, model): - self.deck.addModel(model) - if self.onChangeFunc: - self.onChangeFunc(model) - self.drawCardModels() - - def drawModels(self): + def updateModels(self): self.models.clear() - self._models = sorted(self.deck.models().values(), key=attrgetter("name")) + self._models = sorted(self.deck.models().values(), + key=attrgetter("name")) self.models.addItems(QStringList( [m.name for m in self._models])) - idx = self._models.index(self.deck.currentModel()) - self.models.setCurrentIndex(idx) + for c, m in enumerate(self._models): + if m.id == self.deck.conf['currentModelId']: + self.models.setCurrentIndex(c) + break - def drawCardModels(self): + def updateTemplates(self): if not self.handleCards: return # remove any shortcuts - for s in self.shortcuts: + for s in self.cardShortcuts: s.setEnabled(False) - self.shortcuts = [] - m = self.deck.currentModel - txt = ", ".join([c.name for c in m.cardModels if c.active]) - if len(txt) > 30: - txt = txt[0:30] + "..." + self.cardShortcuts = [] + m = self.deck.currentModel() + ts = m.templates + active = [t['name'] for t in ts if t['actv']] + txt = ngettext("%d card", "%d cards", len(active)) % len(active) self.cards.setText(txt) n = 1 - for c in m.cardModels: - s = QShortcut(QKeySequence("Ctrl+%d" % n), self.parent) - self.parent.connect(s, SIGNAL("activated()"), - lambda c=c: self.toggleCard(c)) - self.shortcuts.append(s) + for t in ts: + s = QShortcut(QKeySequence("Ctrl+%d" % n), self.widget) + self.widget.connect(s, SIGNAL("activated()"), + lambda t=t: self.toggleTemplate(t)) + self.cardShortcuts.append(s) n += 1 def onCardChange(self): - m = QMenu(self.parent) - m.setTitle("menu") - model = self.deck.currentModel - for card in model.cardModels: - action = QAction(self.parent) + m = QMenu(self.widget) + model = self.deck.currentModel() + for template in model.templates: + action = QAction(self.widget) action.setCheckable(True) - if card.active: + if template['actv']: action.setChecked(True) - action.setText(card.name) - self.connect(action, SIGNAL("toggled(bool)"), - lambda b, a=action, c=card: \ - self.cardChangeTriggered(b,a,c)) + action.setText(template['name']) + self.connect(action, SIGNAL("activated()"), + lambda t=template: \ + self.toggleTemplate(t)) m.addAction(action) m.exec_(self.cards.mapToGlobal(QPoint(0,0))) - def cardChangeTriggered(self, bool, action, card): - if bool: - card.active = True - elif self.canDisableModel(): - card.active = False - self.drawCardModels() + def canDisableTemplate(self): + return len([t for t in self.deck.currentModel().templates + if t['actv']]) > 1 - def canDisableModel(self): - active = 0 - model = self.deck.currentModel - for c in model.cardModels: - if c.active: - active += 1 - if active > 1: - return True - return False - - def toggleCard(self, card): - if not card.active: - card.active = True - elif self.canDisableModel(): - card.active = False - self.drawCardModels() + def toggleTemplate(self, card): + if not card['actv']: + card['actv'] = True + elif self.canDisableTemplate(): + card['actv'] = False + self.deck.currentModel().flush() + self.updateTemplates() class AddModel(QDialog): diff --git a/aqt/overview.py b/aqt/overview.py index a9d5cc759..96e301b78 100644 --- a/aqt/overview.py +++ b/aqt/overview.py @@ -23,7 +23,6 @@ class Overview(object): self.refresh() def refresh(self): - print "refreshing" self._renderPage() # Handlers diff --git a/designer/addcards.ui b/designer/addcards.ui index 7896e32d5..3a78739aa 100644 --- a/designer/addcards.ui +++ b/designer/addcards.ui @@ -45,7 +45,14 @@ 4 - + + + + 0 + 10 + + +