mirror of
https://github.com/ankitects/anki.git
synced 2025-09-18 14:02:21 -04:00
finish fields dialog
This commit is contained in:
parent
4eba9eb913
commit
b9522487ad
8 changed files with 152 additions and 602 deletions
|
@ -299,7 +299,7 @@ class StatusDelegate(QItemDelegate):
|
|||
class Browser(QMainWindow):
|
||||
|
||||
def __init__(self, mw):
|
||||
QMainWindow.__init__(self, None)
|
||||
QMainWindow.__init__(self, mw)
|
||||
#applyStyles(self)
|
||||
self.mw = mw
|
||||
self.col = self.mw.col
|
||||
|
@ -491,8 +491,8 @@ class Browser(QMainWindow):
|
|||
self.onRowChanged)
|
||||
|
||||
def setupEditor(self):
|
||||
self.editor = aqt.editor.Editor(self.mw,
|
||||
self.form.fieldsArea)
|
||||
self.editor = aqt.editor.Editor(
|
||||
self.mw, self.form.fieldsArea, self)
|
||||
self.editor.stealFocus = False
|
||||
|
||||
def onRowChanged(self, current, previous):
|
||||
|
@ -534,7 +534,7 @@ class Browser(QMainWindow):
|
|||
|
||||
def onSortChanged(self, idx, ord):
|
||||
type = self.model.activeCols[idx]
|
||||
noSort = ("question", "answer", "template", "cardGroup", "noteGroup")
|
||||
noSort = ("question", "answer", "template", "deck", "ndeck")
|
||||
if type in noSort:
|
||||
showInfo(_("Sorting on this column is not supported. Please "
|
||||
"choose another."))
|
||||
|
|
|
@ -191,7 +191,7 @@ Please create a new card first."""))
|
|||
n = len(self.cards)
|
||||
cur = self.card.template()['ord']+1
|
||||
pos = getOnlyText(
|
||||
_("Enter new card position (1..%s):") % n,
|
||||
_("Enter new card position (1...%s):") % n,
|
||||
default=str(cur))
|
||||
if not pos:
|
||||
return
|
||||
|
|
|
@ -192,9 +192,10 @@ $(function () {
|
|||
|
||||
# caller is responsible for resetting note on reset
|
||||
class Editor(object):
|
||||
def __init__(self, mw, widget, addMode=False):
|
||||
self.widget = widget
|
||||
def __init__(self, mw, widget, parentWindow, addMode=False):
|
||||
self.mw = mw
|
||||
self.widget = widget
|
||||
self.parentWindow = parentWindow
|
||||
self.note = None
|
||||
self.stealFocus = True
|
||||
self.addMode = addMode
|
||||
|
@ -233,7 +234,7 @@ class Editor(object):
|
|||
######################################################################
|
||||
|
||||
def _addButton(self, name, func, key=None, tip=None, size=True, text="",
|
||||
check=False):
|
||||
check=False, native=False):
|
||||
b = QPushButton(text)
|
||||
if check:
|
||||
b.connect(b, SIGNAL("clicked(bool)"), func)
|
||||
|
@ -242,7 +243,8 @@ class Editor(object):
|
|||
if size:
|
||||
b.setFixedHeight(20)
|
||||
b.setFixedWidth(20)
|
||||
b.setStyle(self.plastiqueStyle)
|
||||
if not native:
|
||||
b.setStyle(self.plastiqueStyle)
|
||||
b.setFocusPolicy(Qt.NoFocus)
|
||||
if not text:
|
||||
b.setIcon(QIcon(":/icons/%s.png" % name))
|
||||
|
@ -266,13 +268,15 @@ class Editor(object):
|
|||
self.iconsBox.setMargin(0)
|
||||
self.iconsBox.setSpacing(0)
|
||||
self.outerLayout.addLayout(self.iconsBox)
|
||||
# align to right
|
||||
self.iconsBox.addItem(QSpacerItem(20,1, QSizePolicy.Expanding))
|
||||
b = self._addButton
|
||||
b("fields", self.onFields, "Ctrl+f",
|
||||
shortcut(_("Layout (Ctrl+f)")), size=False, text=_("Fields"))
|
||||
shortcut(_("Layout (Ctrl+f)")), size=False, text=_("Fields..."),
|
||||
native=True)
|
||||
b("layout", self.onCardLayout, "Ctrl+l",
|
||||
shortcut(_("Layout (Ctrl+l)")), size=False, text=_("Layout"))
|
||||
shortcut(_("Layout (Ctrl+l)")), size=False, text=_("Layout..."),
|
||||
native=True)
|
||||
# align to right
|
||||
self.iconsBox.addItem(QSpacerItem(20,1, QSizePolicy.Expanding))
|
||||
b("text_bold", self.toggleBold, "Ctrl+b", _("Bold text (Ctrl+b)"),
|
||||
check=True)
|
||||
b("text_italic", self.toggleItalic, "Ctrl+i", _("Italic text (Ctrl+i)"),
|
||||
|
@ -311,7 +315,7 @@ class Editor(object):
|
|||
def onFields(self):
|
||||
from aqt.fields import FieldDialog
|
||||
self.saveNow()
|
||||
FieldDialog(self.mw, self.note, parent=self.widget)
|
||||
FieldDialog(self.mw, self.note, parent=self.parentWindow)
|
||||
|
||||
def onCardLayout(self):
|
||||
from aqt.clayout import CardLayout
|
||||
|
@ -320,7 +324,7 @@ class Editor(object):
|
|||
ord = self.card.ord
|
||||
else:
|
||||
ord = 0
|
||||
CardLayout(self.mw, self.note, ord=ord, parent=self.widget)
|
||||
CardLayout(self.mw, self.note, ord=ord, parent=self.parentWindow)
|
||||
self.loadNote()
|
||||
|
||||
# JS->Python bridge
|
||||
|
|
475
aqt/fields.py
475
aqt/fields.py
|
@ -5,29 +5,25 @@ from aqt.qt import *
|
|||
import re
|
||||
from anki.consts import *
|
||||
import aqt
|
||||
from aqt.utils import saveGeom, restoreGeom, getBase, mungeQA, \
|
||||
saveSplitter, restoreSplitter, showInfo, askUser, getText, \
|
||||
openHelp
|
||||
from anki.utils import isMac, isWin
|
||||
|
||||
# raise Exception("Remember to disallow media&latex refs in edit.")
|
||||
|
||||
# need to strip the field management code out of this
|
||||
from aqt.utils import showWarning, openHelp, getOnlyText
|
||||
|
||||
class FieldDialog(QDialog):
|
||||
|
||||
def __init__(self, mw, note, ord=0, parent=None):
|
||||
QDialog.__init__(self, parent or mw, Qt.Window)
|
||||
QDialog.__init__(self, parent or mw) #, Qt.Window)
|
||||
self.mw = aqt.mw
|
||||
self.parent = parent or mw
|
||||
self.note = note
|
||||
self.col = self.mw.col
|
||||
self.mm = self.mw.col.models
|
||||
self.model = note.model()
|
||||
self.mw.checkpoint(_("Fields"))
|
||||
self.setWindowModality(Qt.WindowModal)
|
||||
self.form = aqt.forms.fields.Ui_Dialog()
|
||||
self.form.setupUi(self)
|
||||
self.form.buttonBox.button(QDialogButtonBox.Help).setAutoDefault(False)
|
||||
self.form.buttonBox.button(QDialogButtonBox.Close).setAutoDefault(False)
|
||||
self.currentIdx = None
|
||||
self.fillFields()
|
||||
self.setupSignals()
|
||||
self.form.fieldList.setCurrentRow(0)
|
||||
|
@ -36,6 +32,7 @@ class FieldDialog(QDialog):
|
|||
##########################################################################
|
||||
|
||||
def fillFields(self):
|
||||
self.currentIdx = None
|
||||
self.form.fieldList.clear()
|
||||
for f in self.model['flds']:
|
||||
self.form.fieldList.addItem(f['name'])
|
||||
|
@ -47,24 +44,80 @@ class FieldDialog(QDialog):
|
|||
c(f.fieldList, s("currentRowChanged(int)"), self.onRowChange)
|
||||
c(f.fieldAdd, s("clicked()"), self.onAdd)
|
||||
c(f.fieldDelete, s("clicked()"), self.onDelete)
|
||||
c(f.fieldUp, s("clicked()"), self.onUp)
|
||||
c(f.fieldDown, s("clicked()"), self.onDown)
|
||||
c(f.fieldRename, s("clicked()"), self.onRename)
|
||||
c(f.fieldPosition, s("clicked()"), self.onPosition)
|
||||
c(f.sortField, s("clicked()"), self.onSortField)
|
||||
c(f.buttonBox, s("helpRequested()"), self.onHelp)
|
||||
|
||||
def onRowChange(self, idx):
|
||||
if idx == -1:
|
||||
return
|
||||
self.saveField()
|
||||
self.loadField(idx)
|
||||
|
||||
def _uniqueName(self, prompt, ignoreOrd=None):
|
||||
txt = getOnlyText(prompt)
|
||||
if not txt:
|
||||
return
|
||||
for f in self.model['flds']:
|
||||
if ignoreOrd is not None and f['ord'] == ignoreOrd:
|
||||
continue
|
||||
if f['name'] == txt:
|
||||
showWarning(_("That field name is already used."))
|
||||
return
|
||||
return txt
|
||||
|
||||
def onRename(self):
|
||||
name = self._uniqueName(_("New name:"), self.currentIdx)
|
||||
if not name:
|
||||
return
|
||||
idx = self.currentIdx
|
||||
f = self.model['flds'][idx]
|
||||
self.mm.renameField(self.model, f, name)
|
||||
self.saveField()
|
||||
self.fillFields()
|
||||
self.form.fieldList.setCurrentRow(idx)
|
||||
|
||||
def onAdd(self):
|
||||
pass
|
||||
name = self._uniqueName(_("Field name:"))
|
||||
if not name:
|
||||
return
|
||||
self.saveField()
|
||||
self.mw.progress.start()
|
||||
f = self.mm.newField(name)
|
||||
self.mm.addField(self.model, f)
|
||||
self.mw.progress.finish()
|
||||
self.fillFields()
|
||||
self.form.fieldList.setCurrentRow(len(self.model['flds'])-1)
|
||||
|
||||
def onDelete(self):
|
||||
pass
|
||||
if len(self.model['flds']) < 3:
|
||||
return showWarning(_("Notes require at least two fields."))
|
||||
f = self.model['flds'][self.form.fieldList.currentRow()]
|
||||
self.mw.progress.start()
|
||||
self.mm.remField(self.model, f)
|
||||
self.mw.progress.finish()
|
||||
self.fillFields()
|
||||
self.form.fieldList.setCurrentRow(0)
|
||||
|
||||
def onUp(self):
|
||||
pass
|
||||
|
||||
def onDown(self):
|
||||
pass
|
||||
def onPosition(self, delta=-1):
|
||||
l = len(self.model['flds'])
|
||||
txt = getOnlyText(_("New position (1...%d):") % l)
|
||||
if not txt:
|
||||
return
|
||||
try:
|
||||
pos = int(txt)
|
||||
except ValueError:
|
||||
return
|
||||
if not 0 < pos <= l:
|
||||
return
|
||||
self.saveField()
|
||||
f = self.model['flds'][self.currentIdx]
|
||||
self.mw.progress.start()
|
||||
self.mm.moveField(self.model, f, pos-1)
|
||||
self.mw.progress.finish()
|
||||
self.fillFields()
|
||||
self.form.fieldList.setCurrentRow(pos-1)
|
||||
|
||||
def onSortField(self):
|
||||
# don't allow user to disable; it makes no sense
|
||||
|
@ -72,389 +125,35 @@ class FieldDialog(QDialog):
|
|||
self.model['sortf'] = self.form.fieldList.currentRow()
|
||||
|
||||
def loadField(self, idx):
|
||||
self.currentIdx = idx
|
||||
fld = self.model['flds'][idx]
|
||||
f = self.form
|
||||
f.fieldName.setText(fld['name'])
|
||||
f.fontFamily.setCurrentFont(QFont(fld['font']))
|
||||
f.fontSize.setValue(fld['size'])
|
||||
f.sticky.setChecked(fld['sticky'])
|
||||
print self.model['sortf'] == fld['ord']
|
||||
f.sortField.setChecked(self.model['sortf'] == fld['ord'])
|
||||
f.rtl.setChecked(fld['rtl'])
|
||||
|
||||
# Cards & Preview
|
||||
##########################################################################
|
||||
|
||||
def setupCards(self):
|
||||
self.updatingCards = False
|
||||
self.playedAudio = {}
|
||||
f = self.form
|
||||
if self.type == 0:
|
||||
f.templateType.setText(
|
||||
_("Templates that will be created:"))
|
||||
elif self.type == 1:
|
||||
f.templateType.setText(
|
||||
_("Templates used by note:"))
|
||||
else:
|
||||
f.templateType.setText(
|
||||
_("All templates:"))
|
||||
# replace with more appropriate size hints
|
||||
for e in ("cardQuestion", "cardAnswer"):
|
||||
w = getattr(f, e)
|
||||
idx = f.templateLayout.indexOf(w)
|
||||
r = f.templateLayout.getItemPosition(idx)
|
||||
f.templateLayout.removeWidget(w)
|
||||
w.hide()
|
||||
w.deleteLater()
|
||||
w = ResizingTextEdit(self)
|
||||
setattr(f, e, w)
|
||||
f.templateLayout.addWidget(w, r[0], r[1])
|
||||
c = self.connect
|
||||
c(f.cardList, SIGNAL("activated(int)"), self.cardChanged)
|
||||
c(f.editTemplates, SIGNAL("clicked()"), self.onEdit)
|
||||
c(f.cardQuestion, SIGNAL("textChanged()"), self.formatChanged)
|
||||
c(f.cardAnswer, SIGNAL("textChanged()"), self.formatChanged)
|
||||
c(f.alignment, SIGNAL("activated(int)"), self.saveCard)
|
||||
c(f.background, SIGNAL("clicked()"),
|
||||
lambda w=f.background:\
|
||||
self.chooseColour(w, "card"))
|
||||
c(f.questionInAnswer, SIGNAL("clicked()"), self.saveCard)
|
||||
c(f.allowEmptyAnswer, SIGNAL("clicked()"), self.saveCard)
|
||||
c(f.typeAnswer, SIGNAL("activated(int)"), self.saveCard)
|
||||
c(f.flipButton, SIGNAL("clicked()"), self.onFlip)
|
||||
c(f.clozectx, SIGNAL("clicked()"), self.saveCard)
|
||||
def linkClicked(url):
|
||||
QDesktopServices.openUrl(QUrl(url))
|
||||
f.preview.page().setLinkDelegationPolicy(
|
||||
QWebPage.DelegateExternalLinks)
|
||||
self.connect(f.preview,
|
||||
SIGNAL("linkClicked(QUrl)"),
|
||||
linkClicked)
|
||||
f.alignment.addItems(alignmentLabels().values())
|
||||
self.typeFieldNames = self.mm.fieldMap(self.model)
|
||||
s = [_("Don't ask me to type in the answer")]
|
||||
s += [_("Compare with field '%s'") % fi
|
||||
for fi in self.typeFieldNames.keys()]
|
||||
f.typeAnswer.insertItems(0, s)
|
||||
|
||||
def formatToScreen(self, fmt):
|
||||
fmt = fmt.replace("}}<br>", "}}\n")
|
||||
return fmt
|
||||
|
||||
def screenToFormat(self, fmt):
|
||||
fmt = fmt.replace("}}\n", "}}<br>")
|
||||
return fmt
|
||||
|
||||
def onEdit(self):
|
||||
aqt.templates.Templates(self.mw, self.model, self)
|
||||
self.reload()
|
||||
|
||||
def formatChanged(self):
|
||||
if self.updatingCards:
|
||||
def saveField(self):
|
||||
# not initialized yet?
|
||||
if self.currentIdx is None:
|
||||
return
|
||||
text = unicode(self.form.cardQuestion.toPlainText())
|
||||
self.card.template()['qfmt'] = self.screenToFormat(text)
|
||||
text = unicode(self.form.cardAnswer.toPlainText())
|
||||
self.card.template()['afmt'] = self.screenToFormat(text)
|
||||
self.renderPreview()
|
||||
|
||||
def onFlip(self):
|
||||
q = unicode(self.form.cardQuestion.toPlainText())
|
||||
a = unicode(self.form.cardAnswer.toPlainText())
|
||||
self.form.cardAnswer.setPlainText(q)
|
||||
self.form.cardQuestion.setPlainText(a)
|
||||
|
||||
def readCard(self):
|
||||
self.updatingCards = True
|
||||
t = self.card.template()
|
||||
idx = self.currentIdx
|
||||
fld = self.model['flds'][idx]
|
||||
f = self.form
|
||||
f.background.setPalette(QPalette(QColor(t['bg'])))
|
||||
f.cardQuestion.setPlainText(self.formatToScreen(t['qfmt']))
|
||||
f.cardAnswer.setPlainText(self.formatToScreen(t['afmt']))
|
||||
f.questionInAnswer.setChecked(t['hideQ'])
|
||||
f.allowEmptyAnswer.setChecked(t['emptyAns'])
|
||||
f.alignment.setCurrentIndex(t['align'])
|
||||
if t['typeAns'] is None:
|
||||
f.typeAnswer.setCurrentIndex(0)
|
||||
else:
|
||||
f.typeAnswer.setCurrentIndex(t['typeAns'] + 1)
|
||||
# model-level, but there's nowhere else to put this
|
||||
f.clozectx.setChecked(self.model['clozectx'])
|
||||
self.updatingCards = False
|
||||
fld['font'] = f.fontFamily.currentFont().family()
|
||||
fld['size'] = f.fontSize.value()
|
||||
fld['sticky'] = f.sticky.isChecked()
|
||||
fld['rtl'] = f.rtl.isChecked()
|
||||
|
||||
def fillCardList(self):
|
||||
self.form.cardList.clear()
|
||||
cards = []
|
||||
idx = 0
|
||||
for n, c in enumerate(self.cards):
|
||||
if c.ord == self.ord:
|
||||
cards.append(_("%s (current)") % c.template()['name'])
|
||||
idx = n
|
||||
else:
|
||||
cards.append(c.template()['name'])
|
||||
self.form.cardList.addItems(cards)
|
||||
self.form.cardList.setCurrentIndex(idx)
|
||||
self.cardChanged(idx)
|
||||
self.form.cardList.setFocus()
|
||||
|
||||
def cardChanged(self, idx):
|
||||
self.card = self.cards[idx]
|
||||
self.readCard()
|
||||
self.renderPreview()
|
||||
|
||||
def saveCard(self):
|
||||
if self.updatingCards:
|
||||
return
|
||||
t = self.card.template()
|
||||
t['align'] = self.form.alignment.currentIndex()
|
||||
t['bg'] = unicode(
|
||||
self.form.background.palette().window().color().name())
|
||||
t['hideQ'] = self.form.questionInAnswer.isChecked()
|
||||
t['emptyAns'] = self.form.allowEmptyAnswer.isChecked()
|
||||
idx = self.form.typeAnswer.currentIndex()
|
||||
if not idx:
|
||||
t['typeAns'] = None
|
||||
else:
|
||||
t['typeAns'] = idx-1
|
||||
self.model['clozectx'] = self.form.clozectx.isChecked()
|
||||
self.renderPreview()
|
||||
|
||||
def chooseColour(self, button, type="field"):
|
||||
new = QColorDialog.getColor(button.palette().window().color(), self,
|
||||
_("Choose Color"),
|
||||
QColorDialog.DontUseNativeDialog)
|
||||
if new.isValid():
|
||||
button.setPalette(QPalette(new))
|
||||
if type == "field":
|
||||
self.saveField()
|
||||
else:
|
||||
self.saveCard()
|
||||
|
||||
def renderPreview(self):
|
||||
c = self.card
|
||||
styles = self.model['css']
|
||||
styles += "\n.cloze { font-weight: bold; color: blue; }"
|
||||
self.form.preview.setHtml(
|
||||
('<html><head>%s</head><body class="%s">' %
|
||||
(getBase(self.col), c.cssClass())) +
|
||||
"<style>" + styles + "</style>" +
|
||||
mungeQA(c.q(reload=True)) +
|
||||
self.maybeTextInput() +
|
||||
"<hr>" +
|
||||
mungeQA(c.a())
|
||||
+ "</body></html>")
|
||||
clearAudioQueue()
|
||||
if c.id not in self.playedAudio:
|
||||
playFromText(c.q())
|
||||
playFromText(c.a())
|
||||
self.playedAudio[c.id] = True
|
||||
|
||||
def maybeTextInput(self):
|
||||
if self.card.template()['typeAns'] is not None:
|
||||
return "<center><input type=text></center>"
|
||||
return ""
|
||||
def reject(self):
|
||||
self.saveField()
|
||||
self.mm.save(self.model)
|
||||
self.mw.reset()
|
||||
QDialog.reject(self)
|
||||
|
||||
def accept(self):
|
||||
self.reject()
|
||||
|
||||
def reject(self):
|
||||
return QDialog.reject(self)
|
||||
self.mm.save(self.model)
|
||||
saveGeom(self, "CardLayout")
|
||||
saveSplitter(self.form.splitter, "clayout")
|
||||
self.mw.reset()
|
||||
return QDialog.reject(self)
|
||||
|
||||
|
||||
modified = False
|
||||
self.mw.startProgress()
|
||||
self.col.updateProgress(_("Applying changes..."))
|
||||
reset=True
|
||||
if len(self.fieldOrdinalUpdatedIds) > 0:
|
||||
self.col.rebuildFieldOrdinals(self.model.id, self.fieldOrdinalUpdatedIds)
|
||||
modified = True
|
||||
if self.needFieldRebuild:
|
||||
modified = True
|
||||
if modified:
|
||||
self.note.model.setModified()
|
||||
self.col.flushMod()
|
||||
if self.noteedit and self.noteedit.onChange:
|
||||
self.noteedit.onChange("all")
|
||||
reset=False
|
||||
if reset:
|
||||
self.mw.reset()
|
||||
self.col.finishProgress()
|
||||
QDialog.reject(self)
|
||||
|
||||
def onHelp(self):
|
||||
openHelp("CardLayout")
|
||||
|
||||
# Fields
|
||||
##########################################################################
|
||||
|
||||
def setupFields(self):
|
||||
self.fieldOrdinalUpdatedIds = []
|
||||
self.updatingFields = False
|
||||
self.needFieldRebuild = False
|
||||
c = self.connect; f = self.form
|
||||
sc = SIGNAL("stateChanged(int)")
|
||||
cl = SIGNAL("clicked()")
|
||||
c(f.fieldAdd, cl, self.addField)
|
||||
c(f.fieldDelete, cl, self.deleteField)
|
||||
c(f.fieldUp, cl, self.moveFieldUp)
|
||||
c(f.fieldDown, cl, self.moveFieldDown)
|
||||
c(f.preserveWhitespace, sc, self.saveField)
|
||||
c(f.fieldUnique, sc, self.saveField)
|
||||
c(f.fieldRequired, sc, self.saveField)
|
||||
c(f.sticky, sc, self.saveField)
|
||||
c(f.fieldList, SIGNAL("currentRowChanged(int)"),
|
||||
self.fieldChanged)
|
||||
c(f.fieldName, SIGNAL("lostFocus()"),
|
||||
self.saveField)
|
||||
c(f.fontFamily, SIGNAL("currentFontChanged(QFont)"),
|
||||
self.saveField)
|
||||
c(f.fontSize, SIGNAL("valueChanged(int)"),
|
||||
self.saveField)
|
||||
c(f.fontSizeEdit, SIGNAL("valueChanged(int)"),
|
||||
self.saveField)
|
||||
w = self.form.fontColour
|
||||
c(w, SIGNAL("clicked()"),
|
||||
lambda w=w: self.chooseColour(w))
|
||||
c(self.form.rtl,
|
||||
SIGNAL("stateChanged(int)"),
|
||||
self.saveField)
|
||||
|
||||
def fieldChanged(self):
|
||||
row = self.form.fieldList.currentRow()
|
||||
if row == -1:
|
||||
row = 0
|
||||
self.field = self.model['flds'][row]
|
||||
self.readField()
|
||||
self.enableFieldMoveButtons()
|
||||
|
||||
def readField(self):
|
||||
fld = self.field
|
||||
f = self.form
|
||||
self.updatingFields = True
|
||||
f.fieldName.setText(fld['name'])
|
||||
f.fieldUnique.setChecked(fld['uniq'])
|
||||
f.fieldRequired.setChecked(fld['req'])
|
||||
f.fontFamily.setCurrentFont(QFont(fld['font']))
|
||||
f.fontSize.setValue(fld['qsize'])
|
||||
f.fontSizeEdit.setValue(fld['esize'])
|
||||
f.fontColour.setPalette(QPalette(QColor(fld['qcol'])))
|
||||
f.rtl.setChecked(fld['rtl'])
|
||||
f.preserveWhitespace.setChecked(fld['pre'])
|
||||
f.sticky.setChecked(fld['sticky'])
|
||||
self.updatingFields = False
|
||||
|
||||
def saveField(self, *args):
|
||||
self.needFieldRebuild = True
|
||||
if self.updatingFields:
|
||||
return
|
||||
self.updatingFields = True
|
||||
fld = self.field
|
||||
# get name; we'll handle it last
|
||||
name = unicode(self.form.fieldName.text())
|
||||
if not name:
|
||||
return
|
||||
fld['uniq'] = self.form.fieldUnique.isChecked()
|
||||
fld['req'] = self.form.fieldRequired.isChecked()
|
||||
fld['font'] = unicode(
|
||||
self.form.fontFamily.currentFont().family())
|
||||
fld['qsize'] = self.form.fontSize.value()
|
||||
fld['esize'] = self.form.fontSizeEdit.value()
|
||||
fld['qcol'] = str(
|
||||
self.form.fontColour.palette().window().color().name())
|
||||
fld['rtl'] = self.form.rtl.isChecked()
|
||||
fld['pre'] = self.form.preserveWhitespace.isChecked()
|
||||
fld['sticky'] = self.form.sticky.isChecked()
|
||||
self.updatingFields = False
|
||||
if fld['name'] != name:
|
||||
self.mm.renameField(self.model, fld, name)
|
||||
# as the field name has changed, we have to regenerate cards
|
||||
self.cards = self.col.previewCards(self.note, self.type)
|
||||
self.cardChanged(0)
|
||||
self.renderPreview()
|
||||
self.fillFieldList()
|
||||
|
||||
def fillFieldList(self, row = None):
|
||||
oldRow = self.form.fieldList.currentRow()
|
||||
if oldRow == -1:
|
||||
oldRow = 0
|
||||
self.form.fieldList.clear()
|
||||
n = 1
|
||||
for field in self.model['flds']:
|
||||
label = field['name']
|
||||
item = QListWidgetItem(label)
|
||||
self.form.fieldList.addItem(item)
|
||||
n += 1
|
||||
count = self.form.fieldList.count()
|
||||
if row != None:
|
||||
self.form.fieldList.setCurrentRow(row)
|
||||
else:
|
||||
while (count > 0 and oldRow > (count - 1)):
|
||||
oldRow -= 1
|
||||
self.form.fieldList.setCurrentRow(oldRow)
|
||||
self.enableFieldMoveButtons()
|
||||
|
||||
def enableFieldMoveButtons(self):
|
||||
row = self.form.fieldList.currentRow()
|
||||
if row < 1:
|
||||
self.form.fieldUp.setEnabled(False)
|
||||
else:
|
||||
self.form.fieldUp.setEnabled(True)
|
||||
if row == -1 or row >= (self.form.fieldList.count() - 1):
|
||||
self.form.fieldDown.setEnabled(False)
|
||||
else:
|
||||
self.form.fieldDown.setEnabled(True)
|
||||
|
||||
def addField(self):
|
||||
f = self.mm.newField(self.model)
|
||||
l = len(self.model['flds'])
|
||||
f['name'] = _("Field %d") % l
|
||||
self.mw.progress.start()
|
||||
self.mm.addField(self.model, f)
|
||||
self.mw.progress.finish()
|
||||
self.reload()
|
||||
self.form.fieldList.setCurrentRow(l)
|
||||
self.form.fieldName.setFocus()
|
||||
self.form.fieldName.selectAll()
|
||||
|
||||
def deleteField(self):
|
||||
row = self.form.fieldList.currentRow()
|
||||
if row == -1:
|
||||
return
|
||||
if len(self.model.fields) < 2:
|
||||
showInfo(_("Please add a new field first."))
|
||||
return
|
||||
if askUser(_("Delete this field and its data from all notes?")):
|
||||
self.mw.progress.start()
|
||||
self.model.delField(self.field)
|
||||
self.mw.progress.finish()
|
||||
# need to update q/a format
|
||||
self.reload()
|
||||
|
||||
def moveFieldUp(self):
|
||||
row = self.form.fieldList.currentRow()
|
||||
if row == -1:
|
||||
return
|
||||
if row == 0:
|
||||
return
|
||||
self.mw.progress.start()
|
||||
self.model.moveField(self.field, row-1)
|
||||
self.mw.progress.finish()
|
||||
self.form.fieldList.setCurrentRow(row-1)
|
||||
self.reload()
|
||||
|
||||
def moveFieldDown(self):
|
||||
row = self.form.fieldList.currentRow()
|
||||
if row == -1:
|
||||
return
|
||||
if row == len(self.model.fields) - 1:
|
||||
return
|
||||
self.mw.progress.start()
|
||||
self.model.moveField(self.field, row+1)
|
||||
self.mw.progress.finish()
|
||||
self.form.fieldList.setCurrentRow(row+1)
|
||||
self.reload()
|
||||
openHelp("Fields")
|
||||
|
|
150
aqt/main.py
150
aqt/main.py
|
@ -193,6 +193,7 @@ Are you sure?"""):
|
|||
# then load collection and launch into the deck browser
|
||||
print "fixme: safeguard against multiple instances"
|
||||
self.col = Collection(self.pm.collectionPath())
|
||||
self.progress.setupDB(self.col.db)
|
||||
# skip the reset step; open overview directly
|
||||
self.moveToState("review")
|
||||
|
||||
|
@ -212,8 +213,6 @@ Are you sure?"""):
|
|||
getattr(self, "_"+state+"State")(oldState, *args)
|
||||
|
||||
def _deckBrowserState(self, oldState):
|
||||
self.disableDeckMenuItems()
|
||||
self.closeAllWindows()
|
||||
self.deckBrowser.show()
|
||||
|
||||
def _colLoadingState(self, oldState):
|
||||
|
@ -429,27 +428,33 @@ Debug info:\n%s""") % traceback.format_exc(), help="DeckErrors")
|
|||
else:
|
||||
aw.close()
|
||||
|
||||
def close(self, showBrowser=True):
|
||||
"Close current deck."
|
||||
def close(self):
|
||||
"Close and backup collection."
|
||||
if not self.col:
|
||||
return
|
||||
# if we were cramming, restore the standard scheduler
|
||||
if self.col.stdSched():
|
||||
self.col.reset()
|
||||
runHook("deckClosing")
|
||||
print "focusOut() should be handled with deckClosing now"
|
||||
#self.col.close()
|
||||
self.backup()
|
||||
self.closeAllWindows()
|
||||
self.col.close()
|
||||
self.col = None
|
||||
if showBrowser:
|
||||
self.moveToState("deckBrowser")
|
||||
|
||||
# Syncing
|
||||
##########################################################################
|
||||
|
||||
def backup(self):
|
||||
print "backup"
|
||||
|
||||
# Syncing
|
||||
##########################################################################
|
||||
|
||||
def onSync(self):
|
||||
return
|
||||
return showInfo("sync not yet implemented")
|
||||
from aqt.sync import Syncer
|
||||
# close collection if loaded
|
||||
if self.col:
|
||||
self.col.close()
|
||||
#
|
||||
Syncer()
|
||||
|
||||
# Tools
|
||||
##########################################################################
|
||||
|
@ -484,11 +489,7 @@ Debug info:\n%s""") % traceback.format_exc(), help="DeckErrors")
|
|||
|
||||
def closeEvent(self, event):
|
||||
"User hit the X button, etc."
|
||||
print "fixme: exit from edit current, review, etc"
|
||||
if self.state == "editCurrentNote":
|
||||
event.ignore()
|
||||
return self.moveToState("saveEdit")
|
||||
self.close(showBrowser=False)
|
||||
self.close()
|
||||
# if self.pm.profile['syncOnProgramOpen']:
|
||||
# self.showBrowser = False
|
||||
# self.syncDeck(interactive=False)
|
||||
|
@ -857,118 +858,9 @@ Please choose a new deck name:"""))
|
|||
|
||||
def onSchemaMod(self, arg):
|
||||
return askUser(_("""\
|
||||
This operation can't be merged when syncing, so the next \
|
||||
sync will overwrite any changes made on other devices that \
|
||||
haven't been synced here yet. Continue?"""))
|
||||
|
||||
# Media locations
|
||||
##########################################################################
|
||||
|
||||
# def setupMedia(self, deck):
|
||||
# print "setup media"
|
||||
# return
|
||||
# prefix = self.pm.profile['mediaLocation']
|
||||
# prev = deck.getVar("mediaLocation") or ""
|
||||
# # set the media prefix
|
||||
# if not prefix:
|
||||
# next = ""
|
||||
# elif prefix == "dropbox":
|
||||
# p = self.dropboxFolder()
|
||||
# next = os.path.join(p, "Public", "Anki")
|
||||
# else:
|
||||
# next = prefix
|
||||
# # check if the media has moved
|
||||
# migrateFrom = None
|
||||
# if prev != next:
|
||||
# # check if they were using plugin
|
||||
# if not prev:
|
||||
# p = self.dropboxFolder()
|
||||
# p = os.path.join(p, "Public")
|
||||
# deck.mediaPrefix = p
|
||||
# migrateFrom = deck.mediaDir()
|
||||
# if not migrateFrom:
|
||||
# # find the old location
|
||||
# deck.mediaPrefix = prev
|
||||
# dir = deck.mediaDir()
|
||||
# if dir and os.listdir(dir):
|
||||
# # it contains files; we'll need to migrate
|
||||
# migrateFrom = dir
|
||||
# # setup new folder
|
||||
# deck.mediaPrefix = next
|
||||
# if migrateFrom:
|
||||
# # force creation of new folder
|
||||
# dir = deck.mediaDir(create=True)
|
||||
# # migrate old files
|
||||
# self.migrateMedia(migrateFrom, dir)
|
||||
# else:
|
||||
# # chdir if dir exists
|
||||
# dir = deck.mediaDir()
|
||||
# # update location
|
||||
# deck.setVar("mediaLocation", next, mod=False)
|
||||
# if dir and prefix == "dropbox":
|
||||
# self.setupDropbox(deck)
|
||||
|
||||
# def migrateMedia(self, from_, to):
|
||||
# if from_ == to:
|
||||
# return
|
||||
# files = os.listdir(from_)
|
||||
# skipped = False
|
||||
# for f in files:
|
||||
# src = os.path.join(from_, f)
|
||||
# dst = os.path.join(to, f)
|
||||
# if not os.path.isfile(src):
|
||||
# skipped = True
|
||||
# continue
|
||||
# if not os.path.exists(dst):
|
||||
# shutil.copy2(src, dst)
|
||||
# if not skipped:
|
||||
# # everything copied, we can remove old folder
|
||||
# shutil.rmtree(from_, ignore_errors=True)
|
||||
|
||||
# def dropboxFolder(self):
|
||||
# try:
|
||||
# import aqt.dropbox as db
|
||||
# p = db.getPath()
|
||||
# except:
|
||||
# if isWin:
|
||||
# s = QSettings(QSettings.UserScope, "Microsoft", "Windows")
|
||||
# s.beginGroup("CurrentVersion/Explorer/Shell Folders")
|
||||
# p = os.path.join(s.value("Personal"), "My Dropbox")
|
||||
# else:
|
||||
# p = os.path.expanduser("~/Dropbox")
|
||||
# return p
|
||||
|
||||
# def setupDropbox(self, deck):
|
||||
# if not self.pm.profile['dropboxPublicFolder']:
|
||||
# # put a file in the folder
|
||||
# open(os.path.join(
|
||||
# deck.mediaPrefix, "right-click-me.txt"), "w").write("")
|
||||
# # tell user what to do
|
||||
# showInfo(_("""\
|
||||
# A file called right-click-me.txt has been placed in DropBox's public folder. \
|
||||
# After clicking OK, this folder will appear. Please right click on the file (\
|
||||
# command+click on a Mac), choose DropBox>Copy Public Link, and paste the \
|
||||
# link into Anki."""))
|
||||
# # open folder and text prompt
|
||||
# self.onOpenPluginFolder(deck.mediaPrefix)
|
||||
# txt = getText(_("Paste path here:"), parent=self)
|
||||
# if txt[0]:
|
||||
# fail = False
|
||||
# if not txt[0].lower().startswith("http"):
|
||||
# fail = True
|
||||
# if not txt[0].lower().endswith("right-click-me.txt"):
|
||||
# fail = True
|
||||
# if fail:
|
||||
# showInfo(_("""\
|
||||
# That doesn't appear to be a public link. You'll be asked again when the deck \
|
||||
# is next loaded."""))
|
||||
# else:
|
||||
# self.pm.profile['dropboxPublicFolder'] = os.path.dirname(txt[0])
|
||||
# if self.pm.profile['dropboxPublicFolder']:
|
||||
# # update media url
|
||||
# deck.setVar(
|
||||
# "mediaURL", self.pm.profile['dropboxPublicFolder'] + "/" +
|
||||
# os.path.basename(deck.mediaDir()) + "/")
|
||||
This operation can't be merged when syncing, so if you have made \
|
||||
changes on other devices that haven't been synced to this device yet, \
|
||||
they will be lost. Are you sure you want to continue?"""))
|
||||
|
||||
# Advanced features
|
||||
##########################################################################
|
||||
|
|
|
@ -25,10 +25,10 @@ class ProgressManager(object):
|
|||
##########################################################################
|
||||
|
||||
def setupDB(self, db):
|
||||
"Install a handler in the current deck."
|
||||
"Install a handler in the current DB."
|
||||
self.lastDbProgress = 0
|
||||
self.inDB = False
|
||||
db.set_progress_handler(self._dbProgress, 100000)
|
||||
db.set_progress_handler(self._dbProgress, 10000)
|
||||
|
||||
def _dbProgress(self):
|
||||
"Called from SQLite."
|
||||
|
|
|
@ -5,15 +5,14 @@ from aqt.qt import *
|
|||
import os, types, socket, time, traceback
|
||||
import aqt
|
||||
import anki
|
||||
from anki.sync import SyncClient, HttpSyncServerProxy, copyLocalMedia
|
||||
from anki.sync import SYNC_HOST, SYNC_PORT
|
||||
#from anki.sync import SyncClient, HttpSyncServerProxy, copyLocalMedia from
|
||||
#anki.sync import SYNC_HOST, SYNC_PORT
|
||||
from anki.errors import *
|
||||
from anki import Deck
|
||||
from anki.db import sqlite
|
||||
import aqt.forms
|
||||
from anki.hooks import addHook, removeHook
|
||||
|
||||
class SyncManager(object):
|
||||
class Syncer(object):
|
||||
|
||||
# Syncing
|
||||
##########################################################################
|
||||
|
|
|
@ -6,13 +6,16 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>403</width>
|
||||
<width>412</width>
|
||||
<height>352</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Fields</string>
|
||||
</property>
|
||||
<property name="modal">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
|
@ -37,62 +40,28 @@
|
|||
<item>
|
||||
<widget class="QPushButton" name="fieldAdd">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="icons.qrc">
|
||||
<normaloff>:/icons/list-add.png</normaloff>:/icons/list-add.png</iconset>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>false</bool>
|
||||
<string>Add</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="fieldDelete">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="icons.qrc">
|
||||
<normaloff>:/icons/editdelete.png</normaloff>:/icons/editdelete.png</iconset>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>false</bool>
|
||||
<string>Delete</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="fieldUp">
|
||||
<property name="toolTip">
|
||||
<string>Move selected field up</string>
|
||||
</property>
|
||||
<widget class="QPushButton" name="fieldRename">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="icons.qrc">
|
||||
<normaloff>:/icons/arrow-up.png</normaloff>:/icons/arrow-up.png</iconset>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>false</bool>
|
||||
<string>Rename</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="fieldDown">
|
||||
<property name="toolTip">
|
||||
<string>Move selected field down</string>
|
||||
</property>
|
||||
<widget class="QPushButton" name="fieldPosition">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="icons.qrc">
|
||||
<normaloff>:/icons/arrow-down.png</normaloff>:/icons/arrow-down.png</iconset>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>false</bool>
|
||||
<string>Reposition</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -115,27 +84,14 @@
|
|||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="_2">
|
||||
<item row="0" column="1" colspan="2">
|
||||
<widget class="QLineEdit" name="fieldName"/>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_20">
|
||||
<property name="text">
|
||||
<string>Name</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Editing Font</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<item row="0" column="1">
|
||||
<widget class="QFontComboBox" name="fontFamily">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
|
@ -145,14 +101,14 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<item row="3" column="1">
|
||||
<widget class="QCheckBox" name="rtl">
|
||||
<property name="text">
|
||||
<string>Reverse text direction (RTL)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<item row="0" column="2">
|
||||
<widget class="QSpinBox" name="fontSize">
|
||||
<property name="minimum">
|
||||
<number>5</number>
|
||||
|
@ -162,21 +118,21 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<item row="2" column="1">
|
||||
<widget class="QCheckBox" name="sticky">
|
||||
<property name="text">
|
||||
<string>Remember last input</string>
|
||||
<string>Remember last input when adding</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_18">
|
||||
<property name="text">
|
||||
<string>Options</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<item row="1" column="1">
|
||||
<widget class="QRadioButton" name="sortField">
|
||||
<property name="text">
|
||||
<string>Sort by this field in the browser</string>
|
||||
|
@ -201,11 +157,11 @@
|
|||
<tabstop>fieldList</tabstop>
|
||||
<tabstop>fieldAdd</tabstop>
|
||||
<tabstop>fieldDelete</tabstop>
|
||||
<tabstop>fieldUp</tabstop>
|
||||
<tabstop>fieldDown</tabstop>
|
||||
<tabstop>fieldName</tabstop>
|
||||
<tabstop>fieldRename</tabstop>
|
||||
<tabstop>fieldPosition</tabstop>
|
||||
<tabstop>fontFamily</tabstop>
|
||||
<tabstop>fontSize</tabstop>
|
||||
<tabstop>sortField</tabstop>
|
||||
<tabstop>sticky</tabstop>
|
||||
<tabstop>rtl</tabstop>
|
||||
<tabstop>buttonBox</tabstop>
|
||||
|
|
Loading…
Reference in a new issue