-
Notifications
You must be signed in to change notification settings - Fork 0
/
addon.py
156 lines (123 loc) · 5.04 KB
/
addon.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# -*- coding: utf-8 -*-
# See github page to report issues or to contribute:
# https://github.com/seltzy/anki-trivial-clozes
#
# A word of thanks to Arthaey, whose codebase helped me get this started:
# https://github.com/Arthaey/anki-cloze-blanks
#
import re
from PyQt4.QtCore import SIGNAL
from PyQt4.QtGui import QAction, QProgressDialog
from anki.hooks import addHook, wrap
from anki.utils import stripHTML
from aqt import mw
from aqt.editor import Editor
from aqt.utils import askUser, showInfo
FEATURES = {
"unhideCloze" : True,
"forNewCards" : False, # TODO: not yet implemented
"forExistingCards" : True,
"forSelectedCards" : True,
"nonBreakingSpaces" : True,
}
BLANK = "_"
CARD_NAMES_SET = ["Cloze", "Epic Sentence"]
TEXT_FIELDS_SET = ["Text", "Sentence", "Front"]
ADD_BLANKS_MENU_TEXT = _(u"Add blanks to cloze notes")
UNHIDE_CLOZE_MENU_TEXT = _(u"Unhide clozed text in cloze notes")
CLOZE_WORDS_MENU_TEXT = _(u"Make each word into a cloze")
def _forExistingCards(prompt, funcForExistingCards):
if not askUser(_(prompt)):
return
for name in CARD_NAMES_SET:
model = mw.col.models.byName(name);
nids = mw.col.models.nids(model)
funcForExistingCards(nids)
def unhideClozeTextForSelectedCards(browser):
nids = browser.selectedNotes()
_unhideClozeTextInNotes(nids)
def processClozedText(text):
# Only update clozes that do not already have hint text.
# NOTE: Watch out for recursive cloze replacement
regex = r"{{c(\d+)::(([^:{}]+?)(::[^:{}]*?)?)}}"
return re.subn(regex, _unhideClozeTextMatch, text)
def _unhideClozeTextInField(note, text):
newText, num = processClozedText(text)
return newText
def updateClozeTextOnNoteField(note, currentField):
original = note.fields[currentField]
if note.hasTag("uncloze"):
newText = _unhideClozeTextInField(note, original)
if (note.fields[currentField] != newText):
note.fields[currentField] = newText
# logging.debug('Changed field from "'+unicode(original)+'" to "'+unicode(note.fields[currentField])+'"')
return newText
def updateClozeTextFocusLost(modifiedOrNot, note, currentField):
return updateClozeTextOnNoteField(note, currentField)
def updateClozeTextFocusGained(note, currentField):
return updateClozeTextOnNoteField(note, currentField)
def updateClozeTextOnNote(note):
text_fields = set(note.keys()).intersection(TEXT_FIELDS_SET)
for field in text_fields:
updateClozeTextOnNoteField(note, field)
def updateClozeTextTagsUpdated(note):
return updateClozeTextOnNote(note)
def updateClozeTextNoteChanged(nid):
note = mw.col.getNote(nid)
updateClozeTextOnNote(note)
addHook("editFocusGained", updateClozeTextFocusGained)
addHook("editFocusLost", updateClozeTextFocusLost)
addHook("noteChanged", updateClozeTextNoteChanged)
# addHook("tagsUpdated", updateClozeTextTagsUpdated)
def unhideClozeTextForExistingCards():
_forExistingCards(u"Unhide cloze text for ALL cloze cards?", _unhideClozeTextInNotes)
def _unhideClozeTextInNotes(nids):
_updateExistingCards(UNHIDE_CLOZE_MENU_TEXT, nids, processClozedText)
def _unhideClozeTextMatch(match):
num = match.group(1)
text = match.group(3)
return _unhideClozeText(num, text)
def _unhideClozeText(num, text):
# Need to escape curly-braces.
return u"{{{{c{0}::{1}::{2}}}}}".format(num, text, text)
def _updateExistingCards(checkpoint, nids, processFunc):
updatedCount = 0
mw.checkpoint(checkpoint)
mw.progress.start()
for nid in nids:
note = mw.col.getNote(nid)
text_fields = set(note.keys()).intersection(TEXT_FIELDS_SET)
if len(text_fields) == 0:
continue
text_field = text_fields.pop()
text = note[text_field]
newText, num = processFunc(text)
if text != newText:
note[text_field] = newText
note.flush()
updatedCount += num
mw.progress.finish()
mw.reset()
spacesNotice = ""
if FEATURES["nonBreakingSpaces"]:
spacesNotice = " and replaced spaces inside clozes with non-breaking spaces"
showInfo(u"Updated {0} cards (from {1} cloze notes){2}.".format(
updatedCount, len(nids), spacesNotice))
def _setupBrowserMenu(browser):
unhideCloze = QAction(UNHIDE_CLOZE_MENU_TEXT, browser)
browser.connect(unhideCloze, SIGNAL("triggered()"),
lambda b = browser: unhideClozeTextForSelectedCards(b))
browser.form.menuEdit.addSeparator()
if FEATURES["unhideCloze"]:
browser.form.menuEdit.addAction(unhideCloze)
if FEATURES["forNewCards"]:
Editor.onCloze = wrap(Editor.onCloze, addClozeBlanksToNewCards, "before")
# TODO: support making each word into a cloze
if FEATURES["forExistingCards"]:
unhideCloze = QAction(UNHIDE_CLOZE_MENU_TEXT, mw)
mw.connect(unhideCloze, SIGNAL("triggered()"), unhideClozeTextForExistingCards)
mw.form.menuTools.addSeparator()
if FEATURES["unhideCloze"]:
mw.form.menuTools.addAction(unhideCloze)
if FEATURES["forSelectedCards"]:
addHook("browser.setupMenus", _setupBrowserMenu)