refactor card layout, reviewing

This commit is contained in:
Damien Elmes 2011-11-28 14:37:07 +09:00
parent f2a39aad2a
commit cf87d19905
30 changed files with 1377 additions and 1195 deletions

0
anki.bat Normal file → Executable file
View file

View file

@ -29,13 +29,13 @@ Duggan, Matthew Holtz, Meelis Vasser, Michael Penkov, Michael Keppler, Michal
Petr Michalec, Piotr Kubowicz, Richard Colley, Samson Melamed, Stefaan
De Pooter, Susanna Björverud, Tacutu, Timm Preetz, Timo Paulssen, Ursus, Victor
Suba, and Xtru.
Anki icon by Alex Fraser (CC GNU GPL)
Deck icon by Laurent Baumann (CC BY-NC-SA 3.0)
Deck browser icons from:
http://led24.de/iconset
http://p.yusukekamiyamane.com/
Other icons under LGPL or public domain.
<p>
Anki icon by Alex Fraser (CC GNU GPL)<br>
Deck icon by Laurent Baumann (CC BY-NC-SA 3.0)<br>
Deck browser icons from:<br>
http://led24.de/iconset<br>
http://p.yusukekamiyamane.com/<br>
Other icons under LGPL or public domain.<br>
"""
}

View file

@ -9,7 +9,7 @@ import anki
from anki.errors import *
from anki.utils import stripHTML
from aqt.utils import saveGeom, restoreGeom, showWarning, askUser, shortcut, \
tooltip
tooltip, openHelp
from anki.sound import clearAudioQueue
from anki.hooks import addHook, removeHook
from anki.utils import stripHTMLMedia, isMac
@ -46,7 +46,7 @@ class AddCards(QDialog):
self.mw, self.form.modelArea)
def helpRequested(self):
aqt.openHelp("AddItems")
openHelp("AddItems")
def setupButtons(self):
bb = self.form.buttonBox

View file

@ -10,7 +10,7 @@ import anki, anki.utils, aqt.forms
from anki.utils import fmtTimeSpan, ids2str, stripHTMLMedia, isWin, intTime
from aqt.utils import saveGeom, restoreGeom, saveSplitter, restoreSplitter, \
saveHeader, restoreHeader, saveState, restoreState, applyStyles, getTag, \
showInfo, askUser, tooltip
showInfo, askUser, tooltip, openHelp
from anki.errors import *
from anki.db import *
from anki.hooks import runHook, addHook, removeHook
@ -69,7 +69,7 @@ class DataModel(QAbstractTableModel):
return
if role == Qt.FontRole:
f = QFont()
f.setPixelSize(self.browser.mw.config['editFontSize'])
f.setPixelSize(self.browser.mw.pm.profile['editFontSize'])
return f
if role == Qt.TextAlignmentRole:
align = Qt.AlignVCenter
@ -112,7 +112,7 @@ class DataModel(QAbstractTableModel):
# the db progress handler may cause a refresh, so we need to zero out
# old data first
self.cards = []
self.cards = self.col.findCards(txt, self.browser.mw.config['fullSearch'])
self.cards = self.col.findCards(txt, self.browser.mw.pm.profile['fullSearch'])
print "fetch cards in %dms" % ((time.time() - t)*1000)
if reset:
self.endReset()
@ -332,8 +332,8 @@ class Browser(QMainWindow):
self.onSearch()
def setupToolbar(self):
self.form.toolBar.setIconSize(QSize(self.mw.config['iconSize'],
self.mw.config['iconSize']))
self.form.toolBar.setIconSize(QSize(self.mw.pm.profile['iconSize'],
self.mw.pm.profile['iconSize']))
self.form.toolBar.toggleViewAction().setText(_("Toggle Toolbar"))
def setupMenus(self):
@ -372,10 +372,10 @@ class Browser(QMainWindow):
def updateFont(self):
self.form.tableView.setFont(QFont(
self.mw.config['editFontFamily'],
self.mw.config['editFontSize']))
self.mw.pm.profile['editFontFamily'],
self.mw.pm.profile['editFontSize']))
self.form.tableView.verticalHeader().setDefaultSectionSize(
self.mw.config['editLineSize'])
self.mw.pm.profile['editLineSize'])
def closeEvent(self, evt):
saveSplitter(self.form.splitter_2, "editor2")
@ -432,7 +432,7 @@ class Browser(QMainWindow):
self.onSearch)
self.setTabOrder(self.form.searchEdit, self.form.tableView)
self.compModel = QStringListModel()
self.compModel.setStringList(self.mw.config['searchHistory'])
self.compModel.setStringList(self.mw.pm.profile['searchHistory'])
self.searchComp = QCompleter(self.compModel, self.form.searchEdit)
self.searchComp.setCompletionMode(QCompleter.UnfilteredPopupCompletion)
self.searchComp.setCaseSensitivity(Qt.CaseInsensitive)
@ -441,18 +441,18 @@ class Browser(QMainWindow):
def onSearch(self, reset=True):
"Careful: if reset is true, the current note is saved."
txt = unicode(self.form.searchEdit.text()).strip()
sh = self.mw.config['searchHistory']
sh = self.mw.pm.profile['searchHistory']
if txt not in sh:
sh.insert(0, txt)
sh = sh[:30]
self.compModel.setStringList(sh)
self.mw.config['searchHistory'] = sh
self.mw.pm.profile['searchHistory'] = sh
self.model.search(txt, reset)
if not self.model.cards:
# no row change will fire
self.onRowChanged(None, None)
txt = _("No matches found.")
if not self.mw.config['fullSearch']:
if not self.mw.pm.profile['fullSearch']:
txt += "<p>" + _(
_("If your cards have formatting, you may want <br>"
"to enable 'search within formatting' in the<br>"
@ -834,7 +834,7 @@ where id in %s""" % ids2str(sf))
return sf
def onHelp(self):
aqt.openHelp("Browser")
openHelp("Browser")
# Misc menu options
######################################################################
@ -1066,18 +1066,18 @@ where id in %s""" % ids2str(self.selectedCards()), mod)
frm = aqt.forms.browseropts.Ui_Dialog()
frm.setupUi(d)
frm.fontCombo.setCurrentFont(QFont(
self.mw.config['editFontFamily']))
frm.fontSize.setValue(self.mw.config['editFontSize'])
frm.lineSize.setValue(self.mw.config['editLineSize'])
frm.fullSearch.setChecked(self.mw.config['fullSearch'])
self.mw.pm.profile['editFontFamily']))
frm.fontSize.setValue(self.mw.pm.profile['editFontSize'])
frm.lineSize.setValue(self.mw.pm.profile['editLineSize'])
frm.fullSearch.setChecked(self.mw.pm.profile['fullSearch'])
if d.exec_():
self.mw.config['editFontFamily'] = (
self.mw.pm.profile['editFontFamily'] = (
unicode(frm.fontCombo.currentFont().family()))
self.mw.config['editFontSize'] = (
self.mw.pm.profile['editFontSize'] = (
int(frm.fontSize.value()))
self.mw.config['editLineSize'] = (
self.mw.pm.profile['editLineSize'] = (
int(frm.lineSize.value()))
self.mw.config['fullSearch'] = frm.fullSearch.isChecked()
self.mw.pm.profile['fullSearch'] = frm.fullSearch.isChecked()
self.updateFont()
# Edit: replacing
@ -1130,7 +1130,7 @@ where id in %s""" % ids2str(self.selectedCards()), mod)
})
def onFindReplaceHelp(self):
aqt.openHelp("Browser#FindReplace")
openHelp("Browser#FindReplace")
# Edit: finding dupes
######################################################################
@ -1311,7 +1311,7 @@ select id from cards where nid in %s and ord in %s""" % (
self.browser.onSearch()
def onHelp(self):
aqt.openHelp("Browser#GenerateCards")
openHelp("Browser#GenerateCards")
# Change model dialog
######################################################################
@ -1477,4 +1477,4 @@ Are you sure you want to continue?""")):
return QDialog.accept(self)
def onHelp(self):
aqt.openHelp("Browser#ChangeModel")
openHelp("Browser#ChangeModel")

