New upstream version 2.1.0+dfsg~b16
Julian Gilbey
6 years ago
0 | Anki's logo is copyright Alex Fraser, and is licensed under the AGPL3 | |
1 | like the rest of Anki's code, but with extra provisions to allow more | |
2 | liberal use of the logo under limited conditions. | |
0 | Anki's logo is copyright Alex Fraser, and is licensed under the AGPL3 like the | |
1 | rest of Anki's code. | |
3 | 2 | |
4 | Under the following conditions, Anki's logo may be included in blogs, | |
5 | newspaper articles, books, videos and other such material about Anki. | |
3 | The logo is also available under a limited alternative license for inclusion | |
4 | in books, blogs, videos and so on. If the following conditions are met, you | |
5 | may use the logo in your work without the need to license your work under an | |
6 | AGPL3-compatible license: | |
6 | 7 | |
7 | 8 | * The logo must be used to refer to Anki, AnkiMobile or AnkiDroid, |
8 | 9 | and a link to https://apps.ankiweb.net must be provided. When your |
9 | 10 | content is focused specifically on AnkiDroid, a link to |
10 | 11 | https://play.google.com/store/apps/details?id=com.ichi2.anki&hl=en |
11 | 12 | may be provided instead of the first link. |
12 | * The branding of your website or publication must be more prominent | |
13 | than the Anki logo, to make it clear that the text/video/etc you | |
13 | * The work must make it clear that the text/video/etc you | |
14 | 14 | are publishing is your own content and not something originating |
15 | 15 | from the Anki project. |
16 | 16 | * The logo must be used unmodified - no cropping, changing of colours |
6 | 6 | if sys.version_info[0] < 3 or sys.version_info[1] < 6: |
7 | 7 | raise Exception("Anki requires Python 3.6+") |
8 | 8 | |
9 | version="2.1.0beta15" # build scripts grep this line, so preserve formatting | |
9 | if sys.getfilesystemencoding().lower() in ("ascii", "ansi_x3.4-1968"): | |
10 | raise Exception("Anki requires a UTF-8 locale.") | |
11 | ||
12 | version="2.1.0beta16" # build scripts grep this line, so preserve formatting | |
10 | 13 | from anki.storage import Collection |
11 | 14 | __all__ = ["Collection"] |
44 | 44 | |
45 | 45 | key = _("Cards in Plain Text") |
46 | 46 | ext = ".txt" |
47 | hideTags = True | |
48 | 47 | |
49 | 48 | def __init__(self, col): |
50 | 49 | Exporter.__init__(self, col) |
70 | 69 | |
71 | 70 | key = _("Notes in Plain Text") |
72 | 71 | ext = ".txt" |
72 | includeTags = True | |
73 | 73 | |
74 | 74 | def __init__(self, col): |
75 | 75 | Exporter.__init__(self, col) |
76 | 76 | self.includeID = False |
77 | self.includeTags = True | |
78 | 77 | |
79 | 78 | def doExport(self, file): |
80 | 79 | cardIds = self.cardIds() |
106 | 105 | |
107 | 106 | key = _("Anki 2.0 Deck") |
108 | 107 | ext = ".anki2" |
108 | includeSched = False | |
109 | includeMedia = True | |
109 | 110 | |
110 | 111 | def __init__(self, col): |
111 | 112 | Exporter.__init__(self, col) |
112 | self.includeSched = False | |
113 | self.includeMedia = True | |
114 | 113 | |
115 | 114 | def exportInto(self, path): |
116 | 115 | # create a new collection at the target |
257 | 256 | def exportInto(self, path): |
258 | 257 | # open a zip file |
259 | 258 | z = zipfile.ZipFile(path, "w", zipfile.ZIP_DEFLATED, allowZip64=True) |
260 | # if all decks and scheduling included, full export | |
261 | if self.includeSched and not self.did: | |
262 | media = self.exportVerbatim(z) | |
263 | else: | |
264 | # otherwise, filter | |
265 | media = self.exportFiltered(z, path) | |
259 | media = self.doExport(z, path) | |
266 | 260 | # media map |
267 | 261 | z.writestr("media", json.dumps(media)) |
268 | 262 | z.close() |
269 | 263 | |
270 | def exportFiltered(self, z, path): | |
264 | def doExport(self, z, path): | |
271 | 265 | # export into the anki2 file |
272 | 266 | colfile = path.replace(".apkg", ".anki2") |
273 | 267 | AnkiExporter.exportInto(self, colfile) |
284 | 278 | shutil.rmtree(path.replace(".apkg", ".media")) |
285 | 279 | return media |
286 | 280 | |
287 | def exportVerbatim(self, z): | |
288 | # close our deck & write it into the zip file, and reopen | |
289 | self.count = self.col.cardCount() | |
290 | self.col.close() | |
291 | z.write(self.col.path, "collection.anki2") | |
292 | self.col.reopen() | |
293 | # copy all media | |
294 | if not self.includeMedia: | |
295 | return {} | |
296 | mdir = self.col.media.dir() | |
297 | return self._exportMedia(z, os.listdir(mdir), mdir) | |
298 | ||
299 | 281 | def _exportMedia(self, z, files, fdir): |
300 | 282 | media = {} |
301 | 283 | for c, file in enumerate(files): |
318 | 300 | # is zipped up |
319 | 301 | pass |
320 | 302 | |
303 | # Collection package | |
304 | ###################################################################### | |
305 | ||
306 | class AnkiCollectionPackageExporter(AnkiPackageExporter): | |
307 | ||
308 | key = _("Anki Collection Package") | |
309 | ext = ".colpkg" | |
310 | verbatim = True | |
311 | includeSched = None | |
312 | ||
313 | def __init__(self, col): | |
314 | AnkiPackageExporter.__init__(self, col) | |
315 | ||
316 | def doExport(self, z, path): | |
317 | # close our deck & write it into the zip file, and reopen | |
318 | self.count = self.col.cardCount() | |
319 | self.col.close() | |
320 | z.write(self.col.path, "collection.anki2") | |
321 | self.col.reopen() | |
322 | # copy all media | |
323 | if not self.includeMedia: | |
324 | return {} | |
325 | mdir = self.col.media.dir() | |
326 | return self._exportMedia(z, os.listdir(mdir), mdir) | |
327 | ||
321 | 328 | # Export modules |
322 | 329 | ########################################################################## |
323 | 330 | |
325 | 332 | def id(obj): |
326 | 333 | return ("%s (*%s)" % (obj.key, obj.ext), obj) |
327 | 334 | exps = [ |
335 | id(AnkiCollectionPackageExporter), | |
328 | 336 | id(AnkiPackageExporter), |
329 | 337 | id(TextNoteExporter), |
330 | 338 | id(TextCardExporter), |
393 | 393 | else: |
394 | 394 | # wildcard |
395 | 395 | ids = set() |
396 | # should use re.escape in the future | |
397 | val = val.replace("*", ".*") | |
398 | val = val.replace("+", "\\+") | |
396 | val = re.escape(val).replace(r"\*", ".*") | |
399 | 397 | for d in self.col.decks.all(): |
400 | 398 | if re.match("(?i)"+val, d['name']): |
401 | 399 | ids.update(dids(d['id'])) |
11 | 11 | |
12 | 12 | Importers = ( |
13 | 13 | (_("Text separated by tabs or semicolons (*)"), TextImporter), |
14 | (_("Packaged Anki Deck (*.apkg *.zip)"), AnkiPackageImporter), | |
14 | (_("Packaged Anki Deck/Collection (*.apkg *.colpkg *.zip)"), AnkiPackageImporter), | |
15 | 15 | (_("Mnemosyne 2.0 Deck (*.db)"), MnemosyneImporter), |
16 | 16 | (_("Supermemo XML export (*.xml)"), SupermemoXmlImporter), |
17 | 17 | (_("Pauker 1.8 Lesson (*.pau.gz)"), PaukerImporter), |
196 | 196 | return m.group(1) |
197 | 197 | for ord in ords: |
198 | 198 | s = re.sub(clozeReg%ord, qrepl, string) |
199 | s = re.sub(clozeReg%".+?", "\\1", s) | |
199 | s = re.sub(clozeReg%".+?", "\\2", s) | |
200 | 200 | strings.append(s) |
201 | 201 | strings.append(re.sub(clozeReg%".+?", arepl, string)) |
202 | 202 | return strings |
982 | 982 | |
983 | 983 | def _dynOrder(self, o, l): |
984 | 984 | if o == DYN_OLDEST: |
985 | t = "c.mod" | |
985 | t = "(select max(id) from revlog where cid=c.id)" | |
986 | 986 | elif o == DYN_RANDOM: |
987 | 987 | t = "random()" |
988 | 988 | elif o == DYN_SMALLINT: |
78 | 78 | ########################################################################## |
79 | 79 | |
80 | 80 | mplayerCmd = ["mplayer", "-really-quiet", "-noautosub"] |
81 | if isWin: | |
82 | mplayerCmd += ["-ao", "win32"] | |
83 | 81 | |
84 | 82 | # Mplayer in slave mode |
85 | 83 | ########################################################################## |
3 | 3 | from anki.template import furigana; furigana.install() |
4 | 4 | from anki.template import hint; hint.install() |
5 | 5 | |
6 | clozeReg = r"(?s)\{\{c%s::(.*?)(::(.*?))?\}\}" | |
6 | clozeReg = r"(?si)\{\{(c)%s::(.*?)(::(.*?))?\}\}" | |
7 | 7 | |
8 | 8 | modifiers = {} |
9 | 9 | def modifier(symbol): |
177 | 177 | # hook-based field modifier |
178 | 178 | mod, extra = re.search("^(.*?)(?:\((.*)\))?$", mod).groups() |
179 | 179 | txt = runFilter('fmod_' + mod, txt or '', extra or '', context, |
180 | tag, tag_name); | |
180 | tag, tag_name) | |
181 | 181 | if txt is None: |
182 | 182 | return '{unknown field %s}' % tag_name |
183 | 183 | return txt |
186 | 186 | reg = clozeReg |
187 | 187 | if not re.search(reg%ord, txt): |
188 | 188 | return "" |
189 | txt = self._removeFormattingFromMathjax(txt, ord) | |
189 | 190 | def repl(m): |
190 | 191 | # replace chosen cloze with type |
191 | 192 | if type == "q": |
192 | if m.group(3): | |
193 | return "<span class=cloze>[%s]</span>" % m.group(3) | |
193 | if m.group(4): | |
194 | buf = "[%s]" % m.group(4) | |
194 | 195 | else: |
195 | return "<span class=cloze>[...]</span>" | |
196 | buf = "[...]" | |
196 | 197 | else: |
197 | return "<span class=cloze>%s</span>" % m.group(1) | |
198 | buf = m.group(2) | |
199 | # uppercase = no formatting | |
200 | if m.group(1) == "c": | |
201 | buf = "<span class=cloze>%s</span>" % buf | |
202 | return buf | |
198 | 203 | txt = re.sub(reg%ord, repl, txt) |
199 | 204 | # and display other clozes normally |
200 | return re.sub(reg%"\d+", "\\1", txt) | |
205 | return re.sub(reg%"\d+", "\\2", txt) | |
206 | ||
207 | # look for clozes wrapped in mathjax, and change {{cx to {{Cx | |
208 | def _removeFormattingFromMathjax(self, txt, ord): | |
209 | regex = r"(\\[([]).*?"+(clozeReg%ord)+r".*?(\\[\])])" | |
210 | def repl(m): | |
211 | return m.group(0).replace("{{c", "{{C") | |
212 | txt = re.sub(regex, repl, txt) | |
213 | return txt | |
201 | 214 | |
202 | 215 | @modifier('=') |
203 | 216 | def render_delimiter(self, tag_name=None, context=None): |
37 | 37 | |
38 | 38 | from anki.utils import checksum |
39 | 39 | |
40 | # Dialog manager - manages non-modal windows | |
40 | # Dialog manager | |
41 | 41 | ########################################################################## |
42 | # ensures only one copy of the window is open at once, and provides | |
43 | # a way for dialogs to clean up asynchronously when collection closes | |
44 | ||
45 | # to integrate a new window: | |
46 | # - add it to _dialogs | |
47 | # - define close behaviour, by either: | |
48 | # -- setting silentlyClose=True to have it close immediately | |
49 | # -- define a closeWithCallback() method | |
50 | # - have the window opened via aqt.dialogs.open(<name>, self) | |
51 | ||
52 | #- make preferences modal? cmd+q does wrong thing | |
53 | ||
54 | ||
55 | from aqt import addcards, browser, editcurrent, stats, about, \ | |
56 | preferences | |
42 | 57 | |
43 | 58 | class DialogManager: |
44 | 59 | |
45 | def __init__(self): | |
46 | from aqt import addcards, browser, editcurrent, stats, about | |
47 | self._dialogs = { | |
48 | "AddCards": [addcards.AddCards, None], | |
49 | "Browser": [browser.Browser, None], | |
50 | "EditCurrent": [editcurrent.EditCurrent, None], | |
51 | "DeckStats": [stats.DeckStats, None], | |
52 | "About": [about.show, None], | |
53 | } | |
60 | _dialogs = { | |
61 | "AddCards": [addcards.AddCards, None], | |
62 | "Browser": [browser.Browser, None], | |
63 | "EditCurrent": [editcurrent.EditCurrent, None], | |
64 | "DeckStats": [stats.DeckStats, None], | |
65 | "About": [about.show, None], | |
66 | "Preferences": [preferences.Preferences, None], | |
67 | } | |
54 | 68 | |
55 | 69 | def open(self, name, *args): |
56 | 70 | (creator, instance) = self._dialogs[name] |
88 | 102 | # still waiting for others to close |
89 | 103 | pass |
90 | 104 | |
91 | instance.closeWithCallback(callback) | |
105 | if getattr(instance, "silentlyClose", False): | |
106 | instance.close() | |
107 | callback() | |
108 | else: | |
109 | instance.closeWithCallback(callback) | |
92 | 110 | |
93 | 111 | return True |
94 | 112 |
107 | 107 | meta = self.addonMeta(sid) |
108 | 108 | base = self.addonsFolder(sid) |
109 | 109 | if os.path.exists(base): |
110 | self.backupUserFiles(sid) | |
110 | 111 | self.deleteAddon(sid) |
111 | 112 | |
113 | os.mkdir(base) | |
114 | self.restoreUserFiles(sid) | |
115 | ||
112 | 116 | # extract |
113 | os.mkdir(base) | |
114 | 117 | for n in z.namelist(): |
115 | 118 | if n.endswith("/"): |
116 | 119 | # folder; ignore |
117 | 120 | continue |
118 | # write | |
121 | ||
122 | path = os.path.join(base, n) | |
123 | # skip existing user files | |
124 | if os.path.exists(path) and n.startswith("user_files/"): | |
125 | continue | |
119 | 126 | z.extract(n, base) |
120 | 127 | |
121 | 128 | # update metadata |
139 | 146 | errs.append(_("Error downloading %(id)s: %(error)s") % dict(id=n, error=ret[1])) |
140 | 147 | continue |
141 | 148 | data, fname = ret |
149 | fname = fname.replace("_", " ") | |
142 | 150 | self.install(str(n), data, fname) |
143 | 151 | name = os.path.splitext(fname)[0] |
144 | 152 | log.append(_("Downloaded %(fname)s" % dict(fname=name))) |
235 | 243 | meta = self.addonMeta(addon) |
236 | 244 | meta['config'] = conf |
237 | 245 | self.writeAddonMeta(addon, meta) |
246 | ||
247 | # user_files | |
248 | ###################################################################### | |
249 | ||
250 | def _userFilesPath(self, sid): | |
251 | return os.path.join(self.addonsFolder(sid), "user_files") | |
252 | ||
253 | def _userFilesBackupPath(self): | |
254 | return os.path.join(self.addonsFolder(), "files_backup") | |
255 | ||
256 | def backupUserFiles(self, sid): | |
257 | p = self._userFilesPath(sid) | |
258 | if os.path.exists(p): | |
259 | os.rename(p, self._userFilesBackupPath()) | |
260 | ||
261 | def restoreUserFiles(self, sid): | |
262 | p = self._userFilesPath(sid) | |
263 | bp = self._userFilesBackupPath() | |
264 | # did we back up userFiles? | |
265 | if not os.path.exists(bp): | |
266 | return | |
267 | os.rename(bp, p) | |
238 | 268 | |
239 | 269 | # Add-ons Dialog |
240 | 270 | ###################################################################### |
18 | 18 | saveHeader, restoreHeader, saveState, restoreState, applyStyles, getTag, \ |
19 | 19 | showInfo, askUser, tooltip, openHelp, showWarning, shortcut, mungeQA, \ |
20 | 20 | getOnlyText, MenuList, SubMenu |
21 | from anki.hooks import runHook, addHook, remHook | |
21 | from anki.hooks import runHook, addHook, remHook, runFilter | |
22 | 22 | from aqt.webview import AnkiWebView |
23 | 23 | from anki.consts import * |
24 | 24 | from anki.sound import playFromText, clearAudioQueue |
855 | 855 | def _modelTree(self, root): |
856 | 856 | for m in sorted(self.col.models.all(), key=itemgetter("name")): |
857 | 857 | mitem = self.CallbackItem( |
858 | root, m['name'], lambda m=m: self.setFilter("mid", str(m['id']))) | |
858 | root, m['name'], lambda m=m: self.setFilter("note", m['name'])) | |
859 | 859 | mitem.setIcon(0, QIcon(":/icons/notetype.svg")) |
860 | 860 | |
861 | 861 | # Filter tree |
892 | 892 | txt += a + ":" |
893 | 893 | else: |
894 | 894 | txt += a |
895 | if " " in txt or "(" in txt or ")" in txt: | |
896 | txt = '"%s"' % txt | |
895 | for chr in " ()": | |
896 | if chr in txt: | |
897 | txt = '"%s"' % txt | |
898 | break | |
897 | 899 | items.append(txt) |
898 | 900 | txt = "" |
899 | 901 | txt = " ".join(items) |
1002 | 1004 | for nt in sorted(self.col.models.all(), key=lambda nt: nt['name'].lower()): |
1003 | 1005 | # no sub menu if it's a single template |
1004 | 1006 | if len(nt['tmpls']) == 1: |
1005 | noteTypes.addItem(nt['name'], self._filterFunc("mid", str(nt['id']))) | |
1007 | noteTypes.addItem(nt['name'], self._filterFunc("note", nt['name'])) | |
1006 | 1008 | else: |
1007 | 1009 | subm = noteTypes.addMenu(nt['name']) |
1008 | 1010 | |
1009 | subm.addItem(_("All Card Types"), self._filterFunc("mid", str(nt['id']))) | |
1011 | subm.addItem(_("All Card Types"), self._filterFunc("note", nt['name'])) | |
1010 | 1012 | subm.addSeparator() |
1011 | 1013 | |
1012 | 1014 | # add templates |
1013 | 1015 | for c, tmpl in enumerate(nt['tmpls']): |
1014 | 1016 | name = _("%(n)d: %(name)s") % dict(n=c+1, name=tmpl['name']) |
1015 | 1017 | subm.addItem(name, self._filterFunc( |
1016 | "mid", str(nt['id']), "card", str(c+1))) | |
1018 | "note", nt['name'], "card", str(c+1))) | |
1017 | 1019 | |
1018 | 1020 | m.addChild(noteTypes.chunked()) |
1019 | 1021 | return m |
1214 | 1216 | self._previewWindow.setWindowTitle(_("Preview")) |
1215 | 1217 | |
1216 | 1218 | self._previewWindow.finished.connect(self._onPreviewFinished) |
1219 | self._previewWindow.silentlyClose = True | |
1217 | 1220 | vbox = QVBoxLayout() |
1218 | 1221 | vbox.setContentsMargins(0,0,0,0) |
1219 | 1222 | self._previewWeb = AnkiWebView() |
1266 | 1269 | self._renderPreview() |
1267 | 1270 | else: |
1268 | 1271 | self.editor.saveNow(lambda: self._moveCur(QAbstractItemView.MoveUp)) |
1269 | self._updatePreviewButtons() | |
1270 | 1272 | |
1271 | 1273 | def _onPreviewNext(self): |
1272 | 1274 | if self._previewState == "question": |
1274 | 1276 | self._renderPreview() |
1275 | 1277 | else: |
1276 | 1278 | self.editor.saveNow(lambda: self._moveCur(QAbstractItemView.MoveDown)) |
1277 | self._updatePreviewButtons() | |
1278 | 1279 | |
1279 | 1280 | def _onReplayAudio(self): |
1280 | 1281 | self.mw.reviewer.replayAudio(self) |
1329 | 1330 | if not self._previewWindow: |
1330 | 1331 | return |
1331 | 1332 | c = self.card |
1332 | self._updatePreviewButtons() | |
1333 | 1333 | func = "_showQuestion" |
1334 | 1334 | if not c or not self.singleCard: |
1335 | 1335 | txt = _("(please select 1 card)") |
1353 | 1353 | playFromText(txt) |
1354 | 1354 | |
1355 | 1355 | txt = mungeQA(self.col, txt) |
1356 | ||
1356 | txt = runFilter("prepareQA", txt, c, | |
1357 | "preview"+self._previewState.capitalize()) | |
1358 | ||
1359 | self._updatePreviewButtons() | |
1357 | 1360 | self._previewWeb.eval( |
1358 | 1361 | f"{func}({json.dumps(txt)},'{bodyclass}');") |
1359 | 1362 |
14 | 14 | from anki.utils import isMac, isWin, joinFields |
15 | 15 | from aqt.webview import AnkiWebView |
16 | 16 | import json |
17 | from anki.hooks import runFilter | |
18 | ||
17 | 19 | |
18 | 20 | class CardLayout(QDialog): |
19 | 21 | |
295 | 297 | c = self.card |
296 | 298 | ti = self.maybeTextInput |
297 | 299 | bodyclass="card card%d" % (c.ord+1) |
300 | ||
298 | 301 | q = ti(mungeQA(self.mw.col, c.q(reload=True))) |
302 | q = runFilter("prepareQA", q, c, "clayoutQuestion") | |
303 | ||
299 | 304 | a = ti(mungeQA(self.mw.col, c.a()), type='a') |
305 | a = runFilter("prepareQA", a, c, "clayoutAnswer") | |
300 | 306 | |
301 | 307 | # use _showAnswer to avoid the longer delay |
302 | 308 | self.pform.frontWeb.eval("_showAnswer(%s,'%s');" % (json.dumps(q), bodyclass)) |
184 | 184 | ("Ctrl+T, T", self.insertLatex), |
185 | 185 | ("Ctrl+T, E", self.insertLatexEqn), |
186 | 186 | ("Ctrl+T, M", self.insertLatexMathEnv), |
187 | ("Ctrl+M, M", self.insertMathjaxInline), | |
188 | ("Ctrl+M, E", self.insertMathjaxBlock), | |
187 | 189 | ("Ctrl+Shift+X", self.onHtmlEdit), |
188 | 190 | ("Ctrl+Shift+T", self.onFocusTags) |
189 | 191 | ] |
258 | 260 | print("uncaught cmd", cmd) |
259 | 261 | |
260 | 262 | def mungeHTML(self, txt): |
261 | if txt == "<br>": | |
262 | txt = "" | |
263 | txt = re.sub(r"<br>$", "", txt) | |
263 | 264 | return txt |
264 | 265 | |
265 | 266 | # Setting/unsetting the current note |
646 | 647 | def onPaste(self): |
647 | 648 | self.web.onPaste() |
648 | 649 | |
650 | def onCutOrCopy(self): | |
651 | self.web.flagAnkiText() | |
652 | ||
649 | 653 | # Advanced menu |
650 | 654 | ###################################################################### |
651 | 655 | |
652 | 656 | def onAdvanced(self): |
653 | 657 | m = QMenu(self.mw) |
658 | a = m.addAction(_("MathJax inline")) | |
659 | a.triggered.connect(self.insertMathjaxInline) | |
660 | a = m.addAction(_("MathJax block")) | |
661 | a.triggered.connect(self.insertMathjaxBlock) | |
654 | 662 | a = m.addAction(_("LaTeX")) |
655 | 663 | a.triggered.connect(self.insertLatex) |
656 | 664 | a = m.addAction(_("LaTeX equation")) |
672 | 680 | |
673 | 681 | def insertLatexMathEnv(self): |
674 | 682 | self.web.eval("wrap('[$$]', '[/$$]');") |
683 | ||
684 | def insertMathjaxInline(self): | |
685 | self.web.eval("wrap('\\\\(', '\\\\)');") | |
686 | ||
687 | def insertMathjaxBlock(self): | |
688 | self.web.eval("wrap('\\\\[', '\\\\]');") | |
675 | 689 | |
676 | 690 | # Links from HTML |
677 | 691 | ###################################################################### |
693 | 707 | more=onAdvanced, |
694 | 708 | dupes=showDupes, |
695 | 709 | paste=onPaste, |
710 | cutOrCopy=onCutOrCopy, | |
696 | 711 | ) |
697 | 712 | |
698 | 713 | # Pasting, drag & drop, and keyboard layouts |
715 | 730 | self._flagAnkiText() |
716 | 731 | |
717 | 732 | def onCut(self): |
718 | self._markInternal = True | |
719 | 733 | self.triggerPageAction(QWebEnginePage.Cut) |
720 | 734 | |
721 | 735 | def onCopy(self): |
722 | self._markInternal = True | |
723 | 736 | self.triggerPageAction(QWebEnginePage.Copy) |
724 | 737 | |
725 | 738 | def onPaste(self): |
818 | 831 | |
819 | 832 | # add to media and return resulting html link |
820 | 833 | return self.editor._addMedia(newpath) |
834 | ||
835 | def flagAnkiText(self): | |
836 | # be ready to adjust when clipboard event fires | |
837 | self._markInternal = True | |
821 | 838 | |
822 | 839 | def _flagAnkiText(self): |
823 | 840 | # add a comment in the clipboard html so we can tell text is copied |
96 | 96 | It's a good idea to run Tools>Check Database to ensure your collection \ |
97 | 97 | is not corrupt. |
98 | 98 | """)) |
99 | ||
99 | 100 | stdText = _("""\ |
100 | An error occurred. It may have been caused by a harmless bug, <br> | |
101 | or your deck may have a problem. | |
102 | <p>To confirm it's not a problem with your deck, please run | |
103 | <b>Tools > Check Database</b>. | |
104 | <p>If that doesn't fix the problem, please copy the following<br> | |
105 | into a bug report:""") | |
101 | <h1>Error</h1> | |
102 | ||
103 | <p>An error occurred. Please use <b>Tools > Check Database</b> to see if \ | |
104 | that fixes the problem.</p> | |
105 | ||
106 | <p>If problems persist, please report the problem on our \ | |
107 | <a href="https://help.ankiweb.net">support site</a>. Please copy and paste \ | |
108 | the information below into your report.</p>""") | |
109 | ||
106 | 110 | pluginText = _("""\ |
111 | <h1>Error</h1> | |
112 | ||
107 | 113 | <p>An error occurred. Please start Anki while holding down the shift \ |
108 | 114 | key, which will temporarily disable the add-ons you have installed.</p> |
109 | 115 | |
110 | <p>If the problem occurs even with add-ons disabled, please report the \ | |
111 | issue on our support site.</p> | |
116 | <p>If the issue only occurs when add-ons are enabled, please use the \ | |
117 | Tools>Add-ons menu item to disable some add-ons and restart Anki, \ | |
118 | repeating until you discover the add-on that is causing the problem.</p> | |
112 | 119 | |
113 | <p>If the issue only occurs when add-ons are enabled, plesae use the \ | |
114 | Tools>Add-ons menu item to disable one add-on and restart Anki, \ | |
115 | repeating until you discover the add-on that is causing the problem.</p> | |
120 | <p>When you've discovered the add-on that is causing the problem, please \ | |
121 | report the issue on the <a href="https://help.ankiweb.net/discussions/add-ons/">\ | |
122 | add-ons section</a> of our support site. | |
123 | ||
124 | <p>Debug info:</p> | |
116 | 125 | """) |
117 | 126 | if self.mw.addonManager.dirty: |
118 | 127 | txt = pluginText |
119 | 128 | else: |
120 | 129 | txt = stdText |
121 | 130 | # show dialog |
131 | error = self._supportText() + "\n" + error | |
132 | ||
122 | 133 | txt = txt + "<div style='white-space: pre-wrap'>" + error + "</div>" |
123 | 134 | showText(txt, type="html") |
135 | ||
136 | def _supportText(self): | |
137 | import platform | |
138 | from aqt import appVersion | |
139 | ||
140 | if isWin: | |
141 | platname = "Windows " + platform.win32_ver()[0] | |
142 | elif isMac: | |
143 | platname = "Mac " + platform.mac_ver()[0] | |
144 | else: | |
145 | platname = "Linux" | |
146 | ||
147 | return f"""\ | |
148 | Anki {appVersion} Python {platform.python_version()} Qt {QT_VERSION_STR} PyQt {PYQT_VERSION_STR} | |
149 | Platform: {platname} | |
150 | Flags: frz={getattr(sys, "frozen", False)} ao={self.mw.addonManager.dirty} | |
151 | """ |
10 | 10 | from anki.exporting import exporters |
11 | 11 | from anki.hooks import addHook, remHook |
12 | 12 | from anki.lang import ngettext |
13 | ||
13 | import time | |
14 | 14 | |
15 | 15 | class ExportDialog(QDialog): |
16 | 16 | |
25 | 25 | self.exec_() |
26 | 26 | |
27 | 27 | def setup(self, did): |
28 | self.frm.format.insertItems(0, list(zip(*exporters()))[0]) | |
28 | self.exporters = exporters() | |
29 | # if a deck specified, start with .apkg type selected | |
30 | idx = 0 | |
31 | if did: | |
32 | for c, (k,e) in enumerate(self.exporters): | |
33 | if e.ext == ".apkg": | |
34 | idx = c | |
35 | break | |
36 | self.frm.format.insertItems(0, [e[0] for e in self.exporters]) | |
37 | self.frm.format.setCurrentIndex(idx) | |
29 | 38 | self.frm.format.activated.connect(self.exporterChanged) |
30 | self.exporterChanged(0) | |
39 | self.exporterChanged(idx) | |
40 | # deck list | |
31 | 41 | self.decks = [_("All Decks")] + sorted(self.col.decks.allNames()) |
32 | 42 | self.frm.deck.addItems(self.decks) |
33 | 43 | # save button |
40 | 50 | self.frm.deck.setCurrentIndex(index) |
41 | 51 | |
42 | 52 | def exporterChanged(self, idx): |
43 | self.exporter = exporters()[idx][1](self.col) | |
44 | self.isApkg = hasattr(self.exporter, "includeSched") | |
53 | self.exporter = self.exporters[idx][1](self.col) | |
54 | self.isApkg = self.exporter.ext == ".apkg" | |
55 | self.isVerbatim = getattr(self.exporter, "verbatim", False) | |
45 | 56 | self.isTextNote = hasattr(self.exporter, "includeTags") |
46 | self.hideTags = hasattr(self.exporter, "hideTags") | |
47 | self.frm.includeSched.setVisible(self.isApkg) | |
48 | self.frm.includeMedia.setVisible(self.isApkg) | |
57 | self.frm.includeSched.setVisible( | |
58 | getattr(self.exporter, "includeSched", None) is not None) | |
59 | self.frm.includeMedia.setVisible( | |
60 | getattr(self.exporter, "includeMedia", None) is not None) | |
49 | 61 | self.frm.includeTags.setVisible( |
50 | not self.isApkg and not self.hideTags) | |
62 | getattr(self.exporter, "includeTags", None) is not None) | |
63 | # show deck list? | |
64 | self.frm.deck.setVisible(not self.isVerbatim) | |
51 | 65 | |
52 | 66 | def accept(self): |
53 | 67 | self.exporter.includeSched = ( |
61 | 75 | else: |
62 | 76 | name = self.decks[self.frm.deck.currentIndex()] |
63 | 77 | self.exporter.did = self.col.decks.id(name) |
64 | if (self.isApkg and self.exporter.includeSched and not | |
65 | self.exporter.did): | |
66 | verbatim = True | |
67 | # it's a verbatim apkg export, so place on desktop instead of | |
68 | # choosing file; use homedir if no desktop | |
69 | usingHomedir = False | |
70 | file = os.path.join(QStandardPaths.writableLocation( | |
71 | QStandardPaths.DesktopLocation), "collection.apkg") | |
72 | if not os.path.exists(os.path.dirname(file)): | |
73 | usingHomedir = True | |
74 | file = os.path.join(QStandardPaths.writableLocation( | |
75 | QStandardPaths.HomeLocation), "collection.apkg") | |
76 | if os.path.exists(file): | |
77 | if usingHomedir: | |
78 | question = _("%s already exists in your home directory. Overwrite it?") | |
79 | else: | |
80 | question = _("%s already exists on your desktop. Overwrite it?") | |
81 | if not askUser(question % "collection.apkg"): | |
82 | return | |
78 | if self.isVerbatim: | |
79 | name = time.strftime("-%Y-%m-%d@%H-%M-%S", | |
80 | time.localtime(time.time())) | |
81 | deck_name = _("collection")+name | |
83 | 82 | else: |
84 | verbatim = False | |
85 | 83 | # Get deck name and remove invalid filename characters |
86 | 84 | deck_name = self.decks[self.frm.deck.currentIndex()] |
87 | 85 | deck_name = re.sub('[\\\\/?<>:*|"^]', '_', deck_name) |
88 | filename = '{0}{1}'.format(deck_name, self.exporter.ext) | |
89 | while 1: | |
90 | file = getSaveFile(self, _("Export"), "export", | |
91 | self.exporter.key, self.exporter.ext, | |
92 | fname=filename) | |
93 | if not file: | |
94 | return | |
95 | if checkInvalidFilename(os.path.basename(file), dirsep=False): | |
96 | continue | |
97 | break | |
86 | ||
87 | filename = '{0}{1}'.format(deck_name, self.exporter.ext) | |
88 | while 1: | |
89 | file = getSaveFile(self, _("Export"), "export", | |
90 | self.exporter.key, self.exporter.ext, | |
91 | fname=filename) | |
92 | if not file: | |
93 | return | |
94 | if checkInvalidFilename(os.path.basename(file), dirsep=False): | |
95 | continue | |
96 | break | |
98 | 97 | self.hide() |
99 | 98 | if file: |
100 | 99 | self.mw.progress.start(immediate=True) |
112 | 111 | addHook("exportedMediaFiles", exportedMedia) |
113 | 112 | self.exporter.exportInto(file) |
114 | 113 | remHook("exportedMediaFiles", exportedMedia) |
115 | if verbatim: | |
116 | if usingHomedir: | |
117 | msg = _("A file called %s was saved in your home directory.") | |
118 | else: | |
119 | msg = _("A file called %s was saved on your desktop.") | |
120 | msg = msg % "collection.apkg" | |
121 | period = 5000 | |
114 | period = 3000 | |
115 | if self.isVerbatim: | |
116 | msg = _("Collection exported.") | |
122 | 117 | else: |
123 | period = 3000 | |
124 | 118 | if self.isTextNote: |
125 | 119 | msg = ngettext("%d note exported.", "%d notes exported.", |
126 | 120 | self.exporter.count) % self.exporter.count |
45 | 45 | self.tab = QtWidgets.QWidget() |
46 | 46 | self.tab.setObjectName("tab") |
47 | 47 | self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.tab) |
48 | self.verticalLayout_2.setContentsMargins(0, 0, 0, 0) | |
48 | self.verticalLayout_2.setContentsMargins(12, 12, 12, 12) | |
49 | 49 | self.verticalLayout_2.setObjectName("verticalLayout_2") |
50 | 50 | self.gridLayout = QtWidgets.QGridLayout() |
51 | self.gridLayout.setSpacing(12) | |
51 | 52 | self.gridLayout.setObjectName("gridLayout") |
52 | 53 | self.label_27 = QtWidgets.QLabel(self.tab) |
53 | 54 | self.label_27.setObjectName("label_27") |
118 | 119 | self.tab_3 = QtWidgets.QWidget() |
119 | 120 | self.tab_3.setObjectName("tab_3") |
120 | 121 | self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.tab_3) |
121 | self.verticalLayout_4.setContentsMargins(0, 0, 0, 0) | |
122 | self.verticalLayout_4.setContentsMargins(12, 12, 12, 12) | |
122 | 123 | self.verticalLayout_4.setObjectName("verticalLayout_4") |
123 | 124 | self.gridLayout_3 = QtWidgets.QGridLayout() |
125 | self.gridLayout_3.setSpacing(12) | |
124 | 126 | self.gridLayout_3.setObjectName("gridLayout_3") |
125 | 127 | self.label_20 = QtWidgets.QLabel(self.tab_3) |
126 | 128 | self.label_20.setObjectName("label_20") |
181 | 183 | self.tab_2 = QtWidgets.QWidget() |
182 | 184 | self.tab_2.setObjectName("tab_2") |
183 | 185 | self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.tab_2) |
184 | self.verticalLayout_3.setContentsMargins(0, 0, 0, 0) | |
186 | self.verticalLayout_3.setContentsMargins(12, 12, 12, 12) | |
185 | 187 | self.verticalLayout_3.setObjectName("verticalLayout_3") |
186 | 188 | self.gridLayout_2 = QtWidgets.QGridLayout() |
189 | self.gridLayout_2.setSpacing(12) | |
187 | 190 | self.gridLayout_2.setObjectName("gridLayout_2") |
188 | 191 | self.label_17 = QtWidgets.QLabel(self.tab_2) |
189 | 192 | self.label_17.setObjectName("label_17") |
247 | 250 | self.tab_5 = QtWidgets.QWidget() |
248 | 251 | self.tab_5.setObjectName("tab_5") |
249 | 252 | self.verticalLayout_6 = QtWidgets.QVBoxLayout(self.tab_5) |
250 | self.verticalLayout_6.setContentsMargins(0, 0, 0, 0) | |
253 | self.verticalLayout_6.setContentsMargins(12, 12, 12, 12) | |
251 | 254 | self.verticalLayout_6.setObjectName("verticalLayout_6") |
252 | 255 | self.gridLayout_5 = QtWidgets.QGridLayout() |
256 | self.gridLayout_5.setSpacing(12) | |
253 | 257 | self.gridLayout_5.setObjectName("gridLayout_5") |
254 | 258 | self.label_25 = QtWidgets.QLabel(self.tab_5) |
255 | 259 | self.label_25.setObjectName("label_25") |
280 | 284 | self.tab_4 = QtWidgets.QWidget() |
281 | 285 | self.tab_4.setObjectName("tab_4") |
282 | 286 | self.verticalLayout_5 = QtWidgets.QVBoxLayout(self.tab_4) |
283 | self.verticalLayout_5.setContentsMargins(0, 0, 0, 0) | |
287 | self.verticalLayout_5.setContentsMargins(12, 12, 12, 12) | |
288 | self.verticalLayout_5.setSpacing(12) | |
284 | 289 | self.verticalLayout_5.setObjectName("verticalLayout_5") |
285 | 290 | self.label_22 = QtWidgets.QLabel(self.tab_4) |
286 | 291 | self.label_22.setObjectName("label_22") |
8 | 8 | from PyQt5 import QtCore |
9 | 9 | |
10 | 10 | qt_resource_data = b"\ |
11 | \x00\x00\x02\xd7\ | |
12 | \x89\ | |
13 | \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ | |
14 | \x00\x00\x3c\x00\x00\x00\x3c\x08\x06\x00\x00\x00\x3a\xfc\xd9\x72\ | |
15 | \x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\x74\x77\x61\x72\x65\ | |
16 | \x00\x41\x64\x6f\x62\x65\x20\x49\x6d\x61\x67\x65\x52\x65\x61\x64\ | |
17 | \x79\x71\xc9\x65\x3c\x00\x00\x02\x79\x49\x44\x41\x54\x78\xda\xec\ | |
18 | \x9a\xd1\x6d\xc2\x30\x10\x40\x0d\x62\x80\x6c\x40\x3a\x01\xde\xa0\ | |
19 | \x19\x21\x23\xa4\x13\xc0\x06\xf5\x06\xa8\x13\xa4\x1b\xa4\x1b\x44\ | |
20 | \x4c\x10\x98\x20\x6c\x10\x36\x68\x8d\xe4\x48\xc8\x75\x82\xef\xb0\ | |
21 | \x0f\x87\xf8\xa4\xfb\x31\x89\xc3\xf3\x9d\xed\xbb\xb3\x17\x8c\x56\ | |
22 | \xb8\xd4\x5c\xea\x46\x6a\xa2\xda\x2e\x52\x4f\x52\x7f\xa4\x1e\xd9\ | |
23 | \x8b\xc8\x15\xb2\x91\xfa\x7b\x47\x5b\xf5\xec\x64\xe5\x6a\xc5\xca\ | |
24 | \x02\x54\xd7\xea\xc6\x03\x26\x05\xdb\x20\x60\x7b\x6d\xd4\x14\x98\ | |
25 | \x05\x6c\xaf\xdd\x54\x2c\xed\x02\xf6\xd6\xd2\x41\x8b\x70\x08\xdb\ | |
26 | \xab\x08\x15\x36\xf5\x00\xdb\x6b\x1a\x22\x70\xe9\x11\xb8\x9e\x93\ | |
27 | \x75\x9d\x59\x79\xe9\x10\x78\x4b\x30\xa8\xdb\x90\x2c\xdc\x12\x58\ | |
28 | \xb8\x0b\x05\x96\x13\xc0\xf6\xca\x43\x70\xe9\x8c\x38\x2e\x47\xcb\ | |
29 | \xea\x0e\x04\x57\x19\xcc\x59\xe9\x90\x6c\x08\x81\x37\x3e\x81\x3f\ | |
30 | \xb5\xb6\x2b\xfc\x97\xd4\x6f\xc3\x0a\x4d\xb9\x1b\xe8\x61\x6c\xae\ | |
31 | \xda\xfb\xb4\x93\xab\xb4\xf3\x0d\x02\x3c\x34\x57\xd3\x27\xbb\x34\ | |
32 | \x1f\xd8\xff\x4d\xf1\x3c\x68\x0e\xbf\x0f\xb4\x87\x96\xa4\x5f\x7c\ | |
33 | \x2f\x5a\x17\x36\x61\x59\xb2\x99\xc9\x12\x61\xc9\xf4\x55\x81\x4f\ | |
34 | \x13\x01\x4e\x7c\xbb\xf4\xfa\xc9\x0b\xd9\xd1\x62\xd5\x1e\xfc\x4f\ | |
35 | \x4b\x40\xc7\x63\x1f\x38\x3f\x71\x55\xe6\x90\x29\x89\x05\x4e\x2c\ | |
36 | \xdd\xdf\x87\x1c\x2c\x23\xaf\x03\xa6\xf3\x6e\x20\x80\xcf\x0d\x81\ | |
37 | \x07\x55\xf2\x90\x21\xff\xa3\x95\x0c\xd5\x94\x4b\xc0\xe0\xf8\x4c\ | |
38 | \x0f\xf3\x91\x67\x51\xd5\xce\x02\xd0\x61\x49\x00\x5c\x5a\x7e\x13\ | |
39 | \x5d\xe9\x4c\x98\x7d\x25\x91\x13\xbb\xf3\x58\x49\x69\xf7\xc8\x22\ | |
40 | \x51\x31\xfb\x22\x79\x4d\x58\xc4\x2b\x7d\xd5\xbe\x32\x80\x95\xb3\ | |
41 | \x00\xac\x5b\xfa\xae\x57\x71\x82\xb9\x5c\x01\x3c\xc9\x49\xaa\x3a\ | |
42 | \xb6\x1a\x36\x9a\x6b\x27\x8e\x0b\x7a\xad\xd6\xbf\x60\x44\xb5\xeb\ | |
43 | \x1a\xe0\x46\xdc\xd1\x36\xd5\x69\x1e\x94\xfb\x2c\xf0\x41\x2b\x93\ | |
44 | \xae\xa1\x75\xd8\x7b\xfd\xed\x7d\x84\x75\x02\x01\x8d\x39\x49\x6c\ | |
45 | \x80\xb0\x2d\xf3\x78\xac\x5a\x33\xd8\xe9\x7d\xa2\x06\xaa\xb3\xb4\ | |
46 | \xaa\xd0\xde\x2f\x98\xe7\x5a\xb5\x4d\x30\xd2\x58\x2c\x34\x99\xe1\ | |
47 | \xbd\x42\x0d\x48\xab\x3d\x5b\xa9\xdf\xf4\x81\xb2\xb9\x2e\x51\x50\ | |
48 | \x55\x0e\x3b\xcb\xed\x04\x13\x04\xec\x2c\xfb\x17\x94\x95\x06\xc8\ | |
49 | \xa2\x54\x1b\x2c\x68\xea\x6f\x0f\xe8\x13\x15\x60\x2c\x1e\x84\x4e\ | |
50 | \x95\x15\x21\x73\xe8\x6c\x28\x18\x40\x83\x85\x0f\xf6\xff\x30\x80\ | |
51 | \xb4\xa6\x84\xb9\x9a\x84\xdd\xaa\x82\xb9\xc7\xb5\x63\x7e\x73\xe2\ | |
52 | \x9a\x05\x78\xa3\x27\xf1\x10\x4b\x4f\xe2\x76\x5e\xaa\xc0\x1f\xb1\ | |
53 | \x78\x43\xb5\xe5\xb8\xb6\x78\xa1\xe0\x5b\x4b\x6b\x0a\x36\xa1\x1b\ | |
54 | \x78\xd8\xbc\x9a\x6c\x5f\x8d\x67\x4b\x11\x38\x02\x47\xe0\x08\x1c\ | |
55 | \x81\x03\x92\x15\xf2\x3d\x8e\x88\x6b\x6d\x82\x89\x35\x22\x73\x32\ | |
56 | \x65\x5f\xe4\x65\x1e\x4a\x15\xd1\xa5\x23\x70\x04\x9e\x8f\xfc\x09\ | |
57 | \x30\x00\xa0\x1c\x74\x67\x26\xea\x15\x76\x00\x00\x00\x00\x49\x45\ | |
58 | \x4e\x44\xae\x42\x60\x82\ | |
11 | \x00\x00\x02\x68\ | |
12 | \x00\ | |
13 | \x00\x10\x25\x78\x9c\xed\x97\x5b\x6f\xd3\x30\x14\xc7\xdf\xf7\x29\ | |
14 | \x8c\x25\x24\x90\x52\x5f\x13\x3b\xce\x9a\x4d\xda\x85\x09\x69\xc0\ | |
15 | \x24\x36\x10\xbc\x85\xc4\x6b\xcd\xd2\x24\x4a\xb2\xb6\xfb\xf6\x9c\ | |
16 | \x64\xed\xb4\x6e\xd5\x40\x88\xf1\xb2\xb4\x55\xeb\x73\x72\x7c\x2e\ | |
17 | \x3f\xfb\xff\xd0\xf1\xfe\x72\x96\xa3\xb9\xad\x1b\x57\x16\x31\xe6\ | |
18 | \x84\x61\x64\x8b\xb4\xcc\x5c\x31\x89\xf1\xc5\xf9\xbb\x51\x88\x51\ | |
19 | \xd3\x26\x45\x96\xe4\x65\x61\x63\x5c\x94\x78\x7f\x6f\x67\xfc\xea\ | |
20 | \xe8\xd3\xe1\xf9\xb7\xb3\x63\xd4\xcc\x27\xe8\xec\xe2\xe0\xf4\xfd\ | |
21 | \x21\xc2\x23\x4a\xbf\xca\x43\x4a\x8f\xce\x8f\xd0\xe7\x2f\x27\x88\ | |
22 | \x13\x4e\xe9\xf1\x47\x8c\xf0\xb4\x6d\xab\x88\xd2\xc5\x62\x41\x16\ | |
23 | \x92\x94\xf5\x84\x9e\xd4\x49\x35\x75\x69\x43\x21\x90\x76\x81\xb0\ | |
24 | \x89\x42\x32\xce\x49\xd6\x66\x18\x4a\x74\x99\x17\x2e\x6b\xa7\xd0\ | |
25 | \x16\x63\xaf\x31\x9a\x5a\x37\x99\xb6\x6b\x6b\xee\xec\xe2\xa0\x5c\ | |
26 | \xc6\x98\x21\x86\x54\xf7\xc1\xf7\xe7\xe0\x18\xc1\x64\x45\x13\x6f\ | |
27 | \xa9\x2d\x18\x63\x5d\xad\x55\x48\xb4\xcc\x5d\x71\xb5\x2d\x90\x1b\ | |
28 | \x63\x68\xff\xb4\x0f\x8d\x9a\x2a\x49\x81\x41\x55\xdb\xc6\xd6\x73\ | |
29 | \xdb\x91\xb9\xc9\xc1\x71\xe9\xf2\x7c\x54\x5f\xe7\x36\xb2\x73\x5b\ | |
30 | \x94\x59\xb6\x9b\xe6\xae\xda\xf4\x34\x6d\x5d\x5e\xd9\x11\x24\xb3\ | |
31 | \x69\x52\x45\x75\x79\x5d\x6c\x38\x7f\x96\xae\xd8\xf4\xce\x5c\x6b\ | |
32 | \xeb\xdc\xc1\x4f\xc4\x49\xb0\x0b\x4c\x10\xbc\xc6\x13\xd4\xd6\x49\ | |
33 | \xd1\x5c\x96\xf5\x2c\xc6\xb3\xa4\xad\xdd\xf2\x0d\xf7\x18\xbc\xb9\ | |
34 | \x37\xf2\xb9\xf6\xd8\xdb\x55\xe8\x2a\xdc\x65\x31\x4e\xcb\x3c\xb7\ | |
35 | \x69\x0b\x70\xf0\x53\xdb\xb9\xd0\xfe\xe6\xfe\x3e\x47\x0d\x5b\x11\ | |
36 | \xa0\xe6\xca\x00\xd7\x1b\x60\x8e\xd7\x47\xd3\x61\x5f\x1f\x4c\xb7\ | |
37 | \xbe\x47\x24\x2a\xe0\xc6\xec\x62\xfa\x20\xd9\xb6\xfe\x19\x31\x7e\ | |
38 | \xe0\x4b\xd9\xf7\x71\x67\x18\x41\x94\x62\xdc\x13\x84\x2b\xa5\xcc\ | |
39 | \xc3\xb6\x9e\xc8\xa6\x82\x80\x73\xc8\x46\x18\xe3\x4a\xfa\x42\x78\ | |
40 | \xa3\x7e\x6d\x84\x66\x06\xdc\x5a\x8b\x00\x48\x71\x61\x0c\x31\xd2\ | |
41 | \x13\x21\x91\xa1\x0e\xb7\x15\xe8\x8b\x54\x49\x3b\x45\x40\xf1\x83\ | |
42 | \xd2\xd2\xd3\xa7\x2a\x10\xab\x6f\x0e\xbd\xf2\x53\xa5\x42\x22\xa0\ | |
43 | \x4f\xb8\xbb\x8c\xcb\x5b\xd3\x97\x9e\x14\xc4\x30\x30\xf5\xc6\x52\ | |
44 | \x7f\xdf\x02\xe9\xf6\xc4\xa3\x1f\x79\x92\x5e\xad\x8f\xbf\x07\x1c\ | |
45 | \xf9\xd5\xf2\x11\xc2\xbe\x27\x3a\xf9\xe7\x34\x24\x93\xc0\xc1\x13\ | |
46 | \x01\x11\x81\x0a\x5e\x3a\x0d\x61\x14\xd1\xa1\x27\x41\x7b\xdc\xff\ | |
47 | \x0b\x1a\xff\x77\xe0\xc7\x8e\x3f\xd7\x99\x52\x83\xce\x06\x9d\x0d\ | |
48 | \x3a\x7b\x6e\x9d\xc1\xa0\x4c\x19\xe5\x8d\x84\x26\x42\x85\x6a\x10\ | |
49 | \xda\x33\x5f\xad\x41\x68\x2f\x53\x68\x1a\xe6\xf7\x75\x30\x08\x6d\ | |
50 | \x10\xda\x20\xb4\xdf\x0d\xbc\xe1\xb8\x33\xfa\xc5\xb8\xfb\x97\xbc\ | |
51 | \xb7\xf3\x0b\x46\x33\xee\x37\ | |
59 | 52 | \x00\x00\x04\x30\ |
60 | 53 | \x3c\ |
61 | 54 | \x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\ |
125 | 118 | \x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x67\ |
126 | 119 | \x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x67\x3e\x0a\x20\ |
127 | 120 | \x20\x20\x20\x3c\x2f\x67\x3e\x0a\x3c\x2f\x73\x76\x67\x3e\x0a\ |
128 | \x00\x00\x02\x68\ | |
129 | \x00\ | |
130 | \x00\x10\x25\x78\x9c\xed\x97\x5b\x6f\xd3\x30\x14\xc7\xdf\xf7\x29\ | |
131 | \x8c\x25\x24\x90\x52\x5f\x13\x3b\xce\x9a\x4d\xda\x85\x09\x69\xc0\ | |
132 | \x24\x36\x10\xbc\x85\xc4\x6b\xcd\xd2\x24\x4a\xb2\xb6\xfb\xf6\x9c\ | |
133 | \x64\xed\xb4\x6e\xd5\x40\x88\xf1\xb2\xb4\x55\xeb\x73\x72\x7c\x2e\ | |
134 | \x3f\xfb\xff\xd0\xf1\xfe\x72\x96\xa3\xb9\xad\x1b\x57\x16\x31\xe6\ | |
135 | \x84\x61\x64\x8b\xb4\xcc\x5c\x31\x89\xf1\xc5\xf9\xbb\x51\x88\x51\ | |
136 | \xd3\x26\x45\x96\xe4\x65\x61\x63\x5c\x94\x78\x7f\x6f\x67\xfc\xea\ | |
137 | \xe8\xd3\xe1\xf9\xb7\xb3\x63\xd4\xcc\x27\xe8\xec\xe2\xe0\xf4\xfd\ | |
138 | \x21\xc2\x23\x4a\xbf\xca\x43\x4a\x8f\xce\x8f\xd0\xe7\x2f\x27\x88\ | |
139 | \x13\x4e\xe9\xf1\x47\x8c\xf0\xb4\x6d\xab\x88\xd2\xc5\x62\x41\x16\ | |
140 | \x92\x94\xf5\x84\x9e\xd4\x49\x35\x75\x69\x43\x21\x90\x76\x81\xb0\ | |
141 | \x89\x42\x32\xce\x49\xd6\x66\x18\x4a\x74\x99\x17\x2e\x6b\xa7\xd0\ | |
142 | \x16\x63\xaf\x31\x9a\x5a\x37\x99\xb6\x6b\x6b\xee\xec\xe2\xa0\x5c\ | |
143 | \xc6\x98\x21\x86\x54\xf7\xc1\xf7\xe7\xe0\x18\xc1\x64\x45\x13\x6f\ | |
144 | \xa9\x2d\x18\x63\x5d\xad\x55\x48\xb4\xcc\x5d\x71\xb5\x2d\x90\x1b\ | |
145 | \x63\x68\xff\xb4\x0f\x8d\x9a\x2a\x49\x81\x41\x55\xdb\xc6\xd6\x73\ | |
146 | \xdb\x91\xb9\xc9\xc1\x71\xe9\xf2\x7c\x54\x5f\xe7\x36\xb2\x73\x5b\ | |
147 | \x94\x59\xb6\x9b\xe6\xae\xda\xf4\x34\x6d\x5d\x5e\xd9\x11\x24\xb3\ | |
148 | \x69\x52\x45\x75\x79\x5d\x6c\x38\x7f\x96\xae\xd8\xf4\xce\x5c\x6b\ | |
149 | \xeb\xdc\xc1\x4f\xc4\x49\xb0\x0b\x4c\x10\xbc\xc6\x13\xd4\xd6\x49\ | |
150 | \xd1\x5c\x96\xf5\x2c\xc6\xb3\xa4\xad\xdd\xf2\x0d\xf7\x18\xbc\xb9\ | |
151 | \x37\xf2\xb9\xf6\xd8\xdb\x55\xe8\x2a\xdc\x65\x31\x4e\xcb\x3c\xb7\ | |
152 | \x69\x0b\x70\xf0\x53\xdb\xb9\xd0\xfe\xe6\xfe\x3e\x47\x0d\x5b\x11\ | |
153 | \xa0\xe6\xca\x00\xd7\x1b\x60\x8e\xd7\x47\xd3\x61\x5f\x1f\x4c\xb7\ | |
154 | \xbe\x47\x24\x2a\xe0\xc6\xec\x62\xfa\x20\xd9\xb6\xfe\x19\x31\x7e\ | |
155 | \xe0\x4b\xd9\xf7\x71\x67\x18\x41\x94\x62\xdc\x13\x84\x2b\xa5\xcc\ | |
156 | \xc3\xb6\x9e\xc8\xa6\x82\x80\x73\xc8\x46\x18\xe3\x4a\xfa\x42\x78\ | |
157 | \xa3\x7e\x6d\x84\x66\x06\xdc\x5a\x8b\x00\x48\x71\x61\x0c\x31\xd2\ | |
158 | \x13\x21\x91\xa1\x0e\xb7\x15\xe8\x8b\x54\x49\x3b\x45\x40\xf1\x83\ | |
159 | \xd2\xd2\xd3\xa7\x2a\x10\xab\x6f\x0e\xbd\xf2\x53\xa5\x42\x22\xa0\ | |
160 | \x4f\xb8\xbb\x8c\xcb\x5b\xd3\x97\x9e\x14\xc4\x30\x30\xf5\xc6\x52\ | |
161 | \x7f\xdf\x02\xe9\xf6\xc4\xa3\x1f\x79\x92\x5e\xad\x8f\xbf\x07\x1c\ | |
162 | \xf9\xd5\xf2\x11\xc2\xbe\x27\x3a\xf9\xe7\x34\x24\x93\xc0\xc1\x13\ | |
163 | \x01\x11\x81\x0a\x5e\x3a\x0d\x61\x14\xd1\xa1\x27\x41\x7b\xdc\xff\ | |
164 | \x0b\x1a\xff\x77\xe0\xc7\x8e\x3f\xd7\x99\x52\x83\xce\x06\x9d\x0d\ | |
165 | \x3a\x7b\x6e\x9d\xc1\xa0\x4c\x19\xe5\x8d\x84\x26\x42\x85\x6a\x10\ | |
166 | \xda\x33\x5f\xad\x41\x68\x2f\x53\x68\x1a\xe6\xf7\x75\x30\x08\x6d\ | |
167 | \x10\xda\x20\xb4\xdf\x0d\xbc\xe1\xb8\x33\xfa\xc5\xb8\xfb\x97\xbc\ | |
168 | \xb7\xf3\x0b\x46\x33\xee\x37\ | |
169 | 121 | \x00\x00\x06\xb8\ |
170 | 122 | \x89\ |
171 | 123 | \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ |
365 | 317 | \x20\x20\x20\x20\x20\x20\x3c\x2f\x67\x3e\x0a\x20\x20\x20\x20\x20\ |
366 | 318 | \x20\x20\x20\x3c\x2f\x67\x3e\x0a\x20\x20\x20\x20\x3c\x2f\x67\x3e\ |
367 | 319 | \x0a\x3c\x2f\x73\x76\x67\x3e\x0a\ |
320 | \x00\x00\x04\x06\ | |
321 | \x3c\ | |
322 | \x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\ | |
323 | \x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x55\x54\x46\ | |
324 | \x2d\x38\x22\x20\x73\x74\x61\x6e\x64\x61\x6c\x6f\x6e\x65\x3d\x22\ | |
325 | \x6e\x6f\x22\x3f\x3e\x0a\x3c\x21\x44\x4f\x43\x54\x59\x50\x45\x20\ | |
326 | \x73\x76\x67\x20\x50\x55\x42\x4c\x49\x43\x20\x22\x2d\x2f\x2f\x57\ | |
327 | \x33\x43\x2f\x2f\x44\x54\x44\x20\x53\x56\x47\x20\x31\x2e\x31\x2f\ | |
328 | \x2f\x45\x4e\x22\x20\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\ | |
329 | \x2e\x77\x33\x2e\x6f\x72\x67\x2f\x47\x72\x61\x70\x68\x69\x63\x73\ | |
330 | \x2f\x53\x56\x47\x2f\x31\x2e\x31\x2f\x44\x54\x44\x2f\x73\x76\x67\ | |
331 | \x31\x31\x2e\x64\x74\x64\x22\x3e\x0a\x3c\x73\x76\x67\x20\x77\x69\ | |
332 | \x64\x74\x68\x3d\x22\x31\x30\x30\x25\x22\x20\x68\x65\x69\x67\x68\ | |
333 | \x74\x3d\x22\x31\x30\x30\x25\x22\x20\x76\x69\x65\x77\x42\x6f\x78\ | |
334 | \x3d\x22\x30\x20\x30\x20\x36\x30\x20\x36\x30\x22\x20\x76\x65\x72\ | |
335 | \x73\x69\x6f\x6e\x3d\x22\x31\x2e\x31\x22\x20\x78\x6d\x6c\x6e\x73\ | |
336 | \x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\ | |
337 | \x6f\x72\x67\x2f\x32\x30\x30\x30\x2f\x73\x76\x67\x22\x20\x78\x6d\ | |
338 | \x6c\x6e\x73\x3a\x78\x6c\x69\x6e\x6b\x3d\x22\x68\x74\x74\x70\x3a\ | |
339 | \x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\x39\ | |
340 | \x39\x2f\x78\x6c\x69\x6e\x6b\x22\x20\x78\x6d\x6c\x3a\x73\x70\x61\ | |
341 | \x63\x65\x3d\x22\x70\x72\x65\x73\x65\x72\x76\x65\x22\x20\x73\x74\ | |
342 | \x79\x6c\x65\x3d\x22\x66\x69\x6c\x6c\x2d\x72\x75\x6c\x65\x3a\x65\ | |
343 | \x76\x65\x6e\x6f\x64\x64\x3b\x63\x6c\x69\x70\x2d\x72\x75\x6c\x65\ | |
344 | \x3a\x65\x76\x65\x6e\x6f\x64\x64\x3b\x73\x74\x72\x6f\x6b\x65\x2d\ | |
345 | \x6c\x69\x6e\x65\x63\x61\x70\x3a\x72\x6f\x75\x6e\x64\x3b\x73\x74\ | |
346 | \x72\x6f\x6b\x65\x2d\x6c\x69\x6e\x65\x6a\x6f\x69\x6e\x3a\x72\x6f\ | |
347 | \x75\x6e\x64\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6d\x69\x74\x65\x72\ | |
348 | \x6c\x69\x6d\x69\x74\x3a\x31\x2e\x35\x3b\x22\x3e\x0a\x20\x20\x20\ | |
349 | \x20\x3c\x67\x20\x74\x72\x61\x6e\x73\x66\x6f\x72\x6d\x3d\x22\x6d\ | |
350 | \x61\x74\x72\x69\x78\x28\x31\x2c\x30\x2c\x30\x2c\x31\x2c\x2d\x32\ | |
351 | \x38\x30\x2c\x30\x29\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\ | |
352 | \x3c\x67\x20\x69\x64\x3d\x22\x68\x65\x61\x72\x74\x22\x20\x74\x72\ | |
353 | \x61\x6e\x73\x66\x6f\x72\x6d\x3d\x22\x6d\x61\x74\x72\x69\x78\x28\ | |
354 | \x31\x2c\x30\x2c\x30\x2c\x31\x2c\x2d\x31\x34\x31\x31\x2c\x30\x29\ | |
355 | \x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\ | |
356 | \x72\x65\x63\x74\x20\x78\x3d\x22\x31\x36\x39\x31\x22\x20\x79\x3d\ | |
357 | \x22\x30\x22\x20\x77\x69\x64\x74\x68\x3d\x22\x36\x30\x22\x20\x68\ | |
358 | \x65\x69\x67\x68\x74\x3d\x22\x36\x30\x22\x20\x73\x74\x79\x6c\x65\ | |
359 | \x3d\x22\x66\x69\x6c\x6c\x3a\x6e\x6f\x6e\x65\x3b\x22\x2f\x3e\x0a\ | |
360 | \x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x67\x20\x74\ | |
361 | \x72\x61\x6e\x73\x66\x6f\x72\x6d\x3d\x22\x6d\x61\x74\x72\x69\x78\ | |
362 | \x28\x30\x2e\x39\x36\x30\x32\x34\x31\x2c\x30\x2c\x30\x2c\x30\x2e\ | |
363 | \x39\x36\x30\x32\x34\x31\x2c\x31\x34\x33\x31\x2e\x30\x31\x2c\x33\ | |
364 | \x2e\x31\x34\x37\x30\x31\x29\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\ | |
365 | \x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x70\x61\x74\x68\x20\ | |
366 | \x64\x3d\x22\x4d\x33\x30\x32\x2c\x31\x30\x2e\x38\x43\x33\x30\x37\ | |
367 | \x2e\x36\x38\x34\x2c\x30\x20\x33\x31\x39\x2e\x30\x35\x33\x2c\x30\ | |
368 | \x20\x33\x32\x34\x2e\x37\x33\x37\x2c\x35\x2e\x34\x43\x33\x33\x30\ | |
369 | \x2e\x34\x32\x31\x2c\x31\x30\x2e\x38\x20\x33\x33\x30\x2e\x34\x32\ | |
370 | \x31\x2c\x32\x31\x2e\x36\x20\x33\x32\x34\x2e\x37\x33\x37\x2c\x33\ | |
371 | \x32\x2e\x34\x43\x33\x32\x30\x2e\x37\x35\x38\x2c\x34\x30\x2e\x35\ | |
372 | \x20\x33\x31\x30\x2e\x35\x32\x36\x2c\x34\x38\x2e\x36\x20\x33\x30\ | |
373 | \x32\x2c\x35\x34\x43\x32\x39\x33\x2e\x34\x37\x34\x2c\x34\x38\x2e\ | |
374 | \x36\x20\x32\x38\x33\x2e\x32\x34\x32\x2c\x34\x30\x2e\x35\x20\x32\ | |
375 | \x37\x39\x2e\x32\x36\x33\x2c\x33\x32\x2e\x34\x43\x32\x37\x33\x2e\ | |
376 | \x35\x37\x39\x2c\x32\x31\x2e\x36\x20\x32\x37\x33\x2e\x35\x37\x39\ | |
377 | \x2c\x31\x30\x2e\x38\x20\x32\x37\x39\x2e\x32\x36\x33\x2c\x35\x2e\ | |
378 | \x34\x43\x32\x38\x34\x2e\x39\x34\x37\x2c\x30\x20\x32\x39\x36\x2e\ | |
379 | \x33\x31\x36\x2c\x30\x20\x33\x30\x32\x2c\x31\x30\x2e\x38\x5a\x22\ | |
380 | \x20\x73\x74\x79\x6c\x65\x3d\x22\x66\x69\x6c\x6c\x3a\x6e\x6f\x6e\ | |
381 | \x65\x3b\x73\x74\x72\x6f\x6b\x65\x3a\x62\x6c\x61\x63\x6b\x3b\x73\ | |
382 | \x74\x72\x6f\x6b\x65\x2d\x77\x69\x64\x74\x68\x3a\x32\x2e\x38\x32\ | |
383 | \x70\x78\x3b\x22\x2f\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\ | |
384 | \x20\x20\x20\x3c\x2f\x67\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\ | |
385 | \x3c\x2f\x67\x3e\x0a\x20\x20\x20\x20\x3c\x2f\x67\x3e\x0a\x3c\x2f\ | |
386 | \x73\x76\x67\x3e\x0a\ | |
368 | 387 | \x00\x00\x05\x55\ |
369 | 388 | \x3c\ |
370 | 389 | \x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\ |
453 | 472 | \x20\x20\x3c\x2f\x67\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\ |
454 | 473 | \x2f\x67\x3e\x0a\x20\x20\x20\x20\x3c\x2f\x67\x3e\x0a\x3c\x2f\x73\ |
455 | 474 | \x76\x67\x3e\x0a\ |
456 | \x00\x00\x04\x06\ | |
457 | \x3c\ | |
458 | \x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\ | |
459 | \x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x55\x54\x46\ | |
460 | \x2d\x38\x22\x20\x73\x74\x61\x6e\x64\x61\x6c\x6f\x6e\x65\x3d\x22\ | |
461 | \x6e\x6f\x22\x3f\x3e\x0a\x3c\x21\x44\x4f\x43\x54\x59\x50\x45\x20\ | |
462 | \x73\x76\x67\x20\x50\x55\x42\x4c\x49\x43\x20\x22\x2d\x2f\x2f\x57\ | |
463 | \x33\x43\x2f\x2f\x44\x54\x44\x20\x53\x56\x47\x20\x31\x2e\x31\x2f\ | |
464 | \x2f\x45\x4e\x22\x20\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\ | |
465 | \x2e\x77\x33\x2e\x6f\x72\x67\x2f\x47\x72\x61\x70\x68\x69\x63\x73\ | |
466 | \x2f\x53\x56\x47\x2f\x31\x2e\x31\x2f\x44\x54\x44\x2f\x73\x76\x67\ | |
467 | \x31\x31\x2e\x64\x74\x64\x22\x3e\x0a\x3c\x73\x76\x67\x20\x77\x69\ | |
468 | \x64\x74\x68\x3d\x22\x31\x30\x30\x25\x22\x20\x68\x65\x69\x67\x68\ | |
469 | \x74\x3d\x22\x31\x30\x30\x25\x22\x20\x76\x69\x65\x77\x42\x6f\x78\ | |
470 | \x3d\x22\x30\x20\x30\x20\x36\x30\x20\x36\x30\x22\x20\x76\x65\x72\ | |
471 | \x73\x69\x6f\x6e\x3d\x22\x31\x2e\x31\x22\x20\x78\x6d\x6c\x6e\x73\ | |
472 | \x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\ | |
473 | \x6f\x72\x67\x2f\x32\x30\x30\x30\x2f\x73\x76\x67\x22\x20\x78\x6d\ | |
474 | \x6c\x6e\x73\x3a\x78\x6c\x69\x6e\x6b\x3d\x22\x68\x74\x74\x70\x3a\ | |
475 | \x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\x39\ | |
476 | \x39\x2f\x78\x6c\x69\x6e\x6b\x22\x20\x78\x6d\x6c\x3a\x73\x70\x61\ | |
477 | \x63\x65\x3d\x22\x70\x72\x65\x73\x65\x72\x76\x65\x22\x20\x73\x74\ | |
478 | \x79\x6c\x65\x3d\x22\x66\x69\x6c\x6c\x2d\x72\x75\x6c\x65\x3a\x65\ | |
479 | \x76\x65\x6e\x6f\x64\x64\x3b\x63\x6c\x69\x70\x2d\x72\x75\x6c\x65\ | |
480 | \x3a\x65\x76\x65\x6e\x6f\x64\x64\x3b\x73\x74\x72\x6f\x6b\x65\x2d\ | |
481 | \x6c\x69\x6e\x65\x63\x61\x70\x3a\x72\x6f\x75\x6e\x64\x3b\x73\x74\ | |
482 | \x72\x6f\x6b\x65\x2d\x6c\x69\x6e\x65\x6a\x6f\x69\x6e\x3a\x72\x6f\ | |
483 | \x75\x6e\x64\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6d\x69\x74\x65\x72\ | |
484 | \x6c\x69\x6d\x69\x74\x3a\x31\x2e\x35\x3b\x22\x3e\x0a\x20\x20\x20\ | |
485 | \x20\x3c\x67\x20\x74\x72\x61\x6e\x73\x66\x6f\x72\x6d\x3d\x22\x6d\ | |
486 | \x61\x74\x72\x69\x78\x28\x31\x2c\x30\x2c\x30\x2c\x31\x2c\x2d\x32\ | |
487 | \x38\x30\x2c\x30\x29\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\ | |
488 | \x3c\x67\x20\x69\x64\x3d\x22\x68\x65\x61\x72\x74\x22\x20\x74\x72\ | |
489 | \x61\x6e\x73\x66\x6f\x72\x6d\x3d\x22\x6d\x61\x74\x72\x69\x78\x28\ | |
490 | \x31\x2c\x30\x2c\x30\x2c\x31\x2c\x2d\x31\x34\x31\x31\x2c\x30\x29\ | |
491 | \x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\ | |
492 | \x72\x65\x63\x74\x20\x78\x3d\x22\x31\x36\x39\x31\x22\x20\x79\x3d\ | |
493 | \x22\x30\x22\x20\x77\x69\x64\x74\x68\x3d\x22\x36\x30\x22\x20\x68\ | |
494 | \x65\x69\x67\x68\x74\x3d\x22\x36\x30\x22\x20\x73\x74\x79\x6c\x65\ | |
495 | \x3d\x22\x66\x69\x6c\x6c\x3a\x6e\x6f\x6e\x65\x3b\x22\x2f\x3e\x0a\ | |
496 | \x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x67\x20\x74\ | |
497 | \x72\x61\x6e\x73\x66\x6f\x72\x6d\x3d\x22\x6d\x61\x74\x72\x69\x78\ | |
498 | \x28\x30\x2e\x39\x36\x30\x32\x34\x31\x2c\x30\x2c\x30\x2c\x30\x2e\ | |
499 | \x39\x36\x30\x32\x34\x31\x2c\x31\x34\x33\x31\x2e\x30\x31\x2c\x33\ | |
500 | \x2e\x31\x34\x37\x30\x31\x29\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\ | |
501 | \x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x70\x61\x74\x68\x20\ | |
502 | \x64\x3d\x22\x4d\x33\x30\x32\x2c\x31\x30\x2e\x38\x43\x33\x30\x37\ | |
503 | \x2e\x36\x38\x34\x2c\x30\x20\x33\x31\x39\x2e\x30\x35\x33\x2c\x30\ | |
504 | \x20\x33\x32\x34\x2e\x37\x33\x37\x2c\x35\x2e\x34\x43\x33\x33\x30\ | |
505 | \x2e\x34\x32\x31\x2c\x31\x30\x2e\x38\x20\x33\x33\x30\x2e\x34\x32\ | |
506 | \x31\x2c\x32\x31\x2e\x36\x20\x33\x32\x34\x2e\x37\x33\x37\x2c\x33\ | |
507 | \x32\x2e\x34\x43\x33\x32\x30\x2e\x37\x35\x38\x2c\x34\x30\x2e\x35\ | |
508 | \x20\x33\x31\x30\x2e\x35\x32\x36\x2c\x34\x38\x2e\x36\x20\x33\x30\ | |
509 | \x32\x2c\x35\x34\x43\x32\x39\x33\x2e\x34\x37\x34\x2c\x34\x38\x2e\ | |
510 | \x36\x20\x32\x38\x33\x2e\x32\x34\x32\x2c\x34\x30\x2e\x35\x20\x32\ | |
511 | \x37\x39\x2e\x32\x36\x33\x2c\x33\x32\x2e\x34\x43\x32\x37\x33\x2e\ | |
512 | \x35\x37\x39\x2c\x32\x31\x2e\x36\x20\x32\x37\x33\x2e\x35\x37\x39\ | |
513 | \x2c\x31\x30\x2e\x38\x20\x32\x37\x39\x2e\x32\x36\x33\x2c\x35\x2e\ | |
514 | \x34\x43\x32\x38\x34\x2e\x39\x34\x37\x2c\x30\x20\x32\x39\x36\x2e\ | |
515 | \x33\x31\x36\x2c\x30\x20\x33\x30\x32\x2c\x31\x30\x2e\x38\x5a\x22\ | |
516 | \x20\x73\x74\x79\x6c\x65\x3d\x22\x66\x69\x6c\x6c\x3a\x6e\x6f\x6e\ | |
517 | \x65\x3b\x73\x74\x72\x6f\x6b\x65\x3a\x62\x6c\x61\x63\x6b\x3b\x73\ | |
518 | \x74\x72\x6f\x6b\x65\x2d\x77\x69\x64\x74\x68\x3a\x32\x2e\x38\x32\ | |
519 | \x70\x78\x3b\x22\x2f\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\ | |
520 | \x20\x20\x20\x3c\x2f\x67\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\ | |
521 | \x3c\x2f\x67\x3e\x0a\x20\x20\x20\x20\x3c\x2f\x67\x3e\x0a\x3c\x2f\ | |
522 | \x73\x76\x67\x3e\x0a\ | |
475 | \x00\x00\x02\xd7\ | |
476 | \x89\ | |
477 | \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ | |
478 | \x00\x00\x3c\x00\x00\x00\x3c\x08\x06\x00\x00\x00\x3a\xfc\xd9\x72\ | |
479 | \x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\x74\x77\x61\x72\x65\ | |
480 | \x00\x41\x64\x6f\x62\x65\x20\x49\x6d\x61\x67\x65\x52\x65\x61\x64\ | |
481 | \x79\x71\xc9\x65\x3c\x00\x00\x02\x79\x49\x44\x41\x54\x78\xda\xec\ | |
482 | \x9a\xd1\x6d\xc2\x30\x10\x40\x0d\x62\x80\x6c\x40\x3a\x01\xde\xa0\ | |
483 | \x19\x21\x23\xa4\x13\xc0\x06\xf5\x06\xa8\x13\xa4\x1b\xa4\x1b\x44\ | |
484 | \x4c\x10\x98\x20\x6c\x10\x36\x68\x8d\xe4\x48\xc8\x75\x82\xef\xb0\ | |
485 | \x0f\x87\xf8\xa4\xfb\x31\x89\xc3\xf3\x9d\xed\xbb\xb3\x17\x8c\x56\ | |
486 | \xb8\xd4\x5c\xea\x46\x6a\xa2\xda\x2e\x52\x4f\x52\x7f\xa4\x1e\xd9\ | |
487 | \x8b\xc8\x15\xb2\x91\xfa\x7b\x47\x5b\xf5\xec\x64\xe5\x6a\xc5\xca\ | |
488 | \x02\x54\xd7\xea\xc6\x03\x26\x05\xdb\x20\x60\x7b\x6d\xd4\x14\x98\ | |
489 | \x05\x6c\xaf\xdd\x54\x2c\xed\x02\xf6\xd6\xd2\x41\x8b\x70\x08\xdb\ | |
490 | \xab\x08\x15\x36\xf5\x00\xdb\x6b\x1a\x22\x70\xe9\x11\xb8\x9e\x93\ | |
491 | \x75\x9d\x59\x79\xe9\x10\x78\x4b\x30\xa8\xdb\x90\x2c\xdc\x12\x58\ | |
492 | \xb8\x0b\x05\x96\x13\xc0\xf6\xca\x43\x70\xe9\x8c\x38\x2e\x47\xcb\ | |
493 | \xea\x0e\x04\x57\x19\xcc\x59\xe9\x90\x6c\x08\x81\x37\x3e\x81\x3f\ | |
494 | \xb5\xb6\x2b\xfc\x97\xd4\x6f\xc3\x0a\x4d\xb9\x1b\xe8\x61\x6c\xae\ | |
495 | \xda\xfb\xb4\x93\xab\xb4\xf3\x0d\x02\x3c\x34\x57\xd3\x27\xbb\x34\ | |
496 | \x1f\xd8\xff\x4d\xf1\x3c\x68\x0e\xbf\x0f\xb4\x87\x96\xa4\x5f\x7c\ | |
497 | \x2f\x5a\x17\x36\x61\x59\xb2\x99\xc9\x12\x61\xc9\xf4\x55\x81\x4f\ | |
498 | \x13\x01\x4e\x7c\xbb\xf4\xfa\xc9\x0b\xd9\xd1\x62\xd5\x1e\xfc\x4f\ | |
499 | \x4b\x40\xc7\x63\x1f\x38\x3f\x71\x55\xe6\x90\x29\x89\x05\x4e\x2c\ | |
500 | \xdd\xdf\x87\x1c\x2c\x23\xaf\x03\xa6\xf3\x6e\x20\x80\xcf\x0d\x81\ | |
501 | \x07\x55\xf2\x90\x21\xff\xa3\x95\x0c\xd5\x94\x4b\xc0\xe0\xf8\x4c\ | |
502 | \x0f\xf3\x91\x67\x51\xd5\xce\x02\xd0\x61\x49\x00\x5c\x5a\x7e\x13\ | |
503 | \x5d\xe9\x4c\x98\x7d\x25\x91\x13\xbb\xf3\x58\x49\x69\xf7\xc8\x22\ | |
504 | \x51\x31\xfb\x22\x79\x4d\x58\xc4\x2b\x7d\xd5\xbe\x32\x80\x95\xb3\ | |
505 | \x00\xac\x5b\xfa\xae\x57\x71\x82\xb9\x5c\x01\x3c\xc9\x49\xaa\x3a\ | |
506 | \xb6\x1a\x36\x9a\x6b\x27\x8e\x0b\x7a\xad\xd6\xbf\x60\x44\xb5\xeb\ | |
507 | \x1a\xe0\x46\xdc\xd1\x36\xd5\x69\x1e\x94\xfb\x2c\xf0\x41\x2b\x93\ | |
508 | \xae\xa1\x75\xd8\x7b\xfd\xed\x7d\x84\x75\x02\x01\x8d\x39\x49\x6c\ | |
509 | \x80\xb0\x2d\xf3\x78\xac\x5a\x33\xd8\xe9\x7d\xa2\x06\xaa\xb3\xb4\ | |
510 | \xaa\xd0\xde\x2f\x98\xe7\x5a\xb5\x4d\x30\xd2\x58\x2c\x34\x99\xe1\ | |
511 | \xbd\x42\x0d\x48\xab\x3d\x5b\xa9\xdf\xf4\x81\xb2\xb9\x2e\x51\x50\ | |
512 | \x55\x0e\x3b\xcb\xed\x04\x13\x04\xec\x2c\xfb\x17\x94\x95\x06\xc8\ | |
513 | \xa2\x54\x1b\x2c\x68\xea\x6f\x0f\xe8\x13\x15\x60\x2c\x1e\x84\x4e\ | |
514 | \x95\x15\x21\x73\xe8\x6c\x28\x18\x40\x83\x85\x0f\xf6\xff\x30\x80\ | |
515 | \xb4\xa6\x84\xb9\x9a\x84\xdd\xaa\x82\xb9\xc7\xb5\x63\x7e\x73\xe2\ | |
516 | \x9a\x05\x78\xa3\x27\xf1\x10\x4b\x4f\xe2\x76\x5e\xaa\xc0\x1f\xb1\ | |
517 | \x78\x43\xb5\xe5\xb8\xb6\x78\xa1\xe0\x5b\x4b\x6b\x0a\x36\xa1\x1b\ | |
518 | \x78\xd8\xbc\x9a\x6c\x5f\x8d\x67\x4b\x11\x38\x02\x47\xe0\x08\x1c\ | |
519 | \x81\x03\x92\x15\xf2\x3d\x8e\x88\x6b\x6d\x82\x89\x35\x22\x73\x32\ | |
520 | \x65\x5f\xe4\x65\x1e\x4a\x15\xd1\xa5\x23\x70\x04\x9e\x8f\xfc\x09\ | |
521 | \x30\x00\xa0\x1c\x74\x67\x26\xea\x15\x76\x00\x00\x00\x00\x49\x45\ | |
522 | \x4e\x44\xae\x42\x60\x82\ | |
523 | 523 | " |
524 | 524 | |
525 | 525 | qt_resource_name = b"\ |
527 | 527 | \x00\x6f\xa6\x53\ |
528 | 528 | \x00\x69\ |
529 | 529 | \x00\x63\x00\x6f\x00\x6e\x00\x73\ |
530 | \x00\x10\ | |
531 | \x08\x12\xae\xa7\ | |
532 | \x00\x6d\ | |
533 | \x00\x65\x00\x64\x00\x69\x00\x61\x00\x2d\x00\x72\x00\x65\x00\x63\x00\x6f\x00\x72\x00\x64\x00\x2e\x00\x70\x00\x6e\x00\x67\ | |
530 | \x00\x0e\ | |
531 | \x04\x44\x35\x07\ | |
532 | \x00\x63\ | |
533 | \x00\x6f\x00\x6c\x00\x6c\x00\x65\x00\x63\x00\x74\x00\x69\x00\x6f\x00\x6e\x00\x2e\x00\x73\x00\x76\x00\x67\ | |
534 | 534 | \x00\x07\ |
535 | 535 | \x0a\x7a\x5a\x27\ |
536 | 536 | \x00\x74\ |
537 | 537 | \x00\x61\x00\x67\x00\x2e\x00\x73\x00\x76\x00\x67\ |
538 | \x00\x0e\ | |
539 | \x04\x44\x35\x07\ | |
540 | \x00\x63\ | |
541 | \x00\x6f\x00\x6c\x00\x6c\x00\x65\x00\x63\x00\x74\x00\x69\x00\x6f\x00\x6e\x00\x2e\x00\x73\x00\x76\x00\x67\ | |
542 | 538 | \x00\x08\ |
543 | 539 | \x05\x1c\x5a\x47\ |
544 | 540 | \x00\x61\ |
547 | 543 | \x0b\x9e\x57\x87\ |
548 | 544 | \x00\x64\ |
549 | 545 | \x00\x65\x00\x63\x00\x6b\x00\x2e\x00\x73\x00\x76\x00\x67\ |
546 | \x00\x09\ | |
547 | \x08\x97\x87\xa7\ | |
548 | \x00\x68\ | |
549 | \x00\x65\x00\x61\x00\x72\x00\x74\x00\x2e\x00\x73\x00\x76\x00\x67\ | |
550 | 550 | \x00\x0c\ |
551 | 551 | \x0e\xcd\x03\x47\ |
552 | 552 | \x00\x6e\ |
553 | 553 | \x00\x6f\x00\x74\x00\x65\x00\x74\x00\x79\x00\x70\x00\x65\x00\x2e\x00\x73\x00\x76\x00\x67\ |
554 | \x00\x09\ | |
555 | \x08\x97\x87\xa7\ | |
556 | \x00\x68\ | |
557 | \x00\x65\x00\x61\x00\x72\x00\x74\x00\x2e\x00\x73\x00\x76\x00\x67\ | |
554 | \x00\x10\ | |
555 | \x08\x12\xae\xa7\ | |
556 | \x00\x6d\ | |
557 | \x00\x65\x00\x64\x00\x69\x00\x61\x00\x2d\x00\x72\x00\x65\x00\x63\x00\x6f\x00\x72\x00\x64\x00\x2e\x00\x70\x00\x6e\x00\x67\ | |
558 | 558 | " |
559 | 559 | |
560 | 560 | qt_resource_struct = b"\ |
561 | 561 | \x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\ |
562 | 562 | \x00\x00\x00\x00\x00\x02\x00\x00\x00\x07\x00\x00\x00\x02\ |
563 | \x00\x00\x00\x4a\x00\x01\x00\x00\x00\x01\x00\x00\x07\x0f\ | |
564 | \x00\x00\x00\x6c\x00\x00\x00\x00\x00\x01\x00\x00\x09\x7b\ | |
565 | \x00\x00\x00\x10\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ | |
566 | \x00\x00\x00\xb6\x00\x00\x00\x00\x00\x01\x00\x00\x1a\xfd\ | |
567 | \x00\x00\x00\x36\x00\x00\x00\x00\x00\x01\x00\x00\x02\xdb\ | |
568 | \x00\x00\x00\x82\x00\x00\x00\x00\x00\x01\x00\x00\x10\x37\ | |
569 | \x00\x00\x00\x98\x00\x00\x00\x00\x00\x01\x00\x00\x15\xa4\ | |
563 | \x00\x00\x00\x10\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\ | |
564 | \x00\x00\x00\x46\x00\x00\x00\x00\x00\x01\x00\x00\x06\xa0\ | |
565 | \x00\x00\x00\xa8\x00\x00\x00\x00\x00\x01\x00\x00\x1c\x2c\ | |
566 | \x00\x00\x00\x72\x00\x00\x00\x00\x00\x01\x00\x00\x12\xc9\ | |
567 | \x00\x00\x00\x32\x00\x00\x00\x00\x00\x01\x00\x00\x02\x6c\ | |
568 | \x00\x00\x00\x5c\x00\x00\x00\x00\x00\x01\x00\x00\x0d\x5c\ | |
569 | \x00\x00\x00\x8a\x00\x00\x00\x00\x00\x01\x00\x00\x16\xd3\ | |
570 | 570 | " |
571 | 571 | |
572 | 572 | def qInitResources(): |
131 | 131 | self.actionOpenPluginFolder.setText(_("&Open Add-ons Folder...")) |
132 | 132 | self.actionDonate.setText(_("&Support Anki...")) |
133 | 133 | self.actionDownloadSharedPlugin.setText(_("&Browse and Install...")) |
134 | self.actionFullDatabaseCheck.setText(_("&Check Database...")) | |
134 | self.actionFullDatabaseCheck.setText(_("&Check Database")) | |
135 | 135 | self.actionDocumentation.setText(_("&Guide...")) |
136 | 136 | self.actionDocumentation.setShortcut(_("F1")) |
137 | self.actionSwitchProfile.setText(_("&Switch Profile...")) | |
137 | self.actionSwitchProfile.setText(_("&Switch Profile")) | |
138 | 138 | self.actionSwitchProfile.setShortcut(_("Ctrl+Shift+P")) |
139 | 139 | self.actionExport.setText(_("&Export...")) |
140 | 140 | self.actionExport.setShortcut(_("Ctrl+E")) |
145 | 145 | self.actionEmptyCards.setText(_("Empty Cards...")) |
146 | 146 | self.actionCreateFiltered.setText(_("Create Filtered Deck...")) |
147 | 147 | self.actionCreateFiltered.setShortcut(_("F")) |
148 | self.actionNoteTypes.setText(_("Manage Note Types...")) | |
148 | self.actionNoteTypes.setText(_("Manage Note Types")) | |
149 | 149 | self.actionNoteTypes.setShortcut(_("Ctrl+Shift+N")) |
150 | self.actionAdd_ons.setText(_("Add-ons...")) | |
150 | self.actionAdd_ons.setText(_("Add-ons")) | |
151 | 151 | self.actionAdd_ons.setShortcut(_("Ctrl+Shift+A")) |
152 | 152 | |
153 | 153 | from . import icons_rc |
33 | 33 | sizePolicy.setVerticalStretch(0) |
34 | 34 | sizePolicy.setHeightForWidth(self.lang.sizePolicy().hasHeightForWidth()) |
35 | 35 | self.lang.setSizePolicy(sizePolicy) |
36 | self.lang.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToMinimumContentsLengthWithIcon) | |
36 | 37 | self.lang.setObjectName("lang") |
37 | 38 | self.horizontalLayout_2.addWidget(self.lang) |
38 | 39 | self.verticalLayout.addLayout(self.horizontalLayout_2) |
364 | 364 | |
365 | 365 | def setupApkgImport(mw, importer): |
366 | 366 | base = os.path.basename(importer.file).lower() |
367 | full = (base == "collection.apkg") or re.match("backup-.*\\.apkg", base) | |
367 | full = ((base == "collection.apkg") or | |
368 | re.match("backup-.*\\.apkg", base) or | |
369 | base.endswith(".colpkg")) | |
368 | 370 | if not full: |
369 | 371 | # adding |
370 | 372 | return True |
371 | 373 | backup = re.match("backup-.*\\.apkg", base) |
372 | 374 | if not mw.restoringBackup and not askUser(_("""\ |
373 | 375 | This will delete your existing collection and replace it with the data in \ |
374 | the file you're importing. Are you sure?"""), msgfunc=QMessageBox.warning): | |
376 | the file you're importing. Are you sure?"""), msgfunc=QMessageBox.warning, | |
377 | defaultno=True): | |
375 | 378 | return False |
376 | 379 | # schedule replacement; don't do it immediately as we may have been |
377 | 380 | # called as part of the startup routine |
210 | 210 | def doOpen(path): |
211 | 211 | self._openBackup(path) |
212 | 212 | getFile(self.profileDiag, _("Revert to backup"), |
213 | cb=doOpen, filter="*.apkg", dir=self.pm.backupFolder()) | |
213 | cb=doOpen, filter="*.colpkg", dir=self.pm.backupFolder()) | |
214 | 214 | |
215 | 215 | def _openBackup(self, path): |
216 | 216 | try: |
279 | 279 | for w in self.app.topLevelWidgets(): |
280 | 280 | if w.isVisible(): |
281 | 281 | # windows with this property are safe to close immediately |
282 | if getattr(w, "silentlyClose"): | |
282 | if getattr(w, "silentlyClose", None): | |
283 | 283 | w.close() |
284 | 284 | else: |
285 | 285 | showWarning(f"Window should have been closed: {w}") |
365 | 365 | Thread.__init__(self) |
366 | 366 | self.path = path |
367 | 367 | self.data = data |
368 | # make sure we complete before exiting the program | |
369 | self.setDaemon(True) | |
370 | 368 | # create the file in calling thread to ensure the same |
371 | 369 | # file is not created twice |
372 | 370 | open(self.path, "wb").close() |
385 | 383 | path = self.pm.collectionPath() |
386 | 384 | |
387 | 385 | # do backup |
388 | fname = time.strftime("backup-%Y-%m-%d-%H.%M.%S.apkg", time.localtime(time.time())) | |
386 | fname = time.strftime("backup-%Y-%m-%d-%H.%M.%S.colpkg", time.localtime(time.time())) | |
389 | 387 | newpath = os.path.join(dir, fname) |
390 | 388 | data = open(path, "rb").read() |
391 | 389 | b = self.BackupThread(newpath, data) |
395 | 393 | backups = [] |
396 | 394 | for file in os.listdir(dir): |
397 | 395 | # only look for new-style format |
398 | m = re.match("backup-\{4}-.+.apkg", file) | |
396 | m = re.match("backup-\d{4}-\d{2}-.+.colpkg", file) | |
399 | 397 | if not m: |
400 | 398 | continue |
401 | 399 | backups.append(file) |
785 | 783 | aqt.dialogs.open("DeckStats", self) |
786 | 784 | |
787 | 785 | def onPrefs(self): |
788 | import aqt.preferences | |
789 | aqt.preferences.Preferences(self) | |
786 | aqt.dialogs.open("Preferences", self) | |
790 | 787 | |
791 | 788 | def onNoteTypes(self): |
792 | 789 | import aqt.models |
106 | 106 | newPath = os.path.join(_exportFolder, path[len(targetPath)+1:]) |
107 | 107 | return newPath |
108 | 108 | return path |
109 | ||
110 | # work around Windows machines with incorrect mime type | |
111 | RequestHandler.extensions_map['.css'] = "text/css" |
21 | 21 | self.form.buttonBox.button(QDialogButtonBox.Help).setAutoDefault(False) |
22 | 22 | self.form.buttonBox.button(QDialogButtonBox.Close).setAutoDefault(False) |
23 | 23 | self.form.buttonBox.helpRequested.connect(lambda: openHelp("profileprefs")) |
24 | self.silentlyClose = True | |
24 | 25 | self.setupLang() |
25 | 26 | self.setupCollection() |
26 | 27 | self.setupNetwork() |
39 | 40 | self.mw.pm.save() |
40 | 41 | self.mw.reset() |
41 | 42 | self.done(0) |
43 | aqt.dialogs.markClosed("Preferences") | |
42 | 44 | |
43 | 45 | def reject(self): |
44 | 46 | self.accept() |
166 | 166 | playFromText(q) |
167 | 167 | # render & update bottom |
168 | 168 | q = self._mungeQA(q) |
169 | q = runFilter("prepareQuestion", q) | |
169 | q = runFilter("prepareQA", q, c, "reviewQuestion") | |
170 | 170 | |
171 | 171 | bodyclass = "card card%d" % (c.ord+1) |
172 | 172 | |
210 | 210 | if self.autoplay(c): |
211 | 211 | playFromText(a) |
212 | 212 | a = self._mungeQA(a) |
213 | a = runFilter("prepareAnswer", a) | |
213 | a = runFilter("prepareQA", a, c, "reviewAnswer") | |
214 | 214 | # render and update bottom |
215 | 215 | self.web.eval("_showAnswer(%s);" % json.dumps(a)) |
216 | 216 | self._showEaseButtons() |
4 | 4 | from aqt.qt import * |
5 | 5 | import os, time |
6 | 6 | from aqt.utils import saveGeom, restoreGeom, maybeHideClose, addCloseShortcut, \ |
7 | tooltip | |
7 | tooltip, getSaveFile | |
8 | 8 | import aqt |
9 | 9 | |
10 | 10 | # Deck Stats |
54 | 54 | name = time.strftime("-%Y-%m-%d@%H-%M-%S.pdf", |
55 | 55 | time.localtime(time.time())) |
56 | 56 | name = "anki-"+_("stats")+name |
57 | desktopPath = QStandardPaths.writableLocation( | |
58 | QStandardPaths.DesktopLocation) | |
59 | if not os.path.exists(desktopPath): | |
60 | os.mkdir(desktopPath) | |
61 | path = os.path.join(desktopPath, name) | |
62 | return path | |
57 | file = getSaveFile(self, title=_("Save PDF"), | |
58 | dir_description="stats", | |
59 | key="stats", | |
60 | ext=".pdf", | |
61 | fname=name) | |
62 | return file | |
63 | 63 | |
64 | 64 | def saveImage(self): |
65 | 65 | path = self._imagePath() |
66 | if not path: | |
67 | return | |
66 | 68 | self.form.web.page().printToPdf(path) |
67 | tooltip(_("A PDF file was saved to your desktop.")) | |
69 | tooltip(_("Saved.")) | |
68 | 70 | |
69 | 71 | def changePeriod(self, n): |
70 | 72 | self.period = n |
65 | 65 | diag.setWindowTitle(title) |
66 | 66 | layout = QVBoxLayout(diag) |
67 | 67 | diag.setLayout(layout) |
68 | text = QTextEdit() | |
69 | text.setReadOnly(True) | |
68 | text = QTextBrowser() | |
69 | text.setOpenExternalLinks(True) | |
70 | 70 | if type == "text": |
71 | 71 | text.setPlainText(txt) |
72 | 72 | else: |
278 | 278 | """Ask the user for a file to save. Use DIR_DESCRIPTION as config |
279 | 279 | variable. The file dialog will default to open with FNAME.""" |
280 | 280 | config_key = dir_description + 'Directory' |
281 | base = aqt.mw.pm.profile.get(config_key, aqt.mw.pm.base) | |
281 | ||
282 | defaultPath = QStandardPaths.writableLocation(QStandardPaths.DocumentsLocation) | |
283 | base = aqt.mw.pm.profile.get(config_key, defaultPath) | |
282 | 284 | path = os.path.join(base, fname) |
283 | 285 | file = QFileDialog.getSaveFileName( |
284 | 286 | parent, title, path, "{0} (*{1})".format(key, ext), |
76 | 76 | <string>New Cards</string> |
77 | 77 | </attribute> |
78 | 78 | <layout class="QVBoxLayout" name="verticalLayout_2"> |
79 | <property name="margin"> | |
80 | <number>12</number> | |
81 | </property> | |
79 | 82 | <item> |
80 | 83 | <layout class="QGridLayout" name="gridLayout"> |
84 | <property name="spacing"> | |
85 | <number>12</number> | |
86 | </property> | |
81 | 87 | <item row="5" column="2"> |
82 | 88 | <widget class="QLabel" name="label_27"> |
83 | 89 | <property name="text"> |
220 | 226 | <string>Reviews</string> |
221 | 227 | </attribute> |
222 | 228 | <layout class="QVBoxLayout" name="verticalLayout_4"> |
229 | <property name="margin"> | |
230 | <number>12</number> | |
231 | </property> | |
223 | 232 | <item> |
224 | 233 | <layout class="QGridLayout" name="gridLayout_3"> |
234 | <property name="spacing"> | |
235 | <number>12</number> | |
236 | </property> | |
225 | 237 | <item row="1" column="0"> |
226 | 238 | <widget class="QLabel" name="label_20"> |
227 | 239 | <property name="text"> |
359 | 371 | <string>Lapses</string> |
360 | 372 | </attribute> |
361 | 373 | <layout class="QVBoxLayout" name="verticalLayout_3"> |
374 | <property name="margin"> | |
375 | <number>12</number> | |
376 | </property> | |
362 | 377 | <item> |
363 | 378 | <layout class="QGridLayout" name="gridLayout_2"> |
379 | <property name="spacing"> | |
380 | <number>12</number> | |
381 | </property> | |
364 | 382 | <item row="0" column="0"> |
365 | 383 | <widget class="QLabel" name="label_17"> |
366 | 384 | <property name="text"> |
502 | 520 | <string>General</string> |
503 | 521 | </attribute> |
504 | 522 | <layout class="QVBoxLayout" name="verticalLayout_6"> |
523 | <property name="margin"> | |
524 | <number>12</number> | |
525 | </property> | |
505 | 526 | <item> |
506 | 527 | <layout class="QGridLayout" name="gridLayout_5"> |
528 | <property name="spacing"> | |
529 | <number>12</number> | |
530 | </property> | |
507 | 531 | <item row="0" column="0"> |
508 | 532 | <widget class="QLabel" name="label_25"> |
509 | 533 | <property name="text"> |
577 | 601 | <string>Description</string> |
578 | 602 | </attribute> |
579 | 603 | <layout class="QVBoxLayout" name="verticalLayout_5"> |
604 | <property name="spacing"> | |
605 | <number>12</number> | |
606 | </property> | |
607 | <property name="margin"> | |
608 | <number>12</number> | |
609 | </property> | |
580 | 610 | <item> |
581 | 611 | <widget class="QLabel" name="label_22"> |
582 | 612 | <property name="text"> |
164 | 164 | </action> |
165 | 165 | <action name="actionFullDatabaseCheck"> |
166 | 166 | <property name="text"> |
167 | <string>&Check Database...</string> | |
167 | <string>&Check Database</string> | |
168 | 168 | </property> |
169 | 169 | </action> |
170 | 170 | <action name="actionDocumentation"> |
177 | 177 | </action> |
178 | 178 | <action name="actionSwitchProfile"> |
179 | 179 | <property name="text"> |
180 | <string>&Switch Profile...</string> | |
180 | <string>&Switch Profile</string> | |
181 | 181 | </property> |
182 | 182 | <property name="shortcut"> |
183 | 183 | <string>Ctrl+Shift+P</string> |
222 | 222 | </action> |
223 | 223 | <action name="actionNoteTypes"> |
224 | 224 | <property name="text"> |
225 | <string>Manage Note Types...</string> | |
225 | <string>Manage Note Types</string> | |
226 | 226 | </property> |
227 | 227 | <property name="shortcut"> |
228 | 228 | <string>Ctrl+Shift+N</string> |
230 | 230 | </action> |
231 | 231 | <action name="actionAdd_ons"> |
232 | 232 | <property name="text"> |
233 | <string>Add-ons...</string> | |
233 | <string>Add-ons</string> | |
234 | 234 | </property> |
235 | 235 | <property name="shortcut"> |
236 | 236 | <string>Ctrl+Shift+A</string> |
48 | 48 | <horstretch>0</horstretch> |
49 | 49 | <verstretch>0</verstretch> |
50 | 50 | </sizepolicy> |
51 | </property> | |
52 | <property name="sizeAdjustPolicy"> | |
53 | <enum>QComboBox::AdjustToMinimumContentsLengthWithIcon</enum> | |
51 | 54 | </property> |
52 | 55 | </widget> |
53 | 56 | </item> |
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
16 | 16 | # set flag 2 |
17 | 17 | col.setUserFlag(2, [c.id]) |
18 | 18 | c.load() |
19 | print("db is", col.db.all("select id, flags from cards")) | |
20 | 19 | assert c.userFlag() == 2 |
21 | 20 | assert c.flags & origBits == origBits |
22 | 21 | assert len(col.findCards("flag:0")) == 0 |
257 | 257 | } |
258 | 258 | } |
259 | 259 | |
260 | function onCutOrCopy() { | |
261 | pycmd("cutOrCopy"); | |
262 | return true; | |
263 | } | |
264 | ||
260 | 265 | function setFields(fields, prewrap) { |
261 | 266 | var txt = ""; |
262 | 267 | for (var i = 0; i < fields.length; i++) { |
269 | 274 | txt += "<div id=f{0} onkeydown='onKey();' oninput='checkForEmptyField()' onmouseup='onKey();'".format(i); |
270 | 275 | txt += " onfocus='onFocus(this);' onblur='onBlur();' class=field "; |
271 | 276 | txt += "ondragover='onDragOver(this);' onpaste='onPaste(this);' "; |
277 | txt += "oncopy='onCutOrCopy(this);' oncut='onCutOrCopy(this);' "; | |
272 | 278 | txt += "contentEditable=true class=field>{0}</div>".format(f); |
273 | 279 | txt += "</td></tr>"; |
274 | 280 | } |
2 | 2 | extensions: ["tex2jax.js"], |
3 | 3 | TeX: { |
4 | 4 | extensions: ["AMSmath.js","AMSsymbols.js","noErrors.js","noUndefined.js", "mhchem.js"] |
5 | }, | |
6 | tex2jax: { | |
7 | displayMath: [ ["\\[","\\]"] ], | |
5 | 8 | }, |
6 | 9 | messageStyle: "none", |
7 | 10 | skipStartupTypeset: true, |
25 | 25 | } catch(err) { |
26 | 26 | qa.text("Invalid HTML on card: "+err); |
27 | 27 | } |
28 | _removeStylingFromMathjaxCloze(); | |
29 | 28 | _runHook(onUpdateHook); |
30 | 29 | |
31 | 30 | // don't allow drags of images, which cause them to be deleted |
105 | 104 | pycmd("ans"); |
106 | 105 | } |
107 | 106 | } |
108 | ||
109 | function _removeStylingFromMathjaxCloze() { | |
110 | $(".cloze").each(function (i) { | |
111 | if (_clozeIsInsideMathjax(this)) { | |
112 | this.outerHTML = this.innerHTML; | |
113 | } | |
114 | }); | |
115 | } | |
116 | ||
117 | function _clozeIsInsideMathjax(node) { | |
118 | if (!node.previousSibling || node.previousSibling.nodeType !== 3) { | |
119 | return; | |
120 | } | |
121 | // look for mathjax opening in previous text | |
122 | return /\\\(|\$\$/.test(node.previousSibling.textContent); | |
123 | }⏎ |