mirror of
https://github.com/ankitects/anki.git
synced 2025-09-18 22:12:21 -04:00
saving of fields
This commit is contained in:
parent
7e50c32f65
commit
ae1d7bf6fc
5 changed files with 108 additions and 124 deletions
|
@ -41,6 +41,9 @@ class AddCards(QDialog):
|
||||||
|
|
||||||
def setupEditor(self):
|
def setupEditor(self):
|
||||||
self.editor = aqt.editor.Editor(self.mw, self.dialog.fieldsArea)
|
self.editor = aqt.editor.Editor(self.mw, self.dialog.fieldsArea)
|
||||||
|
# get a fact for testing
|
||||||
|
fact = self.mw.deck.getFact(3951)
|
||||||
|
self.editor.setFact(fact)
|
||||||
|
|
||||||
def addChooser(self):
|
def addChooser(self):
|
||||||
return
|
return
|
||||||
|
|
214
aqt/editor.py
214
aqt/editor.py
|
@ -30,16 +30,67 @@ String.prototype.format = function() {
|
||||||
return args[m.match(/\d+/)]; });
|
return args[m.match(/\d+/)]; });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var currentField = null;
|
||||||
|
var changeTimer = null;
|
||||||
|
|
||||||
|
function keyUp() {
|
||||||
|
// esc clears focus
|
||||||
|
if (window.event.which == 27) {
|
||||||
|
currentField.blur();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
clearChangeTimer();
|
||||||
|
changeTimer = setTimeout(function () { saveField("key"); }, 700);
|
||||||
|
};
|
||||||
|
|
||||||
|
function clearChangeTimer() {
|
||||||
|
if (changeTimer) {
|
||||||
|
clearTimeout(changeTimer);
|
||||||
|
changeTimer = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function onFocus(elem) {
|
||||||
|
currentField = elem;
|
||||||
|
setTimeout(foo, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function foo() {
|
||||||
|
var s = document.getSelection();
|
||||||
|
if (s.rangeCount) {
|
||||||
|
var r = s.getRangeAt(0);
|
||||||
|
r.collapse();
|
||||||
|
s.removeAllRanges();
|
||||||
|
s.addRange(r);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function onBlur() {
|
||||||
|
if (currentField) {
|
||||||
|
saveField("focus");
|
||||||
|
}
|
||||||
|
clearChangeTimer();
|
||||||
|
currentField = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
function saveField(type) {
|
||||||
|
// type is either 'focus' or 'key'
|
||||||
|
py.run(type + ":" + currentField.id.substring(1) + ":" + currentField.innerHTML);
|
||||||
|
}
|
||||||
|
|
||||||
function setFields(fields) {
|
function setFields(fields) {
|
||||||
var txt = "";
|
var txt = "";
|
||||||
for (var i=0; i<fields.length; i++) {
|
for (var i=0; i<fields.length; i++) {
|
||||||
var n = fields[i][0];
|
var n = fields[i][0];
|
||||||
var f = fields[i][1];
|
var f = fields[i][1];
|
||||||
txt += "<tr><td class=fname>{0}</td><td width=100%%>".format(n);
|
txt += "<tr><td class=fname>{0}</td><td width=100%%>".format(n);
|
||||||
txt += "<div class=field contentEditable=true>{0}</div>".format(f);
|
txt += "<div id=f{0} onkeyup='keyUp();'".format(i);
|
||||||
|
txt += " onfocus='onFocus(this);' onblur='onBlur();' class=field ";
|
||||||
|
txt += "contentEditable=true>{0}</div>".format(f);
|
||||||
txt += "</td></tr>";
|
txt += "</td></tr>";
|
||||||
}
|
}
|
||||||
$("#fields").html("<table cellpadding=3>"+txt+"</table>");
|
$("#fields").html("<table cellpadding=3>"+txt+"</table>");
|
||||||
|
$("#f0").focus();
|
||||||
};
|
};
|
||||||
</script></head><body>
|
</script></head><body>
|
||||||
<div id="fields"></div>
|
<div id="fields"></div>
|
||||||
|
@ -62,6 +113,7 @@ class Editor(object):
|
||||||
self.mw = mw
|
self.mw = mw
|
||||||
self.fact = None
|
self.fact = None
|
||||||
self.onChange = None
|
self.onChange = None
|
||||||
|
self._loaded = False
|
||||||
# to be handled js side
|
# to be handled js side
|
||||||
#self.lastFocusedEdit = None
|
#self.lastFocusedEdit = None
|
||||||
self.changeTimer = None
|
self.changeTimer = None
|
||||||
|
@ -171,20 +223,33 @@ class Editor(object):
|
||||||
|
|
||||||
def setupWeb(self):
|
def setupWeb(self):
|
||||||
self.web = AnkiWebView(self.widget)
|
self.web = AnkiWebView(self.widget)
|
||||||
|
self.web.setBridge(self.bridge)
|
||||||
self.outerLayout.addWidget(self.web)
|
self.outerLayout.addWidget(self.web)
|
||||||
# pick up the window colour
|
# pick up the window colour
|
||||||
p = self.web.palette()
|
p = self.web.palette()
|
||||||
p.setBrush(QPalette.Base, Qt.transparent)
|
p.setBrush(QPalette.Base, Qt.transparent)
|
||||||
self.web.page().setPalette(p)
|
self.web.page().setPalette(p)
|
||||||
self.web.setAttribute(Qt.WA_OpaquePaintEvent, False)
|
self.web.setAttribute(Qt.WA_OpaquePaintEvent, False)
|
||||||
self.web.setHtml(_html % anki.js.all, loadCB=self._loadFinished)
|
self.web.setHtml(_html % anki.js.all,
|
||||||
|
loadCB=self._loadFinished)
|
||||||
|
|
||||||
|
def bridge(self, str):
|
||||||
|
print str
|
||||||
|
(type, num, txt) = str.split(":", 2)
|
||||||
|
self.fact._fields[int(num)] = txt
|
||||||
|
if type == "focus":
|
||||||
|
runHook("editor.focusLost", self.fact)
|
||||||
|
else:
|
||||||
|
runHook("editor.keyPressed", self.fact)
|
||||||
|
self.fact.flush()
|
||||||
|
|
||||||
def _loadFinished(self, w):
|
def _loadFinished(self, w):
|
||||||
self.web.eval("setFields(%s);" % simplejson.dumps(
|
self._loaded = True
|
||||||
[["Expression", "foo"], ["Reading", "Bar"]]))
|
if self.fact:
|
||||||
|
self.loadFact()
|
||||||
|
|
||||||
def setupTags(self):
|
def setupTags(self):
|
||||||
|
return
|
||||||
# # scrollarea
|
# # scrollarea
|
||||||
# self.fieldsScroll = QScrollArea()
|
# self.fieldsScroll = QScrollArea()
|
||||||
# self.fieldsScroll.setWidgetResizable(True)
|
# self.fieldsScroll.setWidgetResizable(True)
|
||||||
|
@ -203,81 +268,52 @@ class Editor(object):
|
||||||
# self.tagsBox.addWidget(self.tags)
|
# self.tagsBox.addWidget(self.tags)
|
||||||
# self.fieldsBox.addLayout(self.tagsBox)
|
# self.fieldsBox.addLayout(self.tagsBox)
|
||||||
|
|
||||||
|
# update available tags
|
||||||
|
self.tags.setDeck(self.deck)
|
||||||
|
tagsw = self.tagsLabel.sizeHint().width()
|
||||||
|
self.tagsLabel.setFixedWidth(max(tagsw,
|
||||||
|
max(*([
|
||||||
|
l.width() for l in self.labels] + [0])))
|
||||||
|
+ extra)
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def setFact(self, fact, noFocus=False, check=False, scroll=False,
|
|
||||||
forceRedraw=False):
|
def setFact(self, fact):
|
||||||
"Make FACT the current fact."
|
"Make FACT the current fact."
|
||||||
return
|
|
||||||
self.fact = fact
|
self.fact = fact
|
||||||
self.factState = None
|
|
||||||
if self.changeTimer:
|
if self.changeTimer:
|
||||||
self.changeTimer.stop()
|
self.changeTimer.stop()
|
||||||
self.changeTimer = None
|
self.changeTimer = None
|
||||||
if self.needToRedraw() or forceRedraw:
|
if self.fact:
|
||||||
if self.fact:
|
self.loadFact()
|
||||||
self.drawFields(noFocus, check)
|
|
||||||
else:
|
|
||||||
self.widget.hide()
|
|
||||||
return
|
|
||||||
else:
|
else:
|
||||||
self.loadFields(check)
|
self.widget.hide()
|
||||||
|
|
||||||
|
def loadFact(self):
|
||||||
|
if not self._loaded:
|
||||||
|
# will be loaded when page is ready
|
||||||
|
return
|
||||||
|
# fixme: focus on first widget
|
||||||
|
self.web.eval("setFields(%s);" % simplejson.dumps(self.fact.items()))
|
||||||
self.widget.show()
|
self.widget.show()
|
||||||
if scroll:
|
|
||||||
self.fieldsScroll.ensureVisible(0, 0)
|
|
||||||
if not noFocus:
|
|
||||||
# update focus to first field
|
|
||||||
self.fields[self.fact.fields[0].name][1].setFocus()
|
|
||||||
self.deck.setUndoBarrier()
|
|
||||||
if self.deck.mediaDir(create=False):
|
|
||||||
self.initMedia()
|
|
||||||
|
|
||||||
def refresh(self):
|
def refresh(self):
|
||||||
if self.fact:
|
if self.fact:
|
||||||
try:
|
self.fact.load()
|
||||||
self.deck.db.refresh(self.fact)
|
# fixme: what if fact is deleted?
|
||||||
except InvalidRequestError:
|
self.setFact(self.fact)
|
||||||
# not attached to session yet, add cards dialog will handle
|
|
||||||
return
|
|
||||||
self.setFact(self.fact, check=True, forceRedraw=True)
|
|
||||||
|
|
||||||
def focusFirst(self):
|
|
||||||
if self.focusTarget:
|
|
||||||
self.focusTarget.setFocus()
|
|
||||||
|
|
||||||
def initMedia(self):
|
def initMedia(self):
|
||||||
os.chdir(self.deck.mediaDir(create=True))
|
os.chdir(self.deck.mediaDir(create=True))
|
||||||
|
|
||||||
def deckClosedHook(self):
|
def deckClosedHook(self):
|
||||||
self.fact = None
|
self.setFact(None)
|
||||||
|
|
||||||
def _makeGrid(self):
|
# if field.fieldModel.features:
|
||||||
"Rebuild the grid to avoid trigging QT bugs."
|
# w.setLayoutDirection(Qt.RightToLeft)
|
||||||
self.fieldsFrame = QWidget()
|
# else:
|
||||||
self.fieldsGrid = QGridLayout(self.fieldsFrame)
|
# w.setLayoutDirection(Qt.LeftToRight)
|
||||||
self.fieldsFrame.setLayout(self.fieldsGrid)
|
|
||||||
self.fieldsGrid.setMargin(0)
|
|
||||||
self.fieldsGrid.setSpacing(5)
|
|
||||||
|
|
||||||
def drawField(self, field, n):
|
|
||||||
# label
|
|
||||||
l = QLabel(field.name)
|
|
||||||
self.labels.append(l)
|
|
||||||
self.fieldsGrid.addWidget(l, n, 0)
|
|
||||||
# edit widget
|
|
||||||
w = FactEdit(self)
|
|
||||||
w.setTabChangesFocus(True)
|
|
||||||
w.setAcceptRichText(True)
|
|
||||||
w.setMinimumSize(20, 60)
|
|
||||||
w.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
|
||||||
w.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
|
||||||
if field.fieldModel.features:
|
|
||||||
w.setLayoutDirection(Qt.RightToLeft)
|
|
||||||
else:
|
|
||||||
w.setLayoutDirection(Qt.LeftToRight)
|
|
||||||
self.fieldsGrid.addWidget(w, n, 1)
|
|
||||||
self.fields[field.name] = (field, w)
|
|
||||||
self.widgets[w] = field
|
|
||||||
# catch changes
|
# catch changes
|
||||||
w.connect(w, SIGNAL("lostFocus"),
|
w.connect(w, SIGNAL("lostFocus"),
|
||||||
lambda w=w: self.onFocusLost(w))
|
lambda w=w: self.onFocusLost(w))
|
||||||
|
@ -287,56 +323,6 @@ class Editor(object):
|
||||||
lambda w=w: self.formatChanged(w))
|
lambda w=w: self.formatChanged(w))
|
||||||
return w
|
return w
|
||||||
|
|
||||||
def drawFields(self, noFocus=False, check=False):
|
|
||||||
self.parent.setUpdatesEnabled(False)
|
|
||||||
self._makeGrid()
|
|
||||||
# add entries for each field
|
|
||||||
fields = self.fact.fields
|
|
||||||
self.fields = {}
|
|
||||||
self.widgets = {}
|
|
||||||
self.labels = []
|
|
||||||
n = 0
|
|
||||||
first = True
|
|
||||||
last = None
|
|
||||||
|
|
||||||
for field in fields:
|
|
||||||
w = self.drawField(field, n)
|
|
||||||
last = w
|
|
||||||
if first:
|
|
||||||
self.focusTarget = w
|
|
||||||
first = False
|
|
||||||
n += 1
|
|
||||||
|
|
||||||
# update available tags
|
|
||||||
self.tags.setDeck(self.deck)
|
|
||||||
# update fields
|
|
||||||
self.loadFields(check)
|
|
||||||
self.parent.setUpdatesEnabled(True)
|
|
||||||
self.fieldsScroll.setWidget(self.fieldsFrame)
|
|
||||||
if sys.platform.startswith("darwin"):
|
|
||||||
extra = 5
|
|
||||||
elif sys.platform.startswith("win32"):
|
|
||||||
extra = 3
|
|
||||||
else:
|
|
||||||
extra = 2
|
|
||||||
tagsw = self.tagsLabel.sizeHint().width()
|
|
||||||
self.tagsLabel.setFixedWidth(max(tagsw,
|
|
||||||
max(*([
|
|
||||||
l.width() for l in self.labels] + [0])))
|
|
||||||
+ extra)
|
|
||||||
self.parent.setTabOrder(last, self.tags)
|
|
||||||
|
|
||||||
def needToRedraw(self):
|
|
||||||
return True
|
|
||||||
if self.fact is None:
|
|
||||||
return True
|
|
||||||
if len(self.fact._fields) != len(self.fields):
|
|
||||||
return True
|
|
||||||
for field in self.fact.fields:
|
|
||||||
if field.name not in self.fields:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def loadFields(self, check=True, font=True):
|
def loadFields(self, check=True, font=True):
|
||||||
"Update field text (if changed) and font/colours."
|
"Update field text (if changed) and font/colours."
|
||||||
# text
|
# text
|
||||||
|
@ -459,12 +445,6 @@ class Editor(object):
|
||||||
else:
|
else:
|
||||||
p.setColor(QPalette.Base, QColor("#ffffff"))
|
p.setColor(QPalette.Base, QColor("#ffffff"))
|
||||||
self.fields[field.name][1].setPalette(p)
|
self.fields[field.name][1].setPalette(p)
|
||||||
# call relevant hooks
|
|
||||||
invalid = len(empty+dupe)
|
|
||||||
if self.factState != "valid" and not invalid:
|
|
||||||
self.factState = "valid"
|
|
||||||
elif self.factState != "invalid" and invalid:
|
|
||||||
self.factState = "invalid"
|
|
||||||
|
|
||||||
def textForField(self, field):
|
def textForField(self, field):
|
||||||
"Current edited value for field."
|
"Current edited value for field."
|
||||||
|
|
|
@ -62,6 +62,7 @@ class ProgressManager(object):
|
||||||
t.setSingleShot(True)
|
t.setSingleShot(True)
|
||||||
t.connect(t, SIGNAL("timeout()"), handler)
|
t.connect(t, SIGNAL("timeout()"), handler)
|
||||||
t.start(ms)
|
t.start(ms)
|
||||||
|
return t
|
||||||
|
|
||||||
# Creating progress dialogs
|
# Creating progress dialogs
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
|
@ -73,8 +73,8 @@ class AnkiWebView(QWebView):
|
||||||
QWebView.keyPressEvent(self, evt)
|
QWebView.keyPressEvent(self, evt)
|
||||||
def contextMenuEvent(self, evt):
|
def contextMenuEvent(self, evt):
|
||||||
QWebView.contextMenuEvent(self, evt)
|
QWebView.contextMenuEvent(self, evt)
|
||||||
def dropEvent(self, evt):
|
# def dropEvent(self, evt):
|
||||||
pass
|
# pass
|
||||||
def setLinkHandler(self, handler=None):
|
def setLinkHandler(self, handler=None):
|
||||||
if handler:
|
if handler:
|
||||||
self.linkHandler = handler
|
self.linkHandler = handler
|
||||||
|
|
|
@ -127,7 +127,7 @@
|
||||||
</property>
|
</property>
|
||||||
<widget class="QMenu" name="menuPlugins">
|
<widget class="QMenu" name="menuPlugins">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
<string>P&lugins</string>
|
<string>&Add-ons</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="icons.qrc">
|
<iconset resource="icons.qrc">
|
||||||
|
@ -499,17 +499,17 @@
|
||||||
</action>
|
</action>
|
||||||
<action name="actionOpenPluginFolder">
|
<action name="actionOpenPluginFolder">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>&Open Plugin Folder...</string>
|
<string>&Open Add-ons Folder...</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
<action name="actionEnableAllPlugins">
|
<action name="actionEnableAllPlugins">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>&Enable All Plugins</string>
|
<string>&Enable All</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
<action name="actionDisableAllPlugins">
|
<action name="actionDisableAllPlugins">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>&Disable All Plugins</string>
|
<string>&Disable All</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
<action name="actionEditCurrent">
|
<action name="actionEditCurrent">
|
||||||
|
@ -648,7 +648,7 @@
|
||||||
<normaloff>:/icons/layout.png</normaloff>:/icons/layout.png</iconset>
|
<normaloff>:/icons/layout.png</normaloff>:/icons/layout.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Layout...</string>
|
<string>&Layout...</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="shortcut">
|
<property name="shortcut">
|
||||||
<string>L</string>
|
<string>L</string>
|
||||||
|
|
Loading…
Reference in a new issue