View file

@ -7,439 +7,217 @@ from anki.consts import *
import aqt
from anki.sound import playFromText, clearAudioQueue
from aqt.utils import saveGeom, restoreGeom, getBase, mungeQA, \
saveSplitter, restoreSplitter, showInfo, askUser, getText
saveSplitter, restoreSplitter, showInfo, askUser, getOnlyText, \
showWarning, openHelp
from anki.utils import isMac, isWin
import aqt.templates
# fixme: replace font substitutions with native comma list
class ResizingTextEdit(QTextEdit):
def sizeHint(self):
return QSize(200, 800)
# raise Exception("Remember to disallow media&latex refs in edit.")
class CardLayout(QDialog):
# type is previewCards() type
def __init__(self, mw, note, type=0, ord=0, parent=None):
def __init__(self, mw, note, ord=0, parent=None):
QDialog.__init__(self, parent or mw, Qt.Window)
raise Exception("Remember to disallow media&latex refs in edit.")
self.mw = aqt.mw
self.parent = parent or mw
self.note = note
self.type = type
self.ord = ord
self.col = self.mw.col
self.mm = self.mw.col.models
self.model = note.model()
self.form = aqt.forms.clayout.Ui_Dialog()
self.form.setupUi(self)
self.setupTabs()
self.setWindowTitle(_("%s Layout") % self.model['name'])
self.plastiqueStyle = None
if isMac or isWin:
self.plastiqueStyle = QStyleFactory.create("plastique")
self.connect(self.form.buttonBox, SIGNAL("helpRequested()"),
v1 = QVBoxLayout()
v1.addWidget(self.tabs)
self.bbox = QDialogButtonBox(
QDialogButtonBox.Close|QDialogButtonBox.Help)
v1.addWidget(self.bbox)
self.setLayout(v1)
self.connect(self.bbox, SIGNAL("helpRequested()"),
self.onHelp)
self.setupCards()
self.setupFields()
self.form.buttonBox.button(QDialogButtonBox.Help).setAutoDefault(False)
self.form.buttonBox.button(QDialogButtonBox.Close).setAutoDefault(False)
restoreSplitter(self.form.splitter, "clayout")
self.bbox.button(QDialogButtonBox.Help).setAutoDefault(False)
self.bbox.button(QDialogButtonBox.Close).setAutoDefault(False)
self.mw.checkpoint(_("Card Layout"))
self.redraw()
restoreGeom(self, "CardLayout")
if not self.reload(first=True):
return
self.exec_()
def reload(self, first=False):
self.cards = self.col.previewCards(self.note, self.type)
if not self.cards:
self.accept()
if first:
showInfo(_("Please enter some text first."))
else:
showInfo(_("The current note was deleted."))
return
self.fillCardList()
self.fillFieldList()
self.fieldChanged()
self.readField()
return True
def redraw(self):
self.cards = self.col.previewCards(self.note, 2)
self.redrawing = True
self.updateTabs()
self.redrawing = False
self.selectCard(self.ord)
def setupTabs(self):
c = self.connect
self.tabs = QTabWidget()
self.tabs.setTabsClosable(True)
self.tabs.setUsesScrollButtons(True)
add = QPushButton("+")
add.setFixedWidth(30)
c(add, SIGNAL("clicked()"), self.onAddCard)
self.tabs.setCornerWidget(add)
c(self.tabs, SIGNAL("currentChanged(int)"), self.selectCard)
c(self.tabs, SIGNAL("tabCloseRequested(int)"), self.onRemoveTab)
def updateTabs(self):
self.forms = []
self.tabs.clear()
for t in self.model['tmpls']:
self.addTab(t)
def addTab(self, t):
c = self.connect
w = QWidget()
h = QHBoxLayout()
h.addStretch()
rename = QPushButton("Rename")
c(rename, SIGNAL("clicked()"), self.onRename)
h.addWidget(rename)
order = QPushButton(_("Reposition"))
h.addWidget(order)
c(order, SIGNAL("clicked()"), self.onReorder)
h.addStretch()
v = QVBoxLayout()
v.setMargin(3)
v.setSpacing(3)
v.addLayout(h)
l = QHBoxLayout()
l.setMargin(0)
l.setSpacing(3)
left = QWidget()
# template area
tform = aqt.forms.template.Ui_Form()
tform.setupUi(left)
c(tform.front, SIGNAL("textChanged()"), self.onTemplateEdit)
c(tform.back, SIGNAL("textChanged()"), self.onTemplateEdit)
l.addWidget(left, 5)
# preview area
right = QWidget()
pform = aqt.forms.preview.Ui_Form()
pform.setupUi(right)
def linkClicked(url):
QDesktopServices.openUrl(QUrl(url))
for wig in pform.front, pform.back:
wig.page().setLinkDelegationPolicy(
QWebPage.DelegateExternalLinks)
c(wig, SIGNAL("linkClicked(QUrl)"), linkClicked)
l.addWidget(right, 5)
v.addLayout(l)
w.setLayout(v)
self.forms.append({'tform': tform, 'pform': pform})
self.tabs.addTab(w, t['name'])
def onRemoveTab(self, idx):
if not self.mm.remTemplate(self.model, self.cards[idx].template()):
return showWarning(_("""\
Removing this card would cause one or more notes to be deleted. \
Please create a new card first."""))
self.redraw()
# 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)
if self.plastiqueStyle:
f.background.setStyle(self.plastiqueStyle)
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 selectCard(self, idx):
if self.redrawing:
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()
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
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.ord = idx
self.card = self.cards[idx]
self.tab = self.forms[idx]
self.tabs.setCurrentIndex(idx)
self.readCard()
self.renderPreview()
def saveCard(self):
if self.updatingCards:
return
def readCard(self):
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.redrawing = True
self.tab['tform'].front.setPlainText(t['qfmt'])
self.tab['tform'].back.setPlainText(t['afmt'])
self.redrawing = False
def onTemplateEdit(self):
if self.redrawing:
return
text = self.tab['tform'].front.toPlainText()
self.card.template()['qfmt'] = text
text = self.tab['tform'].back.toPlainText()
self.card.template()['afmt'] = text
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 saveCard(self):
t = self.card.template()
self.renderPreview()
def renderPreview(self):
print "preview"
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
styles = "\n.cloze { font-weight: bold; color: blue; }"
html = '<html><body id=card><style>%s</style>%s</body></html>'
self.tab['pform'].front.setHtml(
html % (styles, mungeQA(c.q(reload=True))))
self.tab['pform'].back.setHtml(
html % (styles, mungeQA(c.a())))
def maybeTextInput(self):
return "text input"
if self.card.template()['typeAns'] is not None:
return "<center><input type=text></center>"
return ""
def onRename(self):
name = getOnlyText(_("New name:"))
if not name:
return
if name in [c.template()['name'] for c in self.cards
if c.template()['ord'] != self.ord]:
return showWarning(_("That name is already used."))
self.card.template()['name'] = name
self.tabs.setTabText(self.tabs.currentIndex(), name)
def onReorder(self):
n = len(self.cards)
cur = self.card.template()['ord']+1
pos = getOnlyText(
_("Enter new card position (1..%s):") % n,
default=str(cur))
if not pos:
return
try:
pos = int(pos)
except ValueError:
return
if pos < 1 or pos > n:
return
if pos == cur:
return
pos -= 1
self.mm.moveTemplate(self.model, self.card.template(), pos)
self.ord = pos
self.redraw()
def onAddCard(self):
name = getOnlyText(_("Name:"))
if not name:
return
if name in [c.template()['name'] for c in self.cards]:
return showWarning(_("That name is already used."))
t = self.mm.newTemplate(name)
self.mm.addTemplate(self.model, t)
self.redraw()
# Closing & Help
######################################################################
def accept(self):
self.reject()
def reject(self):
self.mm.save(self.model)
saveGeom(self, "CardLayout")
saveSplitter(self.form.splitter, "clayout")
self.mw.reset()
saveGeom(self, "CardLayout")
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):
aqt.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
if self.plastiqueStyle:
w.setStyle(self.plastiqueStyle)
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("CardLayout")

