anki/aqt/addcards.py
Damien Elmes 8e71554ac4 saveNow() now requires a callback
the current code was freezing when clicking on 'cards' in the
browser - it looks like like the javascript callback was never
being called despite calling processEvents(). so we need to
refactor the code to call saveNow() with a callback that does the
subsequent processing.

a lot of the browser code was implicitly calling saveNow() via
beginReset(), so we've had to change all that code to save
immediately before it begins any processing. found a probable bug in
the process - it doesn't look like onRowChange() was saving before
overwriting the note, so theoretically edits could be lost if the
user switched to another card very quickly after typing something.

onSearch() has been split into a GUI-activated onSearchActivated()
that takes care of saving, and a lower level search() that refreshes
the current search. it keeps track of the last search via an instance
variable so that it refreshes properly if a user accidentally adds
some characters to their search without activating the search, then
does something like reverse the sort order.
2016-07-14 20:23:44 +10:00

223 lines
7.6 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 anki.lang import _
from aqt.qt import *
import aqt.forms
from aqt.utils import saveGeom, restoreGeom, showWarning, askUser, shortcut, \
tooltip, openHelp, addCloseShortcut, downArrow
from anki.sound import clearAudioQueue
from anki.hooks import addHook, remHook, runHook
from anki.utils import stripHTMLMedia, isMac
import aqt.editor, aqt.modelchooser, aqt.deckchooser
class AddCards(QDialog):
def __init__(self, mw):
QDialog.__init__(self, None, Qt.Window)
mw.setupDialogGC(self)
self.mw = mw
self.form = aqt.forms.addcards.Ui_Dialog()
self.form.setupUi(self)
self.setWindowTitle(_("Add"))
self.setMinimumHeight(300)
self.setMinimumWidth(400)
self.setupChoosers()
self.setupEditor()
self.setupButtons()
self.onReset()
self.history = []
self.forceClose = False
restoreGeom(self, "add")
addHook('reset', self.onReset)
addHook('currentModelChanged', self.onModelChange)
addCloseShortcut(self)
self.show()
self.setupNewNote()
def setupEditor(self):
self.editor = aqt.editor.Editor(
self.mw, self.form.fieldsArea, self, True)
def setupChoosers(self):
self.modelChooser = aqt.modelchooser.ModelChooser(
self.mw, self.form.modelArea)
self.deckChooser = aqt.deckchooser.DeckChooser(
self.mw, self.form.deckArea)
def helpRequested(self):
openHelp("addingnotes")
def setupButtons(self):
bb = self.form.buttonBox
ar = QDialogButtonBox.ActionRole
# add
self.addButton = bb.addButton(_("Add"), ar)
self.addButton.clicked.connect(self.addCards)
self.addButton.setShortcut(QKeySequence("Ctrl+Return"))
self.addButton.setToolTip(shortcut(_("Add (shortcut: ctrl+enter)")))
# close
self.closeButton = QPushButton(_("Close"))
self.closeButton.setAutoDefault(False)
bb.addButton(self.closeButton, QDialogButtonBox.RejectRole)
# help
self.helpButton = QPushButton(_("Help"), clicked=self.helpRequested)
self.helpButton.setAutoDefault(False)
bb.addButton(self.helpButton,
QDialogButtonBox.HelpRole)
# history
b = bb.addButton(
_("History")+ " "+downArrow(), ar)
if isMac:
sc = "Ctrl+Shift+H"
else:
sc = "Ctrl+H"
b.setShortcut(QKeySequence(sc))
b.setToolTip(_("Shortcut: %s") % shortcut(sc))
b.clicked.connect(self.onHistory)
b.setEnabled(False)
self.historyButton = b
def setupNewNote(self, set=True):
f = self.mw.col.newNote()
if set:
self.editor.setNote(f, focus=True)
return f
def onModelChange(self):
oldNote = self.editor.note
note = self.setupNewNote(set=False)
if oldNote:
oldFields = list(oldNote.keys())
newFields = list(note.keys())
for n, f in enumerate(note.model()['flds']):
fieldName = f['name']
try:
oldFieldName = oldNote.model()['flds'][n]['name']
except IndexError:
oldFieldName = None
# copy identical fields
if fieldName in oldFields:
note[fieldName] = oldNote[fieldName]
# set non-identical fields by field index
elif oldFieldName and oldFieldName not in newFields:
try:
note.fields[n] = oldNote.fields[n]
except IndexError:
pass
self.editor.currentField = 0
self.editor.setNote(note, focus=True)
def onReset(self, model=None, keep=False):
oldNote = self.editor.note
note = self.setupNewNote(set=False)
flds = note.model()['flds']
# copy fields from old note
if oldNote:
if not keep:
self.removeTempNote(oldNote)
for n in range(len(note.fields)):
try:
if not keep or flds[n]['sticky']:
note.fields[n] = oldNote.fields[n]
else:
note.fields[n] = ""
except IndexError:
break
self.editor.currentField = 0
self.editor.setNote(note, focus=True)
def removeTempNote(self, note):
if not note or not note.id:
return
# we don't have to worry about cards; just the note
self.mw.col._remNotes([note.id])
def addHistory(self, note):
txt = stripHTMLMedia(",".join(note.fields))[:30]
self.history.insert(0, (note.id, txt))
self.history = self.history[:15]
self.historyButton.setEnabled(True)
def onHistory(self):
m = QMenu(self)
for nid, txt in self.history:
a = m.addAction(_("Edit %s") % txt)
a.triggered.connect(lambda b, nid=nid: self.editHistory(nid))
runHook("AddCards.onHistory", self, m)
m.exec_(self.historyButton.mapToGlobal(QPoint(0,0)))
def editHistory(self, nid):
browser = aqt.dialogs.open("Browser", self.mw)
browser.form.searchEdit.lineEdit().setText("nid:%d" % nid)
browser.onSearchActivated()
def addNote(self, note):
note.model()['did'] = self.deckChooser.selectedId()
ret = note.dupeOrEmpty()
if ret == 1:
showWarning(_(
"The first field is empty."),
help="AddItems#AddError")
return
if '{{cloze:' in note.model()['tmpls'][0]['qfmt']:
if not self.mw.col.models._availClozeOrds(
note.model(), note.joinedFields(), False):
if not askUser(_("You have a cloze deletion note type "
"but have not made any cloze deletions. Proceed?")):
return
cards = self.mw.col.addNote(note)
if not cards:
showWarning(_("""\
The input you have provided would make an empty \
question on all cards."""), help="AddItems")
return
self.addHistory(note)
self.mw.requireReset()
return note
def addCards(self):
self.editor.saveNow(self._addCards)
def _addCards(self):
self.editor.saveAddModeVars()
note = self.editor.note
note = self.addNote(note)
if not note:
return
tooltip(_("Added"), period=500)
# stop anything playing
clearAudioQueue()
self.onReset(keep=True)
self.mw.col.autosave()
def keyPressEvent(self, evt):
"Show answer on RET or register answer."
if (evt.key() in (Qt.Key_Enter, Qt.Key_Return)
and self.editor.tags.hasFocus()):
evt.accept()
return
return QDialog.keyPressEvent(self, evt)
def reject(self):
if not self.canClose():
return
remHook('reset', self.onReset)
remHook('currentModelChanged', self.onModelChange)
clearAudioQueue()
self.removeTempNote(self.editor.note)
self.editor.setNote(None)
self.modelChooser.cleanup()
self.deckChooser.cleanup()
self.mw.maybeReset()
saveGeom(self, "add")
aqt.dialogs.close("AddCards")
QDialog.reject(self)
def canClose(self):
if (self.forceClose or self.editor.fieldsAreBlank() or
askUser(_("Close and lose current input?"))):
return True
return False