View file

@ -3,7 +3,7 @@
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
from aqt.qt import *
from aqt.utils import askUser
from aqt.utils import askUser, getOnlyText
class DeckBrowser(object):
@ -27,7 +27,7 @@ class DeckBrowser(object):
if cmd == "open":
self._selDeck(arg)
elif cmd == "opts":
self._optsForRow(int(arg))
self._showOptions(arg)
elif cmd == "download":
self.mw.onGetSharedDeck()
elif cmd == "new":
@ -43,7 +43,7 @@ class DeckBrowser(object):
def _selDeck(self, did):
self.mw.col.decks.select(did)
self.mw.moveToState("overview")
self.mw.onOverview()
# HTML generation
##########################################################################
@ -85,13 +85,15 @@ body { margin: 1em; }
def _deckRow(self, node, depth):
name, did, due, new, children = node
def indent():
return "&nbsp;"*3*depth
# due image
buf = "<tr><td colspan=5>" + self._dueImg(due, new)
buf = "<tr><td colspan=5>" + indent() + self._dueImg(due, new)
# deck link
buf += " <a class=deck href='open:%d'>%s</a></td>"% (did, name)
# options
buf += "<td align=right class=opts>%s</td></tr>" % self.mw.button(
link="opts:%d"%did, name=_("Options")+'&#9662')
link="opts:%d"%did, name="<img valign=bottom src='qrc:/icons/gears.png'>&#9662")
# children
buf += self._renderDeckTree(children, depth+1)
return buf
@ -108,25 +110,29 @@ body { margin: 1em; }
# Options
##########################################################################
def _optsForRow(self, n):
def _showOptions(self, did):
m = QMenu(self.mw)
# delete
a = m.addAction(QIcon(":/icons/editdelete.png"), _("Delete"))
a.connect(a, SIGNAL("triggered()"), lambda n=n: self._deleteRow(n))
a = m.addAction(_("Rename"))
a.connect(a, SIGNAL("triggered()"), lambda did=did: self._rename(did))
a = m.addAction(_("Delete"))
a.connect(a, SIGNAL("triggered()"), lambda did=did: self._delete(did))
m.exec_(QCursor.pos())
def _deleteRow(self, c):
d = self._decks[c]
if d['state'] == 'missing':
return self._hideRow(c)
def _rename(self, did):
deck = self.mw.col.decks.get(did)
newName = getOnlyText(_("New deck name:"))
if not newName:
return
if deck in self.mw.col.decks.allNames():
return showWarning(_("That deck already exists."))
self.mw.col.decks.rename(deck, newName)
self.show()
def _delete(self, did):
if did == 1:
return showWarning(_("The default deck can't be deleted."))
deck = self.mw.col.decks.get(did)
if askUser(_("""\
Delete %s? If this deck is synchronized the online version will \
not be touched.""") % d['name']):
deck = d['path']
os.unlink(deck)
try:
shutil.rmtree(re.sub(".anki$", ".media", deck))
except OSError:
pass
self.mw.config.delRecentDeck(deck)
self.refresh()
Are you sure you wish to delete all of the cards in %s?""")%deck['name']):
self.mw.col.decks.rem(did, True)
self.show()

View file

@ -4,7 +4,7 @@
from aqt.qt import *
import sys, re
import aqt
from aqt.utils import maybeHideClose
from aqt.utils import maybeHideClose, openHelp
class ColOptions(QDialog):
@ -28,7 +28,7 @@ class ColOptions(QDialog):
self.form.mediaURL.setText(self.d.conf['mediaURL'])
def helpRequested(self):
aqt.openHelp("ColOptions")
openHelp("ColOptions")
def reject(self):
needSync = False

View file

@ -9,7 +9,8 @@ from anki.sound import play
from anki.hooks import runHook
from aqt.sound import getAudio
from aqt.webview import AnkiWebView
from aqt.utils import shortcut, showInfo, showWarning, getBase, getFile
from aqt.utils import shortcut, showInfo, showWarning, getBase, getFile, \
openHelp
import aqt
import anki.js
@ -449,7 +450,7 @@ class Editor(object):
form = aqt.forms.edithtml.Ui_Dialog()
form.setupUi(d)
d.connect(form.buttonBox, SIGNAL("helpRequested()"),
lambda: aqt.openHelp("HtmlEditor"))
lambda: openHelp("HtmlEditor"))
form.textEdit.setPlainText(self.note.fields[self.currentField])
form.textEdit.moveCursor(QTextCursor.End)
d.exec_()
@ -582,7 +583,7 @@ class Editor(object):
txtcol)
def colourChanged(self):
recent = self.mw.config['recentColours']
recent = self.mw.pm.profile['recentColours']
self._updateForegroundButton(recent[-1])
def onForeground(self):
@ -606,7 +607,7 @@ class Editor(object):
self.colourChoose = QShortcut(QKeySequence("F6"), p)
p.connect(self.colourChoose, SIGNAL("activated()"),
self.onChooseColourKey)
for n, c in enumerate(reversed(self.mw.config['recentColours'])):
for n, c in enumerate(reversed(self.mw.pm.profile['recentColours'])):
col = QToolButton()
col.setAutoRaise(True)
col.setFixedWidth(64)
@ -640,7 +641,7 @@ class Editor(object):
p.show()
def onRemoveColour(self, colour):
recent = self.mw.config['recentColours']
recent = self.mw.pm.profile['recentColours']
recent.remove(colour)
if not recent:
recent.append("#000000")
@ -659,7 +660,7 @@ class Editor(object):
pass
def onChooseColour(self, colour):
recent = self.mw.config['recentColours']
recent = self.mw.pm.profile['recentColours']
recent.remove(colour)
recent.append(colour)
self.web.eval("setFormat('forecolor', '%s')" % colour)
@ -669,7 +670,7 @@ class Editor(object):
def onNewColour(self):
new = QColorDialog.getColor(Qt.white, self.widget)
self.widget.raise_()
recent = self.mw.config['recentColours']
recent = self.mw.pm.profile['recentColours']
if new.isValid():
txtcol = unicode(new.name())
if txtcol not in recent:
@ -698,7 +699,7 @@ class Editor(object):
# copy to media folder
name = self.mw.col.media.addFile(path)
# remove original?
if canDelete and self.mw.config['deleteMedia']:
if canDelete and self.mw.pm.profile['deleteMedia']:
if os.path.abspath(name) != os.path.abspath(path):
try:
os.unlink(old)
@ -738,7 +739,7 @@ class Editor(object):
######################################################################
def setupKeyboard(self):
if isWin and self.mw.config['preserveKeyboard']:
if isWin and self.mw.pm.profile['preserveKeyboard']:
a = ctypes.windll.user32.ActivateKeyboardLayout
a.restype = ctypes.c_void_p
a.argtypes = [ctypes.c_void_p, ctypes.c_uint]
@ -773,7 +774,7 @@ class EditorWebView(AnkiWebView):
AnkiWebView.__init__(self, parent)
self.editor = editor
self.errtxt = _("An error occured while opening %s")
self.strip = self.editor.mw.config['stripHTML']
self.strip = self.editor.mw.pm.profile['stripHTML']
def keyPressEvent(self, evt):
self._curKey = True

495
aqt/fields.py Normal file
View file

@ -0,0 +1,495 @@
# Copyright: Damien Elmes <anki@ichi2.net>
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
from aqt.qt import *
import re
from anki.consts import *
import aqt
from anki.sound import playFromText, clearAudioQueue
from aqt.utils import saveGeom, restoreGeom, getBase, mungeQA, \
saveSplitter, restoreSplitter, showInfo, askUser, getText, \
openHelp
from anki.utils import isMac, isWin
import aqt.templates
# raise Exception("Remember to disallow media&latex refs in edit.")
# need to strip the field management code out of this
class CardLayout(QDialog):
def __init__(self, mw, note, ord=0, parent=None):
QDialog.__init__(self, parent or mw, Qt.Window)
self.mw = aqt.mw
self.parent = parent or mw
self.note = note
self.ord = ord
self.col = self.mw.col
self.mm = self.mw.col.models
self.model = note.model()
self.setupTabs()
v1 = QVBoxLayout()
v1.addWidget(self.tabs)
self.bbox = QDialogButtonBox(QDialogButtonBox.Close)
v1.addWidget(self.bbox)
self.setLayout(v1)
self.updateTabs()
self.exec_()
return
def setupTabs(self):
self.tabs = QTabWidget()
self.tabs.setTabsClosable(True)
self.tabs.setUsesScrollButtons(True)
self.tabs.setMovable(True)
add = QPushButton("+")
add.setFixedWidth(30)
self.tabs.setCornerWidget(add)
def updateTabs(self):
self.forms = []
self.tabs.clear()
for t in self.model['tmpls']:
self.addTab(t)
def addTab(self, t):
w = QWidget()
h = QHBoxLayout()
h.addStretch()
label = QLabel(_("Name:"))
h.addWidget(label)
edit = QLineEdit()
edit.setFixedWidth(200)
h.addWidget(edit)
h.addStretch()
v = QVBoxLayout()
v.addLayout(h)
l = QHBoxLayout()
l.setMargin(0)
l.setSpacing(3)
left = QWidget()
# template area
tform = aqt.forms.template.Ui_Form()
tform.setupUi(left)
l.addWidget(left, 5)
# preview area
right = QWidget()
pform = aqt.forms.preview.Ui_Form()
pform.setupUi(right)
l.addWidget(right, 5)
v.addLayout(l)
w.setLayout(v)
self.tabs.addTab(w, t['name'])
self.forms.append([tform, pform, edit])
def old():
self.form = aqt.forms.clayout.Ui_Dialog()
self.form.setupUi(self)
self.setWindowTitle(_("%s Layout") % self.model['name'])
self.plastiqueStyle = None
if isMac or isWin:
self.plastiqueStyle = QStyleFactory.create("plastique")
self.connect(self.form.buttonBox, SIGNAL("helpRequested()"),
self.onHelp)
self.setupCards()
self.setupFields()
self.form.buttonBox.button(QDialogButtonBox.Help).setAutoDefault(False)
self.form.buttonBox.button(QDialogButtonBox.Close).setAutoDefault(False)
restoreSplitter(self.form.splitter, "clayout")
restoreGeom(self, "CardLayout")
if not self.reload(first=True):
return
self.exec_()
def reload(self, first=False):
self.cards = self.col.previewCards(self.note, self.type)
if not self.cards:
self.accept()
if first:
showInfo(_("Please enter some text first."))
else:
showInfo(_("The current note was deleted."))
return
self.fillCardList()
self.fillFieldList()
self.fieldChanged()
self.readField()
return True
# 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:
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()
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
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 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()

View file

@ -51,7 +51,7 @@ Error was:<pre>%s</pre>""")
self.form.table, SIGNAL("currentCellChanged(int,int,int,int)"),
self.onCellChanged)
self.form.table.verticalHeader().setDefaultSectionSize(
self.parent.config['editLineSize'])
self.parent.pm.profile['editLineSize'])
self.connect(self.form.search, SIGNAL("textChanged(QString)"),
self.limit)
@ -222,7 +222,7 @@ Error was:<pre>%s</pre>""")
tit = tit[0:40]
if self.type == 0:
# col
dd = self.parent.config['documentDir']
dd = self.parent.pm.profile['documentDir']
p = os.path.join(dd, tit + ".anki")
if os.path.exists(p):
tit += "%d" % time.time()

View file

@ -5,7 +5,7 @@
from aqt.qt import *
import aqt, simplejson
from anki.utils import ids2str
from aqt.utils import showInfo, showWarning
from aqt.utils import showInfo, showWarning, openHelp
# Configuration editing
##########################################################################
@ -24,7 +24,7 @@ class GroupConf(QDialog):
self.setup()
self.connect(self.form.buttonBox,
SIGNAL("helpRequested()"),
lambda: aqt.openHelp("GroupOptions"))
lambda: openHelp("GroupOptions"))
self.connect(self.form.buttonBox.button(QDialogButtonBox.RestoreDefaults),
SIGNAL("clicked()"),
self.onRestore)

View file

@ -4,7 +4,7 @@
from aqt.qt import *
import aqt
from aqt.utils import showInfo, getOnlyText
from aqt.utils import showInfo, getOnlyText, openHelp
COLNAME = 0
COLOPTS = 1
@ -79,7 +79,7 @@ class Groups(QDialog):
button(f.delete_2, self.onDelete)
self.connect(self.form.buttonBox,
SIGNAL("helpRequested()"),
lambda: aqt.openHelp("Groups"))
lambda: openHelp("Groups"))
def onSelectAll(self):
for i in self.items:

View file

@ -65,7 +65,7 @@ class UpdateMap(QDialog):
self.exec_()
def helpRequested(self):
aqt.openHelp("FileImport")
openHelp("FileImport")
def accept(self):
self.updateKey = (
@ -311,4 +311,4 @@ you can enter it here. Use \\t to represent tab."""),
QDialog.reject(self)
def helpRequested(self):
aqt.openHelp("FileImport")
openHelp("FileImport")

View file

@ -191,8 +191,10 @@ Are you sure?"""):
# maybe sync
self.onSync()
# then load collection and launch into the deck browser
print "fixme: safeguard against multiple instances"
self.col = Collection(self.pm.collectionPath())
self.moveToState("overview")
# skip the reset step; open overview directly
self.moveToState("review")
def unloadProfile(self):
self.col = None
@ -633,8 +635,7 @@ Debug info:\n%s""") % traceback.format_exc(), help="DeckErrors")
def onCardLayout(self):
from aqt.clayout import CardLayout
CardLayout(self, self.reviewer.card.note(), type=1,
ord=self.reviewer.card.ord)
CardLayout(self, self.reviewer.card.note(), ord=self.reviewer.card.ord)
def onDeckOpts(self):
import aqt.deckopts
@ -797,7 +798,7 @@ Please choose a new deck name:"""))
self.form.actionDelete.setEnabled(True)
self.form.actionBuryNote.setEnabled(True)
self.form.actionEditCurrent.setEnabled(True)
self.form.actionEditdeck.setEnabled(True)
self.form.actionBrowse.setEnabled(True)
self.updateMarkAction()
runHook("enableCardMenuItems")

View file

@ -196,4 +196,4 @@ class AddModel(QDialog):
QDialog.accept(self)
def onHelp(self):
aqt.openHelp("AddModel")
openHelp("AddModel")

View file

@ -15,7 +15,7 @@ class Models(QDialog):
self.form = aqt.forms.models.Ui_Dialog()
self.form.setupUi(self)
self.connect(self.form.buttonBox, SIGNAL("helpRequested()"),
lambda: aqt.openHelp("Models"))
lambda: openHelp("Models"))
self.setupModels()
self.exec_()

View file

@ -6,7 +6,7 @@ import simplejson
from aqt.qt import *
from anki.consts import NEW_CARDS_RANDOM
from anki.hooks import addHook
from aqt.utils import limitedCount, showInfo
from aqt.utils import showInfo
class Overview(object):
"Deck overview."
@ -57,9 +57,6 @@ class Overview(object):
############################################################
def _renderPage(self):
css = self.mw.sharedCSS + self._overviewCSS
fc = self._ovForecast()
tbl = self._overviewTable()
but = self.mw.button
deck = self.mw.col.decks.current()
sid = deck.get("sharedFrom")
@ -67,85 +64,67 @@ class Overview(object):
shareLink = '<a class=smallLink href="review">Reviews and Updates</a>'
else:
shareLink = ""
header = ""
self.web.stdHtml(self._overviewBody % dict(
title=_("Overview"),
table=tbl,
fcsub=_("Reviews over next two weeks"),
print self._body % dict(
deck=deck['name'],
shareLink=shareLink,
desc="",
header=header,
fcdata=fc,
), css)
desc=self._desc(deck),
table=self._table())
self.web.stdHtml(self._body % dict(
deck=deck['name'],
shareLink=shareLink,
desc=self._desc(deck),
table=self._table()
), self.mw.sharedCSS + self._css)
_overviewBody = """
%(header)s
def _desc(self, deck):
desc = deck.get("desc", "")
if not desc:
return ""
if len(desc) < 160:
return '<div class="descfont description">%s</div>' % desc
else:
return '''
<div class="descfont description descmid" id=shortdesc>%s\
<a href=# onclick="$('shortdesc').hide();$('fulldesc').show();">...More</a></div>
<div class="descfont description descmid" id=fulldesc>%s</div>''' % (
desc[:160], desc)
def _table(self):
counts = self.mw.col.sched.repCounts()
finished = not sum(counts)
but = self.mw.button
if finished:
return '<div class=fin style="white-space: pre-wrap;">%s</div>' % (
self.mw.col.sched.finishedMsg())
else:
return '''
<table width=300 cellpadding=5>
<tr><td align=center valign=top>
<table cellspacing=5>
<tr><td>%s:</td><td><b><font color=#00a>%s</font></b></td></tr>
<tr><td>%s:</td><td><b><font color=#a00>%s</font></b></td></tr>
<tr><td>%s:</td><td><b><font color=#0a0>%s</font></b></td></tr>
</table>
</td><td>%s</td></tr></table>''' % (_("New"), counts[0],
_("In Learning"), counts[1],
_("To Review"), counts[2],
but("study", _("Study")))
_body = """
<center>
<h3>%(deck)s</h3>
%(shareLink)s
%(desc)s
<p>
<div id="placeholder" style="width:350px; height:100px;"></div>
<span class=sub>%(fcsub)s</span>
<p>
%(table)s
</center>
<script>
$("#study").focus();
$(function () {
var d = %(fcdata)s;
if (typeof(d) !== "string") {
$.plot($("#placeholder"), [
{ data: d, bars: { show: true, barWidth: 0.8 }, color: "#0c0" }
], {
xaxis: { ticks: [[0.4, "Today"]] },
yaxis: { tickDecimals: 0 }
});
} else {
$("#placeholder").text(d);
$(".sub").hide();
}
});
</script>
"""
_overviewCSS = """
.due { text-align: right; }
.new { text-align: right; }
.sub { font-size: 80%; color: #555; }
.smallLink { font-size: 12px; }
_css = """
.smallLink { font-size: 10px; }
h3 { margin-bottom: 0; }
.fin { font-size: 12px; font-weight: normal; }
td { font-size: 14px; }
"""
def _overviewTable(self):
return ""
but = self.mw.button
buf = "<table cellspacing=0 cellpadding=3 width=400>"
buf += "<tr><th></th><th align=right>%s</th>" % _("Due")
buf += "<th align=right>%s</th><th></th></tr>" % _("New")
line = "<tr><td><b>%s</b></td><td class=due>%s</td>"
line += "<td class=new>%s</td><td>%s</td></tr>"
buf += line % (
"<a href=chgrp>%s</a>" % _("Selected Groups"),
counts[0], counts[1],
but("study", _("Study"), _("s"), "gbut", id="study") +
but("cram", _("Cram"), "c"))
buf += line % (
_("Whole Deck"),
counts[2], counts[3],
but("opts", _("Study Options")))
buf += "</table>"
return buf
def _ovOpts(self):
return ""
# Data
##########################################################################
def _ovForecast(self):
fc = self.mw.col.sched.dueForecast(14)
if not sum(fc):
return "'%s'" % _('No cards due in next two weeks')
return simplejson.dumps(tuple(enumerate(fc)))

View file

@ -13,12 +13,12 @@ class Preferences(QDialog):
def __init__(self, mw):
QDialog.__init__(self, mw, Qt.Window)
self.mw = mw
self.config = mw.config
self.config = mw.pm.config
self.form = aqt.forms.preferences.Ui_Preferences()
self.form.setupUi(self)
self.needDeckClose = False
self.connect(self.form.buttonBox, SIGNAL("helpRequested()"),
lambda: aqt.openHelp("Preferences"))
lambda: openHelp("Preferences"))
self.setupLang()
self.setupNetwork()
self.setupBackup()

View file

@ -89,36 +89,18 @@ class Reviewer(object):
##########################################################################
_revHtml = """
<table width=100%% height=100%%><tr valign=%(align)s><td>
<div id=q></div>
<hr class=inv id=midhr>
<div id=a></div>
<div id=filler></div>
</td></tr></table>
<a id=ansbut class="ansbut ansbutbig" href=ans onclick="_showans();">
<div class=ansbuttxt>%(showans)s</div>
</a>
<div id=easebuts>
</div>
<div id=qa></div>
<script>
var ankiPlatform = "desktop";
var hideq;
var ans;
var typeans;
function _updateQA (qa) {
hideq = qa[4];
function _updateQA (q) {
location.hash = "";
$("#q").html(qa[0]);
if (hideq) {
ans = qa[1];
$("#a").html("");
} else {
$("#a").html(qa[1]).addClass("inv");
}
$("#midhr").addClass("inv");
$("#easebuts").html(qa[2]).addClass("inv");
$("#ansbut").show();
$("body").removeClass().addClass(qa[3]);
$("#qa").html(q[0]);
$("#qa:first").css("height", "100%%");
//$("#easebuts").html(q[1]).addClass("inv");
//$("#ansbut").show();
typeans = document.getElementById("typeans");
if (typeans) {
typeans.focus();
@ -128,7 +110,8 @@ function _updateQA (qa) {
onQuestion();
}
};
function _showans () {
function _showans (a) {
$("#qa").html(a);
if (typeans) {
py.link("typeans:"+typeans.value);
}
@ -139,7 +122,7 @@ function _showans () {
} else {
location.hash = "a";
}
$("#ansbut").hide();
//$("#ansbut").hide();
$("#defease").focus();
// user hook
if (typeof(onAnswer) === "function") {
@ -167,8 +150,8 @@ $(".ansbut").focus();
def _initWeb(self):
self.web.stdHtml(self._revHtml % dict(
align="middle" if self.mw.config['centerQA'] else "top",
showans=_("Show Answer")), self._styles(),
bodyID="card",
loadCB=lambda x: self._showQuestion())
# Showing the question (and preparing answer)
@ -181,15 +164,14 @@ $(".ansbut").focus();
c = self.card
q = c.q()
a = c.a()
if self.mw.config['autoplaySounds']:
if self.mw.pm.profile['autoplay']:
playFromText(q)
# render
esc = self.mw.col.media.escapeImages
q=esc(mungeQA(q)) + self.typeAnsInput()
a=esc(mungeQA(a))
self.web.eval("_updateQA(%s);" % simplejson.dumps(
[q, a, self._answerButtons(), c.cssClass(),
c.template()['hideQ']]))
(q, self._answerButtons())))
runHook('showQuestion')
# Showing the answer
@ -199,7 +181,7 @@ $(".ansbut").focus();
self.state = "answer"
c = self.card
a = c.a()
if self.mw.config['autoplaySounds']:
if self.mw.pm.profile['autoplay']:
playFromText(a)
# render
runHook('showAnswer')
@ -238,7 +220,7 @@ $(".ansbut").focus();
return buf
def _buttonTime(self, i, green):
if self.mw.config['suppressEstimates']:
if self.mw.pm.profile['showDueTimes']:
return ""
txt = self.mw.col.sched.nextIvlStr(self.card, i+1, True)
if i == 0:
@ -335,9 +317,7 @@ div.ansbuttxt {
position: relative; top: 25%;
}
div#q, div#a {
margin: 0px;
}
body { margin:1em; }
#easebuts {
bottom: 1em;
@ -373,7 +353,6 @@ div#filler {
def _styles(self):
css = self.mw.sharedCSS
css += self.mw.col.models.css()
css += self._css
return css
@ -385,10 +364,10 @@ div#filler {
def typeAns(self):
"None if answer typing disabled."
return self.card.template()['typeAns']
self.card.template()['typeAns']
def typeAnsInput(self):
if self.typeAns() is None:
if not self.typeAns():
return ""
return """
<center>
@ -412,6 +391,8 @@ div#filler {
self.web.eval("_processTyped(%s);" % simplejson.dumps(res))
def getFont(self):
print "fix getFont()"
return ("arial", 20)
f = self.card.model().fields[self.typeAns()]
return (f['font'], f['qsize'])
@ -549,7 +530,7 @@ div#filler {
addWgt(vertSep())
class QClickableProgress(QProgressBar):
def mouseReleaseEvent(self, evt):
aqt.openHelp("ProgressBars")
openHelp("ProgressBars")
progressBarSize = (50, 14)
self.progressBar = QClickableProgress()
self.progressBar.setFixedSize(*progressBarSize)
@ -561,6 +542,7 @@ div#filler {
addWgt(self.progressBar, 0)
def _showStatus(self):
return
self._showStatusWidgets(True)
self._updateRemaining()
self._updateProgress()
@ -569,6 +551,7 @@ div#filler {
self._showStatusWidgets(False)
def _showStatusWidgets(self, shown=True):
return
for w in self._statusWidgets:
w.setShown(shown)
self.mw.form.statusbar.hideOrShow()

View file

@ -26,7 +26,7 @@ class StudyOptions(QDialog):
0, c.revCardOrderLabels().values())
self.connect(self.form.buttonBox,
SIGNAL("helpRequested()"),
lambda: aqt.openHelp("StudyOptions"))
lambda: openHelp("StudyOptions"))
def load(self):
f = self.form

View file

@ -25,8 +25,8 @@ class SyncManager(object):
# vet input
if interactive:
self.ensureSyncParams()
u=self.config['syncUsername']
p=self.config['syncPassword']
u=self.pm.profile['syncUsername']
p=self.pm.profile['syncPassword']
if not u or not p:
return
if self.deck:
@ -35,7 +35,7 @@ class SyncManager(object):
return
if self.deck and not self.deck.syncName:
if interactive:
if (not self.config['mediaLocation']
if (not self.pm.profile['mediaLocation']
and self.deck.db.scalar("select 1 from media limit 1")):
showInfo(_("""\
Syncing sounds and images requires a free file synchronization service like \
@ -105,7 +105,7 @@ will sync automatically from then on."""))
def decksToSync(self):
ok = []
for d in self.config['recentDeckPaths']:
for d in self.pm.profile['recentDeckPaths']:
if os.path.exists(d):
ok.append(d)
return ok
@ -159,7 +159,7 @@ Are you sure?""" % deckName),
elif self.loadAfterSync and self.deckPath:
if self.loadAfterSync == 2:
name = re.sub("[<>]", "", self.syncName)
p = os.path.join(self.config['documentDir'], name + ".anki")
p = os.path.join(self.pm.profile['documentDir'], name + ".anki")
shutil.copy2(self.deckPath, p)
self.deckPath = p
# since we've moved the deck, we have to set sync path
@ -195,7 +195,7 @@ Are you sure?""" % deckName),
self.syncName = name
if name:
# name chosen
p = os.path.join(self.config['documentDir'], name + ".anki")
p = os.path.join(self.pm.profile['documentDir'], name + ".anki")
if os.path.exists(p):
d = askUserDialog(_("""\
This deck already exists on your computer. Overwrite the local copy?"""),
@ -237,7 +237,7 @@ This deck already exists on your computer. Overwrite the local copy?"""),
self.setStatus("")
def badUserPass(self):
aqt.preferences.Preferences(self, self.config).dialog.tabWidget.\
aqt.preferences.Preferences(self, self.pm.profile).dialog.tabWidget.\
setCurrentIndex(1)
def openSyncProgress(self):
@ -282,7 +282,7 @@ This deck already exists on your computer. Overwrite the local copy?"""),
self.updateProgress(label=s % (val / 1024))
def ensureSyncParams(self):
if not self.config['syncUsername'] or not self.config['syncPassword']:
if not self.pm.profile['syncUsername'] or not self.pm.profile['syncPassword']:
d = QDialog(self)
vbox = QVBoxLayout()
l = QLabel(_(
@ -310,8 +310,8 @@ This deck already exists on your computer. Overwrite the local copy?"""),
vbox.addWidget(bb)
d.setLayout(vbox)
d.exec_()
self.config['syncUsername'] = unicode(user.text())
self.config['syncPassword'] = unicode(passwd.text())
self.pm.profile['syncUsername'] = unicode(user.text())
self.pm.profile['syncPassword'] = unicode(passwd.text())
# Synchronising a deck with a public server

View file

@ -17,7 +17,7 @@ class LatestVersionFinder(QThread):
print "autoupdate"
return
self.main = main
self.config = main.config
self.config = main.pm.profile
plat=sys.platform
pver=platform.platform()
d = {"ver": aqt.appVersion,

View file

@ -13,7 +13,7 @@ def openHelp(name):
name = name[0] + ".html#" + name[1]
else:
name = name + ".html"
QDesktopServices.openUrl(QUrl(appHelpSite + name))
QDesktopServices.openUrl(QUrl(aqt.appHelpSite + name))
def openLink(link):
QDesktopServices.openUrl(QUrl(link))
@ -44,7 +44,7 @@ def showInfo(text, parent=None, help="", type="info"):
b.setDefault(True)
if help:
b = mb.addButton(QMessageBox.Help)
b.connect(b, SIGNAL("clicked()"), lambda: aqt.openHelp(help))
b.connect(b, SIGNAL("clicked()"), lambda: openHelp(help))
b.setAutoDefault(False)
return mb.exec_()
@ -84,7 +84,8 @@ def askUser(text, parent=None, help="", defaultno=False):
r = QMessageBox.question(parent, "Anki", text, sb,
default)
if r == QMessageBox.Help:
aqt.openHelp(help)
openHelp(help)
else:
break
return r == QMessageBox.Yes
@ -115,7 +116,7 @@ class ButtonedDialog(QMessageBox):
but = self.clickedButton().text()
if but == "Help":
# FIXME stop dialog closing?
aqt.openHelp(self.help)
openHelp(self.help)
return self.clickedButton().text()
def setDefault(self, idx):
@ -166,7 +167,7 @@ class GetTextDialog(QDialog):
return QDialog.reject(self)
def helpRequested(self):
aqt.openHelp(self.help)
openHelp(self.help)
def getText(prompt, parent=None, help=None, edit=None, default=u"", title="Anki"):
if not parent:
@ -347,11 +348,6 @@ def maybeHideClose(bbox):
if b:
bbox.removeButton(b)
def limitedCount(count):
if count >= 1000:
return "1000+"
return str(count)
# Tooltips
######################################################################

View file

@ -83,13 +83,13 @@ class AnkiWebView(QWebView):
if loadCB:
self._loadFinishedCB = loadCB
QWebView.setHtml(self, html)
def stdHtml(self, body, css="", bodyClass="", loadCB=None):
def stdHtml(self, body, css="", bodyID="", loadCB=None):
self.setHtml("""
<html><head><style>%s</style>
<script>%s</script>
</head>
<body class="%s">%s</body></html>""" % (
css, anki.js.all, bodyClass, body), loadCB)
<body id="%s">%s</body></html>""" % (
css, anki.js.all, bodyID, body), loadCB)
# ensure we're focused
self.setFocus()
def setBridge(self, bridge):

View file

@ -6,46 +6,27 @@
<rect>
<x>0</x>
<y>0</y>
<width>759</width>
<height>560</height>
<width>1311</width>
<height>658</height>
</rect>
</property>
<property name="windowTitle">
<string/>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<widget class="QWidget" name="">
<property name="geometry">
<rect>
<x>20</x>
<y>120</y>
<width>360</width>
<height>366</height>
</rect>
</property>
<property name="childrenCollapsible">
<bool>false</bool>
</property>
<widget class="QWidget" name="layoutWidget">
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>2</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Appearance</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QGridLayout" name="templateLayout">
<property name="verticalSpacing">
<number>6</number>
</property>
<item row="3" column="1">
<item row="1" column="1">
<widget class="QTextEdit" name="cardQuestion">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
@ -67,7 +48,7 @@
</property>
</widget>
</item>
<item row="4" column="1">
<item row="2" column="1">
<widget class="QTextEdit" name="cardAnswer">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
@ -92,7 +73,7 @@
</property>
</widget>
</item>
<item row="3" column="0" rowspan="2">
<item row="1" column="0" rowspan="2">
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<widget class="QLabel" name="label_14">
@ -107,17 +88,6 @@
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="flipButton">
<property name="text">
<string>Flip</string>
</property>
<property name="icon">
<iconset resource="icons.qrc">
<normaloff>:/icons/multisynk.png</normaloff>:/icons/multisynk.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_15">
<property name="text">
@ -133,398 +103,40 @@
</item>
</layout>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_13">
<property name="text">
<string>Alignment</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QComboBox" name="alignment"/>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_16">
<property name="text">
<string>Background</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QPushButton" name="background">
<property name="text">
<string/>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="templateType">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QComboBox" name="cardList">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="editTemplates">
<property name="text">
<string>&amp;Manage...</string>
</property>
<property name="shortcut">
<string>Ctrl+M</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="QComboBox" name="typeAnswer"/>
</item>
<item>
<widget class="QCheckBox" name="questionInAnswer">
<property name="text">
<string>Hide the question when showing answer</string>
</widget>
<widget class="QComboBox" name="typeAnswer">
<property name="geometry">
<rect>
<x>20</x>
<y>500</y>
<width>366</width>
<height>26</height>
</rect>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="allowEmptyAnswer">
<property name="text">
<string>Add cards even if answer is blank</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="clozectx">
<property name="geometry">
<rect>
<x>20</x>
<y>540</y>
<width>371</width>
<height>20</height>
</rect>
</property>
<property name="text">
<string>Include context in cloze answers</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_2">
<attribute name="title">
<string>Fields</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QListWidget" name="fieldList">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>50</width>
<height>60</height>
</size>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QPushButton" name="fieldAdd">
<property name="text">
<string>&amp;Add</string>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="fieldUp">
<property name="toolTip">
<string>Move selected field up</string>
</property>
<property name="text">
<string>&amp;Up</string>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="fieldDown">
<property name="toolTip">
<string>Move selected field down</string>
</property>
<property name="text">
<string>Dow&amp;n</string>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="fieldDelete">
<property name="text">
<string>&amp;Delete</string>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QGridLayout" name="_2">
<item row="0" column="1">
<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>Font</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Size</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Color</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QFontComboBox" name="fontFamily">
<property name="minimumSize">
<size>
<width>0</width>
<height>25</height>
</size>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QPushButton" name="fontColour">
<property name="autoFillBackground">
<bool>true</bool>
</property>
<property name="text">
<string/>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
<item row="2" column="1">
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Reviewing</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="fontSize">
<property name="minimum">
<number>5</number>
</property>
<property name="maximum">
<number>300</number>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Editing</string>
</property>
</widget>
</item>
<item row="0" column="4">
<widget class="QSpinBox" name="fontSizeEdit">
<property name="minimum">
<number>5</number>
</property>
<property name="maximum">
<number>300</number>
</property>
</widget>
</item>
<item row="0" column="5">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>1</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_18">
<property name="text">
<string>Options</string>
</property>
</widget>
</item>
<item row="8" column="1">
<widget class="QCheckBox" name="rtl">
<property name="text">
<string>Reverse text direction (RTL)</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QCheckBox" name="fieldRequired">
<property name="text">
<string>Require text in field</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QCheckBox" name="fieldUnique">
<property name="text">
<string>Prevent duplicates</string>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QCheckBox" name="preserveWhitespace">
<property name="text">
<string>Preserve whitespace</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QCheckBox" name="sticky">
<property name="text">
<string>Keep previous input when adding cards</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Close|QDialogButtonBox::Help</set>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QGroupBox" name="previewGroup">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Preview</string>
</property>
<layout class="QVBoxLayout" name="_3">
<property name="margin">
<number>6</number>
</property>
<item>
<widget class="QWebView" name="preview">
<property name="geometry">
<rect>
<x>450</x>
<y>90</y>
<width>308</width>
<height>499</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>1</horstretch>
@ -540,12 +152,6 @@
</url>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
@ -555,70 +161,11 @@
</customwidget>
</customwidgets>
<tabstops>
<tabstop>tabWidget</tabstop>
<tabstop>cardList</tabstop>
<tabstop>editTemplates</tabstop>
<tabstop>cardQuestion</tabstop>
<tabstop>flipButton</tabstop>
<tabstop>cardAnswer</tabstop>
<tabstop>alignment</tabstop>
<tabstop>background</tabstop>
<tabstop>typeAnswer</tabstop>
<tabstop>questionInAnswer</tabstop>
<tabstop>allowEmptyAnswer</tabstop>
<tabstop>clozectx</tabstop>
<tabstop>buttonBox</tabstop>
<tabstop>fieldList</tabstop>
<tabstop>fieldAdd</tabstop>
<tabstop>fieldUp</tabstop>
<tabstop>fieldDown</tabstop>
<tabstop>fieldDelete</tabstop>
<tabstop>fieldName</tabstop>
<tabstop>fontFamily</tabstop>
<tabstop>fontSize</tabstop>
<tabstop>fontSizeEdit</tabstop>
<tabstop>fontColour</tabstop>
<tabstop>fieldUnique</tabstop>
<tabstop>fieldRequired</tabstop>
<tabstop>sticky</tabstop>
<tabstop>preserveWhitespace</tabstop>
<tabstop>rtl</tabstop>
</tabstops>
<resources>
<include location="icons.qrc"/>
</resources>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>Dialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>Dialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
<connections/>
</ui>

243
designer/fields.ui Normal file
View file

@ -0,0 +1,243 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>372</width>
<height>340</height>
</rect>
</property>
<property name="windowTitle">
<string>Fields</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QListWidget" name="fieldList">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>50</width>
<height>60</height>
</size>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_3">
<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>
</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>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="fieldUp">
<property name="toolTip">
<string>Move selected field up</string>
</property>
<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>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="fieldDown">
<property name="toolTip">
<string>Move selected field down</string>
</property>
<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>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</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>Font</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QFontComboBox" name="fontFamily">
<property name="minimumSize">
<size>
<width>0</width>
<height>25</height>
</size>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_18">
<property name="text">
<string>Options</string>
</property>
</widget>
</item>
<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">
<widget class="QSpinBox" name="fontSize">
<property name="minimum">
<number>5</number>
</property>
<property name="maximum">
<number>300</number>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="sticky">
<property name="text">
<string>Remember last input</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Close|QDialogButtonBox::Help</set>
</property>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>fieldList</tabstop>
<tabstop>fieldAdd</tabstop>
<tabstop>fieldDelete</tabstop>
<tabstop>fieldUp</tabstop>
<tabstop>fieldDown</tabstop>
<tabstop>fieldName</tabstop>
<tabstop>fontFamily</tabstop>
<tabstop>fontSize</tabstop>
<tabstop>sticky</tabstop>
<tabstop>rtl</tabstop>
<tabstop>buttonBox</tabstop>
</tabstops>
<resources>
<include location="icons.qrc"/>
</resources>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>Dialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>Dialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View file

@ -1,6 +1,9 @@
<RCC>
<qresource prefix="/">
<file>icons/arrow-up.png</file>
<file>icons/arrow-down.png</file>
<file>icons/blue.png</file>
<file>icons/gears.png</file>
<file>icons/both.png</file>
<file>icons/green.png</file>
<file>icons/clock-icon.png</file>

BIN
designer/icons/gears.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 701 B

82
designer/preview.ui Normal file
View file

@ -0,0 +1,82 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>335</width>
<height>282</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Front Preview</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QWebView" name="front">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="url">
<url>
<string>about:blank</string>
</url>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Back Preview</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QWebView" name="back">
<property name="url">
<url>
<string>about:blank</string>
</url>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>QWebView</class>
<extends>QWidget</extends>
<header>QtWebKit/QWebView</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

68
designer/template.ui Normal file
View file

@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>279</width>
<height>251</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Front Template</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QTextEdit" name="front"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>10</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Back Template</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QTextEdit" name="back"/>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources>
<include location="icons.qrc"/>
</resources>
<connections/>
</ui>