diff options
author | Thanos Apollo <[email protected]> | 2022-08-04 09:50:48 +0300 |
---|---|---|
committer | Thanos Apollo <[email protected]> | 2022-08-04 09:50:48 +0300 |
commit | 4ddb7273098bee179bb77e0937e560fc0100960c (patch) | |
tree | cebc2f9412e45910408a7885ca78d7dedb77de78 | |
parent | e83759ae9d0513024e390810ddcb18ffdd84675e (diff) |
Add anki addons
175 files changed, 19466 insertions, 0 deletions
diff --git a/.local/share/Anki2/addons21/Anki_connect/__init__.py b/.local/share/Anki2/addons21/Anki_connect/__init__.py new file mode 100644 index 0000000..93c92d0 --- /dev/null +++ b/.local/share/Anki2/addons21/Anki_connect/__init__.py @@ -0,0 +1,1688 @@ +# Copyright 2016-2021 Alex Yatskov +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import aqt + +anki_version = tuple(int(segment) for segment in aqt.appVersion.split(".")) + +if anki_version < (2, 1, 45): + raise Exception("Minimum Anki version supported: 2.1.45") + +import base64 +import glob +import hashlib +import inspect +import json +import os +import os.path +import platform +import re +import time +import unicodedata + +import anki +import anki.exporting +import anki.storage +from anki.cards import Card +from anki.consts import MODEL_CLOZE +from anki.exporting import AnkiPackageExporter +from anki.importing import AnkiPackageImporter +from anki.notes import Note +from anki.errors import NotFoundError +from aqt.qt import Qt, QTimer, QMessageBox, QCheckBox + +from .web import format_exception_reply, format_success_reply +from .edit import Edit +from . import web, util + + +# +# AnkiConnect +# + +class AnkiConnect: + def __init__(self): + self.log = None + self.timer = None + self.server = web.WebServer(self.handler) + + def initLogging(self): + logPath = util.setting('apiLogPath') + if logPath is not None: + self.log = open(logPath, 'w') + + def startWebServer(self): + try: + self.server.listen() + + # only keep reference to prevent garbage collection + self.timer = QTimer() + self.timer.timeout.connect(self.advance) + self.timer.start(util.setting('apiPollInterval')) + except: + QMessageBox.critical( + self.window(), + 'AnkiConnect', + 'Failed to listen on port {}.\nMake sure it is available and is not in use.'.format(util.setting('webBindPort')) + ) + + def save_model(self, models, ankiModel): + models.update_dict(ankiModel) + + def logEvent(self, name, data): + if self.log is not None: + self.log.write('[{}]\n'.format(name)) + json.dump(data, self.log, indent=4, sort_keys=True) + self.log.write('\n\n') + self.log.flush() + + + def advance(self): + self.server.advance() + + + def handler(self, request): + self.logEvent('request', request) + + name = request.get('action', '') + version = request.get('version', 4) + params = request.get('params', {}) + key = request.get('key') + + try: + if key != util.setting('apiKey') and name != 'requestPermission': + raise Exception('valid api key must be provided') + + method = None + + for methodName, methodInst in inspect.getmembers(self, predicate=inspect.ismethod): + apiVersionLast = 0 + apiNameLast = None + + if getattr(methodInst, 'api', False): + for apiVersion, apiName in getattr(methodInst, 'versions', []): + if apiVersionLast < apiVersion <= version: + apiVersionLast = apiVersion + apiNameLast = apiName + + if apiNameLast is None and apiVersionLast == 0: + apiNameLast = methodName + + if apiNameLast is not None and apiNameLast == name: + method = methodInst + break + + if method is None: + raise Exception('unsupported action') + + api_return_value = methodInst(**params) + reply = format_success_reply(version, api_return_value) + + except Exception as e: + reply = format_exception_reply(version, e) + + self.logEvent('reply', reply) + return reply + + + def window(self): + return aqt.mw + + + def reviewer(self): + reviewer = self.window().reviewer + if reviewer is None: + raise Exception('reviewer is not available') + + return reviewer + + + def collection(self): + collection = self.window().col + if collection is None: + raise Exception('collection is not available') + + return collection + + + def decks(self): + decks = self.collection().decks + if decks is None: + raise Exception('decks are not available') + + return decks + + + def scheduler(self): + scheduler = self.collection().sched + if scheduler is None: + raise Exception('scheduler is not available') + + return scheduler + + + def database(self): + database = self.collection().db + if database is None: + raise Exception('database is not available') + + return database + + + def media(self): + media = self.collection().media + if media is None: + raise Exception('media is not available') + + return media + + + def startEditing(self): + self.window().requireReset() + + + def stopEditing(self): + if self.collection() is not None: + self.window().maybeReset() + + + def createNote(self, note): + collection = self.collection() + + model = collection.models.byName(note['modelName']) + if model is None: + raise Exception('model was not found: {}'.format(note['modelName'])) + + deck = collection.decks.byName(note['deckName']) + if deck is None: + raise Exception('deck was not found: {}'.format(note['deckName'])) + + ankiNote = anki.notes.Note(collection, model) + ankiNote.model()['did'] = deck['id'] + if 'tags' in note: + ankiNote.tags = note['tags'] + + for name, value in note['fields'].items(): + for ankiName in ankiNote.keys(): + if name.lower() == ankiName.lower(): + ankiNote[ankiName] = value + break + + allowDuplicate = False + duplicateScope = None + duplicateScopeDeckName = None + duplicateScopeCheckChildren = False + duplicateScopeCheckAllModels = False + + if 'options' in note: + options = note['options'] + if 'allowDuplicate' in options: + allowDuplicate = options['allowDuplicate'] + if type(allowDuplicate) is not bool: + raise Exception('option parameter "allowDuplicate" must be boolean') + if 'duplicateScope' in options: + duplicateScope = options['duplicateScope'] + if 'duplicateScopeOptions' in options: + duplicateScopeOptions = options['duplicateScopeOptions'] + if 'deckName' in duplicateScopeOptions: + duplicateScopeDeckName = duplicateScopeOptions['deckName'] + if 'checkChildren' in duplicateScopeOptions: + duplicateScopeCheckChildren = duplicateScopeOptions['checkChildren'] + if type(duplicateScopeCheckChildren) is not bool: + raise Exception('option parameter "duplicateScopeOptions.checkChildren" must be boolean') + if 'checkAllModels' in duplicateScopeOptions: + duplicateScopeCheckAllModels = duplicateScopeOptions['checkAllModels'] + if type(duplicateScopeCheckAllModels) is not bool: + raise Exception('option parameter "duplicateScopeOptions.checkAllModels" must be boolean') + + duplicateOrEmpty = self.isNoteDuplicateOrEmptyInScope( + ankiNote, + deck, + collection, + duplicateScope, + duplicateScopeDeckName, + duplicateScopeCheckChildren, + duplicateScopeCheckAllModels + ) + + if duplicateOrEmpty == 1: + raise Exception('cannot create note because it is empty') + elif duplicateOrEmpty == 2: + if allowDuplicate: + return ankiNote + raise Exception('cannot create note because it is a duplicate') + elif duplicateOrEmpty == 0: + return ankiNote + else: + raise Exception('cannot create note for unknown reason') + + + def isNoteDuplicateOrEmptyInScope( + self, + note, + deck, + collection, + duplicateScope, + duplicateScopeDeckName, + duplicateScopeCheckChildren, + duplicateScopeCheckAllModels + ): + # Returns: 1 if first is empty, 2 if first is a duplicate, 0 otherwise. + + # note.dupeOrEmpty returns if a note is a global duplicate with the specific model. + # This is used as the default check, and the rest of this function is manually + # checking if the note is a duplicate with additional options. + if duplicateScope != 'deck' and not duplicateScopeCheckAllModels: + return note.dupeOrEmpty() or 0 + + # Primary field for uniqueness + val = note.fields[0] + if not val.strip(): + return 1 + csum = anki.utils.fieldChecksum(val) + + # Create dictionary of deck ids + dids = None + if duplicateScope == 'deck': + did = deck['id'] + if duplicateScopeDeckName is not None: + deck2 = collection.decks.byName(duplicateScopeDeckName) + if deck2 is None: + # Invalid deck, so cannot be duplicate + return 0 + did = deck2['id'] + + dids = {did: True} + if duplicateScopeCheckChildren: + for kv in collection.decks.children(did): + dids[kv[1]] = True + + # Build query + query = 'select id from notes where csum=?' + queryArgs = [csum] + if note.id: + query += ' and id!=?' + queryArgs.append(note.id) + if not duplicateScopeCheckAllModels: + query += ' and mid=?' + queryArgs.append(note.mid) + + # Search + for noteId in note.col.db.list(query, *queryArgs): + if dids is None: + # Duplicate note exists in the collection + return 2 + # Validate that a card exists in one of the specified decks + for cardDeckId in note.col.db.list('select did from cards where nid=?', noteId): + if cardDeckId in dids: + return 2 + + # Not a duplicate + return 0 + + def getCard(self, card_id: int) -> Card: + try: + return self.collection().getCard(card_id) + except NotFoundError: + raise NotFoundError('Card was not found: {}'.format(card_id)) + + def getNote(self, note_id: int) -> Note: + try: + return self.collection().getNote(note_id) + except NotFoundError: + raise NotFoundError('Note was not found: {}'.format(note_id)) + + def deckStatsToJson(self, due_tree): + deckStats = {'deck_id': due_tree.deck_id, + 'name': due_tree.name, + 'new_count': due_tree.new_count, + 'learn_count': due_tree.learn_count, + 'review_count': due_tree.review_count} + if anki_version > (2, 1, 46): + # total_in_deck is not supported on lower Anki versions + deckStats['total_in_deck'] = due_tree.total_in_deck + return deckStats + + def collectDeckTreeChildren(self, parent_node): + allNodes = {parent_node.deck_id: parent_node} + for child in parent_node.children: + for deckId, childNode in self.collectDeckTreeChildren(child).items(): + allNodes[deckId] = childNode + return allNodes + + # + # Miscellaneous + # + + @util.api() + def version(self): + return util.setting('apiVersion') + + + @util.api() + def requestPermission(self, origin, allowed): + results = { + "permission": "denied", + } + + if allowed: + results = { + "permission": "granted", + "requireApikey": bool(util.setting('apiKey')), + "version": util.setting('apiVersion') + } + + elif origin in util.setting('ignoreOriginList'): + pass # defaults to denied + + else: # prompt the user + msg = QMessageBox(None) + msg.setWindowTitle("A website wants to access to Anki") + msg.setText('"{}" requests permission to use Anki through AnkiConnect. Do you want to give it access?'.format(origin)) + msg.setInformativeText("By granting permission, you'll allow the website to modify your collection on your behalf, including the execution of destructive actions such as deck deletion.") + msg.setWindowIcon(self.window().windowIcon()) + msg.setIcon(QMessageBox.Question) + msg.setStandardButtons(QMessageBox.Yes|QMessageBox.No) + msg.setDefaultButton(QMessageBox.No) + msg.setCheckBox(QCheckBox(text='Ignore further requests from "{}"'.format(origin), parent=msg)) + msg.setWindowFlags(Qt.WindowStaysOnTopHint) + pressedButton = msg.exec_() + + if pressedButton == QMessageBox.Yes: + config = aqt.mw.addonManager.getConfig(__name__) + config["webCorsOriginList"] = util.setting('webCorsOriginList') + config["webCorsOriginList"].append(origin) + aqt.mw.addonManager.writeConfig(__name__, config) + results = { + "permission": "granted", + "requireApikey": bool(util.setting('apiKey')), + "version": util.setting('apiVersion') + } + + # if the origin isn't an empty string, the user clicks "No", and the ignore box is checked + elif origin and pressedButton == QMessageBox.No and msg.checkBox().isChecked(): + config = aqt.mw.addonManager.getConfig(__name__) + config["ignoreOriginList"] = util.setting('ignoreOriginList') + config["ignoreOriginList"].append(origin) + aqt.mw.addonManager.writeConfig(__name__, config) + + # else defaults to denied + + return results + + + @util.api() + def getProfiles(self): + return self.window().pm.profiles() + + + @util.api() + def loadProfile(self, name): + if name not in self.window().pm.profiles(): + return False + + if self.window().isVisible(): + cur_profile = self.window().pm.name + if cur_profile != name: + self.window().unloadProfileAndShowProfileManager() + + def waiter(): + # This function waits until main window is closed + # It's needed cause sync can take quite some time + # And if we call loadProfile until sync is ended things will go wrong + if self.window().isVisible(): + QTimer.singleShot(1000, waiter) + else: + self.loadProfile(name) + + waiter() + else: + self.window().pm.load(name) + self.window().loadProfile() + self.window().profileDiag.closeWithoutQuitting() + + return True + + + @util.api() + def sync(self): + self.window().onSync() + + + @util.api() + def multi(self, actions): + return list(map(self.handler, actions)) + + + @util.api() + def getNumCardsReviewedToday(self): + return self.database().scalar('select count() from revlog where id > ?', (self.scheduler().dayCutoff - 86400) * 1000) + + @util.api() + def getNumCardsReviewedByDay(self): + return self.database().all('select date(id/1000 - ?, "unixepoch", "localtime") as day, count() from revlog group by day order by day desc', + int(time.strftime("%H", time.localtime(self.scheduler().dayCutoff))) * 3600) + + + @util.api() + def getCollectionStatsHTML(self, wholeCollection=True): + stats = self.collection().stats() + stats.wholeCollection = wholeCollection + return stats.report() + + + # + # Decks + # + + @util.api() + def deckNames(self): + return self.decks().allNames() + + + @util.api() + def deckNamesAndIds(self): + decks = {} + for deck in self.deckNames(): + decks[deck] = self.decks().id(deck) + + return decks + + + @util.api() + def getDecks(self, cards): + decks = {} + for card in cards: + did = self.database().scalar('select did from cards where id=?', card) + deck = self.decks().get(did)['name'] + if deck in decks: + decks[deck].append(card) + else: + decks[deck] = [card] + + return decks + + + @util.api() + def createDeck(self, deck): + try: + self.startEditing() + did = self.decks().id(deck) + finally: + self.stopEditing() + + return did + + + @util.api() + def changeDeck(self, cards, deck): + self.startEditing() + + did = self.collection().decks.id(deck) + mod = anki.utils.intTime() + usn = self.collection().usn() + + # normal cards + scids = anki.utils.ids2str(cards) + # remove any cards from filtered deck first + self.collection().sched.remFromDyn(cards) + + # then move into new deck + self.collection().db.execute('update cards set usn=?, mod=?, did=? where id in ' + scids, usn, mod, did) + self.stopEditing() + + + @util.api() + def deleteDecks(self, decks, cardsToo=False): + if not cardsToo: + # since f592672fa952260655881a75a2e3c921b2e23857 (2.1.28) + # (see anki$ git log "-Gassert cardsToo") + # you can't delete decks without deleting cards as well. + # however, since 62c23c6816adf912776b9378c008a52bb50b2e8d (2.1.45) + # passing cardsToo to `rem` (long deprecated) won't raise an error! + # this is dangerous, so let's raise our own exception + raise Exception("Since Anki 2.1.28 it's not possible " + "to delete decks without deleting cards as well") + try: + self.startEditing() + decks = filter(lambda d: d in self.deckNames(), decks) + for deck in decks: + did = self.decks().id(deck) + self.decks().rem(did, cardsToo=cardsToo) + finally: + self.stopEditing() + + + @util.api() + def getDeckConfig(self, deck): + if deck not in self.deckNames(): + return False + + collection = self.collection() + did = collection.decks.id(deck) + return collection.decks.confForDid(did) + + + @util.api() + def saveDeckConfig(self, config): + collection = self.collection() + + config['id'] = str(config['id']) + config['mod'] = anki.utils.intTime() + config['usn'] = collection.usn() + if int(config['id']) not in [c['id'] for c in collection.decks.all_config()]: + return False + try: + collection.decks.save(config) + collection.decks.updateConf(config) + except: + return False + return True + + + @util.api() + def setDeckConfigId(self, decks, configId): + configId = int(configId) + for deck in decks: + if not deck in self.deckNames(): + return False + + collection = self.collection() + + for deck in decks: + try: + did = str(collection.decks.id(deck)) + deck_dict = aqt.mw.col.decks.decks[did] + deck_dict['conf'] = configId + collection.decks.save(deck_dict) + except: + return False + + return True + + + @util.api() + def cloneDeckConfigId(self, name, cloneFrom='1'): + configId = int(cloneFrom) + collection = self.collection() + if configId not in [c['id'] for c in collection.decks.all_config()]: + return False + + config = collection.decks.getConf(configId) + return collection.decks.confId(name, config) + + + @util.api() + def removeDeckConfigId(self, configId): + collection = self.collection() + if int(configId) not in [c['id'] for c in collection.decks.all_config()]: + return False + + collection.decks.remConf(configId) + return True + + @util.api() + def getDeckStats(self, decks): + collection = self.collection() + scheduler = self.scheduler() + responseDict = {} + deckIds = list(map(lambda d: collection.decks.id(d), decks)) + + allDeckNodes = self.collectDeckTreeChildren(scheduler.deck_due_tree()) + for deckId, deckNode in allDeckNodes.items(): + if deckId in deckIds: + responseDict[deckId] = self.deckStatsToJson(deckNode) + return responseDict + + @util.api() + def storeMediaFile(self, filename, data=None, path=None, url=None, skipHash=None, deleteExisting=True): + if not (data or path or url): + raise Exception('You must provide a "data", "path", or "url" field.') + if data: + mediaData = base64.b64decode(data) + elif path: + with open(path, 'rb') as f: + mediaData = f.read() + elif url: + mediaData = util.download(url) + + if skipHash is None: + skip = False + else: + m = hashlib.md5() + m.update(mediaData) + skip = skipHash == m.hexdigest() + + if skip: + return None + if deleteExisting: + self.deleteMediaFile(filename) + return self.media().writeData(filename, mediaData) + + + @util.api() + def retrieveMediaFile(self, filename): + filename = os.path.basename(filename) + filename = unicodedata.normalize('NFC', filename) + filename = self.media().stripIllegal(filename) + + path = os.path.join(self.media().dir(), filename) + if os.path.exists(path): + with open(path, 'rb') as file: + return base64.b64encode(file.read()).decode('ascii') + + return False + + + @util.api() + def getMediaFilesNames(self, pattern='*'): + path = os.path.join(self.media().dir(), pattern) + return [os.path.basename(p) for p in glob.glob(path)] + + + @util.api() + def deleteMediaFile(self, filename): + try: + self.media().syncDelete(filename) + except AttributeError: + self.media().trash_files([filename]) + + + @util.api() + def addNote(self, note): + ankiNote = self.createNote(note) + + self.addMediaFromNote(ankiNote, note) + + collection = self.collection() + self.startEditing() + nCardsAdded = collection.addNote(ankiNote) + if nCardsAdded < 1: + raise Exception('The field values you have provided would make an empty question on all cards.') + collection.autosave() + self.stopEditing() + + return ankiNote.id + + + def addMediaFromNote(self, ankiNote, note): + audioObjectOrList = note.get('audio') + self.addMedia(ankiNote, audioObjectOrList, util.MediaType.Audio) + + videoObjectOrList = note.get('video') + self.addMedia(ankiNote, videoObjectOrList, util.MediaType.Video) + + pictureObjectOrList = note.get('picture') + self.addMedia(ankiNote, pictureObjectOrList, util.MediaType.Picture) + + + + def addMedia(self, ankiNote, mediaObjectOrList, mediaType): + if mediaObjectOrList is None: + return + + if isinstance(mediaObjectOrList, list): + mediaList = mediaObjectOrList + else: + mediaList = [mediaObjectOrList] + + for media in mediaList: + if media is not None and len(media['fields']) > 0: + try: + mediaFilename = self.storeMediaFile(media['filename'], + data=media.get('data'), + path=media.get('path'), + url=media.get('url'), + skipHash=media.get('skipHash'), + deleteExisting=media.get('deleteExisting')) + + if mediaFilename is not None: + for field in media['fields']: + if field in ankiNote: + if mediaType is util.MediaType.Picture: + ankiNote[field] += u'<img src="{}">'.format(mediaFilename) + elif mediaType is util.MediaType.Audio or mediaType is util.MediaType.Video: + ankiNote[field] += u'[sound:{}]'.format(mediaFilename) + + except Exception as e: + errorMessage = str(e).replace('&', '&').replace('<', '<').replace('>', '>') + for field in media['fields']: + if field in ankiNote: + ankiNote[field] += errorMessage + + + @util.api() + def canAddNote(self, note): + try: + return bool(self.createNote(note)) + except: + return False + + + @util.api() + def updateNoteFields(self, note): + ankiNote = self.getNote(note['id']) + + self.startEditing() + for name, value in note['fields'].items(): + if name in ankiNote: + ankiNote[name] = value + + audioObjectOrList = note.get('audio') + self.addMedia(ankiNote, audioObjectOrList, util.MediaType.Audio) + + videoObjectOrList = note.get('video') + self.addMedia(ankiNote, videoObjectOrList, util.MediaType.Video) + + pictureObjectOrList = note.get('picture') + self.addMedia(ankiNote, pictureObjectOrList, util.MediaType.Picture) + + ankiNote.flush() + + self.collection().autosave() + self.stopEditing() + + + @util.api() + def addTags(self, notes, tags, add=True): + self.startEditing() + self.collection().tags.bulkAdd(notes, tags, add) + self.stopEditing() + + + @util.api() + def removeTags(self, notes, tags): + return self.addTags(notes, tags, False) + + + @util.api() + def getTags(self): + return self.collection().tags.all() + + + @util.api() + def clearUnusedTags(self): + self.collection().tags.registerNotes() + + + @util.api() + def replaceTags(self, notes, tag_to_replace, replace_with_tag): + self.window().progress.start() + + for nid in notes: + try: + note = self.getNote(nid) + except NotFoundError: + continue + + if note.hasTag(tag_to_replace): + note.delTag(tag_to_replace) + note.addTag(replace_with_tag) + note.flush() + + self.window().requireReset() + self.window().progress.finish() + self.window().reset() + + + @util.api() + def replaceTagsInAllNotes(self, tag_to_replace, replace_with_tag): + self.window().progress.start() + + collection = self.collection() + for nid in collection.db.list('select id from notes'): + note = self.getNote(nid) + if note.hasTag(tag_to_replace): + note.delTag(tag_to_replace) + note.addTag(replace_with_tag) + note.flush() + + self.window().requireReset() + self.window().progress.finish() + self.window().reset() + + + @util.api() + def setEaseFactors(self, cards, easeFactors): + couldSetEaseFactors = [] + for i, card in enumerate(cards): + try: + ankiCard = self.getCard(card) + except NotFoundError: + couldSetEaseFactors.append(False) + continue + + couldSetEaseFactors.append(True) + ankiCard.factor = easeFactors[i] + ankiCard.flush() + + return couldSetEaseFactors + + @util.api() + def setSpecificValueOfCard(self, card, keys, + newValues, warning_check=False): + if isinstance(card, list): + print("card has to be int, not list") + return False + + if not isinstance(keys, list) or not isinstance(newValues, list): + print("keys and newValues have to be lists.") + return False + + if len(newValues) != len(keys): + print("Invalid list lengths.") + return False + + for key in keys: + if key in ["did", "id", "ivl", "lapses", "left", "mod", "nid", + "odid", "odue", "ord", "queue", "reps", "type", "usn"]: + if warning_check is False: + return False + + result = [] + try: + ankiCard = self.getCard(card) + for i, key in enumerate(keys): + setattr(ankiCard, key, newValues[i]) + ankiCard.flush() + result.append(True) + except Exception as e: + result.append([False, str(e)]) + return result + + + @util.api() + def getEaseFactors(self, cards): + easeFactors = [] + for card in cards: + try: + ankiCard = self.getCard(card) + except NotFoundError: + easeFactors.append(None) + continue + + easeFactors.append(ankiCard.factor) + + return easeFactors + + + @util.api() + def suspend(self, cards, suspend=True): + for card in cards: + if self.suspended(card) == suspend: + cards.remove(card) + + if len(cards) == 0: + return False + + scheduler = self.scheduler() + self.startEditing() + if suspend: + scheduler.suspendCards(cards) + else: + scheduler.unsuspendCards(cards) + self.stopEditing() + + return True + + + @util.api() + def unsuspend(self, cards): + self.suspend(cards, False) + + + @util.api() + def suspended(self, card): + card = self.getCard(card) + return card.queue == -1 + + + @util.api() + def areSuspended(self, cards): + suspended = [] + for card in cards: + try: + suspended.append(self.suspended(card)) + except NotFoundError: + suspended.append(None) + + return suspended + + + @util.api() + def areDue(self, cards): + due = [] + for card in cards: + if self.findCards('cid:{} is:new'.format(card)): + due.append(True) + else: + date, ivl = self.collection().db.all('select id/1000.0, ivl from revlog where cid = ?', card)[-1] + if ivl >= -1200: + due.append(bool(self.findCards('cid:{} is:due'.format(card)))) + else: + due.append(date - ivl <= time.time()) + + return due + + + @util.api() + def getIntervals(self, cards, complete=False): + intervals = [] + for card in cards: + if self.findCards('cid:{} is:new'.format(card)): + intervals.append(0) + else: + interval = self.collection().db.list('select ivl from revlog where cid = ?', card) + if not complete: + interval = interval[-1] + intervals.append(interval) + + return intervals + + + + @util.api() + def modelNames(self): + return self.collection().models.allNames() + + + @util.api() + def createModel(self, modelName, inOrderFields, cardTemplates, css = None, isCloze = False): + # https://github.com/dae/anki/blob/b06b70f7214fb1f2ce33ba06d2b095384b81f874/anki/stdmodels.py + if len(inOrderFields) == 0: + raise Exception('Must provide at least one field for inOrderFields') + if len(cardTemplates) == 0: + raise Exception('Must provide at least one card for cardTemplates') + if modelName in self.collection().models.allNames(): + raise Exception('Model name already exists') + + collection = self.collection() + mm = collection.models + + # Generate new Note + m = mm.new(modelName) + if isCloze: + m['type'] = MODEL_CLOZE + + # Create fields and add them to Note + for field in inOrderFields: + fm = mm.newField(field) + mm.addField(m, fm) + + # Add shared css to model if exists. Use default otherwise + if (css is not None): + m['css'] = css + + # Generate new card template(s) + cardCount = 1 + for card in cardTemplates: + cardName = 'Card ' + str(cardCount) + if 'Name' in card: + cardName = card['Name'] + + t = mm.newTemplate(cardName) + cardCount += 1 + t['qfmt'] = card['Front'] + t['afmt'] = card['Back'] + mm.addTemplate(m, t) + + mm.add(m) + return m + + + @util.api() + def modelNamesAndIds(self): + models = {} + for model in self.modelNames(): + models[model] = int(self.collection().models.byName(model)['id']) + + return models + + + @util.api() + def modelNameFromId(self, modelId): + model = self.collection().models.get(modelId) + if model is None: + raise Exception('model was not found: {}'.format(modelId)) + else: + return model['name'] + + + @util.api() + def modelFieldNames(self, modelName): + model = self.collection().models.byName(modelName) + if model is None: + raise Exception('model was not found: {}'.format(modelName)) + else: + return [field['name'] for field in model['flds']] + + + @util.api() + def modelFieldsOnTemplates(self, modelName): + model = self.collection().models.byName(modelName) + if model is None: + raise Exception('model was not found: {}'.format(modelName)) + + templates = {} + for template in model['tmpls']: + fields = [] + for side in ['qfmt', 'afmt']: + fieldsForSide = [] + + # based on _fieldsOnTemplate from aqt/clayout.py + matches = re.findall('{{[^#/}]+?}}', template[side]) + for match in matches: + # remove braces and modifiers + match = re.sub(r'[{}]', '', match) + match = match.split(':')[-1] + + # for the answer side, ignore fields present on the question side + the FrontSide field + if match == 'FrontSide' or side == 'afmt' and match in fields[0]: + continue + fieldsForSide.append(match) + + fields.append(fieldsForSide) + + templates[template['name']] = fields + + return templates + + + @util.api() + def modelTemplates(self, modelName): + model = self.collection().models.byName(modelName) + if model is None: + raise Exception('model was not found: {}'.format(modelName)) + + templates = {} + for template in model['tmpls']: + templates[template['name']] = {'Front': template['qfmt'], 'Back': template['afmt']} + + return templates + + + @util.api() + def modelStyling(self, modelName): + model = self.collection().models.byName(modelName) + if model is None: + raise Exception('model was not found: {}'.format(modelName)) + + return {'css': model['css']} + + + @util.api() + def updateModelTemplates(self, model): + models = self.collection().models + ankiModel = models.byName(model['name']) + if ankiModel is None: + raise Exception('model was not found: {}'.format(model['name'])) + + templates = model['templates'] + for ankiTemplate in ankiModel['tmpls']: + template = templates.get(ankiTemplate['name']) + if template: + qfmt = template.get('Front') + if qfmt: + ankiTemplate['qfmt'] = qfmt + + afmt = template.get('Back') + if afmt: + ankiTemplate['afmt'] = afmt + + self.save_model(models, ankiModel) + + + @util.api() + def updateModelStyling(self, model): + models = self.collection().models + ankiModel = models.byName(model['name']) + if ankiModel is None: + raise Exception('model was not found: {}'.format(model['name'])) + + ankiModel['css'] = model['css'] + + self.save_model(models, ankiModel) + + + @util.api() + def findAndReplaceInModels(self, modelName, findText, replaceText, front=True, back=True, css=True): + if not modelName: + ankiModel = self.collection().models.allNames() + else: + model = self.collection().models.byName(modelName) + if model is None: + raise Exception('model was not found: {}'.format(modelName)) + ankiModel = [modelName] + updatedModels = 0 + for model in ankiModel: + model = self.collection().models.byName(model) + checkForText = False + if css and findText in model['css']: + checkForText = True + model['css'] = model['css'].replace(findText, replaceText) + for tmpls in model.get('tmpls'): + if front and findText in tmpls['qfmt']: + checkForText = True + tmpls['qfmt'] = tmpls['qfmt'].replace(findText, replaceText) + if back and findText in tmpls['afmt']: + checkForText = True + tmpls['afmt'] = tmpls['afmt'].replace(findText, replaceText) + self.save_model(self.collection().models, model) + if checkForText: + updatedModels += 1 + return updatedModels + + + @util.api() + def deckNameFromId(self, deckId): + deck = self.collection().decks.get(deckId) + if deck is None: + raise Exception('deck was not found: {}'.format(deckId)) + + return deck['name'] + + + @util.api() + def findNotes(self, query=None): + if query is None: + return [] + + return list(map(int, self.collection().findNotes(query))) + + + @util.api() + def findCards(self, query=None): + if query is None: + return [] + + return list(map(int, self.collection().findCards(query))) + + + @util.api() + def cardsInfo(self, cards): + result = [] + for cid in cards: + try: + card = self.getCard(cid) + model = card.model() + note = card.note() + fields = {} + for info in model['flds']: + order = info['ord'] + name = info['name'] + fields[name] = {'value': note.fields[order], 'order': order} + + result.append({ + 'cardId': card.id, + 'fields': fields, + 'fieldOrder': card.ord, + 'question': util.cardQuestion(card), + 'answer': util.cardAnswer(card), + 'modelName': model['name'], + 'ord': card.ord, + 'deckName': self.deckNameFromId(card.did), + 'css': model['css'], + 'factor': card.factor, + #This factor is 10 times the ease percentage, + # so an ease of 310% would be reported as 3100 + 'interval': card.ivl, + 'note': card.nid, + 'type': card.type, + 'queue': card.queue, + 'due': card.due, + 'reps': card.reps, + 'lapses': card.lapses, + 'left': card.left, + 'mod': card.mod, + }) + except NotFoundError: + # Anki will give a NotFoundError if the card ID does not exist. + # Best behavior is probably to add an 'empty card' to the + # returned result, so that the items of the input and return + # lists correspond. + result.append({}) + + return result + + @util.api() + def cardsModTime(self, cards): + result = [] + for cid in cards: + try: + card = self.getCard(cid) + result.append({ + 'cardId': card.id, + 'mod': card.mod, + }) + except NotFoundError: + # Anki will give a NotFoundError if the card ID does not exist. + # Best behavior is probably to add an 'empty card' to the + # returned result, so that the items of the input and return + # lists correspond. + result.append({}) + return result + + + @util.api() + def forgetCards(self, cards): + self.startEditing() + scids = anki.utils.ids2str(cards) + self.collection().db.execute('update cards set type=0, queue=0, left=0, ivl=0, due=0, odue=0, factor=0 where id in ' + scids) + self.stopEditing() + + + @util.api() + def relearnCards(self, cards): + self.startEditing() + scids = anki.utils.ids2str(cards) + self.collection().db.execute('update cards set type=3, queue=1 where id in ' + scids) + self.stopEditing() + + + @util.api() + def cardReviews(self, deck, startID): + return self.database().all( + 'select id, cid, usn, ease, ivl, lastIvl, factor, time, type from revlog ''where id>? and cid in (select id from cards where did=?)', + startID, + self.decks().id(deck) + ) + + + @util.api() + def reloadCollection(self): + self.collection().reset() + + + @util.api() + def getLatestReviewID(self, deck): + return self.database().scalar( + 'select max(id) from revlog where cid in (select id from cards where did=?)', + self.decks().id(deck) + ) or 0 + + + @util.api() + def insertReviews(self, reviews): + if len(reviews) > 0: + sql = 'insert into revlog(id,cid,usn,ease,ivl,lastIvl,factor,time,type) values ' + for row in reviews: + sql += '(%s),' % ','.join(map(str, row)) + sql = sql[:-1] + self.database().execute(sql) + + + @util.api() + def notesInfo(self, notes): + result = [] + for nid in notes: + try: + note = self.getNote(nid) + model = note.model() + + fields = {} + for info in model['flds']: + order = info['ord'] + name = info['name'] + fields[name] = {'value': note.fields[order], 'order': order} + + result.append({ + 'noteId': note.id, + 'tags' : note.tags, + 'fields': fields, + 'modelName': model['name'], + 'cards': self.collection().db.list('select id from cards where nid = ? order by ord', note.id) + }) + except NotFoundError: + # Anki will give a NotFoundError if the note ID does not exist. + # Best behavior is probably to add an 'empty card' to the + # returned result, so that the items of the input and return + # lists correspond. + result.append({}) + + return result + + + @util.api() + def deleteNotes(self, notes): + try: + self.collection().remNotes(notes) + finally: + self.stopEditing() + + + @util.api() + def removeEmptyNotes(self): + for model in self.collection().models.all(): + if self.collection().models.useCount(model) == 0: + self.collection().models.rem(model) + self.window().requireReset() + + + @util.api() + def cardsToNotes(self, cards): + return self.collection().db.list('select distinct nid from cards where id in ' + anki.utils.ids2str(cards)) + + + @util.api() + def guiBrowse(self, query=None): + browser = aqt.dialogs.open('Browser', self.window()) + browser.activateWindow() + + if query is not None: + browser.form.searchEdit.lineEdit().setText(query) + if hasattr(browser, 'onSearch'): + browser.onSearch() + else: + browser.onSearchActivated() + + return self.findCards(query) + + + @util.api() + def guiEditNote(self, note): + Edit.open_dialog_and_show_note_with_id(note) + + + @util.api() + def guiSelectedNotes(self): + (creator, instance) = aqt.dialogs._dialogs['Browser'] + if instance is None: + return [] + return instance.selectedNotes() + + @util.api() + def guiAddCards(self, note=None): + if note is not None: + collection = self.collection() + + deck = collection.decks.byName(note['deckName']) + if deck is None: + raise Exception('deck was not found: {}'.format(note['deckName'])) + + collection.decks.select(deck['id']) + savedMid = deck.pop('mid', None) + + model = collection.models.byName(note['modelName']) + if model is None: + raise Exception('model was not found: {}'.format(note['modelName'])) + + collection.models.setCurrent(model) + collection.models.update(model) + + ankiNote = anki.notes.Note(collection, model) + + # fill out card beforehand, so we can be sure of the note id + if 'fields' in note: + for name, value in note['fields'].items(): + if name in ankiNote: + ankiNote[name] = value + + self.addMediaFromNote(ankiNote, note) + + if 'tags' in note: + ankiNote.tags = note['tags'] + + def openNewWindow(): + nonlocal ankiNote + + addCards = aqt.dialogs.open('AddCards', self.window()) + + if savedMid: + deck['mid'] = savedMid + + addCards.editor.set_note(ankiNote) + + addCards.activateWindow() + + aqt.dialogs.open('AddCards', self.window()) + addCards.setAndFocusNote(addCards.editor.note) + + currentWindow = aqt.dialogs._dialogs['AddCards'][1] + + if currentWindow is not None: + currentWindow.closeWithCallback(openNewWindow) + else: + openNewWindow() + + return ankiNote.id + + else: + addCards = aqt.dialogs.open('AddCards', self.window()) + addCards.activateWindow() + + return addCards.editor.note.id + + + @util.api() + def guiReviewActive(self): + return self.reviewer().card is not None and self.window().state == 'review' + + + @util.api() + def guiCurrentCard(self): + if not self.guiReviewActive(): + raise Exception('Gui review is not currently active.') + + reviewer = self.reviewer() + card = reviewer.card + model = card.model() + note = card.note() + + fields = {} + for info in model['flds']: + order = info['ord'] + name = info['name'] + fields[name] = {'value': note.fields[order], 'order': order} + + buttonList = reviewer._answerButtonList() + return { + 'cardId': card.id, + 'fields': fields, + 'fieldOrder': card.ord, + 'question': util.cardQuestion(card), + 'answer': util.cardAnswer(card), + 'buttons': [b[0] for b in buttonList], + 'nextReviews': [reviewer.mw.col.sched.nextIvlStr(reviewer.card, b[0], True) for b in buttonList], + 'modelName': model['name'], + 'deckName': self.deckNameFromId(card.did), + 'css': model['css'], + 'template': card.template()['name'] + } + + + @util.api() + def guiStartCardTimer(self): + if not self.guiReviewActive(): + return False + + card = self.reviewer().card + if card is not None: + card.startTimer() + return True + + return False + + + @util.api() + def guiShowQuestion(self): + if self.guiReviewActive(): + self.reviewer()._showQuestion() + return True + + return False + + + @util.api() + def guiShowAnswer(self): + if self.guiReviewActive(): + self.window().reviewer._showAnswer() + return True + + return False + + + @util.api() + def guiAnswerCard(self, ease): + if not self.guiReviewActive(): + return False + + reviewer = self.reviewer() + if reviewer.state != 'answer': + return False + if ease <= 0 or ease > self.scheduler().answerButtons(reviewer.card): + return False + + reviewer._answerCard(ease) + return True + + + @util.api() + def guiDeckOverview(self, name): + collection = self.collection() + if collection is not None: + deck = collection.decks.byName(name) + if deck is not None: + collection.decks.select(deck['id']) + self.window().onOverview() + return True + + return False + + + @util.api() + def guiDeckBrowser(self): + self.window().moveToState('deckBrowser') + + + @util.api() + def guiDeckReview(self, name): + if self.guiDeckOverview(name): + self.window().moveToState('review') + return True + + return False + + + @util.api() + def guiExitAnki(self): + timer = QTimer() + timer.timeout.connect(self.window().close) + timer.start(1000) # 1s should be enough to allow the response to be sent. + + + @util.api() + def guiCheckDatabase(self): + self.window().onCheckDB() + return True + + + @util.api() + def addNotes(self, notes): + results = [] + for note in notes: + try: + results.append(self.addNote(note)) + except: + results.append(None) + + return results + + + @util.api() + def canAddNotes(self, notes): + results = [] + for note in notes: + results.append(self.canAddNote(note)) + + return results + + + @util.api() + def exportPackage(self, deck, path, includeSched=False): + collection = self.collection() + if collection is not None: + deck = collection.decks.byName(deck) + if deck is not None: + exporter = AnkiPackageExporter(collection) + exporter.did = deck['id'] + exporter.includeSched = includeSched + exporter.exportInto(path) + return True + + return False + + + @util.api() + def importPackage(self, path): + collection = self.collection() + if collection is not None: + try: + self.startEditing() + importer = AnkiPackageImporter(collection, path) + importer.run() + except: + self.stopEditing() + raise + else: + self.stopEditing() + return True + + return False + + + @util.api() + def apiReflect(self, scopes=None, actions=None): + if not isinstance(scopes, list): + raise Exception('scopes has invalid value') + if not (actions is None or isinstance(actions, list)): + raise Exception('actions has invalid value') + + cls = type(self) + scopes2 = [] + result = {'scopes': scopes2} + + if 'actions' in scopes: + if actions is None: + actions = dir(cls) + + methodNames = [] + for methodName in actions: + if not isinstance(methodName, str): + pass + method = getattr(cls, methodName, None) + if method is not None and getattr(method, 'api', False): + methodNames.append(methodName) + + scopes2.append('actions') + result['actions'] = methodNames + + return result + + +# +# Entry +# + +# when run inside Anki, `__name__` would be either numeric, +# or, if installed via `link.sh`, `AnkiConnectDev` +if __name__ != "plugin": + if platform.system() == "Windows" and anki_version == (2, 1, 50): + util.patch_anki_2_1_50_having_null_stdout_on_windows() + + Edit.register_with_anki() + + ac = AnkiConnect() + ac.initLogging() + ac.startWebServer() diff --git a/.local/share/Anki2/addons21/Anki_connect/config.json b/.local/share/Anki2/addons21/Anki_connect/config.json new file mode 100644 index 0000000..d3b51ea --- /dev/null +++ b/.local/share/Anki2/addons21/Anki_connect/config.json @@ -0,0 +1,8 @@ +{ + "apiKey": null, + "apiLogPath": null, + "webBindAddress": "127.0.0.1", + "webBindPort": 8765, + "webCorsOriginList": ["http://localhost"], + "ignoreOriginList": [] +} diff --git a/.local/share/Anki2/addons21/Anki_connect/config.md b/.local/share/Anki2/addons21/Anki_connect/config.md new file mode 100644 index 0000000..6bacba2 --- /dev/null +++ b/.local/share/Anki2/addons21/Anki_connect/config.md @@ -0,0 +1 @@ +Read the documentation on the [AnkiConnect](https://foosoft.net/projects/anki-connect/) project page for details. diff --git a/.local/share/Anki2/addons21/Anki_connect/edit.py b/.local/share/Anki2/addons21/Anki_connect/edit.py new file mode 100644 index 0000000..d414575 --- /dev/null +++ b/.local/share/Anki2/addons21/Anki_connect/edit.py @@ -0,0 +1,458 @@ +import aqt +import aqt.editor +import aqt.browser.previewer +from aqt import gui_hooks +from aqt.qt import QDialog, Qt, QKeySequence, QShortcut +from aqt.utils import disable_help_button, restoreGeom, saveGeom, tooltip +from anki.errors import NotFoundError +from anki.consts import QUEUE_TYPE_SUSPENDED +from anki.utils import ids2str + +from . import anki_version + + +# Edit dialog. Like Edit Current, but: +# * has a Preview button to preview the cards for the note +# * has Previous/Back buttons to navigate the history of the dialog +# * has a Browse button to open the history in the Browser +# * has no bar with the Close button +# +# To register in Anki's dialog system: +# > from .edit import Edit +# > Edit.register_with_anki() +# +# To (re)open (note_id is an integer): +# > Edit.open_dialog_and_show_note_with_id(note_id) + + +DOMAIN_PREFIX = "foosoft.ankiconnect." + + +def get_note_by_note_id(note_id): + return aqt.mw.col.get_note(note_id) + +def is_card_suspended(card): + return card.queue == QUEUE_TYPE_SUSPENDED + +def filter_valid_note_ids(note_ids): + return aqt.mw.col.db.list( + "select id from notes where id in " + ids2str(note_ids) + ) + + +############################################################################## + + +class DecentPreviewer(aqt.browser.previewer.MultiCardPreviewer): + class Adapter: + def get_current_card(self): raise NotImplementedError + def can_select_previous_card(self): raise NotImplementedError + def can_select_next_card(self): raise NotImplementedError + def select_previous_card(self): raise NotImplementedError + def select_next_card(self): raise NotImplementedError + + def __init__(self, adapter: Adapter): + super().__init__(parent=None, mw=aqt.mw, on_close=lambda: None) # noqa + self.adapter = adapter + self.last_card_id = 0 + + def card(self): + return self.adapter.get_current_card() + + def card_changed(self): + current_card_id = self.adapter.get_current_card().id + changed = self.last_card_id != current_card_id + self.last_card_id = current_card_id + return changed + + # the check if we can select next/previous card is needed because + # the buttons sometimes get disabled a tad too late + # and can still be pressed by user. + # this is likely due to Anki sometimes delaying rendering of cards + # in order to avoid rendering them too fast? + def _on_prev_card(self): + if self.adapter.can_select_previous_card(): + self.adapter.select_previous_card() + self.render_card() + + def _on_next_card(self): + if self.adapter.can_select_next_card(): + self.adapter.select_next_card() + self.render_card() + + def _should_enable_prev(self): + return self.showing_answer_and_can_show_question() or \ + self.adapter.can_select_previous_card() + + def _should_enable_next(self): + return self.showing_question_and_can_show_answer() or \ + self.adapter.can_select_next_card() + + def _render_scheduled(self): + super()._render_scheduled() # noqa + self._updateButtons() + + def showing_answer_and_can_show_question(self): + return self._state == "answer" and not self._show_both_sides + + def showing_question_and_can_show_answer(self): + return self._state == "question" + + +class ReadyCardsAdapter(DecentPreviewer.Adapter): + def __init__(self, cards): + self.cards = cards + self.current = 0 + + def get_current_card(self): + return self.cards[self.current] + + def can_select_previous_card(self): + return self.current > 0 + + def can_select_next_card(self): + return self.current < len(self.cards) - 1 + + def select_previous_card(self): + self.current -= 1 + + def select_next_card(self): + self.current += 1 + + +############################################################################## + + +# store note ids instead of notes, as note objects don't implement __eq__ etc +class History: + number_of_notes_to_keep_in_history = 25 + + def __init__(self): + self.note_ids = [] + + def append(self, note): + if note.id in self.note_ids: + self.note_ids.remove(note.id) + self.note_ids.append(note.id) + self.note_ids = self.note_ids[-self.number_of_notes_to_keep_in_history:] + + def has_note_to_left_of(self, note): + return note.id in self.note_ids and note.id != self.note_ids[0] + + def has_note_to_right_of(self, note): + return note.id in self.note_ids and note.id != self.note_ids[-1] + + def get_note_to_left_of(self, note): + note_id = self.note_ids[self.note_ids.index(note.id) - 1] + return get_note_by_note_id(note_id) + + def get_note_to_right_of(self, note): + note_id = self.note_ids[self.note_ids.index(note.id) + 1] + return get_note_by_note_id(note_id) + + def get_last_note(self): # throws IndexError if history empty + return get_note_by_note_id(self.note_ids[-1]) + + def remove_invalid_notes(self): + self.note_ids = filter_valid_note_ids(self.note_ids) + +history = History() + + +# see method `find_cards` of `collection.py` +def trigger_search_for_dialog_history_notes(search_context, use_history_order): + search_context.search = " or ".join( + f"nid:{note_id}" for note_id in history.note_ids + ) + + if use_history_order: + search_context.order = f"""case c.nid { + " ".join( + f"when {note_id} then {n}" + for (n, note_id) in enumerate(reversed(history.note_ids)) + ) + } end asc""" + + +############################################################################## + + +# noinspection PyAttributeOutsideInit +class Edit(aqt.editcurrent.EditCurrent): + dialog_geometry_tag = DOMAIN_PREFIX + "edit" + dialog_registry_tag = DOMAIN_PREFIX + "Edit" + dialog_search_tag = DOMAIN_PREFIX + "edit.history" + + # depending on whether the dialog already exists, + # upon a request to open the dialog via `aqt.dialogs.open()`, + # the manager will call either the constructor or the `reopen` method + def __init__(self, note): + QDialog.__init__(self, None, Qt.WindowType.Window) + aqt.mw.garbage_collect_on_dialog_finish(self) + self.form = aqt.forms.editcurrent.Ui_Dialog() + self.form.setupUi(self) + self.setWindowTitle("Edit") + self.setMinimumWidth(250) + self.setMinimumHeight(400) + restoreGeom(self, self.dialog_geometry_tag) + disable_help_button(self) + + self.form.buttonBox.setVisible(False) # hides the Close button bar + self.setup_editor_buttons() + + self.show() + self.bring_to_foreground() + + history.remove_invalid_notes() + history.append(note) + self.show_note(note) + + gui_hooks.operation_did_execute.append(self.on_operation_did_execute) + gui_hooks.editor_did_load_note.append(self.editor_did_load_note) + + def reopen(self, note): + history.append(note) + self.show_note(note) + self.bring_to_foreground() + + def cleanup_and_close(self): + gui_hooks.editor_did_load_note.remove(self.editor_did_load_note) + gui_hooks.operation_did_execute.remove(self.on_operation_did_execute) + + self.editor.cleanup() + saveGeom(self, self.dialog_geometry_tag) + aqt.dialogs.markClosed(self.dialog_registry_tag) + QDialog.reject(self) + + # This method (mostly) solves (at least on my Windows 10 machine) three issues + # with window activation. Without this not even too hacky a fix, + # * When dialog is opened from Yomichan *for the first time* since app start, + # the dialog opens in background (just like Browser does), + # but does not flash in taskbar (unlike Browser); + # * When dialog is opened, closed, *then main window is focused by clicking in it*, + # then dialog is opened from Yomichan again, same issue as above arises; + # * When dialog is restored from minimized state *and main window isn't minimized*, + # opening the dialog from Yomichan does not reliably focus it; + # sometimes it opens in foreground, sometimes in background. + # With this fix, windows nearly always appear in foreground in all three cases. + # In the case of the first two issues, strictly speaking, the fix is not ideal: + # the window appears in background first, and then quickly pops into foreground. + # It is not *too* unsightly, probably, no-one will notice this; + # still, a better solution must be possible. TODO find one! + # + # Note that operation systems, notably Windows, and desktop managers, may restrict + # background applications from raising windows to prevent them from interrupting + # what the user is currently doing. For details, see: + # https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setforegroundwindow#remarks + # https://doc.qt.io/qt-5/qwidget.html#activateWindow + # https://wiki.qt.io/Technical_FAQ#QWidget_::activateWindow.28.29_-_behavior_under_windows + def bring_to_foreground(self): + aqt.mw.app.processEvents() + self.activateWindow() + self.raise_() + + #################################### hooks enabled during dialog lifecycle + + def on_operation_did_execute(self, changes, handler): + if changes.note_text and handler is not self.editor: + self.reload_notes_after_user_action_elsewhere() + + def editor_did_load_note(self, _editor): + self.enable_disable_next_and_previous_buttons() + + ###################################################### load & reload notes + + # setting editor.card is required for the "Cards…" button to work properly + def show_note(self, note): + self.note = note + cards = note.cards() + + self.editor.set_note(note) + self.editor.card = cards[0] if cards else None + + if any(is_card_suspended(card) for card in cards): + tooltip("Some of the cards associated with this note " + "have been suspended", parent=self) + + def reload_notes_after_user_action_elsewhere(self): + history.remove_invalid_notes() + + try: + self.note.load() # this also updates the fields + except NotFoundError: + try: + self.note = history.get_last_note() + except IndexError: + self.cleanup_and_close() + return + + self.show_note(self.note) + + ################################################################## actions + + # search two times, one is to select the current note or its cards, + # and another to show the whole history, while keeping the above selection + # set sort column to our search tag, which: + # * prevents the column sort indicator from being shown + # * serves as a hint for us to show notes or cards in history order + # (user can then click on any of the column names + # to show history cards in the order of their choosing) + def show_browser(self, *_): + def search_input_select_all(hook_browser, *_): + hook_browser.form.searchEdit.lineEdit().selectAll() + gui_hooks.browser_did_change_row.remove(search_input_select_all) + gui_hooks.browser_did_change_row.append(search_input_select_all) + + browser = aqt.dialogs.open("Browser", aqt.mw) + browser.table._state.sort_column = self.dialog_search_tag # noqa + browser.table._set_sort_indicator() # noqa + + browser.search_for(f"nid:{self.note.id}") + browser.table.select_all() + browser.search_for(self.dialog_search_tag) + + def show_preview(self, *_): + if cards := self.note.cards(): + previewer = DecentPreviewer(ReadyCardsAdapter(cards)) + previewer.open() + return previewer + else: + tooltip("No cards found", parent=self) + return None + + def show_previous(self, *_): + if history.has_note_to_left_of(self.note): + self.show_note(history.get_note_to_left_of(self.note)) + + def show_next(self, *_): + if history.has_note_to_right_of(self.note): + self.show_note(history.get_note_to_right_of(self.note)) + + ################################################## button and hotkey setup + + def setup_editor_buttons(self): + gui_hooks.editor_did_init.append(self.add_preview_button) + gui_hooks.editor_did_init_buttons.append(self.add_right_hand_side_buttons) + + # on Anki 2.1.50, browser mode makes the Preview button visible + extra_kwargs = {} if anki_version < (2, 1, 50) else { + "editor_mode": aqt.editor.EditorMode.BROWSER + } + + self.editor = aqt.editor.Editor(aqt.mw, self.form.fieldsArea, self, + **extra_kwargs) + + gui_hooks.editor_did_init_buttons.remove(self.add_right_hand_side_buttons) + gui_hooks.editor_did_init.remove(self.add_preview_button) + + # * on Anki < 2.1.50, make the button via js (`setupEditor` of browser.py); + # also, make a copy of _links so that opening Anki's browser does not + # screw them up as they are apparently shared between instances?! + # the last part seems to have been fixed in Anki 2.1.50 + # * on Anki 2.1.50, the button is created by setting editor mode, + # see above; so we only need to add the link. + def add_preview_button(self, editor): + QShortcut(QKeySequence("Ctrl+Shift+P"), self, self.show_preview) + + if anki_version < (2, 1, 50): + editor._links = editor._links.copy() + editor.web.eval(""" + $editorToolbar.then(({notetypeButtons}) => + notetypeButtons.appendButton( + {component: editorToolbar.PreviewButton, id: 'preview'} + ) + ); + """) + + editor._links["preview"] = lambda _editor: self.show_preview() and None + + # * on Anki < 2.1.50, button style is okay-ish from get-go, + # except when disabled; adding class `btn` fixes that; + # * on Anki 2.1.50, buttons have weird font size and are square'; + # the style below makes them in line with left-hand side buttons + def add_right_hand_side_buttons(self, buttons, editor): + if anki_version < (2, 1, 50): + extra_button_class = "btn" + else: + extra_button_class = "anki-connect-button" + editor.web.eval(""" + (function(){ + const style = document.createElement("style"); + style.innerHTML = ` + .anki-connect-button { + white-space: nowrap; + width: auto; + padding: 0 2px; + font-size: var(--base-font-size); + } + .anki-connect-button:disabled { + pointer-events: none; + opacity: .4; + } + `; + document.head.appendChild(style); + })(); + """) + + def add(cmd, function, label, tip, keys): + button_html = editor.addButton( + icon=None, + cmd=DOMAIN_PREFIX + cmd, + id=DOMAIN_PREFIX + cmd, + func=function, + label=f" {label} ", + tip=f"{tip} ({keys})", + keys=keys, + ) + + button_html = button_html.replace('class="', + f'class="{extra_button_class} ') + buttons.append(button_html) + + add("browse", self.show_browser, "Browse", "Browse", "Ctrl+F") + add("previous", self.show_previous, "<", "Previous", "Alt+Left") + add("next", self.show_next, ">", "Next", "Alt+Right") + + def run_javascript_after_toolbar_ready(self, js): + js = f"setTimeout(function() {{ {js} }}, 1)" + if anki_version < (2, 1, 50): + js = f'$editorToolbar.then(({{ toolbar }}) => {js})' + else: + js = f'require("anki/ui").loaded.then(() => {js})' + self.editor.web.eval(js) + + def enable_disable_next_and_previous_buttons(self): + def to_js(boolean): + return "true" if boolean else "false" + + disable_previous = not(history.has_note_to_left_of(self.note)) + disable_next = not(history.has_note_to_right_of(self.note)) + + self.run_javascript_after_toolbar_ready(f""" + document.getElementById("{DOMAIN_PREFIX}previous") + .disabled = {to_js(disable_previous)}; + document.getElementById("{DOMAIN_PREFIX}next") + .disabled = {to_js(disable_next)}; + """) + + ########################################################################## + + @classmethod + def browser_will_search(cls, search_context): + if search_context.search == cls.dialog_search_tag: + trigger_search_for_dialog_history_notes( + search_context=search_context, + use_history_order=cls.dialog_search_tag == + search_context.browser.table._state.sort_column # noqa + ) + + @classmethod + def register_with_anki(cls): + if cls.dialog_registry_tag not in aqt.dialogs._dialogs: # noqa + aqt.dialogs.register_dialog(cls.dialog_registry_tag, cls) + gui_hooks.browser_will_search.append(cls.browser_will_search) + + @classmethod + def open_dialog_and_show_note_with_id(cls, note_id): # raises NotFoundError + note = get_note_by_note_id(note_id) + return aqt.dialogs.open(cls.dialog_registry_tag, note) diff --git a/.local/share/Anki2/addons21/Anki_connect/meta.json b/.local/share/Anki2/addons21/Anki_connect/meta.json new file mode 100644 index 0000000..c27a474 --- /dev/null +++ b/.local/share/Anki2/addons21/Anki_connect/meta.json @@ -0,0 +1 @@ +{"name": "AnkiConnect", "mod": 1657829726, "min_point_version": 45, "max_point_version": 45, "branch_index": 1, "disabled": false, "conflicts": [], "update_enabled": true}
\ No newline at end of file diff --git a/.local/share/Anki2/addons21/Anki_connect/util.py b/.local/share/Anki2/addons21/Anki_connect/util.py new file mode 100644 index 0000000..3ae5eb1 --- /dev/null +++ b/.local/share/Anki2/addons21/Anki_connect/util.py @@ -0,0 +1,93 @@ +# Copyright 2016-2021 Alex Yatskov +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import os +import sys + +import anki +import anki.sync +import aqt +import enum + +# +# Utilities +# + +class MediaType(enum.Enum): + Audio = 1 + Video = 2 + Picture = 3 + + +def download(url): + client = anki.sync.AnkiRequestsClient() + client.timeout = setting('webTimeout') / 1000 + + resp = client.get(url) + if resp.status_code != 200: + raise Exception('{} download failed with return code {}'.format(url, resp.status_code)) + + return client.streamContent(resp) + + +def api(*versions): + def decorator(func): + setattr(func, 'versions', versions) + setattr(func, 'api', True) + return func + + return decorator + + +def cardQuestion(card): + if getattr(card, 'question', None) is None: + return card._getQA()['q'] + + return card.question() + + +def cardAnswer(card): + if getattr(card, 'answer', None) is None: + return card._getQA()['a'] + + return card.answer() + + +DEFAULT_CONFIG = { + 'apiKey': None, + 'apiLogPath': None, + 'apiPollInterval': 25, + 'apiVersion': 6, + 'webBacklog': 5, + 'webBindAddress': os.getenv('ANKICONNECT_BIND_ADDRESS', '127.0.0.1'), + 'webBindPort': 8765, + 'webCorsOrigin': os.getenv('ANKICONNECT_CORS_ORIGIN', None), + 'webCorsOriginList': ['http://localhost'], + 'ignoreOriginList': [], + 'webTimeout': 10000, +} + +def setting(key): + try: + return aqt.mw.addonManager.getConfig(__name__).get(key, DEFAULT_CONFIG[key]) + except: + raise Exception('setting {} not found'.format(key)) + + +# see https://github.com/FooSoft/anki-connect/issues/308 +# fixed in https://github.com/ankitects/anki/commit/0b2a226d +def patch_anki_2_1_50_having_null_stdout_on_windows(): + if sys.stdout is None: + sys.stdout = open(os.devnull, "w", encoding="utf8") diff --git a/.local/share/Anki2/addons21/Anki_connect/web.py b/.local/share/Anki2/addons21/Anki_connect/web.py new file mode 100644 index 0000000..1f325b4 --- /dev/null +++ b/.local/share/Anki2/addons21/Anki_connect/web.py @@ -0,0 +1,301 @@ +# Copyright 2016-2021 Alex Yatskov +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import json +import jsonschema +import select +import socket + +from . import util + +# +# WebRequest +# + +class WebRequest: + def __init__(self, method, headers, body): + self.method = method + self.headers = headers + self.body = body + + +# +# WebClient +# + +class WebClient: + def __init__(self, sock, handler): + self.sock = sock + self.handler = handler + self.readBuff = bytes() + self.writeBuff = bytes() + + + def advance(self, recvSize=1024): + if self.sock is None: + return False + + rlist, wlist = select.select([self.sock], [self.sock], [], 0)[:2] + self.sock.settimeout(5.0) + + if rlist: + while True: + try: + msg = self.sock.recv(recvSize) + except (ConnectionResetError, socket.timeout): + self.close() + return False + if not msg: + self.close() + return False + self.readBuff += msg + + req, length = self.parseRequest(self.readBuff) + if req is not None: + self.readBuff = self.readBuff[length:] + self.writeBuff += self.handler(req) + break + + + + if wlist and self.writeBuff: + try: + length = self.sock.send(self.writeBuff) + self.writeBuff = self.writeBuff[length:] + if not self.writeBuff: + self.close() + return False + except: + self.close() + return False + return True + + + def close(self): + if self.sock is not None: + self.sock.close() + self.sock = None + + self.readBuff = bytes() + self.writeBuff = bytes() + + + def parseRequest(self, data): + parts = data.split('\r\n\r\n'.encode('utf-8'), 1) + if len(parts) == 1: + return None, 0 + + lines = parts[0].split('\r\n'.encode('utf-8')) + method = None + + if len(lines) > 0: + request_line_parts = lines[0].split(' '.encode('utf-8')) + method = request_line_parts[0].upper() if len(request_line_parts) > 0 else None + + headers = {} + for line in lines[1:]: + pair = line.split(': '.encode('utf-8')) + headers[pair[0].lower()] = pair[1] if len(pair) > 1 else None + + headerLength = len(parts[0]) + 4 + bodyLength = int(headers.get('content-length'.encode('utf-8'), 0)) + totalLength = headerLength + bodyLength + + if totalLength > len(data): + return None, 0 + + body = data[headerLength : totalLength] + return WebRequest(method, headers, body), totalLength + +# +# WebServer +# + +class WebServer: + def __init__(self, handler): + self.handler = handler + self.clients = [] + self.sock = None + + + def advance(self): + if self.sock is not None: + self.acceptClients() + self.advanceClients() + + + def acceptClients(self): + rlist = select.select([self.sock], [], [], 0)[0] + if not rlist: + return + + clientSock = self.sock.accept()[0] + if clientSock is not None: + clientSock.setblocking(False) + self.clients.append(WebClient(clientSock, self.handlerWrapper)) + + + def advanceClients(self): + self.clients = list(filter(lambda c: c.advance(), self.clients)) + + + def listen(self): + self.close() + + self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + self.sock.setblocking(False) + self.sock.bind((util.setting('webBindAddress'), util.setting('webBindPort'))) + self.sock.listen(util.setting('webBacklog')) + + + def handlerWrapper(self, req): + allowed, corsOrigin = self.allowOrigin(req) + + if req.method == b'OPTIONS': + body = ''.encode('utf-8') + headers = self.buildHeaders(corsOrigin, body) + + if b'access-control-request-private-network' in req.headers and ( + req.headers[b'access-control-request-private-network'] == b'true'): + # include this header so that if a public origin is included in the whitelist, + # then browsers won't fail requests due to the private network access check + headers.append(['Access-Control-Allow-Private-Network', 'true']) + + return self.buildResponse(headers, body) + + try: + params = json.loads(req.body.decode('utf-8')) + jsonschema.validate(params, request_schema) + except (ValueError, jsonschema.ValidationError) as e: + if allowed: + if len(req.body) == 0: + body = f"AnkiConnect v.{util.setting('apiVersion')}".encode() + else: + reply = format_exception_reply(util.setting('apiVersion'), e) + body = json.dumps(reply).encode('utf-8') + headers = self.buildHeaders(corsOrigin, body) + return self.buildResponse(headers, body) + else: + params = {} # trigger the 403 response below + + if allowed or params.get('action', '') == 'requestPermission': + if params.get('action', '') == 'requestPermission': + params['params'] = params.get('params', {}) + params['params']['allowed'] = allowed + params['params']['origin'] = b'origin' in req.headers and req.headers[b'origin'].decode() or '' + if not allowed : + corsOrigin = params['params']['origin'] + + body = json.dumps(self.handler(params)).encode('utf-8') + headers = self.buildHeaders(corsOrigin, body) + else : + headers = [ + ['HTTP/1.1 403 Forbidden', None], + ['Access-Control-Allow-Origin', corsOrigin], + ['Access-Control-Allow-Headers', '*'] + ] + body = ''.encode('utf-8') + + return self.buildResponse(headers, body) + + + def allowOrigin(self, req): + # handle multiple cors origins by checking the 'origin'-header against the allowed origin list from the config + webCorsOriginList = util.setting('webCorsOriginList') + + # keep support for deprecated 'webCorsOrigin' field, as long it is not removed + webCorsOrigin = util.setting('webCorsOrigin') + if webCorsOrigin: + webCorsOriginList.append(webCorsOrigin) + + allowed = False + corsOrigin = 'http://localhost' + allowAllCors = '*' in webCorsOriginList # allow CORS for all domains + + if allowAllCors: + corsOrigin = '*' + allowed = True + elif b'origin' in req.headers: + originStr = req.headers[b'origin'].decode() + if originStr in webCorsOriginList : + corsOrigin = originStr + allowed = True + elif 'http://localhost' in webCorsOriginList and ( + originStr == 'http://127.0.0.1' or originStr == 'https://127.0.0.1' or # allow 127.0.0.1 if localhost allowed + originStr.startswith('http://127.0.0.1:') or originStr.startswith('http://127.0.0.1:') or + originStr.startswith('chrome-extension://') or originStr.startswith('moz-extension://') or originStr.startswith('safari-web-extension://') ) : # allow chrome, firefox and safari extension if localhost allowed + corsOrigin = originStr + allowed = True + else: + allowed = True + + return allowed, corsOrigin + + + def buildHeaders(self, corsOrigin, body): + return [ + ['HTTP/1.1 200 OK', None], + ['Content-Type', 'text/json'], + ['Access-Control-Allow-Origin', corsOrigin], + ['Access-Control-Allow-Headers', '*'], + ['Content-Length', str(len(body))] + ] + + + def buildResponse(self, headers, body): + resp = bytes() + for key, value in headers: + if value is None: + resp += '{}\r\n'.format(key).encode('utf-8') + else: + resp += '{}: {}\r\n'.format(key, value).encode('utf-8') + + resp += '\r\n'.encode('utf-8') + resp += body + return resp + + + def close(self): + if self.sock is not None: + self.sock.close() + self.sock = None + + for client in self.clients: + client.close() + + self.clients = [] + + +def format_success_reply(api_version, result): + if api_version <= 4: + return result + else: + return {"result": result, "error": None} + + +def format_exception_reply(_api_version, exception): + return {"result": None, "error": str(exception)} + + +request_schema = { + "type": "object", + "properties": { + "action": {"type": "string", "minLength": 1}, + "version": {"type": "integer"}, + "params": {"type": "object"}, + }, + "required": ["action"], +}
\ No newline at end of file diff --git a/.local/share/Anki2/addons21/Background_and_gear/LICENSE b/.local/share/Anki2/addons21/Background_and_gear/LICENSE new file mode 100644 index 0000000..0ad25db --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/LICENSE @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +<https://www.gnu.org/licenses/>. diff --git a/.local/share/Anki2/addons21/Background_and_gear/__init__.py b/.local/share/Anki2/addons21/Background_and_gear/__init__.py new file mode 100644 index 0000000..274cddc --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/__init__.py @@ -0,0 +1,188 @@ +# Copyright: ijgnd +# The AnKing +# Code License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html +# Background images were obtained from Pexels.com under this license https://www.pexels.com/photo-license/ +# Gear icons were obtained from Wikimedia Commons https://commons.wikimedia.org/wiki/Category:Noto_Color_Emoji_Pie (license listed in link) + +import os +import random +from typing import List + +from anki import version as anki_version # type: ignore +from anki.utils import pointVersion +from aqt import gui_hooks, mw +from aqt.addons import * +from aqt.editor import pics +# for the toolbar buttons +from aqt.qt import * + +from .adjust_css_files22 import * +from .config import addon_path, addonfoldername, gc +from .gui.resources import initialize_qt_resources +from .gui_updatemanager import setupMenu + +css_folder_for_anki_version = { + "22": "22", + "23": "22", + "24": "22", + "25": "25", + "26": "25", + "27": "25", + "28": "25", + "29": "25", + "30": "25", + "31": "31", + "32": "31", + "33": "31", + "34": "31", + "35": "31", + "36": "36", + "37": "36", + "38": "36", + "39": "36", + "40": "36", + "41": "36", + "42": "36", + "43": "36", + "44": "36", + "45": "36", + "46": "36", +} + +version = pointVersion() +if int(version) in css_folder_for_anki_version: + version_folder = css_folder_for_anki_version[str(version)] +else: # for newer Anki versions try the latest version and hope for the best + version_folder = css_folder_for_anki_version[ + max(css_folder_for_anki_version, key=int) + ] + +SOURCE_ABSOLUTE = os.path.join(addon_path, "sources", "css", version_folder) +WEB_ABSOLUTE = os.path.join(addon_path, "web", "css") +CSS_FILES_TO_REPLACE = [ + os.path.basename(f) for f in os.listdir(WEB_ABSOLUTE) if f.endswith(".css") +] +WEB_EXPORTS_REGEX = r"(user_files.*|web.*)" + + +def main(): + initialize_qt_resources() + setupMenu() + + mw.addonManager.setWebExports(__name__, WEB_EXPORTS_REGEX) + update_css_files() + + gui_hooks.state_did_change.append(maybe_update_css_files) + gui_hooks.webview_will_set_content.append(include_css_files) + gui_hooks.deck_browser_will_render_content.append(replace_gears) + + def on_config_update(config): + update_css_files() + mw.moveToState("deckBrowser") + + mw.addonManager.setConfigUpdatedAction(__name__, on_config_update) + + +def update_css_files(): + # combine template files with config and write into webexports folder + change_copy = [ + os.path.basename(f) for f in os.listdir(SOURCE_ABSOLUTE) if f.endswith(".css") + ] + for file_name in change_copy: + with open(os.path.join(SOURCE_ABSOLUTE, file_name)) as f: + content = f.read() + + if version == 36: + if file_name == "deckbrowser.css": + content = adjust_deckbrowser_css22(content) + if file_name == "toolbar.css" and gc("Toolbar image"): + content = adjust_toolbar_css22(content) + if file_name == "overview.css": + content = adjust_overview_css22(content) + if file_name == "toolbar-bottom.css" and gc("Toolbar image"): + content = adjust_bottomtoolbar_css22(content) + if file_name == "reviewer.css" and gc("Reviewer image"): + content = adjust_reviewer_css22(content) + if ( + file_name == "reviewer-bottom.css" + and gc("Reviewer image") + and gc("Toolbar image") + ): + content = adjust_reviewerbottom_css22(content) + + # for later versions: try the latest + # this code will likely change when new Anki versions are released which might require + # updates of this add-on. + else: + if file_name == "deckbrowser.css": + content = adjust_deckbrowser_css22(content) + if file_name == "toolbar.css" and gc("Toolbar image"): + content = adjust_toolbar_css22(content) + if file_name == "overview.css": + content = adjust_overview_css22(content) + if file_name == "toolbar-bottom.css" and gc("Toolbar image"): + content = adjust_bottomtoolbar_css22(content) + if file_name == "reviewer.css" and gc("Reviewer image"): + content = adjust_reviewer_css22(content) + if file_name == "reviewer-bottom.css": # and gc("Reviewer image"): + content = adjust_reviewerbottom_css22(content) + + with open(os.path.join(WEB_ABSOLUTE, file_name), "w") as f: + f.write(content) + + +# reset background when refreshing page (for use with "random" setting) +def maybe_update_css_files(new_state, old_state): + if new_state != "deckBrowser": + return + + update_css_files() + if not tuple(int(i) for i in anki_version.split(".")) < (2, 1, 27): + mw.toolbar.redraw() + + +def maybe_adjust_file_name(file_name): + if pointVersion() >= 36: + file_name = file_name.lstrip("css/") + return file_name + + +def include_css_files(web_content, context): + new_css: List[str] = web_content.css[:] + for idx, file_name in enumerate(web_content.css): + file_name = maybe_adjust_file_name(file_name) + if file_name in CSS_FILES_TO_REPLACE: + new_css[idx] = f"/_addons/{addonfoldername}/web/css/{file_name}" + new_css.append( + f"/_addons/{addonfoldername}/user_files/css/custom_{file_name}" + ) + web_content.css = new_css + + +def replace_gears(deck_browser, content): + old = """<img src='/_anki/imgs/gears.svg'""" + new = f"""<img src='/_addons/{addonfoldername}/user_files/gear/{get_gearfile()}'""" + if gc("Image name for gear") != "gears.svg": + content.tree = content.tree.replace(old, new) + else: + content.tree = content.tree.replace(old, old) + + +def get_gearfile(): + gear_abs = os.path.join(addon_path, "user_files", "gear") + gear_list = [os.path.basename(f) for f in os.listdir(gear_abs) if f.endswith(pics)] + val = gc("Image name for gear") + if val and val.lower() == "random": + return random.choice(gear_list) + if val in gear_list: + return val + else: + # if empty or illegal value try to use 'AnKing.png' to signal that an illegal values was + # used AnKing's gears folder doesn't contain a file named "gears.svg" + if "AnKing.png" in gear_list: + return "AnKing.png" + else: + return "" + + +main() diff --git a/.local/share/Anki2/addons21/Background_and_gear/adjust_css_files22.py b/.local/share/Anki2/addons21/Background_and_gear/adjust_css_files22.py new file mode 100644 index 0000000..ef82ce2 --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/adjust_css_files22.py @@ -0,0 +1,107 @@ +# Copyright: ijgnd +# AnKingMed +# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html + +import os +import random + +from aqt.editor import pics +from aqt.gui_hooks import state_did_change + +from .config import addon_path, addonfoldername, gc + + +def set_bg_img(filecontent, imgname, location, review=False): + #add background image for normal and nightmode + img_web_rel_path = f"/_addons/{addonfoldername}/user_files/background/{imgname}" + old = "/*AnKing edits*/" + if location == "body": + bg_position = gc("background-position", "center") + bg_color = gc("background-color main", "") + elif location == "top" and gc("Toolbar top/bottom"): + bg_position = "top" + elif location == "bottom" and gc("Toolbar top/bottom"): + bg_position = "bottom;" + else: + bg_position = f"""background-position: {gc("background-position", "center")};""" + if location == "top": + bg_color = gc("background-color top", "") + elif location == "bottom": + bg_color = gc("background-color bottom", "") + if review: + opacity = gc("background opacity review", "1") + else: + opacity = gc("background opacity main", "1") + scale = gc("background scale", "1") + + bracket_start = "body::before {" + bracket_close = "}" + if review and not gc("Reviewer image"): + background = "background-image:none!important;" + else: + background = f""" + background-image: url("{img_web_rel_path}"); + background-size: {gc("background-size", "contain")}; + background-attachment: {gc("background-attachment", "fixed")}!important; + background-repeat: no-repeat; + background-position: {bg_position}; + background-color: {bg_color}!important; + opacity: {opacity}; + content: ""; + top: 0; + left: 0; + bottom: 0; + right: 0; + position: fixed; + z-index: -99; + will-change: transform; + transform: scale({scale}); + """ + + new = f"""{bracket_start}\n{background}\n{bracket_close}""" + result = filecontent.replace(old, new) + return result + +def get_bg_img(): + bg_abs_path = os.path.join(addon_path, "user_files", "background") + bgimg_list = [os.path.basename(f) for f in os.listdir(bg_abs_path) if f.endswith(pics)] + val = gc("Image name for background") + if val and val.lower() == "random": + return random.choice(bgimg_list) + if val in bgimg_list: + return val + else: + # if empty or illegal value show no background to signal that an illegal values was used + return "" + + +img_name = get_bg_img() +def reset_image(new_state, old_state): + global img_name + if new_state == "deckBrowser": + img_name = get_bg_img() +state_did_change.append(reset_image) + +def adjust_deckbrowser_css22(filecontent): + result = set_bg_img(filecontent, img_name, "body") + #do not invert gears if using personal image + if gc("Image name for gear") != "gears.svg": + old_gears = "filter: invert(180);" + new_gears = "/* filter: invert(180); */" + result = result.replace(old_gears, new_gears) + return result + +def adjust_toolbar_css22(filecontent): + return set_bg_img(filecontent, img_name, "top") + +def adjust_bottomtoolbar_css22(filecontent): + return set_bg_img(filecontent, img_name, "bottom") + +def adjust_overview_css22(filecontent): + return set_bg_img(filecontent, img_name, "body") + +def adjust_reviewer_css22(filecontent): + return set_bg_img(filecontent, img_name, "body", True) + +def adjust_reviewerbottom_css22(filecontent): + return set_bg_img(filecontent, img_name, "bottom", True) diff --git a/.local/share/Anki2/addons21/Background_and_gear/config.json b/.local/share/Anki2/addons21/Background_and_gear/config.json new file mode 100644 index 0000000..46258df --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/config.json @@ -0,0 +1,17 @@ +{ + "Image name for background": "samuel-inkilainen-laboratory.jpg", + "Image name for gear": "AnKing.png", + "Keyboard Shortcut": "Ctrl+Shift+b", + "Reviewer image": true, + "Toolbar image": true, + "Toolbar top/bottom": true, + "background-attachment": "fixed", + "background-color bottom": "", + "background-color main": "", + "background-color top": "", + "background opacity main": "1", + "background opacity review": "1", + "background-position": "center", + "background scale": "1", + "background-size": "cover" +} diff --git a/.local/share/Anki2/addons21/Background_and_gear/config.md b/.local/share/Anki2/addons21/Background_and_gear/config.md new file mode 100644 index 0000000..e0d8474 --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/config.md @@ -0,0 +1,42 @@ +<center><div style="vertical-align:middle;"><a href="https://www.ankingmed.com"><img src="../../addons21/1210908941/AnKing/AnKingSmall.png"></a><a href="https://www.ankingmed.com"><img src="../../addons21/1210908941/AnKing/TheAnKing.png"></a></div></center> + +<center><a href="https://www.facebook.com/ankingmed"><img src="../../addons21/1210908941/AnKing/Facebook.jpg"></a> + <a href="https://www.instagram.com/ankingmed"><img src="../../addons21/1210908941/AnKing/Instagram.jpg"></a> + <a href="https://www.youtube.com/theanking"><img src="../../addons21/1210908941/AnKing/YouTube.jpg"></a></center> + +<center><a href="https://www.patreon.com/ankingmed"><img src="../../addons21/1210908941/AnKing/Patreon.jpg"></a></center> + +--- + +<div style="color: #4297F9;"><b>If you enjoy this add-on or want individualized Anki help, please consider supporting us!</b></div> + +<div style="color: red; font-size: 16px; background-color: rgb(25, 25, 25); text-align: center;"><br>Restart is required for any change to take effect<br></div> + +--- + +_<div style="color:#A440C3;">You can add custom images to the folders "background" and "gears" and custom css to the "css" folder. Access this folder by <b>Tools->Background/gear image folder</b>. I would recommend using 20-50% opacity images for the background (this must be configured in an external editor like photoshop).</div>_ + +<span style="color:grey;"><br><b>Background images</b> were obtained from</span> [Pexels.com](https://www.pexels.com/photo-license/) +<span style="color:grey;"><br><b>Gear icons</b> were obtained from</span> [Wikimedia Commons](https://commons.wikimedia.org/wiki/Category:Noto_Color_Emoji_Pie) + +--- + +##Config: + +* **Image name for background:** _(For no image use "". "Random" will shuffle through defaults)_ name of the background image file. + * _<span style="color:red;">preloaded images (all .png) include: AnKing, Beach, BeachAerial, Christ&Surgeon, ColorfulLights, Fire, Island, Milkyway, MoonMountains, NightSky, Ocean, SLCtemple, Sunset, SunsetMountain</div>_ +* **Image name for gear:** _(Anki default is "gears.svg". "Random" will shuffle through defaults)_ name of the file to replace the gear icon. (preloaded images include AnKing.png, flame.svg) + * _<span style="color:red;">preloaded images (all .png) include: AnKing, Bam, Bullseye, Cowboy, Diamond, Dragon, Fire, Flower, Nerd, Rose, Shield, Skull, Star, Sun</div>_ +* **Reviewer image:** _(true or false)_ show the background image in the reviewer screen +* **Toolbar top/bottom:** _(true or false)_ Set the background position of the toolbars to top and bottom (if the main background position is set to center, this will look cleaner for most images) +* **Toolbar image:** _(true or false)_ show the background image in the top and bottom toolbars in addition to the main screen + + +_The following are css values and can be styled with appropriate css values for these css properties. See w3schools.com for more information._ + +* **background-attachment:** _(default "fixed")_ scroll or fixed +* **background-color:** _(default "")_ set to "" to have no background color +* **background-position:** _(default "center")_ left top, right bottom, 25% 50%, 100px 200px, etc +* **background scale:** _(default "1")_ set the scale of the image. 1 is the original image size. 2 is 200% original image size. You can also use 2,1 to scale x,y separately +* **background-size:** _(default "contain", but I would recommend "cover" if you set "Toolbar image" to true)_ contain, cover, 50%, 100px, etc + diff --git a/.local/share/Anki2/addons21/Background_and_gear/config.py b/.local/share/Anki2/addons21/Background_and_gear/config.py new file mode 100644 index 0000000..da450d7 --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/config.py @@ -0,0 +1,44 @@ +import os + +from aqt import mw + + +addon_path = os.path.dirname(__file__) +addonfoldername = os.path.basename(addon_path) + + +def gc(arg="", fail=False): + conf = mw.addonManager.getConfig(__name__) + if conf: + if arg: + return conf.get(arg, fail) + else: + return conf + return fail + + +userOption = None + +def _getUserOption(refresh): + global userOption + if userOption is None or refresh: + userOption = mw.addonManager.getConfig(__name__) + + +def getUserOption(key=None, default=None, refresh=False): + _getUserOption(refresh) + if key is None: + return userOption + if key in userOption: + return userOption[key] + else: + return default + + +def writeConfig(configToWrite=userOption): + mw.addonManager.writeConfig(__name__, configToWrite) + + +def getDefaultConfig(): + addon = __name__.split(".")[0] + return mw.addonManager.addonConfigDefaults(addon) diff --git a/.local/share/Anki2/addons21/Background_and_gear/gui/forms/__init__.py b/.local/share/Anki2/addons21/Background_and_gear/gui/forms/__init__.py new file mode 100644 index 0000000..6e15507 --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/gui/forms/__init__.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +# +# Custom background and gear icon Add-on for Anki +# Copyright (C) 2022 +# +# This file was automatically generated by Anki Add-on Builder v1.0.0-dev.2 +# It is subject to the same licensing terms as the rest of the program +# (see the LICENSE file which accompanies this program). +# +# WARNING! All changes made in this file will be lost! + +""" +Shim that imports forms corresponding to runtime Qt version +""" + +from typing import TYPE_CHECKING + +from aqt.qt import qtmajor + +if TYPE_CHECKING or qtmajor >= 6: + from .qt6 import * # noqa: F401 +else: + from .qt5 import * # noqa: F401 diff --git a/.local/share/Anki2/addons21/Background_and_gear/gui/forms/qt5/__init__.py b/.local/share/Anki2/addons21/Background_and_gear/gui/forms/qt5/__init__.py new file mode 100644 index 0000000..549ed56 --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/gui/forms/qt5/__init__.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# +# Custom background and gear icon Add-on for Anki +# Copyright (C) 2022 +# +# This file was automatically generated by Anki Add-on Builder v1.0.0-dev.2 +# It is subject to the same licensing terms as the rest of the program +# (see the LICENSE file which accompanies this program). +# +# WARNING! All changes made in this file will be lost! + +""" +Initializes generated Qt forms/resources +""" + +__all__ = [ + "settings_dialog" +] + +from . import settings_dialog diff --git a/.local/share/Anki2/addons21/Background_and_gear/gui/forms/qt5/settings_dialog.py b/.local/share/Anki2/addons21/Background_and_gear/gui/forms/qt5/settings_dialog.py new file mode 100644 index 0000000..f1cae30 --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/gui/forms/qt5/settings_dialog.py @@ -0,0 +1,332 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'designer/settings_dialog.ui' +# +# Created by: PyQt5 UI code generator 5.15.6 +# +# WARNING: Any manual changes made to this file will be lost when pyuic5 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt5 import QtCore, QtGui, QtWidgets + + +class Ui_Dialog(object): + def setupUi(self, Dialog): + Dialog.setObjectName("Dialog") + Dialog.resize(548, 601) + self.gridLayout_6 = QtWidgets.QGridLayout(Dialog) + self.gridLayout_6.setObjectName("gridLayout_6") + self.groupBox_4 = QtWidgets.QGroupBox(Dialog) + self.groupBox_4.setTitle("") + self.groupBox_4.setObjectName("groupBox_4") + self.gridLayout_2 = QtWidgets.QGridLayout(self.groupBox_4) + self.gridLayout_2.setObjectName("gridLayout_2") + self.label_4 = QtWidgets.QLabel(self.groupBox_4) + self.label_4.setObjectName("label_4") + self.gridLayout_2.addWidget(self.label_4, 0, 0, 1, 1) + self.comboBox_attachment = QtWidgets.QComboBox(self.groupBox_4) + self.comboBox_attachment.setObjectName("comboBox_attachment") + self.comboBox_attachment.addItem("") + self.comboBox_attachment.addItem("") + self.gridLayout_2.addWidget(self.comboBox_attachment, 0, 1, 1, 1) + self.label_8 = QtWidgets.QLabel(self.groupBox_4) + self.label_8.setObjectName("label_8") + self.gridLayout_2.addWidget(self.label_8, 0, 2, 1, 1) + self.comboBox_size = QtWidgets.QComboBox(self.groupBox_4) + self.comboBox_size.setObjectName("comboBox_size") + self.comboBox_size.addItem("") + self.comboBox_size.addItem("") + self.gridLayout_2.addWidget(self.comboBox_size, 0, 3, 1, 1) + self.label_7 = QtWidgets.QLabel(self.groupBox_4) + self.label_7.setObjectName("label_7") + self.gridLayout_2.addWidget(self.label_7, 1, 0, 1, 1) + self.comboBox_position = QtWidgets.QComboBox(self.groupBox_4) + self.comboBox_position.setObjectName("comboBox_position") + self.comboBox_position.addItem("") + self.comboBox_position.addItem("") + self.comboBox_position.addItem("") + self.comboBox_position.addItem("") + self.comboBox_position.addItem("") + self.comboBox_position.addItem("") + self.comboBox_position.addItem("") + self.comboBox_position.addItem("") + self.comboBox_position.addItem("") + self.gridLayout_2.addWidget(self.comboBox_position, 1, 1, 1, 1) + self.label_9 = QtWidgets.QLabel(self.groupBox_4) + self.label_9.setObjectName("label_9") + self.gridLayout_2.addWidget(self.label_9, 1, 2, 1, 1) + self.scaleBox = QtWidgets.QDoubleSpinBox(self.groupBox_4) + self.scaleBox.setDecimals(1) + self.scaleBox.setMinimum(0.1) + self.scaleBox.setSingleStep(0.1) + self.scaleBox.setProperty("value", 1.0) + self.scaleBox.setObjectName("scaleBox") + self.gridLayout_2.addWidget(self.scaleBox, 1, 3, 1, 1) + self.gridLayout_6.addWidget(self.groupBox_4, 3, 0, 1, 12) + self.RestoreButton = QtWidgets.QPushButton(Dialog) + self.RestoreButton.setObjectName("RestoreButton") + self.gridLayout_6.addWidget(self.RestoreButton, 8, 8, 1, 3) + self.pushButton_videoTutorial = QtWidgets.QPushButton(Dialog) + self.pushButton_videoTutorial.setObjectName("pushButton_videoTutorial") + self.gridLayout_6.addWidget(self.pushButton_videoTutorial, 8, 0, 1, 8) + self.groupBox_3 = QtWidgets.QGroupBox(Dialog) + self.groupBox_3.setObjectName("groupBox_3") + self.gridLayout_3 = QtWidgets.QGridLayout(self.groupBox_3) + self.gridLayout_3.setObjectName("gridLayout_3") + self.label_5 = QtWidgets.QLabel(self.groupBox_3) + self.label_5.setToolTip("") + self.label_5.setToolTipDuration(-1) + self.label_5.setStatusTip("") + self.label_5.setWhatsThis("") + self.label_5.setObjectName("label_5") + self.gridLayout_3.addWidget(self.label_5, 0, 0, 1, 1) + self.Slider_main = QtWidgets.QSlider(self.groupBox_3) + self.Slider_main.setToolTip("") + self.Slider_main.setToolTipDuration(-1) + self.Slider_main.setWhatsThis("") + self.Slider_main.setMaximum(100) + self.Slider_main.setPageStep(1) + self.Slider_main.setProperty("value", 100) + self.Slider_main.setSliderPosition(100) + self.Slider_main.setTracking(True) + self.Slider_main.setOrientation(QtCore.Qt.Horizontal) + self.Slider_main.setTickPosition(QtWidgets.QSlider.NoTicks) + self.Slider_main.setTickInterval(5) + self.Slider_main.setObjectName("Slider_main") + self.gridLayout_3.addWidget(self.Slider_main, 0, 1, 1, 1) + self.label_6 = QtWidgets.QLabel(self.groupBox_3) + self.label_6.setObjectName("label_6") + self.gridLayout_3.addWidget(self.label_6, 1, 0, 1, 1) + self.Slider_review = QtWidgets.QSlider(self.groupBox_3) + self.Slider_review.setMaximum(100) + self.Slider_review.setPageStep(10) + self.Slider_review.setProperty("value", 1) + self.Slider_review.setSliderPosition(1) + self.Slider_review.setOrientation(QtCore.Qt.Horizontal) + self.Slider_review.setInvertedAppearance(False) + self.Slider_review.setTickPosition(QtWidgets.QSlider.NoTicks) + self.Slider_review.setObjectName("Slider_review") + self.gridLayout_3.addWidget(self.Slider_review, 1, 1, 1, 1) + self.gridLayout_6.addWidget(self.groupBox_3, 2, 8, 1, 4) + self.groupBox = QtWidgets.QGroupBox(Dialog) + self.groupBox.setWhatsThis("") + self.groupBox.setTitle("") + self.groupBox.setObjectName("groupBox") + self.gridLayout_5 = QtWidgets.QGridLayout(self.groupBox) + self.gridLayout_5.setObjectName("gridLayout_5") + self.toolButton_gear = QtWidgets.QToolButton(self.groupBox) + self.toolButton_gear.setObjectName("toolButton_gear") + self.gridLayout_5.addWidget(self.toolButton_gear, 1, 2, 1, 1) + self.label = QtWidgets.QLabel(self.groupBox) + self.label.setToolTipDuration(10000) + self.label.setObjectName("label") + self.gridLayout_5.addWidget(self.label, 0, 0, 1, 1) + self.pushButton_randomize = QtWidgets.QPushButton(self.groupBox) + self.pushButton_randomize.setObjectName("pushButton_randomize") + self.gridLayout_5.addWidget(self.pushButton_randomize, 2, 1, 1, 1) + self.pushButton_imageFolder = QtWidgets.QPushButton(self.groupBox) + self.pushButton_imageFolder.setObjectName("pushButton_imageFolder") + self.gridLayout_5.addWidget(self.pushButton_imageFolder, 2, 0, 1, 1) + self.lineEdit_background = QtWidgets.QLineEdit(self.groupBox) + self.lineEdit_background.setObjectName("lineEdit_background") + self.gridLayout_5.addWidget(self.lineEdit_background, 0, 1, 1, 1) + self.lineEdit_gear = QtWidgets.QLineEdit(self.groupBox) + self.lineEdit_gear.setObjectName("lineEdit_gear") + self.gridLayout_5.addWidget(self.lineEdit_gear, 1, 1, 1, 1) + self.label_2 = QtWidgets.QLabel(self.groupBox) + self.label_2.setToolTipDuration(10000) + self.label_2.setObjectName("label_2") + self.gridLayout_5.addWidget(self.label_2, 1, 0, 1, 1) + self.toolButton_background = QtWidgets.QToolButton(self.groupBox) + self.toolButton_background.setObjectName("toolButton_background") + self.gridLayout_5.addWidget(self.toolButton_background, 0, 2, 1, 1) + self.gridLayout_6.addWidget(self.groupBox, 1, 0, 1, 12) + self.groupBox_5 = QtWidgets.QGroupBox(Dialog) + self.groupBox_5.setObjectName("groupBox_5") + self.gridLayout = QtWidgets.QGridLayout(self.groupBox_5) + self.gridLayout.setObjectName("gridLayout") + self.label_11 = QtWidgets.QLabel(self.groupBox_5) + self.label_11.setObjectName("label_11") + self.gridLayout.addWidget(self.label_11, 0, 0, 1, 1) + self.lineEdit_color_main = QtWidgets.QLineEdit(self.groupBox_5) + self.lineEdit_color_main.setObjectName("lineEdit_color_main") + self.gridLayout.addWidget(self.lineEdit_color_main, 0, 1, 1, 1) + self.toolButton_color_main = QtWidgets.QToolButton(self.groupBox_5) + self.toolButton_color_main.setObjectName("toolButton_color_main") + self.gridLayout.addWidget(self.toolButton_color_main, 0, 2, 1, 1) + self.label_12 = QtWidgets.QLabel(self.groupBox_5) + self.label_12.setObjectName("label_12") + self.gridLayout.addWidget(self.label_12, 0, 3, 1, 1) + self.lineEdit_color_top = QtWidgets.QLineEdit(self.groupBox_5) + self.lineEdit_color_top.setObjectName("lineEdit_color_top") + self.gridLayout.addWidget(self.lineEdit_color_top, 0, 4, 1, 1) + self.toolButton_color_top = QtWidgets.QToolButton(self.groupBox_5) + self.toolButton_color_top.setObjectName("toolButton_color_top") + self.gridLayout.addWidget(self.toolButton_color_top, 0, 5, 1, 1) + self.label_13 = QtWidgets.QLabel(self.groupBox_5) + self.label_13.setObjectName("label_13") + self.gridLayout.addWidget(self.label_13, 1, 3, 1, 1) + self.lineEdit_color_bottom = QtWidgets.QLineEdit(self.groupBox_5) + self.lineEdit_color_bottom.setObjectName("lineEdit_color_bottom") + self.gridLayout.addWidget(self.lineEdit_color_bottom, 1, 4, 1, 1) + self.toolButton_color_bottom = QtWidgets.QToolButton(self.groupBox_5) + self.toolButton_color_bottom.setObjectName("toolButton_color_bottom") + self.gridLayout.addWidget(self.toolButton_color_bottom, 1, 5, 1, 1) + self.gridLayout_6.addWidget(self.groupBox_5, 4, 0, 1, 12) + spacerItem = QtWidgets.QSpacerItem(20, 5, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) + self.gridLayout_6.addItem(spacerItem, 7, 0, 1, 1) + self.groupBox_2 = QtWidgets.QGroupBox(Dialog) + self.groupBox_2.setTitle("") + self.groupBox_2.setObjectName("groupBox_2") + self.gridLayout_4 = QtWidgets.QGridLayout(self.groupBox_2) + self.gridLayout_4.setObjectName("gridLayout_4") + self.checkBox_reviewer = QtWidgets.QCheckBox(self.groupBox_2) + self.checkBox_reviewer.setToolTipDuration(10000) + self.checkBox_reviewer.setChecked(True) + self.checkBox_reviewer.setObjectName("checkBox_reviewer") + self.gridLayout_4.addWidget(self.checkBox_reviewer, 0, 0, 1, 1) + self.checkBox_toolbar = QtWidgets.QCheckBox(self.groupBox_2) + self.checkBox_toolbar.setToolTipDuration(10000) + self.checkBox_toolbar.setChecked(True) + self.checkBox_toolbar.setObjectName("checkBox_toolbar") + self.gridLayout_4.addWidget(self.checkBox_toolbar, 1, 0, 1, 1) + self.checkBox_topbottom = QtWidgets.QCheckBox(self.groupBox_2) + self.checkBox_topbottom.setToolTipDuration(10000) + self.checkBox_topbottom.setChecked(True) + self.checkBox_topbottom.setObjectName("checkBox_topbottom") + self.gridLayout_4.addWidget(self.checkBox_topbottom, 2, 0, 1, 1) + self.gridLayout_6.addWidget(self.groupBox_2, 2, 0, 1, 8) + self.OkButton = QtWidgets.QPushButton(Dialog) + self.OkButton.setObjectName("OkButton") + self.gridLayout_6.addWidget(self.OkButton, 8, 11, 1, 1) + spacerItem1 = QtWidgets.QSpacerItem(20, 5, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) + self.gridLayout_6.addItem(spacerItem1, 5, 0, 1, 1) + self.horizontalLayout = QtWidgets.QHBoxLayout() + self.horizontalLayout.setSizeConstraint(QtWidgets.QLayout.SetNoConstraint) + self.horizontalLayout.setObjectName("horizontalLayout") + self.toolButton_palace = QtWidgets.QToolButton(Dialog) + icon = QtGui.QIcon() + icon.addPixmap(QtGui.QPixmap("CustomBackground:AnkiPalace_no_text.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.toolButton_palace.setIcon(icon) + self.toolButton_palace.setIconSize(QtCore.QSize(40, 40)) + self.toolButton_palace.setObjectName("toolButton_palace") + self.horizontalLayout.addWidget(self.toolButton_palace) + self.label_3 = QtWidgets.QLabel(Dialog) + self.label_3.setMinimumSize(QtCore.QSize(400, 0)) + self.label_3.setWordWrap(True) + self.label_3.setObjectName("label_3") + self.horizontalLayout.addWidget(self.label_3) + self.gridLayout_6.addLayout(self.horizontalLayout, 6, 0, 1, 12) + self.horizontalLayout_2 = QtWidgets.QHBoxLayout() + self.horizontalLayout_2.setSizeConstraint(QtWidgets.QLayout.SetNoConstraint) + self.horizontalLayout_2.setSpacing(6) + self.horizontalLayout_2.setObjectName("horizontalLayout_2") + self.toolButton_website = QtWidgets.QToolButton(Dialog) + self.toolButton_website.setMaximumSize(QtCore.QSize(31, 31)) + icon1 = QtGui.QIcon() + icon1.addPixmap(QtGui.QPixmap("CustomBackground:AnKingSmall.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.toolButton_website.setIcon(icon1) + self.toolButton_website.setIconSize(QtCore.QSize(31, 31)) + self.toolButton_website.setObjectName("toolButton_website") + self.horizontalLayout_2.addWidget(self.toolButton_website) + self.toolButton_youtube = QtWidgets.QToolButton(Dialog) + self.toolButton_youtube.setMaximumSize(QtCore.QSize(31, 31)) + icon2 = QtGui.QIcon() + icon2.addPixmap(QtGui.QPixmap("CustomBackground:YouTube.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.toolButton_youtube.setIcon(icon2) + self.toolButton_youtube.setIconSize(QtCore.QSize(31, 31)) + self.toolButton_youtube.setObjectName("toolButton_youtube") + self.horizontalLayout_2.addWidget(self.toolButton_youtube) + self.toolButton_patreon = QtWidgets.QToolButton(Dialog) + self.toolButton_patreon.setMaximumSize(QtCore.QSize(171, 26)) + icon3 = QtGui.QIcon() + icon3.addPixmap(QtGui.QPixmap("CustomBackground:Patreon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.toolButton_patreon.setIcon(icon3) + self.toolButton_patreon.setIconSize(QtCore.QSize(200, 31)) + self.toolButton_patreon.setObjectName("toolButton_patreon") + self.horizontalLayout_2.addWidget(self.toolButton_patreon, 0, QtCore.Qt.AlignHCenter) + self.toolButton_instagram = QtWidgets.QToolButton(Dialog) + self.toolButton_instagram.setMaximumSize(QtCore.QSize(31, 31)) + icon4 = QtGui.QIcon() + icon4.addPixmap(QtGui.QPixmap("CustomBackground:Instagram.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.toolButton_instagram.setIcon(icon4) + self.toolButton_instagram.setIconSize(QtCore.QSize(31, 31)) + self.toolButton_instagram.setObjectName("toolButton_instagram") + self.horizontalLayout_2.addWidget(self.toolButton_instagram) + self.toolButton_facebook = QtWidgets.QToolButton(Dialog) + self.toolButton_facebook.setMaximumSize(QtCore.QSize(31, 31)) + icon5 = QtGui.QIcon() + icon5.addPixmap(QtGui.QPixmap("CustomBackground:Facebook.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.toolButton_facebook.setIcon(icon5) + self.toolButton_facebook.setIconSize(QtCore.QSize(31, 31)) + self.toolButton_facebook.setObjectName("toolButton_facebook") + self.horizontalLayout_2.addWidget(self.toolButton_facebook) + self.gridLayout_6.addLayout(self.horizontalLayout_2, 0, 2, 1, 9) + self.groupBox_5.raise_() + self.groupBox_4.raise_() + self.groupBox_3.raise_() + self.groupBox_2.raise_() + self.groupBox.raise_() + self.pushButton_videoTutorial.raise_() + self.RestoreButton.raise_() + self.OkButton.raise_() + + self.retranslateUi(Dialog) + QtCore.QMetaObject.connectSlotsByName(Dialog) + + def retranslateUi(self, Dialog): + _translate = QtCore.QCoreApplication.translate + Dialog.setWindowTitle(_translate("Dialog", "Custom Background Settings")) + self.label_4.setText(_translate("Dialog", "Attachment:")) + self.comboBox_attachment.setItemText(0, _translate("Dialog", "fixed")) + self.comboBox_attachment.setItemText(1, _translate("Dialog", "scroll")) + self.label_8.setText(_translate("Dialog", "Size:")) + self.comboBox_size.setItemText(0, _translate("Dialog", "cover")) + self.comboBox_size.setItemText(1, _translate("Dialog", "contain")) + self.label_7.setText(_translate("Dialog", "Position:")) + self.comboBox_position.setCurrentText(_translate("Dialog", "left top")) + self.comboBox_position.setItemText(0, _translate("Dialog", "left top")) + self.comboBox_position.setItemText(1, _translate("Dialog", "center top")) + self.comboBox_position.setItemText(2, _translate("Dialog", "right top")) + self.comboBox_position.setItemText(3, _translate("Dialog", "right")) + self.comboBox_position.setItemText(4, _translate("Dialog", "left")) + self.comboBox_position.setItemText(5, _translate("Dialog", "center")) + self.comboBox_position.setItemText(6, _translate("Dialog", "left bottom")) + self.comboBox_position.setItemText(7, _translate("Dialog", "center bottom")) + self.comboBox_position.setItemText(8, _translate("Dialog", "right bottom")) + self.label_9.setText(_translate("Dialog", "Scale:")) + self.RestoreButton.setText(_translate("Dialog", "Restore Default")) + self.pushButton_videoTutorial.setText(_translate("Dialog", "Video Tutorial")) + self.groupBox_3.setTitle(_translate("Dialog", "Background opacity")) + self.label_5.setText(_translate("Dialog", "Main")) + self.label_6.setText(_translate("Dialog", "Review")) + self.toolButton_gear.setText(_translate("Dialog", "...")) + self.label.setToolTip(_translate("Dialog", "<html><head/><body><p>Name of the background image file.</p><p>"Random" will shuffle through defaults</p></body></html>")) + self.label.setText(_translate("Dialog", "Image name for background:")) + self.pushButton_randomize.setText(_translate("Dialog", "Random Images")) + self.pushButton_imageFolder.setText(_translate("Dialog", "Open Image Folders")) + self.label_2.setToolTip(_translate("Dialog", "<html><head/><body><p>Name of the file to replace the gear icon. </p><p>Anki default is gears.svg</p><p>"Random" will shuffle through defaults</p></body></html>")) + self.label_2.setText(_translate("Dialog", "Image name for gear icon:")) + self.toolButton_background.setText(_translate("Dialog", "...")) + self.groupBox_5.setTitle(_translate("Dialog", "Background color")) + self.label_11.setText(_translate("Dialog", "Main:")) + self.toolButton_color_main.setText(_translate("Dialog", "...")) + self.label_12.setText(_translate("Dialog", "Top toolbar:")) + self.toolButton_color_top.setText(_translate("Dialog", "...")) + self.label_13.setText(_translate("Dialog", "Bottom toolbar:")) + self.toolButton_color_bottom.setText(_translate("Dialog", "...")) + self.checkBox_reviewer.setToolTip(_translate("Dialog", "<html><head/><body><p>Show the background image in the reviewer screen</p></body></html>")) + self.checkBox_reviewer.setText(_translate("Dialog", "Show in reviewer")) + self.checkBox_toolbar.setToolTip(_translate("Dialog", "<html><head/><body><p>Show the background image in the top and bottom toolbars in addition to the main screen</p></body></html>")) + self.checkBox_toolbar.setText(_translate("Dialog", "Show in toolbar")) + self.checkBox_topbottom.setToolTip(_translate("Dialog", "<html><head/><body><p>Set the background position of the toolbars to top and bottom (if the main background position is set to center, this will look cleaner for most images)<br/></p></body></html>")) + self.checkBox_topbottom.setText(_translate("Dialog", "Toolbar top/bottom")) + self.OkButton.setText(_translate("Dialog", "OK")) + self.toolButton_palace.setText(_translate("Dialog", "...")) + self.label_3.setText(_translate("Dialog", "Interested in learning how to use Anki effectively? Check out AnkiPalace, a comprehensive series of lessons and video tutorials on Anki designed by the AnKing team.")) + self.toolButton_website.setText(_translate("Dialog", "...")) + self.toolButton_youtube.setText(_translate("Dialog", "...")) + self.toolButton_patreon.setText(_translate("Dialog", "...")) + self.toolButton_instagram.setText(_translate("Dialog", "...")) + self.toolButton_facebook.setText(_translate("Dialog", "..."))
\ No newline at end of file diff --git a/.local/share/Anki2/addons21/Background_and_gear/gui/forms/qt6/__init__.py b/.local/share/Anki2/addons21/Background_and_gear/gui/forms/qt6/__init__.py new file mode 100644 index 0000000..549ed56 --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/gui/forms/qt6/__init__.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# +# Custom background and gear icon Add-on for Anki +# Copyright (C) 2022 +# +# This file was automatically generated by Anki Add-on Builder v1.0.0-dev.2 +# It is subject to the same licensing terms as the rest of the program +# (see the LICENSE file which accompanies this program). +# +# WARNING! All changes made in this file will be lost! + +""" +Initializes generated Qt forms/resources +""" + +__all__ = [ + "settings_dialog" +] + +from . import settings_dialog diff --git a/.local/share/Anki2/addons21/Background_and_gear/gui/forms/qt6/settings_dialog.py b/.local/share/Anki2/addons21/Background_and_gear/gui/forms/qt6/settings_dialog.py new file mode 100644 index 0000000..6f429e9 --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/gui/forms/qt6/settings_dialog.py @@ -0,0 +1,330 @@ +# Form implementation generated from reading ui file 'designer/settings_dialog.ui' +# +# Created by: PyQt6 UI code generator 6.2.3 +# +# WARNING: Any manual changes made to this file will be lost when pyuic6 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt6 import QtCore, QtGui, QtWidgets + + +class Ui_Dialog(object): + def setupUi(self, Dialog): + Dialog.setObjectName("Dialog") + Dialog.resize(548, 601) + self.gridLayout_6 = QtWidgets.QGridLayout(Dialog) + self.gridLayout_6.setObjectName("gridLayout_6") + self.groupBox_4 = QtWidgets.QGroupBox(Dialog) + self.groupBox_4.setTitle("") + self.groupBox_4.setObjectName("groupBox_4") + self.gridLayout_2 = QtWidgets.QGridLayout(self.groupBox_4) + self.gridLayout_2.setObjectName("gridLayout_2") + self.label_4 = QtWidgets.QLabel(self.groupBox_4) + self.label_4.setObjectName("label_4") + self.gridLayout_2.addWidget(self.label_4, 0, 0, 1, 1) + self.comboBox_attachment = QtWidgets.QComboBox(self.groupBox_4) + self.comboBox_attachment.setObjectName("comboBox_attachment") + self.comboBox_attachment.addItem("") + self.comboBox_attachment.addItem("") + self.gridLayout_2.addWidget(self.comboBox_attachment, 0, 1, 1, 1) + self.label_8 = QtWidgets.QLabel(self.groupBox_4) + self.label_8.setObjectName("label_8") + self.gridLayout_2.addWidget(self.label_8, 0, 2, 1, 1) + self.comboBox_size = QtWidgets.QComboBox(self.groupBox_4) + self.comboBox_size.setObjectName("comboBox_size") + self.comboBox_size.addItem("") + self.comboBox_size.addItem("") + self.gridLayout_2.addWidget(self.comboBox_size, 0, 3, 1, 1) + self.label_7 = QtWidgets.QLabel(self.groupBox_4) + self.label_7.setObjectName("label_7") + self.gridLayout_2.addWidget(self.label_7, 1, 0, 1, 1) + self.comboBox_position = QtWidgets.QComboBox(self.groupBox_4) + self.comboBox_position.setObjectName("comboBox_position") + self.comboBox_position.addItem("") + self.comboBox_position.addItem("") + self.comboBox_position.addItem("") + self.comboBox_position.addItem("") + self.comboBox_position.addItem("") + self.comboBox_position.addItem("") + self.comboBox_position.addItem("") + self.comboBox_position.addItem("") + self.comboBox_position.addItem("") + self.gridLayout_2.addWidget(self.comboBox_position, 1, 1, 1, 1) + self.label_9 = QtWidgets.QLabel(self.groupBox_4) + self.label_9.setObjectName("label_9") + self.gridLayout_2.addWidget(self.label_9, 1, 2, 1, 1) + self.scaleBox = QtWidgets.QDoubleSpinBox(self.groupBox_4) + self.scaleBox.setDecimals(1) + self.scaleBox.setMinimum(0.1) + self.scaleBox.setSingleStep(0.1) + self.scaleBox.setProperty("value", 1.0) + self.scaleBox.setObjectName("scaleBox") + self.gridLayout_2.addWidget(self.scaleBox, 1, 3, 1, 1) + self.gridLayout_6.addWidget(self.groupBox_4, 3, 0, 1, 12) + self.RestoreButton = QtWidgets.QPushButton(Dialog) + self.RestoreButton.setObjectName("RestoreButton") + self.gridLayout_6.addWidget(self.RestoreButton, 8, 8, 1, 3) + self.pushButton_videoTutorial = QtWidgets.QPushButton(Dialog) + self.pushButton_videoTutorial.setObjectName("pushButton_videoTutorial") + self.gridLayout_6.addWidget(self.pushButton_videoTutorial, 8, 0, 1, 8) + self.groupBox_3 = QtWidgets.QGroupBox(Dialog) + self.groupBox_3.setObjectName("groupBox_3") + self.gridLayout_3 = QtWidgets.QGridLayout(self.groupBox_3) + self.gridLayout_3.setObjectName("gridLayout_3") + self.label_5 = QtWidgets.QLabel(self.groupBox_3) + self.label_5.setToolTip("") + self.label_5.setToolTipDuration(-1) + self.label_5.setStatusTip("") + self.label_5.setWhatsThis("") + self.label_5.setObjectName("label_5") + self.gridLayout_3.addWidget(self.label_5, 0, 0, 1, 1) + self.Slider_main = QtWidgets.QSlider(self.groupBox_3) + self.Slider_main.setToolTip("") + self.Slider_main.setToolTipDuration(-1) + self.Slider_main.setWhatsThis("") + self.Slider_main.setMaximum(100) + self.Slider_main.setPageStep(1) + self.Slider_main.setProperty("value", 100) + self.Slider_main.setSliderPosition(100) + self.Slider_main.setTracking(True) + self.Slider_main.setOrientation(QtCore.Qt.Orientation.Horizontal) + self.Slider_main.setTickPosition(QtWidgets.QSlider.TickPosition.NoTicks) + self.Slider_main.setTickInterval(5) + self.Slider_main.setObjectName("Slider_main") + self.gridLayout_3.addWidget(self.Slider_main, 0, 1, 1, 1) + self.label_6 = QtWidgets.QLabel(self.groupBox_3) + self.label_6.setObjectName("label_6") + self.gridLayout_3.addWidget(self.label_6, 1, 0, 1, 1) + self.Slider_review = QtWidgets.QSlider(self.groupBox_3) + self.Slider_review.setMaximum(100) + self.Slider_review.setPageStep(10) + self.Slider_review.setProperty("value", 1) + self.Slider_review.setSliderPosition(1) + self.Slider_review.setOrientation(QtCore.Qt.Orientation.Horizontal) + self.Slider_review.setInvertedAppearance(False) + self.Slider_review.setTickPosition(QtWidgets.QSlider.TickPosition.NoTicks) + self.Slider_review.setObjectName("Slider_review") + self.gridLayout_3.addWidget(self.Slider_review, 1, 1, 1, 1) + self.gridLayout_6.addWidget(self.groupBox_3, 2, 8, 1, 4) + self.groupBox = QtWidgets.QGroupBox(Dialog) + self.groupBox.setWhatsThis("") + self.groupBox.setTitle("") + self.groupBox.setObjectName("groupBox") + self.gridLayout_5 = QtWidgets.QGridLayout(self.groupBox) + self.gridLayout_5.setObjectName("gridLayout_5") + self.toolButton_gear = QtWidgets.QToolButton(self.groupBox) + self.toolButton_gear.setObjectName("toolButton_gear") + self.gridLayout_5.addWidget(self.toolButton_gear, 1, 2, 1, 1) + self.label = QtWidgets.QLabel(self.groupBox) + self.label.setToolTipDuration(10000) + self.label.setObjectName("label") + self.gridLayout_5.addWidget(self.label, 0, 0, 1, 1) + self.pushButton_randomize = QtWidgets.QPushButton(self.groupBox) + self.pushButton_randomize.setObjectName("pushButton_randomize") + self.gridLayout_5.addWidget(self.pushButton_randomize, 2, 1, 1, 1) + self.pushButton_imageFolder = QtWidgets.QPushButton(self.groupBox) + self.pushButton_imageFolder.setObjectName("pushButton_imageFolder") + self.gridLayout_5.addWidget(self.pushButton_imageFolder, 2, 0, 1, 1) + self.lineEdit_background = QtWidgets.QLineEdit(self.groupBox) + self.lineEdit_background.setObjectName("lineEdit_background") + self.gridLayout_5.addWidget(self.lineEdit_background, 0, 1, 1, 1) + self.lineEdit_gear = QtWidgets.QLineEdit(self.groupBox) + self.lineEdit_gear.setObjectName("lineEdit_gear") + self.gridLayout_5.addWidget(self.lineEdit_gear, 1, 1, 1, 1) + self.label_2 = QtWidgets.QLabel(self.groupBox) + self.label_2.setToolTipDuration(10000) + self.label_2.setObjectName("label_2") + self.gridLayout_5.addWidget(self.label_2, 1, 0, 1, 1) + self.toolButton_background = QtWidgets.QToolButton(self.groupBox) + self.toolButton_background.setObjectName("toolButton_background") + self.gridLayout_5.addWidget(self.toolButton_background, 0, 2, 1, 1) + self.gridLayout_6.addWidget(self.groupBox, 1, 0, 1, 12) + self.groupBox_5 = QtWidgets.QGroupBox(Dialog) + self.groupBox_5.setObjectName("groupBox_5") + self.gridLayout = QtWidgets.QGridLayout(self.groupBox_5) + self.gridLayout.setObjectName("gridLayout") + self.label_11 = QtWidgets.QLabel(self.groupBox_5) + self.label_11.setObjectName("label_11") + self.gridLayout.addWidget(self.label_11, 0, 0, 1, 1) + self.lineEdit_color_main = QtWidgets.QLineEdit(self.groupBox_5) + self.lineEdit_color_main.setObjectName("lineEdit_color_main") + self.gridLayout.addWidget(self.lineEdit_color_main, 0, 1, 1, 1) + self.toolButton_color_main = QtWidgets.QToolButton(self.groupBox_5) + self.toolButton_color_main.setObjectName("toolButton_color_main") + self.gridLayout.addWidget(self.toolButton_color_main, 0, 2, 1, 1) + self.label_12 = QtWidgets.QLabel(self.groupBox_5) + self.label_12.setObjectName("label_12") + self.gridLayout.addWidget(self.label_12, 0, 3, 1, 1) + self.lineEdit_color_top = QtWidgets.QLineEdit(self.groupBox_5) + self.lineEdit_color_top.setObjectName("lineEdit_color_top") + self.gridLayout.addWidget(self.lineEdit_color_top, 0, 4, 1, 1) + self.toolButton_color_top = QtWidgets.QToolButton(self.groupBox_5) + self.toolButton_color_top.setObjectName("toolButton_color_top") + self.gridLayout.addWidget(self.toolButton_color_top, 0, 5, 1, 1) + self.label_13 = QtWidgets.QLabel(self.groupBox_5) + self.label_13.setObjectName("label_13") + self.gridLayout.addWidget(self.label_13, 1, 3, 1, 1) + self.lineEdit_color_bottom = QtWidgets.QLineEdit(self.groupBox_5) + self.lineEdit_color_bottom.setObjectName("lineEdit_color_bottom") + self.gridLayout.addWidget(self.lineEdit_color_bottom, 1, 4, 1, 1) + self.toolButton_color_bottom = QtWidgets.QToolButton(self.groupBox_5) + self.toolButton_color_bottom.setObjectName("toolButton_color_bottom") + self.gridLayout.addWidget(self.toolButton_color_bottom, 1, 5, 1, 1) + self.gridLayout_6.addWidget(self.groupBox_5, 4, 0, 1, 12) + spacerItem = QtWidgets.QSpacerItem(20, 5, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Fixed) + self.gridLayout_6.addItem(spacerItem, 7, 0, 1, 1) + self.groupBox_2 = QtWidgets.QGroupBox(Dialog) + self.groupBox_2.setTitle("") + self.groupBox_2.setObjectName("groupBox_2") + self.gridLayout_4 = QtWidgets.QGridLayout(self.groupBox_2) + self.gridLayout_4.setObjectName("gridLayout_4") + self.checkBox_reviewer = QtWidgets.QCheckBox(self.groupBox_2) + self.checkBox_reviewer.setToolTipDuration(10000) + self.checkBox_reviewer.setChecked(True) + self.checkBox_reviewer.setObjectName("checkBox_reviewer") + self.gridLayout_4.addWidget(self.checkBox_reviewer, 0, 0, 1, 1) + self.checkBox_toolbar = QtWidgets.QCheckBox(self.groupBox_2) + self.checkBox_toolbar.setToolTipDuration(10000) + self.checkBox_toolbar.setChecked(True) + self.checkBox_toolbar.setObjectName("checkBox_toolbar") + self.gridLayout_4.addWidget(self.checkBox_toolbar, 1, 0, 1, 1) + self.checkBox_topbottom = QtWidgets.QCheckBox(self.groupBox_2) + self.checkBox_topbottom.setToolTipDuration(10000) + self.checkBox_topbottom.setChecked(True) + self.checkBox_topbottom.setObjectName("checkBox_topbottom") + self.gridLayout_4.addWidget(self.checkBox_topbottom, 2, 0, 1, 1) + self.gridLayout_6.addWidget(self.groupBox_2, 2, 0, 1, 8) + self.OkButton = QtWidgets.QPushButton(Dialog) + self.OkButton.setObjectName("OkButton") + self.gridLayout_6.addWidget(self.OkButton, 8, 11, 1, 1) + spacerItem1 = QtWidgets.QSpacerItem(20, 5, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Fixed) + self.gridLayout_6.addItem(spacerItem1, 5, 0, 1, 1) + self.horizontalLayout = QtWidgets.QHBoxLayout() + self.horizontalLayout.setSizeConstraint(QtWidgets.QLayout.SizeConstraint.SetNoConstraint) + self.horizontalLayout.setObjectName("horizontalLayout") + self.toolButton_palace = QtWidgets.QToolButton(Dialog) + icon = QtGui.QIcon() + icon.addPixmap(QtGui.QPixmap("CustomBackground:AnkiPalace_no_text.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off) + self.toolButton_palace.setIcon(icon) + self.toolButton_palace.setIconSize(QtCore.QSize(40, 40)) + self.toolButton_palace.setObjectName("toolButton_palace") + self.horizontalLayout.addWidget(self.toolButton_palace) + self.label_3 = QtWidgets.QLabel(Dialog) + self.label_3.setMinimumSize(QtCore.QSize(400, 0)) + self.label_3.setWordWrap(True) + self.label_3.setObjectName("label_3") + self.horizontalLayout.addWidget(self.label_3) + self.gridLayout_6.addLayout(self.horizontalLayout, 6, 0, 1, 12) + self.horizontalLayout_2 = QtWidgets.QHBoxLayout() + self.horizontalLayout_2.setSizeConstraint(QtWidgets.QLayout.SizeConstraint.SetNoConstraint) + self.horizontalLayout_2.setSpacing(6) + self.horizontalLayout_2.setObjectName("horizontalLayout_2") + self.toolButton_website = QtWidgets.QToolButton(Dialog) + self.toolButton_website.setMaximumSize(QtCore.QSize(31, 31)) + icon1 = QtGui.QIcon() + icon1.addPixmap(QtGui.QPixmap("CustomBackground:AnKingSmall.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off) + self.toolButton_website.setIcon(icon1) + self.toolButton_website.setIconSize(QtCore.QSize(31, 31)) + self.toolButton_website.setObjectName("toolButton_website") + self.horizontalLayout_2.addWidget(self.toolButton_website) + self.toolButton_youtube = QtWidgets.QToolButton(Dialog) + self.toolButton_youtube.setMaximumSize(QtCore.QSize(31, 31)) + icon2 = QtGui.QIcon() + icon2.addPixmap(QtGui.QPixmap("CustomBackground:YouTube.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off) + self.toolButton_youtube.setIcon(icon2) + self.toolButton_youtube.setIconSize(QtCore.QSize(31, 31)) + self.toolButton_youtube.setObjectName("toolButton_youtube") + self.horizontalLayout_2.addWidget(self.toolButton_youtube) + self.toolButton_patreon = QtWidgets.QToolButton(Dialog) + self.toolButton_patreon.setMaximumSize(QtCore.QSize(171, 26)) + icon3 = QtGui.QIcon() + icon3.addPixmap(QtGui.QPixmap("CustomBackground:Patreon.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off) + self.toolButton_patreon.setIcon(icon3) + self.toolButton_patreon.setIconSize(QtCore.QSize(200, 31)) + self.toolButton_patreon.setObjectName("toolButton_patreon") + self.horizontalLayout_2.addWidget(self.toolButton_patreon, 0, QtCore.Qt.AlignmentFlag.AlignHCenter) + self.toolButton_instagram = QtWidgets.QToolButton(Dialog) + self.toolButton_instagram.setMaximumSize(QtCore.QSize(31, 31)) + icon4 = QtGui.QIcon() + icon4.addPixmap(QtGui.QPixmap("CustomBackground:Instagram.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off) + self.toolButton_instagram.setIcon(icon4) + self.toolButton_instagram.setIconSize(QtCore.QSize(31, 31)) + self.toolButton_instagram.setObjectName("toolButton_instagram") + self.horizontalLayout_2.addWidget(self.toolButton_instagram) + self.toolButton_facebook = QtWidgets.QToolButton(Dialog) + self.toolButton_facebook.setMaximumSize(QtCore.QSize(31, 31)) + icon5 = QtGui.QIcon() + icon5.addPixmap(QtGui.QPixmap("CustomBackground:Facebook.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off) + self.toolButton_facebook.setIcon(icon5) + self.toolButton_facebook.setIconSize(QtCore.QSize(31, 31)) + self.toolButton_facebook.setObjectName("toolButton_facebook") + self.horizontalLayout_2.addWidget(self.toolButton_facebook) + self.gridLayout_6.addLayout(self.horizontalLayout_2, 0, 2, 1, 9) + self.groupBox_5.raise_() + self.groupBox_4.raise_() + self.groupBox_3.raise_() + self.groupBox_2.raise_() + self.groupBox.raise_() + self.pushButton_videoTutorial.raise_() + self.RestoreButton.raise_() + self.OkButton.raise_() + + self.retranslateUi(Dialog) + QtCore.QMetaObject.connectSlotsByName(Dialog) + + def retranslateUi(self, Dialog): + _translate = QtCore.QCoreApplication.translate + Dialog.setWindowTitle(_translate("Dialog", "Custom Background Settings")) + self.label_4.setText(_translate("Dialog", "Attachment:")) + self.comboBox_attachment.setItemText(0, _translate("Dialog", "fixed")) + self.comboBox_attachment.setItemText(1, _translate("Dialog", "scroll")) + self.label_8.setText(_translate("Dialog", "Size:")) + self.comboBox_size.setItemText(0, _translate("Dialog", "cover")) + self.comboBox_size.setItemText(1, _translate("Dialog", "contain")) + self.label_7.setText(_translate("Dialog", "Position:")) + self.comboBox_position.setCurrentText(_translate("Dialog", "left top")) + self.comboBox_position.setItemText(0, _translate("Dialog", "left top")) + self.comboBox_position.setItemText(1, _translate("Dialog", "center top")) + self.comboBox_position.setItemText(2, _translate("Dialog", "right top")) + self.comboBox_position.setItemText(3, _translate("Dialog", "right")) + self.comboBox_position.setItemText(4, _translate("Dialog", "left")) + self.comboBox_position.setItemText(5, _translate("Dialog", "center")) + self.comboBox_position.setItemText(6, _translate("Dialog", "left bottom")) + self.comboBox_position.setItemText(7, _translate("Dialog", "center bottom")) + self.comboBox_position.setItemText(8, _translate("Dialog", "right bottom")) + self.label_9.setText(_translate("Dialog", "Scale:")) + self.RestoreButton.setText(_translate("Dialog", "Restore Default")) + self.pushButton_videoTutorial.setText(_translate("Dialog", "Video Tutorial")) + self.groupBox_3.setTitle(_translate("Dialog", "Background opacity")) + self.label_5.setText(_translate("Dialog", "Main")) + self.label_6.setText(_translate("Dialog", "Review")) + self.toolButton_gear.setText(_translate("Dialog", "...")) + self.label.setToolTip(_translate("Dialog", "<html><head/><body><p>Name of the background image file.</p><p>"Random" will shuffle through defaults</p></body></html>")) + self.label.setText(_translate("Dialog", "Image name for background:")) + self.pushButton_randomize.setText(_translate("Dialog", "Random Images")) + self.pushButton_imageFolder.setText(_translate("Dialog", "Open Image Folders")) + self.label_2.setToolTip(_translate("Dialog", "<html><head/><body><p>Name of the file to replace the gear icon. </p><p>Anki default is gears.svg</p><p>"Random" will shuffle through defaults</p></body></html>")) + self.label_2.setText(_translate("Dialog", "Image name for gear icon:")) + self.toolButton_background.setText(_translate("Dialog", "...")) + self.groupBox_5.setTitle(_translate("Dialog", "Background color")) + self.label_11.setText(_translate("Dialog", "Main:")) + self.toolButton_color_main.setText(_translate("Dialog", "...")) + self.label_12.setText(_translate("Dialog", "Top toolbar:")) + self.toolButton_color_top.setText(_translate("Dialog", "...")) + self.label_13.setText(_translate("Dialog", "Bottom toolbar:")) + self.toolButton_color_bottom.setText(_translate("Dialog", "...")) + self.checkBox_reviewer.setToolTip(_translate("Dialog", "<html><head/><body><p>Show the background image in the reviewer screen</p></body></html>")) + self.checkBox_reviewer.setText(_translate("Dialog", "Show in reviewer")) + self.checkBox_toolbar.setToolTip(_translate("Dialog", "<html><head/><body><p>Show the background image in the top and bottom toolbars in addition to the main screen</p></body></html>")) + self.checkBox_toolbar.setText(_translate("Dialog", "Show in toolbar")) + self.checkBox_topbottom.setToolTip(_translate("Dialog", "<html><head/><body><p>Set the background position of the toolbars to top and bottom (if the main background position is set to center, this will look cleaner for most images)<br/></p></body></html>")) + self.checkBox_topbottom.setText(_translate("Dialog", "Toolbar top/bottom")) + self.OkButton.setText(_translate("Dialog", "OK")) + self.toolButton_palace.setText(_translate("Dialog", "...")) + self.label_3.setText(_translate("Dialog", "Interested in learning how to use Anki effectively? Check out AnkiPalace, a comprehensive series of lessons and video tutorials on Anki designed by the AnKing team.")) + self.toolButton_website.setText(_translate("Dialog", "...")) + self.toolButton_youtube.setText(_translate("Dialog", "...")) + self.toolButton_patreon.setText(_translate("Dialog", "...")) + self.toolButton_instagram.setText(_translate("Dialog", "...")) + self.toolButton_facebook.setText(_translate("Dialog", "...")) diff --git a/.local/share/Anki2/addons21/Background_and_gear/gui/resources/__init__.py b/.local/share/Anki2/addons21/Background_and_gear/gui/resources/__init__.py new file mode 100644 index 0000000..d0c427c --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/gui/resources/__init__.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# +# Custom background and gear icon Add-on for Anki +# Copyright (C) 2022 +# +# This file was automatically generated by Anki Add-on Builder v1.0.0-dev.2 +# It is subject to the same licensing terms as the rest of the program +# (see the LICENSE file which accompanies this program). +# +# WARNING! All changes made in this file will be lost! + +""" +Initializes generated Qt forms/resources +""" + +from pathlib import Path +from aqt.qt import QDir + +def initialize_qt_resources(): + QDir.addSearchPath("CustomBackground", str(Path(__file__).parent / "CustomBackground")) + +initialize_qt_resources() diff --git a/.local/share/Anki2/addons21/Background_and_gear/gui_updatemanager.py b/.local/share/Anki2/addons21/Background_and_gear/gui_updatemanager.py new file mode 100644 index 0000000..1b55e56 --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/gui_updatemanager.py @@ -0,0 +1,432 @@ +# -*- coding: utf-8 -*- +# Copyright: Lovac42 (much of this card heavily borrowed from the Dancing Baloney Add-on) +# Copyright: The AnKing +# Also thanks to ijgnord who helped on this +# Support: +# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html + + +import os + +from anki import version as anki_version # type: ignore +from aqt import mw +from aqt.qt import * +from aqt.utils import getFile, openFolder, openLink + +from .config import addon_path, getDefaultConfig, getUserOption, writeConfig +from .gui.forms import settings_dialog + +conf = getUserOption() + +USER_FILES_FOLDER = os.path.join(addon_path, "user_files") +RE_BG_IMG_EXT = "*.gif *.png *.apng *.jpg *.jpeg *.svg *.ico *.bmp" +ANKI_VERSION_TUPLE = tuple(int(i) for i in anki_version.split(".")) + + +def getMenu(parent, menuName): + menu = None + for a in parent.form.menubar.actions(): + if menuName == a.text(): + menu = a.menu() + break + if not menu: + menu = parent.form.menubar.addMenu(menuName) + return menu + + +class SettingsDialog(QDialog): + + def __init__(self, parent): + super().__init__(mw) + + mw.setupDialogGC(self) + self.mw = mw + self.parent = parent + self.setupDialog() + self.loadConfigData() + self.setupConnections() + + self.exec() + + def reject(self): + self.accept() + self.close() + + def accept(self): + QDialog.accept(self) + self.close() + + def setupDialog(self): + self.form = settings_dialog.Ui_Dialog() + self.form.setupUi(self) + + def setupConnections(self): + f = self.form + + # PushButtons ------------- + f.OkButton.clicked.connect(self.accept) + f.RestoreButton.clicked.connect(self.resetConfig) + + f.pushButton_randomize.clicked.connect(self.random) + f.pushButton_imageFolder.clicked.connect(lambda: openFolder(USER_FILES_FOLDER)) + f.pushButton_videoTutorial.clicked.connect(lambda _: self.openWeb("video")) + + f.toolButton_website.clicked.connect(lambda _: self.openWeb("anking")) + f.toolButton_youtube.clicked.connect(lambda _: self.openWeb("youtube")) + f.toolButton_patreon.clicked.connect(lambda _: self.openWeb("patreon")) + f.toolButton_instagram.clicked.connect(lambda _: self.openWeb("instagram")) + f.toolButton_facebook.clicked.connect(lambda _: self.openWeb("facebook")) + f.toolButton_palace.clicked.connect(lambda _: self.openWeb("palace")) + + # Color Pickers ------------- + controller = { + f.toolButton_color_main: (f.lineEdit_color_main,), + f.toolButton_color_top: (f.lineEdit_color_top,), + f.toolButton_color_bottom: (f.lineEdit_color_bottom,), + } + for btn, args in controller.items(): + btn.clicked.connect(lambda a="a", args=args: self.getColors(a, *args)) + + # File Buttons ----------------------- + controller = { + # Image Buttons ----------------------- + f.toolButton_background: (f.lineEdit_background,), + } + for btn, args in controller.items(): + # 'a' is used to get around an issue + # with pything binding + btn.clicked.connect(lambda a="a", args=args: self._getFile(a, *args)) + # File Buttons ----------------------- + controller = { + # Image Buttons ----------------------- + f.toolButton_gear: (f.lineEdit_gear,), + } + for btn, args in controller.items(): + # 'a' is used to get around an issue + # with pything binding + btn.clicked.connect(lambda a="a", args=args: self._getGearFile(a, *args)) + + # Checkboxes ---------------- + controller = { + f.checkBox_reviewer: ("Reviewer image",), + f.checkBox_toolbar: ("Toolbar image",), + f.checkBox_topbottom: ("Toolbar top/bottom",), + } + for cb, args in controller.items(): + cb.stateChanged.connect( + lambda cb=cb, args=args: self._updateCheckbox(cb, *args) + ) + + # Comboboxes --------------- + controller = { + f.comboBox_attachment: ("background-attachment",), + f.comboBox_position: ("background-position",), + f.comboBox_size: ("background-size",), + } + for cb, args in controller.items(): + t = cb.currentText() + cb.currentTextChanged.connect( + lambda t=t, args=args: self._updateComboBox(t, *args) + ) + + # Sliders -------------- + controller = { + f.Slider_main: ("background opacity main",), + f.Slider_review: ("background opacity review",), + } + for slider, args in controller.items(): + s = slider.value() + slider.valueChanged.connect( + lambda s=s, args=args: self._updateSliderLabel(s, *args) + ) + + # QDoubleSpinBox ------------ + f.scaleBox.valueChanged.connect(self._updateSpinBox) + + # LineEdits ------------- + a = f.lineEdit_background + t = a.text() + a.textChanged.connect( + lambda t=a.text(): self._updateLineEdit(t, "Image name for background") + ) + + a = f.lineEdit_gear + t = a.text() + a.textChanged.connect( + lambda t=a.text(): self._updateLineEdit(t, "Image name for gear") + ) + + a = f.lineEdit_color_main + t = a.text() + a.textChanged.connect( + lambda t=a.text(): self._updateLineEdit(t, "background-color main") + ) + + a = f.lineEdit_color_top + t = a.text() + a.textChanged.connect( + lambda t=a.text(): self._updateLineEdit(t, "background-color top") + ) + + a = f.lineEdit_color_bottom + t = a.text() + a.textChanged.connect( + lambda t=a.text(): self._updateLineEdit(t, "background-color bottom") + ) + + def loadConfigData(self): + f = self.form + + # Checkboxes ------------- + c = conf["Reviewer image"] + if f.checkBox_reviewer.isChecked() != c: + f.checkBox_reviewer.click() + + c = conf["Toolbar image"] + if f.checkBox_toolbar.isChecked() != c: + f.checkBox_toolbar.click() + + c = conf["Toolbar top/bottom"] + if f.checkBox_topbottom.isChecked() != c: + f.checkBox_topbottom.click() + + # Comboboxes ------------- + c = conf["background-attachment"] + f.comboBox_attachment.setCurrentText(c) + + c = conf["background-position"] + f.comboBox_position.setCurrentText(c) + + c = conf["background-size"] + f.comboBox_size.setCurrentText(c) + + # Sliders -------------- + c = float(conf["background opacity main"]) + f.Slider_main.setValue(int(c * 100)) + + c = float(conf["background opacity review"]) + f.Slider_review.setValue(int(c * 100)) + + # QDoubleSpinBox ------------------ + c = float(conf["background scale"]) + f.scaleBox.setValue(c) + + # LineEdits ------------- + t = conf["Image name for background"] + f.lineEdit_background.setText(t) + + t = conf["Image name for gear"] + f.lineEdit_gear.setText(t) + + t = conf["background-color main"] + f.lineEdit_color_main.setText(t) + + t = conf["background-color top"] + f.lineEdit_color_top.setText(t) + + t = conf["background-color bottom"] + f.lineEdit_color_bottom.setText(t) + + def _getFile(self, pad, lineEditor, ext=RE_BG_IMG_EXT): + def setWallpaper(path): + f = path.split("user_files/background/")[-1] + lineEditor.setText(f) + + f = getFile( + mw, + "Wallpaper", + cb=setWallpaper, + filter=ext, + dir=f"{addon_path}/user_files/background", + ) + + def _getGearFile(self, pad, lineEditor, ext=RE_BG_IMG_EXT): + def setWallpaper(path): + f = path.split("user_files/gear/")[-1] + lineEditor.setText(f) + + f = getFile( + mw, + "Gear icon", + cb=setWallpaper, + filter=ext, + dir=f"{addon_path}/user_files/gear", + ) + + def _updateCheckbox(self, cb, key): + n = -1 if cb == 2 else 1 + v = True if n == -1 else False + conf[key] = v + writeConfig(conf) + self._refresh() + + def _updateComboBox(self, text, key): + conf[key] = text + writeConfig(conf) + self._refresh() + + def _updateSliderLabel(self, val, key): + conf[key] = str(round(val / 100, 2)) + writeConfig(conf) + self._refresh() + + def _updateSpinBox(self): + f = self.form + n = round(f.scaleBox.value(), 2) + conf["background scale"] = str(n) + writeConfig(conf) + self._refresh() + + def _updateLineEdit(self, text, key): + conf[key] = text + writeConfig(conf) + self._refresh() + + def getColors(self, pad, lineEditor): + qcolor = QColorDialog.getColor() + if not qcolor.isValid(): + return + color = qcolor.name() + lineEditor.setText(color) + + def openWeb(self, site): + if site == "anking": + openLink("https://www.ankingmed.com") + elif site == "youtube": + openLink("https://www.youtube.com/theanking") + elif site == "patreon": + openLink("https://www.patreon.com/ankingmed") + elif site == "instagram": + openLink("https://instagram.com/ankingmed") + elif site == "facebook": + openLink("https://facebook.com/ankingmed") + elif site == "video": + openLink("https://youtu.be/5XAq0KpU3Jc") + elif site == "palace": + openLink( + "https://courses.ankipalace.com/?utm_source=anking_bg_add-on&utm_medium=anki_add-on&utm_campaign=mastery_course" + ) + + def random(self): + f = self.form + f.lineEdit_background.setText("random") + f.lineEdit_gear.setText("random") + self._refresh() + + def resetConfig(self): + global conf + conf = getDefaultConfig() + writeConfig(conf) + self._refresh() + self.close() + SettingsDialogExecute() + + def _refresh(self): + if ANKI_VERSION_TUPLE < (2, 1, 27): + mw.reset(True) + elif ANKI_VERSION_TUPLE < (2, 1, 45): + mw.reset(True) + mw.toolbar.draw() + else: + # this triggers the css update hooked to state_did_change in __init__.py + mw.moveToState("deckBrowser") + + # works around stylesheets getting cached + cmd = """ + (function(){ + var links = document.getElementsByTagName("link"); + for (var cl in links) + { + var link = links[cl]; + if (link.rel === "stylesheet") + link.href += "?v=" + Date.now().toString() + } + })() + """ + mw.deckBrowser.web.eval(cmd) + mw.toolbar.web.eval(cmd) + mw.bottomWeb.eval(cmd) + + +def SettingsDialogExecute(): + SettingsDialog(mw) + + +mw.addonManager.setConfigAction(__name__, SettingsDialogExecute) + + +######################################## + + +def create_get_help_submenu(parent: QMenu) -> QMenu: + submenu_name = "Get Anki Help" + menu_options = [ + ( + "Online Mastery Course", + "https://courses.ankipalace.com/?utm_source=anking_bg_add-on&utm_medium=anki_add-on&utm_campaign=mastery_course", + ), + ("Daily Q and A Support", "https://www.ankipalace.com/memberships"), + ("1-on-1 Tutoring", "https://www.ankipalace.com/tutoring"), + ] + submenu = QMenu(submenu_name, parent) + for name, url in menu_options: + act = QAction(name, mw) + act.triggered.connect(lambda _, u=url: openLink(u)) # type: ignore + submenu.addAction(act) + return submenu + + +def maybe_add_get_help_submenu(menu: QMenu) -> None: + """Adds 'Get Anki Help' submenu in 'Anking' menu if needed. + + The submenu is added if: + - The submenu does not exist in menu + - The submenu is an outdated version - existing is deleted + + With versioning and anking_get_help property, + future version can rename, hide, or change contents in the submenu + """ + submenu_property = "anking_get_help" + submenu_ver = 2 + for act in menu.actions(): + if act.property(submenu_property) or act.text() == "Get Anki Help": + ver = act.property("version") + if ver and ver >= submenu_ver: + return + submenu = create_get_help_submenu(menu) + menu.insertMenu(act, submenu) + menu.removeAction(act) + new_act = submenu.menuAction() + new_act.setProperty(submenu_property, True) + new_act.setProperty("version", submenu_ver) + return + else: + submenu = create_get_help_submenu(menu) + menu.addMenu(submenu) + new_act = submenu.menuAction() + new_act.setProperty(submenu_property, True) + new_act.setProperty("version", submenu_ver) + + +def get_anking_menu() -> QMenu: + """Return AnKing menu. If it doesn't exist, create one. Make sure its submenus are up to date.""" + menu_name = "&AnKing" + menubar = mw.form.menubar + for a in menubar.actions(): + if menu_name == a.text(): + menu = a.menu() + break + else: + menu = menubar.addMenu(menu_name) + maybe_add_get_help_submenu(menu) + return menu + + +######################################## + + +def setupMenu(): + menu = get_anking_menu() + a = QAction("Custom Background and Gear Icon", mw) + a.triggered.connect(SettingsDialogExecute) + menu.addAction(a) diff --git a/.local/share/Anki2/addons21/Background_and_gear/manifest.json b/.local/share/Anki2/addons21/Background_and_gear/manifest.json new file mode 100644 index 0000000..deaf3bd --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/manifest.json @@ -0,0 +1,12 @@ +{ + "name": "Custom Background and gear icon", + "package": "custom_background_and_gear_icon", + "ankiweb_id": "", + "author": "", + "version": "3.4", + "homepage": "", + "conflicts": [ + "custom_background_and_gear_icon" + ], + "mod": 1649519880 +} diff --git a/.local/share/Anki2/addons21/Background_and_gear/meta.json b/.local/share/Anki2/addons21/Background_and_gear/meta.json new file mode 100644 index 0000000..ac8810e --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/meta.json @@ -0,0 +1 @@ +{"name": "Custom Background Image and Gear Icon", "mod": 1649604604, "homepage": "", "conflicts": ["custom_background_and_gear_icon"], "min_point_version": 41, "max_point_version": 50, "branch_index": 1, "disabled": false, "config": {"Image name for background": "1721729412.jpg", "Image name for gear": "Bullseye.png", "Keyboard Shortcut": "Ctrl+Shift+b", "Reviewer image": true, "Toolbar image": true, "Toolbar top/bottom": true, "background-attachment": "fixed", "background-color bottom": "", "background-color main": "", "background-color top": "", "background opacity main": "1", "background opacity review": "0.36", "background-position": "center", "background scale": "1", "background-size": "cover"}}
\ No newline at end of file diff --git a/.local/share/Anki2/addons21/Background_and_gear/sources/css/22/Other css files/editor.css b/.local/share/Anki2/addons21/Background_and_gear/sources/css/22/Other css files/editor.css new file mode 100644 index 0000000..8c79f9d --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/sources/css/22/Other css files/editor.css @@ -0,0 +1,96 @@ +/* night-mode-specific colours */ +/* Copyright: Ankitects Pty Ltd and contributors + * License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */ +.field { + border: 1px solid #aaa; + background: white; + color: black; + padding: 5px; + overflow-wrap: break-word; +} + +.clearfix:after { + content: ""; + display: table; + clear: both; +} + +.fname { + vertical-align: middle; + padding: 0; +} + +img { + max-width: 90%; +} + +body { + margin: 0; +} + +#topbutsOuter { + position: fixed; + top: 0; + padding: 2px; + left: 0; + right: 0; + z-index: 1; +} + +.topbut { + width: 16px; + height: 16px; + margin-top: 4px; +} + +.rainbow { + background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #f77), color-stop(50%, #7f7), color-stop(100%, #77f)); +} + +button.linkb { + -webkit-appearance: none; + border: 0; + box-shadow: none; + padding: 0px 2px; + background: transparent; +} + +.linkb:disabled { + opacity: 0.3; + cursor: not-allowed; +} + +.highlighted { + border-bottom: 3px solid #000; +} + +#fields { + margin-top: 35px; +} + +.dupe { + background: #aa5555; +} + +.nightMode .field { + border-color: #777; + background: #3a3a3a; + color: white; +} +.nightMode button.linkb > img { + filter: invert(180); +} +.nightMode .dupe { + background: #aa5555; +} +.nightMode #dupes a { + color: #77ccff; +} + +.drawing { + zoom: 50%; +} + +.nightMode img.drawing { + filter: invert() hue-rotate(180deg); +} diff --git a/.local/share/Anki2/addons21/Background_and_gear/sources/css/22/Other css files/webview.css b/.local/share/Anki2/addons21/Background_and_gear/sources/css/22/Other css files/webview.css new file mode 100644 index 0000000..3144057 --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/sources/css/22/Other css files/webview.css @@ -0,0 +1,102 @@ +/* night-mode-specific colours */ +.isWin button { + font-size: 12px; +} + +.isMac button { + font-size: 13px; +} + +.isLin button { + font-size: 14px; + -webkit-appearance: none; + border-radius: 3px; + padding: 5px; + border: 1px solid #aaa; +} + +.nightMode button { + -webkit-appearance: none; + color: white; + /* match the fusion button gradient */ + background: linear-gradient(0deg, #555555 0%, #656565 100%); + box-shadow: 0 0 3px #222222; + border: 1px solid #646464; + border-radius: 2px; + padding: 10px; + padding-top: 3px; + padding-bottom: 3px; +} +.nightMode button:hover { + background: #656565; +} + +/* imitate standard macOS dark mode buttons */ +.isMac.nightMode.macos-dark-mode button:not(.linkb) { + background: #656565; + box-shadow: 0 1px 2px #222222; + border-top-color: #848484; + border-top-width: 0.5px; + border-bottom: 0; + border-left: 0; + border-right: 0; + padding-top: 2px; + padding-bottom: 2px; + padding-left: 15px; + padding-right: 15px; + color: #e5e5e5; +} + +/* Copyright: Ankitects Pty Ltd and contributors + * License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */ +body { + margin: 2em; + color: black; +} + +h1 { + margin-bottom: 0.2em; +} + +a { + color: #00a; +} + +body.nightMode { + color: white; + background: #2f2f31; +} + +body.nightMode::-webkit-scrollbar { + background: #2f2f31; +} + +body.nightMode::-webkit-scrollbar:horizontal { + height: 12px; +} + +body.nightMode::-webkit-scrollbar:vertical { + width: 12px; +} + +body.nightMode::-webkit-scrollbar-thumb { + background: #656565; + border-radius: 8px; +} + +body.nightMode::-webkit-scrollbar-thumb:horizontal { + min-width: 50px; +} + +body.nightMode::-webkit-scrollbar-thumb:vertical { + min-height: 50px; +} + +.nightMode a { + color: #77ccff; +} + +body { + overscroll-behavior: none; +} +/*AnKing edits*/
\ No newline at end of file diff --git a/.local/share/Anki2/addons21/Background_and_gear/sources/css/22/deckbrowser.css b/.local/share/Anki2/addons21/Background_and_gear/sources/css/22/deckbrowser.css new file mode 100644 index 0000000..663b83a --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/sources/css/22/deckbrowser.css @@ -0,0 +1,127 @@ +/* night-mode-specific colours */ +.review-count { + color: #0a0; +} + +.new-count { + color: #00a; +} + +.learn-count { + color: #C35617; +} + +.zero-count { + color: #ddd; +} + +.nightMode .review-count { + color: #5CcC00; +} +.nightMode .new-count { + color: #77ccff; +} +.nightMode .learn-count { + color: #FF935B; +} +.nightMode .zero-count { + color: #444; +} + +/* Copyright: Ankitects Pty Ltd and contributors + * AnKingMed + * License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */ +a.deck { + color: black; + text-decoration: none; + min-width: 5em; + display: inline-block; +} + +a.deck:hover { + text-decoration: underline; +} + +tr.deck td { + border-bottom: 1px solid #e7e7e7; +} + +tr.top-level-drag-row td { + border-bottom: 1px solid transparent; +} + +td { + white-space: nowrap; +} + +tr.drag-hover td { + border-bottom: 1px solid #aaa; +} + +body { + margin: 1em; + -webkit-user-select: none; +} + +body.nightMode { + margin: 1em; + -webkit-user-select: none; +} + +.current { + background-color: rgba(231, 231, 231, 0.3)/* #e7e7e7; */ +} + +.decktd { + min-width: 15em; +} + +.count { + min-width: 4em; + text-align: right; +} + +.optscol { + width: 2em; +} + +.collapse { + color: black; + text-decoration: none; + display: inline-block; + width: 1em; +} + +.filtered { + color: #00a !important; +} + +.gears { + width: 1em; + height: 1em; + opacity: 0.5; + padding-top: 0.2em; +} + +.nightMode a.deck { + color: white; +} +.nightMode tr.deck td { + border-bottom-color: #29292B; +} +.nightMode tr.drag-hover td { + border-bottom-color: #777; +} +.nightMode .current { + background-color: rgba(41, 41, 43, 0.3); /* #29292B; */ +} +.nightMode .collapse { + color: white; +} +.nightMode .gears { + filter: invert(180); +} +.nightMode .filtered { + color: #77ccff !important; +} +/*AnKing edits*/
\ No newline at end of file diff --git a/.local/share/Anki2/addons21/Background_and_gear/sources/css/22/overview.css b/.local/share/Anki2/addons21/Background_and_gear/sources/css/22/overview.css new file mode 100644 index 0000000..6ab0431 --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/sources/css/22/overview.css @@ -0,0 +1,67 @@ +/* night-mode-specific colours */ +.review-count { + color: #0a0; +} + +.new-count { + color: #00a; +} + +.learn-count { + color: #C35617; +} + +.zero-count { + color: #ddd; +} + +.nightMode .review-count { + color: #5CcC00; +} +.nightMode .new-count { + color: #77ccff; +} +.nightMode .learn-count { + color: #FF935B; +} +.nightMode .zero-count { + color: #444; +} + +/* Copyright: Ankitects Pty Ltd and contributors + * License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */ +.smallLink { + font-size: 10px; +} + +h3 { + margin-bottom: 0; +} + +.descfont { + padding: 1em; + color: #333; +} + +.description { + white-space: pre-wrap; +} + +#fulldesc { + display: none; +} + +.descmid { + width: 70%; + margin: 0 auto 0; + text-align: left; +} + +.dyn { + text-align: center; +} + +.nightMode .descfont { + color: #ccc; +} +/*AnKing edits*/ diff --git a/.local/share/Anki2/addons21/Background_and_gear/sources/css/22/reviewer-bottom.css b/.local/share/Anki2/addons21/Background_and_gear/sources/css/22/reviewer-bottom.css new file mode 100644 index 0000000..eab7d69 --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/sources/css/22/reviewer-bottom.css @@ -0,0 +1,92 @@ +/* night-mode-specific colours */ +.review-count { + color: #0a0; +} + +.new-count { + color: #00a; +} + +.learn-count { + color: #C35617; +} + +.zero-count { + color: #ddd; +} + +.nightMode .review-count { + color: #5CcC00; +} +.nightMode .new-count { + color: #77ccff; +} +.nightMode .learn-count { + color: #FF935B; +} +.nightMode .zero-count { + color: #444; +} + +/* Copyright: Ankitects Pty Ltd and contributors + * License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */ +body { + margin: 0; + padding: 0; +} + +button { + min-width: 60px; + white-space: nowrap; + margin: 0.5em; +} + +.hitem { + margin-top: 2px; +} + +.stat { + padding-top: 5px; +} + +.stat2 { + padding-top: 3px; + font-weight: normal; +} + +.stattxt { + padding-left: 5px; + padding-right: 5px; + white-space: nowrap; +} + +#ansbut { + margin-bottom: 1em; +} + +.nobold { + font-weight: normal; + display: inline-block; + padding-top: 4px; +} + +.spacer { + height: 18px; +} + +.spacer2 { + height: 16px; +} + +#outer { + border-top: 1px solid #aaa; +} + +#innertable { + padding: 3px; +} + +.nightMode #outer { + border-top-color: #29292B; +} +/*AnKing edits*/
\ No newline at end of file diff --git a/.local/share/Anki2/addons21/Background_and_gear/sources/css/22/reviewer.css b/.local/share/Anki2/addons21/Background_and_gear/sources/css/22/reviewer.css new file mode 100644 index 0000000..daa7169 --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/sources/css/22/reviewer.css @@ -0,0 +1,89 @@ +/* night-mode-specific colours */ +/* Copyright: Ankitects Pty Ltd and contributors + * License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */ +hr { + background-color: #ccc; +} + +body { + margin: 20px; + overflow-wrap: break-word; +} + +body.nightMode { + background-color: #2f2f31; + color: white; +} + +img { + max-width: 95%; + max-height: 95vh; +} + +#_flag { + position: fixed; + right: 10px; + top: 0; + font-size: 30px; + display: none; + -webkit-text-stroke-width: 1px; + -webkit-text-stroke-color: black; +} + +#_mark { + position: fixed; + left: 10px; + top: 0; + font-size: 30px; + color: yellow; + display: none; + -webkit-text-stroke-width: 1px; + -webkit-text-stroke-color: black; +} + +#typeans { + width: 100%; +} + +.typeGood { + background: #0f0; +} + +.typeBad { + background: #f00; +} + +.typeMissed { + background: #ccc; +} + +.replay-button { + text-decoration: none; + display: inline-flex; + vertical-align: middle; + margin: 3px; +} +.replay-button svg { + width: 40px; + height: 40px; +} +.replay-button svg circle { + fill: #fff; + stroke: #414141; +} +.replay-button svg path { + fill: #414141; +} + +.nightMode .latex { + filter: invert(100%); +} + +.drawing { + zoom: 50%; +} + +.nightMode img.drawing { + filter: invert(1) hue-rotate(180deg); +} +/*AnKing edits*/ diff --git a/.local/share/Anki2/addons21/Background_and_gear/sources/css/22/toolbar-bottom.css b/.local/share/Anki2/addons21/Background_and_gear/sources/css/22/toolbar-bottom.css new file mode 100644 index 0000000..e0fb5e1 --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/sources/css/22/toolbar-bottom.css @@ -0,0 +1,9 @@ +/* Copyright: Ankitects Pty Ltd and contributors + * License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */ +#header { + border-bottom: 0; + margin-bottom: 6px; + margin-top: 0; + padding: 9px; +} +/*AnKing edits*/
\ No newline at end of file diff --git a/.local/share/Anki2/addons21/Background_and_gear/sources/css/22/toolbar.css b/.local/share/Anki2/addons21/Background_and_gear/sources/css/22/toolbar.css new file mode 100644 index 0000000..dfc0cc2 --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/sources/css/22/toolbar.css @@ -0,0 +1,74 @@ +/* night-mode-specific colours */ +/* Copyright: Ankitects Pty Ltd and contributors + * License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */ +#header { + padding: 3px; + font-weight: bold; + border-bottom: 1px solid #aaa; +} + +.tdcenter { + white-space: nowrap; +} + +body { + margin: 0; + padding: 0; + -webkit-user-select: none; + overflow: hidden; +} + +* { + -webkit-user-drag: none; +} + +.hitem { + padding-right: 12px; + padding-left: 12px; + text-decoration: none; + color: black; +} + +.hitem:hover { + text-decoration: underline; +} + +.hitem:focus { + outline: 0; +} + +.nightMode .hitem { + color: white; +} +.nightMode #header { + border-bottom-color: #29292B; +} + +.isMac.nightMode #header { + border-bottom-color: #3a3a3a; +} + +@keyframes spin { + 0% { + -webkit-transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(360deg); + } +} +.spin { + animation: spin; + animation-duration: 2s; + animation-iteration-count: infinite; + display: inline-block; + visibility: visible !important; + animation-timing-function: linear; +} + +#sync-spinner { + width: 16px; + height: 16px; + margin-bottom: -3px; + visibility: hidden; +} +/*AnKing edits*/
\ No newline at end of file diff --git a/.local/share/Anki2/addons21/Background_and_gear/sources/css/25/deckbrowser.css b/.local/share/Anki2/addons21/Background_and_gear/sources/css/25/deckbrowser.css new file mode 100644 index 0000000..986818e --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/sources/css/25/deckbrowser.css @@ -0,0 +1,121 @@ +/* night-mode-specific colours */ +.review-count { + color: #0a0; +} + +.new-count { + color: #00a; +} + +.learn-count { + color: #C35617; +} + +.zero-count { + color: #ddd; +} + +.nightMode .review-count { + color: #5CcC00; +} +.nightMode .new-count { + color: #77ccff; +} +.nightMode .learn-count { + color: #FF935B; +} +.nightMode .zero-count { + color: #444; +} + +/* Copyright: Ankitects Pty Ltd and contributors + * License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */ +a.deck { + color: black; + text-decoration: none; + min-width: 5em; + display: inline-block; +} + +a.deck:hover { + text-decoration: underline; +} + +tr.deck td { + border-bottom: 1px solid #e7e7e7; +} + +tr.top-level-drag-row td { + border-bottom: 1px solid transparent; +} + +td { + white-space: nowrap; +} + +tr.drag-hover td { + border-bottom: 1px solid #aaa; +} + +body { + margin: 1em; + -webkit-user-select: none; +} + +.current { + background-color: rgba(231, 231, 231, 0.3)/* #e7e7e7; */ +} + +.decktd { + min-width: 15em; +} + +.count { + min-width: 4em; + text-align: right; +} + +.optscol { + width: 2em; +} + +.collapse { + color: black; + text-decoration: none; + display: inline-block; + width: 1em; +} + +.filtered { + color: #00a !important; +} + +.gears { + width: 1em; + height: 1em; + opacity: 0.5; + padding-top: 0.2em; +} + +.nightMode a.deck { + color: white; +} +.nightMode tr.deck td { + border-bottom-color: #29292B; +} +.nightMode tr.drag-hover td { + border-bottom-color: #777; +} +.nightMode .current { + background-color: rgba(41, 41, 43, 0.3); /* #29292B; */ +} +.nightMode .collapse { + color: white; +} +.nightMode .gears { + filter: invert(180); +} +.nightMode .filtered { + color: #77ccff !important; +} +/*AnKing edits*/
\ No newline at end of file diff --git a/.local/share/Anki2/addons21/Background_and_gear/sources/css/25/overview.css b/.local/share/Anki2/addons21/Background_and_gear/sources/css/25/overview.css new file mode 100644 index 0000000..f42cc84 --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/sources/css/25/overview.css @@ -0,0 +1,67 @@ +/* night-mode-specific colours */ +.review-count { + color: #0a0; +} + +.new-count { + color: #00a; +} + +.learn-count { + color: #C35617; +} + +.zero-count { + color: #ddd; +} + +.nightMode .review-count { + color: #5CcC00; +} +.nightMode .new-count { + color: #77ccff; +} +.nightMode .learn-count { + color: #FF935B; +} +.nightMode .zero-count { + color: #444; +} + +/* Copyright: Ankitects Pty Ltd and contributors + * License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */ +.smallLink { + font-size: 10px; +} + +h3 { + margin-bottom: 0; +} + +.descfont { + padding: 1em; + color: #333; +} + +.description { + white-space: pre-wrap; +} + +#fulldesc { + display: none; +} + +.descmid { + width: 70%; + margin: 0 auto 0; + text-align: left; +} + +.dyn { + text-align: center; +} + +.nightMode .descfont { + color: #ccc; +} +/*AnKing edits*/
\ No newline at end of file diff --git a/.local/share/Anki2/addons21/Background_and_gear/sources/css/25/reviewer-bottom.css b/.local/share/Anki2/addons21/Background_and_gear/sources/css/25/reviewer-bottom.css new file mode 100644 index 0000000..d6fdf8a --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/sources/css/25/reviewer-bottom.css @@ -0,0 +1,92 @@ +/* night-mode-specific colours */ +.review-count { + color: #0a0; +} + +.new-count { + color: #00a; +} + +.learn-count { + color: #C35617; +} + +.zero-count { + color: #ddd; +} + +.nightMode .review-count { + color: #5CcC00; +} +.nightMode .new-count { + color: #77ccff; +} +.nightMode .learn-count { + color: #FF935B; +} +.nightMode .zero-count { + color: #444; +} + +/* Copyright: Ankitects Pty Ltd and contributors + * License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */ +body { + margin: 0; + padding: 0; +} + +button { + min-width: 60px; + white-space: nowrap; + margin: 0.5em; +} + +.hitem { + margin-top: 2px; +} + +.stat { + padding-top: 5px; +} + +.stat2 { + padding-top: 3px; + font-weight: normal; +} + +.stattxt { + padding-left: 5px; + padding-right: 5px; + white-space: nowrap; +} + +#ansbut { + margin-bottom: 1em; +} + +.nobold { + font-weight: normal; + display: inline-block; + padding-top: 4px; +} + +.spacer { + height: 18px; +} + +.spacer2 { + height: 16px; +} + +#outer { + border-top: 1px solid #aaa; +} + +#innertable { + padding: 3px; +} + +.nightMode #outer { + border-top-color: #29292B; +} + /*AnKing edits*/
\ No newline at end of file diff --git a/.local/share/Anki2/addons21/Background_and_gear/sources/css/25/reviewer.css b/.local/share/Anki2/addons21/Background_and_gear/sources/css/25/reviewer.css new file mode 100644 index 0000000..d301cbc --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/sources/css/25/reviewer.css @@ -0,0 +1,90 @@ +/* night-mode-specific colours */ +/* Copyright: Ankitects Pty Ltd and contributors + * License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */ +hr { + background-color: #ccc; +} + +body { + margin: 20px; + overflow-wrap: break-word; +} + +body.nightMode { + background-color: #2f2f31; + color: white; +} + +img { + max-width: 95%; + max-height: 95vh; +} + +#_flag { + position: fixed; + right: 10px; + top: 0; + font-size: 30px; + display: none; + -webkit-text-stroke-width: 1px; + -webkit-text-stroke-color: black; +} + +#_mark { + position: fixed; + left: 10px; + top: 0; + font-size: 30px; + color: yellow; + display: none; + -webkit-text-stroke-width: 1px; + -webkit-text-stroke-color: black; +} + +#typeans { + width: 100%; + box-sizing: border-box; +} + +.typeGood { + background: #0f0; +} + +.typeBad { + background: #f00; +} + +.typeMissed { + background: #ccc; +} + +.replay-button { + text-decoration: none; + display: inline-flex; + vertical-align: middle; + margin: 3px; +} +.replay-button svg { + width: 40px; + height: 40px; +} +.replay-button svg circle { + fill: #fff; + stroke: #414141; +} +.replay-button svg path { + fill: #414141; +} + +.nightMode .latex { + filter: invert(100%); +} + +.drawing { + zoom: 50%; +} + +.nightMode img.drawing { + filter: invert(1) hue-rotate(180deg); +} +/*AnKing edits*/
\ No newline at end of file diff --git a/.local/share/Anki2/addons21/Background_and_gear/sources/css/25/toolbar-bottom.css b/.local/share/Anki2/addons21/Background_and_gear/sources/css/25/toolbar-bottom.css new file mode 100644 index 0000000..e0fb5e1 --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/sources/css/25/toolbar-bottom.css @@ -0,0 +1,9 @@ +/* Copyright: Ankitects Pty Ltd and contributors + * License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */ +#header { + border-bottom: 0; + margin-bottom: 6px; + margin-top: 0; + padding: 9px; +} +/*AnKing edits*/
\ No newline at end of file diff --git a/.local/share/Anki2/addons21/Background_and_gear/sources/css/25/toolbar.css b/.local/share/Anki2/addons21/Background_and_gear/sources/css/25/toolbar.css new file mode 100644 index 0000000..dfc0cc2 --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/sources/css/25/toolbar.css @@ -0,0 +1,74 @@ +/* night-mode-specific colours */ +/* Copyright: Ankitects Pty Ltd and contributors + * License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */ +#header { + padding: 3px; + font-weight: bold; + border-bottom: 1px solid #aaa; +} + +.tdcenter { + white-space: nowrap; +} + +body { + margin: 0; + padding: 0; + -webkit-user-select: none; + overflow: hidden; +} + +* { + -webkit-user-drag: none; +} + +.hitem { + padding-right: 12px; + padding-left: 12px; + text-decoration: none; + color: black; +} + +.hitem:hover { + text-decoration: underline; +} + +.hitem:focus { + outline: 0; +} + +.nightMode .hitem { + color: white; +} +.nightMode #header { + border-bottom-color: #29292B; +} + +.isMac.nightMode #header { + border-bottom-color: #3a3a3a; +} + +@keyframes spin { + 0% { + -webkit-transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(360deg); + } +} +.spin { + animation: spin; + animation-duration: 2s; + animation-iteration-count: infinite; + display: inline-block; + visibility: visible !important; + animation-timing-function: linear; +} + +#sync-spinner { + width: 16px; + height: 16px; + margin-bottom: -3px; + visibility: hidden; +} +/*AnKing edits*/
\ No newline at end of file diff --git a/.local/share/Anki2/addons21/Background_and_gear/sources/css/31/deckbrowser.css b/.local/share/Anki2/addons21/Background_and_gear/sources/css/31/deckbrowser.css new file mode 100644 index 0000000..986818e --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/sources/css/31/deckbrowser.css @@ -0,0 +1,121 @@ +/* night-mode-specific colours */ +.review-count { + color: #0a0; +} + +.new-count { + color: #00a; +} + +.learn-count { + color: #C35617; +} + +.zero-count { + color: #ddd; +} + +.nightMode .review-count { + color: #5CcC00; +} +.nightMode .new-count { + color: #77ccff; +} +.nightMode .learn-count { + color: #FF935B; +} +.nightMode .zero-count { + color: #444; +} + +/* Copyright: Ankitects Pty Ltd and contributors + * License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */ +a.deck { + color: black; + text-decoration: none; + min-width: 5em; + display: inline-block; +} + +a.deck:hover { + text-decoration: underline; +} + +tr.deck td { + border-bottom: 1px solid #e7e7e7; +} + +tr.top-level-drag-row td { + border-bottom: 1px solid transparent; +} + +td { + white-space: nowrap; +} + +tr.drag-hover td { + border-bottom: 1px solid #aaa; +} + +body { + margin: 1em; + -webkit-user-select: none; +} + +.current { + background-color: rgba(231, 231, 231, 0.3)/* #e7e7e7; */ +} + +.decktd { + min-width: 15em; +} + +.count { + min-width: 4em; + text-align: right; +} + +.optscol { + width: 2em; +} + +.collapse { + color: black; + text-decoration: none; + display: inline-block; + width: 1em; +} + +.filtered { + color: #00a !important; +} + +.gears { + width: 1em; + height: 1em; + opacity: 0.5; + padding-top: 0.2em; +} + +.nightMode a.deck { + color: white; +} +.nightMode tr.deck td { + border-bottom-color: #29292B; +} +.nightMode tr.drag-hover td { + border-bottom-color: #777; +} +.nightMode .current { + background-color: rgba(41, 41, 43, 0.3); /* #29292B; */ +} +.nightMode .collapse { + color: white; +} +.nightMode .gears { + filter: invert(180); +} +.nightMode .filtered { + color: #77ccff !important; +} +/*AnKing edits*/
\ No newline at end of file diff --git a/.local/share/Anki2/addons21/Background_and_gear/sources/css/31/overview.css b/.local/share/Anki2/addons21/Background_and_gear/sources/css/31/overview.css new file mode 100644 index 0000000..f42cc84 --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/sources/css/31/overview.css @@ -0,0 +1,67 @@ +/* night-mode-specific colours */ +.review-count { + color: #0a0; +} + +.new-count { + color: #00a; +} + +.learn-count { + color: #C35617; +} + +.zero-count { + color: #ddd; +} + +.nightMode .review-count { + color: #5CcC00; +} +.nightMode .new-count { + color: #77ccff; +} +.nightMode .learn-count { + color: #FF935B; +} +.nightMode .zero-count { + color: #444; +} + +/* Copyright: Ankitects Pty Ltd and contributors + * License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */ +.smallLink { + font-size: 10px; +} + +h3 { + margin-bottom: 0; +} + +.descfont { + padding: 1em; + color: #333; +} + +.description { + white-space: pre-wrap; +} + +#fulldesc { + display: none; +} + +.descmid { + width: 70%; + margin: 0 auto 0; + text-align: left; +} + +.dyn { + text-align: center; +} + +.nightMode .descfont { + color: #ccc; +} +/*AnKing edits*/
\ No newline at end of file diff --git a/.local/share/Anki2/addons21/Background_and_gear/sources/css/31/reviewer-bottom.css b/.local/share/Anki2/addons21/Background_and_gear/sources/css/31/reviewer-bottom.css new file mode 100644 index 0000000..eab7d69 --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/sources/css/31/reviewer-bottom.css @@ -0,0 +1,92 @@ +/* night-mode-specific colours */ +.review-count { + color: #0a0; +} + +.new-count { + color: #00a; +} + +.learn-count { + color: #C35617; +} + +.zero-count { + color: #ddd; +} + +.nightMode .review-count { + color: #5CcC00; +} +.nightMode .new-count { + color: #77ccff; +} +.nightMode .learn-count { + color: #FF935B; +} +.nightMode .zero-count { + color: #444; +} + +/* Copyright: Ankitects Pty Ltd and contributors + * License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */ +body { + margin: 0; + padding: 0; +} + +button { + min-width: 60px; + white-space: nowrap; + margin: 0.5em; +} + +.hitem { + margin-top: 2px; +} + +.stat { + padding-top: 5px; +} + +.stat2 { + padding-top: 3px; + font-weight: normal; +} + +.stattxt { + padding-left: 5px; + padding-right: 5px; + white-space: nowrap; +} + +#ansbut { + margin-bottom: 1em; +} + +.nobold { + font-weight: normal; + display: inline-block; + padding-top: 4px; +} + +.spacer { + height: 18px; +} + +.spacer2 { + height: 16px; +} + +#outer { + border-top: 1px solid #aaa; +} + +#innertable { + padding: 3px; +} + +.nightMode #outer { + border-top-color: #29292B; +} +/*AnKing edits*/
\ No newline at end of file diff --git a/.local/share/Anki2/addons21/Background_and_gear/sources/css/31/reviewer.css b/.local/share/Anki2/addons21/Background_and_gear/sources/css/31/reviewer.css new file mode 100644 index 0000000..d301cbc --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/sources/css/31/reviewer.css @@ -0,0 +1,90 @@ +/* night-mode-specific colours */ +/* Copyright: Ankitects Pty Ltd and contributors + * License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */ +hr { + background-color: #ccc; +} + +body { + margin: 20px; + overflow-wrap: break-word; +} + +body.nightMode { + background-color: #2f2f31; + color: white; +} + +img { + max-width: 95%; + max-height: 95vh; +} + +#_flag { + position: fixed; + right: 10px; + top: 0; + font-size: 30px; + display: none; + -webkit-text-stroke-width: 1px; + -webkit-text-stroke-color: black; +} + +#_mark { + position: fixed; + left: 10px; + top: 0; + font-size: 30px; + color: yellow; + display: none; + -webkit-text-stroke-width: 1px; + -webkit-text-stroke-color: black; +} + +#typeans { + width: 100%; + box-sizing: border-box; +} + +.typeGood { + background: #0f0; +} + +.typeBad { + background: #f00; +} + +.typeMissed { + background: #ccc; +} + +.replay-button { + text-decoration: none; + display: inline-flex; + vertical-align: middle; + margin: 3px; +} +.replay-button svg { + width: 40px; + height: 40px; +} +.replay-button svg circle { + fill: #fff; + stroke: #414141; +} +.replay-button svg path { + fill: #414141; +} + +.nightMode .latex { + filter: invert(100%); +} + +.drawing { + zoom: 50%; +} + +.nightMode img.drawing { + filter: invert(1) hue-rotate(180deg); +} +/*AnKing edits*/
\ No newline at end of file diff --git a/.local/share/Anki2/addons21/Background_and_gear/sources/css/31/toolbar-bottom.css b/.local/share/Anki2/addons21/Background_and_gear/sources/css/31/toolbar-bottom.css new file mode 100644 index 0000000..d37545b --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/sources/css/31/toolbar-bottom.css @@ -0,0 +1,10 @@ +/* Copyright: Ankitects Pty Ltd and contributors + * License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */ +#header { + border-bottom: 0; + margin-bottom: 6px; + margin-top: 0; + padding: 9px; +} +/*AnKing edits*/ + diff --git a/.local/share/Anki2/addons21/Background_and_gear/sources/css/31/toolbar.css b/.local/share/Anki2/addons21/Background_and_gear/sources/css/31/toolbar.css new file mode 100644 index 0000000..e7f671a --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/sources/css/31/toolbar.css @@ -0,0 +1,89 @@ +/* night-mode-specific colours */ +/* Copyright: Ankitects Pty Ltd and contributors + * License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */ +#header { + padding: 3px; + font-weight: bold; + border-bottom: 1px solid #aaa; +} + +.tdcenter { + white-space: nowrap; +} + +body { + margin: 0; + padding: 0; + -webkit-user-select: none; + overflow: hidden; +} + +* { + -webkit-user-drag: none; +} + +.hitem { + padding-right: 12px; + padding-left: 12px; + text-decoration: none; + color: black; +} + +.hitem:hover { + text-decoration: underline; +} + +.hitem:focus { + outline: 0; +} + +.nightMode .hitem { + color: white; +} +.nightMode #header { + border-bottom-color: #29292B; +} +.nightMode .normal-sync { + color: #77ccff; +} +.nightMode .full-sync { + color: #FF935B; +} + +.isMac.nightMode #header { + border-bottom-color: #3a3a3a; +} + +@keyframes spin { + 0% { + -webkit-transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(360deg); + } +} +.spin { + animation: spin; + animation-duration: 2s; + animation-iteration-count: infinite; + display: inline-block; + visibility: visible !important; + animation-timing-function: linear; +} + +#sync-spinner { + width: 16px; + height: 16px; + margin-bottom: -3px; + visibility: hidden; +} + +.normal-sync { + color: #00a; +} + +.full-sync { + color: #C35617; +} + +/*AnKing edits*/
\ No newline at end of file diff --git a/.local/share/Anki2/addons21/Background_and_gear/sources/css/36/deckbrowser.css b/.local/share/Anki2/addons21/Background_and_gear/sources/css/36/deckbrowser.css new file mode 100644 index 0000000..10c9838 --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/sources/css/36/deckbrowser.css @@ -0,0 +1,102 @@ +.review-count { + color: var(--review-count); +} + +.new-count { + color: var(--new-count); +} + +.learn-count { + color: var(--learn-count); +} + +.zero-count { + color: var(--zero-count); +} + +.nightMode .review-count { + color: var(--review-count); +} +.nightMode .new-count { + color: var(--new-count); +} +.nightMode .learn-count { + color: var(--learn-count); +} +.nightMode .zero-count { + color: var(--zero-count); +} + +/* Copyright: Ankitects Pty Ltd and contributors + * License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */ +a.deck { + color: var(--text-fg); + text-decoration: none; + min-width: 5em; + display: inline-block; +} + +a.deck:hover { + text-decoration: underline; +} + +tr.deck td { + border-bottom: 1px solid var(--faint-border); +} + +tr.top-level-drag-row td { + border-bottom: 1px solid transparent; +} + +td { + white-space: nowrap; +} + +tr.drag-hover td { + border-bottom: 1px solid var(--border); +} + +body { + margin: 1em; + -webkit-user-select: none; +} + +.current { + /*background-color: rgba(231, 231, 231, 0.3) */var(--faint-border); +} + +.decktd { + min-width: 15em; +} + +.count { + min-width: 4em; + text-align: right; +} + +.optscol { + width: 2em; +} + +.collapse { + color: var(--text-fg); + text-decoration: none; + display: inline-block; + width: 1em; +} + +.filtered { + color: var(--link) !important; +} + +.gears { + width: 1em; + height: 1em; + opacity: 0.5; + padding-top: 0.2em; +} + +.nightMode .gears { + filter: invert(180); +} +/*AnKing edits*/
\ No newline at end of file diff --git a/.local/share/Anki2/addons21/Background_and_gear/sources/css/36/overview.css b/.local/share/Anki2/addons21/Background_and_gear/sources/css/36/overview.css new file mode 100644 index 0000000..2af967b --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/sources/css/36/overview.css @@ -0,0 +1,62 @@ +.review-count { + color: var(--review-count); +} + +.new-count { + color: var(--new-count); +} + +.learn-count { + color: var(--learn-count); +} + +.zero-count { + color: var(--zero-count); +} + +.nightMode .review-count { + color: var(--review-count); +} +.nightMode .new-count { + color: var(--new-count); +} +.nightMode .learn-count { + color: var(--learn-count); +} +.nightMode .zero-count { + color: var(--zero-count); +} + +/* Copyright: Ankitects Pty Ltd and contributors + * License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */ +.smallLink { + font-size: 10px; +} + +h3 { + margin-bottom: 0; +} + +.descfont { + padding: 1em; + color: var(--slightly-grey-text); +} + +.description { + white-space: pre-wrap; +} + +#fulldesc { + display: none; +} + +.descmid { + width: 70%; + margin: 0 auto 0; + text-align: left; +} + +.dyn { + text-align: center; +} +/*AnKing edits*/
\ No newline at end of file diff --git a/.local/share/Anki2/addons21/Background_and_gear/sources/css/36/reviewer-bottom.css b/.local/share/Anki2/addons21/Background_and_gear/sources/css/36/reviewer-bottom.css new file mode 100644 index 0000000..de7846c --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/sources/css/36/reviewer-bottom.css @@ -0,0 +1,91 @@ +.review-count { + color: var(--review-count); +} + +.new-count { + color: var(--new-count); +} + +.learn-count { + color: var(--learn-count); +} + +.zero-count { + color: var(--zero-count); +} + +.nightMode .review-count { + color: var(--review-count); +} +.nightMode .new-count { + color: var(--new-count); +} +.nightMode .learn-count { + color: var(--learn-count); +} +.nightMode .zero-count { + color: var(--zero-count); +} + +/* Copyright: Ankitects Pty Ltd and contributors + * License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */ +body { + margin: 0; + padding: 0; +} + +button { + min-width: 60px; + white-space: nowrap; + margin: 0.5em; +} + +.hitem { + margin-top: 2px; +} + +.stat { + padding-top: 5px; +} + +.stat2 { + padding-top: 3px; + font-weight: normal; +} + +.stattxt { + padding-left: 5px; + padding-right: 5px; + white-space: nowrap; +} + +#ansbut { + margin-bottom: 1em; +} + +.nobold { + font-weight: normal; + display: inline-block; + padding-top: 4px; +} + +.spacer { + height: 18px; +} + +.spacer2 { + height: 16px; +} + +#outer { + border-top: 1px solid var(--border); +} + +#innertable { + padding: 3px; +} + +.nightMode #outer { + border-top-color: var(--faint-border); +} +/*AnKing edits*/
\ No newline at end of file diff --git a/.local/share/Anki2/addons21/Background_and_gear/sources/css/36/reviewer.css b/.local/share/Anki2/addons21/Background_and_gear/sources/css/36/reviewer.css new file mode 100644 index 0000000..164e2c1 --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/sources/css/36/reviewer.css @@ -0,0 +1,87 @@ +/* Copyright: Ankitects Pty Ltd and contributors + * License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */ +hr { + background-color: #ccc; +} + +body { + margin: 20px; + overflow-wrap: break-word; +} + +body.nightMode { + background-color: var(--window-bg); + color: var(--text-fg); +} + +img { + max-width: 95%; + max-height: 95vh; +} + +#_flag { + position: fixed; + right: 10px; + top: 0; + font-size: 30px; + -webkit-text-stroke-width: 1px; + -webkit-text-stroke-color: black; +} + +#_mark { + position: fixed; + left: 10px; + top: 0; + font-size: 30px; + color:#ff0; + -webkit-text-stroke-width: 1px; + -webkit-text-stroke-color: black; +} + +#typeans { + width: 100%; + box-sizing: border-box; +} + +.typeGood { + background: #0f0; +} + +.typeBad { + background: #f00; +} + +.typeMissed { + background: #ccc; +} + +.replay-button { + text-decoration: none; + display: inline-flex; + vertical-align: middle; + margin: 3px; +} +.replay-button svg { + width: 40px; + height: 40px; +} +.replay-button svg circle { + fill: #fff; + stroke: #414141; +} +.replay-button svg path { + fill: #414141; +} + +.nightMode .latex { + filter: invert(100%); +} + +.drawing { + zoom: 50%; +} + +.nightMode img.drawing { + filter: invert(1) hue-rotate(180deg); +} +/*AnKing edits*/
\ No newline at end of file diff --git a/.local/share/Anki2/addons21/Background_and_gear/sources/css/36/toolbar-bottom.css b/.local/share/Anki2/addons21/Background_and_gear/sources/css/36/toolbar-bottom.css new file mode 100644 index 0000000..95938ef --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/sources/css/36/toolbar-bottom.css @@ -0,0 +1,9 @@ +/* Copyright: Ankitects Pty Ltd and contributors + * License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */ +#header { + border-bottom: 0; + margin-bottom: 6px; + margin-top: 0; + padding: 9px; +} +/*AnKing edits*/ diff --git a/.local/share/Anki2/addons21/Background_and_gear/sources/css/36/toolbar.css b/.local/share/Anki2/addons21/Background_and_gear/sources/css/36/toolbar.css new file mode 100644 index 0000000..a45adb9 --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/sources/css/36/toolbar.css @@ -0,0 +1,78 @@ +/* Copyright: Ankitects Pty Ltd and contributors + * License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */ +#header { + padding: 3px; + font-weight: bold; + border-bottom: 1px solid var(--border); +} + +.tdcenter { + white-space: nowrap; +} + +body { + margin: 0; + padding: 0; + -webkit-user-select: none; + overflow: hidden; +} + +* { + -webkit-user-drag: none; +} + +.hitem { + padding-right: 12px; + padding-left: 12px; + text-decoration: none; + color: var(--text-fg); +} + +.hitem:hover { + text-decoration: underline; +} + +.hitem:focus { + outline: 0; +} + +.nightMode #header { + border-bottom-color: var(--faint-border); +} + +.isMac.nightMode #header { + border-bottom-color: var(--frame-bg); +} + +@keyframes spin { + 0% { + -webkit-transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(360deg); + } +} +.spin { + animation: spin; + animation-duration: 2s; + animation-iteration-count: infinite; + display: inline-block; + visibility: visible !important; + animation-timing-function: linear; +} + +#sync-spinner { + width: 16px; + height: 16px; + margin-bottom: -3px; + visibility: hidden; +} + +.normal-sync { + color: var(--new-count); +} + +.full-sync { + color: var(--learn-count); +} +/*AnKing edits*/
\ No newline at end of file diff --git a/.local/share/Anki2/addons21/Background_and_gear/user_files/background/1721729412.jpg b/.local/share/Anki2/addons21/Background_and_gear/user_files/background/1721729412.jpg Binary files differnew file mode 100644 index 0000000..f1b5b21 --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/user_files/background/1721729412.jpg diff --git a/.local/share/Anki2/addons21/Background_and_gear/user_files/background/samuel-inkilainen-laboratory.jpg b/.local/share/Anki2/addons21/Background_and_gear/user_files/background/samuel-inkilainen-laboratory.jpg Binary files differnew file mode 100644 index 0000000..a1e608e --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/user_files/background/samuel-inkilainen-laboratory.jpg diff --git a/.local/share/Anki2/addons21/Background_and_gear/user_files/background/the-lm7-laboratories-original-characters-brown-wallpaper.jpg b/.local/share/Anki2/addons21/Background_and_gear/user_files/background/the-lm7-laboratories-original-characters-brown-wallpaper.jpg Binary files differnew file mode 100644 index 0000000..0832fc2 --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/user_files/background/the-lm7-laboratories-original-characters-brown-wallpaper.jpg diff --git a/.local/share/Anki2/addons21/Background_and_gear/user_files/background/thumb-1920-1058130.jpg b/.local/share/Anki2/addons21/Background_and_gear/user_files/background/thumb-1920-1058130.jpg Binary files differnew file mode 100644 index 0000000..fa79651 --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/user_files/background/thumb-1920-1058130.jpg diff --git a/.local/share/Anki2/addons21/Background_and_gear/user_files/background/wp7186342.jpg b/.local/share/Anki2/addons21/Background_and_gear/user_files/background/wp7186342.jpg Binary files differnew file mode 100644 index 0000000..263aa29 --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/user_files/background/wp7186342.jpg diff --git a/.local/share/Anki2/addons21/Background_and_gear/user_files/css/custom_deckbrowser.css b/.local/share/Anki2/addons21/Background_and_gear/user_files/css/custom_deckbrowser.css new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/user_files/css/custom_deckbrowser.css diff --git a/.local/share/Anki2/addons21/Background_and_gear/user_files/css/custom_overview.css b/.local/share/Anki2/addons21/Background_and_gear/user_files/css/custom_overview.css new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/user_files/css/custom_overview.css diff --git a/.local/share/Anki2/addons21/Background_and_gear/user_files/css/custom_reviewer-bottom.css b/.local/share/Anki2/addons21/Background_and_gear/user_files/css/custom_reviewer-bottom.css new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/user_files/css/custom_reviewer-bottom.css diff --git a/.local/share/Anki2/addons21/Background_and_gear/user_files/css/custom_reviewer.css b/.local/share/Anki2/addons21/Background_and_gear/user_files/css/custom_reviewer.css new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/user_files/css/custom_reviewer.css diff --git a/.local/share/Anki2/addons21/Background_and_gear/user_files/css/custom_toolbar-bottom.css b/.local/share/Anki2/addons21/Background_and_gear/user_files/css/custom_toolbar-bottom.css new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/user_files/css/custom_toolbar-bottom.css diff --git a/.local/share/Anki2/addons21/Background_and_gear/user_files/css/custom_toolbar.css b/.local/share/Anki2/addons21/Background_and_gear/user_files/css/custom_toolbar.css new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/user_files/css/custom_toolbar.css diff --git a/.local/share/Anki2/addons21/Background_and_gear/user_files/gear/AnKing.png b/.local/share/Anki2/addons21/Background_and_gear/user_files/gear/AnKing.png Binary files differnew file mode 100644 index 0000000..501e376 --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/user_files/gear/AnKing.png diff --git a/.local/share/Anki2/addons21/Background_and_gear/user_files/gear/Bam.png b/.local/share/Anki2/addons21/Background_and_gear/user_files/gear/Bam.png Binary files differnew file mode 100644 index 0000000..3e4e689 --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/user_files/gear/Bam.png diff --git a/.local/share/Anki2/addons21/Background_and_gear/user_files/gear/Bullseye.png b/.local/share/Anki2/addons21/Background_and_gear/user_files/gear/Bullseye.png Binary files differnew file mode 100644 index 0000000..e04dd75 --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/user_files/gear/Bullseye.png diff --git a/.local/share/Anki2/addons21/Background_and_gear/user_files/gear/Cowboy.png b/.local/share/Anki2/addons21/Background_and_gear/user_files/gear/Cowboy.png Binary files differnew file mode 100644 index 0000000..b5d26c0 --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/user_files/gear/Cowboy.png diff --git a/.local/share/Anki2/addons21/Background_and_gear/user_files/gear/Diamond.png b/.local/share/Anki2/addons21/Background_and_gear/user_files/gear/Diamond.png Binary files differnew file mode 100644 index 0000000..1bb9551 --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/user_files/gear/Diamond.png diff --git a/.local/share/Anki2/addons21/Background_and_gear/user_files/gear/Dragon.png b/.local/share/Anki2/addons21/Background_and_gear/user_files/gear/Dragon.png Binary files differnew file mode 100644 index 0000000..3ac2990 --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/user_files/gear/Dragon.png diff --git a/.local/share/Anki2/addons21/Background_and_gear/user_files/gear/Fire.png b/.local/share/Anki2/addons21/Background_and_gear/user_files/gear/Fire.png Binary files differnew file mode 100644 index 0000000..50aa77f --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/user_files/gear/Fire.png diff --git a/.local/share/Anki2/addons21/Background_and_gear/user_files/gear/Flower.png b/.local/share/Anki2/addons21/Background_and_gear/user_files/gear/Flower.png Binary files differnew file mode 100644 index 0000000..17083b9 --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/user_files/gear/Flower.png diff --git a/.local/share/Anki2/addons21/Background_and_gear/user_files/gear/Nerd.png b/.local/share/Anki2/addons21/Background_and_gear/user_files/gear/Nerd.png Binary files differnew file mode 100644 index 0000000..a8e16ea --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/user_files/gear/Nerd.png diff --git a/.local/share/Anki2/addons21/Background_and_gear/user_files/gear/Rose.png b/.local/share/Anki2/addons21/Background_and_gear/user_files/gear/Rose.png Binary files differnew file mode 100644 index 0000000..af0c815 --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/user_files/gear/Rose.png diff --git a/.local/share/Anki2/addons21/Background_and_gear/user_files/gear/Shield.png b/.local/share/Anki2/addons21/Background_and_gear/user_files/gear/Shield.png Binary files differnew file mode 100644 index 0000000..9a27ac9 --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/user_files/gear/Shield.png diff --git a/.local/share/Anki2/addons21/Background_and_gear/user_files/gear/Skull.png b/.local/share/Anki2/addons21/Background_and_gear/user_files/gear/Skull.png Binary files differnew file mode 100644 index 0000000..f3c959e --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/user_files/gear/Skull.png diff --git a/.local/share/Anki2/addons21/Background_and_gear/user_files/gear/Star.png b/.local/share/Anki2/addons21/Background_and_gear/user_files/gear/Star.png Binary files differnew file mode 100644 index 0000000..76626de --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/user_files/gear/Star.png diff --git a/.local/share/Anki2/addons21/Background_and_gear/user_files/gear/Sun.png b/.local/share/Anki2/addons21/Background_and_gear/user_files/gear/Sun.png Binary files differnew file mode 100644 index 0000000..d71edd5 --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/user_files/gear/Sun.png diff --git a/.local/share/Anki2/addons21/Background_and_gear/user_files/gear/gears.svg b/.local/share/Anki2/addons21/Background_and_gear/user_files/gear/gears.svg new file mode 100644 index 0000000..7efbeb3 --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/user_files/gear/gears.svg @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg width="100%" height="100%" viewBox="0 0 60 60" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;"> + <rect id="gears" x="0" y="0" width="60" height="60" style="fill:none;"/> + <clipPath id="_clip1"> + <rect id="gears1" x="0" y="0" width="60" height="60"/> + </clipPath> + <g clip-path="url(#_clip1)"> + <g transform="matrix(-1.03571,0,0,-1.03571,60.9474,60.7657)"> + <path d="M34.69,2.396C31.586,1.868 28.414,1.868 25.31,2.396C24.89,5.368 24.478,8.342 24.072,11.316C22.306,11.877 20.628,12.685 19.088,13.716C16.51,12.179 13.928,10.647 11.342,9.122C8.994,11.22 7.017,13.7 5.494,16.456C7.556,18.637 9.624,20.814 11.697,22.985C11.033,24.716 10.619,26.532 10.466,28.378C7.656,29.436 4.849,30.499 2.044,31.57C2.221,34.714 2.926,37.806 4.131,40.715C7.123,40.463 10.114,40.203 13.104,39.937C14.043,41.534 15.204,42.99 16.553,44.262C15.627,47.117 14.709,49.975 13.797,52.836C16.365,54.658 19.223,56.034 22.249,56.906C23.916,54.41 25.578,51.909 27.234,49.405C29.069,49.667 30.931,49.667 32.766,49.405C34.422,51.909 36.084,54.41 37.751,56.906C40.777,56.034 43.635,54.658 46.203,52.836C45.291,49.975 44.373,47.117 43.447,44.262C44.796,42.99 45.957,41.534 46.896,39.937C49.886,40.203 52.877,40.463 55.869,40.715C57.074,37.806 57.779,34.714 57.956,31.57C55.151,30.499 52.344,29.436 49.534,28.378C49.381,26.532 48.967,24.716 48.303,22.985C50.376,20.814 52.444,18.637 54.506,16.456C52.983,13.7 51.006,11.22 48.658,9.122C46.072,10.647 43.49,12.179 40.912,13.716C39.372,12.685 37.694,11.877 35.928,11.316C35.522,8.342 35.11,5.368 34.69,2.396ZM30,22.545C34.114,22.545 37.455,25.886 37.455,30C37.455,34.114 34.114,37.455 30,37.455C25.886,37.455 22.545,34.114 22.545,30C22.545,25.886 25.886,22.545 30,22.545Z"/> + </g> + </g> +</svg> diff --git a/.local/share/Anki2/addons21/Background_and_gear/web/css/deckbrowser.css b/.local/share/Anki2/addons21/Background_and_gear/web/css/deckbrowser.css new file mode 100644 index 0000000..17597d1 --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/web/css/deckbrowser.css @@ -0,0 +1,121 @@ +.review-count { + color: var(--review-count); +} + +.new-count { + color: var(--new-count); +} + +.learn-count { + color: var(--learn-count); +} + +.zero-count { + color: var(--zero-count); +} + +.nightMode .review-count { + color: var(--review-count); +} +.nightMode .new-count { + color: var(--new-count); +} +.nightMode .learn-count { + color: var(--learn-count); +} +.nightMode .zero-count { + color: var(--zero-count); +} + +/* Copyright: Ankitects Pty Ltd and contributors + * License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */ +a.deck { + color: var(--text-fg); + text-decoration: none; + min-width: 5em; + display: inline-block; +} + +a.deck:hover { + text-decoration: underline; +} + +tr.deck td { + border-bottom: 1px solid var(--faint-border); +} + +tr.top-level-drag-row td { + border-bottom: 1px solid transparent; +} + +td { + white-space: nowrap; +} + +tr.drag-hover td { + border-bottom: 1px solid var(--border); +} + +body { + margin: 1em; + -webkit-user-select: none; +} + +.current { + /*background-color: rgba(231, 231, 231, 0.3) */var(--faint-border); +} + +.decktd { + min-width: 15em; +} + +.count { + min-width: 4em; + text-align: right; +} + +.optscol { + width: 2em; +} + +.collapse { + color: var(--text-fg); + text-decoration: none; + display: inline-block; + width: 1em; +} + +.filtered { + color: var(--link) !important; +} + +.gears { + width: 1em; + height: 1em; + opacity: 0.5; + padding-top: 0.2em; +} + +.nightMode .gears { + /* filter: invert(180); */ +} +body::before { + + background-image: url("/_addons/Background_and_gear/user_files/background/1721729412.jpg"); + background-size: cover; + background-attachment: fixed!important; + background-repeat: no-repeat; + background-position: center; + background-color: !important; + opacity: 1; + content: ""; + top: 0; + left: 0; + bottom: 0; + right: 0; + position: fixed; + z-index: -99; + will-change: transform; + transform: scale(1); + +}
\ No newline at end of file diff --git a/.local/share/Anki2/addons21/Background_and_gear/web/css/overview.css b/.local/share/Anki2/addons21/Background_and_gear/web/css/overview.css new file mode 100644 index 0000000..66d2c28 --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/web/css/overview.css @@ -0,0 +1,81 @@ +.review-count { + color: var(--review-count); +} + +.new-count { + color: var(--new-count); +} + +.learn-count { + color: var(--learn-count); +} + +.zero-count { + color: var(--zero-count); +} + +.nightMode .review-count { + color: var(--review-count); +} +.nightMode .new-count { + color: var(--new-count); +} +.nightMode .learn-count { + color: var(--learn-count); +} +.nightMode .zero-count { + color: var(--zero-count); +} + +/* Copyright: Ankitects Pty Ltd and contributors + * License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */ +.smallLink { + font-size: 10px; +} + +h3 { + margin-bottom: 0; +} + +.descfont { + padding: 1em; + color: var(--slightly-grey-text); +} + +.description { + white-space: pre-wrap; +} + +#fulldesc { + display: none; +} + +.descmid { + width: 70%; + margin: 0 auto 0; + text-align: left; +} + +.dyn { + text-align: center; +} +body::before { + + background-image: url("/_addons/Background_and_gear/user_files/background/1721729412.jpg"); + background-size: cover; + background-attachment: fixed!important; + background-repeat: no-repeat; + background-position: center; + background-color: !important; + opacity: 1; + content: ""; + top: 0; + left: 0; + bottom: 0; + right: 0; + position: fixed; + z-index: -99; + will-change: transform; + transform: scale(1); + +}
\ No newline at end of file diff --git a/.local/share/Anki2/addons21/Background_and_gear/web/css/reviewer-bottom.css b/.local/share/Anki2/addons21/Background_and_gear/web/css/reviewer-bottom.css new file mode 100644 index 0000000..7f4b06c --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/web/css/reviewer-bottom.css @@ -0,0 +1,110 @@ +.review-count { + color: var(--review-count); +} + +.new-count { + color: var(--new-count); +} + +.learn-count { + color: var(--learn-count); +} + +.zero-count { + color: var(--zero-count); +} + +.nightMode .review-count { + color: var(--review-count); +} +.nightMode .new-count { + color: var(--new-count); +} +.nightMode .learn-count { + color: var(--learn-count); +} +.nightMode .zero-count { + color: var(--zero-count); +} + +/* Copyright: Ankitects Pty Ltd and contributors + * License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */ +body { + margin: 0; + padding: 0; +} + +button { + min-width: 60px; + white-space: nowrap; + margin: 0.5em; +} + +.hitem { + margin-top: 2px; +} + +.stat { + padding-top: 5px; +} + +.stat2 { + padding-top: 3px; + font-weight: normal; +} + +.stattxt { + padding-left: 5px; + padding-right: 5px; + white-space: nowrap; +} + +#ansbut { + margin-bottom: 1em; +} + +.nobold { + font-weight: normal; + display: inline-block; + padding-top: 4px; +} + +.spacer { + height: 18px; +} + +.spacer2 { + height: 16px; +} + +#outer { + border-top: 1px solid var(--border); +} + +#innertable { + padding: 3px; +} + +.nightMode #outer { + border-top-color: var(--faint-border); +} +body::before { + + background-image: url("/_addons/Background_and_gear/user_files/background/1721729412.jpg"); + background-size: cover; + background-attachment: fixed!important; + background-repeat: no-repeat; + background-position: bottom;; + background-color: !important; + opacity: 0.36; + content: ""; + top: 0; + left: 0; + bottom: 0; + right: 0; + position: fixed; + z-index: -99; + will-change: transform; + transform: scale(1); + +}
\ No newline at end of file diff --git a/.local/share/Anki2/addons21/Background_and_gear/web/css/reviewer.css b/.local/share/Anki2/addons21/Background_and_gear/web/css/reviewer.css new file mode 100644 index 0000000..b165d06 --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/web/css/reviewer.css @@ -0,0 +1,106 @@ +/* Copyright: Ankitects Pty Ltd and contributors + * License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */ +hr { + background-color: #ccc; +} + +body { + margin: 20px; + overflow-wrap: break-word; +} + +body.nightMode { + background-color: var(--window-bg); + color: var(--text-fg); +} + +img { + max-width: 95%; + max-height: 95vh; +} + +#_flag { + position: fixed; + right: 10px; + top: 0; + font-size: 30px; + -webkit-text-stroke-width: 1px; + -webkit-text-stroke-color: black; +} + +#_mark { + position: fixed; + left: 10px; + top: 0; + font-size: 30px; + color:#ff0; + -webkit-text-stroke-width: 1px; + -webkit-text-stroke-color: black; +} + +#typeans { + width: 100%; + box-sizing: border-box; +} + +.typeGood { + background: #0f0; +} + +.typeBad { + background: #f00; +} + +.typeMissed { + background: #ccc; +} + +.replay-button { + text-decoration: none; + display: inline-flex; + vertical-align: middle; + margin: 3px; +} +.replay-button svg { + width: 40px; + height: 40px; +} +.replay-button svg circle { + fill: #fff; + stroke: #414141; +} +.replay-button svg path { + fill: #414141; +} + +.nightMode .latex { + filter: invert(100%); +} + +.drawing { + zoom: 50%; +} + +.nightMode img.drawing { + filter: invert(1) hue-rotate(180deg); +} +body::before { + + background-image: url("/_addons/Background_and_gear/user_files/background/1721729412.jpg"); + background-size: cover; + background-attachment: fixed!important; + background-repeat: no-repeat; + background-position: center; + background-color: !important; + opacity: 0.36; + content: ""; + top: 0; + left: 0; + bottom: 0; + right: 0; + position: fixed; + z-index: -99; + will-change: transform; + transform: scale(1); + +}
\ No newline at end of file diff --git a/.local/share/Anki2/addons21/Background_and_gear/web/css/toolbar-bottom.css b/.local/share/Anki2/addons21/Background_and_gear/web/css/toolbar-bottom.css new file mode 100644 index 0000000..859c0bf --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/web/css/toolbar-bottom.css @@ -0,0 +1,28 @@ +/* Copyright: Ankitects Pty Ltd and contributors + * License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */ +#header { + border-bottom: 0; + margin-bottom: 6px; + margin-top: 0; + padding: 9px; +} +body::before { + + background-image: url("/_addons/Background_and_gear/user_files/background/1721729412.jpg"); + background-size: cover; + background-attachment: fixed!important; + background-repeat: no-repeat; + background-position: bottom;; + background-color: !important; + opacity: 1; + content: ""; + top: 0; + left: 0; + bottom: 0; + right: 0; + position: fixed; + z-index: -99; + will-change: transform; + transform: scale(1); + +} diff --git a/.local/share/Anki2/addons21/Background_and_gear/web/css/toolbar.css b/.local/share/Anki2/addons21/Background_and_gear/web/css/toolbar.css new file mode 100644 index 0000000..5ae2cb8 --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/web/css/toolbar.css @@ -0,0 +1,97 @@ +/* Copyright: Ankitects Pty Ltd and contributors + * License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */ +#header { + padding: 3px; + font-weight: bold; + border-bottom: 1px solid var(--border); +} + +.tdcenter { + white-space: nowrap; +} + +body { + margin: 0; + padding: 0; + -webkit-user-select: none; + overflow: hidden; +} + +* { + -webkit-user-drag: none; +} + +.hitem { + padding-right: 12px; + padding-left: 12px; + text-decoration: none; + color: var(--text-fg); +} + +.hitem:hover { + text-decoration: underline; +} + +.hitem:focus { + outline: 0; +} + +.nightMode #header { + border-bottom-color: var(--faint-border); +} + +.isMac.nightMode #header { + border-bottom-color: var(--frame-bg); +} + +@keyframes spin { + 0% { + -webkit-transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(360deg); + } +} +.spin { + animation: spin; + animation-duration: 2s; + animation-iteration-count: infinite; + display: inline-block; + visibility: visible !important; + animation-timing-function: linear; +} + +#sync-spinner { + width: 16px; + height: 16px; + margin-bottom: -3px; + visibility: hidden; +} + +.normal-sync { + color: var(--new-count); +} + +.full-sync { + color: var(--learn-count); +} +body::before { + + background-image: url("/_addons/Background_and_gear/user_files/background/1721729412.jpg"); + background-size: cover; + background-attachment: fixed!important; + background-repeat: no-repeat; + background-position: top; + background-color: !important; + opacity: 1; + content: ""; + top: 0; + left: 0; + bottom: 0; + right: 0; + position: fixed; + z-index: -99; + will-change: transform; + transform: scale(1); + +}
\ No newline at end of file diff --git a/.local/share/Anki2/addons21/advanced_review/Bottom_Bar.py b/.local/share/Anki2/addons21/advanced_review/Bottom_Bar.py new file mode 100644 index 0000000..529247a --- /dev/null +++ b/.local/share/Anki2/addons21/advanced_review/Bottom_Bar.py @@ -0,0 +1,378 @@ +#// auth_ Mohamad Janati
+#// Copyright (c) 2019-2021 Mohamad Janati (freaking stupid, right? :|)
+
+import json
+from aqt import mw
+import aqt
+from aqt.qt import *
+from aqt.utils import downArrow, shortcut, showInfo
+from aqt.reviewer import Reviewer
+from anki.hooks import wrap
+from . import Card_Info
+from . import styles
+from .Skip import test
+from .Skip import burySkipped
+from .Skip import try_unburySkipped
+
+
+#// getting config information
+config = mw.addonManager.getConfig(__name__)
+speedFocus_addOn = config[' Speed Focus Add-on']
+bottombarButtons_style = config[' Review_ Bottombar Buttons Style']
+skipMethod = config[' Skip Method']
+skip = config['Button_ Skip Button']
+showSkipped = config['Button_ Show Skipped Button']
+info = config['Button_ Info Button']
+undo = config['Button_ Undo Button']
+skip_shortcut = config ['Button_ Shortcut_ Skip Button'].lower()
+showSkipped_shortcut = config ['Button_ Shortcut_ Show Skipped Button'].lower()
+info_shortcut = config['Button_ Shortcut_ Info Button'].lower()
+undo_shortcut = config['Button_ Shortcut_ Undo Button'].lower()
+info_position = config['Button_ Position_ Info Button'].lower()
+skip_position = config['Button_ Position_ Skip Button'].lower()
+showSkipped_position = config['Button_ Position_ Show Skipped Button'].lower()
+undo_position = config['Button_ Position_ Undo Button'].lower()
+
+edit_label = config['Button Label_ Edit']
+showAnswer_label = config['Button Label_ Show Answer']
+more_label = config['Button Label_ More']
+info_label = config['Button Label_ Info']
+skip_label = config['Button Label_ Skip']
+showSkipped_label = config['Button Label_ Show Skipped']
+undo_label = config['Button Label_ Undo']
+
+custom_buttonSize = config ['Button_ Custom Button Sizes']
+buttons_height = config['Button_ Height_ All Bottombar Buttons']
+answer_width = config['Button_ Width_ Show Answer Button']
+text_size = config['Button_ Text Size']
+
+custom_bottombarButtonBorderColor = config['Color_ Custom Bottombar Button Border Color']
+bottombarButtonBorder_color = config['Color_ Bottombar Button Border Color']
+showAnswerBorderColor_style = config['ShowAnswer_ Border Color Style']
+showAnswerEase1 = config['ShowAnswer_ Ease1']
+showAnswerEase2 = config['ShowAnswer_ Ease2']
+showAnswerEase3 = config['ShowAnswer_ Ease3']
+showAnswerEase4 = config['ShowAnswer_ Ease4']
+showAnswerEase1_color = config['ShowAnswer_ Ease1 Color']
+showAnswerEase2_color = config['ShowAnswer_ Ease2 Color']
+showAnswerEase3_color = config['ShowAnswer_ Ease3 Color']
+showAnswerEase4_color = config['ShowAnswer_ Ease4 Color']
+
+# TODO: set the showSkipped styling and stuff
+edit_style = styles.edit_style
+info_style = styles.info_style
+skip_style = styles.skip_style
+showSkipped_style = styles.showSkipped_style
+undo_style = styles.undo_style
+more_style = styles.more_style
+min_buttonSize = styles.min_buttonSize
+bottombar_neon1 = styles.bottombar_neon1
+bottombar_neon2 = styles.bottombar_neon2
+bottombar_fill1 = styles.bottombar_fill1
+bottombar_fill2 = styles.bottombar_fill2
+
+#// adding shortcuts to _shortcutKeys function in anki
+def _shortcutKeys_wrap(self, _old):
+ original = _old(self)
+ sched_ver = mw.col.sched.version
+ if sched_ver > 2 or skipMethod == 1:
+ original.append((skip_shortcut, lambda: burySkipped()))
+ original.append((showSkipped_shortcut, lambda: try_unburySkipped()))
+ else:
+ original.append((skip_shortcut, lambda: self.nextCard()))
+ original.extend([
+ (info_shortcut, lambda: Card_Info._cs.toggle()),
+ (undo_shortcut, lambda: mw.onUndo())
+ ])
+ return original
+
+
+#// adding button links to link handler function
+def linkHandler_wrap(reviewer, url):
+ sched_ver = mw.col.sched.version
+ if url == "card_info":
+ Card_Info._cs.toggle()
+ elif url == "skip":
+ if sched_ver > 2 or skipMethod == 1:
+ burySkipped()
+ else:
+ reviewer.nextCard()
+ elif url == "showSkipped":
+ if sched_ver > 2 or skipMethod == 1:
+ try_unburySkipped()
+ else:
+ showInfo("Your skip method is not \"Bury\" Hence you don't have any skipped cards that can be shown using this button.")
+ elif url == "undo":
+ mw.onUndo()
+ else:
+ Review_linkHandelr_Original(reviewer, url)
+
+Review_linkHandelr_Original = Reviewer._linkHandler
+Reviewer._linkHandler = linkHandler_wrap
+
+#// Chosing stylinhg for review other buttons in reviewer bottombar based on chosen style
+if bottombarButtons_style == 0:
+ bottomHTML_style = "<style></style>"
+elif bottombarButtons_style == 1:
+ bottomHTML_style = bottombar_neon1
+elif bottombarButtons_style == 2:
+ bottomHTML_style = bottombar_neon2
+elif bottombarButtons_style == 3:
+ bottomHTML_style = bottombar_fill1
+elif bottombarButtons_style == 4:
+ bottomHTML_style = bottombar_fill2
+
+#// info button | written in a separate functions to preserve the original bottombar
+def info_button():
+ if info:
+ return """<button title="Shortcut key: {}" onclick="pycmd('card_info');" {}>{}</button>""".format(info_shortcut.upper(), info_style, info_label)
+ else:
+ return ""
+
+
+#// skip button | written in a separate functions to preserve the original bottombar
+def skip_button():
+ if skip:
+ return """<button title="Shortcut key: {}" onclick="pycmd('skip');" {}>{}</button>""".format(skip_shortcut.upper(), skip_style, skip_label)
+ else:
+ return ""
+
+#// Show Skipped button
+def showSkipped_button():
+ if showSkipped:
+ return """<button title="Shortcut key: {}" onclick="pycmd('showSkipped');" {}>{}</button>""".format(showSkipped_shortcut.upper(), showSkipped_style, showSkipped_label)
+ else:
+ return ""
+
+
+#// undo button
+def undo_button():
+ if undo:
+ return """<button title="Shortcut key: {}" onclick="pycmd('undo');" {}>{}</button>""".format(undo_shortcut, undo_style, undo_label)
+ else:
+ return ""
+
+
+#// Button Positions
+leftSide_button1 = ""
+leftSide_button2 = ""
+leftSide_button3 = ""
+leftSide_button4 = ""
+middleLeftSide_button1 = ""
+middleLeftSide_button2 = ""
+middleLeftSide_button3 = ""
+middleLeftSide_button4 = ""
+middleRightSide_button1 = ""
+middleRightSide_button2 = ""
+middleRightSide_button3 = ""
+middleRightSide_button4 = ""
+rightSide_button1 = ""
+rightSide_button2 = ""
+rightSide_button3 = ""
+rightSide_button4 = ""
+
+if info_position == "right":
+ rightSide_button1 = info_button()
+elif info_position == "middle left":
+ middleLeftSide_button1 = info_button()
+elif info_position == "middle right":
+ middleRightSide_button1 = info_button()
+else:
+ leftSide_button1 = info_button()
+
+if skip_position == "left":
+ leftSide_button2 = skip_button()
+elif skip_position == "middle right":
+ middleRightSide_button2 = skip_button()
+elif skip_position == "right":
+ rightSide_button2 = skip_button()
+else:
+ middleLeftSide_button2 = skip_button()
+
+if showSkipped_position == "left":
+ leftSide_button3 = showSkipped_button()
+elif showSkipped_position == "middle right":
+ middleRightSide_button3 = showSkipped_button()
+elif showSkipped_position == "right":
+ rightSide_button3 = showSkipped_button()
+else:
+ middleLeftSide_button3 = showSkipped_button()
+
+if undo_position == "left":
+ leftSide_button4 = undo_button()
+elif undo_position == "middle right":
+ middleRightSide_button4 = undo_button()
+elif undo_position == "right":
+ rightSide_button4 = undo_button()
+else:
+ middleLeftSide_button4 = undo_button()
+
+#// Speed focus remove conflicts
+if speedFocus_addOn:
+ SF_bottomHTML = """
+var autoAnswerTimeout = 0;
+var autoAgainTimeout = 0;
+var autoAlertTimeout = 0;
+
+var setAutoAnswer = function(ms) {
+ clearTimeout(autoAnswerTimeout);
+ autoAnswerTimeout = setTimeout(function () { pycmd('ans') }, ms);
+}
+var setAutoAgain = function(ms) {
+ clearTimeout(autoAgainTimeout);
+ autoAgainTimeout = setTimeout(function () { pycmd("ease1"); }, ms);
+}
+var setAutoAlert = function(ms) {
+ clearTimeout(autoAlertTimeout);
+ autoAlertTimeout = setTimeout(function () { pycmd("autoalert"); }, ms);
+}"""
+else:
+ SF_bottomHTML = ""
+
+#// setting buttons based on their position
+if leftSide_button1 != "":
+ left_side1 = "<td width=50 align=left valign=top class=stat><br> {} </td>".format(leftSide_button1)
+else:
+ left_side1 = ""
+
+if leftSide_button2 != "":
+ left_side2 = "<td width=50 align=left valign=top class=stat><br> {} </td>".format(leftSide_button2)
+else:
+ left_side2 = ""
+
+if leftSide_button3 != "":
+ left_side3 = "<td width=50 align=left valign=top class=stat><br> {} </td>".format(leftSide_button3)
+else:
+ left_side3 = ""
+
+if leftSide_button4 != "":
+ left_side4 = "<td width=50 align=left valign=top class=stat><br> {} </td>".format(leftSide_button4)
+else:
+ left_side4 = ""
+
+if rightSide_button1 != "":
+ right_side1 = "<td width=50 align=right valign=top class=stat><br> {} </td>".format(rightSide_button1)
+else:
+ right_side1 = ""
+
+if rightSide_button2 != "":
+ right_side2 = "<td width=50 align=right valign=top class=stat><br> {} </td>".format(rightSide_button2)
+else:
+ right_side2 = ""
+
+if rightSide_button3 != "":
+ right_side3 = "<td width=50 align=right valign=top class=stat><br> {} </td>".format(rightSide_button3)
+else:
+ right_side3 = ""
+
+if rightSide_button4 != "":
+ right_side4 = "<td width=50 align=right valign=top class=stat><br> {} </td>".format(rightSide_button4)
+else:
+ right_side4 = ""
+
+#// Review Screen Bottombar HTML
+def _bottomHTML(self):
+ time_color = ""
+ if custom_bottombarButtonBorderColor:
+ time_color = bottombarButtonBorder_color
+
+ return """%(bottomHTML_style)s
+%(min_buttonSize)s
+<center id=outer>
+<table id=innertable width=100%% cellspacing=0 cellpadding=0>
+<tr>
+<td align=left width=50 valign=top class=stat>
+<br>
+<button title="Shortcut key: E" onclick="pycmd('edit');" %(edit_style)s>%(edit_label)s</button></td>
+%(left_side1)s
+%(left_side2)s
+%(left_side3)s
+%(left_side4)s
+<td align=center valign=top id=middle>
+</td>
+%(right_side1)s
+%(right_side2)s
+%(right_side3)s
+%(right_side4)s
+<td width=50 align=right valign=top class=stat style='color: %(time_color)s'><span id=time class=stattxt>
+</span><br>
+<button onclick="pycmd('more');" %(more_style)s>%(more_label)s %(downArrow)s</button>
+</td>
+</tr>
+</table>
+</center>
+<script>
+time = %(time)d;
+%(SF_bottomHTML)s
+</script>
+""" % dict(bottomHTML_style=bottomHTML_style, min_buttonSize=min_buttonSize, rem=self._remaining(), downArrow=downArrow(), time=self.card.time_taken() // 1000,
+ edit_style=edit_style, edit_label=edit_label, left_side1=left_side1, left_side2=left_side2, left_side3=left_side3, left_side4=left_side4, right_side1=right_side1,
+ right_side2=right_side2, right_side3=right_side3, right_side4=right_side4, more_style=more_style, more_label=more_label, SF_bottomHTML=SF_bottomHTML, time_color=time_color)
+
+#// Show Answer Button
+def _showAnswerButton(self):
+ showAnswer_text = showAnswer_label
+ highEase_tooltip = ""
+
+ if self.card.type not in [0, 1] and showAnswerBorderColor_style in [1, 3]:
+ if self.card.factor // 10 < showAnswerEase1:
+ showAnswerBorder_color = showAnswerEase1_color
+ elif (showAnswerEase1 - 1) < self.card.factor // 10 < showAnswerEase2:
+ showAnswerBorder_color = showAnswerEase2_color
+ elif (showAnswerEase2 - 1) < self.card.factor // 10 < showAnswerEase3:
+ showAnswerBorder_color = showAnswerEase3_color
+ elif (showAnswerEase3 - 1) < self.card.factor // 10 < showAnswerEase4:
+ showAnswerBorder_color = showAnswerEase4_color
+ else:
+ showAnswerBorder_color = "#7000A8"
+ showAnswer_text = "<font size=6 color='#7000A8'> ^_~ </font>"
+ else:
+ showAnswerBorder_color = ""
+
+ #// Moved show answer button size from "styles.py" here to make show answer border color based on ease compatible with custom button sizes
+ if custom_buttonSize:
+ if bottombarButtons_style ==0:
+ showAnswer_style = 'style="height: {}px; width: {}px; font-size: {}px; border-color: {};"'.format(buttons_height, answer_width, text_size, showAnswerBorder_color)
+ else:
+ showAnswer_style = 'style="height: {}px; width: {}px; font-size: {}px; border-color: {};" id=main'.format(buttons_height, answer_width, text_size, showAnswerBorder_color)
+ else:
+ if bottombarButtons_style == 0:
+ showAnswer_style = "style='border-color: {}' id=ansbut".format(showAnswerBorder_color) #// removed id=ansbut from it's own code for styling
+ else:
+ showAnswer_style = "style='border-color: {}' id=main".format(showAnswerBorder_color)
+
+ #// removing conflict with speed focus add-on
+ if speedFocus_addOn:
+ c = self.mw.col.decks.confForDid(self.card.odid or self.card.did)
+ if c.get('autoAnswer', 0) > 0:
+ self.bottom.web.eval("setAutoAnswer(%d);" % (c['autoAnswer'] * 1000))
+ if c.get('autoAlert', 0) > 0:
+ self.bottom.web.eval("setAutoAlert(%d);" % (c['autoAlert'] * 1000))
+ middle = '''
+<table cellspacing=0 cellpadding=0><tr><td class=stat2 align=center>
+<span class=stattxt> %(remaining)s </span><br>
+%(middleLeft_side1)s
+%(middleLeft_side2)s
+%(middleLeft_side3)s
+%(middleLeft_side4)s
+<button title="Shortcut key: Space" onclick='pycmd("ans");' %(answer_style)s>%(showAnswer_text)s</button>
+%(middleRight_side1)s
+%(middleRight_side2)s
+%(middleRight_side3)s
+%(middleRight_side4)s
+</td></tr></table>''' % dict(remaining=self._remaining(), middleLeft_side1=middleLeftSide_button1, middleLeft_side2=middleLeftSide_button2, middleLeft_side3=middleLeftSide_button3, middleLeft_side4=middleLeftSide_button4,
+ answer_style=showAnswer_style, middleRight_side1=middleRightSide_button1, middleRight_side2=middleRightSide_button2, middleRight_side3=middleRightSide_button3, middleRight_side4=middleRightSide_button4, showAnswer_text=showAnswer_text)
+ # wrap it in a table so it has the same top margin as the ease buttons
+ middle = "%s" % middle
+ if self.card.should_show_timer():
+ maxTime = self.card.time_limit() / 1000
+ else:
+ maxTime = 0
+ self.bottom.web.eval("showQuestion(%s,%d);" % (json.dumps(middle), maxTime))
+ self.bottom.web.adjustHeightToFit()
+
+
+#// replacing/wraping functions
+Reviewer._shortcutKeys = wrap(Reviewer._shortcutKeys, _shortcutKeys_wrap, 'around')
+Reviewer._showAnswerButton = _showAnswerButton
+Reviewer._bottomHTML = _bottomHTML
diff --git a/.local/share/Anki2/addons21/advanced_review/Button_Colors.py b/.local/share/Anki2/addons21/advanced_review/Button_Colors.py new file mode 100644 index 0000000..0560aed --- /dev/null +++ b/.local/share/Anki2/addons21/advanced_review/Button_Colors.py @@ -0,0 +1,211 @@ +#// auth_ Mohamad Janati
+#// Copyright (c) 2019-2021 Mohamad Janati (freaking stupid, right? :|)
+
+from aqt.reviewer import Reviewer
+from aqt import mw
+from . import styles
+
+
+#// getting confing information
+config = mw.addonManager.getConfig(__name__)
+button_style = config[' Review_ Buttons Style']
+custom_colors = config[' Review_ Custom Colors']
+hide_hard = config['Button_ Hide Hard']
+hide_good = config['Button_ Hide Good']
+hide_easy = config['Button_ Hide Easy']
+custom_buttonSize = config['Button_ Custom Button Sizes']
+interval_style = config[' Review_ Interval Style']
+if custom_buttonSize:
+ buttons_height = config['Button_ Height_ All Bottombar Buttons']
+ reviewButtons_width = config['Button_ Width_ Review Buttons']
+ text_size = config['Button_ Text Size']
+else:
+ buttons_height = ""
+ reviewButtons_width = ""
+ text_size = ""
+again_label = config['Button Label_ Again']
+hard_label = config['Button Label_ Hard']
+good_label = config['Button Label_ Good']
+easy_label = config['Button Label_ Easy']
+
+#// getting styles from styles.py
+text_color = styles.text_color
+again_color = styles.again_color
+hard_color = styles.hard_color
+good_color = styles.good_color
+easy_color = styles.easy_color
+background_color = styles.background_color
+custom_text = styles.custom_text
+custom_background = styles.custom_background
+button_styles = styles.button_styles
+active_extra = styles.active_extra
+neon1 = styles.neon1
+neon2 = styles.neon2
+fill1 = styles.fill1
+fill2 = styles.fill2
+
+def _answerButtonList(self):
+ cnt = self.mw.col.sched.answerButtons(self.card)
+ if cnt == 2:
+ #// button = ((ease, "Label"),)
+ again = ((1, " {} ".format(again_label)),)
+ good = ((2, " {} ".format(good_label)),)
+ buttons = again
+ #// don't add button "good" to returned buttons if it's disabled
+ if not hide_good:
+ buttons += good
+ return buttons
+ elif cnt == 3:
+ again = ((1, " {} ".format(again_label)),)
+ good = ((2, " {} ".format(good_label)),)
+ easy = ((3, " {} ".format(easy_label)),)
+ buttons = again
+ if not hide_good:
+ buttons += good
+ if not hide_easy:
+ buttons += easy
+ return buttons
+ else:
+ again = ((1, " {} ".format(again_label)),)
+ hard = ((2, " {} ".format(hard_label)),)
+ good = ((3, " {} ".format(good_label)),)
+ easy = ((4, " {} ".format(easy_label)),)
+ buttons = again
+ if not hide_hard:
+ buttons += hard
+ if not hide_good:
+ buttons += good
+ if not hide_easy:
+ buttons += easy
+ return buttons
+
+
+def _answerButtons(self):
+ cnt = self.mw.col.sched.answerButtons(self.card)
+ default = self._defaultEase()
+ def but(i, label):
+ #// Setting id name for each button based on their ease value
+ if cnt == 2:
+ if i == 1:
+ button_id = "again"
+ elif i == 2:
+ button_id = "good"
+ else:
+ button_id = ""
+ elif cnt == 3:
+ if i == 1:
+ button_id = "again"
+ elif i == 2:
+ button_id = "good"
+ elif i == 3:
+ button_id = "easy"
+ else:
+ button_id = ""
+ elif cnt == 4:
+ if i == 1:
+ button_id = "again"
+ elif i == 2:
+ button_id = "hard"
+ elif i == 3:
+ button_id = "good"
+ elif i == 4:
+ button_id = "easy"
+ else:
+ button_id = ""
+ else:
+ if i == 1:
+ button_id = "again"
+ elif i == 2:
+ button_id = "hard"
+ elif i == 3:
+ button_id = "good"
+ elif i == 4:
+ button_id = "easy"
+ else:
+ button_id = ""
+ due_plain = self._buttonTime(i)
+ inButton_due = ""
+ if interval_style == 1:
+ if button_id == "again":
+ due = "<font color={}>{}</font>".format(again_color, due_plain)
+ elif button_id == "hard":
+ due = "<font color={}>{}</font>".format(hard_color, due_plain)
+ elif button_id == "good":
+ due = "<font color={}>{}</font>".format(good_color, due_plain)
+ elif button_id == "easy":
+ due = "<font color={}>{}</font>".format(easy_color, due_plain)
+ else:
+ if due_plain:
+ due = due_plain
+ else:
+ return
+ elif interval_style == 2:
+ if due_plain:
+ due = "<br>"
+ inButton_due = " | {}".format(due_plain)
+ else:
+ return
+ else:
+ if due_plain:
+ due = due_plain
+ else:
+ return
+ #// Choosing button classes based on what user has chosen in config
+ if button_style == 1 or button_style == 3:
+ if custom_colors:
+ style = custom_background
+ else:
+ style = background_color
+ elif button_style == 4:
+ style = neon1
+ elif button_style == 5:
+ style = neon2
+ elif button_style == 6:
+ style = fill1
+ elif button_style == 7:
+ style = fill2
+ else:
+ if custom_colors:
+ style = custom_text
+ else:
+ style = text_color
+ #// Choosing style for active button
+ if i == default:
+ extra = "style='{}; height: {}px; width: {}px; font-size: {}px;'".format(active_extra, buttons_height, reviewButtons_width, text_size)
+ else:
+ extra = "style='height: {}px; width: {}px; font-size: {}px;'".format(buttons_height, reviewButtons_width, text_size)
+ #// Choosing button styles based on what user has chosen in config
+ if button_style == 2 or button_style == 3:
+ button_class = "wide"
+ #// replacing styling for active button
+ if i == default:
+ extra = "style='{}; border-radius: 3px; height: {}px;'".format(active_extra, buttons_height)
+ else:
+ extra = "style='height: {}px'".format(buttons_height)
+ else:
+ button_class = "mybuttons"
+ if interval_style == 2:
+ bottombar_table = ""
+ else:
+ bottombar_table = ""
+ return style + button_styles + '''
+<td align=center>{0}
+<button title="Shortcut Key: {1}" data-ease="{1}" onclick='pycmd("ease{1}");' class={2} id={3} {4}>{5}{6}</button>
+</td>'''.format(due, i, button_class, button_id, extra, label, inButton_due)
+ #// adjusting the answer button table for wide button
+ if button_style == 2 or button_style == 3:
+ bottombar_width = "80%"
+ else:
+ bottombar_width = ""
+ buf = "<center><table cellpadding=0 cellspacing=0 width={}><tr>".format(bottombar_width)
+ for ease, label in self._answerButtonList():
+ buf += but(ease, label)
+ buf += "</tr></table>"
+ script = """
+<script>$(function () { $("#defease").focus(); });</script>"""
+ return buf + script
+
+
+#// replacing default functions with customized functions here
+Reviewer._answerButtonList = _answerButtonList
+Reviewer._answerButtons = _answerButtons
diff --git a/.local/share/Anki2/addons21/advanced_review/CHANGELOG.md b/.local/share/Anki2/addons21/advanced_review/CHANGELOG.md new file mode 100644 index 0000000..2da5b3d --- /dev/null +++ b/.local/share/Anki2/addons21/advanced_review/CHANGELOG.md @@ -0,0 +1,421 @@ +<html>
+ <div class="background">
+ <h1>2022/2/13</h1>
+ <ul>
+ <li>Added an option to change Card Info Sidebar default position</li>
+ <li>Fixed description button not showing on deck overview screen</li>
+ <li>Fixed a sidebar error caused by rescheduled cards</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2022/1/30</h1>
+ <ul>
+ <li>Changed load settings prompt message (forgot to do it yesterday)</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2022/1/29</h1>
+ <ul>
+ <li>Added a feature to change <font color=dodgerred>button text size</font> [go to "Button Sizes" and change button text size]</li>
+ <li>Added <font color=dodgerred>another style for button intervals</font> (now you can move button intervals inside the buttons) [to change button interval style go to "Styles" tab and change button interval style]</li>
+ <li>Added a feature to enable <font color=dodgerred>direct config edit</font> (serves no purpose for now, don't enable it unless you're told to | The idea is to quickly be able to add new features without having to add options in settings menu)</li>
+ <li>Added an option to <font color=dodgerred>backup your settings</font> (just press Backup Settings button and it will create a backup file of your settings - you can also share your settings and button stylings with other people by sharing the settings file)</li>
+ <li>Added an option to <font color=dodgerred>load settings</font> file (you can load settings and not go through settings and changing different settings and styles)</li>
+ <li>Fixed button tooltip bug in python 3.10 (Thanks to <a href="https://github.com/sdvcrx">@sdvcrx</a>)</li>
+ <li>Removed "Restore Defaults" button (with the new "Load Settings" function, having this extra button doesn't make sense)</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2021/9/22</h1>
+ <ul>
+ <li>Minor macOS bug fix (Hopefully -_-)</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2021/9/15</h1>
+ <ul>
+ <li>Minor bug fix</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2021/8/23</h1>
+ <ul>
+ <li>Detailed Deck Overview bug fix</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2021/8/22</h1>
+ <ul>
+ <li>Added a new method for skipping cards.<br>
+ <ul>
+ <li>This method is partially manual. The skipped cards won't show automatically unless you finish reviewing normal cards.</li>
+ <li>This method uses Anki's "Bury" function and buries skipped cards. The skipped cards will get unburied once you exit review screen or press press <button>Show Skipped</button> Button.</li>
+ <li>If you want for the skipped cards to show mid-review, you'll have to press <button>Show Skipped</button> Button or press the assigned shortcut (default shortcut is <kbd>Alt</kbd> + <kbd>C</kbd>).</li>
+ <li>If you use V3 sheduler, this is the only method that'll work for you and will be chosen by default.</li>
+ <li>If you use V2 scheduler you can use this method or the old method. You can choose the skip method in <code>Settings Menu -> Misc -> Skip Method</code></li>
+ <li>The old method is <font color=red>Next Card</font> and the new method is <font color=red>Bury</font>.</li>
+ <li>The new "Bury" method might be a bit slower, especially when you use the button. If you choose to use this method, I suggets using shortcuts for skipping cards.</li>
+ </ul>
+ <li>Adjusted Settings Menu height for better viewing on screens with lower resulotions</li>
+ <li>Moved changelog from main Settings Menu window to a separate window</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2021/8/4</h1>
+ <ul>
+ <li>Bug fix (now ARBb is compatible with Anki 2.1.45)</li>
+ <li>From now on, No update will be released for Anki versions older than 2.1.45</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2021/7/31</h1>
+ <ul>
+ <li>Bug Fix</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2021/7/30</h1>
+ <ul>
+ <li>Bug Fix</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2021/7/30</h1>
+ <ul>
+ <li>Added an option to set your custom text as button labels.<br>
+ replace again, hard, good, easy, etc. text with your custom text or emoji.<br>
+ To change button labels and use your own custom text, go to "Button label" tab in the settings.<br>
+ To the person asking me how to change button labels -_- you can use this from now on. No need to change the code.</li>
+ <li>Added an option to hide hard, good, easy buttons. (Requested)<br>
+ (no I haven't forgotten to put again in the list -_- you can't hide again button).<br>
+ To use this option, go to "Bottombar Buttons" and look for "Hide Buttons" part there.</li>
+ <li>Added an option to change the roundness of the buttons.<br>
+ To use this option, go to "Styles" tab and look for "Button Border Radius" there.</li>
+ <li><font color=red>Removed</font> pressed button stats from the add-on.<br>
+ For those who used it, I'll be publishing it as a separate add on named "Pressed Button Stats"</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/6/9</h1>
+ <ul>
+ <li>Added an option to turn off more overview stats.<br></li>
+ <font color=#004182>pressed button count STILL at 90%<br></font>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/12/6</h1>
+ <ul>
+ <li>Added another mode to overview stats (taken from "More Overview Stats 2.1")</li>
+ <li>Fixed conflict with speedfocus add-on (If you use speedfocus you need to enable "Speed focus" option in ARBb settings -> Misc)</li>
+ </ul>
+ </div><div class="background">
+ <h1>2020/6/9</h1>
+ <ul>
+ <li>Added an option to turn off more overview stats.<br></li>
+ <font color=#004182>pressed button count STILL at 90%<br></font>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/5/30</h1>
+ <ul>
+ <li>Changed tooltip behavior.<br>
+ Now it's size won't be as size of the buttons when it's position is fixed.<br></li>
+ <font color=#004182>pressed button count STILL at 90%<br></font>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/5/18</h1>
+ <ul>
+ <li>Minor code changes/improvements.<br></li>
+ <font color=#004182>pressed button count STILL at 90%<br></font>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/5/15</h1>
+ <ul>
+ <li>Now it designs review buttons that other add-ons add (like rememorize).<br>
+ it treats them like other bottombar button so their color and style<br>
+ will be like other bottombar buttons</li>
+ <li>you can style other bottombar buttons that are added by other add-on (like deferer button).<br>
+ you'll need to change their code a bit. if you want to style them leave a comment here or on github page.<br>
+ (the last picture is how the extra buttons the those add-on add look after styling them using this add-on)</li>
+ <li>finally a github page :\ <a href="https://github.com/noobj2/Anki-Advanced-Review-Bottombar">Here it is</a></li>
+ <li>Changed color of timer text in bottombar.<br>
+ now it uses the same color you have set for other bottombar buttons text color. (not a big deal though, right?)<br></li>
+ <font color=#004182>pressed button count STILL at 90%<br></font>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/5/9</h1>
+ <ul>
+ <li>Made neon and fill designs customizable. now you can change their colors using "Colors" tab.<br>
+ Enable custom colors by checking "Custom Review Button Colors" checkbox and <br>
+ changing again, hard, good and easy colors.<br>
+ as these designs don't have a separate hover color, changing hover colors won't<br>
+ change anything about these buttons</li>
+ <li>Made review bottombar buttons, deck overview buttons and main screen bottombar buttons customizable. <br>
+ you can change their border and text colors in "Colors" tab by changing "General Button" text and border colors.<br>
+ you can't chage text or background color for general buttons if their button style is set on default.<br>
+ to change general buttons style go to "Styles" tab and change "General Buttons Style".</li>
+ <li>Added an option to change show answer button border color based on card ease. <br>
+ you can enable than option in "Style" tab by changing "Show Answer Border Color Style" <br>
+ from "Fixed" to "Based on Card Ease". you cand change color for each ease range in "Colors" tab.<br>
+ - (honestly i think it's gonna be usless for most of you :/ it was just something that i needed).</li>
+ <li>+ Other settings menu and bottombar buttons changes and improvements.<br></li>
+ <font color=#004182>pressed button count STILL at 90%<br></font>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/4/28</h1>
+ <ul>
+ <li>Added an option to choose card type [learn, review, re-learn, cram] for button count stats</li>
+ <li>Added an option to manually change decks in button count stats<br></li>
+ <font color=#004182>at 90%<br></font>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/5/01</h1>
+ <ul>
+ <li>Added total time and time per card to information shown in pressed button stats<br></li>
+ <font color=#004182>at 85%<br></font>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/4/28</h1>
+ <ul>
+ <li>Added an option to choose card type [learn, review, re-learn, cram] for button count stats</li>
+ <li>Added an option to manually change decks in button count stats<br></li>
+ <font color=#004182>at 80%<br></font>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/4/27</h1>
+ <ul>
+ <li>Added an option to choose time period for button count stats</li>
+ <li>Added an option to change button count stats scope</li>
+ <li>Button count stats window improvements<br></li>
+ <font color=#004182>at 50%<br></font>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/4/26</h1>
+ <ul>
+ <li><font color=tomato>NEW FEATURE:</font> pressed button count + Percent<br>
+ <font color=red>NOTE:</font> it's work in progress and very basic<br>
+ the only reason i'm publishing it is that i want to hear you opinions on it and see what you need<br>
+ I want to hear your ideas about it, tell me what i can do to make it better<br>
+ you can Email me your ideas<br>
+ however, i think some of you may want to change the time period for this option<br>
+ to do that go to config -> Advanced review bottombar -> open add-on folder -> <br>
+ open Button_Count.py -> go to line 47 you'll see what you need there<br>
+ when you're on a deck, it shows pressed button stats for that deck, <br>
+ when you're in main window, it'll show overall stats<br></li>
+ <font color=#004182>at 15%<br></font>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/4/22</h1>
+ <ul>
+ <li>Made styling main screen and deck overview compatible with anki versions older than 2.1.19</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/4/21</h1>
+ <ul>
+ <li>Added an option to change main screen and deck overview buttons style<br>
+ (Their style will be as other bottombar buttons style)</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/4/20</h1>
+ <ul>
+ <li>Fixed tooltip bug (where it would show hard on tooltip when you<br>
+ pressed good if you were in a cutom study session )</li>
+ <li> Added card info sidebar auto open (opens sidebar automatically when you review a card)</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/4/18</h1>
+ <ul>
+ <li>Minor settings menu improvements</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/4/17</h1>
+ <ul>
+ <li>Fixed Neon 1 style bug</li>
+ <li>Addded correct percentage, fastest reveiw, slowest review, note ID and card ID options to card info sidebar</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/4/16</h1>
+ <ul>
+ <li>Added change button transition time option (for fill and neon designs only)</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/4/15</h1>
+ <ul>
+ <li>Added an option to change cursor type when you hover over bottombar buttons</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/4/14</h1>
+ <ul>
+ <li>Added answer tooltips</li>
+ <li>Adjusted tooltips for neon and fill designs</li>
+ <li>Adjusted tooltips for custom button sizes</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/4/13</h1>
+ <ul>
+ <li>Added a function to get shortcuts (Don't have to test keys that you want to set as shortcuts anymore,<br> if it's Anki's default shortcut for something, the add-on wont accept it)</li>
+ <li>Moved button design tooltip to another tab (noticed it was WAY too big for lower resulotions to be useful)</li>
+ <br><br><font color="red"># NOTE:</font> if you're updating from any version other than 2020/4/12 you might run into some problems trying to<br>
+ open settings menu if you can't open settings menu after update open add-on folder and delete meta.json file if<br>
+ that didn't help go to settings.py and put a # in front of the last line then go to tools -> add-ons and<br> press restore defaults on this addon's config page<br>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/4/12</h1>
+ <ul>
+ <li>Changed settings menu so it's easier to work with on lower resolutions (had to code it all over again)</li>
+ <li>Made picking colors completely automatic (no color code copy/paste, choose the color and it's set)</li>
+ <li>Added an option for you to choose settings menu's position</li>
+ <li>Made wide buttons compatible with no distractions add-on</li>
+ <br><br><font color="red"># NOTE:</font> After update you need to restore config to defaults in tools -> addons<br>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/4/8</h1>
+ <ul>
+ <li>settings menu bugs fixes</li>
+ <li>settings menu minor adjustments for smaller screens</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/4/7</h1>
+ <ul>
+ <li>settings menu improvements</li>
+ <li>added an option to color intervals</li>
+ <li>added an option to style other bottombar buttons</li>
+ <li>added 4 new button designs</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/4/6</h1>
+ <ul>
+ <li>minor settings menu improvements</li>
+ <li>card info sidebar improvements for old scheduler</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/4/5</h1>
+ <ul>
+ <li>minor settings menu improvements</li>
+ <li>added tooltips with pictures for different settings</li>
+ <li>fixed card info sidebar crash bug</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/4/4</h1>
+ <ul>
+ <li>added settings menu</li>
+ <li>minor settings menu adjustments</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/4/2</h1>
+ <ul>
+ <li>fix for wide buttons</li>
+ <li>fixed card info sidebar problem with beta versions of anki (2.1.23 and 2.1.24)</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/4/1</h1>
+ <ul>
+ <li>fixed issue with limiting card reviews in card info sidebar</li>
+ <li>added an option to change active button indicator from border to glow and change it's color</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/3/30</h1>
+ <ul>
+ <li>adjusted colors and gradients for background color change for light mode</li>
+ <li>added background shadow for review buttons (enable in config)</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/3/29</h1>
+ <ul>
+ <li>added undo button (enable in config)</li>
+ <li>fixed button color for old scheduler</li>
+ <li>removed conflict with customize keyboard shortcuts add-on</li>
+ <li>removed conflict with speed focus add-on (needs to be enabled in config)</li>
+ <li>removed conflict with slackers add-on</li>
+ <li>added an option to choose text color in review button background color change</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/3/26</h1>
+ <ul>
+ <li>added change button size option</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/3/25</h1>
+ <ul>
+ <li>added change skip and info button position option</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/3/20</h1>
+ <ul>
+ <li>fixed conflict with "replay button on card" add-on</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/3/7</h1>
+ <ul>
+ <li>adjusted the color for review buttons</li>
+ <li>added an option to choose the font for the text in card info side bar in config</li>
+ <li>added an option so you could limit the maximum number of previous reviews that are shown on sidebar for a card</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/3/6</h1>
+ <ul>
+ <li>made the info sidebar customizable, you can choose what you want to see on card info sidebar in config</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/3/4</h1>
+ <ul>
+ <li>fixed not showing review button colors on new in-app night mode</li>
+ <li>adjusted review button text colors for new in-app night mode</li>
+ <li>adjusted wide button widths</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/2/8</h1>
+ <ul>
+ <li>added an option for you to choose the shortcut key for skip and info buttons (in add-on config)</li>
+ <li>added an option to choose the sidebar theme (in add-on config)</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/1/2</h1>
+ <ul>
+ <li>fix for old scheduler</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2019/12/14</h1>
+ <ul>
+ <li>Initial Release</li>
+ </ul>
+ </div>
+
+</body>
+</html>
diff --git a/.local/share/Anki2/addons21/advanced_review/Card_Info.py b/.local/share/Anki2/addons21/advanced_review/Card_Info.py new file mode 100644 index 0000000..3d5031d --- /dev/null +++ b/.local/share/Anki2/addons21/advanced_review/Card_Info.py @@ -0,0 +1,508 @@ +#// auth_ Mohamad Janati
+#// Copyright (c) 2019-2021 Mohamad Janati (freaking stupid, right? :|)
+
+import os
+import io
+
+from anki.hooks import addHook
+from aqt.qt import *
+from aqt.webview import AnkiWebView
+import aqt.stats
+import time
+import datetime
+from anki.lang import _
+from anki.utils import fmtTimeSpan
+from anki.stats import CardStats
+from aqt import *
+from anki.utils import htmlToTextLine
+from anki.collection import _Collection
+from aqt.reviewer import Reviewer
+
+
+#// sidebar functions
+class StatsSidebar(object):
+ def __init__(self, mw):
+ config = mw.addonManager.getConfig(__name__)
+ sidebar_autoOpen = config['Card Info sidebar_ Auto Open']
+ self.mw = mw
+ self.shown = False
+ addHook("showQuestion", self._update)
+ addHook("reviewCleanup", self._update)
+ if sidebar_autoOpen:
+ addHook("showQuestion", self.show)
+
+ def _addDockable(self, title, w):
+ class DockableWithClose(QDockWidget):
+ closed = pyqtSignal()
+ def closeEvent(self, evt):
+ self.closed.emit()
+ QDockWidget.closeEvent(self, evt)
+ dock = DockableWithClose(title, mw)
+ dock.setObjectName(title)
+ dock.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)
+ dock.setFeatures(QDockWidget.AllDockWidgetFeatures)
+ dock.setWidget(w)
+ if mw.width() < 600:
+ mw.resize(QSize(600, mw.height()))
+ config = mw.addonManager.getConfig(__name__)
+ sidebar_defaultPosition = config['Card Info sidebar_ Default Position']
+ if sidebar_defaultPosition == 1:
+ mw.addDockWidget(Qt.LeftDockWidgetArea, dock)
+ else:
+ mw.addDockWidget(Qt.RightDockWidgetArea, dock)
+ return dock
+
+ def _remDockable(self, dock):
+ mw.removeDockWidget(dock)
+
+ def show(self):
+ if not self.shown:
+ class ThinAnkiWebView(AnkiWebView):
+ def sizeHint(self):
+ return QSize(200, 100)
+ self.web = ThinAnkiWebView()
+ self.shown = self._addDockable("Card Info", self.web)
+ self.shown.closed.connect(self._onClosed)
+ self._update()
+
+ def hide(self):
+ if self.shown:
+ self._remDockable(self.shown)
+ self.shown = None
+
+ def toggle(self):
+ if self.shown:
+ self.hide()
+ else:
+ self.show()
+
+ def _onClosed(self):
+ # schedule removal for after evt has finished
+ self.mw.progress.timer(100, self.hide, False)
+
+ # modified _revlogData function
+ def _revlogData_mod(self, card, cs):
+ config = mw.addonManager.getConfig(__name__)
+ sidebar_font = config['Card Info sidebar_ Font']
+ reviewsToShow = config['Card Info sidebar_ number of reviews to show for a card']
+ limited_review_warning_note = config['Card Info sidebar_ warning note']
+ custom_colors = config[' Review_ Custom Colors']
+ again_color = config['Color_ Again']
+ hard_color = config['Color_ Hard']
+ good_color = config['Color_ Good']
+ easy_color = config['Color_ Easy']
+ entries = self.mw.col.db.all("select id/1000.0, ease, ivl, factor, time/1000.0, type from revlog where cid = ?", card.id)
+ if not entries:
+ return ""
+ s = "<div style='text-align: center; font-family: arial; font-weight: bold;'> Reviews </div>"
+ s += ("<style>th {font-family: %s; font-size: 13px;}</style><table width=100%% ><tr><th align=left>%s</th>") % (sidebar_font, "Date")
+ s += ("<th align=center >%s</th>" * 5) % ("Type", "Button", "Interval", "Ease", "Time")
+ cnt = 0
+ for (date, ease, ivl, factor, taken, type) in reversed(entries):
+ cnt += 1
+ s += "<tr><td>%s</td>" % time.strftime("<b>%y/%m/%d</b><br>%H:%M", time.localtime(date))
+ tstr = ["Learn", "Review", "Relearn", "Filtered", "Resched"][type]
+ import anki.stats as st
+
+ fmt = "<span style='color:%s'>%s</span>"
+ if type == 0:
+ tstr = fmt % (st.colLearn, tstr)
+ elif type == 1:
+ tstr = fmt % (st.colMature, tstr)
+ elif type == 2:
+ tstr = fmt % (st.colRelearn, tstr)
+ elif type == 3:
+ tstr = fmt % (st.colCram, tstr)
+ else:
+ tstr = fmt % ("#000", tstr)
+ if ease == 1:
+ tstr = fmt % (st.colRelearn, tstr)
+ ####################
+ int_due = "%s" % time.strftime("%y/%m/%d", time.localtime(date))
+ if ivl > 0:
+ int_due_date = time.localtime(date + (ivl * 24 * 60 * 60))
+ int_due = time.strftime("%y/%m/%d", int_due_date)
+ ####################
+ if ivl == 0:
+ ivl = "0d"
+ elif ivl > 0:
+ ivl = fmtTimeSpan(ivl * 86400, short=True)
+ else:
+ ivl = cs.time(-ivl)
+
+ if not custom_colors:
+ again_color = "#FF1111"
+ hard_color = "#FF9814"
+ good_color = "#33FF2D"
+ easy_color = "#21C0FF"
+ if self.mw.col.sched_ver() == 1 and type == 3:
+ if ease == 1:
+ button = "<div style='color: {};'>Again</div>".format(again_color)
+ elif ease == 2:
+ button = "<div style='color: {};'>Good</div>".format(good_color)
+ elif ease == 3:
+ button = "<div style='color: {};'>Good</div>".format(good_color)
+ elif ease == 4:
+ button = "<div style='color: {};'>Easy</div>".format(easy_color)
+ else:
+ button = "ease: {}".format(ease)
+ elif self.mw.col.sched_ver() == 1 and (type == 0 or type == 2):
+ if ease == 1:
+ button = "<div style='color: {};'>Again</div>".format(again_color)
+ elif ease == 2:
+ button = "<div style='color: {};'>Good</div>".format(good_color)
+ elif ease == 3:
+ button = "<div style='color: {};'>Easy</div>".format(easy_color)
+ elif ease == 4:
+ button = "<div style='color: {};'>Easy</div>".format(easy_color)
+ else:
+ button = "ease: {}".format(ease)
+ else:
+ if ease == 1:
+ button = "<div style='color: {};'>Again</div>".format(again_color)
+ elif ease == 2:
+ button = "<div style='color: {};'>Hard</div>".format(hard_color)
+ elif ease == 3:
+ button = "<div style='color: {};'>Good</div>".format(good_color)
+ elif ease == 4:
+ button = "<div style='color: {};'>Easy</div>".format(easy_color)
+ else:
+ button = "ease: {}".format(ease)
+ s += ("<td align=center>%s</td>" * 5) % (tstr, button, "%s<br>(%s)" %(ivl, int_due), "%d%%" % (factor / 10) if factor else "", cs.time(taken)) + "</tr>"
+ if reviewsToShow != 0:
+ if cnt > int(reviewsToShow) - 1:
+ break
+ else:
+ continue
+ s += "</table>"
+ warning = ""
+ if limited_review_warning_note:
+ if cnt < card.reps:
+ try:
+ a = int(reviewsToShow)
+ warning = """<div style="font-family: consolas; font-size: 12px;"><hr> You have limited previous review information number to "{}" reviews.</div>""".format(reviewsToShow)
+ except ValueError:
+ warning = """<div style="font-family: consolas; font-size: 12px;"><hr>Some of the history is missing. For more information, please see the browser documentation.</div>"""
+ return s + warning
+
+
+ # adds the modified _revlogData function to Reviewer class in aqt.browser
+ Reviewer._revlogData_mod = _revlogData_mod
+
+
+ # modified report function
+ def report_mod(self):
+ from anki import version
+ anki_version = int(version.replace('.', ''))
+ if anki_version > 2119:
+ from aqt.theme import theme_manager
+ config = mw.addonManager.getConfig(__name__)
+
+ infobar_created = config['Card Info sidebar_ Created']
+ infobar_edited = config['Card Info sidebar_ Edited']
+ infobar_firstReview = config['Card Info sidebar_ First Review']
+ infobar_latestReview = config['Card Info sidebar_ Latest Review']
+ infobar_due = config['Card Info sidebar_ Due']
+ infobar_interval = config['Card Info sidebar_ Interval']
+ infobar_ease = config['Card Info sidebar_ Ease']
+ infobar_reviews = config['Card Info sidebar_ Reviews']
+ infobar_lapses = config['Card Info sidebar_ Lapses']
+ infobar_correctPercent = config['Card Info Sidebar_ Correct Percent']
+ infobar_fastestReview = config['Card Info Sidebar_ Fastest Review']
+ infobar_slowestReview = config['Card Info Sidebar_ Slowest Review']
+ infobar_avgTime = config['Card Info sidebar_ Average Time']
+ infobar_totalTime = config['Card Info sidebar_ Total Time']
+ infobar_cardType = config['Card Info sidebar_ Card Type']
+ infobar_noteType = config['Card Info sidebar_ Note Type']
+ infobar_deck = config['Card Info sidebar_ Deck']
+ infobar_tags = config['Card Info sidebar_ Tags']
+ infobar_noteID = config['Card Info Sidebar_ Note ID']
+ infobar_cardID = config['Card Info Sidebar_ Card ID']
+ infobar_sortField = config['Card Info sidebar_ Sort Field']
+
+ c = self.card
+ fmt = lambda x, **kwargs: fmtTimeSpan(x, short=True, **kwargs)
+ self.txt = "<table width=100%>"
+ if infobar_created:
+ self.addLine("Created", time.strftime("%Y-%m-%d | %H:%M", time.localtime(c.id/1000)))
+ if infobar_edited:
+ if c.note().mod != False and time.localtime(c.id/1000) != time.localtime(c.note().mod):
+ self.addLine("Edited", time.strftime("%Y-%m-%d | %H:%M", time.localtime(c.note().mod)))
+ first = self.col.db.scalar("select min(id) from revlog where cid = ?", c.id)
+ last = self.col.db.scalar("select max(id) from revlog where cid = ?", c.id)
+ if first:
+ if infobar_firstReview:
+ self.addLine("First Review", time.strftime("%Y-%m-%d | %H:%M", time.localtime(first/1000)))
+ if infobar_latestReview:
+ self.addLine("Latest Review", time.strftime("%Y-%m-%d | %H:%M", time.localtime(last/1000)))
+ if c.type != 0:
+ if c.odid or c.queue < 0:
+ next = None
+ else:
+ if c.queue in (2,3):
+ next = time.time()+((c.due - self.col.sched.today)*86400)
+ else:
+ next = c.due
+ next = self.date(next)
+ if next:
+ if infobar_due:
+ self.addLine("Due", next)
+ if c.queue == 2:
+ if infobar_interval:
+ self.addLine("Interval", fmt(c.ivl * 86400))
+ if infobar_ease:
+ self.addLine("Ease", "%d%%" % (c.factor/10.0))
+ if infobar_lapses:
+ self.addLine("Lapses", "%d" % c.lapses)
+ if self.col.sched_ver() == 1:
+ pressed_again = mw.col.db.scalar("select sum(case when ease = 1 then 1 else 0 end) from revlog where cid = ?", c.id)
+ pressed_good = mw.col.db.scalar("select sum(case when ease = 2 then 1 else 0 end) from revlog where cid = ?", c.id)
+ pressed_easy = mw.col.db.scalar("select sum(case when ease = 3 then 1 else 0 end) from revlog where cid = ?", c.id)
+ pressed_all = pressed_again + pressed_good + pressed_easy
+ self.addLine("Again", "{} | {:.0f}%".format(str(pressed_again).rjust(4), float(pressed_again/pressed_all)*100))
+ self.addLine("Good", "{} | {:.0f}%".format(str(pressed_good).rjust(4), float(pressed_good/pressed_all)*100))
+ self.addLine("Easy", "{} | {:.0f}%".format(str(pressed_easy).rjust(4), float(pressed_easy/pressed_all)*100))
+ elif self.col.sched_ver() == 2:
+ pressed_again = mw.col.db.scalar("select sum(case when ease = 1 then 1 else 0 end) from revlog where cid = ?", c.id)
+ pressed_hard = mw.col.db.scalar("select sum(case when ease = 2 then 1 else 0 end) from revlog where cid = ?", c.id)
+ pressed_good = mw.col.db.scalar("select sum(case when ease = 3 then 1 else 0 end) from revlog where cid = ?", c.id)
+ pressed_easy = mw.col.db.scalar("select sum(case when ease = 4 then 1 else 0 end) from revlog where cid = ?", c.id)
+ pressed_all = pressed_again + pressed_hard + pressed_good + pressed_easy
+ if pressed_all < 1:
+ pressed_all = 1
+ self.addLine("Again", "{} | {:.0f}%".format(str(pressed_again).rjust(4), float(pressed_again/pressed_all)*100))
+ self.addLine("Hard", "{} | {:.0f}%".format(str(pressed_hard).rjust(4), float(pressed_hard/pressed_all)*100))
+ self.addLine("Good", "{} | {:.0f}%".format(str(pressed_good).rjust(4), float(pressed_good/pressed_all)*100))
+ self.addLine("Easy", "{} | {:.0f}%".format(str(pressed_easy).rjust(4), float(pressed_easy/pressed_all)*100))
+ if infobar_reviews:
+ self.addLine("Reviews", "%d" % c.reps)
+ (cnt, total) = self.col.db.first("select count(), sum(time)/1000 from revlog where cid = ?", c.id)
+ if infobar_correctPercent and c.reps > 0:
+ self.addLine("Correct Percentage", "{:.0f}%".format(float((c.reps-c.lapses)/c.reps)*100))
+ if infobar_fastestReview:
+ fastes_rev = mw.col.db.scalar("select time/1000.0 from revlog where cid = ? order by time asc limit 1", c.id)
+ self.addLine("Fastest Review", self.time(fastes_rev))
+ if infobar_slowestReview:
+ slowest_rev = mw.col.db.scalar("select time/1000.0 from revlog where cid = ? order by time desc limit 1", c.id)
+ self.addLine("Slowest Review", self.time(slowest_rev))
+ if cnt:
+ if infobar_avgTime:
+ self.addLine("Average Time", self.time(total / float(cnt)))
+ if infobar_totalTime:
+ self.addLine("Total Time", self.time(total))
+ elif c.queue == 0:
+ if infobar_due:
+ self.addLine("Position", c.due)
+ if infobar_cardType:
+ self.addLine("Card Type", c.template()['name'])
+ if infobar_noteType:
+ self.addLine("Note Type", c.model()['name'])
+ if infobar_noteID:
+ self.addLine("Note ID", c.nid)
+ if infobar_cardID:
+ self.addLine("Card ID", c.id)
+ if infobar_deck:
+ self.addLine("Deck", self.col.decks.name(c.did))
+ if c.note().tags:
+ if infobar_tags:
+ self.addLine("Tags", " | ".join(c.note().tags))
+ f = c.note()
+ sort_field = htmlToTextLine(f.fields[self.col.models.sortIdx(f.model())])
+ if infobar_sortField:
+ if len(sort_field) > 40:
+ self.addLine("Sort Field", "[{}<br>{}<br>{}...]".format(sort_field[:20], sort_field[20:41], sort_field[41:58]))
+ else:
+ self.addLine("Sort Field", htmlToTextLine(f.fields[self.col.models.sortIdx(f.model())]))
+ self.txt += "</table>"
+ return self.txt
+
+
+ # adds the modified report functions to CardStats class in anki.stats
+ CardStats.report_mod = report_mod
+
+
+ # modified cardStats function
+ def cardStats_mod(self, card):
+ from anki.stats import CardStats
+ return CardStats(self, card).report_mod()
+
+
+ # adds a modified cardStats function to _Collection class in anki.collection
+ _Collection.cardStats_mod = cardStats_mod
+
+
+ # functions to get more previous cards to add them to sidebard
+ def lastCard2(self):
+ if self._answeredIds:
+ if len(self._answeredIds) > 1:
+ try:
+ return self.mw.col.getCard(self._answeredIds[-2])
+ except TypeError:
+ return
+ def lastCard3(self):
+ if self._answeredIds:
+ if len(self._answeredIds) > 2:
+ try:
+ return self.mw.col.getCard(self._answeredIds[-3])
+ except TypeError:
+ return
+ def lastCard4(self):
+ if self._answeredIds:
+ if len(self._answeredIds) > 3:
+ try:
+ return self.mw.col.getCard(self._answeredIds[-4])
+ except TypeError:
+ return
+
+ # adds functions above to Reviewer class in aqt.reviewer
+ Reviewer.lastCard2 = lastCard2
+ Reviewer.lastCard3 = lastCard3
+ Reviewer.lastCard4 = lastCard4
+
+
+ def _update(self):
+ config = mw.addonManager.getConfig(__name__)
+ infobar_currentReviewCount = config['Card Info sidebar_ Current Review Count']
+ try:
+ sidebar_PreviousCards = int(config['Card Info sidebar_ Number of previous cards to show'])
+ except ValueError:
+ sidebar_PreviousCards = 2
+ if not self.shown:
+ return
+ txt = ""
+ r = self.mw.reviewer
+ d = self.mw.col
+ cs = CardStats(d, r.card)
+ current_card = r.card
+ review_count = len(self.mw.reviewer._answeredIds)
+ styles = """<style>
+ .title {
+ font-family: arial;
+ padding-bottom: 15px;
+ font-weight: bold;
+ }</style>"""
+ currentReviewCount = "<div class='title'>Current Card</div><div style='font-family: courier; font-size: 10px;'>Current Review Count: {}</div>".format(review_count)
+ if current_card:
+ txt += styles
+ if infobar_currentReviewCount:
+ txt += currentReviewCount
+ else:
+ txt += "<div class='title'>Current Card</div>"
+ txt += d.cardStats_mod(current_card)
+ txt += "<p>"
+ txt += r._revlogData_mod(current_card, cs)
+ card2 = r.lastCard()
+ if card2 and sidebar_PreviousCards > 1:
+ if sidebar_PreviousCards == 2:
+ txt += "<hr><div class='title'>Last Card</div>"
+ else:
+ txt += "<hr><div class='title'>Card 2</div>"
+ txt += d.cardStats_mod(card2)
+ txt += "<p>"
+ txt += r._revlogData_mod(card2, cs)
+ if sidebar_PreviousCards < 3:
+ if infobar_currentReviewCount:
+ txt += currentReviewCount
+ card3 = r.lastCard2()
+ if card3 and sidebar_PreviousCards > 2:
+ txt += "<hr><div class='title''>Card 3</div>"
+ txt += d.cardStats_mod(card3)
+ txt += "<p>"
+ txt += r._revlogData_mod(card3, cs)
+ if sidebar_PreviousCards < 4:
+ if infobar_currentReviewCount:
+ txt += currentReviewCount
+ card4 = r.lastCard3()
+ if card4 and sidebar_PreviousCards > 3:
+ txt += "<hr><div class='title''>Card 4</div>"
+ txt += d.cardStats_mod(card4)
+ txt += "<p>"
+ txt += r._revlogData_mod(card4, cs)
+ if infobar_currentReviewCount:
+ txt += currentReviewCount
+ if not txt:
+ styles = """<style>
+ .title {
+ font-family: arial;
+ padding-bottom: 15px;
+ font-weight: bold;
+ }</style>"""
+ txt = styles
+ card2 = r.lastCard()
+ if card2 and sidebar_PreviousCards > 1:
+ txt += "<div class='title'>Last Card</div>"
+ txt += d.cardStats_mod(card2)
+ txt += "<p>"
+ txt += r._revlogData_mod(card2, cs)
+ if sidebar_PreviousCards < 3:
+ if infobar_currentReviewCount:
+ txt += currentReviewCount
+ card3 = r.lastCard2()
+ if card3 and sidebar_PreviousCards > 2:
+ txt += "<hr><div class='title''>Card 2</div>"
+ txt += d.cardStats_mod(card3)
+ txt += "<p>"
+ txt += r._revlogData_mod(card3, cs)
+ if sidebar_PreviousCards < 4:
+ if infobar_currentReviewCount:
+ txt += currentReviewCount
+ card4 = r.lastCard3()
+ if card4 and sidebar_PreviousCards > 3:
+ txt += "<hr><div class='title''>Card 3</div>"
+ txt += d.cardStats_mod(card4)
+ txt += "<p>"
+ txt += r._revlogData_mod(card4, cs)
+ if infobar_currentReviewCount:
+ txt += currentReviewCount
+ style = self._style()
+ self.web.setHtml("""
+<html>
+ <head>
+ <style>%s</style>
+ </head>
+ <body>
+ <center>%s</center>
+ </body>
+</html>
+"""% (style, txt))
+
+
+ def _style(self):
+ from anki import version
+ anki_version = int(version.replace('.', ''))
+ if anki_version > 2119:
+ from aqt.theme import theme_manager
+ config = mw.addonManager.getConfig(__name__)
+ sidebar_theme = config['Card Info sidebar_ theme']
+ sidebar_font = config['Card Info sidebar_ Font']
+ from . import styles
+ dark_styles = styles.dark
+ light_styles = styles.light
+ if anki_version > 2119:
+ if sidebar_theme == 2:
+ mystyle = dark_styles
+ elif sidebar_theme == 1:
+ mystyle = light_styles
+ else:
+ if theme_manager.night_mode:
+ mystyle = dark_styles
+ else:
+ mystyle = light_styles
+ else:
+ if sidebar_theme == 2:
+ mystyle = dark_styles
+ else:
+ mystyle = light_styles
+
+
+ from anki import version
+ if version.startswith("2.0."):
+ return ""
+ return mystyle + "td { font-size: 75%; font-family:" + "{}".format(sidebar_font) + ";}"
+
+
+_cs = StatsSidebar(mw)
+
+
+def cardStats(on):
+ _cs.toggle()
diff --git a/.local/share/Anki2/addons21/advanced_review/Deck_Overview.py b/.local/share/Anki2/addons21/advanced_review/Deck_Overview.py new file mode 100644 index 0000000..9b53121 --- /dev/null +++ b/.local/share/Anki2/addons21/advanced_review/Deck_Overview.py @@ -0,0 +1,646 @@ +
+
+import time
+from datetime import date, timedelta
+from aqt import mw
+from copy import deepcopy
+from aqt.utils import shortcut, showInfo, tr
+from anki import version
+anki_version = int(version.replace('.', ''))
+if anki_version > 2119:
+ from aqt.deckbrowser import DeckBrowserBottomBar
+ from aqt.overview import OverviewBottomBar
+from aqt.deckbrowser import DeckBrowser
+from aqt.overview import Overview
+from . import styles
+
+config = mw.addonManager.getConfig(__name__)
+studyNow_label = config['Button Label_ Study Now']
+more_overviewStats = config[' More Overview Stats']
+bottombarButtons_style = config[' Review_ Bottombar Buttons Style']
+style_mainScreenButtons = config[' Style Main Screen Buttons']
+
+bottombar_neon1 = styles.bottombar_neon1
+bottombar_neon2 = styles.bottombar_neon2
+bottombar_fill1 = styles.bottombar_fill1
+bottombar_fill2 = styles.bottombar_fill2
+
+#// Chosing stylinhg for review other buttons in reviewer bottombar based on chosen style
+if bottombarButtons_style == 0:
+ bottomHTML_style = "<style></style>"
+elif bottombarButtons_style == 1:
+ bottomHTML_style = bottombar_neon1
+elif bottombarButtons_style == 2:
+ bottomHTML_style = bottombar_neon2
+elif bottombarButtons_style == 3:
+ bottomHTML_style = bottombar_fill1
+elif bottombarButtons_style == 4:
+ bottomHTML_style = bottombar_fill2
+
+#// Main Screen Bottombar Buttons
+def _drawButtons(self):
+ buf = "{}".format(bottomHTML_style)
+ if style_mainScreenButtons:
+ #// style='height: px' -> to prevent changing main screen buttons heights
+ # based on height defined in #main {}
+ mainScreen_style = """id=main style='height: px' """
+ else:
+ mainScreen_style = ""
+ drawLinks = deepcopy(self.drawLinks)
+ for b in drawLinks:
+ b.insert(0, "{}".format(mainScreen_style))
+ if b[0]:
+ b[0] = ("Shortcut key: %s") % shortcut(b[0])
+ buf += """
+<button %s title='%s' onclick='pycmd(\"%s\");'>%s</button>""" % (tuple(b))
+ if anki_version > 2121:
+ self.bottom.draw(
+ buf=buf,
+ link_handler=self._linkHandler,
+ web_context=DeckBrowserBottomBar(self),
+ )
+ else:
+ self.bottom.draw(buf)
+ self.bottom.web.onBridgeCmd = self._linkHandler
+
+#// Deck Overview Bottombar Buttons
+def _renderBottom(self):
+ links = [
+ ["O", "opts", tr.actions_options()],
+ ]
+ if self.mw.col.decks.current()["dyn"]:
+ links.append(["R", "refresh", tr.actions_rebuild()])
+ links.append(["E", "empty", tr.studying_empty()])
+ else:
+ links.append(["C", "studymore", tr.actions_custom_study()])
+ # links.append(["F", "cram", ("Filter/Cram")])
+ if self.mw.col.sched.haveBuried():
+ links.append(["U", "unbury", tr.studying_unbury()])
+ links.append(["", "description", tr.scheduling_description()])
+ buf = "{}".format(bottomHTML_style)
+ if style_mainScreenButtons:
+ #// style='height: px' -> to prevent changing main screen buttons heights
+ # based on height defined in #main {}
+ mainScreen_style = """id=main style='height: px' """
+ else:
+ mainScreen_style = ""
+ for b in links:
+ b.insert(0, "{}".format(mainScreen_style))
+ if b[0]:
+ b[0] = ("Shortcut key: %s") % shortcut(b[0])
+ buf += """
+<button %s title="%s" onclick='pycmd("%s")'>%s</button>""" % tuple(b)
+ if anki_version > 2121:
+ self.bottom.draw(
+ buf=buf,
+ link_handler=self._linkHandler,
+ web_context=OverviewBottomBar(self)
+ )
+ else:
+ self.bottom.draw(buf)
+ self.bottom.web.onBridgeCmd = self._linkHandler
+
+
+#// Deck Overview Study Now Button | code from more overview stats to add more overview stats, OBVIOUSLY
+if more_overviewStats == 1:
+ def _table(self):
+ """Returns html table with more statistics than before."""
+ sched = self.mw.col.sched
+ deck = self.mw.col.decks.current()
+ dconf = self.mw.col.decks.confForDid(deck.get('id'))
+ but = self.mw.button
+
+ # Get default counts
+ # 0 = new, 1 = learn, 2 = review
+ counts = list(sched.counts())
+ finished = not sum(counts)
+ counts = _limit(counts)
+
+ totals = [
+ #new
+ sched.col.db.scalar("""
+ select count() from (select id from cards where did = %s
+ and queue = 0)""" % deck.get('id')),
+ # learn
+ sched.col.db.scalar("""
+ select count() from (select id from cards where did = %s
+ and queue in (1,3))""" % deck.get('id')),
+ # review
+ sched.col.db.scalar("""
+ select count() from (select id from cards where did = %s
+ and queue = 2)""" % deck.get('id')),
+ # suspended
+ sched.col.db.scalar("""
+ select count() from (select id from cards where did = %s
+ and queue = -1)""" % deck.get('id')),
+ # buried
+ sched.col.db.scalar("""
+ select count() from (select id from cards where did = %s
+ and queue = -2)""" % deck.get('id')),
+ ]
+
+ if (dconf.get('new')):
+ dueTomorrow = _limit([
+ # new
+ min(dconf.get('new').get('perDay'), totals[0]),
+ # review
+ sched.col.db.scalar("""
+ select count() from cards where did = %s and queue = 3
+ and due = ?""" % deck.get('id'), sched.today + 1),
+ sched.col.db.scalar("""
+ select count() from cards where did = %s and queue = 2
+ and due = ?""" % deck.get('id'), sched.today + 1)
+ ])
+
+ html = ''
+
+ # Style if less than 2.1.20
+ if (int(version.replace('.', '')) < 2120):
+ html += '''
+ <style>
+ .new-count {color: #00a}
+ .learn-count {color: #C35617}
+ .review-count {color: #0a0}
+ </style>'''
+
+ # No need to show due if we have finished collection today
+ if finished:
+ mssg = sched.finishedMsg()
+ html += '''
+ <div style="white-space: pre-wrap;">%s</div>
+ <table cellspacing=5>''' % mssg
+ else:
+ html +='''%s
+ <table cellpadding=5>
+ <tr><td align=center valign=top nowrap="nowrap">
+ <table cellspacing=5>
+ <tr><td nowrap="nowrap">%s:</td><td align=right>
+ <span title="new" class="new-count">%s</span>
+ <span title="learn" class="learn-count">%s</span>
+ <span title="review" class="review-count">%s</span>
+ </td></tr>''' % (bottomHTML_style, tr.browsing_sidebar_due_today(), counts[0], counts[1], counts[2])
+
+ if (dconf.get('new')):
+ html += '''
+ <tr><td nowrap="nowrap">%s:</td><td align=right>
+ <span title="new" class="new-count">%s</span>
+ <span title="learn" class="learn-count">%s</span>
+ <span title="review" class="review-count">%s</span>
+ </td></tr>''' % (tr.statistics_due_tomorrow(), dueTomorrow[0],
+ dueTomorrow[1], dueTomorrow[2])
+
+ html += '''
+ <tr>
+ <td nowrap="nowrap">%s:</td>
+ <td align=right nowrap="nowrap">
+ <span title="new" class="new-count">%s</span>
+ <span title="learn" class="learn-count">%s</span>
+ <span title="review" class="review-count">%s</span>
+ <span title="buried" style="color:#ffa500">%s</span>
+ <span title="suspended" style="color:#adb300">%s</span>
+ </td>
+ </tr>
+ </table>''' % (tr.statistics_counts_total_cards(), totals[0], totals[1], totals[2], totals[4],
+ totals[3])
+
+ if not finished:
+ if style_mainScreenButtons:
+ #// style='height: px' -> to prevent changing main screen buttons heights
+ # based on height defined in #main {}
+ mainScreen_style = """id=main style='height: px' """
+ else:
+ mainScreen_style = ""
+ if style_mainScreenButtons:
+ studyButton_id = "main"
+ else:
+ studyButton_id = "study"
+ html += '''</td>
+ <td align=center nowrap="nowrap">%s</td>
+ </tr></table>''' % (but("study", ("{}".format(studyNow_label)), id="{}".format(studyButton_id), extra="autofocus"))
+
+ return html
+
+elif more_overviewStats == 2:
+ def _table(self):
+ stat_colors = {
+ "New" : "#00a",
+ "Learning" : "#a00",
+ "Review" : "#080",
+ "Percent" : "#888",
+ "Mature" : "#0051ff",
+ "Young" : "#0051ff",
+ "Learned" : "#080",
+ "Unseen" : "#a00",
+ "Suspended" : "#e7a100",
+ "Done on Date" : "#ddd",
+ "Days until done" : "#ddd",
+ "Total" : "#ddd",
+ }
+ date_format = "%d.%m.%Y"
+ correction_for_notes = 1
+ last_match_length = 0
+ current_deck_name = self.mw.col.decks.current()['name']
+ date_format = "%m/%d/%Y"
+
+ try:
+ learn_per_day = self.mw.col.decks.confForDid(self.mw.col.decks.current()['id'])['new']['perDay']
+ except:
+ learn_per_day = 0
+
+ total, mature, young, unseen, suspended, due = self.mw.col.db.first(
+ u'''
+ select
+ -- total
+ count(id),
+ -- mature
+ sum(case when queue = 2 and ivl >= 21
+ then 1 else 0 end),
+ -- young / learning
+ sum(case when queue in (1, 3) or (queue = 2 and ivl < 21)
+ then 1 else 0 end),
+ -- unseen
+ sum(case when queue = 0
+ then 1 else 0 end),
+ -- suspended
+ sum(case when queue < 0
+ then 1 else 0 end),
+ -- due
+ sum(case when queue = 1 and due <= ?
+ then 1 else 0 end)
+ from cards where did in {:s}
+ '''.format(self.mw.col.sched._deckLimit()), round(time.time())
+ )
+ if not total:
+ return u'<p> No Cards Found.</p>'
+
+ scheduled_counts = list(self.mw.col.sched.counts())
+ deck_is_finished = not sum(scheduled_counts)
+
+ cards = {}
+
+ cards['mature'] = mature // int(correction_for_notes)
+ cards['young'] = young // int(correction_for_notes)
+ cards['unseen'] = unseen // int(correction_for_notes)
+ cards['suspended'] = suspended // int(correction_for_notes)
+
+ cards['total'] = total // int(correction_for_notes)
+ cards['learned'] = cards['mature'] + cards['young']
+ cards['unlearned'] = cards['total'] - cards['learned']
+
+ cards['new'] = scheduled_counts[0]
+ cards['learning'] = scheduled_counts[1]
+ cards['review'] = scheduled_counts[2]
+ # cards['due'] = due + cards['review']
+
+ cards['total_without_suspended'] = cards['total'] - cards['suspended']
+
+ try:
+ daysUntilDone = math.ceil(cards['unseen'] / learn_per_day)
+ except:
+ daysUntilDone = 0
+
+ try:
+ cards['doneDate'] = (date.today()+timedelta(days=daysUntilDone)).strftime(date_format)
+ except:
+ showInfo("Unsupported date format. Defaulting to Day.Month.Year instead. Use one of the shorthands: \"us\", \"asia\" or \"eu\", or specify the date like \"\%d.\%m.\%Y\", \"\%m/\%d/\%Y\" etc.\n For more information check the table at: https://docs.python.org/2/library/datetime.html#strftime-and-strptime-behavior", type="warning", title="More Overview Stats 2.1 Warning")
+ print(date_format)
+ cards['doneDate'] = (date.today()+timedelta(days=daysUntilDone)).strftime("%d.%m.%Y")
+
+ cards['daysLeft'] = daysUntilDone
+
+ if(daysUntilDone == 1):
+ cards['daysLeft'] = '{} day'.format(daysUntilDone)
+ else:
+ cards['daysLeft'] = '{} days'.format(daysUntilDone)
+
+ cards_percent = {}
+
+ cards_percent['mature'] = cards['mature'] * 1.0 / cards['total']
+ cards_percent['young'] = cards['young'] * 1.0 / cards['total']
+ cards_percent['unseen'] = cards['unseen'] * 1.0 / cards['total']
+ cards_percent['suspended'] = cards['suspended'] * 1.0 / cards['total']
+
+ cards_percent['total'] = 1.0
+ cards_percent['learned'] = cards['learned'] * 1.0 / cards['total']
+ cards_percent['unlearned'] = cards['unlearned'] * 1.0 / cards['total']
+
+ cards_percent['new'] = cards['new'] * 1.0 / cards['total']
+ cards_percent['learning'] = cards['learning'] * 1.0 / cards['total']
+ cards_percent['review'] = cards['review'] * 1.0 / cards['total']
+ # cards_percent['due'] = cards['due'] * 1.0 / cards['total']
+
+ cards_percent_without_suspended = {}
+
+ if(cards['total_without_suspended'] != 0):
+ cards_percent_without_suspended['mature'] = cards['mature'] * 1.0 / cards['total_without_suspended']
+ cards_percent_without_suspended['young'] = cards['young'] * 1.0 / cards['total_without_suspended']
+ cards_percent_without_suspended['unseen'] = cards['unseen'] * 1.0 / cards['total_without_suspended']
+ cards_percent_without_suspended['suspended'] = cards['suspended'] * 1.0 / cards['total_without_suspended']
+
+ cards_percent_without_suspended['total'] = 1.0
+ cards_percent_without_suspended['learned'] = cards['learned'] * 1.0 / cards['total_without_suspended']
+ cards_percent_without_suspended['unlearned'] = cards['unlearned'] * 1.0 / cards['total_without_suspended']
+
+ cards_percent_without_suspended['new'] = cards['new'] * 1.0 / cards['total_without_suspended']
+ cards_percent_without_suspended['learning'] = cards['learning'] * 1.0 / cards['total_without_suspended']
+ cards_percent_without_suspended['review'] = cards['review'] * 1.0 / cards['total_without_suspended']
+ else:
+ cards_percent_without_suspended['mature'] = 0
+ cards_percent_without_suspended['young'] = 0
+ cards_percent_without_suspended['unseen'] = 0
+ cards_percent_without_suspended['suspended'] = 0
+
+ cards_percent_without_suspended['total'] = 1.0
+ cards_percent_without_suspended['learned'] = 0
+ cards_percent_without_suspended['unlearned'] = 0
+
+ cards_percent_without_suspended['new'] = 0
+ cards_percent_without_suspended['learning'] = 0
+ cards_percent_without_suspended['review'] = 0
+
+ labels = {}
+
+ labels['mature'] = tr.statistics_counts_mature_cards()
+ labels['young'] = tr.statistics_counts_young_cards()
+ labels['unseen'] = ('Unseen')
+ labels['suspended'] = tr.statistics_counts_suspended_cards()
+
+ labels['total'] = tr.statistics_counts_total_cards()
+ labels['learned'] = ('Learned')
+ labels['unlearned'] = ('Unlearned')
+
+ labels['new'] = tr.statistics_counts_new_cards()
+ labels['learning'] = tr.statistics_counts_learning_cards()
+ labels['review'] = ('Review')
+ # labels['due'] = ('Due')
+
+ labels['doneDate'] = ('Done in')
+
+ for key in labels:
+ labels[key] = u'{:s}:'.format(labels[key])
+
+ button = self.mw.button
+
+ output_table = u'''
+ <style type="text/css">
+ <!--
+ hr {
+ height: 1px;
+ border: none;
+ border-top: 1px solid #aaa;
+ }
+
+ td {
+ vertical-align: top;
+ }
+
+ td.row1 {
+ text-align: left;
+ }
+
+ td.row2 {
+ text-align: right;
+ padding-left: 1.2em;
+ padding-right: 1.2em;
+ }
+
+ td.row3 {
+ text-align: left;
+ padding-left: 1.2em;
+ padding-right: 1.2em;
+ }
+
+ td.row4 {
+ text-align: right;
+ }
+
+ td.new {
+ font-weight: bold;
+ color: ''' + stat_colors["New"] + ''';
+ }
+
+ td.learning {
+ font-weight: bold;
+ color: ''' + stat_colors["Learning"] + ''';
+ }
+
+ td.review {
+ font-weight: bold;
+ color: ''' + stat_colors["Review"] + ''';
+ }
+
+ td.percent {
+ font-weight: normal;
+ color: ''' + stat_colors["Percent"] + ''';
+ }
+
+ td.mature {
+ font-weight: normal;
+ color: ''' + stat_colors["Mature"] + ''';
+ }
+
+ td.young {
+ font-weight: normal;
+ color: ''' + stat_colors["Young"] + ''';
+ }
+
+ td.learned {
+ font-weight: normal;
+ color: ''' + stat_colors["Learned"] + ''';
+ }
+
+ td.unseen {
+ font-weight: normal;
+ color: ''' + stat_colors["Unseen"] + ''';
+ }
+
+ td.suspended {
+ font-weight: normal;
+ color: ''' + stat_colors["Suspended"] + ''';
+ }
+
+ td.doneDate {
+ font-weight: bold;
+ color: ''' + stat_colors["Done on Date"] + ''';
+ }
+
+ td.daysLeft {
+ font-weight: bold;
+ color: ''' + stat_colors["Days until done"] + ''';
+ }
+
+ td.total {
+ font-weight: bold;
+ color: ''' + stat_colors["Total"] + ''';
+ }
+ -->
+ </style>
+
+ <table cellspacing="2">
+ '''
+
+ if not deck_is_finished:
+ output_table += u'''
+ <tr>
+ <td class="row1">{label[new]:s}</td>
+ <td class="row2 new">{cards[new]:d}</td>
+ <td class="row3 percent">{percent[new]:.0%}</td>
+ <td class="row4 percent">{percent2[new]:.0%}</td>
+ </tr>
+ <tr>
+ <td class="row1">{label[learning]:s}</td>
+ <td class="row2 learning">{cards[learning]:d}</td>
+ <td class="row3 percent">{percent[learning]:.0%}</td>
+ <td class="row4 percent">{percent2[learning]:.0%}</td>
+ </tr>
+ <tr>
+ <td class="row1">{label[review]:s}</td>
+ <td class="row2 review">{cards[review]:d}</td>
+ <td class="row3 percent">{percent[review]:.0%}</td>
+ <td class="row4 percent">{percent2[review]:.0%}</td>
+ </tr>
+ <tr>
+ <td colspan="4"><hr /></td>
+ </tr>
+ '''.format(label=labels, cards=cards, percent=cards_percent, percent2=cards_percent_without_suspended)
+ output_table += u'''
+ <tr>
+ <td class="row1">{label[mature]:s}</td>
+ <td class="row2 mature">{cards[mature]:d}</td>
+ <td class="row3 percent">{percent[mature]:.0%}</td>
+ <td class="row4 percent">{percent2[mature]:.0%}</td>
+ </tr>
+ <tr>
+ <td class="row1">{label[young]:s}</td>
+ <td class="row2 young">{cards[young]:d}</td>
+ <td class="row3 percent">{percent[young]:.0%}</td>
+ <td class="row4 percent">{percent2[young]:.0%}</td>
+ </tr>
+ <tr>
+ <td colspan="4"><hr /></td>
+ </tr>
+ <tr>
+ <td class="row1">{label[learned]:s}</td>
+ <td class="row2 learned">{cards[learned]:d}</td>
+ <td class="row3 percent">{percent[learned]:.0%}</td>
+ <td class="row4 percent">{percent2[learned]:.0%}</td>
+ </tr>
+ <tr>
+ <td class="row1">{label[unseen]:s}</td>
+ <td class="row2 unseen">{cards[unseen]:d}</td>
+ <td class="row3 percent">{percent[unseen]:.0%}</td>
+ <td class="row4 percent">{percent2[unseen]:.0%}</td>
+ </tr>
+ <tr>
+ <td class="row1">{label[suspended]:s}</td>
+ <td class="row2 suspended">{cards[suspended]:d}</td>
+ <td class="row3 percent">{percent[suspended]:.0%}</td>
+ <td class="row4 percent">ignored</td>
+ </tr>
+ <tr>
+ <td colspan="4"><hr /></td>
+ </tr>
+ <tr>
+ <td class="row1">{label[total]:s}</td>
+ <td class="row2 total">{cards[total]:d}</td>
+ <td class="row3 percent">{percent[total]:.0%}</td>
+ <td class="row4 percent">{percent2[total]:.0%}</td>
+ </tr>
+ <td colspan="4"><hr /></td>
+ <tr>
+ <td class="row1">{label[doneDate]:s}</td>
+ <td class="row2 daysLeft">{cards[daysLeft]:s}</td>
+ <td class="row3">on:</td>
+ <td class="row4 doneDate">{cards[doneDate]:s}</td>
+ </tr>
+ '''.format(label=labels, cards=cards, percent=cards_percent, percent2=cards_percent_without_suspended)
+
+ output = ''
+
+ if deck_is_finished:
+ if (config == None or not 'options' in config) or (config['options'].get('Show table for finished decks', True)):
+ output += output_table
+ output += u'''
+ </table>
+ <hr style="margin: 1.5em 0; border-top: 1px dotted #888;" />
+ '''
+ output += u'''
+ <div style="white-space: pre-wrap;">{:s}</div>
+ '''.format(self.mw.col.sched.finishedMsg())
+ else:
+ if style_mainScreenButtons:
+ #// style='height: px' -> to prevent changing main screen buttons heights
+ # based on height defined in #main {}
+ mainScreen_style = """id=main style='height: px' """
+ else:
+ mainScreen_style = ""
+ if style_mainScreenButtons:
+ studyButton_id = "main"
+ else:
+ studyButton_id = "study"
+
+ output += output_table
+ output += bottomHTML_style
+ output += u'''
+ <tr>
+ <td colspan="4" style="text-align: center; padding-top: 0.6em;">{button:s}</td>
+ </tr>
+ </table>
+ '''.format(button=button('study', tr.studying_study_now(), id='{}'.format(studyButton_id), extra="autofocus"))
+
+ return output
+
+else:
+ def _table(self):
+ counts = list(self.mw.col.sched.counts())
+ finished = not sum(counts)
+ if self.mw.col.sched_ver() == 1:
+ for n in range(len(counts)):
+ if counts[n] >= 1000:
+ counts[n] = "1000+"
+ but = self.mw.button
+ if finished:
+ return '<div style="white-space: pre-wrap;">%s</div>' % (
+ self.mw.col.sched.finishedMsg()
+ )
+ else:
+ if style_mainScreenButtons:
+ #// style='height: px' -> to prevent changing main screen buttons heights
+ # based on height defined in #main {}
+ mainScreen_style = """id=main style='height: px' """
+ else:
+ mainScreen_style = ""
+ if style_mainScreenButtons:
+ studyButton_id = "main"
+ else:
+ studyButton_id = "study"
+ return """%s
+ <table width=400 cellpadding=5>
+ <tr><td align=center valign=top>
+ <table cellspacing=5>
+ <tr><td>%s:</td><td><b><span class=new-count>%s</span></b></td></tr>
+ <tr><td>%s:</td><td><b><span class=learn-count>%s</span></b></td></tr>
+ <tr><td>%s:</td><td><b><span class=review-count>%s</span></b></td></tr>
+ </table>
+ </td><td align=center>
+ %s</td></tr></table>""" % (
+ bottomHTML_style,
+ tr.actions_new(),
+ counts[0],
+ tr.scheduling_learning(),
+ counts[1],
+ tr.studying_to_review(),
+ counts[2],
+ but("study", tr.studying_study_now(), id="{}".format(studyButton_id), extra="autofocus"),
+ )
+
+def _limit(counts):
+ for i, count in enumerate(counts):
+ if count >= 1000:
+ counts[i] = "1000+"
+ return counts
+
+
+Overview._renderBottom = _renderBottom
+DeckBrowser._drawButtons = _drawButtons
+Overview._table = _table
diff --git a/.local/share/Anki2/addons21/advanced_review/LICENSE b/.local/share/Anki2/addons21/advanced_review/LICENSE new file mode 100644 index 0000000..3877ae0 --- /dev/null +++ b/.local/share/Anki2/addons21/advanced_review/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<https://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<https://www.gnu.org/licenses/why-not-lgpl.html>.
diff --git a/.local/share/Anki2/addons21/advanced_review/README.md b/.local/share/Anki2/addons21/advanced_review/README.md new file mode 100644 index 0000000..b20ba61 --- /dev/null +++ b/.local/share/Anki2/addons21/advanced_review/README.md @@ -0,0 +1,12 @@ +# Anki-Advanced-Review-Bottombar
+An Anki add-on that makes you able to change Anki button styles to your liking.
+
+You can see these images to see the settings menu and the things you can do with this add-on:
+
+<a href="https://imgur.com/a/pb53ODD" rel="nofollow"><img src="https://img.shields.io/badge/Images-button%20styles-blue"></a>
+
+<a href="https://imgur.com/a/LDzwvrG" rel="nofollow"><img src="https://img.shields.io/badge/Images-features-blue"></a>
+
+<a href="https://imgur.com/a/LGUqiUJ" rel="nofollow"><img src="https://img.shields.io/badge/Images-settings%20menu-blue"></a>
+
+If you find the settings too complicated, just hover over each title in the settings, a little help box will appear in which I have explained what the option does (used images to show you what it does in most cases).
diff --git a/.local/share/Anki2/addons21/advanced_review/Settings.py b/.local/share/Anki2/addons21/advanced_review/Settings.py new file mode 100644 index 0000000..4c279c3 --- /dev/null +++ b/.local/share/Anki2/addons21/advanced_review/Settings.py @@ -0,0 +1,2667 @@ +#// auth_ Mohamad Janati
+#// Copyright (c) 2019-2022 Mohamad Janati (freaking stupid, right? :|)
+
+from os.path import join, dirname
+from datetime import datetime
+from aqt import mw
+from aqt.qt import *
+from aqt.utils import tooltip, showInfo, askUser, getText
+import random
+import os
+import json
+import subprocess
+
+
+def refreshConfig():
+ #// Makes the information that it gets fron "config" global so I can use them for loading the current settings in "loadCurrent(self)" function
+ global C_style_mainScreenButtons, C_button_style, C_hover_effect, C_active_indicator, C_bottombarButtons_style, C_cursor_style, C_interval_style, C_showAnswerBorderColor_style, C_buttonTransition_time, C_buttonBorderRadius, C_reviewTooltip, C_reviewTooltip_timer, C_reviewTooltipText_color, C_reviewTooltip_style, C_reviewTooltip_position, C_info, C_skip, C_showSkipped, C_undo, C_hideHard, C_hideGood, C_hideEasy, C_right_info, C_middleRight_info, C_middleLeft_info, C_left_info, C_right_skip, C_middleRight_skip, C_middleLeft_skip, C_left_skip, C_right_showSkipped, C_middleRight_showSkipped, C_middleLeft_showSkipped, C_left_showSkipped, C_right_undo, C_middleRight_undo, C_middleLeft_undo, C_left_undo, C_skip_shortcut, C_showSkipped_shortcut, C_info_shortcut, C_undo_shortcut, C_custom_sizes, C_text_size, C_buttons_height, C_reviewButtons_width, C_edit_width, C_answer_width, C_more_width, C_info_width, C_skip_width, C_showSkipped_width, C_undo_width, C_buttonLabel_studyNow, C_buttonLabel_edit, C_buttonLabel_showAnswer, C_buttonLabel_more, C_buttonLabel_info, C_buttonLabel_skip, C_buttonLabel_showSkipped, C_buttonLabel_undo, C_buttonLabel_again, C_buttonLabel_hard, C_buttonLabel_good, C_buttonLabel_easy, C_sidebar_position, C_sidebar_theme, C_sidebar_font, C_sidebar_PreviousCards, C_sidebar_reviewsToShow, C_sidebar_currentReviewCount, C_sidebar_reviewsToShow, C_sidebar_dateCreated, C_sidebar_dateEdited, C_sidebar_firstReview, C_sidebar_latestReview, C_sidebar_due, C_sidebar_interval, C_sidebar_ease, C_sidebar_numberOfReviews, C_sidebar_lapses, C_infobar_correctPercent, C_infobar_fastestReview, C_infobar_slowestReview, C_sidebar_averageTime, C_sidebar_totalTime, C_sidebar_cardType, C_sidebar_noteType, C_sidebar_deck, C_sidebar_tags, C_infobar_noteID, C_infobar_cardID, C_sidebar_sortField, C_sidebar_autoOpen, C_sidebar_warningNote, C_custom_reviewButtonColors, C_custom_reviewButtonTextColor, C_custom_activeIndicatorColor, C_custom_bottombarButtonTextColor, C_custom_bottombarButtonBorderColor, C_reviewButtonText_color, C_activeIndicator_color, C_bottombarButtonText_color, C_bottombarButtonBorder_color, C_again_color, C_againHover_color, C_hard_color, C_hardHover_color, C_good_color, C_goodHover_color, C_easy_color, C_easyHover_color, C_button_colors, C_showAnswerEase1, C_showAnswerEase2, C_showAnswerEase3, C_showAnswerEase4, C_showAnswerEase1_color, C_showAnswerEase2_color, C_showAnswerEase3_color, C_showAnswerEase4_color, C_speedFocus, C_configEdit, C_overViewStats, C_settingsMenu_palce, C_skipMethod
+
+ config = mw.addonManager.getConfig(__name__)
+
+ #// Gets the information from the config and assigns them to the "C_" variables so I can make them global | "C_" is added to the name of the parts of the settings variables to avoid confusion :D
+ #// Just delete the "C_" from the name to find related parts of the settings (C_style_mainScreenButtons -> style_mainScreenButtons)
+ C_style_mainScreenButtons = config[' Style Main Screen Buttons']
+
+ C_button_style = config[' Review_ Buttons Style']
+ C_hover_effect = config[' Review_ Hover Effect']
+ C_active_indicator = config[' Review_ Active Button Indicator']
+ C_bottombarButtons_style = config[' Review_ Bottombar Buttons Style']
+ C_cursor_style = config[' Review_ Cursor Style']
+ C_interval_style = config[' Review_ Interval Style']
+ C_buttonTransition_time = config[' Review_ Button Transition Time']
+ # Button Border Radius is used for all buttons, not just the review buttons
+ C_buttonBorderRadius = config[' Review_ Button Border Radius']
+
+ C_reviewTooltip = config['Tooltip']
+ C_reviewTooltip_timer = config['Tooltip Timer']
+ C_reviewTooltipText_color = config['Tooltip Text Color']
+ C_reviewTooltip_style = config['Tooltip Style']
+ C_reviewTooltip_position = config['Tooltip Position']
+
+ C_info = config['Button_ Info Button']
+ C_skip = config['Button_ Skip Button']
+ C_showSkipped = config['Button_ Show Skipped Button']
+ C_undo = config['Button_ Undo Button']
+ C_hideHard = config['Button_ Hide Hard']
+ C_hideGood = config['Button_ Hide Good']
+ C_hideEasy = config['Button_ Hide Easy']
+ C_info_position = config['Button_ Position_ Info Button']
+ C_skip_position = config['Button_ Position_ Skip Button']
+ C_showSkipped_position = config['Button_ Position_ Show Skipped Button']
+ C_undo_position = config['Button_ Position_ Undo Button']
+ C_skip_shortcut = config ['Button_ Shortcut_ Skip Button']
+ C_showSkipped_shortcut = config ['Button_ Shortcut_ Show Skipped Button']
+ C_info_shortcut = config['Button_ Shortcut_ Info Button']
+ C_undo_shortcut = config['Button_ Shortcut_ Undo Button']
+
+ C_custom_sizes = config ['Button_ Custom Button Sizes']
+ C_text_size = config['Button_ Text Size']
+ C_buttons_height = config['Button_ Height_ All Bottombar Buttons']
+ C_reviewButtons_width = config['Button_ Width_ Review Buttons']
+ C_edit_width = config['Button_ Width_ Edit Button']
+ C_answer_width = config['Button_ Width_ Show Answer Button']
+ C_more_width = config['Button_ Width_ More Button']
+ C_info_width = config['Button_ Width_ Info Button']
+ C_skip_width = config['Button_ Width_ Skip Button']
+ C_showSkipped_width = config['Button_ Width_ Show Skipped Button']
+ C_undo_width = config['Button_ Width_ Undo Button']
+
+ C_buttonLabel_studyNow = config['Button Label_ Study Now']
+ C_buttonLabel_edit = config['Button Label_ Edit']
+ C_buttonLabel_showAnswer = config['Button Label_ Show Answer']
+ C_buttonLabel_more = config['Button Label_ More']
+ C_buttonLabel_info = config['Button Label_ Info']
+ C_buttonLabel_skip = config['Button Label_ Skip']
+ C_buttonLabel_showSkipped = config['Button Label_ Show Skipped']
+ C_buttonLabel_undo = config['Button Label_ Undo']
+ C_buttonLabel_again = config['Button Label_ Again']
+ C_buttonLabel_hard = config['Button Label_ Hard']
+ C_buttonLabel_good = config['Button Label_ Good']
+ C_buttonLabel_easy = config['Button Label_ Easy']
+
+ C_sidebar_position = config['Card Info sidebar_ Default Position']
+ C_sidebar_theme = config['Card Info sidebar_ theme']
+ C_sidebar_font = config['Card Info sidebar_ Font']
+ C_sidebar_PreviousCards = config['Card Info sidebar_ Number of previous cards to show']
+ C_sidebar_reviewsToShow = config['Card Info sidebar_ number of reviews to show for a card']
+ C_sidebar_currentReviewCount = config['Card Info sidebar_ Current Review Count']
+ C_sidebar_dateCreated = config['Card Info sidebar_ Created']
+ C_sidebar_dateEdited = config['Card Info sidebar_ Edited']
+ C_sidebar_firstReview = config['Card Info sidebar_ First Review']
+ C_sidebar_latestReview = config['Card Info sidebar_ Latest Review']
+ C_sidebar_due = config['Card Info sidebar_ Due']
+ C_sidebar_interval = config['Card Info sidebar_ Interval']
+ C_sidebar_ease = config['Card Info sidebar_ Ease']
+ C_sidebar_numberOfReviews = config['Card Info sidebar_ Reviews']
+ C_sidebar_lapses = config['Card Info sidebar_ Lapses']
+ C_infobar_correctPercent = config['Card Info Sidebar_ Correct Percent']
+ C_infobar_fastestReview = config['Card Info Sidebar_ Fastest Review']
+ C_infobar_slowestReview = config['Card Info Sidebar_ Slowest Review']
+ C_sidebar_averageTime = config['Card Info sidebar_ Average Time']
+ C_sidebar_totalTime = config['Card Info sidebar_ Total Time']
+ C_sidebar_cardType = config['Card Info sidebar_ Card Type']
+ C_sidebar_noteType = config['Card Info sidebar_ Note Type']
+ C_sidebar_deck = config['Card Info sidebar_ Deck']
+ C_sidebar_tags = config['Card Info sidebar_ Tags']
+ C_infobar_noteID = config['Card Info Sidebar_ Note ID']
+ C_infobar_cardID = config['Card Info Sidebar_ Card ID']
+ C_sidebar_sortField = config['Card Info sidebar_ Sort Field']
+ C_sidebar_autoOpen = config['Card Info sidebar_ Auto Open']
+ C_sidebar_warningNote = config['Card Info sidebar_ warning note']
+
+ C_custom_reviewButtonColors = config[' Review_ Custom Colors']
+ C_custom_reviewButtonTextColor = config[' Review_ Custom Review Button Text Color']
+ C_custom_activeIndicatorColor = config[' Review_ Custom Active Indicator Color']
+ C_custom_bottombarButtonTextColor = config['Color_ Custom Bottombar Button Text Color']
+ C_custom_bottombarButtonBorderColor = config['Color_ Custom Bottombar Button Border Color']
+ C_reviewButtonText_color = config['Color_ General Text Color']
+ C_activeIndicator_color = config['Color_ Active Button Indicator']
+ C_bottombarButtonText_color = config['Color_ Bottombar Button Text Color']
+ C_bottombarButtonBorder_color = config['Color_ Bottombar Button Border Color']
+ C_again_color = config['Color_ Again']
+ C_againHover_color = config['Color_ Again on hover']
+ C_hard_color = config['Color_ Hard']
+ C_hardHover_color = config['Color_ Hard on hover']
+ C_good_color = config['Color_ Good']
+ C_goodHover_color = config['Color_ Good on hover']
+ C_easy_color = config['Color_ Easy']
+ C_easyHover_color = config['Color_ Easy on hover']
+
+ C_showAnswerBorderColor_style = config['ShowAnswer_ Border Color Style']
+ C_showAnswerEase1 = config['ShowAnswer_ Ease1']
+ C_showAnswerEase2 = config['ShowAnswer_ Ease2']
+ C_showAnswerEase3 = config['ShowAnswer_ Ease3']
+ C_showAnswerEase4 = config['ShowAnswer_ Ease4']
+ C_showAnswerEase1_color = config['ShowAnswer_ Ease1 Color']
+ C_showAnswerEase2_color = config['ShowAnswer_ Ease2 Color']
+ C_showAnswerEase3_color = config['ShowAnswer_ Ease3 Color']
+ C_showAnswerEase4_color = config['ShowAnswer_ Ease4 Color']
+
+ C_button_colors = config[' Button Colors']
+ C_speedFocus = config[' Speed Focus Add-on']
+ C_configEdit = config[' Direct Config Edit']
+ C_overViewStats = config[' More Overview Stats']
+ C_settingsMenu_palce = config[' Settings Menu Place']
+ C_skipMethod = config[' Skip Method']
+
+ #// it's easier to store extra button positions as text in config | but here in the settings, I hate to turn it into true/false as each checkbox is diabled/enabled like that :|
+ #// Every checkbox is disabled by default
+ C_right_info = False
+ C_middleRight_info = False
+ C_middleLeft_info = False
+ C_left_info = False
+ C_right_skip = False
+ C_middleRight_skip = False
+ C_middleLeft_skip = False
+ C_left_showSkipped = False
+ C_right_showSkipped = False
+ C_middleRight_showSkipped = False
+ C_middleLeft_showSkipped = False
+ C_left_showSkipped = False
+ C_right_undo = False
+ C_middleRight_undo = False
+ C_middleLeft_undo = False
+ C_left_undo = False
+
+ #// here we enable (make it "True") the correct checkbox based on the config value
+ #// All of this is for loading the current settings in "loadCurrent(self)" function
+ if C_info_position == "right":
+ C_right_info = True
+ elif C_info_position == "middle right":
+ C_middleRight_info = True
+ elif C_info_position == "middle left":
+ C_middleLeft_info = True
+ else:
+ C_left_info = True
+ if C_skip_position == "right":
+ C_right_skip = True
+ elif C_skip_position == "middle right":
+ C_middleRight_skip = True
+ elif C_skip_position == "middle left":
+ C_middleLeft_skip = True
+ else:
+ C_left_skip = True
+ if C_showSkipped_position == "right":
+ C_right_showSkipped = True
+ elif C_showSkipped_position == "middle right":
+ C_middleRight_showSkipped = True
+ elif C_showSkipped_position == "middle left":
+ C_middleLeft_showSkipped = True
+ else:
+ C_left_showSkipped = True
+ if C_undo_position == "right":
+ C_right_undo = True
+ elif C_undo_position == "middle right":
+ C_middleRight_undo = True
+ elif C_undo_position == "middle left":
+ C_middleLeft_undo = True
+ else:
+ C_left_undo = True
+
+class GetShortcut(QDialog):
+ def __init__(self, parent, button_variable):
+ QDialog.__init__(self, parent=parent)
+ self.parent = parent
+ self.button_variable = button_variable
+ #// when recording a shortcut, there is 0 active (pushed) key at first | by pressing each key, this increases by +1
+ self.active = 0
+ #// and the state of all the accepted keys on the keyboard is "False" | by pressing each key, the state for that button changes to "True"
+ self.ctrl = False
+ self.alt = False
+ self.shift = False
+ self.f1 = False
+ self.f2 = False
+ self.f3 = False
+ self.f4 = False
+ self.f5 = False
+ self.f3 = False
+ self.f6 = False
+ self.f7 = False
+ self.f8 = False
+ self.f9 = False
+ self.f10 = False
+ self.f11 = False
+ self.f12 = False
+ self.extra = None
+ self.getShortcutWindow()
+
+ def getShortcutWindow(self):
+ #// Sets up the screen that asks you to press the shortcut you want to assign
+ text = QLabel('<div style="font-size: 15px">Press the new shortcut key...</div>')
+ mainLayout = QVBoxLayout()
+ mainLayout.addWidget(text)
+ self.setLayout(mainLayout)
+ self.setWindowTitle('Set Shortcut')
+
+ def keyPressEvent(self, evt):
+ #// increases the active keys count upon pressing each key
+ self.active += 1
+ #// limits the allowed keys to keyboard keys
+ if evt.key() > 30 and evt.key() < 127:
+ self.extra = chr(evt.key())
+ #// stores the pressed key in a variable so we could later add it in a list and use it as a key combination
+ elif evt.key() == Qt.Key_Control:
+ self.ctrl = True
+ elif evt.key() == Qt.Key_Alt:
+ self.alt = True
+ elif evt.key() == Qt.Key_Shift:
+ self.shift = True
+ elif evt.key() == Qt.Key_F1:
+ self.f1 = True
+ elif evt.key() == Qt.Key_F2:
+ self.f2 = True
+ elif evt.key() == Qt.Key_F3:
+ self.f3 = True
+ elif evt.key() == Qt.Key_F4:
+ self.f4 = True
+ elif evt.key() == Qt.Key_F5:
+ self.f5 = True
+ elif evt.key() == Qt.Key_F6:
+ self.f6 = True
+ elif evt.key() == Qt.Key_F7:
+ self.f7 = True
+ elif evt.key() == Qt.Key_F8:
+ self.f8 = True
+ elif evt.key() == Qt.Key_F9:
+ self.f9 = True
+ elif evt.key() == Qt.Key_F10:
+ self.f10 = True
+ elif evt.key() == Qt.Key_F11:
+ self.f11 = True
+ elif evt.key() == Qt.Key_F12:
+ self.f12 = True
+
+ def keyReleaseEvent(self, evt):
+ #// reduces the number of held keys upon releasing each key
+ self.active -= 1
+ #// I was having fun -_- don't blame me
+ shiftList = ["Who uses \"Shift\" alone as a shortcut key?", "You wanna use \"Shift\" without any other key as your shortcut??\n\n SERIOUSLY?!?!", "You must have forgotten to press another key...\n\n I don't wanna believe that there is someone who uses \"Shift\" without another key as a shortcut", "Dude, you can't just use \"Shift\"\n\n You should combine it with another key", "Are you really trying to use \"Shift\" as your shortcut?!!\n\n C'mon...", "What's so special about \"Shift\" that you wanna use it alone as a shortcut??", "\"Shift\" is scared of being used alone, you should use it with another key :)"]
+ if os.name == "nt":
+ altList = ["Who uses \"Alt\" alone as a shortcut key?", "You wanna use \"Alt\" without any other key as your shortcut??\n\n SERIOUSLY?!?!", "You must have forgotten to press another key...\n\n I don't wanna believe that there is someone who uses \"Alt\" without another key as a shortcut", "Dude, you can't just use \"Alt\"\n\n You should combine it with another key", "Are you really trying to use \"Alt\" as your shortcut?!!\n\n C'mon...", "What's so special about \"Alt\" that you wanna use it alone as a shortcut??", "\"Alt\" is scared of being used alone, you should use it with another key :)"]
+ ctrlList = ["Who uses \"Ctrl\" alone as a shortcut key?", "You wanna use \"Ctrl\" without any other key as your shortcut??\n\n SERIOUSLY?!?!", "You must have forgotten to press another key...\n\n I don't wanna believe that there is someone who uses \"Ctrl\" without another key as a shortcut", "Dude, you can't just use \"Ctrl\"\n\n You should combine it with another key", "Are you really trying to use \"Ctrl\" as your shortcut?!!\n\n C'mon...", "What's so special about \"Ctrl\" that you wanna use it alone as a shortcut??", "\"Ctrl\" is scared of being used alone, you should use it with another key :)"]
+ else:
+ altList = ["Who uses \"Option\" alone as a shortcut key?", "You wanna use \"Option\" without any other key as your shortcut??\n\n SERIOUSLY?!?!", "You must have forgotten to press another key...\n\n I don't wanna believe that there is someone who uses \"Option\" without another key as a shortcut", "Dude, you can't just use \"Option\"\n\n You should combine it with another key", "Are you really trying to use \"Option\" as your shortcut?!!\n\n C'mon...", "What's so special about \"Option\" that you wanna use it alone as a shortcut??", "\"Option\" is scared of being used alone, you should use it with another key :)"]
+ ctrlList = ["Who uses \"Command\" alone as a shortcut key?", "You wanna use \"Command\" without any other key as your shortcut??\n\n SERIOUSLY?!?!", "You must have forgotten to press another key...\n\n I don't wanna believe that there is someone who uses \"Command\" without another key as a shortcut", "Dude, you can't just use \"Command\"\n\n You should combine it with another key", "Are you really trying to use \"Command\" as your shortcut?!!\n\n C'mon...", "What's so special about \"Command\" that you wanna use it alone as a shortcut??", "\"Command\" is scared of being used alone, you should use it with another key :)"]
+ shiftAlone = random.choice(shiftList)
+ altAlone = random.choice(altList)
+ ctrlAlone = random.choice(ctrlList)
+ if not (self.f1 or self.f2 or self.f3 or self.f4 or self.f5 or self.f6 or self.f7 or self.f8 or self.f9 or self.f10 or self.f11 or self.f12):
+ if not self.extra:
+ #// special treats for buttons that everyone knows can't be used as a shortcut on their own -_-
+ if self.alt:
+ showInfo("{}".format(altAlone), title="Advanced Review Bottombar")
+ elif self.shift:
+ showInfo("{}".format(shiftAlone), title="Advanced Review Bottombar")
+ elif self.ctrl:
+ showInfo("{}".format(ctrlAlone), title="Advanced Review Bottombar")
+ #// lets the users that the pressed key is not allowed to be used in a shortcut
+ elif evt.key() == Qt.Key_Escape:
+ showInfo("You can't use \"Escape\" as shortcut, try something else", title="Advanced Review Bottombar")
+ elif evt.key() == Qt.Key_Tab:
+ showInfo("Don't you know \"Tab\" Always does something? Why do you even try to set it as your shortcut?", title="Advanced Review Bottombar")
+ elif evt.key() == Qt.Key_Backspace:
+ showInfo("Are you really trying to set \"Backspace\" as your shortcut? Seriously?", title="Advanced Review Bottombar")
+ elif evt.key() == Qt.Key_Enter:
+ showInfo("Have you ever seen anyone using \"Enter\" as a shortcut??\n\nWhy are you even trying to set it as your shortcut?", title="Advanced Review Bottombar")
+ elif evt.key() == Qt.Key_Return:
+ showInfo("Have you ever seen anyone using \"Enter\" as a shortcut??\n\nWhy are you even trying to set it as your shortcut?", title="Advanced Review Bottombar")
+ elif evt.key() == Qt.Key_Insert:
+ showInfo("You can't use \"Insert\" as shortcut, try something else", title="Advanced Review Bottombar")
+ elif evt.key() == Qt.Key_Delete:
+ showInfo("Who even thinks about using \"Delete\" as shortcut?? WTF man, SERIOUSLY????", title="Advanced Review Bottombar")
+ elif evt.key() == Qt.Key_Pause:
+ showInfo("You can't use \"Pause/Break\" as shortcut, try something else", title="Advanced Review Bottombar")
+ elif evt.key() == Qt.Key_Home:
+ showInfo("Do you really think \"Home Key\" is a good key a a shortcut??!", title="Advanced Review Bottombar")
+ elif evt.key() == Qt.Key_Left:
+ showInfo("You can't use \"Left\" as shortcut, try something else", title="Advanced Review Bottombar")
+ elif evt.key() == Qt.Key_Up:
+ showInfo("You can't use \"Up\" as shortcut, try something else", title="Advanced Review Bottombar")
+ elif evt.key() == Qt.Key_Right:
+ showInfo("You can't use \"Right\" as shortcut, try something else", title="Advanced Review Bottombar")
+ elif evt.key() == Qt.Key_Down:
+ showInfo("You can't use \"Down\" as shortcut, try something else", title="Advanced Review Bottombar")
+ elif evt.key() == Qt.Key_PageUp:
+ showInfo("You can't use \"Page Up\" as shortcut, try something else.\n\nEven if you could, would it really be a SHORTcut?? I mean look at you keyboard...", title="Advanced Review Bottombar")
+ elif evt.key() == Qt.Key_PageDown:
+ showInfo("You can't use \"Page Down\" as shortcut, try something else.\n\nEven if you could, would it really be a SHORTcut?? I mean look at you keyboard...", title="Advanced Review Bottombar")
+ elif evt.key() == Qt.Key_CapsLock:
+ showInfo("wHO THINKS IT'S A GOOD IDEA TO USE \"cAPs LoCk\" AS A SHORTCUT???", title="Advanced Review Bottombar")
+ elif evt.key() == Qt.Key_NumLock:
+ showInfo("Why \"Num Lock\"???\n\n It's THE MOST remote key on keyboard, it can't be a SHORTcut man...", title="Advanced Review Bottombar")
+ else:
+ showInfo("You can't use that as shortcut, try something else.", title="Advanced Review Bottombar")
+ self.alt = False
+ self.shift = False
+ self.ctrl = False
+ self.extra = None
+ self.active = 0
+ evt = False
+ combination = []
+ return
+
+ #// the (empty) list for storing keys and then turning them into a shortcut
+ combination = []
+ if self.ctrl:
+ combination.append("Ctrl")
+ if self.shift:
+ combination.append("Shift")
+ if self.alt:
+ combination.append("Alt")
+ if self.f1:
+ combination.append("F1")
+ if self.f2:
+ combination.append("F2")
+ if self.f3:
+ combination.append("F3")
+ if self.f4:
+ combination.append("F4")
+ if self.f5:
+ combination.append("F5")
+ if self.f6:
+ combination.append("F6")
+ if self.f7:
+ combination.append("F7")
+ if self.f8:
+ combination.append("F8")
+ if self.f9:
+ combination.append("F9")
+ if self.f10:
+ combination.append("F10")
+ if self.f11:
+ combination.append("F11")
+ if self.f12:
+ combination.append("F12")
+ if self.extra:
+ combination.append(self.extra)
+ combination = "+".join(combination)
+ #// preventing users from assigning a defauls Anki shortcut to something else | to avoid conflicts and stuff :|
+ if combination in ["E", " ", "F5", "Ctrl+1", "Ctrl+2", "Ctrl+3", "Ctrl+4", "Shift+*", "=", "-", "Shift+!", "Shift+@", "Ctrl+Delete", "V", "Shift+V", "O", "1", "2", "3", "4", "5", "6", "7", "T", "Y", "A", "S", "D", "F", "B", "I", "/", "F1", "Ctrl+Q", "Ctrl+E", "Ctrl+P", "Ctrl+Shift+I", "Ctrl+Shift+P", "Ctrl+Shift+A", "Ctrl+Shift+:", "Ctrl+Shif+N", "Ctrl+Z"]:
+ if combination == "E":
+ showInfo("\"E\" is default Anki shortcut for \"Edit Current Card\" You can't use this shortcut.", type="warning", title="Advanced Review Bottombar")
+ if combination == " ":
+ showInfo("\"Space Bar\" is default Anki shortcut for \"Show Answer\" or \"Default Review Button\" You can't use this shortcut.", type="warning", title="Advanced Review Bottombar")
+ if combination == "F5":
+ showInfo("\"F5\" is default Anki shortcut for \"Replay Audio\" You can't use this shortcut.", type="warning", title="Advanced Review Bottombar")
+ if combination == "Ctrl+1":
+ showInfo("\"Ctrl+1\" is default Anki shortcut for \"Set Red Flag\" You can't use this shortcut.", type="warning", title="Advanced Review Bottombar")
+ if combination == "Ctrl+2":
+ showInfo("\"Ctrl+2\" is default Anki shortcut for \"Set Orange Flag\" You can't use this shortcut.", type="warning", title="Advanced Review Bottombar")
+ if combination == "Ctrl+3":
+ showInfo("\"Ctrl+3\" is default Anki shortcut for \"Set Green Flag\" You can't use this shortcut.", type="warning", title="Advanced Review Bottombar")
+ if combination == "Ctrl+4":
+ showInfo("\"Ctrl+4\" is default Anki shortcut for \"Set Blue Flag\" You can't use this shortcut.", type="warning", title="Advanced Review Bottombar")
+ if combination == "Shift+*":
+ showInfo("\"*\" is default Anki shortcut for \"Mark Current Card\" You can't use this shortcut.", type="warning", title="Advanced Review Bottombar")
+ if combination == "=":
+ showInfo("\"=\" is default Anki shortcut for \"Bury Note\" You can't use this shortcut.", type="warning", title="Advanced Review Bottombar")
+ if combination == "-":
+ showInfo("\"-\" is default Anki shortcut for \"Bury Current Card\" You can't use this shortcut.", type="warning", title="Advanced Review Bottombar")
+ if combination == "Shift+!":
+ showInfo("\"!\" is default Anki shortcut for \"Suspend Note\" You can't use this shortcut.", type="warning", title="Advanced Review Bottombar")
+ if combination == "Shift+@":
+ showInfo("\"@\" is default Anki shortcut for \"Suspend Current Card\" You can't use this shortcut.", type="warning", title="Advanced Review Bottombar")
+ if combination == "Ctrl+Delete":
+ showInfo("\"Ctrl+Delete\" is default Anki shortcut for \"Delete Note\" You can't use this shortcut.", type="warning", title="Advanced Review Bottombar")
+ if combination == "V":
+ showInfo("\"V\" is default Anki shortcut for \"Replay Audio\" You can't use this shortcut.", type="warning", title="Advanced Review Bottombar")
+ if combination == "Shift+V":
+ showInfo("\"Shift+V\" is default Anki shortcut for \"Record Voice\" You can't use this shortcut.", type="warning", title="Advanced Review Bottombar")
+ if combination == "O":
+ showInfo("\"O\" is default Anki shortcut for \"Options\" You can't use this shortcut.", type="warning", title="Advanced Review Bottombar")
+ if combination == "1":
+ showInfo("\"1\" is default Anki shortcut for \"Answer with ease 1 (Again)\" You can't use this shortcut.", type="warning", title="Advanced Review Bottombar")
+ if combination == "2":
+ showInfo("\"2\" is default Anki shortcut for \"Answer with ease 2 (usually Hard)\" You can't use this shortcut.", type="warning", title="Advanced Review Bottombar")
+ if combination == "3":
+ showInfo("\"3\" is default Anki shortcut for \"Answer with ease 3 (usually Good)\" You can't use this shortcut.", type="warning", title="Advanced Review Bottombar")
+ if combination == "4":
+ showInfo("\"4\" is default Anki shortcut for \"Answer with ease 4 (Easy)\" You can't use this shortcut.", type="warning", title="Advanced Review Bottombar")
+ if combination == "5":
+ showInfo("\"5\" is default Anki shortcut for \"Puase Audio\" You can't use this shortcut.", type="warning", title="Advanced Review Bottombar")
+ if combination == "6":
+ showInfo("\"6\" is default Anki shortcut for \"Seek Backward\" You can't use this shortcut.", type="warning", title="Advanced Review Bottombar")
+ if combination == "7":
+ showInfo("\"7\" is default Anki shortcut for \"Seek Forward\" You can't use this shortcut.", type="warning", title="Advanced Review Bottombar")
+ if combination == "T":
+ showInfo("\"T\" is default Anki shortcut for \"Stats\" You can't use this shortcut.", type="warning", title="Advanced Review Bottombar")
+ if combination == "Y":
+ showInfo("\"Y\" is default Anki shortcut for \"Sync\" You can't use this shortcut.", type="warning", title="Advanced Review Bottombar")
+ if combination == "A":
+ showInfo("\"A\" is default Anki shortcut for \"Add Cards\" You can't use this shortcut.", type="warning", title="Advanced Review Bottombar")
+ if combination == "S":
+ showInfo("\"S\" is default Anki shortcut for \"Toggle Study Current Deck\" You can't use this shortcut.", type="warning", title="Advanced Review Bottombar")
+ if combination == "D":
+ showInfo("\"D\" is default Anki shortcut for \"Decks View\" You can't use this shortcut.", type="warning", title="Advanced Review Bottombar")
+ if combination == "F":
+ showInfo("\"F\" is default Anki shortcut for \"Create Filtered Deck\" You can't use this shortcut.", type="warning", title="Advanced Review Bottombar")
+ if combination == "B":
+ showInfo("\"B\" is default Anki shortcut for \"Browse\" You can't use this shortcut.", type="warning", title="Advanced Review Bottombar")
+ if combination == "I":
+ showInfo("\"I\" is default Anki shortcut for \"Card Info Window\" You can't use this shortcut.", type="warning", title="Advanced Review Bottombar")
+ if combination == "/":
+ showInfo("\"/\" is default Anki shortcut for \"Study Deck\" You can't use this shortcut.", type="warning", title="Advanced Review Bottombar")
+ if combination == "F1":
+ showInfo("\"F1\" is default Anki shortcut for \"Open Guide (Anki Manual)\" You can't use this shortcut.", type="warning", title="Advanced Review Bottombar")
+ if combination == "Ctrl+Q":
+ showInfo("\"Ctrl+Q\" is default Anki shortcut for \"Exit\" You can't use this shortcut.", type="warning", title="Advanced Review Bottombar")
+ if combination == "Ctrl+E":
+ showInfo("\"Ctrl+E\" is default Anki shortcut for \"Export\" You can't use this shortcut.", type="warning", title="Advanced Review Bottombar")
+ if combination == "Ctrl+P":
+ showInfo("\"Ctrl+P\" is default Anki shortcut for \"Preferences\" You can't use this shortcut.", type="warning", title="Advanced Review Bottombar")
+ if combination == "Ctrl+Shift+I":
+ showInfo("\"Ctrl+Shift+I\" is default Anki shortcut for \"Import\" You can't use this shortcut.", type="warning", title="Advanced Review Bottombar")
+ if combination == "Ctrl+Shift+P":
+ showInfo("\"Ctrl+Shift+P\" is default Anki shortcut for \"Swith Profile\" You can't use this shortcut.", type="warning", title="Advanced Review Bottombar")
+ if combination == "Ctrl+Shift+A":
+ showInfo("\"Ctrl+Shift+A\" is default Anki shortcut for \"Add-ons\" You can't use this shortcut.", type="warning", title="Advanced Review Bottombar")
+ if combination == "Ctrl+Shift+:":
+ showInfo("\"Ctrl+Shift+:\" is default Anki shortcut for \"Debug Console\" You can't use this shortcut.", type="warning", title="Advanced Review Bottombar")
+ if combination == "Ctrl+Shift+N":
+ showInfo("\"Ctrl+Shift+N\" is default Anki shortcut for \"Manage Note Types\" You can't use this shortcut.", type="warning", title="Advanced Review Bottombar")
+ if combination == "Ctrl+Shift+Z":
+ showInfo("\"Ctrl+Shift+Z\" is default Anki shortcut for \"Undo\" You can't use this shortcut.", type="warning", title="Advanced Review Bottombar")
+ self.ctrl = False
+ self.alt = False
+ self.shift = False
+ self.extra = None
+ self.f1 = False
+ self.f5 = False
+ self.active = 0
+ combination = []
+ return
+ self.parent.updateShortcut(self.button_variable, combination)
+ self.close()
+
+class SettingsMenu(QDialog):
+ refreshConfig()
+ addon_path = dirname(__file__)
+ images = join(addon_path, 'images')
+ begin = "<div style='font-size: 14px'>"
+ end = "</div>"
+ info_shortcut = C_info_shortcut
+ skip_shortcut = C_skip_shortcut
+ showSkipped_shortcut = C_showSkipped_shortcut
+ undo_shortcut = C_undo_shortcut
+ def __init__(self, parent=None):
+ super(SettingsMenu, self).__init__(parent)
+ self.mainWindow()
+ self.reviewButtonText_color = C_reviewButtonText_color
+ self.activeIndicator_color = C_activeIndicator_color
+ self.bottombarButtonText_color = C_bottombarButtonText_color
+ self.bottombarButtonBorder_color = C_bottombarButtonBorder_color
+ self.reviewTooltipText_color = C_reviewTooltipText_color
+ self.again_color = C_again_color
+ self.againHover_color = C_againHover_color
+ self.hard_color = C_hard_color
+ self.hardHover_color = C_hardHover_color
+ self.good_color = C_good_color
+ self.goodHover_color = C_goodHover_color
+ self.easy_color = C_easy_color
+ self.easyHover_color = C_easyHover_color
+ self.showAnswerEase1_color = C_showAnswerEase1_color
+ self.showAnswerEase2_color = C_showAnswerEase2_color
+ self.showAnswerEase3_color = C_showAnswerEase3_color
+ self.showAnswerEase4_color = C_showAnswerEase4_color
+ def mainWindow(self):
+ images = self.images
+ self.createFirstTab()
+ self.createSecondTab()
+ self.createThirdTab()
+ self.createFourthTab()
+ self.createFifthTab()
+ self.createSixthTab()
+ self.createSeventhTab()
+ self.createEighthTab()
+ self.createNinthTab()
+ self.loadCurrent()
+
+ #// Create the bottom row of settings menu
+ loadSettingsButton = QPushButton("&Load Settings")
+ loadSettingsButton.clicked.connect(self.onLoadSettings)
+ saveSettingsButton = QPushButton("&Backup Settings")
+ saveSettingsButton.clicked.connect(self.onSaveSettings)
+ acceptButton = QPushButton("&Apply")
+ acceptButton.clicked.connect(self.onApply)
+ rejectButton = QPushButton("&Discard")
+ rejectButton.clicked.connect(self.reject)
+ rejectButton.clicked.connect(lambda: tooltip("Changes Discarded."))
+ buttonbox = QHBoxLayout()
+ buttonbox.addWidget(loadSettingsButton)
+ buttonbox.addWidget(saveSettingsButton)
+ buttonbox.addStretch()
+ buttonbox.addWidget(acceptButton)
+ buttonbox.addWidget(rejectButton)
+
+ #// create tabs widget and adds each tab
+ tabs = QTabWidget()
+ tabs.addTab(self.tab1, "Styles")
+ tabs.addTab(self.tab2, "Answer Tooltip")
+ tabs.addTab(self.tab3, "Bottombar Buttons")
+ tabs.addTab(self.tab4, "Button Sizes")
+ tabs.addTab(self.tab5, "Button Labels")
+ tabs.addTab(self.tab6, "Sidebar")
+ tabs.addTab(self.tab7, "Colors")
+ tabs.addTab(self.tab8, "Misc")
+ tabs.addTab(self.tab9, "About")
+
+ vbox = QVBoxLayout()
+ vbox.addWidget(tabs)
+ vbox.addLayout(buttonbox)
+
+ self.setLayout(vbox)
+ self.setWindowTitle("Advanced Review Bottombar Settings Menu")
+ self.setWindowIcon(QIcon(images + "/icon.png"))
+
+ def createFirstTab(self):
+ begin = self.begin
+ end = self.end
+ images = self.images
+ buttonStyle_label = QLabel("Button Style:")
+ buttonStyle_label.setToolTip("{0} Changes the way review buttons look.{1}".format(begin, end))
+ buttonStyle_label.setFixedWidth(180)
+ self.button_style = QComboBox()
+ self.button_style.addItems(["Default + Text Color", "Default + Background Color", "Wide + Text Color", "Wide + Background Color", "Neon 1", "Neon 2", "Fill 1", "Fill 2"])
+ self.button_style.setToolTip("{0}To see designs please go to about tab.{1}".format(begin, end))
+ self.button_style.setMinimumWidth(180)
+ reviewButtonDesigns_button = QPushButton("Show Designs")
+ reviewButtonDesigns_button.setFixedWidth(180)
+ reviewButtonDesigns_text = "{0}Default + Text Color <br> <img src='{2}/buttonStyle_defaultText.png'><hr> Default\
+ + Bakground Color<br> <img src='{2}/buttonStyle_defaultBackground.png'><hr>\
+ Wide + Text Color<br> <img src='{2}/buttonStyle_wideText.png'><hr>Wide +\
+ Background Color<br> <img src='{2}/buttonStyle_wideBackground.png'><hr>Neon1 (Easy is hovered over)<br>\
+ <img src='{2}/buttonStyle_neon1.png'><hr>Neon2 (Easy is hovered over)<br>\
+ <img src='{2}/buttonStyle_neon2.png'><hr> Fill1 (Easy is hovered over)<br><img src='{2}/buttonStyle_fill1.png'><hr>\
+ Fill2 (Easy is hovered over)<br><img src='{2}/buttonStyle_fill2.png'>{1}".format(begin, end, images)
+ reviewButton_designs = QLabel()
+ reviewButton_designs.setText(reviewButtonDesigns_text)
+ reviewButtonDesigns_scroll = QScrollArea()
+ reviewButtonDesigns_scroll.setWidget(reviewButton_designs)
+ reviewButtonDesigns_layout = QVBoxLayout()
+ reviewButtonDesigns_layout.addWidget(reviewButtonDesigns_scroll)
+ reviewButtonDesigns_window = QDialog()
+ reviewButtonDesigns_window.setWindowTitle("Advanced Review Bottombar [Review Button Designs]")
+ reviewButtonDesigns_window.setWindowIcon(QIcon(images + "/icon.png"))
+ reviewButtonDesigns_window.setLayout(reviewButtonDesigns_layout)
+ reviewButtonDesigns_button.clicked.connect(lambda: reviewButtonDesigns_window.exec_())
+ buttonStyle_holder = QHBoxLayout()
+ buttonStyle_holder.addWidget(buttonStyle_label)
+ buttonStyle_holder.addWidget(self.button_style)
+ buttonStyle_holder.addWidget(reviewButtonDesigns_button)
+ buttonStyle_holder.addStretch()
+ bottombaButtonsStyle_label = QLabel("General Buttons Style:")
+ bottombaButtonsStyle_label.setToolTip("{0} Changes The way general buttons (main screen bottombar, deck overview, show answer, edit, etc.) look. {1}".format(begin, end))
+ bottombaButtonsStyle_label.setFixedWidth(180)
+ self.bottombarButtons_style = QComboBox()
+ self.bottombarButtons_style.addItems(["Default", "Neon 1", "Neon 2", "Fill1", "Fill 2"])
+ self.bottombarButtons_style.setToolTip("{0}To see what every design looks like, please go to about tab{1}".format(begin, end))
+ self.bottombarButtons_style.setMinimumWidth(180)
+ otherBottombarButtonDesigns_button = QPushButton("Show Designs")
+ otherBottombarButtonDesigns_button.setFixedWidth(180)
+ otherBottombarButtonDesigns_text = "{0} Default<br><img src='{2}/bottombarButtonsStyle_default.png'><hr>\
+ Neon1 (Show answer is hovered over)<br> <img src='{2}/bottombarButtonsStyle_neon1.png'>\
+ <hr>Neon2 (Show answer is hovered over)<br> <img src='{2}/bottombarButtonsStyle_neon2.png'><hr>Fill1 (Show answer is hovered over)<br>\
+ <img src='{2}/bottombarButtonsStyle_fill1.png'><hr>Fill2 (Show answer is hovered over)<br>\
+ <img src='{2}/bottombarButtonsStyle_fill2.png'>{1}".format(begin, end, images)
+ otherBottombarButton_designs = QLabel()
+ otherBottombarButton_designs.setText(otherBottombarButtonDesigns_text)
+ otherBottombarButtonDesigns_scroll = QScrollArea()
+ otherBottombarButtonDesigns_scroll.setWidget(otherBottombarButton_designs)
+ otherBottombarButtonDesigns_layout = QVBoxLayout()
+ otherBottombarButtonDesigns_layout.addWidget(otherBottombarButtonDesigns_scroll)
+ otherBottombarButtonDesigns_window = QDialog()
+ otherBottombarButtonDesigns_window.setWindowTitle("Advanced Review Bottombar [Other Bottombar Buttons Designs]")
+ otherBottombarButtonDesigns_window.setWindowIcon(QIcon(images + "/icon.png"))
+ otherBottombarButtonDesigns_window.setLayout(otherBottombarButtonDesigns_layout)
+ otherBottombarButtonDesigns_button.clicked.connect(lambda: otherBottombarButtonDesigns_window.exec_())
+ bottombarButtonsStyle_holder = QHBoxLayout()
+ bottombarButtonsStyle_holder.addWidget(bottombaButtonsStyle_label)
+ bottombarButtonsStyle_holder.addWidget(self.bottombarButtons_style)
+ bottombarButtonsStyle_holder.addWidget(otherBottombarButtonDesigns_button)
+ bottombarButtonsStyle_holder.addStretch()
+ hoverEffect_label = QLabel("Hover Effect:")
+ hoverEffect_label.setToolTip("{0} Changes the way review buttons look when you hover over them.\
+ <hr> This option does not change hover effect for neon buttons.<hr> If you use\
+ custom colors for review buttons, brighten and glow colors will be the color\
+ you have set for each buttons hover color.{1}".format(begin, end))
+ hoverEffect_label.setFixedWidth(180)
+ self.hover_effect = QComboBox()
+ self.hover_effect.addItems(["Disable", "Brighten", "Glow", "Brighten + Glow"])
+ self.hover_effect.setToolTip("{0} Disable -> Buttons won't change as you hover\
+ over them.<hr> Brighten -> The text or the background color will get brightened\
+ as you hover over them.<br><img src='{2}/hoverEffect_brighten.png'><hr> Glow ->\
+ There will be a shadow around the button as you hover them.<br><img src='{2}/hoverEffect_glow.png'><hr>\
+ Glow + Brighten -> Combines both glow and brighten effects.<br> <img src='{2}/hoverEffect_glowBrighten.png'>{1}".format(begin, end, images))
+ self.hover_effect.setMinimumWidth(180)
+ hoverEffect_holder = QHBoxLayout()
+ hoverEffect_holder.addWidget(hoverEffect_label)
+ hoverEffect_holder.addWidget(self.hover_effect)
+ hoverEffect_holder.addStretch()
+ activeIndicator_label = QLabel("Active Indicator:")
+ activeIndicator_label.setToolTip("{0} Changes the way active review button looks. active button\
+ is the button that is clicked if you press spacebar or enter.<hr> This option can not change active\
+ indicator for neon and fill buttons as it's disabled on those designs. {1}".format(begin, end))
+ activeIndicator_label.setFixedWidth(180)
+ self.active_indicator = QComboBox()
+ self.active_indicator.addItems(["Disable", "Border", "Glow"])
+ self.active_indicator.setToolTip("{0} Indicator is turned off and all review buttons are the\
+ same.<br> {1} <img src='{2}/activeIndicator_none.png'>{0}<hr> Indicator is\
+ set on border and there is a thin border around active button.<br>\
+ {1} <img src='{2}/activeIndicator_border.png'>{0}<hr> Indicator is set on\
+ glow and active button is glowing. <br> {1}\
+ <img src='{2}/activeIndicator_glow.png'>".format(begin, end, images))
+ self.active_indicator.setMinimumWidth(180)
+ activeIndicator_holder = QHBoxLayout()
+ activeIndicator_holder.addWidget(activeIndicator_label)
+ activeIndicator_holder.addWidget(self.active_indicator)
+ activeIndicator_holder.addStretch()
+ cursorStyle_label = QLabel("Cursor Style:")
+ cursorStyle_label.setToolTip("{0}Changes the cursor style when hovered over buttons.{1}".format(begin, end))
+ cursorStyle_label.setFixedWidth(180)
+ self.cursor_style = QComboBox()
+ self.cursor_style.addItems(["Normal", "Pointer"])
+ self.cursor_style.setFixedWidth(180)
+ cursorStyle_holder = QHBoxLayout()
+ cursorStyle_holder.addWidget(cursorStyle_label)
+ cursorStyle_holder.addWidget(self.cursor_style)
+ cursorStyle_holder.addStretch()
+ showAnswerBorderType_label = QLabel("Show Answer Border Color Style:")
+ showAnswerBorderType_label.setToolTip("{0}Changes how show answer border color behaves.<hr>\
+ if set on \"Fixed\" it's border color will be the same as other bottombar buttons.<br>\
+ if set on \"Bases on Card Ease\" it's color will change based on card ease.\
+ <hr> you can change it's color for each ease range in colors tab.{1}".format(begin, end))
+ showAnswerBorderType_label.setFixedWidth(180)
+ self.showAnswerBorderColor_style = QComboBox()
+ self.showAnswerBorderColor_style.addItems(["Fixed", "Show Based on Card Ease"])
+ self.showAnswerBorderColor_style.setFixedWidth(180)
+ showAnswerBorderType_holder = QHBoxLayout()
+ showAnswerBorderType_holder.addWidget(showAnswerBorderType_label)
+ showAnswerBorderType_holder.addWidget(self.showAnswerBorderColor_style)
+ showAnswerBorderType_holder.addStretch()
+ intervalStyle_label = QLabel("Button Interval Style:")
+ intervalStyle_label.setToolTip("{0}Changes the style of button intervals.{1}".format(begin, end))
+ intervalStyle_label.setFixedWidth(180)
+ self.interval_style = QComboBox()
+ self.interval_style.addItems(["Stock", "Colored Stock", "Inside the Buttons"])
+ self.interval_style.setFixedWidth(180)
+ intervalStyle_holder = QHBoxLayout()
+ intervalStyle_holder.addWidget(intervalStyle_label)
+ intervalStyle_holder.addWidget(self.interval_style)
+ intervalStyle_holder.addStretch()
+ buttonTransitionTime_label = QLabel("Button Transition Time:")
+ buttonTransitionTime_label.setToolTip("{0}Changes button animation time for fill and neon styles.{1}".format(begin, end))
+ buttonTransitionTime_label.setFixedWidth(180)
+ self.buttonTransition_time = QSpinBox()
+ self.buttonTransition_time.setMinimum(0)
+ self.buttonTransition_time.setMaximum(10000)
+ self.buttonTransition_time.setSingleStep(20)
+ self.buttonTransition_time.setFixedWidth(180)
+ buttonTransitionTime_ms = QLabel("ms")
+ buttonTransitionTime_holder = QHBoxLayout()
+ buttonTransitionTime_holder.addWidget(buttonTransitionTime_label)
+ buttonTransitionTime_holder.addWidget(self.buttonTransition_time)
+ buttonTransitionTime_holder.addWidget(buttonTransitionTime_ms)
+ buttonTransitionTime_holder.addStretch()
+ buttonBorderRadius_label = QLabel("Button Border Radius:")
+ buttonBorderRadius_label.setToolTip("{0}Changer the roundness of the buttons.{1}".format(begin, end))
+ buttonBorderRadius_label.setFixedWidth(180)
+ self.buttonBorderRadius = QSpinBox()
+ self.buttonBorderRadius.setMinimum(0)
+ self.buttonBorderRadius.setMaximum(50)
+ self.buttonBorderRadius.setSingleStep(1)
+ self.buttonBorderRadius.setFixedWidth(180)
+ buttonBorderRadius_px = QLabel("px")
+ buttonBorderRadius_holder = QHBoxLayout()
+ buttonBorderRadius_holder.addWidget(buttonBorderRadius_label)
+ buttonBorderRadius_holder.addWidget(self.buttonBorderRadius)
+ buttonBorderRadius_holder.addWidget(buttonBorderRadius_px)
+ def buttonStyle_signal():
+ buttonStyle_index = self.button_style.currentIndex()
+ self.hover_effect.setDisabled(True)
+ if buttonStyle_index in [0, 1, 2, 3]:
+ self.hover_effect.setEnabled(True)
+ self.active_indicator.setDisabled(True)
+ if buttonStyle_index in [0, 1, 2, 3]:
+ self.active_indicator.setEnabled(True)
+ # self.cursor_style.setDisabled(True)
+ self.buttonTransition_time.setDisabled(True)
+ if buttonStyle_index in [4, 5, 6, 7]:
+ # self.cursor_style.setEnabled(True)
+ self.buttonTransition_time.setEnabled(True)
+ buttonStyle_signal()
+ self.button_style.currentIndexChanged.connect(buttonStyle_signal)
+ self.style_mainScreenButtons = QCheckBox("Style Main Screen Buttons")
+ self.style_mainScreenButtons.setToolTip("{0}Changes style of main screen and deck overview buttons if enabled.<hr>\
+ <img src='{2}/changeMainScreenButtons.png'><br>\
+ <img src='{2}/changeMainScreenButtons2.png'><br>\
+ <img src='{2}/changeMainScreenButtons3.png'>{1}".format(begin, end, images))
+ self.style_mainScreenButtons.setFixedWidth(180)
+ tab1line5 = QHBoxLayout()
+ tab1line5.addWidget(self.style_mainScreenButtons)
+ tab1line5.addStretch()
+ layout = QVBoxLayout()
+ layout.addLayout(buttonStyle_holder)
+ layout.addLayout(bottombarButtonsStyle_holder)
+ layout.addLayout(hoverEffect_holder)
+ layout.addLayout(activeIndicator_holder)
+ layout.addLayout(cursorStyle_holder)
+ layout.addLayout(showAnswerBorderType_holder)
+ layout.addLayout(intervalStyle_holder)
+ layout.addLayout(buttonTransitionTime_holder)
+ layout.addLayout(buttonBorderRadius_holder)
+ layout.addLayout(tab1line5)
+ layout.addStretch()
+ layout_holder = QWidget()
+ layout_holder.setLayout(layout)
+ self.tab1 = QScrollArea()
+ self.tab1.setFixedWidth(640)
+ self.tab1.setAlignment(Qt.AlignHCenter)
+ self.tab1.setWidgetResizable(True)
+ self.tab1.setWidget(layout_holder)
+
+ def createSecondTab(self):
+ begin = self.begin
+ end = self.end
+ images = self.images
+ reviewTooltip_label = QLabel("Review Confirmation Tooltip:")
+ reviewTooltip_label.setToolTip("{0}Shows a tooltip when you press any of\
+ review buttons, showing you what button you have pressed.{1}".format(begin, end))
+ reviewTooltip_label.setFixedWidth(180)
+ self.reviewTooltip_on = QRadioButton("On")
+ self.reviewTooltip_on.setFixedWidth(90)
+ self.reviewTooltip_off = QRadioButton("off")
+ self.reviewTooltip_off.setFixedWidth(90)
+ reviewTooltip_holder = QHBoxLayout()
+ reviewTooltip_holder.addWidget(reviewTooltip_label)
+ reviewTooltip_holder.addWidget(self.reviewTooltip_on)
+ reviewTooltip_holder.addWidget(self.reviewTooltip_off)
+ reviewTooltip_holder.addStretch()
+ tab2box1 = QGroupBox()
+ tab2box1.setLayout(reviewTooltip_holder)
+ reviewTooltipStyle_label = QLabel("Tooltip Position:")
+ reviewTooltipStyle_label.setToolTip("{0}Changes the position of answer tooltip.{1}".format(begin, end))
+ reviewTooltipStyle_label.setFixedWidth(180)
+ self.reviewTooltip_style = QComboBox()
+ self.reviewTooltip_style.addItems(["On Buttons", "Fixed Position"])
+ self.reviewTooltip_style.setToolTip("{0}On buttons -> Shows the tooltip on the button that you have pressed<hr>\
+ Fixed Position -> Shows all the tooltips in a position that you have chosen in review toolip position box.{1}".format(begin, end))
+ self.reviewTooltip_style.setFixedWidth(180)
+ reviewTooltipStyle_holder = QHBoxLayout()
+ reviewTooltipStyle_holder.addWidget(reviewTooltipStyle_label)
+ reviewTooltipStyle_holder.addWidget(self.reviewTooltip_style)
+ reviewTooltipStyle_holder.addStretch()
+ reviewTooltipTimer_label = QLabel("Tooltip Show Duration:")
+ reviewTooltipTimer_label.setToolTip("{0}Changes lenghth of the period that tooltip is shown.<hr>the unit is millisecond, 1000ms = 1s{1} (I know everybody knows this, put it here just in case :|)".format(begin, end))
+ reviewTooltipTimer_label.setFixedWidth(180)
+ self.reviewTooltip_timer = QSpinBox()
+ self.reviewTooltip_timer.setFixedWidth(180)
+ self.reviewTooltip_timer.setMinimum(100)
+ self.reviewTooltip_timer.setMaximum(10000)
+ reviewerTooltipTimer_ms = QLabel("ms")
+ reviewTooltipTimer_holder = QHBoxLayout()
+ reviewTooltipTimer_holder.addWidget(reviewTooltipTimer_label)
+ reviewTooltipTimer_holder.addWidget(self.reviewTooltip_timer)
+ reviewTooltipTimer_holder.addWidget(reviewerTooltipTimer_ms)
+ reviewTooltipTimer_holder.addStretch()
+ reviewTooltipTextColor_label = QLabel("Tooltip Text Color:")
+ reviewTooltipTextColor_label.setToolTip("{0}Changes color of the text inside tooltips.{1}".format(begin, end))
+ reviewTooltipTextColor_label.setFixedWidth(180)
+ self.reviewTooltipTextColor_button = QPushButton()
+ self.reviewTooltipTextColor_button.setFixedWidth(180)
+ self.reviewTooltipTextColor_button.clicked.connect(lambda: self.getNewColor("reviewTooltipText_color", self.reviewTooltipTextColor_button))
+ reviewTooltipTextColor_holder = QHBoxLayout()
+ reviewTooltipTextColor_holder.addWidget(reviewTooltipTextColor_label)
+ reviewTooltipTextColor_holder.addWidget(self.reviewTooltipTextColor_button)
+ reviewTooltipTextColor_holder.addStretch()
+ tab2line2 = QVBoxLayout()
+ tab2line2.addLayout(reviewTooltipStyle_holder)
+ tab2line2.addLayout(reviewTooltipTimer_holder)
+ tab2line2.addLayout(reviewTooltipTextColor_holder)
+ tab2box2 = QGroupBox()
+ tab2box2.setLayout(tab2line2)
+ self.reviewTooltipPositionX = QSlider(Qt.Horizontal)
+ self.reviewTooltipPositionX.setFixedWidth(200)
+ self.reviewTooltipPositionX.setMinimum(0)
+ self.reviewTooltipPositionX.setMaximum(1850)
+ self.reviewTooltipPositionX.setPageStep(100)
+ self.reviewTooltipPositionX.setSliderPosition(0)
+ reviewerTooltipPosition_holder = QHBoxLayout()
+ self.reviewTooltipPositionY = QSlider(Qt.Vertical)
+ self.reviewTooltipPositionY.setFixedHeight(200)
+ self.reviewTooltipPositionY.setMinimum(-950)
+ self.reviewTooltipPositionY.setMaximum(0)
+ self.reviewTooltipPositionY.setPageStep(100)
+ self.reviewTooltipPositionY.setSliderPosition(0)
+ reviewerTooltipPosition_holder = QHBoxLayout()
+ reviewerTooltipPosition_holder.addWidget(self.reviewTooltipPositionX)
+ reviewerTooltipPosition_holder.addWidget(self.reviewTooltipPositionY)
+ reviewerTooltipPosition_holder.addStretch()
+ tab2line3 = QVBoxLayout()
+ tab2line3.addLayout(reviewerTooltipPosition_holder)
+ tab2box3 = QGroupBox("Tooltip Position")
+ tab2box3.setToolTip("{0}Changes position of the fixed tooltip.<hr>\
+ (<font color=red># NOTE:</font> If your resulotion is not 1920 x 1080, it's not accurate, but you\
+ can find the place that you wanna put the tooltip on, by toying with the sliders\
+ and restarting anki till you get the desired result.<br>\
+ <font color=red># NOTE:</font> If your resulotion is 1920 x 1080 the sliders are accurate for\
+ maximized anki window.<br> <font color=red># NOTE:</font> If you set the position for a window that\
+ it's size is for example 500 x 500, the position will not be accurate when you\
+ change anki's window size to any other size. and if you decide to resize anki's\
+ window, you should set the positions again in order for the tooltip to be in the\
+ position you want.){1}".format(begin, end))
+ tab2box3.setLayout(tab2line3)
+ tab2box3.setDisabled(True)
+ if self.reviewTooltip_style.currentIndex() == 1:
+ tab2box3.setEnabled(True)
+ self.reviewTooltip_off.toggled.connect(tab2box3.setDisabled)
+ self.reviewTooltip_style.currentIndexChanged.connect(tab2box3.setEnabled)
+ tab2box2.setDisabled(True)
+ if self.reviewTooltip_on.isChecked():
+ tab2box2.setEnabled(True)
+ self.reviewTooltip_on.toggled.connect(tab2box2.setEnabled)
+ layout = QVBoxLayout()
+ layout.addWidget(tab2box1)
+ layout.addWidget(tab2box2)
+ layout.addWidget(tab2box3)
+ layout.addStretch()
+ layout_holder = QWidget()
+ layout_holder.setLayout(layout)
+ self.tab2 = QScrollArea()
+ self.tab2.setFixedWidth(640)
+ self.tab2.setAlignment(Qt.AlignHCenter)
+ self.tab2.setWidgetResizable(True)
+ self.tab2.setWidget(layout_holder)
+
+ def createThirdTab(self):
+ begin = self.begin
+ end = self.end
+ images = self.images
+ self.info = QCheckBox("Info")
+ self.info.setToolTip("{0} If enabled adds info button to review bottombar. {1}".format(begin, end))
+ self.skip = QCheckBox("Skip")
+ self.skip.setToolTip("{0} If enabled adds skip card button to review bottombar. {1}".format(begin, end))
+ self.showSkipped = QCheckBox("Show Skipped")
+ self.showSkipped.setToolTip("{0} If enabled adds show skipped button to review bottombar. {1}".format(begin, end))
+ self.undo = QCheckBox("Undo")
+ self.undo.setToolTip("{0} If enabled adds undo review button to review bottombar. {1}".format(begin, end))
+ extraButtonsPart = QHBoxLayout()
+ extraButtonsPart.addWidget(self.info)
+ extraButtonsPart.addWidget(self.skip)
+ extraButtonsPart.addWidget(self.showSkipped)
+ extraButtonsPart.addWidget(self.undo)
+ extraButtonsBox = QGroupBox("Extra Buttons")
+ extraButtonsBox.setLayout(extraButtonsPart)
+ self.hideHard = QCheckBox("Hide Hard")
+ self.hideHard.setToolTip("{0}Hides the Hard button.{1}".format(begin, end))
+ self.hideGood = QCheckBox("Hide Good")
+ self.hideGood.setToolTip("{0}Hides the Good button.{1}".format(begin, end))
+ self.hideEasy = QCheckBox("Hide Easy")
+ self.hideEasy.setToolTip("{0}Hides the Easy button.{1}".format(begin, end))
+ hideButtonsPart = QHBoxLayout()
+ hideButtonsPart.addWidget(self.hideHard)
+ hideButtonsPart.addWidget(self.hideGood)
+ hideButtonsPart.addWidget(self.hideEasy)
+ hideButtonsPart.addWidget(QLabel(""))
+ hideButtonsBox = QGroupBox("Hide Buttons")
+ hideButtonsBox.setLayout(hideButtonsPart)
+ infoPosition_label = QLabel("Info:")
+ infoPosition_label.setToolTip("{0} Changes info button position in bottombar. {1}".format(begin, end))
+ self.left_info = QRadioButton("Left")
+ self.middleLeft_info = QRadioButton("Middle left")
+ self.middleRight_info = QRadioButton("Middle right")
+ self.right_info = QRadioButton("Right")
+ infoPosition_holder = QHBoxLayout()
+ infoPosition_holder.addWidget(infoPosition_label)
+ infoPosition_holder.addWidget(self.left_info)
+ infoPosition_holder.addWidget(self.middleLeft_info)
+ infoPosition_holder.addWidget(self.middleRight_info)
+ infoPosition_holder.addWidget(self.right_info)
+ infoButtonPositionBox = QGroupBox()
+ infoButtonPositionBox.setDisabled(True)
+ if self.info.isChecked():
+ self.infoButtonPositionBox.setEnabled(True)
+ self.info.toggled.connect(infoButtonPositionBox.setEnabled)
+ infoButtonPositionBox.setLayout(infoPosition_holder)
+ skipPosition_label = QLabel("Skip:")
+ skipPosition_label.setToolTip("{0} Changes skip button position in bottombar. {1}".format(begin, end))
+ self.left_skip = QRadioButton("Left")
+ self.middleLeft_skip = QRadioButton("Middle left")
+ self.middleRight_skip = QRadioButton("Middle right")
+ self.right_skip = QRadioButton("Right")
+ skipPosition_holder = QHBoxLayout()
+ skipPosition_holder.addWidget(skipPosition_label)
+ skipPosition_holder.addWidget(self.left_skip)
+ skipPosition_holder.addWidget(self.middleLeft_skip)
+ skipPosition_holder.addWidget(self.middleRight_skip)
+ skipPosition_holder.addWidget(self.right_skip)
+ skipButtonPositionBox = QGroupBox()
+ skipButtonPositionBox.setDisabled(True)
+ if self.skip.isChecked():
+ skipButtonPositionBox.setEnabled(True)
+ self.skip.toggled.connect(skipButtonPositionBox.setEnabled)
+ skipButtonPositionBox.setLayout(skipPosition_holder)
+ showSkippedPosition_label = QLabel("Show Skipped:")
+ showSkippedPosition_label.setToolTip("{0} Changes show skipped button position in bottombar. {1}".format(begin, end))
+ self.left_showSkipped = QRadioButton("Left")
+ self.middleLeft_showSkipped = QRadioButton("Middle left")
+ self.middleRight_showSkipped = QRadioButton("Middle right")
+ self.right_showSkipped = QRadioButton("Right")
+ showSkippedPosition_holder = QHBoxLayout()
+ showSkippedPosition_holder.addWidget(showSkippedPosition_label)
+ showSkippedPosition_holder.addWidget(self.left_showSkipped)
+ showSkippedPosition_holder.addWidget(self.middleLeft_showSkipped)
+ showSkippedPosition_holder.addWidget(self.middleRight_showSkipped)
+ showSkippedPosition_holder.addWidget(self.right_showSkipped)
+ showSkippedButtonPositionBox = QGroupBox()
+ showSkippedButtonPositionBox.setDisabled(True)
+ if self.showSkipped.isChecked():
+ showSkippedButtonPositionBox.setEnabled(True)
+ self.showSkipped.toggled.connect(showSkippedButtonPositionBox.setEnabled)
+ showSkippedButtonPositionBox.setLayout(showSkippedPosition_holder)
+ undoPosition_label = QLabel("Undo:")
+ undoPosition_label.setToolTip("{0} Changes undo review button position in bottombar. {1}".format(begin, end))
+ self.left_undo = QRadioButton("Left")
+ self.middleLeft_undo = QRadioButton("Middle left")
+ self.middleRight_undo = QRadioButton("Middle right")
+ self.right_undo = QRadioButton("Right")
+ undoPosition_holder = QHBoxLayout()
+ undoPosition_holder.addWidget(undoPosition_label)
+ undoPosition_holder.addWidget(self.left_undo)
+ undoPosition_holder.addWidget(self.middleLeft_undo)
+ undoPosition_holder.addWidget(self.middleRight_undo)
+ undoPosition_holder.addWidget(self.right_undo)
+ undoButtonPositionBox = QGroupBox()
+ undoButtonPositionBox.setDisabled(True)
+ if self.undo.isChecked():
+ undoButtonPositionBox.setEnabled(True)
+ self.undo.toggled.connect(undoButtonPositionBox.setEnabled)
+ undoButtonPositionBox.setLayout(undoPosition_holder)
+ buttonPositionsPart = QVBoxLayout()
+ buttonPositionsPart.addWidget(infoButtonPositionBox)
+ buttonPositionsPart.addWidget(skipButtonPositionBox)
+ buttonPositionsPart.addWidget(showSkippedButtonPositionBox)
+ buttonPositionsPart.addWidget(undoButtonPositionBox)
+ buttonPositionsBox = QGroupBox("Button Positions")
+ buttonPositionsBox.setLayout(buttonPositionsPart)
+ infoShortcut_label = QLabel("Info:")
+ infoShortcut_label.setToolTip("{0} Changes show card info shortcut.<hr> Shortcut will work even if\
+ you disable the Info button.<hr> Info button can be a sngle key\
+ like \"i\" or \"f4\" or a combination of keys like \"ctrl+i\" or \"alt+i\".<hr>\
+ <font color=red>NOTE: </font>Make sure the shortcut you want to set for the\
+ button isn't already in use by anki itself or another add-on. {1}".format(begin, end))
+ infoShortcut_label.setFixedWidth(125)
+ self.infoShortcut_button = QPushButton(self)
+ self.infoShortcut_button.setFixedWidth(300)
+ self.infoShortcut_button.clicked.connect(lambda: self.showGetShortcut("info_shortcut"))
+ infoShortcut_holder = QHBoxLayout()
+ infoShortcut_holder.addWidget(infoShortcut_label)
+ infoShortcut_holder.addStretch()
+ infoShortcut_holder.addWidget(self.infoShortcut_button)
+ skipShortcut_label = QLabel("Skip:")
+ skipShortcut_label.setToolTip("{0} Changes skip card shortcut.<hr> Shortcut will work even if\
+ you disable the skip button.<hr> skip button can be a sngle key\
+ like \"s\" or \"f6\" or a combination of keys like \"ctrl+s\" or \"alt+s\".<hr>\
+ <font color=red>NOTE: </font>Make sure the shortcut you want to set for the\
+ button isn't already in use by anki itself or another add-on. {1}".format(begin, end))
+ skipShortcut_label.setFixedWidth(125)
+ self.skipShortcut_button = QPushButton()
+ self.skipShortcut_button.setFixedWidth(300)
+ self.skipShortcut_button.clicked.connect(lambda: self.showGetShortcut("skip_shortcut"))
+ skipShortcut_holder = QHBoxLayout()
+ skipShortcut_holder.addWidget(skipShortcut_label)
+ skipShortcut_holder.addStretch()
+ skipShortcut_holder.addWidget(self.skipShortcut_button)
+ showSkippedShortcut_label = QLabel("Show Skipped:")
+ showSkippedShortcut_label.setToolTip("{0} Changes Show Skipped cards shortcut.<hr> Shortcut will work even if\
+ you disable the Show Skipped Cards button.<hr> Show Skipped cards button shortcut can be a sngle key\
+ like \"s\" or \"f6\" or a combination of keys like \"ctrl+s\" or \"alt+s\".<hr>\
+ <font color=red>NOTE: </font>Make sure the shortcut you want to set for the\
+ button isn't already in use by anki itself or another add-on. {1}".format(begin, end))
+ showSkippedShortcut_label.setFixedWidth(125)
+ self.showSkippedShortcut_button = QPushButton()
+ self.showSkippedShortcut_button.setFixedWidth(300)
+ self.showSkippedShortcut_button.clicked.connect(lambda: self.showGetShortcut("showSkipped_shortcut"))
+ showSkippedShortcut_holder = QHBoxLayout()
+ showSkippedShortcut_holder.addWidget(showSkippedShortcut_label)
+ showSkippedShortcut_holder.addStretch()
+ showSkippedShortcut_holder.addWidget(self.showSkippedShortcut_button)
+ undoShortcut_label = QLabel("Undo:")
+ undoShortcut_label.setToolTip("{0} Changes undo review shortcut.<hr> Shortcut will work even if\
+ you disable the undo button.<hr> undo button shortcut can be a sngle key\
+ like \"z\" or \"f1\" or a combination of keys like \"ctrl+z\" or \"alt+z\".<hr>\
+ <font color=red>NOTE: </font>Make sure the shortcut you want to set for the button isn't already\
+ in use by anki itself or another add-on.<hr> the default shortcut for this\
+ action is (Ctrl+Z), by changing th shortcut here, the default shortcut\
+ will still wok, in other words, if you set a shortcut for undo review\
+ here, you will have two shortcuts for it, one is the default (Ctrl+Z)\
+ and the other is the shortcut that you have set. {1}".format(begin, end))
+ undoShortcut_label.setFixedWidth(125)
+ self.undoShortcut_button = QPushButton()
+ self.undoShortcut_button.setFixedWidth(300)
+ self.undoShortcut_button.clicked.connect(lambda: self.showGetShortcut("undo_shortcut"))
+ undoShortcut_holder = QHBoxLayout()
+ undoShortcut_holder.addWidget(undoShortcut_label)
+ undoShortcut_holder.addStretch()
+ undoShortcut_holder.addWidget(self.undoShortcut_button)
+ buttonShortcutsPart = QVBoxLayout()
+ buttonShortcutsPart.addLayout(infoShortcut_holder)
+ buttonShortcutsPart.addLayout(skipShortcut_holder)
+ buttonShortcutsPart.addLayout(showSkippedShortcut_holder)
+ buttonShortcutsPart.addLayout(undoShortcut_holder)
+ buttonShortcutsBox = QGroupBox("Button Shortcuts")
+ buttonShortcutsBox.setLayout(buttonShortcutsPart)
+ layout = QVBoxLayout()
+ layout.addWidget(extraButtonsBox)
+ layout.addWidget(hideButtonsBox)
+ layout.addWidget(buttonPositionsBox)
+ layout.addWidget(buttonShortcutsBox)
+ layout.addStretch()
+ layout_holder = QWidget()
+ layout_holder.setLayout(layout)
+ self.tab3 = QScrollArea()
+ self.tab3.setFixedWidth(640)
+ self.tab3.setAlignment(Qt.AlignHCenter)
+ self.tab3.setWidgetResizable(True)
+ self.tab3.setWidget(layout_holder)
+
+ def createFourthTab(self):
+ begin = self.begin
+ end = self.end
+ images = self.images
+ customSizes_label = QLabel('Custom Sizes:')
+ customSizes_label.setToolTip("{0} Enables and disables custom button sizes.<hr> If Enabled\
+ button sizes will change according to the sizes you set for each button.<hr>\
+ If disabled button sizes will be set on default size.<hr> <font color=red>NOTE: </font>If you use\
+ wide buttons, you won't be able to change review buttons width and even when\
+ you disable custom button sizes, review buttons will be wide. {1}".format(begin, end))
+ customSizes_label.setFixedWidth(180)
+ self.customSizes_on = QRadioButton("On")
+ self.customSizes_on.setToolTip("{0} Button sizes is disabled and buttons height and width\
+ is set to height and width that we have set.<hr> {1} <img src='{2}/buttonSizes_on.png'>\
+ <br><img src='{2}/buttonSizes_on2.png'>".format(begin, end, images))
+ self.customSizes_on.setFixedWidth(90)
+ self.customSizes_off = QRadioButton("Off")
+ self.customSizes_off.setToolTip("{0} Button sizes is disabled and all buttons are in default size.<hr>\
+ {1} <img src='{2}/buttonSizes_off.png'><img src='{2}/buttonSizes_off2.png'>".format(begin, end, images))
+ self.customSizes_off.setFixedWidth(90)
+ tab4line1 = QHBoxLayout()
+ tab4line1.addWidget(customSizes_label)
+ tab4line1.addWidget(self.customSizes_on)
+ tab4line1.addWidget(self.customSizes_off)
+ tab4line1.addStretch()
+ tab4box1 = QGroupBox()
+ tab4box1.setLayout(tab4line1)
+ textSize_label = QLabel("Buttons Text Size:")
+ textSize_label.setToolTip("{0}Sets the text size for all bottombar buttons.\
+ It only works on review screen buttons and it will not affect main screen\
+ and study screen buttons{1}".format(begin, end))
+ textSize_label.setFixedWidth(180)
+ self.text_size = QSpinBox()
+ self.text_size.setFixedWidth(120)
+ self.text_size.setMinimum(0)
+ self.text_size.setMaximum(200)
+ textSize_px = QLabel("px")
+ textSize_holder = QHBoxLayout()
+ textSize_holder.addWidget(textSize_label)
+ textSize_holder.addWidget(self.text_size)
+ textSize_holder.addWidget(textSize_px)
+ buttonsHeight_label = QLabel("Bottombar Buttons Height:")
+ buttonsHeight_label.setToolTip("{0} Sets height for all bottombar buttons including edit, info,\
+ ski, show answer, undo review, more and review buttons.".format(begin, end))
+ buttonsHeight_label.setFixedWidth(180)
+ self.buttons_height = QSpinBox()
+ self.buttons_height.setFixedWidth(120)
+ self.buttons_height.setMinimum(0)
+ self.buttons_height.setMaximum(200)
+ buttonsHeight_px = QLabel("px")
+ buttonsHeight_holder = QHBoxLayout()
+ buttonsHeight_holder.addWidget(buttonsHeight_label)
+ buttonsHeight_holder.addWidget(self.buttons_height)
+ buttonsHeight_holder.addWidget(buttonsHeight_px)
+ reviewButtonsWidth_label = QLabel("Review Buttons Width:")
+ reviewButtonsWidth_label.setToolTip("{0} Sets width for review buttons\
+ (again, hard, good and easy buttons).{1}".format(begin, end))
+ reviewButtonsWidth_label.setFixedWidth(180)
+ self.reviewButtons_width = QSpinBox()
+ self.reviewButtons_width.setFixedWidth(120)
+ self.reviewButtons_width.setMinimum(0)
+ self.reviewButtons_width.setMaximum(400)
+ reviewButtonsWidth_px = QLabel("px")
+ reviewButtonsWidth_holder = QHBoxLayout()
+ reviewButtonsWidth_holder.addWidget(reviewButtonsWidth_label)
+ reviewButtonsWidth_holder.addWidget(self.reviewButtons_width)
+ reviewButtonsWidth_holder.addWidget(reviewButtonsWidth_px)
+ editWidth_label = QLabel("Edit Width:")
+ editWidth_label.setToolTip("{0} Sets width for edit button.{1}".format(begin, end))
+ editWidth_label.setFixedWidth(180)
+ self.edit_width = QSpinBox()
+ self.edit_width.setFixedWidth(120)
+ self.edit_width.setMinimum(0)
+ self.edit_width.setMaximum(400)
+ editWidth_px = QLabel("px")
+ editWidth_holder = QHBoxLayout()
+ editWidth_holder.addWidget(editWidth_label)
+ editWidth_holder.addWidget(self.edit_width)
+ editWidth_holder.addWidget(editWidth_px)
+ answerWidth_label = QLabel("Show Answer Width:")
+ answerWidth_label.setToolTip("{0} Sets width for show answer button.{1}".format(begin, end))
+ answerWidth_label.setFixedWidth(180)
+ self.answer_width = QSpinBox()
+ self.answer_width.setFixedWidth(120)
+ self.answer_width.setMinimum(0)
+ self.answer_width.setMaximum(400)
+ answerWidth_px = QLabel("px")
+ answerWidth_holder = QHBoxLayout()
+ answerWidth_holder.addWidget(answerWidth_label)
+ answerWidth_holder.addWidget(self.answer_width)
+ answerWidth_holder.addWidget(answerWidth_px)
+ moreWidth_label = QLabel("More Width:")
+ moreWidth_label.setToolTip("{0} Sets width for more button.{1}".format(begin, end))
+ moreWidth_label.setFixedWidth(180)
+ self.more_width = QSpinBox()
+ self.more_width.setFixedWidth(120)
+ self.more_width.setMinimum(0)
+ self.more_width.setMaximum(400)
+ moreWidth_px = QLabel("px")
+ moreWidth_holder = QHBoxLayout()
+ moreWidth_holder.addWidget(moreWidth_label)
+ moreWidth_holder.addWidget(self.more_width)
+ moreWidth_holder.addWidget(moreWidth_px)
+ infoWidth_label = QLabel("Info Width:")
+ infoWidth_label.setToolTip("{0} Sets width for info button.{1}".format(begin, end))
+ infoWidth_label.setFixedWidth(180)
+ self.info_width = QSpinBox()
+ self.info_width.setFixedWidth(120)
+ self.info_width.setMinimum(0)
+ self.info_width.setMaximum(400)
+ infoWidth_px = QLabel("px")
+ infoWidth_holder = QHBoxLayout()
+ infoWidth_holder.addWidget(infoWidth_label)
+ infoWidth_holder.addWidget(self.info_width)
+ infoWidth_holder.addWidget(infoWidth_px)
+ skipWidth_label = QLabel("Skip Width:")
+ skipWidth_label.setToolTip("{0} Sets width for skip button.{1}".format(begin, end))
+ skipWidth_label.setFixedWidth(180)
+ self.skip_width = QSpinBox()
+ self.skip_width.setFixedWidth(120)
+ self.skip_width.setMinimum(0)
+ self.skip_width.setMaximum(400)
+ skipWidth_px = QLabel("px")
+ skipWidth_holder = QHBoxLayout()
+ skipWidth_holder.addWidget(skipWidth_label)
+ skipWidth_holder.addWidget(self.skip_width)
+ skipWidth_holder.addWidget(skipWidth_px)
+ showSkippedWidth_label = QLabel("Show Skipped Width:")
+ showSkippedWidth_label.setToolTip("{0} Sets width for Show Skipped button.{1}".format(begin, end))
+ showSkippedWidth_label.setFixedWidth(180)
+ self.showSkipped_width = QSpinBox()
+ self.showSkipped_width.setFixedWidth(120)
+ self.showSkipped_width.setMinimum(0)
+ self.showSkipped_width.setMaximum(400)
+ showSkippedWidth_px = QLabel("px")
+ showSkippedWidth_holder = QHBoxLayout()
+ showSkippedWidth_holder.addWidget(showSkippedWidth_label)
+ showSkippedWidth_holder.addWidget(self.showSkipped_width)
+ showSkippedWidth_holder.addWidget(showSkippedWidth_px)
+ undoWidth_label = QLabel("Undo Width:")
+ undoWidth_label.setToolTip("{0} Sets width for undo button.{1}".format(begin, end))
+ undoWidth_label.setFixedWidth(180)
+ self.undo_width = QSpinBox()
+ self.undo_width.setFixedWidth(120)
+ self.undo_width.setMinimum(0)
+ self.undo_width.setMaximum(400)
+ undoWidth_px = QLabel("px")
+ undoWidth_holder = QHBoxLayout()
+ undoWidth_holder.addWidget(undoWidth_label)
+ undoWidth_holder.addWidget(self.undo_width)
+ undoWidth_holder.addWidget(undoWidth_px)
+ tab4line2 = QVBoxLayout()
+ tab4line2.addLayout(textSize_holder)
+ tab4line2.addLayout(buttonsHeight_holder)
+ tab4line2.addLayout(reviewButtonsWidth_holder)
+ tab4line2.addLayout(editWidth_holder)
+ tab4line2.addLayout(answerWidth_holder)
+ tab4line2.addLayout(moreWidth_holder)
+ tab4line2.addLayout(infoWidth_holder)
+ tab4line2.addLayout(skipWidth_holder)
+ tab4line2.addLayout(showSkippedWidth_holder)
+ tab4line2.addLayout(undoWidth_holder)
+ tab4box2 = QGroupBox()
+ tab4box2.setDisabled(True)
+ if self.customSizes_on.isChecked():
+ tab4box2.setEnabled(True)
+ self.customSizes_on.toggled.connect(tab4box2.setEnabled)
+ tab4box2.setLayout(tab4line2)
+ layout = QVBoxLayout()
+ layout.addWidget(tab4box1)
+ layout.addWidget(tab4box2)
+ layout.addStretch()
+ layout_holder = QWidget()
+ layout_holder.setLayout(layout)
+ self.tab4 = QScrollArea()
+ self.tab4.setFixedWidth(640)
+ self.tab4.setAlignment(Qt.AlignHCenter)
+ self.tab4.setWidgetResizable(True)
+ self.tab4.setWidget(layout_holder)
+
+ def createFifthTab(self):
+ begin = self.begin
+ end = self.end
+ images = self.images
+ buttonLabel_studyNow_label = QLabel("Study Now:")
+ buttonLabel_studyNow_label.setToolTip("{0}Replaces the text for \"Study Now\" Button with your custom text.{1}".format(begin, end))
+ buttonLabel_studyNow_label.setFixedWidth(90)
+ self.buttonLabel_studyNow = QLineEdit()
+ buttonlabel_studyNow_holder = QHBoxLayout()
+ buttonlabel_studyNow_holder.addWidget(buttonLabel_studyNow_label)
+ buttonlabel_studyNow_holder.addWidget(self.buttonLabel_studyNow)
+ buttonLabel_studyNow_box = QGroupBox()
+ buttonLabel_studyNow_box.setLayout(buttonlabel_studyNow_holder)
+ buttonLabel_edit_label = QLabel("Edit:")
+ buttonLabel_edit_label.setToolTip("{0}Replaces the text for \"Edit\" Button with your custom text.{1}".format(begin, end))
+ buttonLabel_edit_label.setFixedWidth(90)
+ self.buttonLabel_edit = QLineEdit()
+ buttonlabel_edit_holder = QHBoxLayout()
+ buttonlabel_edit_holder.addWidget(buttonLabel_edit_label)
+ buttonlabel_edit_holder.addWidget(self.buttonLabel_edit)
+ buttonLabel_edit_box = QGroupBox()
+ buttonLabel_edit_box.setLayout(buttonlabel_edit_holder)
+ firstLine = QHBoxLayout()
+ firstLine.addWidget(buttonLabel_studyNow_box)
+ firstLine.addWidget(buttonLabel_edit_box)
+ buttonLabel_showAnswer_label = QLabel("Show Answer:")
+ buttonLabel_showAnswer_label.setToolTip("{0}Replaces the text for \"Show Answer\" Button with your custom text.{1}".format(begin, end))
+ buttonLabel_showAnswer_label.setFixedWidth(90)
+ self.buttonLabel_showAnswer = QLineEdit()
+ buttonlabel_showAnswer_holder = QHBoxLayout()
+ buttonlabel_showAnswer_holder.addWidget(buttonLabel_showAnswer_label)
+ buttonlabel_showAnswer_holder.addWidget(self.buttonLabel_showAnswer)
+ buttonLabel_showAnswer_box = QGroupBox()
+ buttonLabel_showAnswer_box.setLayout(buttonlabel_showAnswer_holder)
+ buttonLabel_more_label = QLabel("More:")
+ buttonLabel_more_label.setToolTip("{0}Replaces the text for \"More\" Button with your custom text.{1}".format(begin, end))
+ buttonLabel_more_label.setFixedWidth(90)
+ self.buttonLabel_more = QLineEdit()
+ buttonlabel_more_holder = QHBoxLayout()
+ buttonlabel_more_holder.addWidget(buttonLabel_more_label)
+ buttonlabel_more_holder.addWidget(self.buttonLabel_more)
+ buttonLabel_more_box = QGroupBox()
+ buttonLabel_more_box.setLayout(buttonlabel_more_holder)
+ secondLine = QHBoxLayout()
+ secondLine.addWidget(buttonLabel_showAnswer_box)
+ secondLine.addWidget(buttonLabel_more_box)
+ buttonLabel_info_label = QLabel("Info:")
+ buttonLabel_info_label.setToolTip("{0}Replaces the text for \"Info\" Button with your custom text.{1}".format(begin, end))
+ buttonLabel_info_label.setFixedWidth(90)
+ self.buttonLabel_info = QLineEdit()
+ buttonlabel_info_holder = QHBoxLayout()
+ buttonlabel_info_holder.addWidget(buttonLabel_info_label)
+ buttonlabel_info_holder.addWidget(self.buttonLabel_info)
+ buttonLabel_info_box = QGroupBox()
+ buttonLabel_info_box.setLayout(buttonlabel_info_holder)
+ buttonLabel_skip_label = QLabel("Skip:")
+ buttonLabel_skip_label.setToolTip("{0}Replaces the text for \"Skip\" Button with your custom text.{1}".format(begin, end))
+ buttonLabel_skip_label.setFixedWidth(90)
+ self.buttonLabel_skip = QLineEdit()
+ buttonlabel_skip_holder = QHBoxLayout()
+ buttonlabel_skip_holder.addWidget(buttonLabel_skip_label)
+ buttonlabel_skip_holder.addWidget(self.buttonLabel_skip)
+ buttonLabel_skip_box = QGroupBox()
+ buttonLabel_skip_box.setLayout(buttonlabel_skip_holder)
+ thirdLine = QHBoxLayout()
+ thirdLine.addWidget(buttonLabel_info_box)
+ thirdLine.addWidget(buttonLabel_skip_box)
+ buttonLabel_undo_label = QLabel("Undo:")
+ buttonLabel_undo_label.setToolTip("{0}Replaces the text for \"Undo\" Button with your custom text.{1}".format(begin, end))
+ buttonLabel_undo_label.setFixedWidth(90)
+ self.buttonLabel_undo = QLineEdit()
+ buttonlabel_undo_holder = QHBoxLayout()
+ buttonlabel_undo_holder.addWidget(buttonLabel_undo_label)
+ buttonlabel_undo_holder.addWidget(self.buttonLabel_undo)
+ buttonLabel_undo_box = QGroupBox()
+ buttonLabel_undo_box.setLayout(buttonlabel_undo_holder)
+ buttonLabel_showSkipped_label = QLabel("Show Skipped:")
+ buttonLabel_showSkipped_label.setToolTip("{0}Replaces the text for \"Show Skipped\" Button with your custom text.{1}".format(begin, end))
+ buttonLabel_showSkipped_label.setFixedWidth(90)
+ self.buttonLabel_showSkipped = QLineEdit()
+ buttonlabel_showSkipped_holder = QHBoxLayout()
+ buttonlabel_showSkipped_holder.addWidget(buttonLabel_showSkipped_label)
+ buttonlabel_showSkipped_holder.addWidget(self.buttonLabel_showSkipped)
+ buttonLabel_showSkipped_box = QGroupBox()
+ buttonLabel_showSkipped_box.setLayout(buttonlabel_showSkipped_holder)
+ fourthLine = QHBoxLayout()
+ fourthLine.addWidget(buttonLabel_undo_box)
+ fourthLine.addWidget(buttonLabel_showSkipped_box)
+ buttonLabel_again_label = QLabel("Again:")
+ buttonLabel_again_label.setToolTip("{0}Replaces the text for \"Again\" Button with your custom text.{1}".format(begin, end))
+ buttonLabel_again_label.setFixedWidth(90)
+ self.buttonLabel_again = QLineEdit()
+ buttonlabel_again_holder = QHBoxLayout()
+ buttonlabel_again_holder.addWidget(buttonLabel_again_label)
+ buttonlabel_again_holder.addWidget(self.buttonLabel_again)
+ buttonLabel_again_box = QGroupBox()
+ buttonLabel_again_box.setLayout(buttonlabel_again_holder)
+ buttonLabel_hard_label = QLabel("Hard:")
+ buttonLabel_hard_label.setToolTip("{0}Replaces the text for \"Hard\" Button with your custom text.{1}".format(begin, end))
+ buttonLabel_hard_label.setFixedWidth(90)
+ self.buttonLabel_hard = QLineEdit()
+ buttonlabel_hard_holder = QHBoxLayout()
+ buttonlabel_hard_holder.addWidget(buttonLabel_hard_label)
+ buttonlabel_hard_holder.addWidget(self.buttonLabel_hard)
+ buttonLabel_hard_box = QGroupBox()
+ buttonLabel_hard_box.setLayout(buttonlabel_hard_holder)
+ fifthLine = QHBoxLayout()
+ fifthLine.addWidget(buttonLabel_again_box)
+ fifthLine.addWidget(buttonLabel_hard_box)
+ buttonLabel_good_label = QLabel("Good:")
+ buttonLabel_good_label.setToolTip("{0}Replaces the text for \"Good\" Button with your custom text.{1}".format(begin, end))
+ buttonLabel_good_label.setFixedWidth(90)
+ self.buttonLabel_good = QLineEdit()
+ buttonlabel_good_holder = QHBoxLayout()
+ buttonlabel_good_holder.addWidget(buttonLabel_good_label)
+ buttonlabel_good_holder.addWidget(self.buttonLabel_good)
+ buttonLabel_good_box = QGroupBox()
+ buttonLabel_good_box.setLayout(buttonlabel_good_holder)
+ buttonLabel_easy_label = QLabel("Easy:")
+ buttonLabel_easy_label.setToolTip("{0}Replaces the text for \"Easy\" Button with your custom text.{1}".format(begin, end))
+ buttonLabel_easy_label.setFixedWidth(90)
+ self.buttonLabel_easy = QLineEdit()
+ buttonlabel_easy_holder = QHBoxLayout()
+ buttonlabel_easy_holder.addWidget(buttonLabel_easy_label)
+ buttonlabel_easy_holder.addWidget(self.buttonLabel_easy)
+ buttonLabel_easy_box = QGroupBox()
+ buttonLabel_easy_box.setLayout(buttonlabel_easy_holder)
+ sixthLine = QHBoxLayout()
+ sixthLine.addWidget(buttonLabel_good_box)
+ sixthLine.addWidget(buttonLabel_easy_box)
+ layout = QVBoxLayout()
+ layout.addLayout(firstLine)
+ layout.addLayout(secondLine)
+ layout.addLayout(thirdLine)
+ layout.addLayout(fourthLine)
+ layout.addLayout(fifthLine)
+ layout.addLayout(sixthLine)
+ layout.addStretch()
+ layout_holder = QWidget()
+ layout_holder.setLayout(layout)
+ self.tab5 = QScrollArea()
+ self.tab5.setFixedWidth(640)
+ self.tab5.setAlignment(Qt.AlignHCenter)
+ self.tab5.setWidgetResizable(True)
+ self.tab5.setWidget(layout_holder)
+
+ def createSixthTab(self):
+ begin = self.begin
+ end = self.end
+ images = self.images
+ sidebarPosition_label = QLabel("Card Info Sidebar Default Position:")
+ sidebarPosition_label.setToolTip("{0}Chanes the default position of the sidebar (duh!){1}".format(begin, end))
+ sidebarPosition_label.setFixedWidth(195)
+ self.sidebar_position = QComboBox()
+ self.sidebar_position.addItems(["Right", "Left"])
+ self.sidebar_position.setMinimumWidth(200)
+ sidebarPosition_holder = QHBoxLayout()
+ sidebarPosition_holder.addWidget(sidebarPosition_label)
+ sidebarPosition_holder.addWidget(self.sidebar_position)
+ sidebarPosition_holder.addStretch()
+ sidebarTheme_label = QLabel("Card Info Sidebar Theme:")
+ sidebarTheme_label.setToolTip("{0} Changes sidebar theme. {1}".format(begin, end))
+ sidebarTheme_label.setFixedWidth(195)
+ self.sidebar_theme = QComboBox()
+ self.sidebar_theme.addItems(["Auto", "Day/Light", "Night/Dark"])
+ self.sidebar_theme.setToolTip("{0} Auto: Chooses the sidebar theme based on your anki theme.<br>\
+ <font color=red>NOTE: </font>This option only supports anki's native night\
+ mode and does not work with night mode add-on.<hr> Day: Forces sidebar to use light theme whether your anki is\
+ in night mode or not.<hr> Night: Forces sidebar to use dark theme whether your anki is\
+ in night mode or not. {1}".format(begin, end))
+ self.sidebar_theme.setMinimumWidth(200)
+ sideBarTheme_holder = QHBoxLayout()
+ sideBarTheme_holder.addWidget(sidebarTheme_label)
+ sideBarTheme_holder.addWidget(self.sidebar_theme)
+ sideBarTheme_holder.addStretch()
+ sidebarFont_label = QLabel("Card Info Sidebar Font:")
+ sidebarFont_label.setToolTip("{0} Changes card info sidebar font. {1}".format(begin, end))
+ sidebarFont_label.setFixedWidth(195)
+ self.sidebar_font = QFontComboBox()
+ self.sidebar_font.setMinimumWidth(200)
+ sidebarFont_holder = QHBoxLayout()
+ sidebarFont_holder.addWidget(sidebarFont_label)
+ sidebarFont_holder.addWidget(self.sidebar_font)
+ sidebarFont_holder.addStretch()
+ sidebarPreviousCards_label = QLabel("Number of Previous Cards To Show:")
+ sidebarPreviousCards_label.setToolTip("{0} Changes number of previous cards that show on the card\
+ info sidebar.</font> {1}".format(begin, end))
+ sidebarPreviousCards_label.setFixedWidth(195)
+ self.sidebar_PreviousCards = QSpinBox()
+ self.sidebar_PreviousCards.setMinimumWidth(200)
+ self.sidebar_PreviousCards.setMaximum(4)
+ sidebarPreviousCards_holder = QHBoxLayout()
+ sidebarPreviousCards_holder.addWidget(sidebarPreviousCards_label)
+ sidebarPreviousCards_holder.addWidget(self.sidebar_PreviousCards)
+ sidebarPreviousCards_holder.addStretch()
+ sidebarReviewsToShow_label = QLabel("Card Previous Reviews To Show:")
+ sidebarReviewsToShow_label.setToolTip("{0} Changes number of previous reviews for a card to show on\
+ the card info sidebar.<hr><font color=red> If you want it to show all reviews for a card, set it on 0.{1}".format(begin, end))
+ sidebarReviewsToShow_label.setFixedWidth(195)
+ self.sidebar_reviewsToShow = QSpinBox()
+ self.sidebar_reviewsToShow.setMinimumWidth(200)
+ self.sidebar_reviewsToShow.setMaximum(500)
+ sidebarReviewsToShow_holder = QHBoxLayout()
+ sidebarReviewsToShow_holder.addWidget(sidebarReviewsToShow_label)
+ sidebarReviewsToShow_holder.addWidget(self.sidebar_reviewsToShow)
+ sidebarReviewsToShow_holder.addStretch()
+ tab5line1 = QVBoxLayout()
+ tab5line1.addLayout(sidebarPosition_holder)
+ tab5line1.addLayout(sideBarTheme_holder)
+ tab5line1.addLayout(sidebarFont_holder)
+ tab5line1.addLayout(sidebarPreviousCards_holder)
+ tab5line1.addLayout(sidebarReviewsToShow_holder)
+ tab5box1 = QGroupBox()
+ tab5box1.setLayout(tab5line1)
+ self.sidebar_currentReviewCount = QCheckBox("Current Review Count")
+ self.sidebar_currentReviewCount.setFixedWidth(140)
+ self.sidebar_dateCreated = QCheckBox('Date Created')
+ self.sidebar_dateCreated.setFixedWidth(140)
+ self.sidebar_dateEdited = QCheckBox('Dated Edited')
+ self.sidebar_dateEdited.setFixedWidth(140)
+ tab5subline1 = QHBoxLayout()
+ tab5subline1.addWidget(self.sidebar_currentReviewCount)
+ tab5subline1.addWidget(self.sidebar_dateCreated)
+ tab5subline1.addWidget(self.sidebar_dateEdited)
+ tab5subline1.addStretch()
+ self.sidebar_firstReview = QCheckBox('First Review')
+ self.sidebar_firstReview.setFixedWidth(140)
+ self.sidebar_latestReview = QCheckBox('Latest Review')
+ self.sidebar_latestReview.setFixedWidth(140)
+ self.sidebar_due = QCheckBox('Due')
+ self.sidebar_due.setFixedWidth(140)
+ tab5subline2 = QHBoxLayout()
+ tab5subline2.addWidget(self.sidebar_firstReview)
+ tab5subline2.addWidget(self.sidebar_latestReview)
+ tab5subline2.addWidget(self.sidebar_due)
+ tab5subline2.addStretch()
+ self.sidebar_interval = QCheckBox('Interval')
+ self.sidebar_interval.setFixedWidth(140)
+ self.sidebar_ease = QCheckBox('Ease')
+ self.sidebar_ease.setFixedWidth(140)
+ self.sidebar_numberOfReviews = QCheckBox('Number of Reviews')
+ self.sidebar_numberOfReviews.setFixedWidth(140)
+ tab5subline3 = QHBoxLayout()
+ tab5subline3.addWidget(self.sidebar_interval)
+ tab5subline3.addWidget(self.sidebar_ease)
+ tab5subline3.addWidget(self.sidebar_numberOfReviews)
+ tab5subline3.addStretch()
+ self.sidebar_lapses = QCheckBox('Lapses')
+ self.sidebar_lapses.setFixedWidth(140)
+ self.sidebar_averageTime = QCheckBox('Average Time')
+ self.sidebar_averageTime.setFixedWidth(140)
+ self.sidebar_totalTime = QCheckBox('Total Time')
+ self.sidebar_totalTime.setFixedWidth(140)
+ tab5subline4 = QHBoxLayout()
+ tab5subline4.addWidget(self.sidebar_lapses)
+ tab5subline4.addWidget(self.sidebar_averageTime)
+ tab5subline4.addWidget(self.sidebar_totalTime)
+ tab5subline4.addStretch()
+ self.sidebar_cardType = QCheckBox('Card Type')
+ self.sidebar_cardType.setFixedWidth(140)
+ self.sidebar_noteType = QCheckBox('Note Type')
+ self.sidebar_noteType.setFixedWidth(140)
+ self.sidebar_deck = QCheckBox('Deck')
+ self.sidebar_deck.setFixedWidth(140)
+ tab5subline5 = QHBoxLayout()
+ tab5subline5.addWidget(self.sidebar_cardType)
+ tab5subline5.addWidget(self.sidebar_noteType)
+ tab5subline5.addWidget(self.sidebar_deck)
+ tab5subline5.addStretch()
+ self.sidebar_tags = QCheckBox('Tags')
+ self.sidebar_tags.setFixedWidth(140)
+ self.sidebar_sortField = QCheckBox('Sort Field')
+ self.sidebar_sortField.setFixedWidth(140)
+ self.sidebar_warningNote = QCheckBox('Warning Note')
+ self.sidebar_warningNote.setFixedWidth(140)
+ tab5subline6 = QHBoxLayout()
+ tab5subline6.addWidget(self.sidebar_tags)
+ tab5subline6.addWidget(self.sidebar_sortField)
+ tab5subline6.addWidget(self.sidebar_warningNote)
+ tab5subline6.addStretch()
+ self.sidebar_correctPercent = QCheckBox('Correct Percentage')
+ self.sidebar_correctPercent.setFixedWidth(140)
+ self.sidebar_fastestReview = QCheckBox('Fastest Review')
+ self.sidebar_fastestReview.setFixedWidth(140)
+ self.sidebar_slowestReview = QCheckBox('Slowest Review')
+ self.sidebar_slowestReview.setFixedWidth(140)
+ tab5subline7 = QHBoxLayout()
+ tab5subline7.addWidget(self.sidebar_correctPercent)
+ tab5subline7.addWidget(self.sidebar_fastestReview)
+ tab5subline7.addWidget(self.sidebar_slowestReview)
+ tab5subline7.addStretch()
+ self.sidebar_noteID = QCheckBox('Note ID')
+ self.sidebar_noteID.setFixedWidth(140)
+ self.sidebar_cardID = QCheckBox('Card ID')
+ self.sidebar_cardID.setFixedWidth(140)
+ self.sidebar_autoOpen = QCheckBox('Auto Open')
+ self.sidebar_autoOpen.setToolTip("Opens sidebar automatically when you review a card")
+ self.sidebar_autoOpen.setFixedWidth(140)
+ tab5subline8 = QHBoxLayout()
+ tab5subline8.addWidget(self.sidebar_noteID)
+ tab5subline8.addWidget(self.sidebar_cardID)
+ tab5subline8.addWidget(self.sidebar_autoOpen)
+ tab5subline8.addStretch()
+ tab5line2 = QVBoxLayout()
+ tab5line2.addLayout(tab5subline1)
+ tab5line2.addLayout(tab5subline2)
+ tab5line2.addLayout(tab5subline3)
+ tab5line2.addLayout(tab5subline4)
+ tab5line2.addLayout(tab5subline5)
+ tab5line2.addLayout(tab5subline6)
+ tab5line2.addLayout(tab5subline7)
+ tab5line2.addLayout(tab5subline8)
+ tab5box2 = QGroupBox()
+ tab5box2.setLayout(tab5line2)
+ layout = QVBoxLayout()
+ layout.addWidget(tab5box1)
+ layout.addWidget(tab5box2)
+ layout.addStretch()
+ layout_holder = QWidget()
+ layout_holder.setLayout(layout)
+ self.tab6 = QScrollArea()
+ self.tab6.setFixedWidth(640)
+ self.tab6.setAlignment(Qt.AlignHCenter)
+ self.tab6.setWidgetResizable(True)
+ self.tab6.setWidget(layout_holder)
+
+ def createSeventhTab(self):
+ begin = self.begin
+ end = self.end
+ images = self.images
+ self.custom_reviewButtonColors = QGroupBox("Custom Review Button Colors")
+ self.custom_reviewButtonColors.setToolTip("{0}Changes text or background color and hover colors for review buttons.<hr>\
+ as the neon and fill buttons don't have a separate hover color, changing hover color will not affect those buttons.{1}".format(begin, end))
+ self.custom_reviewButtonColors.setCheckable(True)
+ self.custom_reviewButtonColors.setChecked(False)
+ againColor_label = QLabel("Again:")
+ againColor_label.setFixedWidth(140)
+ self.againColor_button = QPushButton()
+ self.againColor_button.clicked.connect(lambda: self.getNewColor("again_color", self.againColor_button))
+ againColor_holder = QHBoxLayout()
+ againColor_holder.addWidget(againColor_label)
+ againColor_holder.addWidget(self.againColor_button)
+ againColor_box = QGroupBox()
+ againColor_box.setLayout(againColor_holder)
+ againHoverColor_label = QLabel("Again on Hover:")
+ againHoverColor_label.setFixedWidth(140)
+ self.againHoverColor_button = QPushButton()
+ self.againHoverColor_button.clicked.connect(lambda: self.getNewColor("againHover_color", self.againHoverColor_button))
+ againHoverColor_holder = QHBoxLayout()
+ againHoverColor_holder.addWidget(againHoverColor_label)
+ againHoverColor_holder.addWidget(self.againHoverColor_button)
+ againHover_box = QGroupBox()
+ againHover_box.setLayout(againHoverColor_holder)
+ again_line = QHBoxLayout()
+ again_line.addWidget(againColor_box)
+ again_line.addWidget(againHover_box)
+ hardColor_label = QLabel("Hard:")
+ hardColor_label.setFixedWidth(140)
+ self.hardColor_button = QPushButton()
+ self.hardColor_button.clicked.connect(lambda: self.getNewColor("hard_color", self.hardColor_button))
+ hardColor_holder = QHBoxLayout()
+ hardColor_holder.addWidget(hardColor_label)
+ hardColor_holder.addWidget(self.hardColor_button)
+ hardColor_box = QGroupBox()
+ hardColor_box.setLayout(hardColor_holder)
+ hardHoverColor_label = QLabel("Hard on Hover:")
+ hardHoverColor_label.setFixedWidth(140)
+ self.hardHoverColor_button = QPushButton()
+ self.hardHoverColor_button.clicked.connect(lambda: self.getNewColor("hardHover_color", self.hardHoverColor_button))
+ hardHoverColor_holder = QHBoxLayout()
+ hardHoverColor_holder.addWidget(hardHoverColor_label)
+ hardHoverColor_holder.addWidget(self.hardHoverColor_button)
+ hardHover_box = QGroupBox()
+ hardHover_box.setLayout(hardHoverColor_holder)
+ hard_line = QHBoxLayout()
+ hard_line.addWidget(hardColor_box)
+ hard_line.addWidget(hardHover_box)
+ goodColor_label = QLabel("Good:")
+ goodColor_label.setFixedWidth(140)
+ self.goodColor_button = QPushButton()
+ self.goodColor_button.clicked.connect(lambda: self.getNewColor("good_color", self.goodColor_button))
+ goodColor_holder = QHBoxLayout()
+ goodColor_holder.addWidget(goodColor_label)
+ goodColor_holder.addWidget(self.goodColor_button)
+ goodColor_box = QGroupBox()
+ goodColor_box.setLayout(goodColor_holder)
+ goodHoverColor_label = QLabel("Good on Hover:")
+ goodHoverColor_label.setFixedWidth(140)
+ self.goodHoverColor_button = QPushButton()
+ self.goodHoverColor_button.clicked.connect(lambda: self.getNewColor("goodHover_color", self.goodHoverColor_button))
+ goodHoverColor_holder = QHBoxLayout()
+ goodHoverColor_holder.addWidget(goodHoverColor_label)
+ goodHoverColor_holder.addWidget(self.goodHoverColor_button)
+ goodHover_box = QGroupBox()
+ goodHover_box.setLayout(goodHoverColor_holder)
+ good_line = QHBoxLayout()
+ good_line.addWidget(goodColor_box)
+ good_line.addWidget(goodHover_box)
+ easyColor_label = QLabel("Easy:")
+ easyColor_label.setFixedWidth(140)
+ self.easyColor_button = QPushButton()
+ self.easyColor_button.clicked.connect(lambda: self.getNewColor("easy_color", self.easyColor_button))
+ easyColor_holder = QHBoxLayout()
+ easyColor_holder.addWidget(easyColor_label)
+ easyColor_holder.addWidget(self.easyColor_button)
+ easyColor_box = QGroupBox()
+ easyColor_box.setLayout(easyColor_holder)
+ easyHoverColor_label = QLabel("Easy on Hover:")
+ easyHoverColor_label.setFixedWidth(140)
+ self.easyHoverColor_button = QPushButton()
+ self.easyHoverColor_button.clicked.connect(lambda: self.getNewColor("easyHover_color", self.easyHoverColor_button))
+ easyHoverColor_holder = QHBoxLayout()
+ easyHoverColor_holder.addWidget(easyHoverColor_label)
+ easyHoverColor_holder.addWidget(self.easyHoverColor_button)
+ easyHover_box = QGroupBox()
+ easyHover_box.setLayout(easyHoverColor_holder)
+ easy_line = QHBoxLayout()
+ easy_line.addWidget(easyColor_box)
+ easy_line.addWidget(easyHover_box)
+ reviewButtonColors_layout = QVBoxLayout()
+ reviewButtonColors_layout.addLayout(again_line)
+ reviewButtonColors_layout.addLayout(hard_line)
+ reviewButtonColors_layout.addLayout(good_line)
+ reviewButtonColors_layout.addLayout(easy_line)
+ self.custom_reviewButtonColors.setLayout(reviewButtonColors_layout)
+ self.custom_reviewButtonTextColor = QCheckBox("Review Button Text:")
+ self.custom_reviewButtonTextColor.setToolTip("{0}Changes the color of general textcolor inside buttons.\
+ <hr> This option does not work on Default + Text Color and Wide + Text Color Styles {1}".format(begin, end))
+ self.custom_reviewButtonTextColor.setFixedWidth(140)
+ self.reviewButtonTextColor_button = QPushButton()
+ self.reviewButtonTextColor_button.clicked.connect(lambda: self.getNewColor("reviewButtonText_color", self.reviewButtonTextColor_button))
+ reviewButtonTextColor_holder = QHBoxLayout()
+ reviewButtonTextColor_holder.addWidget(self.custom_reviewButtonTextColor)
+ reviewButtonTextColor_holder.addWidget(self.reviewButtonTextColor_button)
+ reviewButtonTextColor_box = QGroupBox()
+ reviewButtonTextColor_box.setLayout(reviewButtonTextColor_holder)
+ self.reviewButtonTextColor_button.setDisabled(True)
+ if self.custom_reviewButtonTextColor.isChecked():
+ self.reviewButtonTextColor_button.setEnabled(True)
+ self.custom_reviewButtonTextColor.toggled.connect(self.reviewButtonTextColor_button.setEnabled)
+ self.custom_activeIndicatorColor = QCheckBox("Active Indicator:")
+ self.custom_activeIndicatorColor.setToolTip("{0}Changes the active indicator color.<hr>\
+ This option doesn not work on neon and fill buttons as they don't have active indicator for active buttons.{1}".format(begin, end))
+ self.custom_activeIndicatorColor.setFixedWidth(140)
+ self.activeIndicatorColor_button = QPushButton()
+ self.activeIndicatorColor_button.clicked.connect(lambda: self.getNewColor("activeIndicator_color", self.activeIndicatorColor_button))
+ activeIndicatorColor_holder = QHBoxLayout()
+ activeIndicatorColor_holder.addWidget(self.custom_activeIndicatorColor)
+ activeIndicatorColor_holder.addWidget(self.activeIndicatorColor_button)
+ activeIndicatorColor_box = QGroupBox()
+ activeIndicatorColor_box.setLayout(activeIndicatorColor_holder)
+ self.activeIndicatorColor_button.setDisabled(True)
+ if self.custom_activeIndicatorColor.isChecked():
+ self.activeIndicatorColor_button.setEnabled(True)
+ self.custom_activeIndicatorColor.toggled.connect(self.activeIndicatorColor_button.setEnabled)
+ reviewButtonTextColor_activeIndicatorColor_line = QHBoxLayout()
+ reviewButtonTextColor_activeIndicatorColor_line.addWidget(reviewButtonTextColor_box)
+ reviewButtonTextColor_activeIndicatorColor_line.addWidget(activeIndicatorColor_box)
+ self.custom_bottombarButtonTextColor = QCheckBox("General Button Text:")
+ self.custom_bottombarButtonTextColor.setToolTip("{0}Changes color of text inside all buttons including bottombar buttons, deck overview buttons and main screen bottombar buttons.{1}".format(begin, end))
+ self.custom_bottombarButtonTextColor.setFixedWidth(140)
+ self.bottombarButtonTextColor_button = QPushButton()
+ self.bottombarButtonTextColor_button.clicked.connect(lambda: self.getNewColor("bottombarButtonText_color", self.bottombarButtonTextColor_button))
+ bottombarButtonTextColor_holder = QHBoxLayout()
+ bottombarButtonTextColor_holder.addWidget(self.custom_bottombarButtonTextColor)
+ bottombarButtonTextColor_holder.addWidget(self.bottombarButtonTextColor_button)
+ bottombarButtonTextColor_box = QGroupBox()
+ bottombarButtonTextColor_box.setLayout(bottombarButtonTextColor_holder)
+ self.bottombarButtonTextColor_button.setDisabled(True)
+ if self.custom_bottombarButtonTextColor.isChecked():
+ self.bottombarButtonTextColor_button.setEnabled(True)
+ self.custom_bottombarButtonTextColor.toggled.connect(self.bottombarButtonTextColor_button.setEnabled)
+ self.custom_bottombarButtonBorderColor = QCheckBox("General Button Border:")
+ self.custom_bottombarButtonBorderColor.setToolTip("{0}Changes border color for all buttons including bottombar buttons, deck overview buttons and main screen bottombar buttons.{1}".format(begin, end))
+ self.custom_bottombarButtonBorderColor.setFixedWidth(140)
+ self.bottombarButtonBorderColor_button = QPushButton()
+ self.bottombarButtonBorderColor_button.clicked.connect(lambda: self.getNewColor("bottombarButtonBorder_color", self.bottombarButtonBorderColor_button))
+ bottombarButtonBorderColor_holder = QHBoxLayout()
+ bottombarButtonBorderColor_holder.addWidget(self.custom_bottombarButtonBorderColor)
+ bottombarButtonBorderColor_holder.addWidget(self.bottombarButtonBorderColor_button)
+ bottombarButtonBorderColor_box = QGroupBox()
+ bottombarButtonBorderColor_box.setLayout(bottombarButtonBorderColor_holder)
+ self.bottombarButtonBorderColor_button.setDisabled(True)
+ if self.custom_bottombarButtonBorderColor.isChecked():
+ self.bottombarButtonBorderColor_button.setEnabled(True)
+ self.custom_bottombarButtonBorderColor.toggled.connect(self.bottombarButtonBorderColor_button.setEnabled)
+ bottobarButtonTextColor_bottombarButtonBorderColor_line = QHBoxLayout()
+ bottobarButtonTextColor_bottombarButtonBorderColor_line.addWidget(bottombarButtonTextColor_box)
+ bottobarButtonTextColor_bottombarButtonBorderColor_line.addWidget(bottombarButtonBorderColor_box)
+ showAnswerEase1_label = QLabel("Ease less than")
+ showAnswerEase1_label.setFixedWidth(120)
+ self.showAnswerEase1 = QSpinBox()
+ self.showAnswerEase1.setFixedWidth(90)
+ self.showAnswerEase1.setMinimum(130)
+ self.showAnswerEase1.setMaximum(999999)
+ self.showAnswerEase1_button = QPushButton()
+ self.showAnswerEase1_button.setFixedWidth(210)
+ self.showAnswerEase1_button.clicked.connect(lambda: self.getNewColor("showAnswerEase1", self.showAnswerEase1_button))
+ showAnswerEase1_holder = QHBoxLayout()
+ showAnswerEase1_holder.addWidget(showAnswerEase1_label)
+ showAnswerEase1_holder.addWidget(self.showAnswerEase1)
+ showAnswerEase1_holder.addStretch()
+ showAnswerEase1_holder.addWidget(self.showAnswerEase1_button)
+ showAnswerEase1_box = QGroupBox()
+ showAnswerEase1_box.setLayout(showAnswerEase1_holder)
+ showAnswerEase2_label = QLabel("Ease from {} to".format(C_showAnswerEase1))
+ showAnswerEase2_label.setFixedWidth(120)
+ self.showAnswerEase2 = QSpinBox()
+ self.showAnswerEase2.setMinimum(130)
+ self.showAnswerEase2.setMaximum(999999)
+ self.showAnswerEase2.setFixedWidth(90)
+ self.showAnswerEase2_button = QPushButton()
+ self.showAnswerEase2_button.setFixedWidth(210)
+ self.showAnswerEase2_button.clicked.connect(lambda: self.getNewColor("showAnswerEase2", self.showAnswerEase2_button))
+ showAnswerEase2_holder = QHBoxLayout()
+ showAnswerEase2_holder.addWidget(showAnswerEase2_label)
+ showAnswerEase2_holder.addWidget(self.showAnswerEase2)
+ showAnswerEase2_holder.addStretch()
+ showAnswerEase2_holder.addWidget(self.showAnswerEase2_button)
+ showAnswerEase2_box = QGroupBox()
+ showAnswerEase2_box.setLayout(showAnswerEase2_holder)
+ showAnswerEase3_label = QLabel("Ease from {} to".format(C_showAnswerEase2))
+ showAnswerEase3_label.setFixedWidth(120)
+ self.showAnswerEase3 = QSpinBox()
+ self.showAnswerEase3.setMinimum(130)
+ self.showAnswerEase3.setMaximum(999999)
+ self.showAnswerEase3.setFixedWidth(90)
+ self.showAnswerEase3_button = QPushButton()
+ self.showAnswerEase3_button.setFixedWidth(210)
+ self.showAnswerEase3_button.clicked.connect(lambda: self.getNewColor("showAnswerEase3", self.showAnswerEase3_button))
+ showAnswerEase3_holder = QHBoxLayout()
+ showAnswerEase3_holder.addWidget(showAnswerEase3_label)
+ showAnswerEase3_holder.addWidget(self.showAnswerEase3)
+ showAnswerEase3_holder.addStretch()
+ showAnswerEase3_holder.addWidget(self.showAnswerEase3_button)
+ showAnswerEase3_box = QGroupBox()
+ showAnswerEase3_box.setLayout(showAnswerEase3_holder)
+ showAnswerEase4_label = QLabel("Ease from {} to".format(C_showAnswerEase3))
+ showAnswerEase4_label.setFixedWidth(120)
+ self.showAnswerEase4 = QSpinBox()
+ self.showAnswerEase4.setMinimum(130)
+ self.showAnswerEase4.setMaximum(999999)
+ self.showAnswerEase4.setFixedWidth(90)
+ self.showAnswerEase4_button = QPushButton()
+ self.showAnswerEase4_button.setFixedWidth(210)
+ self.showAnswerEase4_button.clicked.connect(lambda: self.getNewColor("showAnswerEase4", self.showAnswerEase4_button))
+ showAnswerEase4_holder = QHBoxLayout()
+ showAnswerEase4_holder.addWidget(showAnswerEase4_label)
+ showAnswerEase4_holder.addWidget(self.showAnswerEase4)
+ showAnswerEase4_holder.addStretch()
+ showAnswerEase4_holder.addWidget(self.showAnswerEase4_button)
+ showAnswerEase4_box = QGroupBox()
+ showAnswerEase4_box.setLayout(showAnswerEase4_holder)
+ showAnswerColors_layout = QVBoxLayout()
+ showAnswerColors_layout.addWidget(showAnswerEase1_box)
+ showAnswerColors_layout.addWidget(showAnswerEase2_box)
+ showAnswerColors_layout.addWidget(showAnswerEase3_box)
+ showAnswerColors_layout.addWidget(showAnswerEase4_box)
+ self.showAnswerColors_box = QGroupBox()
+ self.showAnswerColors_box.setLayout(showAnswerColors_layout)
+ def showAnswerType_signal():
+ self.showAnswerColors_box.setEnabled(True)
+ if self.showAnswerBorderColor_style.currentIndex() == 0:
+ self.showAnswerColors_box.setDisabled(True)
+ showAnswerType_signal()
+ self.showAnswerBorderColor_style.currentIndexChanged.connect(showAnswerType_signal)
+ layout = QVBoxLayout()
+ layout.addWidget(self.custom_reviewButtonColors)
+ layout.addLayout(reviewButtonTextColor_activeIndicatorColor_line)
+ layout.addLayout(bottobarButtonTextColor_bottombarButtonBorderColor_line)
+ layout.addWidget(self.showAnswerColors_box)
+ layout.addStretch()
+ layout_holder = QWidget()
+ layout_holder.setLayout(layout)
+ self.tab7 = QScrollArea()
+ #// I use this part to control the initial settings menu width -_-
+ self.tab7.setFixedWidth(645)
+ self.tab7.setAlignment(Qt.AlignHCenter)
+ self.tab7.setWidgetResizable(True)
+ self.tab7.setWidget(layout_holder)
+
+ def createEighthTab(self):
+ begin = self.begin
+ end = self.end
+ images = self.images
+ overViewStats_label = QLabel("More Overview Stats:")
+ overViewStats_label.setToolTip("{0}Shows number of new, learn and review cards for today and\
+ tomorrow and show total number of new, review, learn, buried and suspended cards on deck overview\
+ if enabled.{1}".format(begin, end))
+ overViewStats_label.setFixedWidth(180)
+ self.overViewStats = QComboBox()
+ self.overViewStats.addItems(["Stock", "Stock-ish", "Detailed"])
+ self.overViewStats.setFixedWidth(150)
+ overViewStats_holder = QHBoxLayout()
+ overViewStats_holder.addWidget(overViewStats_label)
+ overViewStats_holder.addWidget(self.overViewStats)
+ overViewStats_holder.addStretch()
+ settingsMenuPlace_label = QLabel("Settings Menu Placement:")
+ settingsMenuPlace_label.setToolTip("{0}Changes the position of settings menu.{1}".format(begin, end))
+ settingsMenuPlace_label.setFixedWidth(180)
+ self.settingsMenu_place = QComboBox()
+ self.settingsMenu_place.addItems(["Main Toolbar", "Tools Menu"])
+ self.settingsMenu_place.setFixedWidth(150)
+ settingsMenuPlace_holder = QHBoxLayout()
+ settingsMenuPlace_holder.addWidget(settingsMenuPlace_label)
+ settingsMenuPlace_holder.addWidget(self.settingsMenu_place)
+ settingsMenuPlace_holder.addStretch()
+ skipMethod_label = QLabel("Skip Method:")
+ skipMethod_label.setToolTip("{0}Changes Skip method.\n\"Next Card\" just skips the card and the skipped cards will be shown again randomly\
+ while \"Bury\" bureis the skipped cards and the skipped cards will get unburied when you finish reviewing normal cards. You\
+ can manually unbury skipped cards by pressing the \"Show Skipped\" button or by pressing the shortcut key that you've chosen for the button.{1}".format(begin, end))
+ skipMethod_label.setFixedWidth(180)
+ self.skipMethod = QComboBox()
+ self.skipMethod.addItems(["Next Card", "Bury"])
+ self.skipMethod.setFixedWidth(150)
+ skipMethod_holder = QHBoxLayout()
+ skipMethod_holder.addWidget(skipMethod_label)
+ skipMethod_holder.addWidget(self.skipMethod)
+ skipMethod_holder.addStretch()
+ skipMethod_box = QVBoxLayout()
+ skipMethod_box.addLayout(overViewStats_holder)
+ skipMethod_box.addLayout(settingsMenuPlace_holder)
+ skipMethod_box.addLayout(skipMethod_holder)
+ general_box = QGroupBox()
+ general_box.setLayout(skipMethod_box)
+ buttonColors_label = QLabel("Button Colors:")
+ buttonColors_label.setToolTip("{0} Enables and disables change button color.<hr> If you use\
+ any other add-on to change review button colors, turn this off and you can\
+ use other functions of this add-on without using this add-on to change\
+ review button colors.<hr> This should be enabled for options Review Button\
+ Background Shadow, Button Style, Change Style, Review Active Button Indicator\
+ and Review Buttons Width to work. {1}".format(begin, end, images))
+ buttonColors_label.setFixedWidth(180)
+ self.buttonColors_on = QRadioButton("On")
+ self.buttonColors_on.setToolTip("{0} Enabled -> Buttons are styled by this\
+ add-on. <br><img src='{2}/buttonColors_on.png'>{1}".format(begin, end, images))
+ self.buttonColors_on.setFixedWidth(90)
+ self.buttonColors_off = QRadioButton("Off")
+ self.buttonColors_off.setToolTip("{0} Disabled -> Buttons are\
+ not styled by this add-on and since there is no other add-on styling them,\
+ they are in default mode.<hr><img src='{2}/buttonColors_off.png'>{1}".format(begin, end, images))
+ self.buttonColors_off.setFixedWidth(90)
+ buttonColors_holder = QHBoxLayout()
+ buttonColors_holder.addWidget(buttonColors_label)
+ buttonColors_holder.addWidget(self.buttonColors_on)
+ buttonColors_holder.addWidget(self.buttonColors_off)
+ buttonColors_holder.addStretch()
+ buttonColors_box = QGroupBox()
+ buttonColors_box.setLayout(buttonColors_holder)
+ speedFocus_label = QLabel("Speed Focus Add-on:")
+ speedFocus_label.setToolTip("{0} Removes the conflict with speed focus add-on so you can\
+ use this add-on on speed focus add-on at the same time without having issues.<hr>\
+ Don't forget to disable this option when you don't want to use speed focus add-on,\
+ otherwise this add-on will automatically reveal the answer after the time\
+ you set on speed focus add-on is passed.<hr> DON'T ENABLE THIS IF YOU DON'T\
+ HAVE SPEED FOCUS ADD-ON. {1}".format(begin, end))
+ speedFocus_label.setFixedWidth(180)
+ self.speedFocus_on = QRadioButton("On")
+ self.speedFocus_on.setFixedWidth(90)
+ self.speedFocus_off = QRadioButton("Off")
+ self.speedFocus_off.setFixedWidth(90)
+ speedFocus_holder = QHBoxLayout()
+ speedFocus_holder.addWidget(speedFocus_label)
+ speedFocus_holder.addWidget(self.speedFocus_on)
+ speedFocus_holder.addWidget(self.speedFocus_off)
+ speedFocus_holder.addStretch()
+ speedFocus_box = QGroupBox()
+ speedFocus_box.setLayout(speedFocus_holder)
+ configEdit_label = QLabel("Direct Config Edit:")
+ configEdit_label.setToolTip("{0} Enables direct config editor.\
+ If you enable this option, clicking on \"Config\" in add-ons window\
+ won't open ARBb settings window and will open the config editor instead.{1}".format(begin, end))
+ configEdit_label.setFixedWidth(180)
+ self.configEdit_on = QRadioButton("On")
+ self.configEdit_on.setFixedWidth(90)
+ self.configEdit_off = QRadioButton("Off")
+ self.configEdit_off.setFixedWidth(90)
+ configEdit_holder = QHBoxLayout()
+ configEdit_holder.addWidget(configEdit_label)
+ configEdit_holder.addWidget(self.configEdit_on)
+ configEdit_holder.addWidget(self.configEdit_off)
+ configEdit_holder.addStretch()
+ configEdit_box = QGroupBox()
+ configEdit_box.setLayout(configEdit_holder)
+ layout = QVBoxLayout()
+ layout.addWidget(general_box)
+ layout.addWidget(buttonColors_box)
+ layout.addWidget(speedFocus_box)
+ layout.addWidget(configEdit_box)
+ layout.addStretch()
+ layout_holder = QWidget()
+ layout_holder.setLayout(layout)
+ self.tab8 = QScrollArea()
+ self.tab8.setFixedWidth(640)
+ self.tab8.setAlignment(Qt.AlignHCenter)
+ self.tab8.setWidgetResizable(True)
+ self.tab8.setWidget(layout_holder)
+ self.tab1.setDisabled(True)
+ if self.buttonColors_on.isChecked():
+ self.tab1.setEnabled(True)
+ self.buttonColors_on.toggled.connect(self.tab1.setEnabled)
+
+ def createNinthTab(self):
+ begin = self.begin
+ end = self.end
+ images = self.images
+ about_text = """
+ <div class="None">
+ <font color="tomato">Don't know what each option does?<br></font>
+ hover over the title and you'll see a brief description about that option <br><br>
+
+ <font color="tomato">Wanna see what each design is like withot having to restart anki?<br></font>
+ by hovering over options in front of titles, if the option is related to styling buttons<br>
+ or changing how something looks like, you'll see pictures showing you what each option looks like<br>
+ Hovering over them won't show you the animations, if the buttons are animated<br><br><br>
+
+ <font color="tomato">Have an idea for a new feature?<br></font>
+ Feel free to tell me in comment section on <a href="https://ankiweb.net/shared/info/1136455830">Add-on's Page</a> or <a href="mailto:[email protected]">Email me</a><br><br>
+
+ <font color="tomato">Saw a cool button design somewhere?<br></font>
+ senf me a link to where you saw that design, i'll try to replicate that design<br>
+ and put it on the add-on as and option for you to choose<br>
+ <a href="https://ankiweb.net/shared/info/1136455830">Add-on's Page</a> or <a href="mailto:[email protected]">my Email</a><br><br>
+
+ <font color="tomato">Encountered a bug or some part is not acting how it's supposed to?<br></font>
+ Tell me what your settings were on add-on, what's your anki version or if anki showed you an error log,<br>
+ copy the error log and comment it on <a href="https://ankiweb.net/shared/info/1136455830">Add-on's Page</a> or or <a href="mailto:[email protected]">Email me</a> <br>
+ (the more information you give me,<br>
+ the sooner i find out what's causing the problem and i fix the bug)<br><br>
+
+ <font color="tomato">Like the add-on?<br></font>
+ Give it a like on <a href="https://ankiweb.net/shared/review/1136455830">Add-on's Page</a>
+ </div>
+ """
+ about = QLabel()
+ about.setText(about_text)
+ about.setOpenExternalLinks(True)
+ about_scroll = QScrollArea()
+ about_scroll.setWidget(about)
+ changeLog_window = QDialog()
+ changeLog_window.setWindowFlags(Qt.WindowCloseButtonHint | Qt.WindowMaximizeButtonHint | Qt.WindowMinimizeButtonHint)
+ changeLog_window.setWindowTitle("Changelog")
+ changeLog_window.setWindowIcon(QIcon(images + "\icon.png"))
+ changeLog_button = QPushButton("Show Changelog")
+ self.changeLog_webView = QWebEngineView()
+ self.loadChaneLog()
+ changeLog_layout = QVBoxLayout()
+ changeLog_layout.addWidget(self.changeLog_webView)
+ changeLog_window.setLayout(changeLog_layout)
+ changeLog_button.clicked.connect(lambda: changeLog_window.exec_())
+ layout = QVBoxLayout()
+ layout.addWidget(about_scroll)
+ layout.addWidget(changeLog_button)
+ layout_holder = QWidget()
+ layout_holder.setLayout(layout)
+ self.tab9 = QScrollArea()
+ self.tab9.setAlignment(Qt.AlignHCenter)
+ self.tab9.setWidgetResizable(True)
+ self.tab9.setWidget(layout_holder)
+
+ def loadChaneLog(self):
+ #// For some weird reason, using dirname(__file__) inside the .format() thingy doesn't seem to be working on macOS
+ #// Can't confirm tho -_- since I can't test my add-on on mac
+ addon_path = dirname(__file__)
+ file = "{}/changelog.html".format(addon_path)
+ with open(file, 'r') as f:
+ html = f.read()
+ self.changeLog_webView.setHtml(html)
+
+ def loadCurrent(self):
+ self.button_style.setCurrentIndex(C_button_style)
+ self.bottombarButtons_style.setCurrentIndex(C_bottombarButtons_style)
+ self.hover_effect.setCurrentIndex(C_hover_effect)
+ self.active_indicator.setCurrentIndex(C_active_indicator)
+ self.cursor_style.setCurrentIndex(C_cursor_style)
+ self.interval_style.setCurrentIndex(C_interval_style)
+ self.showAnswerBorderColor_style.setCurrentIndex(C_showAnswerBorderColor_style)
+ self.buttonTransition_time.setValue(int(C_buttonTransition_time))
+ self.buttonBorderRadius.setValue(int(C_buttonBorderRadius))
+ if C_style_mainScreenButtons:
+ self.style_mainScreenButtons.setChecked(True)
+ if C_reviewTooltip:
+ self.reviewTooltip_on.setChecked(True)
+ else:
+ self.reviewTooltip_off.setChecked(True)
+ self.reviewTooltip_style.setCurrentIndex(C_reviewTooltip_style)
+ self.reviewTooltip_timer.setValue(int(C_reviewTooltip_timer))
+ self.changeButtonColor(self.reviewTooltipTextColor_button, C_reviewTooltipText_color)
+ self.reviewTooltipPositionX.setValue(int(C_reviewTooltip_position[0]))
+ self.reviewTooltipPositionY.setValue(int(C_reviewTooltip_position[1]))
+ if C_info:
+ self.info.setChecked(True)
+ if C_skip:
+ self.skip.setChecked(True)
+ if C_showSkipped:
+ self.showSkipped.setChecked(True)
+ if C_undo:
+ self.undo.setChecked(True)
+ if C_hideHard:
+ self.hideHard.setChecked(True)
+ if C_hideGood:
+ self.hideGood.setChecked(True)
+ if C_hideEasy:
+ self.hideEasy.setChecked(True)
+ if C_right_info:
+ self.right_info.setChecked(True)
+ elif C_middleRight_info:
+ self.middleRight_info.setChecked(True)
+ elif C_middleLeft_info:
+ self.middleLeft_info.setChecked(True)
+ else:
+ self.left_info.setChecked(True)
+ if C_right_skip:
+ self.right_skip.setChecked(True)
+ elif C_middleRight_skip:
+ self.middleRight_skip.setChecked(True)
+ elif C_middleLeft_skip:
+ self.middleLeft_skip.setChecked(True)
+ else:
+ self.left_skip.setChecked(True)
+ if C_right_showSkipped:
+ self.right_showSkipped.setChecked(True)
+ elif C_middleRight_showSkipped:
+ self.middleRight_showSkipped.setChecked(True)
+ elif C_middleLeft_showSkipped:
+ self.middleLeft_showSkipped.setChecked(True)
+ else:
+ self.left_showSkipped.setChecked(True)
+ if C_right_undo:
+ self.right_undo.setChecked(True)
+ elif C_middleRight_undo:
+ self.middleRight_undo.setChecked(True)
+ elif C_middleLeft_undo:
+ self.middleLeft_undo.setChecked(True)
+ else:
+ self.left_undo.setChecked(True)
+ self.infoShortcut_button.setText("Change Shortcut (Current: {})".format(C_info_shortcut))
+ self.skipShortcut_button.setText("Change Shortcut (Current: {})".format(C_skip_shortcut))
+ self.showSkippedShortcut_button.setText("Change Shortcut (Current: {})".format(C_showSkipped_shortcut))
+ self.undoShortcut_button.setText("Change Shortcut (Current: {})".format(C_undo_shortcut))
+ if C_custom_sizes:
+ self.customSizes_on.setChecked(True)
+ else:
+ self.customSizes_off.setChecked(True)
+ self.text_size.setValue(int(C_text_size))
+ self.buttons_height.setValue(int(C_buttons_height))
+ self.reviewButtons_width.setValue(int(C_reviewButtons_width))
+ self.edit_width.setValue(int(C_edit_width))
+ self.answer_width.setValue(int(C_answer_width))
+ self.more_width.setValue(int(C_more_width))
+ self.info_width.setValue(int(C_info_width))
+ self.skip_width.setValue(int(C_skip_width))
+ self.showSkipped_width.setValue(int(C_showSkipped_width))
+ self.undo_width.setValue(int(C_undo_width))
+ self.buttonLabel_studyNow.setText(C_buttonLabel_studyNow)
+ self.buttonLabel_edit.setText(C_buttonLabel_edit)
+ self.buttonLabel_showAnswer.setText(C_buttonLabel_showAnswer)
+ self.buttonLabel_more.setText(C_buttonLabel_more)
+ self.buttonLabel_info.setText(C_buttonLabel_info)
+ self.buttonLabel_skip.setText(C_buttonLabel_skip)
+ self.buttonLabel_showSkipped.setText(C_buttonLabel_showSkipped)
+ self.buttonLabel_undo.setText(C_buttonLabel_undo)
+ self.buttonLabel_again.setText(C_buttonLabel_again)
+ self.buttonLabel_hard.setText(C_buttonLabel_hard)
+ self.buttonLabel_good.setText(C_buttonLabel_good)
+ self.buttonLabel_easy.setText(C_buttonLabel_easy)
+ self.sidebar_position.setCurrentIndex(C_sidebar_position)
+ self.sidebar_theme.setCurrentIndex(C_sidebar_theme)
+ self.sidebar_font.setCurrentFont(QFont(C_sidebar_font))
+ self.sidebar_PreviousCards.setValue(int(C_sidebar_PreviousCards))
+ self.sidebar_reviewsToShow.setValue(int(C_sidebar_reviewsToShow))
+ if C_sidebar_currentReviewCount:
+ self.sidebar_currentReviewCount.setChecked(True)
+ if C_sidebar_dateCreated:
+ self.sidebar_dateCreated.setChecked(True)
+ if C_sidebar_dateEdited:
+ self.sidebar_dateEdited.setChecked(True)
+ if C_sidebar_firstReview:
+ self.sidebar_firstReview.setChecked(True)
+ if C_sidebar_latestReview:
+ self.sidebar_latestReview.setChecked(True)
+ if C_sidebar_due:
+ self.sidebar_due.setChecked(True)
+ if C_sidebar_interval:
+ self.sidebar_interval.setChecked(True)
+ if C_sidebar_ease:
+ self.sidebar_ease.setChecked(True)
+ if C_sidebar_numberOfReviews:
+ self.sidebar_numberOfReviews.setChecked(True)
+ if C_sidebar_lapses:
+ self.sidebar_lapses.setChecked(True)
+ if C_sidebar_averageTime:
+ self.sidebar_averageTime.setChecked(True)
+ if C_sidebar_totalTime:
+ self.sidebar_totalTime.setChecked(True)
+ if C_sidebar_cardType:
+ self.sidebar_cardType.setChecked(True)
+ if C_sidebar_noteType:
+ self.sidebar_noteType.setChecked(True)
+ if C_sidebar_deck:
+ self.sidebar_deck.setChecked(True)
+ if C_sidebar_tags:
+ self.sidebar_tags.setChecked(True)
+ if C_sidebar_sortField:
+ self.sidebar_sortField.setChecked(True)
+ if C_sidebar_warningNote:
+ self.sidebar_warningNote.setChecked(True)
+ if C_infobar_correctPercent:
+ self.sidebar_correctPercent.setChecked(True)
+ if C_infobar_fastestReview:
+ self.sidebar_fastestReview.setChecked(True)
+ if C_infobar_slowestReview:
+ self.sidebar_slowestReview.setChecked(True)
+ if C_infobar_noteID:
+ self.sidebar_noteID.setChecked(True)
+ if C_infobar_cardID:
+ self.sidebar_cardID.setChecked(True)
+ if C_sidebar_autoOpen:
+ self.sidebar_autoOpen.setChecked(True)
+ if C_custom_reviewButtonColors:
+ self.custom_reviewButtonColors.setChecked(True)
+ if C_custom_reviewButtonTextColor:
+ self.custom_reviewButtonTextColor.setChecked(True)
+ if C_custom_activeIndicatorColor:
+ self.custom_activeIndicatorColor.setChecked(True)
+ if C_custom_bottombarButtonTextColor:
+ self.custom_bottombarButtonTextColor.setChecked(True)
+ if C_custom_bottombarButtonBorderColor:
+ self.custom_bottombarButtonBorderColor.setChecked(True)
+ self.changeButtonColor(self.reviewButtonTextColor_button, C_reviewButtonText_color)
+ self.changeButtonColor(self.activeIndicatorColor_button, C_activeIndicator_color)
+ self.changeButtonColor(self.bottombarButtonTextColor_button, C_bottombarButtonText_color)
+ self.changeButtonColor(self.bottombarButtonBorderColor_button, C_bottombarButtonBorder_color)
+ self.changeButtonColor(self.againColor_button, C_again_color)
+ self.changeButtonColor(self.againHoverColor_button, C_againHover_color)
+ self.changeButtonColor(self.hardColor_button, C_hard_color)
+ self.changeButtonColor(self.hardHoverColor_button, C_hardHover_color)
+ self.changeButtonColor(self.goodColor_button, C_good_color)
+ self.changeButtonColor(self.goodHoverColor_button, C_goodHover_color)
+ self.changeButtonColor(self.easyColor_button, C_easy_color)
+ self.changeButtonColor(self.easyHoverColor_button, C_easyHover_color)
+ self.showAnswerEase1.setValue(int(C_showAnswerEase1))
+ self.showAnswerEase2.setValue(int(C_showAnswerEase2))
+ self.showAnswerEase3.setValue(int(C_showAnswerEase3))
+ self.showAnswerEase4.setValue(int(C_showAnswerEase4))
+ self.changeButtonColor(self.showAnswerEase1_button, C_showAnswerEase1_color)
+ self.changeButtonColor(self.showAnswerEase2_button, C_showAnswerEase2_color)
+ self.changeButtonColor(self.showAnswerEase3_button, C_showAnswerEase3_color)
+ self.changeButtonColor(self.showAnswerEase4_button, C_showAnswerEase4_color)
+ if C_button_colors:
+ self.buttonColors_on.setChecked(True)
+ else:
+ self.buttonColors_off.setChecked(True)
+ if C_speedFocus:
+ self.speedFocus_on.setChecked(True)
+ else:
+ self.speedFocus_off.setChecked(True)
+ if C_configEdit:
+ self.configEdit_on.setChecked(True)
+ else:
+ self.configEdit_off.setChecked(True)
+ self.overViewStats.setCurrentIndex(C_overViewStats)
+ self.settingsMenu_place.setCurrentIndex(C_settingsMenu_palce)
+ self.skipMethod.setCurrentIndex(C_skipMethod)
+
+ def onLoadSettings(self):
+ addon_path = dirname(__file__)
+ #// Open a file browser to choose the settings file (returns a tuple) the first item in the tuple is the settings file location
+ fileName_tuple = QFileDialog.getOpenFileName(self, 'Open file', r'{}\user_files'.format(addon_path))
+ #// If user cancels the operation and no file is chosen, then return without doing anything
+ if not fileName_tuple[0]:
+ return
+ #// Select the settings file from the tuple
+ settingsFile = fileName_tuple[0]
+ #// Open and read the JSON File
+ settings = open("{}".format(settingsFile), "r")
+ conf = json.load(settings)
+ settingsFile_name = os.path.basename(settingsFile)
+ load = askUser("Replace current settings with settings file <{}>?".format(settingsFile_name), self, None, defaultno=True, title="Advanced Review Bottomabr")
+ if load:
+ mw.addonManager.writeConfig(__name__, conf)
+ showInfo("<div style='font-size: 15px;'>Settings Loaded Succesfully.\
+ </div><div style='color: red; font-size: 15px;'> Changes will take \
+ effect after you restart anki.</div>", title="Advanced Review Bottombar Settings")
+ self.close()
+ refreshConfig()
+ else:
+ return
+ settings.close()
+
+ def onSaveSettings(self):
+ addon_path = dirname(__file__)
+ #// Choose a name for the backup file
+ file_name = "ARBb {}".format(datetime.now().strftime("%d-%b-%Y %H-%M-%S"))
+ path_to_file = "{}\\user_files\\{}.json".format(addon_path, file_name)
+ f = open(path_to_file, "w")
+
+ if self.left_skip.isChecked():
+ skip_position = "left"
+ elif self.middleRight_skip.isChecked():
+ skip_position ="middle right"
+ elif self.right_skip.isChecked():
+ skip_position ="right"
+ else:
+ skip_position = "middle left"
+ if self.left_showSkipped.isChecked():
+ showSkipped_position = "left"
+ elif self.middleRight_showSkipped.isChecked():
+ showSkipped_position ="middle right"
+ elif self.right_showSkipped.isChecked():
+ showSkipped_position ="right"
+ else:
+ showSkipped_position = "middle left"
+ if self.middleLeft_info.isChecked():
+ info_position = "middle left"
+ elif self.middleRight_info.isChecked():
+ info_position = "middle right"
+ elif self.right_info.isChecked():
+ info_position = "right"
+ else:
+ info_position = "left"
+ if self.left_undo.isChecked():
+ undo_position = "left"
+ elif self.middleLeft_undo.isChecked():
+ undo_position = "middle left"
+ elif self.right_undo.isChecked():
+ undo_position = "right"
+ else:
+ undo_position = "middle right"
+ if self.showAnswerEase1.value() > self.showAnswerEase2.value():
+ self.showAnswerEase2.setValue((self.showAnswerEase1.value() + 50))
+ if self.showAnswerEase2.value() > self.showAnswerEase3.value():
+ self.showAnswerEase3.setValue((self.showAnswerEase2.value() + 50))
+ if self.showAnswerEase3.value() > self.showAnswerEase4.value():
+ self.showAnswerEase4.setValue((self.showAnswerEase3.value() + 50))
+ conf = {
+ " Button Colors": self.buttonColors_on.isChecked(),
+ " Speed Focus Add-on": self.speedFocus_on.isChecked(),
+ " Direct Config Edit": self.configEdit_on.isChecked(),
+ " More Overview Stats": self.overViewStats.currentIndex(),
+ " Settings Menu Place": self.settingsMenu_place.currentIndex(),
+ " Skip Method": self.skipMethod.currentIndex(),
+ " Style Main Screen Buttons": self.style_mainScreenButtons.isChecked(),
+ " Review_ Active Button Indicator": self.active_indicator.currentIndex(),
+ " Review_ Buttons Style": self.button_style.currentIndex(),
+ " Review_ Hover Effect": self.hover_effect.currentIndex(),
+ " Review_ Custom Colors": self.custom_reviewButtonColors.isChecked(),
+ " Review_ Custom Review Button Text Color": self.custom_reviewButtonTextColor.isChecked(),
+ " Review_ Custom Active Indicator Color": self.custom_activeIndicatorColor.isChecked(),
+ " Review_ Bottombar Buttons Style": self.bottombarButtons_style.currentIndex(),
+ " Review_ Cursor Style": self.cursor_style.currentIndex(),
+ " Review_ Interval Style": self.interval_style.currentIndex(),
+ " Review_ Button Transition Time": self.buttonTransition_time.value(),
+ " Review_ Button Border Radius": self.buttonBorderRadius.value(),
+ "Button_ Info Button": self.info.isChecked(),
+ "Button_ Skip Button": self.skip.isChecked(),
+ "Button_ Show Skipped Button": self.showSkipped.isChecked(),
+ "Button_ Undo Button": self.undo.isChecked(),
+ "Button_ Hide Hard": self.hideHard.isChecked(),
+ "Button_ Hide Good": self.hideGood.isChecked(),
+ "Button_ Hide Easy": self.hideEasy.isChecked(),
+ "Button_ Custom Button Sizes": self.customSizes_on.isChecked(),
+ "Button_ Shortcut_ Skip Button": self.skip_shortcut,
+ "Button_ Shortcut_ Show Skipped Button": self.showSkipped_shortcut,
+ "Button_ Shortcut_ Info Button": self.info_shortcut,
+ "Button_ Shortcut_ Undo Button": self.undo_shortcut,
+ "Button_ Position_ Info Button": info_position,
+ "Button_ Position_ Skip Button": skip_position,
+ "Button_ Position_ Show Skipped Button": showSkipped_position,
+ "Button_ Position_ Undo Button": undo_position,
+ "Button_ Text Size": self.text_size.value(),
+ "Button_ Height_ All Bottombar Buttons": self.buttons_height.value(),
+ "Button_ Width_ Edit Button": self.edit_width.value(),
+ "Button_ Width_ Show Answer Button": self.answer_width.value(),
+ "Button_ Width_ Info Button": self.info_width.value(),
+ "Button_ Width_ Skip Button": self.skip_width.value(),
+ "Button_ Width_ Show Skipped Button": self.showSkipped_width.value(),
+ "Button_ Width_ More Button": self.more_width.value(),
+ "Button_ Width_ Review Buttons": self.reviewButtons_width.value(),
+ "Button_ Width_ Undo Button": self.undo_width.value(),
+ "Button Label_ Study Now": self.buttonLabel_studyNow.text(),
+ "Button Label_ Edit": self.buttonLabel_edit.text(),
+ "Button Label_ Show Answer": self.buttonLabel_showAnswer.text(),
+ "Button Label_ More": self.buttonLabel_more.text(),
+ "Button Label_ Info": self.buttonLabel_info.text(),
+ "Button Label_ Skip": self.buttonLabel_skip.text(),
+ "Button Label_ Show Skipped": self.buttonLabel_showSkipped.text(),
+ "Button Label_ Undo": self.buttonLabel_undo.text(),
+ "Button Label_ Again": self.buttonLabel_again.text(),
+ "Button Label_ Hard": self.buttonLabel_hard.text(),
+ "Button Label_ Good": self.buttonLabel_good.text(),
+ "Button Label_ Easy": self.buttonLabel_easy.text(),
+ "Card Info sidebar_ Number of previous cards to show": self.sidebar_PreviousCards.value(),
+ "Card Info sidebar_ Default Position": self.sidebar_position.currentIndex(),
+ "Card Info sidebar_ theme": self.sidebar_theme.currentIndex(),
+ "Card Info sidebar_ Created": self.sidebar_dateCreated.isChecked(),
+ "Card Info sidebar_ Edited": self.sidebar_dateEdited.isChecked(),
+ "Card Info sidebar_ First Review": self.sidebar_firstReview.isChecked(),
+ "Card Info sidebar_ Latest Review": self.sidebar_latestReview.isChecked(),
+ "Card Info sidebar_ Due": self.sidebar_due.isChecked(),
+ "Card Info sidebar_ Interval": self.sidebar_interval.isChecked(),
+ "Card Info sidebar_ Ease": self.sidebar_ease.isChecked(),
+ "Card Info sidebar_ Reviews": self.sidebar_numberOfReviews.isChecked(),
+ "Card Info sidebar_ Lapses": self.sidebar_lapses.isChecked(),
+ "Card Info Sidebar_ Correct Percent": self.sidebar_correctPercent.isChecked(),
+ "Card Info Sidebar_ Fastest Review": self.sidebar_fastestReview.isChecked(),
+ "Card Info Sidebar_ Slowest Review": self.sidebar_slowestReview.isChecked(),
+ "Card Info sidebar_ Average Time": self.sidebar_averageTime.isChecked(),
+ "Card Info sidebar_ Total Time": self.sidebar_totalTime.isChecked(),
+ "Card Info sidebar_ Card Type": self.sidebar_cardType.isChecked(),
+ "Card Info sidebar_ Note Type": self.sidebar_noteType.isChecked(),
+ "Card Info sidebar_ Deck": self.sidebar_deck.isChecked(),
+ "Card Info sidebar_ Tags": self.sidebar_tags.isChecked(),
+ "Card Info Sidebar_ Note ID": self.sidebar_noteID.isChecked(),
+ "Card Info Sidebar_ Card ID": self.sidebar_cardID.isChecked(),
+ "Card Info sidebar_ Sort Field": self.sidebar_sortField.isChecked(),
+ "Card Info sidebar_ Current Review Count": self.sidebar_currentReviewCount.isChecked(),
+ "Card Info sidebar_ Font": self.sidebar_font.currentFont().family(),
+ "Card Info sidebar_ number of reviews to show for a card": self.sidebar_reviewsToShow.value(),
+ "Card Info sidebar_ Auto Open": self.sidebar_autoOpen.isChecked(),
+ "Card Info sidebar_ warning note": self.sidebar_warningNote.isChecked(),
+ "Color_ General Text Color": self.reviewButtonText_color,
+ "Color_ Active Button Indicator": self.activeIndicator_color,
+ "Color_ Bottombar Button Text Color": self.bottombarButtonText_color,
+ "Color_ Bottombar Button Border Color": self.bottombarButtonBorder_color,
+ "Color_ Custom Bottombar Button Text Color": self.custom_bottombarButtonTextColor.isChecked(),
+ "Color_ Custom Bottombar Button Border Color": self.custom_bottombarButtonBorderColor.isChecked(),
+ "Color_ Again": self.again_color,
+ "Color_ Again on hover": self.againHover_color,
+ "Color_ Hard": self.hard_color,
+ "Color_ Hard on hover": self.hardHover_color,
+ "Color_ Good":self.good_color,
+ "Color_ Good on hover": self.goodHover_color,
+ "Color_ Easy": self.easy_color,
+ "Color_ Easy on hover": self.easyHover_color,
+ "Tooltip": self.reviewTooltip_on.isChecked(),
+ "Tooltip Timer": self.reviewTooltip_timer.value(),
+ "Tooltip Text Color": self.reviewTooltipText_color,
+ "Tooltip Style": self.reviewTooltip_style.currentIndex(),
+ "Tooltip Position": [self.reviewTooltipPositionX.value(), self.reviewTooltipPositionY.value()],
+ "ShowAnswer_ Border Color Style": self.showAnswerBorderColor_style.currentIndex(),
+ "ShowAnswer_ Ease1": self.showAnswerEase1.value(),
+ "ShowAnswer_ Ease2": self.showAnswerEase2.value(),
+ "ShowAnswer_ Ease3": self.showAnswerEase3.value(),
+ "ShowAnswer_ Ease4": self.showAnswerEase4.value(),
+ "ShowAnswer_ Ease1 Color": self.showAnswerEase1_color,
+ "ShowAnswer_ Ease2 Color": self.showAnswerEase2_color,
+ "ShowAnswer_ Ease3 Color": self.showAnswerEase3_color,
+ "ShowAnswer_ Ease4 Color": self.showAnswerEase4_color
+ }
+ #// Save settings in a JSON file
+ json.dump(conf, f, indent=4)
+ #// Open file explorer after saving so users know where the backup file is (and maybe save it somewhere else)
+ subprocess.Popen(f'explorer /select, "{path_to_file}"')
+ f.close()
+
+ def onApply(self):
+ if self.left_skip.isChecked():
+ skip_position = "left"
+ elif self.middleRight_skip.isChecked():
+ skip_position ="middle right"
+ elif self.right_skip.isChecked():
+ skip_position ="right"
+ else:
+ skip_position = "middle left"
+ if self.left_showSkipped.isChecked():
+ showSkipped_position = "left"
+ elif self.middleRight_showSkipped.isChecked():
+ showSkipped_position ="middle right"
+ elif self.right_showSkipped.isChecked():
+ showSkipped_position ="right"
+ else:
+ showSkipped_position = "middle left"
+ if self.middleLeft_info.isChecked():
+ info_position = "middle left"
+ elif self.middleRight_info.isChecked():
+ info_position = "middle right"
+ elif self.right_info.isChecked():
+ info_position = "right"
+ else:
+ info_position = "left"
+ if self.left_undo.isChecked():
+ undo_position = "left"
+ elif self.middleLeft_undo.isChecked():
+ undo_position = "middle left"
+ elif self.right_undo.isChecked():
+ undo_position = "right"
+ else:
+ undo_position = "middle right"
+ if self.showAnswerEase1.value() > self.showAnswerEase2.value():
+ self.showAnswerEase2.setValue((self.showAnswerEase1.value() + 50))
+ if self.showAnswerEase2.value() > self.showAnswerEase3.value():
+ self.showAnswerEase3.setValue((self.showAnswerEase2.value() + 50))
+ if self.showAnswerEase3.value() > self.showAnswerEase4.value():
+ self.showAnswerEase4.setValue((self.showAnswerEase3.value() + 50))
+
+ conf = {
+ " Button Colors": self.buttonColors_on.isChecked(),
+ " Speed Focus Add-on": self.speedFocus_on.isChecked(),
+ " Direct Config Edit": self.configEdit_on.isChecked(),
+ " More Overview Stats": self.overViewStats.currentIndex(),
+ " Settings Menu Place": self.settingsMenu_place.currentIndex(),
+ " Skip Method": self.skipMethod.currentIndex(),
+ " Style Main Screen Buttons": self.style_mainScreenButtons.isChecked(),
+ " Review_ Active Button Indicator": self.active_indicator.currentIndex(),
+ " Review_ Buttons Style": self.button_style.currentIndex(),
+ " Review_ Hover Effect": self.hover_effect.currentIndex(),
+ " Review_ Custom Colors": self.custom_reviewButtonColors.isChecked(),
+ " Review_ Custom Review Button Text Color": self.custom_reviewButtonTextColor.isChecked(),
+ " Review_ Custom Active Indicator Color": self.custom_activeIndicatorColor.isChecked(),
+ " Review_ Bottombar Buttons Style": self.bottombarButtons_style.currentIndex(),
+ " Review_ Cursor Style": self.cursor_style.currentIndex(),
+ " Review_ Interval Style": self.interval_style.currentIndex(),
+ " Review_ Button Transition Time": self.buttonTransition_time.value(),
+ " Review_ Button Border Radius": self.buttonBorderRadius.value(),
+ "Button_ Info Button": self.info.isChecked(),
+ "Button_ Skip Button": self.skip.isChecked(),
+ "Button_ Show Skipped Button": self.showSkipped.isChecked(),
+ "Button_ Undo Button": self.undo.isChecked(),
+ "Button_ Hide Hard": self.hideHard.isChecked(),
+ "Button_ Hide Good": self.hideGood.isChecked(),
+ "Button_ Hide Easy": self.hideEasy.isChecked(),
+ "Button_ Custom Button Sizes": self.customSizes_on.isChecked(),
+ "Button_ Shortcut_ Skip Button": self.skip_shortcut,
+ "Button_ Shortcut_ Show Skipped Button": self.showSkipped_shortcut,
+ "Button_ Shortcut_ Info Button": self.info_shortcut,
+ "Button_ Shortcut_ Undo Button": self.undo_shortcut,
+ "Button_ Position_ Info Button": info_position,
+ "Button_ Position_ Skip Button": skip_position,
+ "Button_ Position_ Show Skipped Button": showSkipped_position,
+ "Button_ Position_ Undo Button": undo_position,
+ "Button_ Text Size": self.text_size.value(),
+ "Button_ Height_ All Bottombar Buttons": self.buttons_height.value(),
+ "Button_ Width_ Edit Button": self.edit_width.value(),
+ "Button_ Width_ Show Answer Button": self.answer_width.value(),
+ "Button_ Width_ Info Button": self.info_width.value(),
+ "Button_ Width_ Skip Button": self.skip_width.value(),
+ "Button_ Width_ Show Skipped Button": self.showSkipped_width.value(),
+ "Button_ Width_ More Button": self.more_width.value(),
+ "Button_ Width_ Review Buttons": self.reviewButtons_width.value(),
+ "Button_ Width_ Undo Button": self.undo_width.value(),
+ "Button Label_ Study Now": self.buttonLabel_studyNow.text(),
+ "Button Label_ Edit": self.buttonLabel_edit.text(),
+ "Button Label_ Show Answer": self.buttonLabel_showAnswer.text(),
+ "Button Label_ More": self.buttonLabel_more.text(),
+ "Button Label_ Info": self.buttonLabel_info.text(),
+ "Button Label_ Skip": self.buttonLabel_skip.text(),
+ "Button Label_ Show Skipped": self.buttonLabel_showSkipped.text(),
+ "Button Label_ Undo": self.buttonLabel_undo.text(),
+ "Button Label_ Again": self.buttonLabel_again.text(),
+ "Button Label_ Hard": self.buttonLabel_hard.text(),
+ "Button Label_ Good": self.buttonLabel_good.text(),
+ "Button Label_ Easy": self.buttonLabel_easy.text(),
+ "Card Info sidebar_ Number of previous cards to show": self.sidebar_PreviousCards.value(),
+ "Card Info sidebar_ Default Position": self.sidebar_position.currentIndex(),
+ "Card Info sidebar_ theme": self.sidebar_theme.currentIndex(),
+ "Card Info sidebar_ Created": self.sidebar_dateCreated.isChecked(),
+ "Card Info sidebar_ Edited": self.sidebar_dateEdited.isChecked(),
+ "Card Info sidebar_ First Review": self.sidebar_firstReview.isChecked(),
+ "Card Info sidebar_ Latest Review": self.sidebar_latestReview.isChecked(),
+ "Card Info sidebar_ Due": self.sidebar_due.isChecked(),
+ "Card Info sidebar_ Interval": self.sidebar_interval.isChecked(),
+ "Card Info sidebar_ Ease": self.sidebar_ease.isChecked(),
+ "Card Info sidebar_ Reviews": self.sidebar_numberOfReviews.isChecked(),
+ "Card Info sidebar_ Lapses": self.sidebar_lapses.isChecked(),
+ "Card Info Sidebar_ Correct Percent": self.sidebar_correctPercent.isChecked(),
+ "Card Info Sidebar_ Fastest Review": self.sidebar_fastestReview.isChecked(),
+ "Card Info Sidebar_ Slowest Review": self.sidebar_slowestReview.isChecked(),
+ "Card Info sidebar_ Average Time": self.sidebar_averageTime.isChecked(),
+ "Card Info sidebar_ Total Time": self.sidebar_totalTime.isChecked(),
+ "Card Info sidebar_ Card Type": self.sidebar_cardType.isChecked(),
+ "Card Info sidebar_ Note Type": self.sidebar_noteType.isChecked(),
+ "Card Info sidebar_ Deck": self.sidebar_deck.isChecked(),
+ "Card Info sidebar_ Tags": self.sidebar_tags.isChecked(),
+ "Card Info Sidebar_ Note ID": self.sidebar_noteID.isChecked(),
+ "Card Info Sidebar_ Card ID": self.sidebar_cardID.isChecked(),
+ "Card Info sidebar_ Sort Field": self.sidebar_sortField.isChecked(),
+ "Card Info sidebar_ Current Review Count": self.sidebar_currentReviewCount.isChecked(),
+ "Card Info sidebar_ Font": self.sidebar_font.currentFont().family(),
+ "Card Info sidebar_ number of reviews to show for a card": self.sidebar_reviewsToShow.value(),
+ "Card Info sidebar_ Auto Open": self.sidebar_autoOpen.isChecked(),
+ "Card Info sidebar_ warning note": self.sidebar_warningNote.isChecked(),
+ "Color_ General Text Color": self.reviewButtonText_color,
+ "Color_ Active Button Indicator": self.activeIndicator_color,
+ "Color_ Bottombar Button Text Color": self.bottombarButtonText_color,
+ "Color_ Bottombar Button Border Color": self.bottombarButtonBorder_color,
+ "Color_ Custom Bottombar Button Text Color": self.custom_bottombarButtonTextColor.isChecked(),
+ "Color_ Custom Bottombar Button Border Color": self.custom_bottombarButtonBorderColor.isChecked(),
+ "Color_ Again": self.again_color,
+ "Color_ Again on hover": self.againHover_color,
+ "Color_ Hard": self.hard_color,
+ "Color_ Hard on hover": self.hardHover_color,
+ "Color_ Good":self.good_color,
+ "Color_ Good on hover": self.goodHover_color,
+ "Color_ Easy": self.easy_color,
+ "Color_ Easy on hover": self.easyHover_color,
+ "Tooltip": self.reviewTooltip_on.isChecked(),
+ "Tooltip Timer": self.reviewTooltip_timer.value(),
+ "Tooltip Text Color": self.reviewTooltipText_color,
+ "Tooltip Style": self.reviewTooltip_style.currentIndex(),
+ "Tooltip Position": [self.reviewTooltipPositionX.value(), self.reviewTooltipPositionY.value()],
+ "ShowAnswer_ Border Color Style": self.showAnswerBorderColor_style.currentIndex(),
+ "ShowAnswer_ Ease1": self.showAnswerEase1.value(),
+ "ShowAnswer_ Ease2": self.showAnswerEase2.value(),
+ "ShowAnswer_ Ease3": self.showAnswerEase3.value(),
+ "ShowAnswer_ Ease4": self.showAnswerEase4.value(),
+ "ShowAnswer_ Ease1 Color": self.showAnswerEase1_color,
+ "ShowAnswer_ Ease2 Color": self.showAnswerEase2_color,
+ "ShowAnswer_ Ease3 Color": self.showAnswerEase3_color,
+ "ShowAnswer_ Ease4 Color": self.showAnswerEase4_color
+ }
+ mw.addonManager.writeConfig(__name__, conf)
+ showInfo("<div style='color: red;\
+ font-size: 15px;'> Changes will take effect after you restart anki. </div>\
+ <div style='font-size: 15px;'> Sidebar changes will take effect immediately.\
+ </div>", title="Advanced Review Bottombar Settings")
+ refreshConfig()
+ self.close()
+
+ def getNewColor(self, color_variable, color_button):
+ color_window = QColorDialog()
+ color = color_window.getColor()
+ if color.isValid():
+ color = color.name()
+ if color_variable == "again_color":
+ self.again_color = color
+ elif color_variable == "againHover_color":
+ self.againHover_color = color
+ elif color_variable == "hard_color":
+ self.hard_color = color
+ elif color_variable == "hardHover_color":
+ self.hardHover_color = color
+ elif color_variable == "good_color":
+ self.good_color = color
+ elif color_variable == "goodHover_color":
+ self.goodHover_color = color
+ elif color_variable == "easy_color":
+ self.easy_color = color
+ elif color_variable == "easyHover_color":
+ self.easyHover_color = color
+ elif color_variable == "reviewButtonText_color":
+ self.reviewButtonText_color = color
+ elif color_variable == "activeIndicator_color":
+ self.activeIndicator_color = color
+ elif color_variable == "reviewTooltipText_color":
+ self.reviewTooltipText_color = color
+ elif color_variable == "bottombarButtonText_color":
+ self.bottombarButtonText_color = color
+ elif color_variable == "bottombarButtonBorder_color":
+ self.bottombarButtonBorder_color = color
+ elif color_variable == "showAnswerEase1":
+ self.showAnswerEase1_color = color
+ elif color_variable == "showAnswerEase2":
+ self.showAnswerEase2_color = color
+ elif color_variable == "showAnswerEase3":
+ self.showAnswerEase3_color = color
+ elif color_variable == "showAnswerEase4":
+ self.showAnswerEase4_color = color
+ self.changeButtonColor(color_button, color)
+
+ def changeButtonColor(self, button, color):
+ pixmap = QPixmap(95, 18)
+ qcolour = QColor(0, 0, 0)
+ qcolour.setNamedColor(color)
+ pixmap.fill(qcolour)
+ button.setIcon(QIcon(pixmap))
+ button.setIconSize(QSize(95, 18))
+
+ def updateShortcut(self, button_variable, combination=None):
+ """Update hotkey label and attribute"""
+ if button_variable == "info_shortcut":
+ shortcut = combination or self.info_shortcut
+ self.infoShortcut_button.setText("Change Shortcut (Current: {})".format(shortcut))
+ elif button_variable == "skip_shortcut":
+ shortcut = combination or self.skip_shortcut
+ self.skipShortcut_button.setText("Change Shortcut (Current: {})".format(shortcut))
+ elif button_variable == "showSkipped_shortcut":
+ shortcut = combination or self.showSkippedp_shortcut
+ self.showSkippedShortcut_button.setText("Change Shortcut (Current: {})".format(shortcut))
+ elif button_variable == "undo_shortcut":
+ shortcut = combination or self.undo_shortcut
+ self.undoShortcut_button.setText("Change Shortcut (Current: {})".format(shortcut))
+ else:
+ return
+
+ if combination:
+ if button_variable == "info_shortcut":
+ self.info_shortcut = combination
+ elif button_variable == "skip_shortcut":
+ self.skip_shortcut = combination
+ elif button_variable == "showSkipped_shortcut":
+ self.showSkipped_shortcut = combination
+ elif button_variable == "undo_shortcut":
+ self.undo_shortcut = combination
+ else:
+ return
+
+ def showGetShortcut(self, button_variable):
+ getShortcut = GetShortcut(self, button_variable)
+ getShortcut.exec()
+
+def open_settings():
+ settings = SettingsMenu()
+ #// For styling settings menu -_-
+ # settings.setStyle(QStyleFactory.create("Fusion"))
+ settings.exec()
+
+def setupMenu():
+ settings = QAction('&Advanced Review Bottombar Settings', mw)
+ if C_settingsMenu_palce == 1:
+ mw.form.menuTools.addAction(settings)
+ else:
+ mw.ARBB_menu = QMenu('&ARBb', mw)
+ mw.ARBB_menu.addAction(settings)
+ mw.form.menubar.insertMenu(mw.form.menuHelp.menuAction(), mw.ARBB_menu)
+ settings.triggered.connect(open_settings)
+ settings.setShortcut(QKeySequence('Shift+A'))
+setupMenu()
+
+if not C_configEdit:
+ mw.addonManager.setConfigAction(__name__, open_settings)
diff --git a/.local/share/Anki2/addons21/advanced_review/Skip.py b/.local/share/Anki2/addons21/advanced_review/Skip.py new file mode 100644 index 0000000..47d5d11 --- /dev/null +++ b/.local/share/Anki2/addons21/advanced_review/Skip.py @@ -0,0 +1,39 @@ +
+from aqt import mw
+from aqt import gui_hooks
+from aqt.utils import showInfo
+from aqt.reviewer import Reviewer
+
+#// Bury and mark for unburying
+def burySkipped():
+ if hasattr(mw.reviewer, "skipped_cards"):
+ mw.reviewer.skipped_cards.append(mw.reviewer.card.id)
+ else:
+ mw.reviewer.skipped_cards = [mw.reviewer.card.id,]
+ mw.checkpoint(_("Bury"))
+ mw.col.sched.buryCards([mw.reviewer.card.id])
+ mw.reset()
+
+#// Unbury skipped cards
+def unburySkipped():
+ allburied = [int(x) for x in mw.col.findCards("is:buried")]
+ to_rebury = []
+ if allburied:
+ for card in allburied:
+ if not card in mw.reviewer.skipped_cards:
+ to_rebury.append(card)
+ del mw.reviewer.skipped_cards
+ mw.col.sched.unburyCards()
+ if to_rebury:
+ mw.col.sched.buryCards(to_rebury)
+ mw.reset()
+
+def try_unburySkipped():
+ if hasattr(mw.reviewer, "skipped_cards"):
+ unburySkipped()
+
+#// Unbury skipped cards upon exiting review screeen
+gui_hooks.reviewer_will_end.append(try_unburySkipped)
+
+def test():
+ showInfo("{}".format(mw.col.sched.version))
diff --git a/.local/share/Anki2/addons21/advanced_review/Tooltip.py b/.local/share/Anki2/addons21/advanced_review/Tooltip.py new file mode 100644 index 0000000..dbfba29 --- /dev/null +++ b/.local/share/Anki2/addons21/advanced_review/Tooltip.py @@ -0,0 +1,613 @@ +#// auth_ Mohamad Janati
+#// Copyright (c) 2019-2021 Mohamad Janati (freaking stupid, right? :|)
+
+
+from aqt.utils import tooltip
+from anki.hooks import wrap
+import aqt
+from aqt.reviewer import Reviewer
+import aqt
+from aqt.qt import *
+from aqt import mw
+from anki import version
+anki_version = int(version.replace('.', ''))
+if anki_version > 2119:
+ from typing import Optional
+
+config = mw.addonManager.getConfig(__name__)
+tooltip_timer = config["Tooltip Timer"]
+tooltip_textColor = config["Tooltip Text Color"]
+reviewButton_style = config[" Review_ Buttons Style"]
+custom_sizes = config ['Button_ Custom Button Sizes']
+buttons_height = config['Button_ Height_ All Bottombar Buttons']
+reviewButtons_width = config['Button_ Width_ Review Buttons']
+custom_reviewButtonColors = config[' Review_ Custom Colors']
+againHover_color = config['Color_ Again on hover']
+hardHover_color = config['Color_ Hard on hover']
+goodHover_color = config['Color_ Good on hover']
+easyHover_color = config['Color_ Easy on hover']
+tooltip_style = config['Tooltip Style']
+tooltip_position = config['Tooltip Position']
+info_position = config["Button_ Position_ Info Button"]
+skip_position = config['Button_ Position_ Skip Button']
+undo_position = config['Button_ Position_ Undo Button']
+info = config['Button_ Info Button']
+skip = config['Button_ Skip Button']
+undo = config['Button_ Undo Button']
+edit_width = config["Button_ Width_ Edit Button"]
+info_width = config["Button_ Width_ Info Button"]
+skip_width = config["Button_ Width_ Skip Button"]
+more_width = config["Button_ Width_ More Button"]
+undo_width = config["Button_ Width_ Undo Button"]
+if not custom_sizes:
+ edit_width = 72
+ info_width = 64
+ skip_width = 64
+ undo_width = 64
+ more_width = 72
+if custom_reviewButtonColors:
+ again_backgroundColor = config['Color_ Again on hover']
+ hard_backgroundColor = config['Color_ Hard on hover']
+ good_backgroundColor = config['Color_ Good on hover']
+ easy_backgroundColor = config['Color_ Easy on hover']
+else:
+ again_backgroundColor = "#FF1111"
+ hard_backgroundColor = "#FF9814"
+ good_backgroundColor = "#33FF2D"
+ easy_backgroundColor = "#21C0FF"
+
+def myTooltip(self, ease): #\\ should i store text, color and position all in this function?? YESSS
+ button_count = self.mw.col.sched.answerButtons(self.card)
+ if button_count == 2:
+ if ease == 1:
+ button = ["Again", again_backgroundColor, -1, -39]
+ elif ease == 2:
+ button = ["Good", good_backgroundColor, 70, -39]
+ else:
+ button = ["", "transparent", 20000, 20000]
+ elif button_count == 3:
+ if ease == 1:
+ button = ["Again", again_backgroundColor, -37, -39]
+ elif ease == 2:
+ button = ["Good", good_backgroundColor, 35, -39]
+ elif ease == 3:
+ button = ["Easy", easy_backgroundColor, 107, -39]
+ else:
+ button = ["", "transparent", 20000, 20000]
+ else:
+ if ease == 1:
+ button = ["Again", again_backgroundColor, -73, -39]
+ elif ease == 2:
+ button = ["Hard", hard_backgroundColor, -3, -39]
+ elif ease == 3:
+ button = ["Good", good_backgroundColor, 70, -39]
+ elif ease == 4:
+ button = ["Easy", easy_backgroundColor, 142, -39]
+ else:
+ button = ["", "transparent", 20000, 20000]
+ button_width = 60
+ button_height = 26
+ x_offset = button[2]
+ y_offset = button[3]
+ text = button[0]
+ if custom_sizes and reviewButton_style not in [2, 3]:
+ button_height = buttons_height
+ button_width = reviewButtons_width
+ y_offset -= (buttons_height - 30)
+ if button_count == 2:
+ if reviewButtons_width < 61:
+ if ease == 1:
+ x_offset -= reviewButtons_width/4
+ x_offset += 19
+ elif ease == 2:
+ x_offset += reviewButtons_width/4
+ x_offset -= 15
+ elif 60 < reviewButtons_width < 81:
+ if ease == 1:
+ x_offset -= reviewButtons_width/4
+ x_offset += 14
+ elif ease == 2:
+ x_offset += reviewButtons_width/4
+ x_offset -= 10
+ elif 80 < reviewButtons_width < 101:
+ if ease == 1:
+ x_offset -= reviewButtons_width/4
+ x_offset += 9
+ elif ease == 2:
+ x_offset += reviewButtons_width/4
+ x_offset -= 5
+ elif 100 < reviewButtons_width < 121:
+ if ease == 1:
+ x_offset -= reviewButtons_width/4
+ x_offset += 4
+ elif ease == 2:
+ x_offset += reviewButtons_width/4
+ elif 120 < reviewButtons_width < 141:
+ if ease == 1:
+ x_offset -= reviewButtons_width/4
+ x_offset -= 1
+ elif ease == 2:
+ x_offset += reviewButtons_width/4
+ x_offset += 5
+ elif 140 < reviewButtons_width < 161:
+ if ease == 1:
+ x_offset -= reviewButtons_width/4
+ x_offset -= 6
+ elif ease == 2:
+ x_offset += reviewButtons_width/4
+ x_offset += 10
+ elif 160 < reviewButtons_width < 181:
+ if ease == 1:
+ x_offset -= reviewButtons_width/4
+ x_offset -= 11
+ elif ease == 2:
+ x_offset += reviewButtons_width/4
+ x_offset += 15
+ elif 180 < reviewButtons_width < 201:
+ if ease == 1:
+ x_offset -= reviewButtons_width/4
+ x_offset -= 16
+ elif ease == 2:
+ x_offset += reviewButtons_width/4
+ x_offset += 20
+ elif 200 < reviewButtons_width < 221:
+ if ease == 1:
+ x_offset -= reviewButtons_width/4
+ x_offset -= 21
+ elif ease == 2:
+ x_offset += reviewButtons_width/4
+ x_offset += 25
+ elif 220 < reviewButtons_width < 241:
+ if ease == 1:
+ x_offset -= reviewButtons_width/4
+ x_offset -= 26
+ elif ease == 2:
+ x_offset += reviewButtons_width/4
+ x_offset += 30
+ elif 240 < reviewButtons_width < 261:
+ if ease == 1:
+ x_offset -= reviewButtons_width/4
+ x_offset -= 31
+ elif ease == 2:
+ x_offset += reviewButtons_width/4
+ x_offset += 35
+ elif 260 < reviewButtons_width < 281:
+ if ease == 1:
+ x_offset -= reviewButtons_width/4
+ x_offset -= 36
+ elif ease == 2:
+ x_offset += reviewButtons_width/4
+ x_offset += 40
+ elif 280 < reviewButtons_width < 301:
+ if ease == 1:
+ x_offset -= reviewButtons_width/4
+ x_offset -= 41
+ elif ease == 2:
+ x_offset += reviewButtons_width/4
+ x_offset += 45
+ elif 300 < reviewButtons_width < 321:
+ if ease == 1:
+ x_offset -= reviewButtons_width/4
+ x_offset -= 46
+ elif ease == 2:
+ x_offset += reviewButtons_width/4
+ x_offset += 50
+ elif 320 < reviewButtons_width < 341:
+ if ease == 1:
+ x_offset -= reviewButtons_width/4
+ x_offset -= 51
+ elif ease == 2:
+ x_offset += reviewButtons_width/4
+ x_offset += 55
+ elif 340 < reviewButtons_width < 361:
+ if ease == 1:
+ x_offset -= reviewButtons_width/4
+ x_offset -= 56
+ elif ease == 2:
+ x_offset += reviewButtons_width/4
+ x_offset += 60
+ elif 360 < reviewButtons_width < 381:
+ if ease == 1:
+ x_offset -= reviewButtons_width/4
+ x_offset -= 61
+ elif ease == 2:
+ x_offset += reviewButtons_width/4
+ x_offset += 65
+ elif 380 < reviewButtons_width < 401:
+ if ease == 1:
+ x_offset -= reviewButtons_width/4
+ x_offset -= 65
+ elif ease == 2:
+ x_offset += reviewButtons_width/4
+ x_offset += 70
+ elif button_count == 3:
+ if ease == 1:
+ x_offset -= button_width
+ x_offset += 62
+ elif ease == 2:
+ x_offset += 2
+ elif ease == 3:
+ x_offset += button_width
+ x_offset -= 59
+ elif button_count == 4:
+ if reviewButtons_width < 61:
+ if ease == 1:
+ x_offset -= reviewButtons_width/4
+ x_offset += 29
+ elif ease == 2:
+ x_offset -= reviewButtons_width/4
+ x_offset += 21
+ elif ease == 3:
+ x_offset += reviewButtons_width/4
+ x_offset -= 15
+ elif ease == 4:
+ x_offset += reviewButtons_width/4
+ x_offset -= 25
+ elif 60 < reviewButtons_width < 81:
+ if ease == 1:
+ x_offset -= reviewButtons_width/2
+ x_offset += 21
+ elif ease == 2:
+ x_offset -= reviewButtons_width/4
+ x_offset += 15
+ elif ease == 3:
+ x_offset += reviewButtons_width/4
+ x_offset -= 10
+ elif ease == 4:
+ x_offset += reviewButtons_width/2
+ x_offset -= 18
+ elif 80 < reviewButtons_width < 101:
+ if ease == 1:
+ x_offset -= reviewButtons_width/2
+ elif ease == 2:
+ x_offset -= reviewButtons_width/4
+ x_offset += 10
+ elif ease == 3:
+ x_offset += reviewButtons_width/4
+ x_offset -= 5
+ elif ease == 4:
+ x_offset += reviewButtons_width/2
+ x_offset += 2
+ elif 100 < reviewButtons_width < 121:
+ if ease == 1:
+ x_offset -= reviewButtons_width
+ x_offset += 37
+ elif ease == 2:
+ x_offset -= reviewButtons_width/3
+ x_offset += 15
+ elif ease == 3:
+ x_offset += reviewButtons_width/3
+ x_offset -= 9
+ elif ease == 4:
+ x_offset += reviewButtons_width
+ x_offset -= 33
+ elif 120 < reviewButtons_width < 141:
+ if ease == 1:
+ x_offset -= reviewButtons_width
+ x_offset += 26
+ elif ease == 2:
+ x_offset -= reviewButtons_width/3
+ x_offset += 13
+ elif ease == 3:
+ x_offset += reviewButtons_width/3
+ x_offset -= 6
+ elif ease == 4:
+ x_offset += reviewButtons_width
+ x_offset -= 23
+ elif 140 < reviewButtons_width < 161:
+ if ease == 1:
+ x_offset -= reviewButtons_width
+ x_offset += 16
+ elif ease == 2:
+ x_offset -= reviewButtons_width/3
+ x_offset += 7
+ elif ease == 3:
+ x_offset += reviewButtons_width/3
+ x_offset -= 3
+ elif ease == 4:
+ x_offset += reviewButtons_width
+ x_offset -= 13
+ elif 160 < reviewButtons_width < 181:
+ if ease == 1:
+ x_offset -= reviewButtons_width
+ elif ease == 2:
+ x_offset -= reviewButtons_width/3
+ x_offset += 5
+ elif ease == 3:
+ x_offset += reviewButtons_width/3
+ x_offset += 3
+ elif ease == 4:
+ x_offset += reviewButtons_width
+ x_offset -= 3
+ elif 180 < reviewButtons_width < 201:
+ if ease == 1:
+ x_offset -= reviewButtons_width
+ x_offset -= 5
+ elif ease == 2:
+ x_offset -= reviewButtons_width/3
+ x_offset += 4
+ elif ease == 3:
+ x_offset += reviewButtons_width/3
+ x_offset += 4
+ elif ease == 4:
+ x_offset += reviewButtons_width
+ x_offset += 7
+ elif 200 < reviewButtons_width < 221:
+ if ease == 1:
+ x_offset -= reviewButtons_width
+ x_offset -= 13
+ elif ease == 2:
+ x_offset -= reviewButtons_width/3
+ x_offset -= 1
+ elif ease == 3:
+ x_offset += reviewButtons_width/3
+ x_offset += 7
+ elif ease == 4:
+ x_offset += reviewButtons_width
+ x_offset += 17
+ elif 220 < reviewButtons_width < 241:
+ if ease == 1:
+ x_offset -= reviewButtons_width
+ x_offset -= 23
+ elif ease == 2:
+ x_offset -= reviewButtons_width/3
+ x_offset -= 5
+ elif ease == 3:
+ x_offset += reviewButtons_width/3
+ x_offset += 11
+ elif ease == 4:
+ x_offset += reviewButtons_width
+ x_offset += 27
+ elif 240 < reviewButtons_width < 261:
+ if ease == 1:
+ x_offset -= reviewButtons_width
+ x_offset -= 33
+ elif ease == 2:
+ x_offset -= reviewButtons_width/3
+ x_offset -= 8
+ elif ease == 3:
+ x_offset += reviewButtons_width/3
+ x_offset += 14
+ elif ease == 4:
+ x_offset += reviewButtons_width
+ x_offset += 37
+ elif 260 < reviewButtons_width < 281:
+ if ease == 1:
+ x_offset -= reviewButtons_width
+ x_offset -= 43
+ elif ease == 2:
+ x_offset -= reviewButtons_width/3
+ x_offset -= 11
+ elif ease == 3:
+ x_offset += reviewButtons_width/3
+ x_offset += 17
+ elif ease == 4:
+ x_offset += reviewButtons_width
+ x_offset += 47
+ elif 280 < reviewButtons_width < 301:
+ if ease == 1:
+ x_offset -= reviewButtons_width
+ x_offset -= 53
+ elif ease == 2:
+ x_offset -= reviewButtons_width/3
+ x_offset -= 14
+ elif ease == 3:
+ x_offset += reviewButtons_width/3
+ x_offset += 21
+ elif ease == 4:
+ x_offset += reviewButtons_width
+ x_offset += 57
+ elif 300 < reviewButtons_width < 321:
+ if ease == 1:
+ x_offset -= reviewButtons_width
+ x_offset -= 63
+ elif ease == 2:
+ x_offset -= reviewButtons_width/3
+ x_offset -= 17
+ elif ease == 3:
+ x_offset += reviewButtons_width/3
+ x_offset += 24
+ elif ease == 4:
+ x_offset += reviewButtons_width
+ x_offset += 67
+ elif 320 < reviewButtons_width < 341:
+ if ease == 1:
+ x_offset -= reviewButtons_width
+ x_offset -= 73
+ elif ease == 2:
+ x_offset -= reviewButtons_width/3
+ x_offset -= 22
+ elif ease == 3:
+ x_offset += reviewButtons_width/3
+ x_offset += 27
+ elif ease == 4:
+ x_offset += reviewButtons_width
+ x_offset += 77
+ elif 340 < reviewButtons_width < 361:
+ if ease == 1:
+ x_offset -= reviewButtons_width
+ x_offset -= 83
+ elif ease == 2:
+ x_offset -= reviewButtons_width/3
+ x_offset -= 25
+ elif ease == 3:
+ x_offset += reviewButtons_width/3
+ x_offset += 31
+ elif ease == 4:
+ x_offset += reviewButtons_width
+ x_offset += 87
+ elif 360 < reviewButtons_width < 381:
+ if ease == 1:
+ x_offset -= reviewButtons_width
+ x_offset -= 93
+ elif ease == 2:
+ x_offset -= reviewButtons_width/3
+ x_offset -= 28
+ elif ease == 3:
+ x_offset += reviewButtons_width/3
+ x_offset += 34
+ elif ease == 4:
+ x_offset += reviewButtons_width
+ x_offset += 97
+ elif 380 < reviewButtons_width < 401:
+ if ease == 1:
+ x_offset -= reviewButtons_width
+ x_offset -= 103
+ elif ease == 2:
+ x_offset -= reviewButtons_width/3
+ x_offset -= 31
+ elif ease == 3:
+ x_offset += reviewButtons_width/3
+ x_offset += 37
+ elif ease == 4:
+ x_offset += reviewButtons_width
+ x_offset += 107
+ if reviewButton_style in [4, 5, 6, 7]:
+ y_offset -= 1
+ if button_count == 2:
+ if ease == 1:
+ x_offset -= 4
+ elif ease == 2:
+ x_offset += 2
+ elif button_count == 3:
+ if ease == 1:
+ x_offset -= 1
+ elif ease == 2:
+ x_offset -= 1
+ elif button_count == 4:
+ if ease == 1:
+ x_offset -= 5
+ elif ease == 2:
+ x_offset -= 2
+ elif ease == 3:
+ x_offset += 1
+ elif ease == 4:
+ x_offset += 5
+ if reviewButton_style in [4, 5, 6, 7] and not custom_sizes:
+ button_height = 29
+ button_width = 72
+ y_offset += 2
+ if button_count == 2:
+ if ease == 1:
+ x_offset -= 3
+ elif ease == 2:
+ x_offset += 4
+ elif button_count == 3:
+ if ease == 1:
+ x_offset -= 10
+ elif ease == 2:
+ x_offset += 4
+ elif ease == 3:
+ x_offset += 10
+ elif button_count == 4:
+ if ease == 1:
+ x_offset -= 10
+ elif ease == 2:
+ x_offset += 4
+ elif ease == 3:
+ x_offset += 8
+ elif ease == 4:
+ x_offset += 15
+ if not info or info_position in ['middle left', 'middle right']:
+ x_offset -= 36
+ if info and info_position == "left":
+ x_offset += (info_width-64)/2
+ if info and info_position == "right":
+ x_offset -= 72
+ x_offset -= (info_width-64)/2
+ if skip and skip_position == "left":
+ x_offset += 36
+ x_offset += (skip_width-64)/2
+ if skip and skip_position == "right":
+ x_offset -= 36
+ x_offset -= (skip_width-64)/2
+ if undo and undo_position == "left":
+ x_offset += 52
+ x_offset += (undo_width-86)/2
+ if undo and undo_position == "right":
+ x_offset -= 52
+ x_offset -= (undo_width-86)/2
+ if reviewButton_style in [4, 5, 6, 7]:
+ if not info or info_position in ['middle left', 'middle right']:
+ x_offset -= 8
+ if info and info_position == "right":
+ x_offset -= 16
+ if undo and undo_position == "left":
+ x_offset +=8
+ if undo and undo_position == "right":
+ x_offset -= 17
+ x_offset -= 7
+ x_offset -= (more_width-72)/2
+ x_offset += (edit_width-72)/2
+ if tooltip_style == 1:
+ x_offset = tooltip_position[0]
+ y_offset = -tooltip_position[1]
+ button_width = 72
+ button_height = 29
+ background_color = button[1]
+
+ # if self.state == "answer": #// don't show tooltip if user hasn't pressed show answer button (you're really cool for a bug, so, I'm fucking keeping you :D)
+ showTooltip(text, background_color, tooltip_textColor, button_width, button_height, x_offset, y_offset, period=tooltip_timer)
+if anki_version > 2119:
+ _tooltipTimer: Optional[QTimer] = None
+ _tooltipLabel: Optional[QLabel] = None
+else:
+ _tooltipTimer = None
+ _tooltipLabel = None
+
+def showTooltip(text, background_color, tooltip_textColor, button_width, button_height, x_offset, y_offset, period=3000, parent=None):
+ global _tooltipTimer, _tooltipLabel
+
+ class CustomLabel(QLabel):
+ silentlyClose = True
+
+ def mousePressEvent(self, evt):
+ evt.accept()
+ self.hide()
+
+ closeTooltip()
+ aw = parent or aqt.mw.app.activeWindow() or aqt.mw
+ table = """
+ <table align=center>
+ <tr>
+ <td><div style='color: {0};'>{1}</div></td>
+ </tr>
+ </table>
+ """.format(tooltip_textColor, text)
+ lab = CustomLabel(table, aw)
+ lab.setFrameStyle(QFrame.Panel)
+ lab.setLineWidth(2)
+ lab.setFixedWidth(button_width)
+ lab.setFixedHeight(button_height)
+ lab.setWindowFlags(Qt.ToolTip)
+ p = QPalette()
+ p.setColor(QPalette.Window, QColor(background_color))
+ p.setColor(QPalette.WindowText, QColor("transparent"))
+ lab.setPalette(p)
+ if tooltip_style == 0:
+ x_coordinate = int(x_offset+(aw.width()-button_width)/2)
+ y_coordinate = y_offset+aw.height()
+ lab.move(aw.mapToGlobal(QPoint(x_coordinate, y_coordinate)))
+ else:
+ x_coordinate = min(x_offset, aw.width())
+ y_coordinate = min(y_offset, aw.height())
+ lab.move(aw.mapToGlobal(QPoint(x_coordinate, y_coordinate)))
+ lab.show()
+ _tooltipTimer = aqt.mw.progress.timer(period, closeTooltip, False, requiresCollection=False)
+ _tooltipLabel = lab
+
+
+def closeTooltip():
+ global _tooltipLabel, _tooltipTimer
+ if _tooltipLabel:
+ try:
+ _tooltipLabel.deleteLater()
+ except:
+ # already deleted as parent window closed
+ pass
+ _tooltipLabel = None
+ if _tooltipTimer:
+ _tooltipTimer.stop()
+ _tooltipTimer = None
+
+Reviewer._answerCard = wrap(Reviewer._answerCard, myTooltip, 'before')
diff --git a/.local/share/Anki2/addons21/advanced_review/__init__.py b/.local/share/Anki2/addons21/advanced_review/__init__.py new file mode 100644 index 0000000..b4f483f --- /dev/null +++ b/.local/share/Anki2/addons21/advanced_review/__init__.py @@ -0,0 +1,19 @@ +#// auth_ Mohamad Janati
+#// Copyright (c) 2019-2021 Mohamad Janati (freaking stupid, right? :|)
+
+from aqt import mw
+from anki import version
+from anki.collection import _Collection
+config = mw.addonManager.getConfig(__name__)
+button_colors = config[' Button Colors']
+reviewTooltip = config['Tooltip']
+
+anki_version = int(version.replace('.', ''))
+from . import Bottom_Bar
+from . import Settings
+from . import Deck_Overview
+if reviewTooltip:
+ from . import Tooltip
+
+if button_colors:
+ from . import Button_Colors
diff --git a/.local/share/Anki2/addons21/advanced_review/changelog.html b/.local/share/Anki2/addons21/advanced_review/changelog.html new file mode 100644 index 0000000..f364b34 --- /dev/null +++ b/.local/share/Anki2/addons21/advanced_review/changelog.html @@ -0,0 +1,462 @@ +
+<!DOCTYPE html>
+<html>
+<style media="screen">
+body {
+ background-color: #1D2027;
+ font-family: system-ui;
+ line-height: 25px;
+}
+
+h1 {
+ font-size: 25px;
+ font-weight: 15px;
+ color: white;
+ background-color: #575C66;
+ border-radius: 7px 7px 0px 0px;
+ padding: 5px;
+ padding-left: 15px;
+}
+
+li {
+ margin-bottom: 15px;
+ padding-right: 10px;
+}
+
+button {
+ padding: 10px;
+ border-radius: 6px;
+ border: none;
+ background-color: #575C66;
+ color: white;
+}
+
+.background {
+ background-color: #99A2B3;
+ margin: 15px;
+ padding-bottom: 10px;
+ border-radius: 7px;
+}
+</style>
+<body>
+
+ <div class="background">
+ <h1>2022/2/13</h1>
+ <ul>
+ <li>Added an option to change Card Info Sidebar default position</li>
+ <li>Fixed description button not showing on deck overview screen</li>
+ <li>Fixed a sidebar error caused by rescheduled cards</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2022/1/30</h1>
+ <ul>
+ <li>Changed load settings prompt message (forgot to do it yesterday)</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2022/1/29</h1>
+ <ul>
+ <li>Added a feature to change <font color=dodgerred>button text size</font> [go to "Button Sizes" and change button text size]</li>
+ <li>Added <font color=dodgerred>another style for button intervals</font> (now you can move button intervals inside the buttons) [to change button interval style go to "Styles" tab and change button interval style]</li>
+ <li>Added a feature to enable <font color=dodgerred>direct config edit</font> (serves no purpose for now, don't enable it unless you're told to | The idea is to quickly be able to add new features without having to add options in settings menu)</li>
+ <li>Added an option to <font color=dodgerred>backup your settings</font> (just press Backup Settings button and it will create a backup file of your settings - you can also share your settings and button stylings with other people by sharing the settings file)</li>
+ <li>Added an option to <font color=dodgerred>load settings</font> file (you can load settings and not go through settings and changing different settings and styles)</li>
+ <li>Fixed button tooltip bug in python 3.10 (Thanks to <a href="https://github.com/sdvcrx">@sdvcrx</a>)</li>
+ <li>Removed "Restore Defaults" button (with the new "Load Settings" function, having this extra button doesn't make sense)</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2021/9/22</h1>
+ <ul>
+ <li>Minor macOS bug fix (Hopefully -_-)</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2021/9/15</h1>
+ <ul>
+ <li>Minor bug fix</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2021/8/23</h1>
+ <ul>
+ <li>Detailed Deck Overview bug fix</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2021/8/22</h1>
+ <ul>
+ <li>Added a new method for skipping cards.<br>
+ <ul>
+ <li>This method is partially manual. The skipped cards won't show automatically unless you finish reviewing normal cards.</li>
+ <li>This method uses Anki's "Bury" function and buries skipped cards. The skipped cards will get unburied once you exit review screen or press press <button>Show Skipped</button> Button.</li>
+ <li>If you want for the skipped cards to show mid-review, you'll have to press <button>Show Skipped</button> Button or press the assigned shortcut (default shortcut is <kbd>Alt</kbd> + <kbd>C</kbd>).</li>
+ <li>If you use V3 sheduler, this is the only method that'll work for you and will be chosen by default.</li>
+ <li>If you use V2 scheduler you can use this method or the old method. You can choose the skip method in <code>Settings Menu -> Misc -> Skip Method</code></li>
+ <li>The old method is <font color=red>Next Card</font> and the new method is <font color=red>Bury</font>.</li>
+ <li>The new "Bury" method might be a bit slower, especially when you use the button. If you choose to use this method, I suggets using shortcuts for skipping cards.</li>
+ </ul>
+ <li>Adjusted Settings Menu height for better viewing on screens with lower resulotions</li>
+ <li>Moved changelog from main Settings Menu window to a separate window</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2021/8/4</h1>
+ <ul>
+ <li>Bug fix (now ARBb is compatible with Anki 2.1.45)</li>
+ <li>From now on, No update will be released for Anki versions older than 2.1.45</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2021/7/31</h1>
+ <ul>
+ <li>Bug Fix</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2021/7/30</h1>
+ <ul>
+ <li>Bug Fix</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2021/7/30</h1>
+ <ul>
+ <li>Added an option to set your custom text as button labels.<br>
+ replace again, hard, good, easy, etc. text with your custom text or emoji.<br>
+ To change button labels and use your own custom text, go to "Button label" tab in the settings.<br>
+ To the person asking me how to change button labels -_- you can use this from now on. No need to change the code.</li>
+ <li>Added an option to hide hard, good, easy buttons. (Requested)<br>
+ (no I haven't forgotten to put again in the list -_- you can't hide again button).<br>
+ To use this option, go to "Bottombar Buttons" and look for "Hide Buttons" part there.</li>
+ <li>Added an option to change the roundness of the buttons.<br>
+ To use this option, go to "Styles" tab and look for "Button Border Radius" there.</li>
+ <li><font color=red>Removed</font> pressed button stats from the add-on.<br>
+ For those who used it, I'll be publishing it as a separate add on named "Pressed Button Stats"</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/6/9</h1>
+ <ul>
+ <li>Added an option to turn off more overview stats.<br></li>
+ <font color=#004182>pressed button count STILL at 90%<br></font>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/12/6</h1>
+ <ul>
+ <li>Added another mode to overview stats (taken from "More Overview Stats 2.1")</li>
+ <li>Fixed conflict with speedfocus add-on (If you use speedfocus you need to enable "Speed focus" option in ARBb settings -> Misc)</li>
+ </ul>
+ </div><div class="background">
+ <h1>2020/6/9</h1>
+ <ul>
+ <li>Added an option to turn off more overview stats.<br></li>
+ <font color=#004182>pressed button count STILL at 90%<br></font>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/5/30</h1>
+ <ul>
+ <li>Changed tooltip behavior.<br>
+ Now it's size won't be as size of the buttons when it's position is fixed.<br></li>
+ <font color=#004182>pressed button count STILL at 90%<br></font>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/5/18</h1>
+ <ul>
+ <li>Minor code changes/improvements.<br></li>
+ <font color=#004182>pressed button count STILL at 90%<br></font>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/5/15</h1>
+ <ul>
+ <li>Now it designs review buttons that other add-ons add (like rememorize).<br>
+ it treats them like other bottombar button so their color and style<br>
+ will be like other bottombar buttons</li>
+ <li>you can style other bottombar buttons that are added by other add-on (like deferer button).<br>
+ you'll need to change their code a bit. if you want to style them leave a comment here or on github page.<br>
+ (the last picture is how the extra buttons the those add-on add look after styling them using this add-on)</li>
+ <li>finally a github page :\ <a href="https://github.com/noobj2/Anki-Advanced-Review-Bottombar">Here it is</a></li>
+ <li>Changed color of timer text in bottombar.<br>
+ now it uses the same color you have set for other bottombar buttons text color. (not a big deal though, right?)<br></li>
+ <font color=#004182>pressed button count STILL at 90%<br></font>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/5/9</h1>
+ <ul>
+ <li>Made neon and fill designs customizable. now you can change their colors using "Colors" tab.<br>
+ Enable custom colors by checking "Custom Review Button Colors" checkbox and <br>
+ changing again, hard, good and easy colors.<br>
+ as these designs don't have a separate hover color, changing hover colors won't<br>
+ change anything about these buttons</li>
+ <li>Made review bottombar buttons, deck overview buttons and main screen bottombar buttons customizable. <br>
+ you can change their border and text colors in "Colors" tab by changing "General Button" text and border colors.<br>
+ you can't chage text or background color for general buttons if their button style is set on default.<br>
+ to change general buttons style go to "Styles" tab and change "General Buttons Style".</li>
+ <li>Added an option to change show answer button border color based on card ease. <br>
+ you can enable than option in "Style" tab by changing "Show Answer Border Color Style" <br>
+ from "Fixed" to "Based on Card Ease". you cand change color for each ease range in "Colors" tab.<br>
+ - (honestly i think it's gonna be usless for most of you :/ it was just something that i needed).</li>
+ <li>+ Other settings menu and bottombar buttons changes and improvements.<br></li>
+ <font color=#004182>pressed button count STILL at 90%<br></font>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/4/28</h1>
+ <ul>
+ <li>Added an option to choose card type [learn, review, re-learn, cram] for button count stats</li>
+ <li>Added an option to manually change decks in button count stats<br></li>
+ <font color=#004182>at 90%<br></font>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/5/01</h1>
+ <ul>
+ <li>Added total time and time per card to information shown in pressed button stats<br></li>
+ <font color=#004182>at 85%<br></font>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/4/28</h1>
+ <ul>
+ <li>Added an option to choose card type [learn, review, re-learn, cram] for button count stats</li>
+ <li>Added an option to manually change decks in button count stats<br></li>
+ <font color=#004182>at 80%<br></font>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/4/27</h1>
+ <ul>
+ <li>Added an option to choose time period for button count stats</li>
+ <li>Added an option to change button count stats scope</li>
+ <li>Button count stats window improvements<br></li>
+ <font color=#004182>at 50%<br></font>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/4/26</h1>
+ <ul>
+ <li><font color=tomato>NEW FEATURE:</font> pressed button count + Percent<br>
+ <font color=red>NOTE:</font> it's work in progress and very basic<br>
+ the only reason i'm publishing it is that i want to hear you opinions on it and see what you need<br>
+ I want to hear your ideas about it, tell me what i can do to make it better<br>
+ you can Email me your ideas<br>
+ however, i think some of you may want to change the time period for this option<br>
+ to do that go to config -> Advanced review bottombar -> open add-on folder -> <br>
+ open Button_Count.py -> go to line 47 you'll see what you need there<br>
+ when you're on a deck, it shows pressed button stats for that deck, <br>
+ when you're in main window, it'll show overall stats<br></li>
+ <font color=#004182>at 15%<br></font>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/4/22</h1>
+ <ul>
+ <li>Made styling main screen and deck overview compatible with anki versions older than 2.1.19</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/4/21</h1>
+ <ul>
+ <li>Added an option to change main screen and deck overview buttons style<br>
+ (Their style will be as other bottombar buttons style)</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/4/20</h1>
+ <ul>
+ <li>Fixed tooltip bug (where it would show hard on tooltip when you<br>
+ pressed good if you were in a cutom study session )</li>
+ <li> Added card info sidebar auto open (opens sidebar automatically when you review a card)</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/4/18</h1>
+ <ul>
+ <li>Minor settings menu improvements</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/4/17</h1>
+ <ul>
+ <li>Fixed Neon 1 style bug</li>
+ <li>Addded correct percentage, fastest reveiw, slowest review, note ID and card ID options to card info sidebar</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/4/16</h1>
+ <ul>
+ <li>Added change button transition time option (for fill and neon designs only)</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/4/15</h1>
+ <ul>
+ <li>Added an option to change cursor type when you hover over bottombar buttons</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/4/14</h1>
+ <ul>
+ <li>Added answer tooltips</li>
+ <li>Adjusted tooltips for neon and fill designs</li>
+ <li>Adjusted tooltips for custom button sizes</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/4/13</h1>
+ <ul>
+ <li>Added a function to get shortcuts (Don't have to test keys that you want to set as shortcuts anymore,<br> if it's Anki's default shortcut for something, the add-on wont accept it)</li>
+ <li>Moved button design tooltip to another tab (noticed it was WAY too big for lower resulotions to be useful)</li>
+ <br><br><font color="red"># NOTE:</font> if you're updating from any version other than 2020/4/12 you might run into some problems trying to<br>
+ open settings menu if you can't open settings menu after update open add-on folder and delete meta.json file if<br>
+ that didn't help go to settings.py and put a # in front of the last line then go to tools -> add-ons and<br> press restore defaults on this addon's config page<br>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/4/12</h1>
+ <ul>
+ <li>Changed settings menu so it's easier to work with on lower resolutions (had to code it all over again)</li>
+ <li>Made picking colors completely automatic (no color code copy/paste, choose the color and it's set)</li>
+ <li>Added an option for you to choose settings menu's position</li>
+ <li>Made wide buttons compatible with no distractions add-on</li>
+ <br><br><font color="red"># NOTE:</font> After update you need to restore config to defaults in tools -> addons<br>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/4/8</h1>
+ <ul>
+ <li>settings menu bugs fixes</li>
+ <li>settings menu minor adjustments for smaller screens</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/4/7</h1>
+ <ul>
+ <li>settings menu improvements</li>
+ <li>added an option to color intervals</li>
+ <li>added an option to style other bottombar buttons</li>
+ <li>added 4 new button designs</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/4/6</h1>
+ <ul>
+ <li>minor settings menu improvements</li>
+ <li>card info sidebar improvements for old scheduler</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/4/5</h1>
+ <ul>
+ <li>minor settings menu improvements</li>
+ <li>added tooltips with pictures for different settings</li>
+ <li>fixed card info sidebar crash bug</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/4/4</h1>
+ <ul>
+ <li>added settings menu</li>
+ <li>minor settings menu adjustments</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/4/2</h1>
+ <ul>
+ <li>fix for wide buttons</li>
+ <li>fixed card info sidebar problem with beta versions of anki (2.1.23 and 2.1.24)</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/4/1</h1>
+ <ul>
+ <li>fixed issue with limiting card reviews in card info sidebar</li>
+ <li>added an option to change active button indicator from border to glow and change it's color</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/3/30</h1>
+ <ul>
+ <li>adjusted colors and gradients for background color change for light mode</li>
+ <li>added background shadow for review buttons (enable in config)</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/3/29</h1>
+ <ul>
+ <li>added undo button (enable in config)</li>
+ <li>fixed button color for old scheduler</li>
+ <li>removed conflict with customize keyboard shortcuts add-on</li>
+ <li>removed conflict with speed focus add-on (needs to be enabled in config)</li>
+ <li>removed conflict with slackers add-on</li>
+ <li>added an option to choose text color in review button background color change</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/3/26</h1>
+ <ul>
+ <li>added change button size option</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/3/25</h1>
+ <ul>
+ <li>added change skip and info button position option</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/3/20</h1>
+ <ul>
+ <li>fixed conflict with "replay button on card" add-on</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/3/7</h1>
+ <ul>
+ <li>adjusted the color for review buttons</li>
+ <li>added an option to choose the font for the text in card info side bar in config</li>
+ <li>added an option so you could limit the maximum number of previous reviews that are shown on sidebar for a card</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/3/6</h1>
+ <ul>
+ <li>made the info sidebar customizable, you can choose what you want to see on card info sidebar in config</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/3/4</h1>
+ <ul>
+ <li>fixed not showing review button colors on new in-app night mode</li>
+ <li>adjusted review button text colors for new in-app night mode</li>
+ <li>adjusted wide button widths</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/2/8</h1>
+ <ul>
+ <li>added an option for you to choose the shortcut key for skip and info buttons (in add-on config)</li>
+ <li>added an option to choose the sidebar theme (in add-on config)</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2020/1/2</h1>
+ <ul>
+ <li>fix for old scheduler</li>
+ </ul>
+ </div>
+ <div class="background">
+ <h1>2019/12/14</h1>
+ <ul>
+ <li>Initial Release</li>
+ </ul>
+ </div>
+
+</body>
+</html>
diff --git a/.local/share/Anki2/addons21/advanced_review/config.json b/.local/share/Anki2/addons21/advanced_review/config.json new file mode 100644 index 0000000..c93c211 --- /dev/null +++ b/.local/share/Anki2/addons21/advanced_review/config.json @@ -0,0 +1,115 @@ +{
+ " Button Colors": true,
+ " Speed Focus Add-on": false,
+ " Direct Config Edit": false,
+ " More Overview Stats": false,
+ " Settings Menu Place": 0,
+ " Skip Method": 0,
+ " Style Main Screen Buttons": true,
+ " Review_ Active Button Indicator": 1,
+ " Review_ Hover Effect": 1,
+ " Review_ Buttons Style": 0,
+ " Review_ Bottombar Buttons Style": 0,
+ " Review_ Custom Colors": false,
+ " Review_ Custom Review Button Text Color": false,
+ " Review_ Custom Active Indicator Color": false,
+ " Review_ Cursor Style": 0,
+ " Review_ Button Transition Time": 500,
+ " Review_ Button Border Radius": 5,
+ " Review_ Interval Style": 0,
+ "Button_ Info Button": true,
+ "Button_ Skip Button": true,
+ "Button_ Show Skipped Button": false,
+ "Button_ Undo Button": false,
+ "Button_ Hide Hard": false,
+ "Button_ Hide Good": false,
+ "Button_ Hide Easy": false,
+ "Button_ Custom Button Sizes": false,
+ "Button_ Shortcut_ Skip Button": "c",
+ "Button_ Shortcut_ Show Skipped Button": "Alt + c",
+ "Button_ Shortcut_ Info Button": "f4",
+ "Button_ Shortcut_ Undo Button": "x",
+ "Button_ Position_ Info Button": "left",
+ "Button_ Position_ Skip Button": "middle left",
+ "Button_ Position_ Show Skipped Button": "middle left",
+ "Button_ Position_ Undo Button": "middle right",
+ "Button_ Height_ All Bottombar Buttons": 40,
+ "Button_ Width_ Edit Button": 150,
+ "Button_ Width_ Show Answer Button": 150,
+ "Button_ Width_ Info Button": 150,
+ "Button_ Width_ Skip Button": 150,
+ "Button_ Width_ Show Skipped Button": 150,
+ "Button_ Width_ More Button": 150,
+ "Button_ Width_ Review Buttons": 150,
+ "Button_ Width_ Undo Button": 150,
+ "Button_ Text Size": 14,
+ "Button Label_ Study Now": "Study Now",
+ "Button Label_ Edit": "Edit",
+ "Button Label_ Show Answer": "Show Answer",
+ "Button Label_ More": "More",
+ "Button Label_ Info": "Info",
+ "Button Label_ Skip": "Skip",
+ "Button Label_ Show Skipped": "Show Skipped",
+ "Button Label_ Undo": "Undo Review",
+ "Button Label_ Again": "Again",
+ "Button Label_ Hard": "Hard",
+ "Button Label_ Good": "Good",
+ "Button Label_ Easy": "Easy",
+ "Card Info sidebar_ Number of previous cards to show": 0,
+ "Card Info sidebar_ Default Position": 0,
+ "Card Info sidebar_ theme": 0,
+ "Card Info sidebar_ Created": true,
+ "Card Info sidebar_ Edited": true,
+ "Card Info sidebar_ First Review": true,
+ "Card Info sidebar_ Latest Review": true,
+ "Card Info sidebar_ Due": true,
+ "Card Info sidebar_ Interval": true,
+ "Card Info sidebar_ Ease": true,
+ "Card Info sidebar_ Reviews": true,
+ "Card Info sidebar_ Lapses": true,
+ "Card Info Sidebar_ Correct Percent": true,
+ "Card Info Sidebar_ Fastest Review": true,
+ "Card Info Sidebar_ Slowest Review": true,
+ "Card Info sidebar_ Average Time": true,
+ "Card Info sidebar_ Total Time": true,
+ "Card Info sidebar_ Card Type": true,
+ "Card Info sidebar_ Note Type": true,
+ "Card Info sidebar_ Deck": true,
+ "Card Info sidebar_ Tags": true,
+ "Card Info Sidebar_ Note ID": false,
+ "Card Info Sidebar_ Card ID": false,
+ "Card Info sidebar_ Sort Field": true,
+ "Card Info sidebar_ Current Review Count": true,
+ "Card Info sidebar_ Font": "consolas",
+ "Card Info sidebar_ number of reviews to show for a card": 0,
+ "Card Info sidebar_ Auto Open": false,
+ "Card Info sidebar_ warning note": false,
+ "Color_ General Text Color": "#ffffff",
+ "Color_ Active Button Indicator": "#ffffff",
+ "Color_ Bottombar Button Text Color": "#FF1111",
+ "Color_ Bottombar Button Border Color": "#FF1111",
+ "Color_ Custom Bottombar Button Text Color": false,
+ "Color_ Custom Bottombar Button Border Color": false,
+ "Color_ Again": "#BA0C0C",
+ "Color_ Again on hover": "#FF1111",
+ "Color_ Hard": "#BF720F",
+ "Color_ Hard on hover": "#FF9814",
+ "Color_ Good": "#20A11C",
+ "Color_ Good on hover": "#33FF2D",
+ "Color_ Easy": "#188AB8",
+ "Color_ Easy on hover": "#21C0FF",
+ "Tooltip": true,
+ "Tooltip Timer": 500,
+ "Tooltip Text Color": "#000",
+ "Tooltip Style": 0,
+ "Tooltip Position": [100, -100],
+ "ShowAnswer_ Border Color Style": 0,
+ "ShowAnswer_ Ease1": 200,
+ "ShowAnswer_ Ease2": 250,
+ "ShowAnswer_ Ease3": 300,
+ "ShowAnswer_ Ease4": 350,
+ "ShowAnswer_ Ease1 Color": "#FF1111",
+ "ShowAnswer_ Ease2 Color": "#FF9814",
+ "ShowAnswer_ Ease3 Color": "#33FF2D",
+ "ShowAnswer_ Ease4 Color": "#21C0FF"
+}
diff --git a/.local/share/Anki2/addons21/advanced_review/images/activeIndicator_border.png b/.local/share/Anki2/addons21/advanced_review/images/activeIndicator_border.png Binary files differnew file mode 100644 index 0000000..725cc89 --- /dev/null +++ b/.local/share/Anki2/addons21/advanced_review/images/activeIndicator_border.png diff --git a/.local/share/Anki2/addons21/advanced_review/images/activeIndicator_glow.png b/.local/share/Anki2/addons21/advanced_review/images/activeIndicator_glow.png Binary files differnew file mode 100644 index 0000000..d67a3a0 --- /dev/null +++ b/.local/share/Anki2/addons21/advanced_review/images/activeIndicator_glow.png diff --git a/.local/share/Anki2/addons21/advanced_review/images/activeIndicator_none.png b/.local/share/Anki2/addons21/advanced_review/images/activeIndicator_none.png Binary files differnew file mode 100644 index 0000000..4205062 --- /dev/null +++ b/.local/share/Anki2/addons21/advanced_review/images/activeIndicator_none.png diff --git a/.local/share/Anki2/addons21/advanced_review/images/bottombarButtonsStyle_default.png b/.local/share/Anki2/addons21/advanced_review/images/bottombarButtonsStyle_default.png Binary files differnew file mode 100644 index 0000000..0bbf97d --- /dev/null +++ b/.local/share/Anki2/addons21/advanced_review/images/bottombarButtonsStyle_default.png diff --git a/.local/share/Anki2/addons21/advanced_review/images/bottombarButtonsStyle_fill1.png b/.local/share/Anki2/addons21/advanced_review/images/bottombarButtonsStyle_fill1.png Binary files differnew file mode 100644 index 0000000..4219ea8 --- /dev/null +++ b/.local/share/Anki2/addons21/advanced_review/images/bottombarButtonsStyle_fill1.png diff --git a/.local/share/Anki2/addons21/advanced_review/images/bottombarButtonsStyle_fill2.png b/.local/share/Anki2/addons21/advanced_review/images/bottombarButtonsStyle_fill2.png Binary files differnew file mode 100644 index 0000000..184147c --- /dev/null +++ b/.local/share/Anki2/addons21/advanced_review/images/bottombarButtonsStyle_fill2.png diff --git a/.local/share/Anki2/addons21/advanced_review/images/bottombarButtonsStyle_neon1.png b/.local/share/Anki2/addons21/advanced_review/images/bottombarButtonsStyle_neon1.png Binary files differnew file mode 100644 index 0000000..570d500 --- /dev/null +++ b/.local/share/Anki2/addons21/advanced_review/images/bottombarButtonsStyle_neon1.png diff --git a/.local/share/Anki2/addons21/advanced_review/images/bottombarButtonsStyle_neon2.png b/.local/share/Anki2/addons21/advanced_review/images/bottombarButtonsStyle_neon2.png Binary files differnew file mode 100644 index 0000000..07e8673 --- /dev/null +++ b/.local/share/Anki2/addons21/advanced_review/images/bottombarButtonsStyle_neon2.png diff --git a/.local/share/Anki2/addons21/advanced_review/images/buttonColors_off.png b/.local/share/Anki2/addons21/advanced_review/images/buttonColors_off.png Binary files differnew file mode 100644 index 0000000..a591dee --- /dev/null +++ b/.local/share/Anki2/addons21/advanced_review/images/buttonColors_off.png diff --git a/.local/share/Anki2/addons21/advanced_review/images/buttonColors_on.png b/.local/share/Anki2/addons21/advanced_review/images/buttonColors_on.png Binary files differnew file mode 100644 index 0000000..cd30844 --- /dev/null +++ b/.local/share/Anki2/addons21/advanced_review/images/buttonColors_on.png diff --git a/.local/share/Anki2/addons21/advanced_review/images/buttonSizes_off.png b/.local/share/Anki2/addons21/advanced_review/images/buttonSizes_off.png Binary files differnew file mode 100644 index 0000000..f1ea2d7 --- /dev/null +++ b/.local/share/Anki2/addons21/advanced_review/images/buttonSizes_off.png diff --git a/.local/share/Anki2/addons21/advanced_review/images/buttonSizes_off2.png b/.local/share/Anki2/addons21/advanced_review/images/buttonSizes_off2.png Binary files differnew file mode 100644 index 0000000..fa32d70 --- /dev/null +++ b/.local/share/Anki2/addons21/advanced_review/images/buttonSizes_off2.png diff --git a/.local/share/Anki2/addons21/advanced_review/images/buttonSizes_on.png b/.local/share/Anki2/addons21/advanced_review/images/buttonSizes_on.png Binary files differnew file mode 100644 index 0000000..a363381 --- /dev/null +++ b/.local/share/Anki2/addons21/advanced_review/images/buttonSizes_on.png diff --git a/.local/share/Anki2/addons21/advanced_review/images/buttonSizes_on2.png b/.local/share/Anki2/addons21/advanced_review/images/buttonSizes_on2.png Binary files differnew file mode 100644 index 0000000..757a4b8 --- /dev/null +++ b/.local/share/Anki2/addons21/advanced_review/images/buttonSizes_on2.png diff --git a/.local/share/Anki2/addons21/advanced_review/images/buttonStyle_defaultBackground.png b/.local/share/Anki2/addons21/advanced_review/images/buttonStyle_defaultBackground.png Binary files differnew file mode 100644 index 0000000..d4cb226 --- /dev/null +++ b/.local/share/Anki2/addons21/advanced_review/images/buttonStyle_defaultBackground.png diff --git a/.local/share/Anki2/addons21/advanced_review/images/buttonStyle_defaultText.png b/.local/share/Anki2/addons21/advanced_review/images/buttonStyle_defaultText.png Binary files differnew file mode 100644 index 0000000..725cc89 --- /dev/null +++ b/.local/share/Anki2/addons21/advanced_review/images/buttonStyle_defaultText.png diff --git a/.local/share/Anki2/addons21/advanced_review/images/buttonStyle_fill1.png b/.local/share/Anki2/addons21/advanced_review/images/buttonStyle_fill1.png Binary files differnew file mode 100644 index 0000000..39e48c9 --- /dev/null +++ b/.local/share/Anki2/addons21/advanced_review/images/buttonStyle_fill1.png diff --git a/.local/share/Anki2/addons21/advanced_review/images/buttonStyle_fill2.png b/.local/share/Anki2/addons21/advanced_review/images/buttonStyle_fill2.png Binary files differnew file mode 100644 index 0000000..ec598c8 --- /dev/null +++ b/.local/share/Anki2/addons21/advanced_review/images/buttonStyle_fill2.png diff --git a/.local/share/Anki2/addons21/advanced_review/images/buttonStyle_neon1.png b/.local/share/Anki2/addons21/advanced_review/images/buttonStyle_neon1.png Binary files differnew file mode 100644 index 0000000..61d4990 --- /dev/null +++ b/.local/share/Anki2/addons21/advanced_review/images/buttonStyle_neon1.png diff --git a/.local/share/Anki2/addons21/advanced_review/images/buttonStyle_neon2.png b/.local/share/Anki2/addons21/advanced_review/images/buttonStyle_neon2.png Binary files differnew file mode 100644 index 0000000..5d29166 --- /dev/null +++ b/.local/share/Anki2/addons21/advanced_review/images/buttonStyle_neon2.png diff --git a/.local/share/Anki2/addons21/advanced_review/images/buttonStyle_wideBackground.png b/.local/share/Anki2/addons21/advanced_review/images/buttonStyle_wideBackground.png Binary files differnew file mode 100644 index 0000000..887a428 --- /dev/null +++ b/.local/share/Anki2/addons21/advanced_review/images/buttonStyle_wideBackground.png diff --git a/.local/share/Anki2/addons21/advanced_review/images/buttonStyle_wideText.png b/.local/share/Anki2/addons21/advanced_review/images/buttonStyle_wideText.png Binary files differnew file mode 100644 index 0000000..d61643c --- /dev/null +++ b/.local/share/Anki2/addons21/advanced_review/images/buttonStyle_wideText.png diff --git a/.local/share/Anki2/addons21/advanced_review/images/changeMainScreenButtons.png b/.local/share/Anki2/addons21/advanced_review/images/changeMainScreenButtons.png Binary files differnew file mode 100644 index 0000000..fccf09b --- /dev/null +++ b/.local/share/Anki2/addons21/advanced_review/images/changeMainScreenButtons.png diff --git a/.local/share/Anki2/addons21/advanced_review/images/changeMainScreenButtons2.png b/.local/share/Anki2/addons21/advanced_review/images/changeMainScreenButtons2.png Binary files differnew file mode 100644 index 0000000..f1575e0 --- /dev/null +++ b/.local/share/Anki2/addons21/advanced_review/images/changeMainScreenButtons2.png diff --git a/.local/share/Anki2/addons21/advanced_review/images/changeMainScreenButtons3.png b/.local/share/Anki2/addons21/advanced_review/images/changeMainScreenButtons3.png Binary files differnew file mode 100644 index 0000000..290b7ad --- /dev/null +++ b/.local/share/Anki2/addons21/advanced_review/images/changeMainScreenButtons3.png diff --git a/.local/share/Anki2/addons21/advanced_review/images/changeStyle_text.png b/.local/share/Anki2/addons21/advanced_review/images/changeStyle_text.png Binary files differnew file mode 100644 index 0000000..725cc89 --- /dev/null +++ b/.local/share/Anki2/addons21/advanced_review/images/changeStyle_text.png diff --git a/.local/share/Anki2/addons21/advanced_review/images/coloredDues.png b/.local/share/Anki2/addons21/advanced_review/images/coloredDues.png Binary files differnew file mode 100644 index 0000000..43b00fc --- /dev/null +++ b/.local/share/Anki2/addons21/advanced_review/images/coloredDues.png diff --git a/.local/share/Anki2/addons21/advanced_review/images/hoverEffect_brighten.png b/.local/share/Anki2/addons21/advanced_review/images/hoverEffect_brighten.png Binary files differnew file mode 100644 index 0000000..20dd9e9 --- /dev/null +++ b/.local/share/Anki2/addons21/advanced_review/images/hoverEffect_brighten.png diff --git a/.local/share/Anki2/addons21/advanced_review/images/hoverEffect_glow.png b/.local/share/Anki2/addons21/advanced_review/images/hoverEffect_glow.png Binary files differnew file mode 100644 index 0000000..f7de78c --- /dev/null +++ b/.local/share/Anki2/addons21/advanced_review/images/hoverEffect_glow.png diff --git a/.local/share/Anki2/addons21/advanced_review/images/hoverEffect_glowBrighten.png b/.local/share/Anki2/addons21/advanced_review/images/hoverEffect_glowBrighten.png Binary files differnew file mode 100644 index 0000000..6344c76 --- /dev/null +++ b/.local/share/Anki2/addons21/advanced_review/images/hoverEffect_glowBrighten.png diff --git a/.local/share/Anki2/addons21/advanced_review/images/icon.png b/.local/share/Anki2/addons21/advanced_review/images/icon.png Binary files differnew file mode 100644 index 0000000..e0c0d2a --- /dev/null +++ b/.local/share/Anki2/addons21/advanced_review/images/icon.png diff --git a/.local/share/Anki2/addons21/advanced_review/manifest.json b/.local/share/Anki2/addons21/advanced_review/manifest.json new file mode 100644 index 0000000..8015fd3 --- /dev/null +++ b/.local/share/Anki2/addons21/advanced_review/manifest.json @@ -0,0 +1 @@ +{"package": "1136455830", "name": "Advanced Review Bottombar"}
diff --git a/.local/share/Anki2/addons21/advanced_review/meta.json b/.local/share/Anki2/addons21/advanced_review/meta.json new file mode 100644 index 0000000..2b26b01 --- /dev/null +++ b/.local/share/Anki2/addons21/advanced_review/meta.json @@ -0,0 +1 @@ +{"name": "Advanced Review Bottom Bar", "mod": 1644739078, "min_point_version": 45, "max_point_version": 50, "branch_index": 1, "disabled": false, "config": {" Button Colors": true, " Speed Focus Add-on": false, " Direct Config Edit": false, " More Overview Stats": 0, " Settings Menu Place": 0, " Skip Method": 0, " Style Main Screen Buttons": true, " Review_ Active Button Indicator": 1, " Review_ Buttons Style": 0, " Review_ Hover Effect": 3, " Review_ Custom Colors": false, " Review_ Custom Review Button Text Color": false, " Review_ Custom Active Indicator Color": false, " Review_ Bottombar Buttons Style": 0, " Review_ Cursor Style": 0, " Review_ Interval Style": 0, " Review_ Button Transition Time": 500, " Review_ Button Border Radius": 5, "Button_ Info Button": true, "Button_ Skip Button": true, "Button_ Show Skipped Button": false, "Button_ Undo Button": false, "Button_ Hide Hard": false, "Button_ Hide Good": false, "Button_ Hide Easy": false, "Button_ Custom Button Sizes": false, "Button_ Shortcut_ Skip Button": "c", "Button_ Shortcut_ Show Skipped Button": "Alt + c", "Button_ Shortcut_ Info Button": "f4", "Button_ Shortcut_ Undo Button": "x", "Button_ Position_ Info Button": "left", "Button_ Position_ Skip Button": "middle left", "Button_ Position_ Show Skipped Button": "middle left", "Button_ Position_ Undo Button": "middle right", "Button_ Text Size": 14, "Button_ Height_ All Bottombar Buttons": 40, "Button_ Width_ Edit Button": 150, "Button_ Width_ Show Answer Button": 150, "Button_ Width_ Info Button": 150, "Button_ Width_ Skip Button": 150, "Button_ Width_ Show Skipped Button": 150, "Button_ Width_ More Button": 150, "Button_ Width_ Review Buttons": 150, "Button_ Width_ Undo Button": 150, "Button Label_ Study Now": "Study Now", "Button Label_ Edit": "Edit", "Button Label_ Show Answer": "Show Answer", "Button Label_ More": "More", "Button Label_ Info": "Info", "Button Label_ Skip": "Skip", "Button Label_ Show Skipped": "Show Skipped", "Button Label_ Undo": "Undo Review", "Button Label_ Again": "Again", "Button Label_ Hard": "Hard", "Button Label_ Good": "Good", "Button Label_ Easy": "Easy", "Card Info sidebar_ Number of previous cards to show": 0, "Card Info sidebar_ Default Position": 0, "Card Info sidebar_ theme": 0, "Card Info sidebar_ Created": true, "Card Info sidebar_ Edited": true, "Card Info sidebar_ First Review": true, "Card Info sidebar_ Latest Review": true, "Card Info sidebar_ Due": true, "Card Info sidebar_ Interval": true, "Card Info sidebar_ Ease": true, "Card Info sidebar_ Reviews": true, "Card Info sidebar_ Lapses": true, "Card Info Sidebar_ Correct Percent": true, "Card Info Sidebar_ Fastest Review": true, "Card Info Sidebar_ Slowest Review": true, "Card Info sidebar_ Average Time": true, "Card Info sidebar_ Total Time": true, "Card Info sidebar_ Card Type": true, "Card Info sidebar_ Note Type": true, "Card Info sidebar_ Deck": true, "Card Info sidebar_ Tags": true, "Card Info Sidebar_ Note ID": false, "Card Info Sidebar_ Card ID": false, "Card Info sidebar_ Sort Field": true, "Card Info sidebar_ Current Review Count": true, "Card Info sidebar_ Font": "FreeMono", "Card Info sidebar_ number of reviews to show for a card": 0, "Card Info sidebar_ Auto Open": false, "Card Info sidebar_ warning note": false, "Color_ General Text Color": "#ffffff", "Color_ Active Button Indicator": "#ffffff", "Color_ Bottombar Button Text Color": "#FF1111", "Color_ Bottombar Button Border Color": "#FF1111", "Color_ Custom Bottombar Button Text Color": false, "Color_ Custom Bottombar Button Border Color": false, "Color_ Again": "#BA0C0C", "Color_ Again on hover": "#FF1111", "Color_ Hard": "#BF720F", "Color_ Hard on hover": "#FF9814", "Color_ Good": "#20A11C", "Color_ Good on hover": "#33FF2D", "Color_ Easy": "#188AB8", "Color_ Easy on hover": "#21C0FF", "Tooltip": true, "Tooltip Timer": 500, "Tooltip Text Color": "#000", "Tooltip Style": 0, "Tooltip Position": [100, -100], "ShowAnswer_ Border Color Style": 0, "ShowAnswer_ Ease1": 200, "ShowAnswer_ Ease2": 250, "ShowAnswer_ Ease3": 300, "ShowAnswer_ Ease4": 350, "ShowAnswer_ Ease1 Color": "#FF1111", "ShowAnswer_ Ease2 Color": "#FF9814", "ShowAnswer_ Ease3 Color": "#33FF2D", "ShowAnswer_ Ease4 Color": "#21C0FF"}}
\ No newline at end of file diff --git a/.local/share/Anki2/addons21/advanced_review/styles.py b/.local/share/Anki2/addons21/advanced_review/styles.py new file mode 100644 index 0000000..a11156f --- /dev/null +++ b/.local/share/Anki2/addons21/advanced_review/styles.py @@ -0,0 +1,1169 @@ +#// auth_ Mohamad Janati
+#// Copyright (c) 2019-2021 Mohamad Janati (freaking stupid, right? :|)
+
+from aqt import mw
+from anki import version
+anki_version = int(version.replace('.', ''))
+
+is_nightMode = False
+if anki_version > 2119:
+ from aqt.theme import theme_manager
+ is_nightMode = theme_manager.night_mode
+
+#// getting config information
+config = mw.addonManager.getConfig(__name__)
+
+#// getting colors that user defined in config
+again_color = config['Color_ Again']
+again_hover_color = config['Color_ Again on hover']
+hard_color = config['Color_ Hard']
+hard_hover_color = config['Color_ Hard on hover']
+good_color = config['Color_ Good']
+good_hover_color = config['Color_ Good on hover']
+easy_color = config['Color_ Easy']
+easy_hover_color = config['Color_ Easy on hover']
+button_style = config[' Review_ Buttons Style']
+custom_colors = config[' Review_ Custom Colors']
+hover_effect = config[' Review_ Hover Effect']
+active_indicator = config[' Review_ Active Button Indicator']
+bottombarButtons_style = config[' Review_ Bottombar Buttons Style']
+textColor_background = config['Color_ General Text Color']
+activeIndicator_color = config['Color_ Active Button Indicator']
+bottombarButtonText_color = config['Color_ Bottombar Button Text Color']
+bottombarButtonBorder_color = config['Color_ Bottombar Button Border Color']
+custom_bottombarButtonTextColor = config['Color_ Custom Bottombar Button Text Color']
+custom_bottombarButtonBorderColor = config['Color_ Custom Bottombar Button Border Color']
+custom_buttonSize = config ['Button_ Custom Button Sizes']
+buttons_height = config['Button_ Height_ All Bottombar Buttons']
+edit_width = config['Button_ Width_ Edit Button']
+# answer_width = config['Button_ Width_ Show Answer Button']
+info_width = config['Button_ Width_ Info Button']
+skip_width = config['Button_ Width_ Skip Button']
+text_size = config["Button_ Text Size"]
+showSkipped_width = config['Button_ Width_ Show Skipped Button']
+undo_width = config['Button_ Width_ Undo Button']
+more_width = config['Button_ Width_ More Button']
+custom_activeIndicatorColor = config[" Review_ Custom Active Indicator Color"]
+custom_reviewButtonTextColor = config[" Review_ Custom Review Button Text Color"]
+cursor_style = config[' Review_ Cursor Style']
+transition = "{}s".format(float(config[' Review_ Button Transition Time']/1000))
+border_radius = "{}px".format(config[' Review_ Button Border Radius'])
+
+######//////__BEGIN__ EXTRAS __BEGIN__//////######
+#// rplacing textColor with "default" if custom review button text color is disabled
+if custom_reviewButtonTextColor == False:
+ textColor_background = "default"
+
+#//replacing indicato color with "default" is custom active indicator color is disabled
+if custom_activeIndicatorColor == False:
+ activeIndicator_color = "default"
+
+#// Choosing cursor style
+if cursor_style == 0:
+ cursor = "normal"
+elif cursor_style == 1:
+ cursor = "pointer"
+
+#// Choosing button text color
+if not custom_reviewButtonTextColor:
+ if is_nightMode:
+ textColor = "#dedede"
+ if button_style > 3:
+ textColor = "black"
+ else:
+ textColor = "black"
+else:
+ textColor = textColor_background
+
+#// choosing color for general buttons
+if custom_bottombarButtonTextColor:
+ bottombarButton_textColor = bottombarButtonText_color
+else:
+ if is_nightMode:
+ bottombarButton_textColor = "#F0F0F0"
+ else:
+ bottombarButton_textColor = "#2F2F31"
+
+#// choosing border color for general buttons
+if custom_bottombarButtonBorderColor:
+ border_color = bottombarButtonBorder_color
+else:
+ if is_nightMode:
+ if bottombarButtons_style == 0:
+ border_color = ""
+ elif bottombarButtons_style in [1, 2]:
+ border_color = "#939399"
+ elif bottombarButtons_style == 3:
+ border_color = "#dedede"
+ elif bottombarButtons_style == 4:
+ border_color = "#BFBFC7"
+ else:
+ if bottombarButtons_style == 0:
+ border_color = ""
+ elif bottombarButtons_style == 1:
+ border_color = "#404040"
+ elif bottombarButtons_style == 2:
+ border_color = "#939399"
+ elif bottombarButtons_style in[3, 4]:
+ border_color = "#080808"
+
+#// seting colors for neon and fill desings when custom colors is disabled
+if not custom_colors:
+ again_color = "#FF1111"
+ hard_color = "#FF9814"
+ good_color = "#33FF2D"
+ easy_color = "#21C0FF"
+
+######//////__END__ EXTRAS __END__ //////######
+
+######//////__BEGIN__ GENERAL BUTTON DESIGNS __BEGIN__ //////######
+#// changing height and width of bottombar buttons based on button design or sizes that user has given
+#// bottomHTML_style makes us able to add classes ids or edit bottombar buttons altogether
+#// edit_stle, info_style, etc. make us able to define classes, ids or add styles to each button
+#// classes and ids should be defined in bottomHTML_style first
+if is_nightMode:
+ bottombar_neon1 = """<style>
+ #main {
+ padding: 5px 20px;
+ color: %(text)s;
+ border: 1px solid %(border_color)s;
+ border-radius: %(border_radius)s;
+ background: none;
+ cursor: %(cursor)s;
+ transition: %(transition)s;
+ }
+ #main:hover {
+ background: %(text)s;
+ border-radius: %(border_radius)s;
+ color: black;
+ box-shadow: 0 2px 10px 0 %(text)s;
+ }
+ </style>""" % dict (text=bottombarButton_textColor, border_color=border_color, border_radius=border_radius, cursor=cursor, transition=transition)
+ bottombar_neon2 = """<style>
+ #main {
+ padding: 5px 20px;
+ color: black;
+ border: 1px solid %(border_color)s;
+ border-radius: %(border_radius)s;
+ background: %(text)s;
+ cursor: %(cursor)s;
+ transition: %(transition)s;
+ box-shadow: 0 2px 10px 0 %(text)s;
+ }
+ #main:hover {
+ background: none;
+ border-radius: %(border_radius)s;
+ color: %(text)s;
+ box-shadow: none;
+ }
+ </style>""" % dict (text=bottombarButton_textColor, border_color=border_color, border_radius=border_radius, cursor=cursor, transition=transition)
+ bottombar_fill1 = """<style>
+ #main {
+ padding: 5px 20px;
+ border: 1px solid %(border_color)s;
+ background: none;
+ cursor: %(cursor)s;
+ transition: %(transition)s;
+ position: relative;
+ overflow: hidden;
+ border-radius: %(border_radius)s;
+ color: %(text)s;
+ }
+ #main:hover {
+ color: #2F2F31;
+ }
+ #main::before {
+ content: "";
+ position: absolute;
+ left: 0;
+ width: 100%%;
+ height: 0%%;
+ background: %(text)s;
+ z-index: -1;
+ transition: %(transition)s;
+ bottom: 0;
+ border-radius: 50%% 50%% 0 0;
+ }
+ #main:hover::before {
+ height: 180%%;
+ }
+ </style>""" % dict(text=bottombarButton_textColor, border_color=border_color, cursor=cursor, transition=transition, border_radius=border_radius)
+ bottombar_fill2 = """<style>
+ #main {
+ padding: 5px 20px;
+ border: 1px solid %(border_color)s;
+ background: none;
+ cursor: %(cursor)s;
+ transition: %(transition)s;
+ position: relative;
+ overflow: hidden;
+ border-radius: %(border_radius)s;
+ color: black;
+ }
+ #main:hover {
+ color: %(text)s;
+ }
+ #main::before {
+ content: "";
+ position: absolute;
+ left: 0;
+ width: 100%%;
+ height:180%%;
+ background: %(text)s;
+ z-index: -1;
+ transition: %(transition)s;
+ bottom: 0;
+ border-radius: 50%% 50%% 0 0;
+ }
+ #main:hover::before {
+ height: 0%%;
+ }
+ </style>""" % dict(text=bottombarButton_textColor, border_color=border_color, cursor=cursor, transition=transition, border_radius=border_radius)
+else:
+ bottombar_neon1 = """<style>
+ #main {
+ padding: 5px 20px;
+ color: %(text)s;
+ border: 1px solid %(border_color)s;
+ border-radius: %(border_radius)s;
+ background: none;
+ cursor: %(cursor)s;
+ transition: %(transition)s;
+ }
+ #main:hover {
+ background: %(text)s;
+ border-radius: %(border_radius)s;
+ color: #f0f0f0;
+ box-shadow: 0 2px 10px 0 %(text)s;
+ }
+ </style>""" % dict (text=bottombarButton_textColor, border_color=border_color, border_radius=border_radius, cursor=cursor, transition=transition)
+ bottombar_neon2 = """<style>
+ #main {
+ padding: 5px 20px;
+ color: white;
+ border: 1px solid %(border_color)s;
+ border-radius: %(border_radius)s;
+ background: %(text)s;
+ cursor: %(cursor)s;
+ transition: %(transition)s;
+ box-shadow: 0 2px 10px 0 %(text)s;
+ }
+ #main:hover {
+ border: 1px solid %(text)s;
+ background: none;
+ border-radius: %(border_radius)s;
+ color: %(text)s;
+ box-shadow: none;
+ }
+ </style>""" % dict (text=bottombarButton_textColor, border_color=border_color, border_radius=border_radius, cursor=cursor, transition=transition)
+ bottombar_fill1 = """<style>
+ #main {
+ padding: 5px 20px;
+ border: 1px solid %(border_color)s;
+ background: none;
+ cursor: %(cursor)s;
+ transition: %(transition)s;
+ position: relative;
+ overflow: hidden;
+ border-radius: %(border_radius)s;
+ color: %(text)s;
+ }
+ #main:hover {
+ color: white;
+ }
+ #main::before {
+ content: "";
+ position: absolute;
+ left: 0;
+ width: 100%%;
+ height: 0%%;
+ background: %(text)s;
+ z-index: -1;
+ transition: %(transition)s;
+ bottom: 0;
+ border-radius: 50%% 50%% 0 0;
+ }
+ #main:hover::before {
+ height: 180%%;
+ }
+ </style>""" % dict(text=bottombarButton_textColor, border_color=border_color, cursor=cursor, transition=transition, border_radius=border_radius)
+ bottombar_fill2 = """<style>
+ #main {
+ padding: 5px 20px;
+ border: 1px solid %(border_color)s;
+ background: none;
+ cursor: %(cursor)s;
+ transition: %(transition)s;
+ position: relative;
+ overflow: hidden;
+ border-radius: %(border_radius)s;
+ color: white;
+ }
+ #main:hover {
+ color: %(text)s;
+ }
+ #main::before {
+ content: "";
+ position: absolute;
+ left: 0;
+ width: 100%%;
+ height:180%%;
+ background: %(text)s;
+ z-index: -1;
+ transition: %(transition)s;
+ bottom: 0;
+ border-radius: 50%% 50%% 0 0;
+ }
+ #main:hover::before {
+ height: 0%%;
+ }
+ </style>""" % dict(text=bottombarButton_textColor, border_color=border_color, cursor=cursor, transition=transition, border_radius=border_radius)
+
+if custom_buttonSize:
+ if bottombarButtons_style == 0:
+ edit_style = 'style="height: {}px; width: {}px; font-size: {}px;"'.format(buttons_height, edit_width, text_size)
+ info_style = 'style="height: {}px; width: {}px; font-size: {}px;"'.format(buttons_height, info_width, text_size)
+ skip_style = 'style="height: {}px; width: {}px; font-size: {}px;"'.format(buttons_height, skip_width, text_size)
+ showSkipped_style = 'style="height: {}px; width: {}px; font-size: {}px;"'.format(buttons_height, showSkipped_width, text_size)
+ undo_style = 'style="height: {}px; width: {}px; font-size: {}px;"'.format(buttons_height, undo_width, text_size)
+ more_style = 'style="height: {}px; width: {}px; font-size: {}px;"'.format(buttons_height, more_width, text_size)
+ min_buttonSize = "<style> button {min-width: 5px; cursor: %s} </style>" % cursor
+ else:
+ edit_style = 'style="height: {}px; width: {}px; font-size: {}px;" id=main'.format(buttons_height, edit_width, text_size)
+ info_style = 'style="height: {}px; width: {}px; font-size: {}px;" id=main'.format(buttons_height, info_width, text_size)
+ skip_style = 'style="height: {}px; width: {}px; font-size: {}px;" id=main'.format(buttons_height, skip_width, text_size)
+ showSkipped_style = 'style="height: {}px; width: {}px; font-size: {}px;" id=main'.format(buttons_height, showSkipped_width, text_size)
+ undo_style = 'style="height: {}px; width: {}px; font-size: {}px;" id=main'.format(buttons_height, undo_width, text_size)
+ more_style = 'style="height: {}px; width: {}px; font-size: {}px;" id=main'.format(buttons_height, more_width, text_size)
+ min_buttonSize = "<style> button {min-width: 5px; cursor: %s} </style>" % cursor
+else:
+ if bottombarButtons_style == 0:
+ edit_style = ""
+ info_style = ""
+ skip_style = ""
+ showSkipped_style = ""
+ undo_style = ""
+ more_style = ""
+ min_buttonSize = "<style> button {cursor: %s} </style>" % cursor
+ else:
+ edit_style = "id=main"
+ info_style = "id=main"
+ skip_style = "id=main"
+ showSkipped_style = "id=main"
+ undo_style = "id=main"
+ more_style = "id=main"
+ min_buttonSize = "<style> button {cursor: %s} </style>" % cursor
+######//////__END__ GENERAL BUTTON DESIGNS __END__//////######
+
+######//////__BEGIN__ ACtIVE BUTTON INDOCATORS __BEGIN__//////######
+#// active button indicator
+if activeIndicator_color == "default":
+ activeIndicator_color = "#21BFFF"
+if active_indicator == 2:
+ active_extra = "border: solid 1px; border-color: {0}; box-shadow: 0 0 5px {0}, 0 0 20px {0}, 0 0 40px {0}".format(activeIndicator_color)
+else:
+ active_extra = "border: solid 1px; border-color: {}".format(activeIndicator_color)
+if active_indicator == 0 or button_style in [4, 5, 6, 7]:
+ active_extra = ""
+######//////__END__ ACtIVE BUTTON INDOCATORS __END__//////######
+
+
+######//////__BEGIN__ HOVER EFFECTS __BEGIN__//////######
+if not custom_colors:
+ again_shadow = "box-shadow: 0 0 5px #BA0C0C, 0 0 20px #BA0C0C, 0 0 40px #BA0C0C;"
+ hard_shadow = "box-shadow: 0 0 5px #BF720F, 0 0 20px #BF720F, 0 0 40px #BF720F;"
+ good_shadow = "box-shadow: 0 0 5px #20A11C, 0 0 20px #20A11C, 0 0 40px #20A11C;"
+ easy_shadow = "box-shadow: 0 0 5px #188AB8, 0 0 20px #188AB8, 0 0 40px #188AB8;"
+else:
+ again_shadow = "box-shadow: 0 0 5px {0}, 0 0 20px {0}, 0 0 40px {0};".format(again_hover_color)
+ hard_shadow = "box-shadow: 0 0 5px {0}, 0 0 20px {0}, 0 0 40px {0};".format(hard_hover_color)
+ good_shadow = "box-shadow: 0 0 5px {0}, 0 0 20px {0}, 0 0 40px {0};".format(good_hover_color)
+ easy_shadow = "box-shadow: 0 0 5px {0}, 0 0 20px {0}, 0 0 40px {0};".format(easy_hover_color)
+again_hover = ""
+hard_hover = ""
+good_hover = ""
+easy_hover = ""
+if not custom_colors:
+ if button_style == 0 or button_style == 2:
+ if hover_effect == 1:
+ again_hover = "color: #FF1111;"
+ hard_hover = "color: #FF9814;"
+ good_hover = "color: #33FF2D;"
+ easy_hover = "color: #21C0FF;"
+ elif hover_effect == 2:
+ again_hover = again_shadow
+ hard_hover = hard_shadow
+ good_hover = good_shadow
+ easy_hover = easy_shadow
+ elif hover_effect == 3:
+ again_hover = "color: #FF1111; {}".format(again_shadow)
+ hard_hover = "color: #FF9814; {}".format(hard_shadow)
+ good_hover = "color: #33FF2D; {}".format(good_shadow)
+ easy_hover = "color: #21C0FF; {}".format(easy_shadow)
+ elif button_style == 1 or button_style == 3:
+ if hover_effect == 1:
+ if is_nightMode:
+ again_hover = "background: #FF1111;"
+ hard_hover = "background: #FF9814;"
+ good_hover = "background: #33FF2D;"
+ easy_hover = "background: #21C0FF;"
+ else:
+ again_hover = "background: linear-gradient(0deg, #E02A1C, #FF3020);"
+ hard_hover = "background: linear-gradient(0deg, #E08C08, #FF9F09);"
+ good_hover = "background: linear-gradient(0deg, #22D414, #27F217);"
+ easy_hover = "background: linear-gradient(0deg, #11A7D1, #13C0F0);"
+ elif hover_effect == 2:
+ again_hover = again_shadow
+ hard_hover = hard_shadow
+ good_hover = good_shadow
+ easy_hover = easy_shadow
+ elif hover_effect == 3:
+ if is_nightMode:
+ again_hover = "background: #FF1111; {}".format(again_shadow)
+ hard_hover = "background: #FF9814; {}".format(hard_shadow)
+ good_hover = "background: #2CDB27; {}".format(good_shadow)
+ easy_hover = "background: #21C0FF; {}".format(easy_shadow)
+ else:
+ again_hover = "background: linear-gradient(0deg, #E02A1C, #FF3020); {}".format(again_shadow)
+ hard_hover = "background: linear-gradient(0deg, #E08C08, #FF9F09); {}".format(hard_shadow)
+ good_hover = "background: linear-gradient(0deg, #22D414, #27F217); {}".format(good_shadow)
+ easy_hover = "background: linear-gradient(0deg, #11A7D1, #13C0F0); {}".format(easy_shadow)
+else:
+ if button_style == 0 or button_style == 2:
+ if hover_effect == 1:
+ again_hover = "color: {};".format(again_hover_color)
+ hard_hover = "color: {};".format(hard_hover_color)
+ good_hover = "color: {};".format(good_hover_color)
+ easy_hover = "color: {};".format(easy_hover_color)
+ elif hover_effect == 2:
+ again_hover = again_shadow
+ hard_hover = hard_shadow
+ good_hover = good_shadow
+ easy_hover = easy_shadow
+ elif hover_effect == 3:
+ again_hover = "color: {}; {}".format(again_hover_color, again_shadow)
+ hard_hover = "color: {}; {}".format(hard_hover_color, hard_shadow)
+ good_hover = "color: {}; {}".format(good_hover_color, good_shadow)
+ easy_hover = "color: {}; {}".format(easy_hover_color, easy_shadow)
+ elif button_style == 1 or button_style == 3:
+ if hover_effect == 1:
+ again_hover = "background: {};".format(again_hover_color)
+ hard_hover = "background: {};".format(hard_hover_color)
+ good_hover = "background: {};".format(good_hover_color)
+ easy_hover = "background: {};".format(easy_hover_color)
+ elif hover_effect == 2:
+ again_hover = again_shadow
+ hard_hover = hard_shadow
+ good_hover = good_shadow
+ easy_hover = easy_shadow
+ elif hover_effect == 3:
+ again_hover = "background: {}; {}".format(again_hover_color, again_shadow)
+ hard_hover = "background: {}; {}".format(hard_hover_color, hard_shadow)
+ good_hover = "background: {}; {}".format(good_hover_color, good_shadow)
+ easy_hover = "background: {}; {}".format(easy_hover_color, easy_shadow)
+######//////__END__ HOVER EFFECTS __END__//////######
+
+######//////__BEGIN__ REVIEW BUTTON DESIGNS __BEGIN__//////######
+fill2 = """<style>
+ #again {
+ padding: 5px 20px;
+ border: 1px solid %(again_color)s;
+ background: none;
+ cursor: %(cursor)s;
+ transition: %(transition)s;
+ position: relative;
+ overflow: hidden;
+ border-radius: %(border_radius)s;
+ color: %(text)s;
+ }
+ #again:hover {
+ color: %(again_color)s;
+ }
+ #again::before {
+ content: "";
+ position: absolute;
+ left: 0;
+ width: 100%%;
+ height:180%%;
+ background: %(again_color)s;
+ z-index: -1;
+ transition: %(transition)s;
+ bottom: 0;
+ border-radius: 50%% 50%% 0 0;
+ }
+ #again:hover::before {
+ height: 0%%;
+ }
+ #hard {
+ padding: 5px 20px;
+ border: 1px solid %(hard_color)s;
+ background: none;
+ cursor: %(cursor)s;
+ transition: %(transition)s;
+ position: relative;
+ overflow: hidden;
+ border-radius: %(border_radius)s;
+ color: %(text)s;
+ }
+ #hard:hover {
+ color: %(hard_color)s;
+ }
+ #hard::before {
+ content: "";
+ position: absolute;
+ left: 0;
+ width: 100%%;
+ height:180%%;
+ background: %(hard_color)s;
+ z-index: -1;
+ transition: %(transition)s;
+ bottom: 0;
+ border-radius: 50%% 50%% 0 0;
+ }
+ #hard:hover::before {
+ height: 0%%;
+ }
+ #good {
+ padding: 5px 20px;
+ border: 1px solid %(good_color)s;
+ background: none;
+ cursor: %(cursor)s;
+ transition: %(transition)s;
+ position: relative;
+ overflow: hidden;
+ border-radius: %(border_radius)s;
+ color: %(text)s;
+ }
+ #good:hover {
+ color: %(good_color)s;
+ }
+ #good::before {
+ content: "";
+ position: absolute;
+ left: 0;
+ width: 100%%;
+ height:180%%;
+ background: %(good_color)s;
+ z-index: -1;
+ transition: %(transition)s;
+ bottom: 0;
+ border-radius: 50%% 50%% 0 0;
+ }
+ #good:hover::before {
+ height: 0%%;
+ }
+ #easy {
+ padding: 5px 20px;
+ border: 1px solid %(easy_color)s;
+ background: none;
+ cursor: %(cursor)s;
+ transition: %(transition)s;
+ position: relative;
+ overflow: hidden;
+ border-radius: %(border_radius)s;
+ color: %(text)s;
+ }
+ #easy:hover {
+ color: %(easy_color)s;
+ }
+ #easy::before {
+ content: "";
+ position: absolute;
+ left: 0;
+ width: 100%%;
+ height:180%%;
+ background: %(easy_color)s;
+ z-index: -1;
+ transition: %(transition)s;
+ bottom: 0;
+ border-radius: 50%% 50%% 0 0;
+ }
+ #easy:hover::before {
+ height: 0%%;
+ }
+ </style>""" % dict(text=textColor, again_color=again_color,hard_color=hard_color, good_color=good_color,
+ easy_color=easy_color,cursor=cursor, transition=transition, border_radius=border_radius)
+
+fill1 = """<style>
+ #again {
+ padding: 5px 20px;
+ border: 1px solid %(again_color)s;
+ background: none;
+ cursor: %(cursor)s;
+ transition: %(transition)s;
+ position: relative;
+ overflow: hidden;
+ border-radius: %(border_radius)s;
+ color: %(again_color)s;
+ }
+ #again:hover {
+ color: %(text)s;
+ }
+ #again::before {
+ content: "";
+ position: absolute;
+ left: 0;
+ width: 100%%;
+ height: 0%%;
+ background: %(again_color)s;
+ z-index: -1;
+ transition: %(transition)s;
+ bottom: 0;
+ border-radius: 50%% 50%% 0 0;
+ }
+ #again:hover::before {
+ height: 180%%;
+ }
+ #hard {
+ padding: 5px 20px;
+ border: 1px solid %(hard_color)s;
+ background: none;
+ cursor: %(cursor)s;
+ transition: %(transition)s;
+ position: relative;
+ overflow: hidden;
+ border-radius: %(border_radius)s;
+ color: %(hard_color)s;
+ }
+ #hard:hover {
+ color: %(text)s;
+ }
+ #hard::before {
+ content: "";
+ position: absolute;
+ left: 0;
+ width: 100%%;
+ height: 0%%;
+ background: %(hard_color)s;
+ z-index: -1;
+ transition: %(transition)s;
+ bottom: 0;
+ border-radius: 50%% 50%% 0 0;
+ }
+ #hard:hover::before {
+ height: 180%%;
+ }
+ #good {
+ padding: 5px 20px;
+ border: 1px solid %(good_color)s;
+ background: none;
+ cursor: %(cursor)s;
+ transition: %(transition)s;
+ position: relative;
+ overflow: hidden;
+ border-radius: %(border_radius)s;
+ color: %(good_color)s;
+ }
+ #good:hover {
+ color: %(text)s;
+ }
+ #good::before {
+ content: "";
+ position: absolute;
+ left: 0;
+ width: 100%%;
+ height: 0%%;
+ background: %(good_color)s;
+ z-index: -1;
+ transition: %(transition)s;
+ bottom: 0;
+ border-radius: 50%% 50%% 0 0;
+ }
+ #good:hover::before {
+ height: 180%%;
+ }
+ #easy {
+ padding: 5px 20px;
+ border: 1px solid %(easy_color)s;
+ background: none;
+ cursor: %(cursor)s;
+ transition: %(transition)s;
+ position: relative;
+ overflow: hidden;
+ border-radius: %(border_radius)s;
+ color: %(easy_color)s;
+ }
+ #easy:hover {
+ color: %(text)s;
+ }
+ #easy::before {
+ content: "";
+ position: absolute;
+ left: 0;
+ width: 100%%;
+ height: 0%%;
+ background: %(easy_color)s;
+ z-index: -1;
+ transition: %(transition)s;
+ bottom: 0;
+ border-radius: 50%% 50%% 0 0;
+ }
+ #easy:hover::before {
+ height: 180%%;
+ }
+ </style>""" % dict(text=textColor, again_color=again_color, hard_color=hard_color, good_color=good_color,
+ easy_color=easy_color, cursor=cursor, transition=transition, border_radius=border_radius)
+neon2 = """<style>
+ #again {
+ color: %(text)s;
+ border: 1px solid %(again_color)s;
+ padding: 5px 20px;
+ border-radius: %(border_radius)s;
+ background: %(again_color)s;
+ cursor: %(cursor)s;
+ transition: %(transition)s;
+ box-shadow: 0 2px 20px 0 %(again_color)s inset, 0 2px 20px 0 %(again_color)s;
+ }
+ #again:hover {
+ background: none;
+ border-radius: %(border_radius)s;
+ color: %(again_color)s;
+ box-shadow: none;
+ }
+ #hard {
+ color: %(text)s;
+ border: 1px solid %(hard_color)s;
+ padding: 5px 20px;
+ border-radius: %(border_radius)s;
+ background: %(hard_color)s;
+ cursor: %(cursor)s;
+ transition: %(transition)s;
+ box-shadow: 0 2px 20px 0 %(hard_color)s inset, 0 2px 20px 0 %(hard_color)s;
+ }
+ #hard:hover {
+ background: none;
+ border-radius: %(border_radius)s;
+ color: %(hard_color)s;
+ box-shadow: none;
+ }
+ #good {
+ color: %(text)s;
+ border: 1px solid %(good_color)s;
+ padding: 5px 20px;
+ border-radius: %(border_radius)s;
+ background: %(good_color)s;
+ cursor: %(cursor)s;
+ transition: %(transition)s;
+ box-shadow: 0 2px 20px 0 %(good_color)s inset, 0 2px 20px 0 %(good_color)s;
+ }
+ #good:hover {
+ background: none;
+ border-radius: %(border_radius)s;
+ color: %(good_color)s;
+ box-shadow: none;
+ }
+ #easy {
+ color: %(text)s;
+ border: 1px solid %(easy_color)s;
+ padding: 5px 20px;
+ border-radius: %(border_radius)s;
+ background: %(easy_color)s;
+ cursor: %(cursor)s;
+ transition: %(transition)s;
+ box-shadow: 0 2px 20px 0 %(easy_color)s inset, 0 2px 20px 0 %(easy_color)s;
+ }
+ #easy:hover {
+ background: none;
+ border-radius: %(border_radius)s;
+ color: %(easy_color)s;
+ box-shadow: none;
+ }
+ </style>""" % dict(text=textColor, again_color=again_color,hard_color=hard_color,
+ good_color=good_color, easy_color=easy_color, cursor=cursor, transition=transition, border_radius=border_radius)
+neon1 = """<style>
+ #again {
+ background: none;
+ color: %(again_color)s;
+ border: 1px solid %(again_color)s;
+ padding: 5px 20px;
+ border-radius: %(border_radius)s;
+ cursor: %(cursor)s;
+ transition: %(transition)s;
+ box-shadow: none;
+ }
+ #again:hover {
+ background: %(again_color)s;
+ color: %(text)s;
+ border-radius: %(border_radius)s;
+ box-shadow: 0 2px 20px 0 %(again_color)s inset, 0 2px 20px 0 %(again_color)s;
+ }
+ #hard {
+ background: none;
+ color: %(hard_color)s;
+ border: 1px solid %(hard_color)s;
+ padding: 5px 20px;
+ border-radius: %(border_radius)s;
+ cursor: %(cursor)s;
+ transition: %(transition)s;
+ box-shadow: none;
+ }
+ #hard:hover {
+ background: %(hard_color)s;
+ color: %(text)s;
+ border-radius: %(border_radius)s;
+ box-shadow: 0 2px 20px 0 %(hard_color)s inset, 0 2px 20px 0 %(hard_color)s;
+ }
+ #good {
+ background: none;
+ color: %(good_color)s;
+ border: 1px solid %(good_color)s;
+ padding: 5px 20px;
+ border-radius: %(border_radius)s;
+ cursor: %(cursor)s;
+ transition: %(transition)s;
+ box-shadow: none;
+ }
+ #good:hover {
+ background: %(good_color)s;
+ color: %(text)s;
+ border-radius: %(border_radius)s;
+ box-shadow: 0 2px 20px 0 %(good_color)s inset, 0 2px 20px 0 %(good_color)s;
+ }
+ #easy {
+ background: none;
+ color: %(easy_color)s;
+ border: 1px solid %(easy_color)s;
+ padding: 5px 20px;
+ border-radius: %(border_radius)s;
+ cursor: %(cursor)s;
+ transition: %(transition)s;
+ box-shadow: none;
+ }
+ #easy:hover {
+ background: %(easy_color)s;
+ color: %(text)s;
+ border-radius: %(border_radius)s;
+ box-shadow: 0 2px 20px 0 %(easy_color)s inset, 0 2px 20px 0 %(easy_color)s;
+ }
+ </style>""" % dict(text=textColor, again_color=again_color, hard_color=hard_color,
+ good_color=good_color, easy_color=easy_color, cursor=cursor, transition=transition, border_radius=border_radius)
+#// styling for text color change method
+text_color = """<style>
+ #again {
+ color: #BA0C0C;
+ cursor: %(cursor)s;
+ }
+ #again:hover{
+ %(again_hover)s
+ }
+ #hard {
+ color: #BF720F;
+ cursor: %(cursor)s;
+ }
+ #hard:hover{
+ %(hard_hover)s
+ }
+ #good {
+ color: #20A11C;
+ cursor: %(cursor)s;
+ }
+ #good:hover{
+ %(good_hover)s
+ }
+ #easy {
+ color: #188AB8;
+ cursor: %(cursor)s;
+ }
+ #easy:hover{
+ %(easy_hover)s
+ }
+ </style>""" % dict(cursor=cursor, again_hover=again_hover, hard_hover=hard_hover, good_hover=good_hover, easy_hover=easy_hover)
+
+#// styling for background color change method
+if is_nightMode: #// style if anki version is 2.1.20 and night mode is enabled
+ background_color = """<style>
+ #again, #hard, #good, #easy {
+ border: hidden;
+ color: %(text)s;
+ cursor: %(cursor)s;
+ text-shadow: none;
+ }
+ #again {
+ background: #BA0C0C;
+ }
+ #again:hover{
+ color: %(text)s;
+ %(again_hover)s
+ }
+ #hard {
+ background: #BF720F;
+ }
+ #hard:hover{
+ color: %(text)s;
+ %(hard_hover)s
+ }
+ #good {
+ background: #20A11C;
+ }
+ #good:hover{
+ color: %(text)s;
+ %(good_hover)s
+ }
+ #easy {
+ background: #188AB8;
+ }
+ #easy:hover{
+ color: %(text)s;
+ %(easy_hover)s
+ }
+ </style>""" % dict (text=textColor, cursor=cursor, again_hover=again_hover, hard_hover=hard_hover, good_hover=good_hover, easy_hover=easy_hover)
+else: #// style if anki version is older than 2.1.20 or night mode is disabled
+ background_color = """<style>
+ #again, #hard, #good, #easy {
+ border: hidden;
+ color: %(text)s;
+ cursor: %(cursor)s;
+ text-shadow: none;
+ border-radius: 3px;
+ }
+ #again {
+ background: linear-gradient(0deg, #B52217, #CF271A);
+ }
+ #again:hover{
+ color: %(text)s;
+ %(again_hover)s
+ }
+ #hard {
+ background: linear-gradient(0deg, #BF7707, #D98708);
+ }
+ #hard:hover{
+ color: %(text)s;
+ %(hard_hover)s
+ }
+ #good {
+ background: linear-gradient(0deg, #1CA810, #20C212);
+ }
+ #good:hover{
+ color: %(text)s;
+ %(good_hover)s
+ }
+ #easy {
+ background: linear-gradient(0deg, #0F95BA, #11AAD4);
+ }
+ #easy:hover{
+ color: %(text)s;
+ %(easy_hover)s
+ }
+ </style>""" % dict (text=textColor, cursor=cursor, again_hover=again_hover, hard_hover=hard_hover, good_hover=good_hover, easy_hover=easy_hover)
+
+#// styling for custom text color change method
+custom_text = """<style>
+ #again {
+ color: %(again_color)s;
+ cursor: %(cursor)s;
+ }
+ #again:hover{
+ %(again_hover)s
+ }
+ #hard {
+ color: %(hard_color)s;
+ cursor: %(cursor)s;
+ }
+ #hard:hover{
+ %(hard_hover)s
+ }
+ #good {
+ color: %(good_color)s;
+ cursor: %(cursor)s;
+ }
+ #good:hover{
+ %(good_hover)s
+ }
+ #easy {
+ color: %(easy_color)s;
+ cursor: %(cursor)s;
+ }
+ #easy:hover{
+ %(easy_hover)s
+ }
+ </style>""" % dict(again_color=again_color, hard_color=hard_color, good_color=good_color, easy_color=easy_color, easy_hover_color=easy_hover_color,
+ again_hover=again_hover, hard_hover=hard_hover, good_hover=good_hover, easy_hover=easy_hover, cursor=cursor)
+
+#// styling for custom background color change method
+if is_nightMode: #// style if anki version is 2.1.20 and above and night mode is endabled
+ custom_background = """<style>
+ #again, #hard, #good, #easy {
+ border: hidden;
+ color: %(text)s;
+ cursor: %(cursor)s;
+ text-shadow: none;
+ }
+ #again {
+ background: %(again_color)s;
+ }
+ #again:hover{
+ color: %(text)s;
+ %(again_hover)s
+ }
+ #hard {
+ background: %(hard_color)s;
+ }
+ #hard:hover{
+ color: %(text)s;
+ %(hard_hover)s
+ }
+ #good {
+ background: %(good_color)s;
+ }
+ #good:hover{
+ color: %(text)s;
+ %(good_hover)s
+ }
+ #easy {
+ background: %(easy_color)s;
+ }
+ #easy:hover{
+ color: %(text)s;
+ %(easy_hover)s
+ }
+ </style>""" % dict (text=textColor, cursor=cursor, again_color=again_color, hard_color=hard_color, good_color=good_color, easy_color=easy_color,
+ again_hover=again_hover, hard_hover=hard_hover, good_hover=good_hover, easy_hover=easy_hover)
+else: #// style if anki is in day/light mode or anki version is older than 2.1.20
+ custom_background = """<style>
+ #again, #hard, #good, #easy {
+ border: hidden;
+ color: %(text)s;
+ cursor: %(cursor)s;
+ text-shadow: none;
+ border-radius: 3px
+ }
+ #again {
+ background: %(again_color)s;
+ }
+ #again:hover{
+ color: %(text)s;
+ background: %(again_hover_color)s;
+ %(again_hover)s
+ }
+ #hard {
+ background: %(hard_color)s;
+ }
+ #hard:hover{
+ color: %(text)s;
+ background: %(hard_hover_color)s;
+ %(hard_hover)s
+ }
+ #good {
+ background: %(good_color)s;
+ }
+ #good:hover{
+ color: %(text)s;
+ background: %(good_hover_color)s;
+ %(good_hover)s
+ }
+ #easy {
+ background: %(easy_color)s;
+ }
+ #easy:hover{
+ color: %(text)s;
+ background: %(easy_hover_color)s;
+ %(easy_hover)s
+ }
+ </style>""" % dict (text=textColor, cursor=cursor, again_color=again_color, again_hover_color=again_hover_color, hard_color=hard_color, hard_hover_color=hard_hover_color, good_color=good_color, good_hover_color=good_hover_color, easy_color=easy_color, easy_hover_color=easy_hover_color,
+ again_hover=again_hover, hard_hover=hard_hover, good_hover=good_hover, easy_hover=easy_hover)
+
+button_styles = """<style>
+ .wide {
+ min-width: 70px;
+ width: 98%;
+ margin: 6px 0px;
+ padding: 3px 0px;
+ font-size: 12px
+ }
+ .mybuttons {
+ padding: 3px;
+ font-size: 12px;
+ }
+ </style>"""
+######//////__END__ REVIEW BUTTONS DESIGNS __END__//////######
+
+######//////__BEGIN__ CARD INFO STYLING __BEGIN//////######
+light = """
+body {
+ margin: 8px;
+ background-color: #dedede;
+}
+p {
+ margin-top: 1em;
+ margin-bottom: 1em;
+}
+h1,h2,h3,h4{
+ display: block;
+ font-size: 1.17em;
+ margin-top: 1em;
+ margin-bottom: 1em;
+ margin-left: 0;
+ margin-right: 0;
+ font-weight: bold;
+}
+a:link {
+ text-decoration: none;
+ color:black;
+}
+
+/* anki.stats
+
+colYoung = "#7c7"
+colMature = "#070"
+colCum = "rgba(0,0,0,0.9)"
+colLearn = "#00F"
+colRelearn = "#c00"
+colCram = "#ff0"
+colIvl = "#077"
+colHour = "#ccc"
+colTime = "#770"
+colUnseen = "#000"
+colSusp = "#ff0"
+*/
+
+.color_learn {color: #00F;}
+.color_mature {color: #070;}
+.color_relearn {color: #c00;}
+.color_type3 {color: #3c9690;}
+.color_rest {color: #000;}
+.color_ease3 {color: navy;}
+.color_ease4 {color: darkgreen;}
+
+.critical_color_lower {color: red;}
+.critical_color_upper {color: blue;}
+"""
+dark = """
+body {
+ background-color: #272828;
+ color: #d7d7d7;
+ margin: 8px;
+}
+p {
+ background-color: #272828;
+ color: #d7d7d7;
+ margin-top: 1em;
+ margin-bottom: 1em;
+}
+h1,h2,h3,h4{
+ background-color: #272828;
+ color: #d7d7d7;
+ display: block;
+ font-size: 1.17em;
+ margin-top: 1em;
+ margin-bottom: 1em;
+ margin-left: 0;
+ margin-right: 0;
+ font-weight: bold;
+}
+a:link {
+ text-decoration: none;
+ color:#d7d7d7;
+}
+
+.color_learn {color: #01b3f5;}
+.color_mature {color: #070;}
+.color_relearn {color: #ff0000;}
+.color_type3 {color: #57d9d1;}
+.color_rest {color: #dedede;}
+.color_ease3 {color: #00aaff;}
+.color_ease4 {color: #55ff00;}
+
+.critical_color_lower {color: #ff0000;}
+.critical_color_upper {color: #00aaff;}
+"""
+######//////__END__ CARD INFO __END__//////######
diff --git a/.local/share/Anki2/addons21/advanced_review/user_files/Default Settings.json b/.local/share/Anki2/addons21/advanced_review/user_files/Default Settings.json new file mode 100644 index 0000000..933622c --- /dev/null +++ b/.local/share/Anki2/addons21/advanced_review/user_files/Default Settings.json @@ -0,0 +1,115 @@ +{
+ " Button Colors": true,
+ " Speed Focus Add-on": false,
+ " Direct Config Edit": false,
+ " More Overview Stats": 1,
+ " Settings Menu Place": 0,
+ " Skip Method": 0,
+ " Style Main Screen Buttons": true,
+ " Review_ Active Button Indicator": 1,
+ " Review_ Hover Effect": 1,
+ " Review_ Buttons Style": 0,
+ " Review_ Bottombar Buttons Style": 0,
+ " Review_ Custom Colors": false,
+ " Review_ Custom Review Button Text Color": false,
+ " Review_ Custom Active Indicator Color": false,
+ " Review_ Cursor Style": 0,
+ " Review_ Button Transition Time": 500,
+ " Review_ Button Border Radius": 5,
+ " Review_ Interval Style": 0,
+ "Button_ Info Button": true,
+ "Button_ Skip Button": true,
+ "Button_ Show Skipped Button": false,
+ "Button_ Undo Button": false,
+ "Button_ Hide Hard": false,
+ "Button_ Hide Good": false,
+ "Button_ Hide Again": false,
+ "Button_ Custom Button Sizes": false,
+ "Button_ Shortcut_ Skip Button": "c",
+ "Button_ Shortcut_ Show Skipped Button": "Alt + c",
+ "Button_ Shortcut_ Info Button": "f4",
+ "Button_ Shortcut_ Undo Button": "x",
+ "Button_ Position_ Info Button": "left",
+ "Button_ Position_ Skip Button": "middle left",
+ "Button_ Position_ Show Skipped Button": "middle left",
+ "Button_ Position_ Undo Button": "middle right",
+ "Button_ Text Size": 14,
+ "Button_ Height_ All Bottombar Buttons": 30,
+ "Button_ Width_ Edit Button": 60,
+ "Button_ Width_ Show Answer Button": 100,
+ "Button_ Width_ Info Button": 60,
+ "Button_ Width_ Skip Button": 60,
+ "Button_ Width_ Show Skipped Button": 60,
+ "Button_ Width_ More Button": 60,
+ "Button_ Width_ Review Buttons": 60,
+ "Button_ Width_ Undo Button": 60,
+ "Button Label_ Study Now": "Study Now",
+ "Button Label_ Edit": "Edit",
+ "Button Label_ Show Answer": "Show Answer",
+ "Button Label_ More": "More",
+ "Button Label_ Info": "Info",
+ "Button Label_ Skip": "Skip",
+ "Button Label_ Show Skipped": "Show Skipped",
+ "Button Label_ Undo": "Undo Review",
+ "Button Label_ Again": "Again",
+ "Button Label_ Hard": "Hard",
+ "Button Label_ Good": "Good",
+ "Button Label_ Easy": "Easy",
+ "Card Info sidebar_ Number of previous cards to show": 0,
+ "Card Info sidebar_ Default Position": 0,
+ "Card Info sidebar_ theme": 0,
+ "Card Info sidebar_ Created": true,
+ "Card Info sidebar_ Edited": true,
+ "Card Info sidebar_ First Review": true,
+ "Card Info sidebar_ Latest Review": true,
+ "Card Info sidebar_ Due": true,
+ "Card Info sidebar_ Interval": true,
+ "Card Info sidebar_ Ease": true,
+ "Card Info sidebar_ Reviews": true,
+ "Card Info sidebar_ Lapses": true,
+ "Card Info Sidebar_ Correct Percent": true,
+ "Card Info Sidebar_ Fastest Review": true,
+ "Card Info Sidebar_ Slowest Review": true,
+ "Card Info sidebar_ Average Time": true,
+ "Card Info sidebar_ Total Time": true,
+ "Card Info sidebar_ Card Type": true,
+ "Card Info sidebar_ Note Type": true,
+ "Card Info sidebar_ Deck": true,
+ "Card Info sidebar_ Tags": true,
+ "Card Info Sidebar_ Note ID": false,
+ "Card Info Sidebar_ Card ID": false,
+ "Card Info sidebar_ Sort Field": true,
+ "Card Info sidebar_ Current Review Count": true,
+ "Card Info sidebar_ Font": "consolas",
+ "Card Info sidebar_ number of reviews to show for a card": 0,
+ "Card Info sidebar_ Auto Open": false,
+ "Card Info sidebar_ warning note": false,
+ "Color_ General Text Color": "#ffffff",
+ "Color_ Active Button Indicator": "#ffffff",
+ "Color_ Bottombar Button Text Color": "#FF1111",
+ "Color_ Bottombar Button Border Color": "#FF1111",
+ "Color_ Custom Bottombar Button Text Color": false,
+ "Color_ Custom Bottombar Button Border Color": false,
+ "Color_ Again": "#BA0C0C",
+ "Color_ Again on hover": "#FF1111",
+ "Color_ Hard": "#BF720F",
+ "Color_ Hard on hover": "#FF9814",
+ "Color_ Good": "#20A11C",
+ "Color_ Good on hover": "#33FF2D",
+ "Color_ Easy": "#188AB8",
+ "Color_ Easy on hover": "#21C0FF",
+ "Tooltip": true,
+ "Tooltip Timer": 500,
+ "Tooltip Text Color": "#000",
+ "Tooltip Style": 0,
+ "Tooltip Position": [100, -100],
+ "ShowAnswer_ Border Color Style": 0,
+ "ShowAnswer_ Ease1": 200,
+ "ShowAnswer_ Ease2": 250,
+ "ShowAnswer_ Ease3": 300,
+ "ShowAnswer_ Ease4": 350,
+ "ShowAnswer_ Ease1 Color": "#FF1111",
+ "ShowAnswer_ Ease2 Color": "#FF9814",
+ "ShowAnswer_ Ease3 Color": "#33FF2D",
+ "ShowAnswer_ Ease4 Color": "#21C0FF"
+ }
diff --git a/.local/share/Anki2/addons21/advanced_review/user_files/Large and Colorful Buttons.json b/.local/share/Anki2/addons21/advanced_review/user_files/Large and Colorful Buttons.json new file mode 100644 index 0000000..4a3f4a5 --- /dev/null +++ b/.local/share/Anki2/addons21/advanced_review/user_files/Large and Colorful Buttons.json @@ -0,0 +1,118 @@ +{
+ " Button Colors": true,
+ " Speed Focus Add-on": false,
+ " Direct Config Edit": false,
+ " More Overview Stats": 1,
+ " Settings Menu Place": 0,
+ " Skip Method": 0,
+ " Style Main Screen Buttons": true,
+ " Review_ Active Button Indicator": 1,
+ " Review_ Buttons Style": 1,
+ " Review_ Hover Effect": 1,
+ " Review_ Custom Colors": true,
+ " Review_ Custom Review Button Text Color": true,
+ " Review_ Custom Active Indicator Color": true,
+ " Review_ Bottombar Buttons Style": 0,
+ " Review_ Cursor Style": 1,
+ " Review_ Interval Style": 1,
+ " Review_ Button Transition Time": 300,
+ " Review_ Button Border Radius": 6,
+ "Button_ Info Button": true,
+ "Button_ Skip Button": false,
+ "Button_ Show Skipped Button": false,
+ "Button_ Undo Button": false,
+ "Button_ Hide Hard": false,
+ "Button_ Hide Good": false,
+ "Button_ Hide Easy": false,
+ "Button_ Custom Button Sizes": true,
+ "Button_ Shortcut_ Skip Button": "c",
+ "Button_ Shortcut_ Show Skipped Button": "Alt + c",
+ "Button_ Shortcut_ Info Button": "f4",
+ "Button_ Shortcut_ Undo Button": "x",
+ "Button_ Position_ Info Button": "left",
+ "Button_ Position_ Skip Button": "middle left",
+ "Button_ Position_ Show Skipped Button": "middle left",
+ "Button_ Position_ Undo Button": "middle right",
+ "Button_ Text Size": 14,
+ "Button_ Height_ All Bottombar Buttons": 64,
+ "Button_ Width_ Edit Button": 60,
+ "Button_ Width_ Show Answer Button": 360,
+ "Button_ Width_ Info Button": 60,
+ "Button_ Width_ Skip Button": 60,
+ "Button_ Width_ Show Skipped Button": 60,
+ "Button_ Width_ More Button": 60,
+ "Button_ Width_ Review Buttons": 96,
+ "Button_ Width_ Undo Button": 60,
+ "Button Label_ Study Now": "Study Now",
+ "Button Label_ Edit": "Edit",
+ "Button Label_ Show Answer": "Show Answer",
+ "Button Label_ More": "More",
+ "Button Label_ Info": "Info",
+ "Button Label_ Skip": "Skip",
+ "Button Label_ Show Skipped": "Show Skipped",
+ "Button Label_ Undo": "Undo Review",
+ "Button Label_ Again": "Again",
+ "Button Label_ Hard": "Hard",
+ "Button Label_ Good": "Good",
+ "Button Label_ Easy": "Easy",
+ "Card Info sidebar_ Number of previous cards to show": 3,
+ "Card Info sidebar_ Default Position": 0,
+ "Card Info sidebar_ theme": 0,
+ "Card Info sidebar_ Created": true,
+ "Card Info sidebar_ Edited": true,
+ "Card Info sidebar_ First Review": true,
+ "Card Info sidebar_ Latest Review": true,
+ "Card Info sidebar_ Due": true,
+ "Card Info sidebar_ Interval": true,
+ "Card Info sidebar_ Ease": true,
+ "Card Info sidebar_ Reviews": true,
+ "Card Info sidebar_ Lapses": true,
+ "Card Info Sidebar_ Correct Percent": true,
+ "Card Info Sidebar_ Fastest Review": true,
+ "Card Info Sidebar_ Slowest Review": true,
+ "Card Info sidebar_ Average Time": true,
+ "Card Info sidebar_ Total Time": true,
+ "Card Info sidebar_ Card Type": false,
+ "Card Info sidebar_ Note Type": false,
+ "Card Info sidebar_ Deck": false,
+ "Card Info sidebar_ Tags": true,
+ "Card Info Sidebar_ Note ID": false,
+ "Card Info Sidebar_ Card ID": false,
+ "Card Info sidebar_ Sort Field": false,
+ "Card Info sidebar_ Current Review Count": true,
+ "Card Info sidebar_ Font": "Consolas",
+ "Card Info sidebar_ number of reviews to show for a card": 5,
+ "Card Info sidebar_ Auto Open": false,
+ "Card Info sidebar_ warning note": false,
+ "Color_ General Text Color": "#ffffff",
+ "Color_ Active Button Indicator": "#155182",
+ "Color_ Bottombar Button Text Color": "#FF1111",
+ "Color_ Bottombar Button Border Color": "#FF1111",
+ "Color_ Custom Bottombar Button Text Color": false,
+ "Color_ Custom Bottombar Button Border Color": false,
+ "Color_ Again": "#5a5a5a",
+ "Color_ Again on hover": "#d32f2f",
+ "Color_ Hard": "#5a5a5a",
+ "Color_ Hard on hover": "#455a64",
+ "Color_ Good": "#5a5a5a",
+ "Color_ Good on hover": "#4caf50",
+ "Color_ Easy": "#5a5a5a",
+ "Color_ Easy on hover": "#03a9f4",
+ "Tooltip": true,
+ "Tooltip Timer": 300,
+ "Tooltip Text Color": "#000",
+ "Tooltip Style": 0,
+ "Tooltip Position": [
+ 100,
+ -100
+ ],
+ "ShowAnswer_ Border Color Style": 0,
+ "ShowAnswer_ Ease1": 200,
+ "ShowAnswer_ Ease2": 250,
+ "ShowAnswer_ Ease3": 300,
+ "ShowAnswer_ Ease4": 350,
+ "ShowAnswer_ Ease1 Color": "#FF1111",
+ "ShowAnswer_ Ease2 Color": "#FF9814",
+ "ShowAnswer_ Ease3 Color": "#33FF2D",
+ "ShowAnswer_ Ease4 Color": "#21C0FF"
+}
diff --git a/.local/share/Anki2/addons21/advanced_review/user_files/My Settings (Large Buttons).json b/.local/share/Anki2/addons21/advanced_review/user_files/My Settings (Large Buttons).json new file mode 100644 index 0000000..4ae525d --- /dev/null +++ b/.local/share/Anki2/addons21/advanced_review/user_files/My Settings (Large Buttons).json @@ -0,0 +1,118 @@ +{
+ " Button Colors": true,
+ " Speed Focus Add-on": false,
+ " Direct Config Edit": false,
+ " More Overview Stats": 1,
+ " Settings Menu Place": 0,
+ " Skip Method": 0,
+ " Style Main Screen Buttons": true,
+ " Review_ Active Button Indicator": 1,
+ " Review_ Buttons Style": 6,
+ " Review_ Hover Effect": 1,
+ " Review_ Custom Colors": false,
+ " Review_ Custom Review Button Text Color": false,
+ " Review_ Custom Active Indicator Color": false,
+ " Review_ Bottombar Buttons Style": 3,
+ " Review_ Cursor Style": 0,
+ " Review_ Interval Style": 1,
+ " Review_ Button Transition Time": 300,
+ " Review_ Button Border Radius": 5,
+ "Button_ Info Button": true,
+ "Button_ Skip Button": false,
+ "Button_ Show Skipped Button": false,
+ "Button_ Undo Button": false,
+ "Button_ Hide Hard": false,
+ "Button_ Hide Good": false,
+ "Button_ Hide Easy": false,
+ "Button_ Custom Button Sizes": true,
+ "Button_ Shortcut_ Skip Button": "c",
+ "Button_ Shortcut_ Show Skipped Button": "Alt + c",
+ "Button_ Shortcut_ Info Button": "f4",
+ "Button_ Shortcut_ Undo Button": "x",
+ "Button_ Position_ Info Button": "left",
+ "Button_ Position_ Skip Button": "middle left",
+ "Button_ Position_ Show Skipped Button": "middle left",
+ "Button_ Position_ Undo Button": "middle right",
+ "Button_ Text Size": 18,
+ "Button_ Height_ All Bottombar Buttons": 50,
+ "Button_ Width_ Edit Button": 60,
+ "Button_ Width_ Show Answer Button": 250,
+ "Button_ Width_ Info Button": 60,
+ "Button_ Width_ Skip Button": 60,
+ "Button_ Width_ Show Skipped Button": 60,
+ "Button_ Width_ More Button": 75,
+ "Button_ Width_ Review Buttons": 100,
+ "Button_ Width_ Undo Button": 60,
+ "Button Label_ Study Now": "Study Now",
+ "Button Label_ Edit": "Edit",
+ "Button Label_ Show Answer": "Show Answer",
+ "Button Label_ More": "More",
+ "Button Label_ Info": "Info",
+ "Button Label_ Skip": "Skip",
+ "Button Label_ Show Skipped": "Show Skipped",
+ "Button Label_ Undo": "Undo Review",
+ "Button Label_ Again": "Again",
+ "Button Label_ Hard": "Hard",
+ "Button Label_ Good": "Good",
+ "Button Label_ Easy": "Easy",
+ "Card Info sidebar_ Number of previous cards to show": 3,
+ "Card Info sidebar_ Default Position": 0,
+ "Card Info sidebar_ theme": 0,
+ "Card Info sidebar_ Created": true,
+ "Card Info sidebar_ Edited": true,
+ "Card Info sidebar_ First Review": true,
+ "Card Info sidebar_ Latest Review": true,
+ "Card Info sidebar_ Due": true,
+ "Card Info sidebar_ Interval": true,
+ "Card Info sidebar_ Ease": true,
+ "Card Info sidebar_ Reviews": true,
+ "Card Info sidebar_ Lapses": true,
+ "Card Info Sidebar_ Correct Percent": true,
+ "Card Info Sidebar_ Fastest Review": true,
+ "Card Info Sidebar_ Slowest Review": true,
+ "Card Info sidebar_ Average Time": true,
+ "Card Info sidebar_ Total Time": true,
+ "Card Info sidebar_ Card Type": false,
+ "Card Info sidebar_ Note Type": false,
+ "Card Info sidebar_ Deck": false,
+ "Card Info sidebar_ Tags": true,
+ "Card Info Sidebar_ Note ID": false,
+ "Card Info Sidebar_ Card ID": false,
+ "Card Info sidebar_ Sort Field": false,
+ "Card Info sidebar_ Current Review Count": true,
+ "Card Info sidebar_ Font": "Consolas",
+ "Card Info sidebar_ number of reviews to show for a card": 5,
+ "Card Info sidebar_ Auto Open": false,
+ "Card Info sidebar_ warning note": false,
+ "Color_ General Text Color": "#ffffff",
+ "Color_ Active Button Indicator": "#ffffff",
+ "Color_ Bottombar Button Text Color": "#FF1111",
+ "Color_ Bottombar Button Border Color": "#FF1111",
+ "Color_ Custom Bottombar Button Text Color": true,
+ "Color_ Custom Bottombar Button Border Color": true,
+ "Color_ Again": "#BA0C0C",
+ "Color_ Again on hover": "#FF1111",
+ "Color_ Hard": "#BF720F",
+ "Color_ Hard on hover": "#FF9814",
+ "Color_ Good": "#20A11C",
+ "Color_ Good on hover": "#33FF2D",
+ "Color_ Easy": "#188AB8",
+ "Color_ Easy on hover": "#21C0FF",
+ "Tooltip": true,
+ "Tooltip Timer": 300,
+ "Tooltip Text Color": "#000",
+ "Tooltip Style": 0,
+ "Tooltip Position": [
+ 100,
+ -100
+ ],
+ "ShowAnswer_ Border Color Style": 1,
+ "ShowAnswer_ Ease1": 200,
+ "ShowAnswer_ Ease2": 250,
+ "ShowAnswer_ Ease3": 300,
+ "ShowAnswer_ Ease4": 350,
+ "ShowAnswer_ Ease1 Color": "#FF1111",
+ "ShowAnswer_ Ease2 Color": "#FF9814",
+ "ShowAnswer_ Ease3 Color": "#33FF2D",
+ "ShowAnswer_ Ease4 Color": "#21C0FF"
+}
diff --git a/.local/share/Anki2/addons21/advanced_review/user_files/My Settings.json b/.local/share/Anki2/addons21/advanced_review/user_files/My Settings.json new file mode 100644 index 0000000..7153a93 --- /dev/null +++ b/.local/share/Anki2/addons21/advanced_review/user_files/My Settings.json @@ -0,0 +1,118 @@ +{
+ " Button Colors": true,
+ " Speed Focus Add-on": false,
+ " Direct Config Edit": false,
+ " More Overview Stats": 1,
+ " Settings Menu Place": 0,
+ " Skip Method": 0,
+ " Style Main Screen Buttons": true,
+ " Review_ Active Button Indicator": 1,
+ " Review_ Buttons Style": 6,
+ " Review_ Hover Effect": 1,
+ " Review_ Custom Colors": false,
+ " Review_ Custom Review Button Text Color": false,
+ " Review_ Custom Active Indicator Color": false,
+ " Review_ Bottombar Buttons Style": 3,
+ " Review_ Cursor Style": 0,
+ " Review_ Interval Style": 1,
+ " Review_ Button Transition Time": 300,
+ " Review_ Button Border Radius": 5,
+ "Button_ Info Button": true,
+ "Button_ Skip Button": false,
+ "Button_ Show Skipped Button": false,
+ "Button_ Undo Button": false,
+ "Button_ Hide Hard": false,
+ "Button_ Hide Good": false,
+ "Button_ Hide Easy": false,
+ "Button_ Custom Button Sizes": false,
+ "Button_ Shortcut_ Skip Button": "c",
+ "Button_ Shortcut_ Show Skipped Button": "Alt + c",
+ "Button_ Shortcut_ Info Button": "f4",
+ "Button_ Shortcut_ Undo Button": "x",
+ "Button_ Position_ Info Button": "left",
+ "Button_ Position_ Skip Button": "middle left",
+ "Button_ Position_ Show Skipped Button": "middle left",
+ "Button_ Position_ Undo Button": "middle right",
+ "Button_ Text Size": 21,
+ "Button_ Height_ All Bottombar Buttons": 51,
+ "Button_ Width_ Edit Button": 60,
+ "Button_ Width_ Show Answer Button": 100,
+ "Button_ Width_ Info Button": 60,
+ "Button_ Width_ Skip Button": 60,
+ "Button_ Width_ Show Skipped Button": 60,
+ "Button_ Width_ More Button": 60,
+ "Button_ Width_ Review Buttons": 200,
+ "Button_ Width_ Undo Button": 60,
+ "Button Label_ Study Now": "Study Now",
+ "Button Label_ Edit": "Edit",
+ "Button Label_ Show Answer": "Show Answer",
+ "Button Label_ More": "More",
+ "Button Label_ Info": "Info",
+ "Button Label_ Skip": "Skip",
+ "Button Label_ Show Skipped": "Show Skipped",
+ "Button Label_ Undo": "Undo Review",
+ "Button Label_ Again": "Again",
+ "Button Label_ Hard": "Hard",
+ "Button Label_ Good": "Good",
+ "Button Label_ Easy": "Easy",
+ "Card Info sidebar_ Number of previous cards to show": 3,
+ "Card Info sidebar_ Default Position": 0,
+ "Card Info sidebar_ theme": 0,
+ "Card Info sidebar_ Created": true,
+ "Card Info sidebar_ Edited": true,
+ "Card Info sidebar_ First Review": true,
+ "Card Info sidebar_ Latest Review": true,
+ "Card Info sidebar_ Due": true,
+ "Card Info sidebar_ Interval": true,
+ "Card Info sidebar_ Ease": true,
+ "Card Info sidebar_ Reviews": true,
+ "Card Info sidebar_ Lapses": true,
+ "Card Info Sidebar_ Correct Percent": true,
+ "Card Info Sidebar_ Fastest Review": true,
+ "Card Info Sidebar_ Slowest Review": true,
+ "Card Info sidebar_ Average Time": true,
+ "Card Info sidebar_ Total Time": true,
+ "Card Info sidebar_ Card Type": false,
+ "Card Info sidebar_ Note Type": false,
+ "Card Info sidebar_ Deck": false,
+ "Card Info sidebar_ Tags": true,
+ "Card Info Sidebar_ Note ID": false,
+ "Card Info Sidebar_ Card ID": false,
+ "Card Info sidebar_ Sort Field": false,
+ "Card Info sidebar_ Current Review Count": true,
+ "Card Info sidebar_ Font": "Consolas",
+ "Card Info sidebar_ number of reviews to show for a card": 5,
+ "Card Info sidebar_ Auto Open": false,
+ "Card Info sidebar_ warning note": false,
+ "Color_ General Text Color": "#ffffff",
+ "Color_ Active Button Indicator": "#ffffff",
+ "Color_ Bottombar Button Text Color": "#FF1111",
+ "Color_ Bottombar Button Border Color": "#FF1111",
+ "Color_ Custom Bottombar Button Text Color": true,
+ "Color_ Custom Bottombar Button Border Color": true,
+ "Color_ Again": "#BA0C0C",
+ "Color_ Again on hover": "#FF1111",
+ "Color_ Hard": "#BF720F",
+ "Color_ Hard on hover": "#FF9814",
+ "Color_ Good": "#20A11C",
+ "Color_ Good on hover": "#33FF2D",
+ "Color_ Easy": "#188AB8",
+ "Color_ Easy on hover": "#21C0FF",
+ "Tooltip": true,
+ "Tooltip Timer": 300,
+ "Tooltip Text Color": "#000",
+ "Tooltip Style": 0,
+ "Tooltip Position": [
+ 100,
+ -100
+ ],
+ "ShowAnswer_ Border Color Style": 1,
+ "ShowAnswer_ Ease1": 200,
+ "ShowAnswer_ Ease2": 250,
+ "ShowAnswer_ Ease3": 300,
+ "ShowAnswer_ Ease4": 350,
+ "ShowAnswer_ Ease1 Color": "#FF1111",
+ "ShowAnswer_ Ease2 Color": "#FF9814",
+ "ShowAnswer_ Ease3 Color": "#33FF2D",
+ "ShowAnswer_ Ease4 Color": "#21C0FF"
+}
diff --git a/.local/share/Anki2/addons21/anki_reworked/__init__.py b/.local/share/Anki2/addons21/anki_reworked/__init__.py new file mode 100644 index 0000000..81b2627 --- /dev/null +++ b/.local/share/Anki2/addons21/anki_reworked/__init__.py @@ -0,0 +1,705 @@ +import json +### Custom util functions +from .utils.modules import * +### Logger for debuging +from .utils.logger import logger + +from typing import Any, List, Optional +from PyQt5.QtWidgets import QWidget +from platform import system, version, release +from ctypes import * +# import the main window object (mw) from aqt +from aqt import AnkiQt, DialogManager, mw +from aqt.theme import theme_manager, colors +from aqt import gui_hooks +## QT dialog windows +# Browser import legacy check (2.1.22) +if module_exists("aqt.browser.browser"): + from aqt.browser.browser import Browser +else: + from aqt.browser import Browser +# DeckStats import legacy check +if module_has_attribute("aqt.stats", "NewDeckStats"): + from aqt.stats import DeckStats, NewDeckStats +else: + from aqt.stats import DeckStats +from aqt.addcards import AddCards +from aqt.editcurrent import EditCurrent +from aqt.about import ClosableQDialog +from aqt.preferences import Preferences +from aqt.addons import AddonsDialog +# FilteredDeckConfigDialog import non-legacy check +if module_exists("aqt.filtered_deck"): + from aqt.filtered_deck import FilteredDeckConfigDialog +# QT page views +from aqt.toolbar import Toolbar, TopToolbar +from aqt.deckbrowser import DeckBrowser, DeckBrowserBottomBar +from aqt.overview import Overview, OverviewBottomBar +from aqt.editor import Editor +from aqt.reviewer import Reviewer, ReviewerBottomBar +from aqt.webview import AnkiWebView, WebContent + +### Load config data here +from .config import config, write_config, get_config + +## Addon compatibility fixes +# More Overview Stats 2.1 addon compatibility fix +addon_more_overview_stats_fix = config['addon_more_overview_stats'] +addon_advanced_review_bottom_bar = config['addon_advanced_review_bottom_bar'] + +## Customization +theme = config['theme'] +# Init script/file path +from .utils.css_files import css_files_dir +from .utils.themes import themes, write_theme, get_theme +logger.debug(css_files_dir) +logger.debug(themes) +themes_parsed = get_theme(theme) +color_mode = 2 if theme_manager.get_night_mode() else 1 # 1 = light and 2 = dark + +dwmapi = None +## Darkmode windows titlebar thanks to miere43 +def set_dark_titlebar(window, dwmapi) -> None: + if dwmapi: + handler_window = c_void_p(int(window.winId())) + DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 = c_int(19) + DWMWA_USE_IMMERSIVE_DARK_MODE = c_int(20) + windows_version = int(version().split('.')[2]) + attribute = DWMWA_USE_IMMERSIVE_DARK_MODE if windows_version >= 18985 else DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 + if windows_version >= 17763 and int(release()) >= 10: + dwmapi.DwmSetWindowAttribute(handler_window, attribute, byref(c_int(1)), c_size_t(4)) +def set_dark_titlebar_qt(obj, dwmapi, fix=True) -> None: + if dwmapi and theme_manager.get_night_mode(): + set_dark_titlebar(obj, dwmapi) + # Trick to refresh the titlebar after dark titlebar is set + if fix: + obj.showFullScreen() + obj.showNormal() +if system() == "Windows" and theme_manager.get_night_mode(): + dwmapi = WinDLL("dwmapi") + if dwmapi: + dwmapi.DwmSetWindowAttribute.argtypes = [c_void_p, c_int, c_void_p, c_size_t] + dwmapi.DwmSetWindowAttribute.restype = c_int + set_dark_titlebar(mw, dwmapi) +logger.debug(dwmapi) + +### CSS injections +def load_custom_style(): + theme_colors = "" + for color_name in themes_parsed.get("colors"): + color = themes_parsed.get("colors").get(color_name) + if color[3]: + theme_colors += f"{color[3]}: {color[color_mode]};\n " + else: + theme_colors += f"--{color_name.lower().replace('_','-')}: {color[color_mode]};\n " + custom_style = """ +<style> + :root, + :root .isMac, + :root .isWin, + :root .isLin { + %s + } + html { + font-family: %s; + font-size: %spx; + } +</style> + """ % (theme_colors, config["font"], config["font_size"]) + return custom_style + +## Adds styling on the different webview contents, before the content is set +def on_webview_will_set_content(web_content: WebContent, context: Optional[Any]) -> None: + logger.debug(context) # Logs content being loaded, find out the instance + web_content.css.append(css_files_dir['global']) # Global css + web_content.head += load_custom_style() # Custom styling + # Deckbrowser + if isinstance(context, DeckBrowser): + web_content.css.append(css_files_dir['DeckBrowser']) + # TopToolbar + elif isinstance(context, TopToolbar): + web_content.css.append(css_files_dir['TopToolbar']) + # BottomToolbar (Buttons) + elif isinstance(context, DeckBrowserBottomBar) or isinstance(context, OverviewBottomBar): + web_content.css.append(css_files_dir['BottomBar']) + # Overview + elif isinstance(context, Overview): + if addon_more_overview_stats_fix: + web_content.head += "<style>center > table tr:first-of-type {display: table-row; flex-direction: unset;}</style>" + web_content.css.append(css_files_dir['Overview']) + # Editor + elif isinstance(context, Editor): + web_content.css.append(css_files_dir['Editor']) + # Reviewer + elif isinstance(context, Reviewer): + web_content.css.append(css_files_dir['Reviewer']) + elif isinstance(context, ReviewerBottomBar): + if addon_advanced_review_bottom_bar: + #web_content.head += "<style>td.stat[align='left']:nth-of-type(2) {position: absolute; z-index: 1;}</style>" + web_content.body += "<script>const center = document.getElementById('outer');center.classList.add('arbb');</script>" + else: + web_content.css.append(css_files_dir['BottomBar']) + web_content.css.append(css_files_dir['ReviewerBottomBar']) + # Button padding bottom + web_content.body += "<div style='height: 9px; opacity: 0; pointer-events: none;'></div>" + web_content.body += "<div id='padFix' style='height: 30px; opacity: 0; pointer-events: none;'><script>const e = document.getElementById('padFix');e.parentElement.removeChild(e);</script></div>" + mw.bottomWeb.adjustHeightToFit() + # CardLayout + elif context_name_includes(context, "aqt.clayout.CardLayout"): + web_content.css.append(css_files_dir['CardLayout']) + ## Legacy webviews + # ResetRequired on card edit (legacy) + elif context_name_includes(context, "aqt.main.ResetRequired"): + web_content.css.append(css_files_dir['legacy']) +gui_hooks.webview_will_set_content.append(on_webview_will_set_content) + +# TopToolbar styling fix through height change by adding <br> tag +def redraw_toolbar() -> None: + # Reload the webview content with added <br/> tag, making the bar larger in height + mw.toolbar.web.setFixedHeight(60) + mw.toolbar.web.eval(""" + var br = document.createElement("br"); + document.body.appendChild(br); + """) + + if 'Qt6' in QPalette.ColorRole.__module__: + mw.toolbar.web.eval(""" + var div = document.createElement("div"); + div.style.width = "5px"; + div.style.height = "10px"; + document.body.appendChild(div); + """) + # Auto adjust the height, then redraw the toolbar + mw.toolbar.web.adjustHeightToFit() + mw.toolbar.redraw() + +def redraw_toolbar_legacy(links: List[str], _: Toolbar) -> None: + # Utilizing the link hook, we inject <br/> tag through javascript + inject_br = """ + <script> + while (document.body.querySelectorAll("br").length > 1) + document.body.querySelectorAll("br")[0].remove(); + var br = document.createElement("br"); + document.body.appendChild(br); + </script> + """ + mw.toolbar.web.setFixedHeight(60) + links.append(inject_br) + mw.toolbar.web.adjustHeightToFit() + +if attribute_exists(gui_hooks, "main_window_did_init"): + pass + #gui_hooks.main_window_did_init.append(redraw_toolbar) +elif attribute_exists(gui_hooks, "top_toolbar_did_init_links"): + gui_hooks.top_toolbar_did_init_links.append(redraw_toolbar_legacy) + pass + +# Dialog window styling +def on_dialog_manager_did_open_dialog(dialog_manager: DialogManager, dialog_name: str, dialog_instance: QWidget) -> None: + logger.debug(dialog_name) + dialog: AnkiQt = dialog_manager._dialogs[dialog_name][1] + # If dwmapi found and nightmode is enabled, set dark titlebar to dialog window + set_dark_titlebar_qt(dialog, dwmapi) + # AddCards + if dialog_name == "AddCards": + context: AddCards = dialog_manager._dialogs[dialog_name][1] + context.setStyleSheet(open(css_files_dir['QAddCards'], encoding='utf-8').read()) + # Addons popup + elif dialog_name == "AddonsDialog": + context: AddonsDialog = dialog_manager._dialogs[dialog_name][1] + context.setStyleSheet(open(css_files_dir['QAddonsDialog'], encoding='utf-8').read()) + # Browser + elif dialog_name == "Browser": + context: Browser = dialog_manager._dialogs[dialog_name][1] + context.setStyleSheet(open(css_files_dir['QBrowser'], encoding='utf-8').read()) + pass + # EditCurrent + elif dialog_name == "EditCurrent": + context: EditCurrent = dialog_manager._dialogs[dialog_name][1] + context.setStyleSheet(open(css_files_dir['QEditCurrent'], encoding='utf-8').read()) + # FilteredDeckConfigDialog + elif module_exists("aqt.filtered_deck") and dialog_name == "FilteredDeckConfigDialog": + context: FilteredDeckConfigDialog = dialog_manager._dialogs[dialog_name][1] + context.setStyleSheet(open(css_files_dir['QFilteredDeckConfigDialog'], encoding='utf-8').read()) + # Statistics / NewDeckStats + elif dialog_name == "NewDeckStats": + context: NewDeckStats = dialog_manager._dialogs[dialog_name][1] + context.setStyleSheet(open(css_files_dir['QNewDeckStats'], encoding='utf-8').read()) + # About + elif dialog_name == "About": + context: ClosableQDialog = dialog_manager._dialogs[dialog_name][1] + context.setStyleSheet(open(css_files_dir['QAbout'], encoding='utf-8').read()) + # Preferences + elif dialog_name == "Preferences": + context: Preferences = dialog_manager._dialogs[dialog_name][1] + context.setStyleSheet(open(css_files_dir['QPreferences'], encoding='utf-8').read()) + # sync_log - kore ha nani desu??? + elif dialog_name == "sync_log": + pass + +if attribute_exists(gui_hooks, "dialog_manager_did_open_dialog"): + gui_hooks.dialog_manager_did_open_dialog.append(on_dialog_manager_did_open_dialog) +else: + ## Legacy dialog window styling + # Sad monkey patch, instead of hooks :c + # setupDialogGC is being called on almost all dialog windows, utilizing this, the + # function is used as a type of hook to inject CSS styling on the QT instances + def monkey_setup_dialog_gc(obj: Any) -> None: + obj.finished.connect(lambda: mw.gcWindow(obj)) + logger.debug(obj) + set_dark_titlebar_qt(obj, dwmapi) + # AddCards + if isinstance(obj, AddCards): + obj.setStyleSheet(open(css_files_dir['QAddCards'], encoding='utf-8').read()) + # EditCurrent + elif isinstance(obj, EditCurrent): + obj.setStyleSheet(open(css_files_dir['QEditCurrent'], encoding='utf-8').read()) + # Statistics / DeckStats + elif isinstance(obj, DeckStats): + obj.setStyleSheet(open(css_files_dir['QNewDeckStats'], encoding='utf-8').read()) + # About + elif isinstance(obj, ClosableQDialog): + obj.setStyleSheet(open(css_files_dir['QAbout'], encoding='utf-8').read()) + # Preferences + ## Haven't found a solution for preferences yet :c + mw.setupDialogGC = monkey_setup_dialog_gc # Should be rare enough for other addons to also patch this I hope. + + # Addons popup + if attribute_exists(gui_hooks, "addons_dialog_will_show"): + def on_addons_dialog_will_show(dialog: AddonsDialog) -> None: + logger.debug(dialog) + set_dark_titlebar_qt(dialog, dwmapi) + dialog.setStyleSheet(open(css_files_dir['QAddonsDialog'], encoding='utf-8').read()) + gui_hooks.addons_dialog_will_show.append(on_addons_dialog_will_show) + # Browser + if attribute_exists(gui_hooks, "browser_will_show"): + def on_browser_will_show(browser: Browser) -> None: + logger.debug(browser) + set_dark_titlebar_qt(browser, dwmapi) + browser.setStyleSheet(open(css_files_dir['QBrowser'], encoding='utf-8').read()) + gui_hooks.browser_will_show.append(on_browser_will_show) + +## CONFIG DIALOG +from aqt import mw, gui_hooks +from aqt.qt import * +from .config import config +from aqt.utils import showInfo +from aqt.webview import AnkiWebView + +class ThemeEditor(QDialog): + def __init__(self, parent, *args, **kwargs): + super().__init__(parent=parent or mw, *args, **kwargs) + self.config_editor = parent + self.setWindowModality(Qt.ApplicationModal) + self.setWindowTitle(f'Anki-redesign Advanced Editor') + self.setSizePolicy(self.make_size_policy()) + self.setMinimumSize(420, 420) + self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose) + set_dark_titlebar_qt(self, dwmapi, fix=False) + # Root layout + self.root_layout = QVBoxLayout(self) + # Main layout + self.layout = QVBoxLayout() + self.textedit = QTextEdit() + themes_plaintext = open(themes[theme], encoding='utf-8').read() + self.textedit.setPlainText(themes_plaintext) + self.layout.addWidget(self.textedit) + self.root_layout.addLayout(self.layout) + self.root_layout.addLayout(self.make_button_box()) + + def save_edit(self) -> None: + themes_parsed = json.loads(self.textedit.toPlainText()) + write_theme(themes[theme], themes_parsed) + self.config_editor.update() + self.accept() + + def make_button_box(self) -> QWidget: + def cancel(): + button = QPushButton('Cancel') + button.setCursor(QCursor(Qt.CursorShape.PointingHandCursor)) + qconnect(button.clicked, self.accept) + return button + def save(): + button = QPushButton('Save') + button.setCursor(QCursor(Qt.CursorShape.PointingHandCursor)) + button.setDefault(True) + button.setShortcut("Ctrl+Return") + button.clicked.connect(lambda _: self.save_edit()) + return button + + button_box = QHBoxLayout() + button_box.addStretch() + button_box.addWidget(cancel()) + button_box.addWidget(save()) + return button_box + + def make_size_policy(self) -> QSizePolicy: + size_policy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Minimum) + size_policy.setHorizontalStretch(0) + size_policy.setVerticalStretch(0) + size_policy.setHeightForWidth(self.sizePolicy().hasHeightForWidth()) + return size_policy + +class ConfigDialog(QDialog): + def __init__(self, parent: QWidget, *args, **kwargs): + super().__init__(parent=parent or mw, *args, **kwargs) + self.setWindowModality(Qt.ApplicationModal) + self.setWindowTitle(f'Anki-redesign Configuration') + self.setSizePolicy(self.make_size_policy()) + self.setMinimumSize(420, 580) + self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose) + set_dark_titlebar_qt(self, dwmapi, fix=False) + + # Color/theme + ## Loads theme color + self.theme_colors = themes_parsed.get("colors") + self.updates = [] + self.theme_general = ["TEXT_FG", "WINDOW_BG", "FRAME_BG", "BUTTON_BG", "TOOLTIP_BG", "BORDER", "MEDIUM_BORDER", "FAINT_BORDER", "HIGHLIGHT_BG", "HIGHLIGHT_FG" , "LINK", "DISABLED", "SLIGHTLY_GREY_TEXT", "PRIMARY_COLOR"] + self.theme_decks = ["CURRENT_DECK", "NEW_COUNT", "LEARN_COUNT", "REVIEW_COUNT", "ZERO_COUNT"] + self.theme_browse = ["BURIED_FG", "SUSPENDED_FG", "MARKED_BG", "FLAG1_BG", "FLAG1_FG", "FLAG2_BG", "FLAG2_FG", "FLAG3_BG", "FLAG3_FG", "FLAG4_BG", "FLAG4_FG", "FLAG5_BG", "FLAG5_FG", "FLAG6_BG", "FLAG6_FG", "FLAG7_BG", "FLAG7_FG"] + self.theme_extra = [] + + # Root layout + self.root_layout = QVBoxLayout(self) + # Main layout + self.layout = QVBoxLayout() + ## Initialize tab screen + self.tabs = QTabWidget(objectName="tabs") + self.tabs.setFocusPolicy(Qt.FocusPolicy.StrongFocus) + self.tab_general = QWidget(objectName="general") + self.tab_general.setLayout(self.create_color_picker_layout(self.theme_general)) + self.tab_decks = QWidget(objectName="decks") + self.tab_decks.setLayout(self.create_color_picker_layout(self.theme_decks)) + self.tab_browse = QWidget(objectName="browse") + self.tab_browse.setLayout(self.create_color_picker_layout(self.theme_browse)) + self.tab_extra = QWidget(objectName="extra") + self.tab_extra.setLayout(self.create_color_picker_layout(self.theme_extra)) + + self.tab_settings = QWidget(objectName="settings") + self.settings_layout = QFormLayout() + self.theme_label = QLabel("Theme:") + self.theme_label.setStyleSheet('QLabel { font-size: 14px; font-weight: bold }') + self.settings_layout.addRow(self.theme_label) + for key in themes: + self.radio = self.radio_button(key) + self.settings_layout.addRow(key, self.radio) + self.settings_layout.addRow(QLabel()) + + self.font_label = QLabel("Font:") + self.font_label.setStyleSheet('QLabel { font-size: 14px; font-weight: bold }') + self.settings_layout.addRow(self.font_label) + self.interface_font = QFontComboBox() + self.interface_font.setFixedWidth(200) + self.interface_font.setCurrentFont(QFont(config["font"])) + self.settings_layout.addRow(self.interface_font) + + self.font_size = QSpinBox() + self.font_size.setFixedWidth(200) + self.font_size.setValue(config["font_size"]) + self.font_size.setSuffix("px") + self.settings_layout.addRow(self.font_size) + + self.settings_layout.addRow(QLabel()) + + + self.fix_label = QLabel("Addon-Compatibility Fixes: ") + self.fix_label.setStyleSheet('QLabel { font-size: 14px; font-weight: bold }') + self.settings_layout.addRow(self.fix_label) + self.reload_theme = self.checkbox("theme_reload") + self.settings_layout.addRow("QT6 theme on-start reload fix", self.reload_theme) + self.addon_more_overview_stats_check = self.checkbox("addon_more_overview_stats") + self.settings_layout.addRow("More Overview Stats 21", self.addon_more_overview_stats_check) + self.addon_advanced_review_bottom_bar_check = self.checkbox("addon_advanced_review_bottom_bar") + self.settings_layout.addRow("Advanced Review Bottom Bar", self.addon_advanced_review_bottom_bar_check) + + self.tab_settings.setLayout(self.settings_layout) + + ## Add tabs + self.tabs.resize(300,200) + self.tabs.addTab(self.tab_settings,"Settings") + self.tabs.addTab(self.tab_general,"General") + self.tabs.addTab(self.tab_decks,"Decks") + self.tabs.addTab(self.tab_browse,"Browse") + #self.tabs.addTab(self.tab_extra,"Extra") + + ## Add tabs to widget + self.layout.addWidget(self.tabs) + + self.root_layout.addLayout(self.layout) + self.root_layout.addLayout(self.make_button_box()) + self.setLayout(self.root_layout) + self.show() + + def update(self) -> None: + global themes_parsed + themes_parsed = get_theme(theme) + self.theme_colors = themes_parsed.get("colors") + for update in self.updates: + update() + + def checkbox(self, key: str) -> QCheckBox: + checkbox = QCheckBox() + + def update() -> None: + value = config[key] + checkbox.setChecked(value) + + self.updates.append(update) + update() + return checkbox + + def radio_button(self, key: str) -> QRadioButton: + radio = QRadioButton() + + def update() -> None: + if theme == key: + radio.setChecked(True) + elif radio.isChecked(): + radio.setChecked(False) + + def toggle(checked) -> None: + global theme + if checked: + theme = key + self.update() + + self.updates.append(update) + radio.toggled.connect(lambda checked: toggle(checked)) + update() + return radio + + def color_input(self, key: str) -> QPushButton: + button = QPushButton() + button.setCursor(QCursor(Qt.CursorShape.PointingHandCursor)) + button.setFixedWidth(25) + button.setFixedHeight(25) + + color_dialog = QColorDialog(self) + + def set_color(rgb: str) -> None: + # Check for valid color + color = QColor() + color.setNamedColor(rgb) + if not color.isValid(): + return + # Update color + color_dialog.setCurrentColor(color) + button.setStyleSheet('QPushButton{ background-color: "%s"; border: none; border-radius: 2px}' % rgb) + + def update() -> None: + rgb = self.theme_colors.get(key)[color_mode] + set_color(rgb) + + def save(color: QColor) -> None: + rgb = color.name(QColor.NameFormat.HexRgb) + self.theme_colors[key][color_mode] = rgb + set_color(rgb) + + self.updates.append(update) + color_dialog.colorSelected.connect(lambda color: save(color)) + button.clicked.connect(lambda _: color_dialog.exec()) + return button + + def create_color_picker_layout(self, colors) -> None: + layout = QFormLayout() + for key in colors: + self.test = self.color_input(key) + layout.addRow(self.theme_colors.get(key)[0], self.test) + return layout + + def theme_file_editor(self) -> None: + diag = ThemeEditor(self) + diag.show() + + def make_button_box(self) -> QWidget: + def advanced(): + button = QPushButton('Advanced') + button.setCursor(QCursor(Qt.CursorShape.PointingHandCursor)) + qconnect(button.clicked, self.theme_file_editor) + return button + def cancel(): + button = QPushButton('Cancel') + button.setCursor(QCursor(Qt.CursorShape.PointingHandCursor)) + qconnect(button.clicked, self.accept) + return button + def save(): + button = QPushButton('Save') + button.setCursor(QCursor(Qt.CursorShape.PointingHandCursor)) + button.setDefault(True) + button.setShortcut("Ctrl+Return") + button.clicked.connect(lambda _: self.save()) + return button + + button_box = QHBoxLayout() + button_box.addWidget(advanced()) + button_box.addStretch() + button_box.addWidget(cancel()) + button_box.addWidget(save()) + return button_box + + def make_size_policy(self) -> QSizePolicy: + size_policy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Minimum) + size_policy.setHorizontalStretch(0) + size_policy.setVerticalStretch(0) + size_policy.setHeightForWidth(self.sizePolicy().hasHeightForWidth()) + return size_policy + + def save(self) -> None: + # Save settings and update config + global config, color_mode + config["font"] = self.interface_font.currentFont().family() + config["font_size"] = self.font_size.value() + config['addon_more_overview_stats'] = self.addon_more_overview_stats_check.isChecked() + config['addon_advanced_review_bottom_bar'] = self.addon_advanced_review_bottom_bar_check.isChecked() + config['theme_reload'] = self.reload_theme.isChecked() + config["theme"] = theme + write_config(config) + config = get_config() + + # Write and update theme + color_mode = 2 if theme_manager.get_night_mode() else 1 # 1 = light and 2 = dark + themes_parsed["colors"] = self.theme_colors + write_theme(themes[theme], themes_parsed) + update_theme() + + # mw.reset() + # ShowInfo for both new and legacy support + showInfo(_("Changes will take effect when you restart Anki.")) + #showInfo(tr.preferences_changes_will_take_effect_when_you()) + self.accept() + +def check_legacy_colors() -> None: + try: + _ = colors.items() + except: + return False + return True + +def refresh_all_windows() -> None: + # Redraw top toolbar + mw.toolbar.draw() + if attribute_exists(gui_hooks, "top_toolbar_did_init_links"): + gui_hooks.top_toolbar_did_init_links.append(lambda a,b: [redraw_toolbar_legacy(a,b), gui_hooks.top_toolbar_did_init_links.remove(print)]) + + # Redraw main body + if mw.state == "review": + mw.reviewer._initWeb() + if mw.reviewer.state == "question": + mw.reviewer._showQuestion() + else: + mw.reviewer._showAnswer() + elif mw.state == "overview": + mw.overview.refresh() + elif mw.state == "deckBrowser": + mw.deckBrowser.show() + + if attribute_exists(gui_hooks, "top_toolbar_did_init_links"): + gui_hooks.top_toolbar_did_init_links.remove(redraw_toolbar) + +def update_theme() -> None: + themes_parsed = get_theme(theme) + theme_colors = themes_parsed.get("colors") + # Apply theme on colors + ncolors = {} + # Legacy color check + logger.debug(dir(colors)) + legacy = check_legacy_colors() + for color_name in theme_colors: + c = theme_colors.get(color_name) + ncolors[color_name] = c[color_mode] + if legacy: + colors[f"day{c[3].replace('--','-')}"] = c[1] + colors[f"night{c[3].replace('--','-')}"] = c[2] + # Potentially add fusion fixes too? + else: + logger.debug(getattr(colors, color_name, False)) + if getattr(colors, color_name, False): + setattr(colors, color_name, (c[1], c[2])) + # Apply theme on palette + apply_theme(ncolors) + refresh_all_windows() + +def apply_theme(colors) -> None: + # Load palette + palette = QPalette() + # QT mappings + #logger.debug(QPalette.ColorRole.__dict__) + color_map = { + QPalette.ColorRole.Window: "WINDOW_BG", + QPalette.ColorRole.WindowText: "TEXT_FG", + QPalette.ColorRole.Base: "FRAME_BG", + QPalette.ColorRole.AlternateBase: "WINDOW_BG", + QPalette.ColorRole.ToolTipBase: "TOOLTIP_BG", + QPalette.ColorRole.ToolTipText: "TEXT_FG", + QPalette.ColorRole.Text: "TEXT_FG", + QPalette.ColorRole.Button: "BUTTON_BG", + QPalette.ColorRole.ButtonText: "TEXT_FG", + QPalette.ColorRole.BrightText: "HIGHLIGHT_FG", + QPalette.ColorRole.HighlightedText: "HIGHLIGHT_FG", + QPalette.ColorRole.Link: "LINK", + QPalette.ColorRole.NoRole: "WINDOW_BG", + } + for color_role in color_map: + palette.setColor(color_role, QColor(colors[color_map[color_role]])) + + highlight_bg = QColor(colors["HIGHLIGHT_BG"]) + highlight_bg.setAlpha(64) + palette.setColor(QPalette.ColorRole.Highlight, highlight_bg) + + disabled_color = QColor(colors["DISABLED"]) + palette.setColor(QPalette.ColorRole.PlaceholderText, disabled_color) + palette.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.Text, disabled_color) + palette.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.ButtonText, disabled_color) + palette.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.HighlightedText, disabled_color) + + # Update palette + mw.app.setPalette(palette) + theme_manager.default_palette = palette + theme_manager._apply_style(mw.app) + + # Update webview bg + AnkiWebView._getWindowColor = lambda *args: QColor(colors["WINDOW_BG"]) + AnkiWebView.get_window_bg_color = lambda *args: QColor(colors["WINDOW_BG"]) + +# Create menu actions +def create_menu_action(parent: QWidget, dialog_class: QDialog, dialog_name: str) -> QAction: + def open_dialog(): + dialog = dialog_class(mw) + return dialog.exec() + + action = QAction(dialog_name, parent) + #qconnect(action.triggered, open_dialog) + action.triggered.connect(open_dialog) + return action + +# Load in the Anki-redesign menu +if not hasattr(mw, 'anki_redesign'): + mw.anki_redesign = QMenu("&Anki-redesign", mw) + mw.form.menubar.insertMenu(mw.form.menuHelp.menuAction(), mw.anki_redesign) + + mw.anki_redesign.addAction(create_menu_action(mw.anki_redesign, ConfigDialog, "&Config")) + mw.anki_redesign.addSeparator() + mw.reset() + update_theme() + mw.reset() + + # Rereload view to fix theme change on startup + if 'Qt6' in QPalette.ColorRole.__module__: + logger.debug('QT6 DETECTED....') + config = get_config() + if config["theme_reload"]: + update_theme() + +def on_theme_did_change() -> None: + global color_mode + color_mode = 2 if theme_manager.get_night_mode() else 1 # 1 = light and 2 = dark + update_theme() + logger.debug("THEME CHANGEEEED") + refresh_all_windows() + mw.reset() + update_theme() + + +if attribute_exists(gui_hooks, "theme_did_change"): + gui_hooks.theme_did_change.append(on_theme_did_change) + logger.debug("YEP")
\ No newline at end of file diff --git a/.local/share/Anki2/addons21/anki_reworked/config.json b/.local/share/Anki2/addons21/anki_reworked/config.json new file mode 100644 index 0000000..b1e6ac5 --- /dev/null +++ b/.local/share/Anki2/addons21/anki_reworked/config.json @@ -0,0 +1,8 @@ +{ + "addon_more_overview_stats": "false", + "addon_advanced_review_bottom_bar": "false", + "font": "Segoe UI", + "font_size": "12", + "theme": "Catppuccin", + "theme_reload": "false" +} diff --git a/.local/share/Anki2/addons21/anki_reworked/config.md b/.local/share/Anki2/addons21/anki_reworked/config.md new file mode 100644 index 0000000..33498b4 --- /dev/null +++ b/.local/share/Anki2/addons21/anki_reworked/config.md @@ -0,0 +1,9 @@ +This is the advanced documentation for Anki-redesign's configuration: +Use the built-in config at the menu (Anki-redesign > Config) for a better user experience. + +- addon_more_overview_stats: either `true` or `false` +- addon_advanced_review_bottom_bar: either `true` or `false` +- font: customize own font here +- font_size: customize own font size here +- theme: customize theme here +- theme_reload: either `true` or `false` diff --git a/.local/share/Anki2/addons21/anki_reworked/config.py b/.local/share/Anki2/addons21/anki_reworked/config.py new file mode 100644 index 0000000..9e508f8 --- /dev/null +++ b/.local/share/Anki2/addons21/anki_reworked/config.py @@ -0,0 +1,22 @@ +from aqt import mw + +def get_config() -> dict: + config: dict = mw.addonManager.getConfig(__name__) or dict() + ## Addon fix + config['addon_more_overview_stats'] = True if config.get('addon_more_overview_stats', "false").lower() == "true" else False + config['addon_advanced_review_bottom_bar'] = True if config.get('addon_advanced_review_bottom_bar', "false").lower() == "true" else False + ## Customization + config['font'] = config.get('font', "Segoe UI") + config['font_size'] = int(config.get('font_size', "12")) + config['theme'] = config.get('theme', 'Anki') + config['theme_reload'] = True if config.get('theme_reload', "false").lower() == "true" else False + + return config + +def write_config(config): + for key in config.keys(): + if not isinstance(config[key], str): + config[key] = str(config[key]) + mw.addonManager.writeConfig(__name__, config) + +config = get_config() diff --git a/.local/share/Anki2/addons21/anki_reworked/files/BottomBar.css b/.local/share/Anki2/addons21/anki_reworked/files/BottomBar.css new file mode 100644 index 0000000..1e07817 --- /dev/null +++ b/.local/share/Anki2/addons21/anki_reworked/files/BottomBar.css @@ -0,0 +1,40 @@ +body #outer button { + color: var(--text-fg, var(--legacy-text-fg)); + border: none; + border-radius: 15px; + padding: 6px 24px; + margin: 0 5px; + cursor: pointer; + font-weight: 500; + font-size: 0.875rem; + line-height: 1.75; + letter-spacing: 0.02857em; + text-transform: uppercase; + min-width: 64px; + background-color: var(--frame-bg, var(--legacy-frame-bg)); + transition: background 0.2s linear, color 0.3s ease-out; + box-shadow: rgb(0 0 0 / 20%) 0px 3px 7px -2px, + rgb(0 0 0 / 5%) 0px 5px 10px 0px, rgb(0 0 0 / 12%) 0px 1px 5px 0px; +} +body.nightMode #outer button { + background: initial; + background-color: var(--frame-bg, var(--legacy-frame-bg)); +} + +body #outer button:focus, +body #outer button.focus { + background-color: var(--primary-color); + color: var(--frame-bg, var(--legacy-frame-bg)); + outline: none; +} +body.nightMode #outer button:focus, +body.nightMode #outer button.focus { + color: var(--text-fg, var(--legacy-text-fg)); +} + +body #outer button:hover { + background-color: rgba(0, 0, 0, 0.05); +} +body.nightMode #outer button:hover { + background-color: rgba(255, 255, 255, 0.1); +} diff --git a/.local/share/Anki2/addons21/anki_reworked/files/CardLayout.css b/.local/share/Anki2/addons21/anki_reworked/files/CardLayout.css new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/.local/share/Anki2/addons21/anki_reworked/files/CardLayout.css @@ -0,0 +1 @@ + diff --git a/.local/share/Anki2/addons21/anki_reworked/files/DeckBrowser.css b/.local/share/Anki2/addons21/anki_reworked/files/DeckBrowser.css new file mode 100644 index 0000000..0b8f82c --- /dev/null +++ b/.local/share/Anki2/addons21/anki_reworked/files/DeckBrowser.css @@ -0,0 +1,48 @@ +body table:first-of-type { + width: 90%; + max-width: 600px; +} +body table:first-of-type th { + padding: 0 6px; +} +body table:first-of-type td { + padding: 6px; +} +body table:first-of-type tr.deck td { + border-bottom: none; +} +body table:first-of-type tr.deck td.opts { + padding-left: 20px; +} +body table:first-of-type tr.deck td.opts > a { + display: flex; +} +body table:first-of-type tr.current { + background-color: transparent; +} +body table:first-of-type tr.current td { + background-color: var(--faint-border, var(--legacy-faint-border)); +} +body table:first-of-type tr.current td:first-of-type { + border-radius: 10px 0 0 10px; +} +body table:first-of-type tr.current td:last-of-type { + border-radius: 0 10px 10px 0; +} +body table:first-of-type tr a { + color: var(--text-fg, var(--legacy-text-fg)) !important; +} +body table:first-of-type tr a.filtered { + color: var(--link, var(--legacy-link)) !important; +} +a.deck { + padding: 5px 10px 5px 5px; + transition: padding 1s, background-color 0.5s, margin-right 1s; + border-radius: 6px; + margin-right: 5px; +} +body table:first-of-type tr a.deck:hover { + padding: 5px 10px 5px 10px; + background-color: rgba(0, 0, 0, 0.1); + margin-right: 0; +} diff --git a/.local/share/Anki2/addons21/anki_reworked/files/Editor.css b/.local/share/Anki2/addons21/anki_reworked/files/Editor.css new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/.local/share/Anki2/addons21/anki_reworked/files/Editor.css @@ -0,0 +1 @@ + diff --git a/.local/share/Anki2/addons21/anki_reworked/files/Overview.css b/.local/share/Anki2/addons21/anki_reworked/files/Overview.css new file mode 100644 index 0000000..7c7571a --- /dev/null +++ b/.local/share/Anki2/addons21/anki_reworked/files/Overview.css @@ -0,0 +1,59 @@ +body .descmid { + text-align: center; +} +center > table tr:first-of-type { + display: flex; + flex-direction: column; +} +td table { + width: 50%; +} +td table tr:first-of-type { + display: table-row; + flex-direction: unset; +} +td table tr td:last-of-type { + text-align: right; +} +button#study.but, +button#main.but { + padding: 10px 27px; + box-sizing: border-box; + width: 100%; + margin-bottom: 20px; + + color: var(--text-fg, var(--legacy-text-fg)); + border: none; + border-radius: 15px; + margin: 0 5px; + cursor: pointer; + font-weight: 500; + font-size: 0.875rem; + line-height: 1.75; + letter-spacing: 0.02857em; + text-transform: uppercase; + min-width: 64px; + background: initial; + background-color: var(--frame-bg, var(--legacy-frame-bg)); + transition: background 0.2s linear, color 0.3s ease-out; + box-shadow: rgb(0 0 0 / 20%) 0px 3px 7px -2px, + rgb(0 0 0 / 5%) 0px 5px 10px 0px, rgb(0 0 0 / 12%) 0px 1px 5px 0px; +} +body.nightMode button#study.but:hover, +body.nightMode button#main.but:hover { + background-color: rgba(255, 255, 255, 0.1); +} +button#study.but:focus, +button#main.but:focus { + background-color: var(--primary-color) !important; + color: var(--frame-bg, var(--legacy-frame-bg)); + outline: none; +} +body.nightMode button#study.but:focus, +body.nightMode button#main.but:focus { + color: var(--text-fg); +} +button#study.but:hover, +button#main.but:hover { + background-color: rgba(0, 0, 0, 0.05); +} diff --git a/.local/share/Anki2/addons21/anki_reworked/files/QAbout.css b/.local/share/Anki2/addons21/anki_reworked/files/QAbout.css new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/.local/share/Anki2/addons21/anki_reworked/files/QAbout.css diff --git a/.local/share/Anki2/addons21/anki_reworked/files/QAddCards.css b/.local/share/Anki2/addons21/anki_reworked/files/QAddCards.css new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/.local/share/Anki2/addons21/anki_reworked/files/QAddCards.css @@ -0,0 +1 @@ + diff --git a/.local/share/Anki2/addons21/anki_reworked/files/QAddonsDialog.css b/.local/share/Anki2/addons21/anki_reworked/files/QAddonsDialog.css new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/.local/share/Anki2/addons21/anki_reworked/files/QAddonsDialog.css diff --git a/.local/share/Anki2/addons21/anki_reworked/files/QBrowser.css b/.local/share/Anki2/addons21/anki_reworked/files/QBrowser.css new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/.local/share/Anki2/addons21/anki_reworked/files/QBrowser.css diff --git a/.local/share/Anki2/addons21/anki_reworked/files/QEditCurrent.css b/.local/share/Anki2/addons21/anki_reworked/files/QEditCurrent.css new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/.local/share/Anki2/addons21/anki_reworked/files/QEditCurrent.css diff --git a/.local/share/Anki2/addons21/anki_reworked/files/QFilteredDeckConfigDialog.css b/.local/share/Anki2/addons21/anki_reworked/files/QFilteredDeckConfigDialog.css new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/.local/share/Anki2/addons21/anki_reworked/files/QFilteredDeckConfigDialog.css diff --git a/.local/share/Anki2/addons21/anki_reworked/files/QNewDeckStats.css b/.local/share/Anki2/addons21/anki_reworked/files/QNewDeckStats.css new file mode 100644 index 0000000..88cb00c --- /dev/null +++ b/.local/share/Anki2/addons21/anki_reworked/files/QNewDeckStats.css @@ -0,0 +1,3 @@ +body { + background-color: red; +}
\ No newline at end of file diff --git a/.local/share/Anki2/addons21/anki_reworked/files/QPreferences.css b/.local/share/Anki2/addons21/anki_reworked/files/QPreferences.css new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/.local/share/Anki2/addons21/anki_reworked/files/QPreferences.css diff --git a/.local/share/Anki2/addons21/anki_reworked/files/Reviewer.css b/.local/share/Anki2/addons21/anki_reworked/files/Reviewer.css new file mode 100644 index 0000000..9e47f09 --- /dev/null +++ b/.local/share/Anki2/addons21/anki_reworked/files/Reviewer.css @@ -0,0 +1,9 @@ +body #typeans { + padding: 8px 12px; + border-radius: 5px; +} +body.card { + width: 90%; + margin: 20px auto; + max-width: 1080px; +} diff --git a/.local/share/Anki2/addons21/anki_reworked/files/ReviewerBottomBar.css b/.local/share/Anki2/addons21/anki_reworked/files/ReviewerBottomBar.css new file mode 100644 index 0000000..768270e --- /dev/null +++ b/.local/share/Anki2/addons21/anki_reworked/files/ReviewerBottomBar.css @@ -0,0 +1,60 @@ +body { + overflow: hidden; +} +body #outer { + border-top: 1px solid transparent; +} +body.nightMode #outer { + border-top-color: transparent; +} +body #outer table#innertable { + padding: 6px; + transform: translateY(8px); +} +body button { + margin: 0; +} +body .stat { + position: relative; + padding-top: 0px; + min-width: 120px; +} +body .stat2 { + padding-top: 0px; + font-weight: normal; +} +td[align="center"] { + position: relative; +} +body .stattxt, +td[align="center"] > span { + position: absolute; + display: block; + left: 50%; + transform: translate(-50%, -13px); + color: var(--text-fg, var(--legacy-text-fg)); +} +body .stattxt#time { + transform: translate(-50%, -6px); +} +body #outer button#ansbut, +body #outer table button { + transform: translateY(-4px); +} +/* ARBb addon fix */ +body #outer.arbb .stat { + min-width: auto; + padding-left: 2px; + padding-right: 2px; +} +body #outer.arbb table#innertable { + padding: initial; + width: calc(100% - 10px); +} +body #outer.arbb button { + /* color: var(--text-fg, var(--legacy-text-fg)); */ + letter-spacing: 0.02857em; + text-transform: uppercase; + margin-left: 2px; + margin-right: 2px; +} diff --git a/.local/share/Anki2/addons21/anki_reworked/files/TopToolbar.css b/.local/share/Anki2/addons21/anki_reworked/files/TopToolbar.css new file mode 100644 index 0000000..3b4b6ee --- /dev/null +++ b/.local/share/Anki2/addons21/anki_reworked/files/TopToolbar.css @@ -0,0 +1,25 @@ +body #outer { + display: flex; + justify-content: center; +} +body center #header { + width: auto; + border-bottom: none; + border-radius: 0 0 12px 12px; + background-color: var(--frame-bg, var(--legacy-frame-bg)); + height: 40px; + box-shadow: rgb(0 0 0 / 10%) 0px 0px 11px -2px, + rgb(0 0 0 / 14%) 0px 2px 2px 0px, rgb(0 0 0 / 12%) 0px 4px 5px 0px; +} +body.nightMode center #header { + background-color: var(--faint-border, var(--legacy-faint-border)); +} +body center #header .hitem { + padding-left: initial; + padding-right: initial; + padding: 0 15px; + color: var(--text-fg, var(--legacy-text-fg)) !important; +} +body center #header a#sync { + color: var(--link, var(--legacy-link)) !important; +} diff --git a/.local/share/Anki2/addons21/anki_reworked/files/global.css b/.local/share/Anki2/addons21/anki_reworked/files/global.css new file mode 100644 index 0000000..a145312 --- /dev/null +++ b/.local/share/Anki2/addons21/anki_reworked/files/global.css @@ -0,0 +1,96 @@ +:root, +:root .isWin, +:root .isMac, +:root .isLin { + --focus-color: rgba(81, 135, 169, 0.7); +} +/* Force LIGHT MODE Background */ +:root body:not(.nightMode), +:root body.isWin:not(.nightMode), +:root body.isMac:not(.nightMode), +:root body.isLin:not(.nightMode) { + /* Force variable styling on legacy versions */ + --legacy-text-fg: black; + --legacy-frame-bg: white; + --legacy-border: #aaa; + --legacy-medium-border: #b6b6b6; + --legacy-faint-border: #e7e7e7; + --legacy-link: #00a; + --legacy-review-count: #0a0; + --legacy-new-count: #00a; + --legacy-learn-count: #c35617; + --legacy-zero-count: #ddd; + --legacy-slightly-grey-text: #333; + --legacy-highlight-bg: #77ccff; + --legacy-highlight-fg: black; + --legacy-disabled: #777; + --legacy-flag1-fg: #e25252; + --legacy-flag2-fg: #ffb347; + --legacy-flag3-fg: #54c414; + --legacy-flag4-fg: #578cff; + --legacy-flag5-fg: #ff82ee; + --legacy-flag6-fg: #00d1b5; + --legacy-flag7-fg: #9649dd; + --legacy-flag1-bg: #ff9b9b; + --legacy-flag2-bg: #ffb347; + --legacy-flag3-bg: #93e066; + --legacy-flag4-bg: #9dbcff; + --legacy-flag5-bg: #f5a8eb; + --legacy-flag6-bg: #7edbd7; + --legacy-flag7-bg: #cca3f1; + --legacy-buried-fg: #aaaa33; + --legacy-suspended-fg: #dd0; + --legacy-suspended-bg: #ffffb2; + --legacy-marked-bg: #cce; + --legacy-tooltip-bg: #fcfcfc; +} +/* Force DARK MODE Background */ +:root body.nightMode, +:root body.isWin.nightMode, +:root body.isMac.nightMode, +:root body.isLin.nightMode { + /* Force variable styling on legacy versions */ + --legacy-text-fg: white; + --legacy-frame-bg: #3a3a3a; + --legacy-border: #777; + --legacy-medium-border: #444; + --legacy-faint-border: #29292b; + --legacy-link: #77ccff; + --legacy-review-count: #5ccc00; + --legacy-new-count: #77ccff; + --legacy-learn-count: #ff935b; + --legacy-zero-count: #444; + --legacy-slightly-grey-text: #ccc; + --legacy-highlight-bg: #77ccff; + --legacy-highlight-fg: white; + --legacy-disabled: #777; + --legacy-flag1-fg: #ff7b7b; + --legacy-flag2-fg: #f5aa41; + --legacy-flag3-fg: #86ce5d; + --legacy-flag4-fg: #6f9dff; + --legacy-flag5-fg: #f097e4; + --legacy-flag6-fg: #5ccfca; + --legacy-flag7-fg: #9f63d3; + --legacy-flag1-bg: #aa5555; + --legacy-flag2-bg: #ac653a; + --legacy-flag3-bg: #559238; + --legacy-flag4-bg: #506aa3; + --legacy-flag5-bg: #975d8f; + --legacy-flag6-bg: #399185; + --legacy-flag7-bg: #624b77; + --legacy-buried-fg: #777733; + --legacy-suspended-fg: #ffffb2; + --legacy-suspended-bg: #aaaa33; + --legacy-marked-bg: #77c; + --legacy-tooltip-bg: #272727; +} + +body { + background-color: var(--window-bg) !important; +} +html { + font-size: 12px; +} +html body a { + color: var(--link, var(--legacy-link)) !important; +} diff --git a/.local/share/Anki2/addons21/anki_reworked/files/legacy.css b/.local/share/Anki2/addons21/anki_reworked/files/legacy.css new file mode 100644 index 0000000..2ce3692 --- /dev/null +++ b/.local/share/Anki2/addons21/anki_reworked/files/legacy.css @@ -0,0 +1,18 @@ +button { + box-sizing: border-box; + color: var(--text-fg, var(--legacy-text-fg)); + border: none; + border-radius: 15px; + cursor: pointer; + padding: 6px 24px; + font-weight: 500; + font-size: 0.875rem; + line-height: 1.75; + letter-spacing: 0.02857em; + text-transform: uppercase; + background: initial; + background-color: var(--frame-bg, var(--legacy-frame-bg)); + transition: background 0.2s linear, color 0.3s ease-out; + box-shadow: rgb(0 0 0 / 20%) 0px 3px 7px -2px, + rgb(0 0 0 / 5%) 0px 5px 10px 0px, rgb(0 0 0 / 12%) 0px 1px 5px 0px; +}
\ No newline at end of file diff --git a/.local/share/Anki2/addons21/anki_reworked/manifest.json b/.local/share/Anki2/addons21/anki_reworked/manifest.json new file mode 100644 index 0000000..33ce43f --- /dev/null +++ b/.local/share/Anki2/addons21/anki_reworked/manifest.json @@ -0,0 +1,10 @@ +{ + "name": "Anki Redesign", + "package": "anki-redesign", + "ankiweb_id": "308574457", + "author": "Shirajuki", + "version": "0.1.3", + "homepage": "https://github.com/shirajuki/anki-redesign", + "conflicts": ["308574457", "688199788"], + "manifest_version": 2 +} diff --git a/.local/share/Anki2/addons21/anki_reworked/meta.json b/.local/share/Anki2/addons21/anki_reworked/meta.json new file mode 100644 index 0000000..17e78d2 --- /dev/null +++ b/.local/share/Anki2/addons21/anki_reworked/meta.json @@ -0,0 +1 @@ +{"homepage": "https://github.com/shirajuki/anki-redesign", "name": "Anki-redesign", "conflicts": ["308574457", "688199788"], "mod": 1656801893, "min_point_version": 20, "max_point_version": 54, "branch_index": 0, "disabled": true, "config": {"addon_more_overview_stats": "True", "addon_advanced_review_bottom_bar": "True", "font": "3270Medium Nerd Font", "font_size": "12", "theme": "Catppuccin", "theme_reload": "True"}, "update_enabled": true}
\ No newline at end of file diff --git a/.local/share/Anki2/addons21/anki_reworked/themes/Anki.json b/.local/share/Anki2/addons21/anki_reworked/themes/Anki.json new file mode 100644 index 0000000..31a4818 --- /dev/null +++ b/.local/share/Anki2/addons21/anki_reworked/themes/Anki.json @@ -0,0 +1,230 @@ +{
+ "colors": {
+ "BORDER": [
+ "Border",
+ "#aaa",
+ "#777",
+ "--border"
+ ],
+ "BURIED_FG": [
+ "Buried Foreground",
+ "#aaaa33",
+ "#777733",
+ "--buried-fg"
+ ],
+ "BUTTON_BG": [
+ "Button Background",
+ "#eff0f1",
+ "#454545",
+ ""
+ ],
+ "CURRENT_DECK": [
+ "Selected Deck",
+ "#e7e7e7",
+ "#29292b",
+ "--current-deck"
+ ],
+ "DISABLED": [
+ "Disabled",
+ "#777",
+ "#777",
+ "--disabled"
+ ],
+ "FAINT_BORDER": [
+ "Faint Border",
+ "#e7e7e7",
+ "#29292b",
+ "--faint-border"
+ ],
+ "FLAG1_BG": [
+ "Flag1 (Browse Cards List)",
+ "#ff9b9b",
+ "#aa5555",
+ "--flag1-bg"
+ ],
+ "FLAG1_FG": [
+ "Flag1 (Browse Sidebar)",
+ "#e25252",
+ "#ff7b7b",
+ "--flag1-fg"
+ ],
+ "FLAG2_BG": [
+ "Flag2 (Browse Cards List)",
+ "#ffb347",
+ "#ac653a",
+ "--flag2-bg"
+ ],
+ "FLAG2_FG": [
+ "Flag2 (Browse Sidebar)",
+ "#ffb347",
+ "#f5aa41",
+ "--flag2-fg"
+ ],
+ "FLAG3_BG": [
+ "Flag3 (Browse Cards List)",
+ "#93e066",
+ "#559238",
+ "--flag3-bg"
+ ],
+ "FLAG3_FG": [
+ "Flag3 (Browse Sidebar)",
+ "#54c414",
+ "#86ce5d",
+ "--flag3-fg"
+ ],
+ "FLAG4_BG": [
+ "Flag4 (Browse Cards List)",
+ "#9dbcff",
+ "#506aa3",
+ "--flag4-bg"
+ ],
+ "FLAG4_FG": [
+ "Flag4 (Browse Sidebar)",
+ "#578cff",
+ "#6f9dff",
+ "--flag4-fg"
+ ],
+ "FLAG5_BG": [
+ "Flag5 (Browse Cards List)",
+ "#f5a8eb",
+ "#975d8f",
+ "--flag5-bg"
+ ],
+ "FLAG5_FG": [
+ "Flag5 (Browse Sidebar)",
+ "#ff82ee",
+ "#f097e4",
+ "--flag5-fg"
+ ],
+ "FLAG6_BG": [
+ "Flag6 (Browse Cards List)",
+ "#7edbd7",
+ "#399185",
+ "--flag6-bg"
+ ],
+ "FLAG6_FG": [
+ "Flag6 (Browse Sidebar)",
+ "#00d1b5",
+ "#5ccfca",
+ "--flag6-fg"
+ ],
+ "FLAG7_BG": [
+ "Flag7 (Browse Cards List)",
+ "#cca3f1",
+ "#624b77",
+ "--flag7-bg"
+ ],
+ "FLAG7_FG": [
+ "Flag7 (Browse Sidebar)",
+ "#9649dd",
+ "#9f63d3",
+ "--flag7-fg"
+ ],
+ "FRAME_BG": [
+ "Frame Background",
+ "white",
+ "#3a3a3a",
+ "--frame-bg"
+ ],
+ "HIGHLIGHT_BG": [
+ "Highlighted Background",
+ "#77ccff",
+ "#77ccff",
+ "--highlight-bg"
+ ],
+ "HIGHLIGHT_FG": [
+ "Highlighted Text",
+ "black",
+ "white",
+ "--highlight-fg"
+ ],
+ "LEARN_COUNT": [
+ "Learn Count",
+ "#c35617",
+ "#ff935b",
+ "--learn-count"
+ ],
+ "LINK": [
+ "Hyperlink",
+ "#00a",
+ "#77ccff",
+ "--link"
+ ],
+ "MARKED_BG": [
+ "Marked Background",
+ "#cce",
+ "#77c",
+ "--marked-bg"
+ ],
+ "MEDIUM_BORDER": [
+ "Medium Border",
+ "#b6b6b6",
+ "#444",
+ "--medium-border"
+ ],
+ "NEW_COUNT": [
+ "New Count",
+ "#00a",
+ "#77ccff",
+ "--new-count"
+ ],
+ "PRIMARY_COLOR": [
+ "Primary Color",
+ "#0093d0",
+ "#0093d0",
+ "--primary-color"
+ ],
+ "REVIEW_COUNT": [
+ "Review Count",
+ "#0a0",
+ "#5ccc00",
+ "--review-count"
+ ],
+ "SLIGHTLY_GREY_TEXT": [
+ "Switch Text",
+ "#333",
+ "#ccc",
+ "--slightly-grey-text"
+ ],
+ "SUSPENDED_BG": [
+ "Suspended Background",
+ "#ffffb2",
+ "#aaaa33",
+ "--suspended-bg"
+ ],
+ "SUSPENDED_FG": [
+ "Suspended Foreground",
+ "#dd0",
+ "#ffffb2",
+ "--suspended-fg"
+ ],
+ "TEXT_FG": [
+ "Text Foreground",
+ "black",
+ "white",
+ "--text-fg"
+ ],
+ "TOOLTIP_BG": [
+ "Tooltip Background",
+ "#fcfcfc",
+ "#272727",
+ "--tooltip-bg"
+ ],
+ "WINDOW_BG": [
+ "Window Background",
+ "#fafafa",
+ "#2f2f31",
+ "--window-bg"
+ ],
+ "ZERO_COUNT": [
+ "Zero Count",
+ "#ddd",
+ "#444",
+ "--zero-count"
+ ]
+ },
+ "version": {
+ "major": -1,
+ "minor": -1
+ }
+}
\ No newline at end of file diff --git a/.local/share/Anki2/addons21/anki_reworked/themes/Catppuccin.json b/.local/share/Anki2/addons21/anki_reworked/themes/Catppuccin.json new file mode 100644 index 0000000..39684d2 --- /dev/null +++ b/.local/share/Anki2/addons21/anki_reworked/themes/Catppuccin.json @@ -0,0 +1,230 @@ +{
+ "colors": {
+ "BORDER": [
+ "Border",
+ "#aaa",
+ "#434c5e",
+ "--border"
+ ],
+ "BURIED_FG": [
+ "Buried Foreground",
+ "#aaaa33",
+ "#777733",
+ "--buried-fg"
+ ],
+ "BUTTON_BG": [
+ "Button Background",
+ "#e7e7e7",
+ "#1A1826",
+ "--button-bg"
+ ],
+ "CURRENT_DECK": [
+ "Selected Deck",
+ "#e7e7e7",
+ "#1A1826",
+ "--current-deck"
+ ],
+ "DISABLED": [
+ "Disabled",
+ "#777",
+ "#C3BAC6",
+ "--disabled"
+ ],
+ "FAINT_BORDER": [
+ "Faint Border",
+ "#e7e7e7",
+ "#1A1826",
+ "--faint-border"
+ ],
+ "FLAG1_BG": [
+ "Flag1 (Browse Cards List)",
+ "#BF616A",
+ "#BF616A",
+ "--flag1-bg"
+ ],
+ "FLAG1_FG": [
+ "Flag1 (BrowseSidebar)",
+ "#BF616A",
+ "#BF616A",
+ "--flag1-fg"
+ ],
+ "FLAG2_BG": [
+ "Flag2 (Browse Cards List)",
+ "#D08770",
+ "#D08770",
+ "--flag2-bg"
+ ],
+ "FLAG2_FG": [
+ "Flag2 (Browse Sidebar)",
+ "#D08770",
+ "#D08770",
+ "--flag2-fg"
+ ],
+ "FLAG3_BG": [
+ "Flag3 (Browse Cards List)",
+ "#A3BE8C",
+ "#A3BE8C",
+ "--flag3-bg"
+ ],
+ "FLAG3_FG": [
+ "Flag3 (Browse Sidebar)",
+ "#A3BE8C",
+ "#A3BE8C",
+ "--flag3-fg"
+ ],
+ "FLAG4_BG": [
+ "Flag4 (Browse Cards List)",
+ "#5E81AC",
+ "#5E81AC",
+ "--flag4-bg"
+ ],
+ "FLAG4_FG": [
+ "Flag4 (Browse Sidebar)",
+ "#5E81AC",
+ "#5E81AC",
+ "--flag4-fg"
+ ],
+ "FLAG5_BG": [
+ "Flag5 (Browse Cards List)",
+ "#B48EAD",
+ "#B48EAD",
+ "--flag5-bg"
+ ],
+ "FLAG5_FG": [
+ "Flag5 (Browse Sidebar)",
+ "#B48EAD",
+ "#B48EAD",
+ "--flag5-fg"
+ ],
+ "FLAG6_BG": [
+ "Flag6 (Browse Cards List)",
+ "#7edbd7",
+ "#399185",
+ "--flag6-bg"
+ ],
+ "FLAG6_FG": [
+ "Flag6 (Browse Sidebar)",
+ "#00d1b5",
+ "#5ccfca",
+ "--flag6-fg"
+ ],
+ "FLAG7_BG": [
+ "Flag7 (Browse Cards List)",
+ "#cca3f1",
+ "#624b77",
+ "--flag7-bg"
+ ],
+ "FLAG7_FG": [
+ "Flag7 (Browse Sidebar)",
+ "#9649dd",
+ "#9f63d3",
+ "--flag7-fg"
+ ],
+ "FRAME_BG": [
+ "Frame Background",
+ "white",
+ "#1A1826",
+ "--frame-bg"
+ ],
+ "HIGHLIGHT_BG": [
+ "Highlighted Background",
+ "#77ccff",
+ "#81A1C1",
+ "--highlight-bg"
+ ],
+ "HIGHLIGHT_FG": [
+ "Highlighted Text",
+ "black",
+ "#ECEFF4",
+ "--highlight-fg"
+ ],
+ "LEARN_COUNT": [
+ "Learn Count",
+ "#c35617",
+ "#D08770",
+ "--learn-count"
+ ],
+ "LINK": [
+ "Hyperlink",
+ "#6e9fda",
+ "#89DCEB",
+ "--link"
+ ],
+ "MARKED_BG": [
+ "Marked Background",
+ "#cce",
+ "#ebcb8b",
+ "--marked-bg"
+ ],
+ "MEDIUM_BORDER": [
+ "Medium Border",
+ "#b6b6b6",
+ "#434C5E",
+ "--medium-border"
+ ],
+ "NEW_COUNT": [
+ "New Count",
+ "#00a",
+ "#5E81AC",
+ "--new-count"
+ ],
+ "PRIMARY_COLOR": [
+ "Primary Color",
+ "#0093d0",
+ "#0093d0",
+ "--primary-color"
+ ],
+ "REVIEW_COUNT": [
+ "Review Count",
+ "#0a0",
+ "#A3BE8C",
+ "--review-count"
+ ],
+ "SLIGHTLY_GREY_TEXT": [
+ "Switch Text",
+ "#333",
+ "#D8DEE9",
+ "--slightly-grey-text"
+ ],
+ "SUSPENDED_BG": [
+ "Suspended Background",
+ "#ffffb2",
+ "#161320",
+ "--suspended-bg"
+ ],
+ "SUSPENDED_FG": [
+ "Suspended Foreground",
+ "#dd0",
+ "#ECEFF4",
+ "--suspended-fg"
+ ],
+ "TEXT_FG": [
+ "Text Foreground",
+ "black",
+ "#d9e0ee",
+ "--text-fg"
+ ],
+ "TOOLTIP_BG": [
+ "Tooltip Background",
+ "#fcfcfc",
+ "#4C566A",
+ "--tooltip-bg"
+ ],
+ "WINDOW_BG": [
+ "Window Background",
+ "#fafafa",
+ "#1E1E2E",
+ "--window-bg"
+ ],
+ "ZERO_COUNT": [
+ "Zero Count",
+ "#ddd",
+ "#434C5E",
+ "--zero-count"
+ ]
+ },
+ "version": {
+ "major": 1,
+ "minor": 5
+ }
+}
\ No newline at end of file diff --git a/.local/share/Anki2/addons21/anki_reworked/themes/Nord.json b/.local/share/Anki2/addons21/anki_reworked/themes/Nord.json new file mode 100644 index 0000000..25c507b --- /dev/null +++ b/.local/share/Anki2/addons21/anki_reworked/themes/Nord.json @@ -0,0 +1,230 @@ +{
+ "colors": {
+ "BORDER": [
+ "Border",
+ "#D8DEE9",
+ "#434C5E",
+ "--border"
+ ],
+ "BURIED_FG": [
+ "Buried Foreground",
+ "#aaaa33",
+ "#777733",
+ "--buried-fg"
+ ],
+ "BUTTON_BG": [
+ "Button Background",
+ "#D8DEE9",
+ "#3B4252",
+ ""
+ ],
+ "CURRENT_DECK": [
+ "Selected Deck",
+ "#E5E9F0",
+ "#3B4252",
+ "--current-deck"
+ ],
+ "DISABLED": [
+ "Disabled",
+ "#D8DEE9",
+ "#D8DEE9",
+ "--disabled"
+ ],
+ "FAINT_BORDER": [
+ "Faint Border",
+ "#ECEFF4",
+ "#3B4252",
+ "--faint-border"
+ ],
+ "FLAG1_BG": [
+ "Flag1 (Browse Cards List)",
+ "#BF616A",
+ "#BF616A",
+ "--flag1-bg"
+ ],
+ "FLAG1_FG": [
+ "Flag1 (BrowseSidebar)",
+ "#BF616A",
+ "#BF616A",
+ "--flag1-fg"
+ ],
+ "FLAG2_BG": [
+ "Flag2 (Browse Cards List)",
+ "#D08770",
+ "#D08770",
+ "--flag2-bg"
+ ],
+ "FLAG2_FG": [
+ "Flag2 (Browse Sidebar)",
+ "#D08770",
+ "#D08770",
+ "--flag2-fg"
+ ],
+ "FLAG3_BG": [
+ "Flag3 (Browse Cards List)",
+ "#A3BE8C",
+ "#A3BE8C",
+ "--flag3-bg"
+ ],
+ "FLAG3_FG": [
+ "Flag3 (Browse Sidebar)",
+ "#A3BE8C",
+ "#A3BE8C",
+ "--flag3-fg"
+ ],
+ "FLAG4_BG": [
+ "Flag4 (Browse Cards List)",
+ "#5E81AC",
+ "#5E81AC",
+ "--flag4-bg"
+ ],
+ "FLAG4_FG": [
+ "Flag4 (Browse Sidebar)",
+ "#5E81AC",
+ "#5E81AC",
+ "--flag4-fg"
+ ],
+ "FLAG5_BG": [
+ "Flag5 (Browse Cards List)",
+ "#B48EAD",
+ "#B48EAD",
+ "--flag5-bg"
+ ],
+ "FLAG5_FG": [
+ "Flag5 (Browse Sidebar)",
+ "#B48EAD",
+ "#B48EAD",
+ "--flag5-fg"
+ ],
+ "FLAG6_BG": [
+ "Flag6 (Browse Cards List)",
+ "#7edbd7",
+ "#399185",
+ "--flag6-bg"
+ ],
+ "FLAG6_FG": [
+ "Flag6 (Browse Sidebar)",
+ "#00d1b5",
+ "#5ccfca",
+ "--flag6-fg"
+ ],
+ "FLAG7_BG": [
+ "Flag7 (Browse Cards List)",
+ "#cca3f1",
+ "#624b77",
+ "--flag7-bg"
+ ],
+ "FLAG7_FG": [
+ "Flag7 (Browse Sidebar)",
+ "#9649dd",
+ "#9f63d3",
+ "--flag7-fg"
+ ],
+ "FRAME_BG": [
+ "Frame Background",
+ "#E5E9F0",
+ "#3B4252",
+ "--frame-bg"
+ ],
+ "HIGHLIGHT_BG": [
+ "Highlighted Background",
+ "#81A1C1",
+ "#81A1C1",
+ "--highlight-bg"
+ ],
+ "HIGHLIGHT_FG": [
+ "Highlighted Text",
+ "white",
+ "#ECEFF4",
+ "--highlight-fg"
+ ],
+ "LEARN_COUNT": [
+ "Learn Count",
+ "#D08770",
+ "#D08770",
+ "--learn-count"
+ ],
+ "LINK": [
+ "Hyperlink",
+ "#8FBCBB",
+ "#8FBCBB",
+ "--link"
+ ],
+ "MARKED_BG": [
+ "Marked Background",
+ "#EBCB8B",
+ "#EBCB8B",
+ "--marked-bg"
+ ],
+ "MEDIUM_BORDER": [
+ "Medium Border",
+ "#E5E9F0",
+ "#434C5E",
+ "--medium-border"
+ ],
+ "NEW_COUNT": [
+ "New Count",
+ "#5E81AC",
+ "#5E81AC",
+ "--new-count"
+ ],
+ "PRIMARY_COLOR": [
+ "Primary Color",
+ "#0093d0",
+ "#0093d0",
+ "--primary-color"
+ ],
+ "REVIEW_COUNT": [
+ "Review Count",
+ "#A3BE8C",
+ "#A3BE8C",
+ "--review-count"
+ ],
+ "SLIGHTLY_GREY_TEXT": [
+ "Switch Text",
+ "#3B4252",
+ "#D8DEE9",
+ "--slightly-grey-text"
+ ],
+ "SUSPENDED_BG": [
+ "Suspended Background",
+ "#88C0D0",
+ "#88C0D0",
+ "--suspended-bg"
+ ],
+ "SUSPENDED_FG": [
+ "Suspended Foreground",
+ "#3B4252",
+ "#ECEFF4",
+ "--suspended-fg"
+ ],
+ "TEXT_FG": [
+ "Text Foreground",
+ "#2E3440",
+ "#E5E9F0",
+ "--text-fg"
+ ],
+ "TOOLTIP_BG": [
+ "Tooltip Background",
+ "#D8DEE9",
+ "#4C566A",
+ "--tooltip-bg"
+ ],
+ "WINDOW_BG": [
+ "Window Background",
+ "white",
+ "#2E3440",
+ "--window-bg"
+ ],
+ "ZERO_COUNT": [
+ "Zero Count",
+ "#D8DEE9",
+ "#434C5E",
+ "--zero-count"
+ ]
+ },
+ "version": {
+ "major": 1,
+ "minor": 5
+ }
+}
\ No newline at end of file diff --git a/.local/share/Anki2/addons21/anki_reworked/themes/readme.txt b/.local/share/Anki2/addons21/anki_reworked/themes/readme.txt new file mode 100644 index 0000000..43f5cc1 --- /dev/null +++ b/.local/share/Anki2/addons21/anki_reworked/themes/readme.txt @@ -0,0 +1,8 @@ +To create your own customized themes
+make a copy of Anki.json, name and edit it according
+to your wishes.
+
+This addon is also compatible with the themes of ReColor,
+which means that the uploaded community themes can also
+be downloaded and used:
+- https://github.com/AnKingMed/AnkiRecolor/wiki/Themes
diff --git a/.local/share/Anki2/addons21/anki_reworked/user_files/readme.txt b/.local/share/Anki2/addons21/anki_reworked/user_files/readme.txt new file mode 100644 index 0000000..f612d51 --- /dev/null +++ b/.local/share/Anki2/addons21/anki_reworked/user_files/readme.txt @@ -0,0 +1,11 @@ +To create your own customized style design, +copy over the css files from ./files to ./user_files +you would like to edit. + +CSS files starting with "Q" indicates that the syntax is of +Qt CSS, which is in it's core quite similar to the usual CSS. +Documentation here: +- https://doc.qt.io/qt-5/stylesheet-syntax.html + +User edited themes should that is also customized will be saved +in this folder. diff --git a/.local/share/Anki2/addons21/anki_reworked/utils/css_files.py b/.local/share/Anki2/addons21/anki_reworked/utils/css_files.py new file mode 100644 index 0000000..0b88564 --- /dev/null +++ b/.local/share/Anki2/addons21/anki_reworked/utils/css_files.py @@ -0,0 +1,37 @@ +import os +from aqt import mw + +mw.addonManager.setWebExports(__name__, r"files/.*\.(css|svg|gif|png)|user_files/.*\.(css|svg|gif|png)") +addon_package = mw.addonManager.addonFromModule(__name__) + +this_script_dir = os.path.join(os.path.dirname(__file__), "..") +files_dir = os.path.join(this_script_dir, 'files') +user_files_dir = os.path.join(this_script_dir, 'user_files') +css_files_dir = { + 'BottomBar': f"/_addons/{addon_package}/files/BottomBar.css", + 'CardLayout': f"/_addons/{addon_package}/files/CardLayout.css", + 'DeckBrowser': f"/_addons/{addon_package}/files/DeckBrowser.css", + 'Editor': f"/_addons/{addon_package}/files/Editor.css", + 'global': f"/_addons/{addon_package}/files/global.css", + 'legacy': f"/_addons/{addon_package}/files/legacy.css", + 'Overview': f"/_addons/{addon_package}/files/Overview.css", + 'QAbout': os.path.join(files_dir, 'QAbout.css'), + 'QAddCards': os.path.join(files_dir, 'QAddCards.css'), + 'QAddonsDialog': os.path.join(files_dir, 'QAddonsDialog.css'), + 'QBrowser': os.path.join(files_dir, 'QBrowser.css'), + 'QFilteredDeckConfigDialog': os.path.join(files_dir, 'QFilteredDeckConfigDialog.css'), + 'QEditCurrent': os.path.join(files_dir, 'QEditCurrent.css'), + 'QNewDeckStats': os.path.join(files_dir, 'QNewDeckStats.css'), + 'QPreferences': os.path.join(files_dir, 'QPreferences.css'), + 'Reviewer': f"/_addons/{addon_package}/files/Reviewer.css", + 'ReviewerBottomBar': f"/_addons/{addon_package}/files/ReviewerBottomBar.css", + 'TopToolbar': f"/_addons/{addon_package}/files/TopToolbar.css", +} +# Replace pathing for user customised styled files +for file in os.listdir(user_files_dir): + file = file.replace(".css", "") + if css_files_dir.get(file, "") != "": + if file.startswith("Q"): + css_files_dir[file] = os.path.join(user_files_dir, file+'.css') + else: + css_files_dir[file] = f"/_addons/{addon_package}/user_files/{file}.css" diff --git a/.local/share/Anki2/addons21/anki_reworked/utils/logger.py b/.local/share/Anki2/addons21/anki_reworked/utils/logger.py new file mode 100644 index 0000000..3c67988 --- /dev/null +++ b/.local/share/Anki2/addons21/anki_reworked/utils/logger.py @@ -0,0 +1,17 @@ +import os +import logging +# declare an empty logger class +class EmptyLogger(): + def debug(self, *_): + return None +logger = EmptyLogger() +# init logger +if 'ANKI_REDESIGN_DEBUG_LOGGING' in os.environ: + filename = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "user_files", "test.log") + logging.basicConfig(format='%(asctime)s %(levelname)-8s [%(filename)s:%(lineno)d] %(message)s', + datefmt='%Y%m%d-%H:%M:%S', + filename=filename, + level=logging.DEBUG) + logger = logging.getLogger('anki-redesign') + logger.setLevel(logging.DEBUG) + logger.debug("Initialized anki") diff --git a/.local/share/Anki2/addons21/anki_reworked/utils/modules.py b/.local/share/Anki2/addons21/anki_reworked/utils/modules.py new file mode 100644 index 0000000..fc55a12 --- /dev/null +++ b/.local/share/Anki2/addons21/anki_reworked/utils/modules.py @@ -0,0 +1,18 @@ +def module_exists(module_name): + try: + __import__(module_name) + except ImportError: + return False + else: + return True + +def module_has_attribute(module_name, attribute): + if module_exists(module_name): + return hasattr(__import__(module_name), attribute) + return False + +def attribute_exists(object, attribute): + return attribute in object.__dict__ + +def context_name_includes(context, classname): + return classname in str(context.__class__)
\ No newline at end of file diff --git a/.local/share/Anki2/addons21/anki_reworked/utils/themes.py b/.local/share/Anki2/addons21/anki_reworked/utils/themes.py new file mode 100644 index 0000000..36af2d9 --- /dev/null +++ b/.local/share/Anki2/addons21/anki_reworked/utils/themes.py @@ -0,0 +1,34 @@ +import os +import json +from aqt import mw +from .logger import logger + +this_script_dir = os.path.join(os.path.dirname(__file__), "..") +themes_dir = os.path.join(this_script_dir, 'themes') + +def get_themes_dict() -> dict: + # Replace pathing for theme files (ReColor compatible) + themes = {} + for file in os.listdir(themes_dir): + if "json" in file: + file = file.replace(".json", "") + if themes.get(file, "") == "": + themes[file] = os.path.join(themes_dir, file+'.json') + return themes + +def get_theme(theme: str) -> dict: + themes_parsed = json.loads(open(themes[theme], encoding='utf-8').read()) + theme_colors = themes_parsed.get("colors") + # Add extra color_keys on theme files if not exist (ReColor compatible) + if not theme_colors.get("PRIMARY_COLOR", False): + theme_colors["PRIMARY_COLOR"] = ["Primary Color", "#0093d0", "#0093d0", "--primary-color"] + if not theme_colors.get("FOCUS_SHADOW", False): + theme_colors["FOCUS_SHADOW"] = ["Focus Shadow", "#ff93d0", "#0093d0", "--focus-shadow-color"] + themes_parsed["colors"] = theme_colors + return themes_parsed + +def write_theme(file, theme_content): + with open(file, "w") as f: + json.dump(theme_content, f, indent=2, sort_keys=True) + +themes = get_themes_dict() diff --git a/.local/share/Anki2/addons21/button_colors/__init__.py b/.local/share/Anki2/addons21/button_colors/__init__.py new file mode 100644 index 0000000..90a2f8c --- /dev/null +++ b/.local/share/Anki2/addons21/button_colors/__init__.py @@ -0,0 +1,32 @@ +from aqt import mw
+from aqt import gui_hooks
+
+def buttonColours(buttons_tuple, reviewer, card):
+ config = mw.addonManager.getConfig(__name__)
+ button_count = mw.col.sched.answerButtons(card)
+ colours = config['colours'].get(str(button_count) + ' answers')
+
+ # if coulours found in config
+ if colours:
+
+ # Create new list of coloured buttons
+ coloured_buttons = []
+ for button in buttons_tuple:
+ text = button[1]
+
+ # See if colour exists else paint black
+ try:
+ colour = colours[button[0] - 1]
+ except IndexError:
+ colour = "black"
+
+ # Add colour to button
+ font = "<font color='{}'>{}</font>".format(colour, text)
+
+ coloured_buttons.append((button[0], font))
+
+ return tuple(coloured_buttons)
+ else:
+ return buttons_tuple
+
+gui_hooks.reviewer_will_init_answer_buttons.append(buttonColours)
\ No newline at end of file diff --git a/.local/share/Anki2/addons21/button_colors/config.json b/.local/share/Anki2/addons21/button_colors/config.json new file mode 100644 index 0000000..5fe0df2 --- /dev/null +++ b/.local/share/Anki2/addons21/button_colors/config.json @@ -0,0 +1,7 @@ +{
+ "colours": {
+ "2 answers": ["red", "green"],
+ "3 answers": ["red", "green", "blue"],
+ "4 answers": ["red", "orange", "green", "blue"]
+ }
+}
diff --git a/.local/share/Anki2/addons21/button_colors/meta.json b/.local/share/Anki2/addons21/button_colors/meta.json new file mode 100644 index 0000000..4f2dbe1 --- /dev/null +++ b/.local/share/Anki2/addons21/button_colors/meta.json @@ -0,0 +1 @@ +{"name": "Button Colours Good Again", "mod": 1617721040, "min_point_version": 35, "max_point_version": 42, "branch_index": 1, "disabled": false}
\ No newline at end of file diff --git a/.local/share/Anki2/addons21/keybindings/__init__.py b/.local/share/Anki2/addons21/keybindings/__init__.py new file mode 100644 index 0000000..05b9932 --- /dev/null +++ b/.local/share/Anki2/addons21/keybindings/__init__.py @@ -0,0 +1,2 @@ +from . import custom_shortcuts +from . import cs_functions diff --git a/.local/share/Anki2/addons21/keybindings/config.json b/.local/share/Anki2/addons21/keybindings/config.json new file mode 100644 index 0000000..892e614 --- /dev/null +++ b/.local/share/Anki2/addons21/keybindings/config.json @@ -0,0 +1,152 @@ +{ + "main debug":"Ctrl+:", + "main deckbrowser":"D", + "main study":"S", + "main add":"A", + "main browse":"B", + "main stats":"T", + "main sync":"Y", + "editor card layout":"Ctrl+L", + "editor bold":"Ctrl+B", + "editor italic":"Ctrl+I", + "editor underline":"Ctrl+U", + "editor superscript":"Ctrl++", + "editor subscript":"Ctrl+=", + "editor remove format":"Ctrl+R", + "editor foreground":"F7", + "editor change col":"F8", + "editor change deck":"<nop>", + "editor cloze":"Ctrl+Shift+C", + "editor cloze alt":"Ctrl+Shift+Alt+C", + "editor cloze forced increment":"<nop>", + "editor cloze no increment":"<nop>", + "editor add media":"F3", + "editor record sound":"F5", + "editor insert latex":"Ctrl+T, T", + "editor insert latex equation":"Ctrl+T, E", + "editor insert latex math environment":"Ctrl+T, M", + "editor insert mathjax inline":"Ctrl+M, M", + "editor insert mathjax block":"Ctrl+M, E", + "editor insert mathjax chemistry":"Ctrl+M, C", + "editor html edit":"Ctrl+Shift+X", + "editor focus tags":"Ctrl+Shift+T", + "editor confirm add card":"Ctrl+Return", + "editor add card close window":"<default>", + "editor change note type":"Ctrl+N", + "editor toggle sticky current":"F9", + "editor toggle sticky all":"Shift+F9", + "editor block indent": "<nop>", + "editor block outdent": "<nop>", + "editor list insert ordered": "<nop>", + "editor list insert unordered": "<nop>", + "editor _extras": { + "paste custom text":"<nop>" + }, + "editor _pastes": { + }, + "editor _duplicates": { + }, + "reviewer edit current":"E", + "reviewer flip card 1":" ", + "reviewer flip card 2":"Qt.Key_Return", + "reviewer flip card 3":"Qt.Key_Enter", + "reviewer replay audio 1":"r", + "reviewer replay audio 2":"F5", + "reviewer set flag 1":"Ctrl+1", + "reviewer set flag 2":"Ctrl+2", + "reviewer set flag 3":"Ctrl+3", + "reviewer set flag 4":"Ctrl+4", + "reviewer set flag 5":"Ctrl+5", + "reviewer set flag 6":"Ctrl+6", + "reviewer set flag 7":"Ctrl+7", + "reviewer set flag 0":"Ctrl+0", + "reviewer mark card":"*", + "reviewer bury note":"=", + "reviewer bury card":"-", + "reviewer suspend note":"!", + "reviewer suspend card":"@", + "reviewer delete note":"Ctrl+Delete", + "reviewer play recorded voice":"v", + "reviewer record voice":"Shift+v", + "reviewer set due date":"Ctrl+Shift+D", + "reviewer options menu":"o", + "reviewer choice 1":"1", + "reviewer choice 2":"2", + "reviewer choice 3":"3", + "reviewer choice 4":"4", + "reviewer pause audio":"5", + "reviewer seek backward":"6", + "reviewer seek forward":"7", + "reviewer card info":"I", + "reviewer previous card info":"Alt+I", + "reviewer _duplicates":{ + }, + "reviewer more options":"M", + "m_toolbox quit":"Ctrl+Q", + "m_toolbox preferences":"Ctrl+P", + "m_toolbox undo":"Ctrl+Z", + "m_toolbox see documentation":"F1", + "m_toolbox switch profile":"Ctrl+Shift+P", + "m_toolbox export":"Ctrl+E", + "m_toolbox import":"Ctrl+Shift+I", + "m_toolbox study":"/", + "m_toolbox create filtered deck":"F", + "m_toolbox addons":"Ctrl+Shift+A", + "m_toolbox _duplicates":{ + }, + "window_browser preview": "Ctrl+Shift+P", + "window_browser reschedule":"Ctrl+Shift+D", + "window_browser select all":"Ctrl+Alt+A", + "window_browser undo":"Ctrl+Alt+Z", + "window_browser invert selection":"Ctrl+Alt+S", + "window_browser find":"Ctrl+F", + "window_browser goto note":"Ctrl+Shift+N", + "window_browser goto next note":"Ctrl+N", + "window_browser goto previous note":"Ctrl+P", + "window_browser guide":"F1", + "window_browser change note type":"Ctrl+Shift+M", + "window_browser find and replace":"Ctrl+Alt+F", + "window_browser filter":"Ctrl+Shift+F", + "window_browser goto card list":"Ctrl+Shift+L", + "window_browser reposition":"Ctrl+Shift+S", + "window_browser first card":"Home", + "window_browser last card":"End", + "window_browser close":"Ctrl+W", + "window_browser info":"Ctrl+Shift+I", + "window_browser add tag":"Ctrl+Shift+A", + "window_browser remove tag":"Ctrl+Alt+Shift+A", + "window_browser suspend":"Ctrl+J", + "window_browser delete":"Ctrl+Del", + "window_browser add note":"Ctrl+E", + "window_browser change deck":"Ctrl+D", + "window_browser flag_red":"Ctrl+1", + "window_browser flag_orange":"Ctrl+2", + "window_browser flag_green":"Ctrl+3", + "window_browser flag_blue":"Ctrl+4", + "window_browser goto sidebar":"Ctrl+Shift+R", + "window_browser toggle mark":"Ctrl+K", + "window_browser clear unused tags":"<nop>", + "window_browser find duplicates":"<nop>", + "window_browser select notes":"<nop>", + "window_browser manage note types":"<nop>", + "window_browser save current filter":"<nop>", + "window_browser remove current filter":"<nop>", + "window_browser sidebar search": "Alt+1", + "window_browser sidebar select": "Alt+2", + "window_browser forget card": "Ctrl+Alt+N", + "window_browser _filters":{ + "_concat":{}, + "_orConcat":{} + }, + "Ω enable main": "y", + "Ω enable editor": "y", + "Ω enable reviewer": "y", + "Ω enable m_toolbox": "y", + "Ω enable window_browser": "y", + "Ω enable conflict warning": "y", + "Ω custom paste text": "", + "Ω custom paste extra texts": { + }, + "Ω custom paste end style": "n" +} + diff --git a/.local/share/Anki2/addons21/keybindings/config.md b/.local/share/Anki2/addons21/keybindings/config.md new file mode 100644 index 0000000..eca22df --- /dev/null +++ b/.local/share/Anki2/addons21/keybindings/config.md @@ -0,0 +1,309 @@ +### To disable a shortcut, set that shortcut to `<nop>`. + +### For macOS users, `Ctrl` is `⌘`, `Alt` is `⌥`, and `Meta` is `^` (control key). + + +## Editor Options + +"editor \_duplicates": Takes functions and binds them to new shortcuts. + +This object takes inputs of the form "(function keyword)":"(shortcut)", separated by commas. (e.g. {"editor bold":"Ctrl+Shift+9", "editor cloze":"Alt+Shift+2"}) + +If you want to set more than one duplicate per shortcut for the reviewer, you can do so by adding the suffix "+++" *immediately* after the shortcut name followed by any distinct string. (e.g. {"editor bold+++ first": "Ctrl+Shift+0", "editor bold+++ 2nd": "Ctrl+Shift+8"}) + +All the keywords are exactly the same as the keywords used in the json file, making the lines copy-pastable. + +"editor add media": Add external media + +"editor add card close window": In the Add Card dialog, closes the given window. For some reason, Macs have a default shortcut for this while all other OSes don't. Since the default is OS dependent, it is given to be `<default>` rather than any specified value. + +"editor block indent": Indents the active field. Useful for indenting lists. + +"editor block outdent": Outdents (unindents) the active field. Useful for unindenting lists. + +"editor bold": Toggle bold + +"editor card layout": Change Card Layout + +"editor change col": Change text color + +*Note that this shortcut currently exclusively uses the legacy version of color editing. This is incompatible with the new color editing as of Anki 2.1.49. If you don't want to use the old version, disable this shortcut by setting it to <nop>.* + +"editor cloze": Insert cloze (increments cloze ID if `Alt` is *not* part of your keybind, so `Ctrl+Shift+C` does increment ID, while `Ctrl+Shift+Alt+C` does not) + +"editor cloze alt": Insert cloze (behaves identically to "editor cloze") + +"editor cloze forced increment": Insert cloze, **always increments the cloze ID number**, *does not activate cloze add-ons* + +"editor cloze no increment": Insert cloze, **never increments the cloze ID number**, *does not activate cloze add-ons* + +The reason for the seemingly weird editor cloze behavior is Anki's internal implementation of the cloze insertion shortcuts. Anki's implementation is used in "editor cloze" and "editor cloze alt" and should play well with other addons, while a different implementation is used for "forced increment" and "no increment". + +"editor change note type": Change the type of the given note + +"editor confirm add card": In the add card editing window, adds the card + +"editor focus tags": Switch focus to the Tags field + +"editor foreground": Set foreground color + +*Note that this shortcut currently exclusively uses the legacy version of color editing. This is incompatible with the new color editing as of Anki 2.1.49. If you don't want to use the old version, disable this shortcut by setting it to <nop>.* + +"editor html edit": Edit the card's HTML + +"editor insert latex": Insert LaTeX formatted text + +"editor insert latex equation": Insert a LaTeX equation + +"editor insert latex math environment": Insert a LaTeX math environment + +"editor insert mathjax block": Insert a MathJax block + +"editor insert mathjax chemistry": Insert a chemistry thing in MathJax + +"editor insert mathjax inline": Insert an inline MathJax expresesion + +"editor list insert ordered": Put an ordered list into the active field. +k +"editor list insert unordered": Put an unordered list into the active field. + +"editor italic": Toggle italics + +"editor record sound": Record sound + +"editor remove format": Remove card formatting + +"editor subscript": Toggle subscript + +"editor superscript": Toggle superscript + +"editor toggle sticky all": *Anki 2.1.45+* Toggle the stickiness of all fields. "Toggle all" is interpreted the same was as in vanilla Anki: If any field is sticky, turn all stickies off. Otherwise, turn all stickies on. + +"editor toggle sticky current": *Anki 2.1.45+* Toggle the stickiness of the current field. + +*In the current implementation for toggling stickies, setting this to something other than the default will lose you the ability to click to toggle the sticky pins. If you want to retain this ability, using a duplicate `"editor toggle sticky current"` will still allow you to click pins.* + +"editor underline": Toggle underline + +**In the future, these shortcuts may be removed and put into a new add-on, as they are not part of Anki's default functionality.** + +Within "editor \_extras": + +"paste custom text": Pastes a custom piece of html text into a card field (defined in "Ω custom paste text") + +"editor \_pastes": Same functionality as paste custom text, but allows any number of texts with any label (texts are defined in "Ω custom paste extra texts", labels must match) + +e.g. `"editor _pastes": {"dashes":"Ctrl+Shift+5", "dots":"Ctrl+Shift+6"}` matched with `"Ω custom paste extra texts": {"dashes":"<b>--</b>","dots":". . ."}` pastes the corresponding text with the corresponding label + +## Main Toolbox Options + +"m\_toolbox \_duplicates": Takes functions and binds them to new shortcuts. + +This object takes inputs of the form "(function keyword)":"(shortcut)", separated by commas. (e.g. {"m\_toolbox undo":"u","m\_toolbox study":"9"}) + +All the keywords are exactly the same as the keywords used in the json file, making the lines copy-pastable. + +If you want to set more than one duplicate per shortcut for the reviewer, you can do so by adding the suffix "+++" *immediately* after the shortcut name followed by any distinct string. (e.g. {"m\_toolbox study+++ first": "6", "m\_toolbox study+++ 2nd": "V"}) + +"m_toolbox addons": Go to the addons window + +"m_toolbox create filtered deck": Create a filtered deck + +"m_toolbox export": Export the user's decks + +"m_toolbox import": Import a deck file (.apkg, etc.) + +"m_toolbox preferences": Go to the user preferences window + +"m_toolbox quit": Quit Anki + +"m_toolbox see documentation": Go to the Anki manual + +"m_toolbox study": Start studying the selected deck + +"m_toolbox switch profile": Switch user profiles + +"m_toolbox undo": Undo the **last main window (reviewer)** action + +## Home Options + +**NOTE: Setting these to "Ctrl+:", "d", "s", "a", "b", "t", or "y" will not work if they are not the default setting for that function.** + +"main add": Add new card + +"main browse": Enter card browser + +"main debug": Open debug screen + +"main deckbrowser": Go back to home screen + +"main stats": Enter stats screen + +"main study": Study current deck + +"main sync": Synchronize with AnkiWeb + +## Reviewer Options + +"reviewer \_duplicates": Takes functions and binds them to new shortcuts. + +This object takes inputs of the form "(function keyword)":"(shortcut)", separated by commas. (e.g. {"reviewer mark card":"]","reviewer flip card 1":"-"}) + +All the keywords are exactly the same as the keywords used in the json file, making the lines copy-pastable. (Those who want to remove the numbers from stuff like "reviewer flip card" can do so as well) + +If you want to set more than one duplicate per shortcut for the reviewer, you can do so by adding the suffix "+++" *immediately* after the shortcut name followed by any distinct string. (e.g. {"reviewer flip card 3+++ first": "l", "reviewer flip card 3+++ 2nd": "t"}) + +**Make sure to remap keys to empty keyspace.** + +"reviewer bury card": Bury this card + +"reviewer bury note": Bury this note (card and associated cards) + +"reviewer card info": Display info about this card + +"reviewer choice [1234]": Select 1/2/3/4 (Again/Hard/Good/Easy or Again/Good/Easy) + +"reviewer delete note": Delete this note + +"reviewer edit current": Edit the card currently being reviewed + +"reviewer flip card [123]": Flip the card to see the answer + +"reviewer mark card": Mark this card with a star + +"reviewer more options": Display the menu showing other review options for this card (e.g. flagging the card, suspend/delete/bury, playing audio, etc.) + +"reviewer options menu": Go to the review options menu + +"reviewer pause audio": Pause the audio being played + +"reviewer play recorded voice": If there is a recorded voice, play it + +"reviewer previous card info": Display info about the previous card + +"reviewer record voice": Record your voice + +"reviewer replay audio [12]": Replay audio attached to the card + +"reviewer set due date": Reschedules a card in the review schedule in Anki 2.1.41+ + +"reviewer set flag [12345670]": Set a flag on this card (or none for 0), changing colors depending on the number (1/2/3/4, and for 2.1.45+, 5/6/7 as well) + +"reviewer seek backward": Rewind the audio 5 seconds + +"reviewer seek forward": More the audio forward 5 seconds + +"reviewer suspend card": Suspend this card + +"reviewer suspend note": Suspend this note + +## Browser Window Options + +"window\_browser \_filters": Auto-fills the search bar of the browser with the given text. Can be used for filters such as current deck (`deck:current`) or cards due for review (`is:due`) + +The syntax for this is: `"(filter name): (shortcut)"`, though one may need to escape quotes with `\` e.g. `"deck: Something something"` becomes `\"deck: Something something\"` + +Sub-objects within `_filters`: + +"\_concat": Instead of replacing text in the search bar, adds the text to the end of the search bar + +"\_orConcat": Adds the "or" + the text to the end of the search bar ("or" acts like logical or for searches) + +"window_browser add card": Adds a new card (goes to the add window) + +"window_browser add tag": Adds a tag to the selected card + +"window_browser change deck": Changes the deck of the selected card + +"window_browser change note type": Changes the note type of the selected card + +"window_browser clear flags": Removes all flags from a card + +"window_browser clear unused tags": *Not in vanilla Anki*, Removes all unused tags + +"window_browser close": Closes the browser + +"window_browser delete": Deletes the selected card + +"window_browser filter": Adds filters to a search + +"window_browser find": Finds a pattern + +"window_browser find duplicates": *Not in vanilla Anki*, Finds cards with the same fields + +"window_browser find and replace": Finds a pattern and replaces it with another pattern + +"window_browser first card": Selects only the first card in the list + +"window_browser flag_blue": Toggles the blue flag + +"window_browser flag_green": Toggles the green flag + +"window_browser flag_purple": Toggles the purple flag + +"window_browser flag_red": Toggles the red flag + +"window_browser forget card": Forgets the selected card (sets the card as new) + +"window_browser goto card list": Switches focus to the card list + +"window_browser goto next note": Selects the note after the selected note in the list + +"window_browser goto note": Switches focus to the note fields + +"window_browser goto previous note": Selects the note before the selected note + +"window_browser goto sidebar": Goes to the sidebar of decks/note types + +"window_browser guide": Opens the browser guide in the default browser + +"window_browser info": Shows info of the selected card + +"window_browser invert selection": Inverts the selection of cards + +"window_browser last card": Goes to the last card on the list + +"window_browser manage note types": *Not in vanilla Anki*, Goes to the note type management window + +"window_browser preview": Emulates what the card will look like during review + +"window_browser remove current filter": *Not in vanilla Anki*, Removes the most recently used filter previously saved to the sidebar + +"window_browser remove tag": Removes tags from a card + +"window_browser reposition": Repositions a new card in the new card queue + +"window_browser reschedule": Reschedules a card in the review schedule, named "set due date" in Anki 2.1.41+ + +"window_browser save current filter": *Not in vanilla Anki*, Saves the current filter to the sidebar (and lets you name it) + +"window_browser select all": Selects all cards + +"window_browser select notes": *Not in vanilla Anki*, Selects only the current notes + +"window_browser sidebar search": *Useful only in Anki 2.1.45+* Sets the sidebar to initialize a search of the selected item in the sidebar + +"window_browser sidebar select": *Useful only in Anki 2.1.45+* Sets the sidebar to just select the item in the sidebar + +"window_browser suspend": Suspends the selected cards + +"window_browser toggle mark": Toggles the mark on the selected cards + +"window_browser undo": Undoes the latest **browser** action (**m\_toolbox undo** undoes **reviewer** actions) + +## Add-on Preferences + +**These options are for changing a few settings on the add-on itself.** + +"Ω custom paste end style": For the exceptionally lazy (like me). If set to "y", inserts a `</span>` and a zero-width space at the end of the custom text to stop the custom style from bleeding into new text. + +Otherwise, the custom paste will behave exactly like regular paste. + +"Ω custom paste text": Controls what html will be pasted by "custom paste" in "editor \_extras" +e.g. `"Ω custom paste text": "<span style=\"font-size: 20px; color:#3399ff\">◆</span>"` + +"Ω custom paste extra texts": Controls what html will be pasted by the correspondingly labeled paste in "editor \_pastes" (See "editor \_pastes" for an example) + +"Ω enable main/window_browser/editor/etc.": If set to "n", doesn't enable the corresponding set of shortcuts for the respective functions (useful for addon compatability in a pinch) + +"Ω enable conflict warning": If set to "y", shows a warning window whenever two shortcuts of the same type are set to the same key. diff --git a/.local/share/Anki2/addons21/keybindings/cs_functions.py b/.local/share/Anki2/addons21/keybindings/cs_functions.py new file mode 100644 index 0000000..2232393 --- /dev/null +++ b/.local/share/Anki2/addons21/keybindings/cs_functions.py @@ -0,0 +1,231 @@ +import re +import anki +from aqt.utils import tooltip, showInfo +try: + from aqt.utils import ( + HelpPage, + TR, + tr, + ) +except: + pass +from aqt.qt import * +from aqt import mw +try: + from aqt.operations.card import set_card_deck +except: + pass + + +def get_version(): + """Return the integer subversion of Anki on which the addon is run ("2.1.11" -> 11)""" + return int(anki.version.split('.')[2]) + +def cs_editor_on_alt_cloze(self): + self.saveNow(self.cs_u_onAltCloze, keepFocus=True) + +def cs_editor_on_std_cloze(self): + self.saveNow(self.cs_u_onStdCloze, keepFocus=True) + +def cs_editor_generate_cloze(self, altModifier = False): + # check that the model is set up for cloze deletion + if not re.search('{{(.*:)*cloze:',self.note.model()['tmpls'][0]['qfmt']): + if self.addMode: + tooltip(_("Warning, cloze deletions will not work until " + "you switch the type at the top to Cloze.")) + else: + showInfo(_("""\ +To make a cloze deletion on an existing note, you need to change it \ +to a cloze type first, via Edit>Change Note Type.""")) + return + # find the highest existing cloze + highest = 0 + for name, val in list(self.note.items()): + m = re.findall(r"\{\{c(\d+)::", val) + if m: + highest = max(highest, sorted([int(x) for x in m])[-1]) + # reuse last? + if not altModifier: + highest += 1 + highest = max(1, highest) + self.web.eval("wrap('{{c%d::', '}}');" % highest) + +#If the shortcut has "+++" in it for multiple duplications, +#Truncate the shortcut from that point to get the original name +def normalizeShortcutName(scut): + prefix_idx = scut.find('+++') + if scut.find('+++') != -1: + # If the multiple duplicates "+++" is found, + # truncate the shortcut to the proper name + scut = scut[:prefix_idx] + return scut + +#Converts json shortcuts into functions for the reviewer +#sToF: shortcutToFunction +def review_sToF(self,scut): + + #"reviewer" is retained for copy-pastability, may be removed later + #"self.mw.onEditCurrent" is exactly how it was in reviewer.py, DO NOT CHANGE + sdict = { + "reviewer edit current": self.mw.onEditCurrent, + "reviewer flip card": self.onEnterKey, + "reviewer flip card 1": self.onEnterKey, + "reviewer flip card 2": self.onEnterKey, + "reviewer flip card 3": self.onEnterKey, + "reviewer options menu": self.onOptions, + "reviewer record voice": self.onRecordVoice, + "reviewer play recorded voice": self.onReplayRecorded, + "reviewer play recorded voice 1": self.onReplayRecorded, + "reviewer play recorded voice 2": self.onReplayRecorded, + "reviewer delete note": self.onDelete, + "reviewer suspend card": self.onSuspendCard, + "reviewer suspend note": self.onSuspend, + "reviewer bury card": self.onBuryCard, + "reviewer bury note": self.onBuryNote, + "reviewer mark card": self.onMark, + "reviewer set flag 1": lambda: self.setFlag(1), + "reviewer set flag 2": lambda: self.setFlag(2), + "reviewer set flag 3": lambda: self.setFlag(3), + "reviewer set flag 4": lambda: self.setFlag(4), + "reviewer set flag 0": lambda: self.setFlag(0), + "reviewer replay audio": self.replayAudio, + "reviewer replay audio 1": self.replayAudio, + "reviewer replay audio 2": self.replayAudio, + "reviewer choice 1": lambda: self._answerCard(1), + "reviewer choice 2": lambda: self._answerCard(2), + "reviewer choice 3": lambda: self._answerCard(3), + "reviewer choice 4": lambda: self._answerCard(4), + } + if get_version() >= 20: + sdict["reviewer pause audio"] = self.on_pause_audio + sdict["reviewer seek backward"] = self.on_seek_backward + sdict["reviewer seek forward"] = self.on_seek_forward + if get_version() >= 33: + sdict["reviewer more options"] = self.showContextMenu + if get_version() >= 41: + sdict["reviewer set due date"] = self.on_set_due + if get_version() >= 45: + sdict["reviewer card info"] = self.on_card_info + sdict["reviewer set flag 5"] = lambda: self.setFlag(5) + sdict["reviewer set flag 6"] = lambda: self.setFlag(6) + sdict["reviewer set flag 7"] = lambda: self.setFlag(7) + if get_version() >= 48: + sdict["reviewer previous card info"] = self.on_previous_card_info + + scut = normalizeShortcutName(scut) + if scut in sdict: + return sdict[scut] + return None + +#Converts json shortcuts into functions for the reviewer +#sToF: shortcutToFunction +def editor_sToF(self,scut): + sdict = { + "editor card layout": (self.onCardLayout, True), + "editor bold": (self.toggleBold,), + "editor italic": (self.toggleItalic,), + "editor underline": (self.toggleUnderline,), + "editor superscript": (self.toggleSuper,), + "editor subscript": (self.toggleSub,), + "editor remove format": (self.removeFormat,), + "editor foreground": (self.onForeground,), + "editor change col": (self.onChangeCol,), + "editor cloze": (self.cs_onStdCloze,), + "editor cloze alt": (self.cs_onAltCloze,), + "editor add media": (self.onAddMedia,), + "editor record sound": (self.onRecSound,), + "editor insert latex": (self.insertLatex,), + "editor insert latex equation": (self.insertLatexEqn,), + "editor insert latex math environment": (self.insertLatexMathEnv,), + "editor insert mathjax inline": (self.insertMathjaxInline,), + "editor insert mathjax block": (self.insertMathjaxBlock,), + "editor html edit": (self.onHtmlEdit,), + "editor focus tags": (self.onFocusTags, True), + "editor toggle sticky current": (self.csToggleStickyCurrent,), + "editor toggle sticky all": (self.csToggleStickyAll,), + + + } + if get_version() >= 45: + sdict.update({ + "editor html edit": (lambda: + self.web.eval( + """{const currentField = getCurrentField(); if (currentField) { currentField.toggleHtmlEdit(); }}""" + ), ), + "editor block indent": (lambda: + self.web.eval( + """ document.execCommand("indent"); """ + ), ), + "editor block outdent": (lambda: + self.web.eval( + """ document.execCommand("outdent") """ + ), ), + "editor list insert unordered": (lambda: + self.web.eval( + """ document.execCommand("insertUnorderedList"); """ + ), ), + "editor list insert ordered": (lambda: + self.web.eval( + """ document.execCommand("insertOrderedList"); """ + ), ), + }) + + scut = normalizeShortcutName(scut) + if scut in sdict: + return sdict[scut] + return None + +def editor_changeDeck(self): + if not self.card: + return + from aqt.studydeck import StudyDeck + cid = self.card.id + did = self.card.did + current = self.mw.col.decks.get(did)["name"] + ret = StudyDeck( + self.mw, + current=current, + accept=tr(TR.BROWSING_MOVE_CARDS), + title=tr(TR.BROWSING_CHANGE_DECK), + help=HelpPage.BROWSING, + parent=self.mw, + ) + if not ret.name: + return + did = self.mw.col.decks.id(ret.name) + try: + set_card_deck(parent=self.widget, card_ids=[cid], deck_id=did).run_in_background() + except: + self.mw.col.set_deck([cid], did) + + +#Performs a preliminary check for if any filter is saved before removing it +def remove_filter(self): + name = self._currentFilterIsSaved() + if name: + self._onRemoveFilter() + +#For reviewer shortcut assignments: +#Takes as input ls (the list of shortcuts, of the form (shortcut, function pointer)) +#and replacements (a dict mapping function pointers to new shortcuts) +#Then, for every tuple in the list, if its function pointer has a replacement shortcut, replace it +#Changes the list in-place +def reviewer_find_and_replace_functions(ls, replacements): + for i, val in enumerate(ls): + func = val[1] + if func in replacements: + ls[i] = (replacements[func].pop(), func) + if not replacements[func]: + replacements.pop(func) + +#For reviewer shortcut assignments: +#Takes as input ls (the list of shortcuts, of the form (shortcut, function pointer)) +#and replacements (a dict mapping old shortcuts to new shortcuts) +#Then, for every tuple in the list, if its shortcut is in the list, replace it +#Changes the list in-place +#Prefer reviewer_find_and_replace_functions to this because functions are less fragile +def reviewer_find_and_replace_scuts(ls, replacements): + for i, val in enumerate(ls): + scut = val[0] + if scut in replacements: + ls[i] = (replacements.pop(scut), val[1]) diff --git a/.local/share/Anki2/addons21/keybindings/custom_shortcuts.py b/.local/share/Anki2/addons21/keybindings/custom_shortcuts.py new file mode 100644 index 0000000..c0209a6 --- /dev/null +++ b/.local/share/Anki2/addons21/keybindings/custom_shortcuts.py @@ -0,0 +1,882 @@ +# Last updated to be useful for: Anki 2.1.45 +import warnings +from anki.lang import _ +from aqt import mw +from aqt.qt import * +from anki.hooks import runHook,addHook,wrap +try: + from aqt.utils import ( + TR, + shortcut, + showWarning, + tr, + ) + tr_import = True +except: + from aqt.utils import showWarning + tr_import = False +from aqt.toolbar import Toolbar +from aqt.editor import Editor, EditorWebView +try: + from aqt.editor import EditorMode + editor_mode_import = True +except: + editor_mode_import = False +from aqt.reviewer import Reviewer +from aqt.browser import Browser +from aqt.modelchooser import ModelChooser + +try: + from aqt.notetypechooser import NotetypeChooser + notetypechooser_import = True +except: + notetypechooser_import = False +from aqt.addcards import AddCards +from anki.utils import json +from bs4 import BeautifulSoup +from . import cs_functions as functions +try: + from aqt.operations.notetype import update_notetype_legacy +except: + pass + +# Anki before version 2.1.20 does not use aqt.gui_hooks +try: + from aqt import gui_hooks + new_hooks = True +except: + new_hooks = False + +# Gets config.json as config +config = mw.addonManager.getConfig(__name__) +CS_CONFLICTSTR = "Custom Shortcut Conflicts: \n\n" +# config_scuts initialized after cs_traverseKeys +Qt_functions = {"Qt.Key_Enter": Qt.Key_Enter, + "Qt.Key_Return": Qt.Key_Return, + "Qt.Key_Escape": Qt.Key_Escape, + "Qt.Key_Space": Qt.Key_Space, + "Qt.Key_Tab": Qt.Key_Tab, + "Qt.Key_Backspace": Qt.Key_Backspace, + "Qt.Key_Delete": Qt.Key_Delete, + "Qt.Key_Left": Qt.Key_Left, + "Qt.Key_Down": Qt.Key_Down, + "Qt.Key_Right": Qt.Key_Right, + "Qt.Key_Up": Qt.Key_Up, + "Qt.Key_PageUp": Qt.Key_PageUp, + "Qt.Key_PageDown": Qt.Key_PageDown, + "<nop>": "" + } + +# There is a weird interaction with QShortcuts wherein if there are 2 (or more) +# QShortcuts mapped to the same key and function and both are enabled, +# the shortcut doesn't work + +# There isn't an obvious way to get the original QShortcut objects, as +# The addons executes after the setup phase (which creates QShortcut objects) + +def cs_traverseKeys(Rep, D): + ret = {} + for key in D: + if isinstance(D[key],dict): + ret[key] = cs_traverseKeys(Rep,D[key]) + elif D[key] in Rep: + ret[key] = Rep[D[key]] + else: + ret[key] = D[key] + return ret + + +# Since QShortcuts cannot reveal their action (to the best of my knowledge), +# This map reconstructs what each QShortcut is supposed to do from its id +# The ids were found manually but so far have remained constant +mainShortcutIds = {-1: "main debug", + -2: "main deckbrowser", + -3: "main study", + -4: "main add", + -5: "main browse", + -6: "main stats", + -7: "main sync" + } + +# This contains the processed shortcuts used for the rest of the functions +config_scuts = cs_traverseKeys(Qt_functions,config) + +# The main shortcuts are now found by manually going through all QT shortcuts +# and replacing them with their custom shortcut equivalent + +mainShortcutPairs = { + "Ctrl+:": config_scuts["main debug"], + "D": config_scuts["main deckbrowser"], + "S": config_scuts["main study"], + "A": config_scuts["main add"], + "B": config_scuts["main browse"], + "T": config_scuts["main stats"], + "Y": config_scuts["main sync"], + } + +# Finds all the shortcuts, figures out relevant ones from hardcoded shortcut check, +# and sets it to the right one +# This function has a side effect of changing the shortcut's id +def cs_main_setupShortcuts(): + mwShortcuts = mw.findChildren(QShortcut) + if functions.get_version() >= 50: + for child in mwShortcuts: + if child.key().toString() in mainShortcutPairs: + oldScut = child.key().toString() + newScut = mainShortcutPairs[oldScut] + child.setKey(newScut) + mainShortcutPairs.pop(oldScut) # Only replace shortcuts once (the first time would be the main shortcut) + else: # If possible, use the old shortcut remapping method + # This may be removed if the new method is fount to be more stable + for scut in mwShortcuts: + if scut.id() in mainShortcutIds: + scut.setKey(config_scuts[mainShortcutIds[scut.id()]]) + + +# Governs the shortcuts on the main toolbar +def cs_mt_setupShortcuts(): + m = mw.form + # Goes through and includes anything on the duplicates list + scuts_list = { + "m_toolbox quit": [config_scuts["m_toolbox quit"]], + "m_toolbox preferences": [config_scuts["m_toolbox preferences"]], + "m_toolbox undo": [config_scuts["m_toolbox undo"]], + "m_toolbox see documentation": [config_scuts["m_toolbox see documentation"]], + "m_toolbox switch profile": [config_scuts["m_toolbox switch profile"]], + "m_toolbox export": [config_scuts["m_toolbox export"]], + "m_toolbox import": [config_scuts["m_toolbox import"]], + "m_toolbox study": [config_scuts["m_toolbox study"]], + "m_toolbox create filtered deck": [config_scuts["m_toolbox create filtered deck"]], + "m_toolbox addons": [config_scuts["m_toolbox addons"]] + } + for act, key in config_scuts["m_toolbox _duplicates"].items(): + scuts_list[functions.normalizeShortcutName(act)].append(key) + m.actionExit.setShortcuts(scuts_list["m_toolbox quit"]) + m.actionPreferences.setShortcuts(scuts_list["m_toolbox preferences"]) + m.actionUndo.setShortcuts(scuts_list["m_toolbox undo"]) + m.actionDocumentation.setShortcuts(scuts_list["m_toolbox see documentation"]) + m.actionSwitchProfile.setShortcuts(scuts_list["m_toolbox switch profile"]) + m.actionExport.setShortcuts(scuts_list["m_toolbox export"]) + m.actionImport.setShortcuts(scuts_list["m_toolbox import"]) + m.actionStudyDeck.setShortcuts(scuts_list["m_toolbox study"]) + m.actionCreateFiltered.setShortcuts(scuts_list["m_toolbox create filtered deck"]) + m.actionAdd_ons.setShortcuts(scuts_list["m_toolbox addons"]) + +# Governs the shortcuts on the review window +# This replacement method is pretty blind but tries to minimize disruption +# Replaces shortcuts at the start first +# If other addons append shortcuts, this shouldn't bother those addons +def cs_review_setupShortcuts(self, _old): + # More fragile replacement: For these shortcuts, + # Their functions are lambdas, so we can't directly address them + # I'm not completely satisfied by this option + new_scut_replacements = { + "Ctrl+1" : config_scuts["reviewer set flag 1"], + "Ctrl+2" : config_scuts["reviewer set flag 2"], + "Ctrl+3" : config_scuts["reviewer set flag 3"], + "Ctrl+4" : config_scuts["reviewer set flag 4"], + "1" : config_scuts["reviewer choice 1"], + "2" : config_scuts["reviewer choice 2"], + "3" : config_scuts["reviewer choice 3"], + "4" : config_scuts["reviewer choice 4"], + } + if functions.get_version() >= 45: + new_scut_replacements["Ctrl+5"] = config_scuts["reviewer set flag 5"] + new_scut_replacements["Ctrl+6"] = config_scuts["reviewer set flag 6"] + new_scut_replacements["Ctrl+7"] = config_scuts["reviewer set flag 7"] + + # Less fragile replacement: For these shortcuts, address them by pointer and replace shortcut + # The keys are dicts because we will want to replace multiply shortcut keys + new_function_replacements = { + self.mw.onEditCurrent : [config_scuts["reviewer edit current"]], + self.onEnterKey : [ + config_scuts["reviewer flip card 1"], + config_scuts["reviewer flip card 2"], + config_scuts["reviewer flip card 3"]], + self.replayAudio : [ + config_scuts["reviewer replay audio 1"], + config_scuts["reviewer replay audio 2"], + ], + self.onMark : [config_scuts["reviewer mark card"]], + self.onBuryNote : [config_scuts["reviewer bury note"]], + self.onBuryCard : [config_scuts["reviewer bury card"]], + self.onSuspend : [config_scuts["reviewer suspend note"]], + self.onSuspendCard : [config_scuts["reviewer suspend card"]], + self.onDelete : [config_scuts["reviewer delete note"]], + self.onReplayRecorded : [config_scuts["reviewer play recorded voice"]], + self.onRecordVoice : [config_scuts["reviewer record voice"]], + self.onOptions : [config_scuts["reviewer options menu"]], + } + cuts = _old(self) + # Order is important: shortcut-based replacement should come first + functions.reviewer_find_and_replace_scuts(cuts,new_scut_replacements) + + if functions.get_version() >= 20: + new_function_replacements[self.on_pause_audio] = [config_scuts["reviewer pause audio"]] + new_function_replacements[self.on_seek_backward] = [config_scuts["reviewer seek backward"]] + new_function_replacements[self.on_seek_forward] = [config_scuts["reviewer seek forward"]] + if functions.get_version() >= 33: + new_function_replacements[self.showContextMenu] = [config_scuts["reviewer more options"]] + if functions.get_version() >= 41: + new_function_replacements[self.on_set_due] = [config_scuts["reviewer set due date"]] + if functions.get_version() >= 45: + new_function_replacements[self.on_card_info] = [config_scuts["reviewer card info"]] + if functions.get_version() >= 48: + new_function_replacements[self.on_previous_card_info] = [config_scuts["reviewer previous card info"]] + functions.reviewer_find_and_replace_functions(cuts,new_function_replacements) + for scut in config_scuts["reviewer _duplicates"]: + cuts.append((config_scuts["reviewer _duplicates"][scut], self.sToF(scut))) + return cuts + +# The function to setup shortcuts on the Editor +# Something funky is going on with the default MathJax and LaTeX shortcuts +# It does not affect the function (as I currently know of) +def cs_editor_setupShortcuts(self): + dupes = [] + # if a third element is provided, enable shortcut even when no field is selected + cuts = [ + (config_scuts["editor card layout"], self.onCardLayout, True), + (config_scuts["editor bold"], self.toggleBold), + (config_scuts["editor italic"], self.toggleItalic), + (config_scuts["editor underline"], self.toggleUnderline), + (config_scuts["editor superscript"], self.toggleSuper), + (config_scuts["editor subscript"], self.toggleSub), + (config_scuts["editor remove format"], self.removeFormat), + (config_scuts["editor cloze"], self.onCloze), + (config_scuts["editor cloze alt"], self.onCloze), + (config_scuts["editor cloze forced increment"], self.cs_onStdCloze), + (config_scuts["editor cloze no increment"], self.cs_onAltCloze), + (config_scuts["editor add media"], self.onAddMedia), + (config_scuts["editor record sound"], self.onRecSound), + (config_scuts["editor insert latex"], self.insertLatex), + (config_scuts["editor insert latex equation"], self.insertLatexEqn), + (config_scuts["editor insert latex math environment"], self.insertLatexMathEnv), + (config_scuts["editor insert mathjax inline"], self.insertMathjaxInline), + (config_scuts["editor insert mathjax block"], self.insertMathjaxBlock), + (config_scuts["editor focus tags"], self.onFocusTags, True), + (config_scuts["editor _extras"]["paste custom text"], + lambda text=config_scuts["Ω custom paste text"]: self.customPaste(text)), + ] + # Due to the svelte changes, these shortcuts "break" + # in the sense that they no longer correspond to the most recent shortcuts + cuts += [(config_scuts["editor foreground"], self.onForeground), + (config_scuts["editor change col"], self.onChangeCol), + ] + if functions.get_version() >= 45: + if functions.get_version() >= 50: + pass + else: + cuts += [ + (config_scuts["editor html edit"], lambda: + self.web.eval( + """{const currentField = getCurrentField(); if (currentField) { currentField.toggleHtmlEdit(); }}""" + )), + ] + cuts += [ + (config_scuts["editor toggle sticky current"], self.csToggleStickyCurrent), + (config_scuts["editor toggle sticky all"], self.csToggleStickyAll), + (config_scuts["editor block indent"], lambda: + self.web.eval( + """ + { + document.execCommand("indent"); + } + """ + ) + ), + (config_scuts["editor block outdent"], lambda: + self.web.eval( + """ + { + document.execCommand("outdent") + } + """ + ) + ), + (config_scuts["editor list insert unordered"], lambda: + self.web.eval( + """ + document.execCommand("insertUnorderedList"); + """ + ) + ), + (config_scuts["editor list insert ordered"], lambda: + self.web.eval( + """ + document.execCommand("insertOrderedList"); + """ + ) + ), + ] + else: + cuts += [ + (config_scuts["editor html edit"], self.onHtmlEdit),] + + for scut in config_scuts["editor _duplicates"]: + if self.sToF(scut): + dupes.append((config_scuts["editor _duplicates"][scut],)+self.sToF(scut)) + cuts += dupes + for label in config_scuts["editor _pastes"]: + if label in config_scuts["Ω custom paste extra texts"]: + scut = config_scuts["editor _pastes"][label] + temp = config_scuts["Ω custom paste extra texts"][label] + cuts.append((scut, lambda text=temp: self.customPaste(text))) + # There is a try-except clause to handle 2.1.0 version, which does not have this shortcut + try: + cuts.append((config_scuts["editor insert mathjax chemistry"], self.insertMathjaxChemistry)) + except AttributeError: + pass + if new_hooks: + gui_hooks.editor_did_init_shortcuts(cuts, self) + else: + runHook("setupEditorShortcuts", cuts, self) + for row in cuts: + if len(row) == 2: + keys, fn = row + fn = self._addFocusCheck(fn) + else: + keys, fn, _ = row + scut = QShortcut(QKeySequence(keys), self.widget, activated=fn) + +# Wrapper function to add another shortcut to change note type +# Not with the other custom shortcut editor functions because +# the Anki functionality handling card type is not +# in the Editor itself +def cs_editorChangeNoteType(self): + NOTE_TYPE_STR = "editor change note type" + new_scuts = {config_scuts[NOTE_TYPE_STR]} + for act, key in config_scuts["editor _duplicates"].items(): + if functions.normalizeShortcutName(act) == NOTE_TYPE_STR: + new_scuts.add(key) + for scut in new_scuts: + if functions.get_version() >= 41: + QShortcut(QKeySequence(scut), self._widget, activated=self.on_activated) + elif functions.get_version() >= 36: + QShortcut(QKeySequence(scut), self.widget, activated=self.on_activated) + else: + QShortcut(QKeySequence(scut), self.widget, activated=self.onModelChange) + +def cs_editorNotetypeChooser(self, show_label: bool): + NOTE_TYPE_STR = "editor change note type" + new_scuts = {config_scuts[NOTE_TYPE_STR]} + for act, key in config_scuts["editor _duplicates"].items(): + if functions.normalizeShortcutName(act) == NOTE_TYPE_STR: + new_scuts.add(key) + for scut in new_scuts: + # Since the hard-coded shortcut for this is Ctrl+N, + # Don't destroy the existing shortcut + # (has the side effect of leaving Ctrl+N assigned at all times) + if scut == "Ctrl+N": + continue + qconnect(QShortcut(QKeySequence(scut), self._widget).activated, + self.on_button_activated + ) + +def cs_editorUpdateStickyPins(self, stickies, model): + stickiesStr = "" + + # Hack: the svelte interface exposes a function "setSticky" which manually sets the sticky displays of all the pins + # Manually create a set of stickies in order to set the sticky pins to the right display value + stickiesStr += "[" + firstInput = True + for sticky in stickies: + if not firstInput: + stickiesStr += "," + firstInput = False + stickiesStr += ("true" if sticky else "false") + stickiesStr += "]" + + try: + update_notetype_legacy(parent=self.mw, notetype=model).run_in_background( + initiator=self + ) + self.web.eval("setSticky({})".format(stickiesStr)) + except: + pass + +def cs_editorToggleSticky(self, index: int): + model = self.note.note_type() + flds = model["flds"] + stickies = [] + flds[index]["sticky"] = not flds[index]["sticky"] + for fld in flds: + stickies.append(fld["sticky"]) + cs_editorUpdateStickyPins(self, stickies, model) + + +# Toggle sticky on all fields +# "Toggle" is interpreted as it is in Anki: +# If any sticky is on, turn everything off +# If all stickies are off, turn everything on +def cs_editorToggleStickyAll(self): + model = self.note.note_type() + flds = model["flds"] + + any_sticky = any([fld["sticky"] for fld in flds]) + stickies = [] + for fld in flds: + if not any_sticky or fld["sticky"]: + fld["sticky"] = not fld["sticky"] + stickies.append(fld["sticky"]) + cs_editorUpdateStickyPins(self, stickies, model) + +# Toggle sticky on the current field +def cs_editorToggleStickyCurrent(self): + if self.currentField is not None: + cs_editorToggleSticky(self,self.currentField) + +# Intercepts the bridge functions normally used to toggle stickiness +def cs_captureBridgeToggleSticky(self, cmd, _old): + # If we intercept a "toggle sticky all" command, then + if cmd.startswith("toggleStickyAll") and config_scuts["editor toggle sticky all"] != "Shift+F9": + model = self.note.note_type() + return model["flds"]["sticky"] + elif cmd.startswith("toggleSticky") and config_scuts["editor toggle sticky current"] != "F9": + model = self.note.note_type() + (_, num) = cmd.split(":",1) + idx = int(num) + + return model["flds"][idx]["sticky"] + return _old(self,cmd) + + +# Wrapper function to change the shortcut to add a card +# Not with the other custom shortcut editor functions because +# the add card button is not within the Editor class +def cs_editorAddCard(self): + ADD_CARD_STR = "editor confirm add card" + self.addButton.setShortcut(QKeySequence(config_scuts[ADD_CARD_STR])) + for act, key in config_scuts["editor _duplicates"].items(): + if functions.normalizeShortcutName(act) == ADD_CARD_STR: + QShortcut(QKeySequence(key), self, activated=self.addCards) + +def cs_editorChangeDeck(self): + CHANGE_DECK_STR = "editor change deck" + new_scuts = {config_scuts[CHANGE_DECK_STR]} + for act, key in config_scuts["editor _duplicates"].items(): + if functions.normalizeShortcutName(act) == CHANGE_DECK_STR: + new_scuts.add(key) + for scut in new_scuts: + QShortcut(QKeySequence(scut), self.widget, activated=self.cs_changeDeck) + +# IMPLEMENTS Browser shortcuts +def cs_browser_setupShortcuts(self): + f = self.form + try: + f.previewButton.setShortcut(config_scuts["window_browser preview"]) + except: + pass + try: + f.action_set_due_date.setShortcut(config_scuts["window_browser reschedule"]) + except: + f.actionReschedule.setShortcut(config_scuts["window_browser reschedule"]) + f.actionSelectAll.setShortcut(config_scuts["window_browser select all"]) + f.actionUndo.setShortcut(config_scuts["window_browser undo"]) + f.actionInvertSelection.setShortcut(config_scuts["window_browser invert selection"]) + f.actionFind.setShortcut(config_scuts["window_browser find"]) + f.actionNote.setShortcut(config_scuts["window_browser goto note"]) + f.actionNextCard.setShortcut(config_scuts["window_browser goto next note"]) + f.actionPreviousCard.setShortcut(config_scuts["window_browser goto previous note"]) + f.actionChangeModel.setShortcut(config_scuts["window_browser change note type"]) + f.actionGuide.setShortcut(config_scuts["window_browser guide"]) + f.actionFindReplace.setShortcut(config_scuts["window_browser find and replace"]) + try: + f.actionTags.setShortcut(config_scuts["window_browser filter"]) + except AttributeError: + f.actionSidebarFilter.setShortcut(config_scuts["window_browser filter"]) + f.actionCardList.setShortcut(config_scuts["window_browser goto card list"]) + f.actionReposition.setShortcut(config_scuts["window_browser reposition"]) + f.actionFirstCard.setShortcut(config_scuts["window_browser first card"]) + f.actionLastCard.setShortcut(config_scuts["window_browser last card"]) + f.actionClose.setShortcut(config_scuts["window_browser close"]) + f.action_Info.setShortcut(config_scuts["window_browser info"]) + f.actionAdd_Tags.setShortcut(config_scuts["window_browser add tag"]) + f.actionRemove_Tags.setShortcut(config_scuts["window_browser remove tag"]) + f.actionToggle_Suspend.setShortcut(config_scuts["window_browser suspend"]) + f.actionDelete.setShortcut(config_scuts["window_browser delete"]) + f.actionAdd.setShortcut(config_scuts["window_browser add note"]) + f.actionChange_Deck.setShortcut(config_scuts["window_browser change deck"]) + f.actionRed_Flag.setShortcut(config_scuts["window_browser flag_red"]) + try: + f.actionOrange_Flag.setShortcut(config_scuts["window_browser flag_orange"]) + except AttributeError: + f.actionPurple_Flag.setShortcut(config_scuts["window_browser flag_orange"]) + f.actionGreen_Flag.setShortcut(config_scuts["window_browser flag_green"]) + f.actionBlue_Flag.setShortcut(config_scuts["window_browser flag_blue"]) + f.actionSidebar.setShortcut(config_scuts["window_browser goto sidebar"]) + f.actionToggle_Mark.setShortcut(config_scuts["window_browser toggle mark"]) + f.actionClear_Unused_Tags.setShortcut(config_scuts["window_browser clear unused tags"]) + f.actionFindDuplicates.setShortcut(config_scuts["window_browser find duplicates"]) + f.actionSelectNotes.setShortcut(config_scuts["window_browser select notes"]) + f.actionManage_Note_Types.setShortcut(config_scuts["window_browser manage note types"]) + try: + f.action_forget.setShortcut(config_scuts["window_browser forget card"]) + except AttributeError: + pass + +# Mimics the style of other Anki functions, analogue of customPaste +# Note that the saveNow function used earler takes the cursor to the end of the line, +# as it is meant to save work before entering a new window +def cs_editor_custom_paste(self, text): + self._customPaste(text) + +# Mimics the style of other Anki functions, analogue of _customPaste +def cs_uEditor_custom_paste(self, text): + html = text + if config_scuts["Ω custom paste end style"].upper() == "Y": + html += "</span>\u200b" + with warnings.catch_warnings() as w: + warnings.simplefilter('ignore', UserWarning) + html = str(BeautifulSoup(html, "html.parser")) + self.doPaste(html,True,True) + +# detects shortcut conflicts +# Gets all the shortcuts in a given object of the form {name: scut, ...} and names them +# Returns a dictionary of the form {scut: [labels of objects with that scut], ...} +def cs_getAllScuts(obj, strCont): + res = {} + for key in obj: + if isinstance(obj[key], dict): + rec = cs_getAllScuts(obj[key], key + " in " + strCont) + for term in rec: + if term in res: + res[term] += rec[term] + else: + res[term] = rec[term] + else: + text_scut = obj[key].upper() + if text_scut in res: + res[text_scut].append(key + " in " + strCont) + else: + res[text_scut] = [key + " in " + strCont] + return res + +# Ignores the Add-on (Ω) options +def cs_conflictDetect(): + if config["Ω enable conflict warning"].upper() != "Y": + return + ext_list = {} + for e in config: + sub = e[0:(e.find(" "))] + val = config[e] + if sub == "Ω": + continue + if sub not in ext_list: + ext_list[sub] = {} + if isinstance(val, dict): + scuts = cs_getAllScuts(val, e) + for scut in scuts: + if scut in ext_list[sub]: + ext_list[sub][scut] += scuts[scut] + else: + ext_list[sub][scut] = scuts[scut] + else: + text_val = val.upper() + if text_val in ext_list[sub]: + ext_list[sub][text_val].append(e) + else: + ext_list[sub][text_val] = [e] + conflictStr = CS_CONFLICTSTR + conflict = False + for key in ext_list: + for k in ext_list[key]: + if len(ext_list[key][k]) == 1: + continue + if k == "<NOP>": + continue + if not k: + continue + conflict = True + conflictStr += ", ".join(ext_list[key][k]) + conflictStr += "\nshare '" + k + "' as a shortcut\n\n" + if conflict: + conflictStr += "\nThese shortcuts will not work.\n" + conflictStr += "Please change them in the config.json." + showWarning(conflictStr) + +def cs_toolbarCenterLinks(self): + try: + links = [ + self.create_link( + "decks", + tr(TR.ACTIONS_DECKS), + self._deckLinkHandler, + tip=tr(TR.ACTIONS_SHORTCUT_KEY, val=config_scuts["main deckbrowser"]), + id="decks", + ), + self.create_link( + "add", + tr(TR.ACTIONS_ADD), + self._addLinkHandler, + tip=tr(TR.ACTIONS_SHORTCUT_KEY, val=config_scuts["main add"]), + id="add", + ), + self.create_link( + "browse", + tr(TR.QT_MISC_BROWSE), + self._browseLinkHandler, + tip=tr(TR.ACTIONS_SHORTCUT_KEY, val=config_scuts["main browse"]), + id="browse", + ), + self.create_link( + "stats", + tr(TR.QT_MISC_STATS), + self._statsLinkHandler, + tip=tr(TR.ACTIONS_SHORTCUT_KEY, val=config_scuts["main stats"]), + id="stats", + ), + ] + + links.append(self._create_sync_link()) + + gui_hooks.top_toolbar_did_init_links(links, self) + + return "\n".join(links) + except: + pass + try: + links = [ + self.create_link( + "decks", + _("Decks"), + self._deckLinkHandler, + tip=_("Shortcut key: %s") % config_scuts["main deckbrowser"], + id="decks", + ), + self.create_link( + "add", + _("Add"), + self._addLinkHandler, + tip=_("Shortcut key: %s") % config_scuts["main add"], + id="add", + ), + self.create_link( + "browse", + _("Browse"), + self._browseLinkHandler, + tip=_("Shortcut key: %s") % config_scuts["main browse"], + id="browse", + ), + self.create_link( + "stats", + _("Stats"), + self._statsLinkHandler, + tip=_("Shortcut key: %s") % config_scuts["main stats"], + id="stats", + ), + ] + + links.append(self._create_sync_link()) + + gui_hooks.top_toolbar_did_init_links(links, self) + + return "\n".join(links) + except: + links = [ + ["decks", _("Decks"), _("Shortcut key: %s") % config_scuts["main deckbrowser"]], + ["add", _("Add"), _("Shortcut key: %s") % config_scuts["main add"]], + ["browse", _("Browse"), _("Shortcut key: %s") % config_scuts["main browse"]], + ["stats", _("Stats"), _("Shortcut key: %s") % config_scuts["main stats"]], + ["sync", _("Sync"), _("Shortcut key: %s") % config_scuts["main sync"]], + ] + return self._linkHTML(links) + +def cs_browser_basicFilter(self, txt): + self.form.searchEdit.lineEdit().setText(txt) + self.onSearchActivated() + +def cs_browser_concatFilter(self, txt): + cur = str(self.form.searchEdit.lineEdit().text()) + if cur and cur != self._searchPrompt: + txt = cur + " " + txt + self.form.searchEdit.lineEdit().setText(txt) + self.onSearchActivated() + +def cs_browser_orConcatFilter(self, txt): + cur = str(self.form.searchEdit.lineEdit().text()) + if cur: + txt = cur + " or " + txt + self.form.searchEdit.lineEdit().setText(txt) + self.onSearchActivated() + +# Inserts the custom filter shortcuts upon browser startup +def cs_browser_setupEditor(self): + if functions.get_version() >= 50 and editor_mode_import: + QShortcut(QKeySequence(config_scuts["window_browser preview"]), self, self.onTogglePreview) + def add_preview_button(editor): + editor._links["preview"] = lambda _editor: self.onTogglePreview() + gui_hooks.editor_did_init.append(add_preview_button) + self.editor = Editor(self.mw, self.form.fieldsArea, self, editor_mode=EditorMode.BROWSER,) + gui_hooks.editor_did_init.remove(add_preview_button) + elif functions.get_version() >= 45: + QShortcut(QKeySequence(config_scuts["window_browser preview"]), self, self.onTogglePreview) + def add_preview_button(editor): + preview_shortcut = config_scuts["window_browser preview"] + + editor._links["preview"] = lambda _editor: self.onTogglePreview() + editor.web.eval( + "$editorToolbar.then(({ notetypeButtons }) => notetypeButtons.appendButton({ component: editorToolbar.PreviewButton, id: 'preview' }));" + ) + gui_hooks.editor_did_init.append(add_preview_button) + self.editor = Editor(self.mw, self.form.fieldsArea, self) + gui_hooks.editor_did_init.remove(add_preview_button) + elif functions.get_version() >= 39: + def add_preview_button(leftbuttons, editor): + preview_shortcut = config_scuts["window_browser preview"] + leftbuttons.insert( + 0, + editor.addButton( + None, + "preview", + lambda _editor: self.onTogglePreview(), + tr( + TR.BROWSING_PREVIEW_SELECTED_CARD, + val=shortcut(preview_shortcut), + ), + tr(TR.ACTIONS_PREVIEW), + id="previewButton", + keys=preview_shortcut, + disables=False, + rightside=False, + toggleable=True, + ), + ) + gui_hooks.editor_did_init_left_buttons.append(add_preview_button) + self.editor = Editor(self.mw, self.form.fieldsArea, self) + gui_hooks.editor_did_init_left_buttons.remove(add_preview_button) + else: + self.editor = Editor(self.mw, self.form.fieldsArea, self) + self.csFilterScuts = {} + self.csFilterFuncs = {} + self.csCatFilterScuts = {} + self.csCatFilterFuncs = {} + self.csOCatFilterScuts = {} + self.csOCatFilterFuncs = {} + for filt in config_scuts["window_browser _filters"]: + scut = config_scuts["window_browser _filters"][filt] + if isinstance(scut, dict): + continue + self.csFilterFuncs[filt] = lambda txt=filt: cs_browser_basicFilter(self, txt) + self.csFilterScuts[filt] = QShortcut(QKeySequence(scut), self) + self.csFilterScuts[filt].activated.connect(self.csFilterFuncs[filt]) + if "_concat" in config_scuts["window_browser _filters"]: + for filt in config_scuts["window_browser _filters"]["_concat"]: + scut = config_scuts["window_browser _filters"]["_concat"][filt] + self.csCatFilterFuncs[filt] = lambda txt=filt: cs_browser_concatFilter(self, txt) + self.csCatFilterScuts[filt] = QShortcut(QKeySequence(scut), self) + self.csCatFilterScuts[filt].activated.connect(self.csCatFilterFuncs[filt]) + if "_orConcat" in config_scuts["window_browser _filters"]: + for filt in config_scuts["window_browser _filters"]["_orConcat"]: + scut = config_scuts["window_browser _filters"]["_orConcat"][filt] + self.csOCatFilterFuncs[filt] = lambda txt=filt: cs_browser_orConcatFilter(self, txt) + self.csOCatFilterScuts[filt] = QShortcut(QKeySequence(scut), self) + self.csOCatFilterScuts[filt].activated.connect(self.csOCatFilterFuncs[filt]) + if config_scuts["window_browser save current filter"]: + self.csSaveFilterScut = QShortcut(QKeySequence(config_scuts["window_browser save current filter"]), self) + self.csSaveFilterScut.activated.connect(self._onSaveFilter) + if config_scuts["window_browser remove current filter"]: + self.csRemoveFilterScut = QShortcut(QKeySequence(config_scuts["window_browser remove current filter"]), self) + self.csRemoveFilterScut.activated.connect(self.csRemoveFilterFunc) + +# Corresponds to _setup_tools in the SidebarToolbar class in Anki 2.1.45 +sidebar_tool_names = [ + "window_browser sidebar search", + "window_browser sidebar select" + ] + + +def cs_sidebar_setup_tools(self): + from aqt.theme import theme_manager + for row, tool in enumerate(self._tools): + action = self.addAction( + theme_manager.icon_from_resources(tool[1]), tool[2]() + ) + action.setCheckable(True) + # If we are aware of the row, set it in the tools + # otherwise, use the default + action.setShortcut( + config_scuts[sidebar_tool_names[row]] + if row < len(sidebar_tool_names) else + f"Alt+{row + 1}" + ) + self._action_group.addAction(action) + # always start with first tool + active = 0 + self._action_group.actions()[active].setChecked(True) + self.sidebar.tool = self._tools[active][0] + +def cs_injectCloseShortcut(scuts): + def inject_shortcut(self): + try: + from aqt.utils import is_mac + isMac = is_mac + except: + from aqt.utils import isMac + cutExistingShortcut = False + for scut in scuts: + if scut == "<default>": + continue + addedShortcut = False + if isMac and not cutExistingShortcut: + for child in self.findChildren(QShortcut): + if child.key().toString() == 'Ctrl+W': + child.setKey(scut) + addedShortcut = cutExistingShortcut = True + if not addedShortcut: + shortcut = QShortcut(QKeySequence(scut), self) + qconnect(shortcut.activated, self.reject) + setattr(self, "_closeShortcut", shortcut) + return inject_shortcut + +# Functions that execute on startup +if config_scuts["Ω enable main"].upper() == 'Y': + Toolbar._centerLinks = cs_toolbarCenterLinks + cs_main_setupShortcuts() +if config_scuts["Ω enable editor"].upper() == 'Y': + Editor.cs_changeDeck = functions.editor_changeDeck + Editor.sToF = functions.editor_sToF + Editor.cs_u_onAltCloze = lambda self: functions.cs_editor_generate_cloze(self, altModifier=True) + Editor.cs_u_onStdCloze = lambda self: functions.cs_editor_generate_cloze(self, altModifier=False) + Editor.cs_onAltCloze = functions.cs_editor_on_alt_cloze + Editor.cs_onStdCloze = functions.cs_editor_on_std_cloze + Editor.customPaste = cs_editor_custom_paste + Editor._customPaste = cs_uEditor_custom_paste + Editor.setupShortcuts = cs_editor_setupShortcuts + Editor.setupShortcuts = wrap(Editor.setupShortcuts, cs_editorChangeDeck) + if functions.get_version() >= 45: + Editor.csToggleStickyCurrent = cs_editorToggleStickyCurrent + Editor.csToggleStickyAll = cs_editorToggleStickyAll + Editor.onBridgeCmd = wrap(Editor.onBridgeCmd, cs_captureBridgeToggleSticky, "around") + if notetypechooser_import: + NotetypeChooser._setup_ui = wrap(NotetypeChooser._setup_ui, cs_editorNotetypeChooser) + ModelChooser.setupModels = wrap(ModelChooser.setupModels, cs_editorChangeNoteType) + AddCards.setupButtons = wrap(AddCards.setupButtons, cs_editorAddCard) + try: + gui_hooks.add_cards_did_init.append(cs_injectCloseShortcut([config_scuts["editor add card close window"]])) + except: + pass +if config_scuts["Ω enable reviewer"].upper() == 'Y': + Reviewer._shortcutKeys = wrap(Reviewer._shortcutKeys, cs_review_setupShortcuts, "around") + Reviewer.sToF = functions.review_sToF +if config_scuts["Ω enable m_toolbox"].upper() == 'Y': + cs_mt_setupShortcuts() +# Hooks to setup shortcuts at the right time +if config_scuts["Ω enable window_browser"].upper() == 'Y': + Browser.csRemoveFilterFunc = functions.remove_filter + Browser.setupEditor = cs_browser_setupEditor + addHook('browser.setupMenus', cs_browser_setupShortcuts) + if functions.get_version() >= 45: + from aqt.browser import SidebarToolbar + SidebarToolbar._setup_tools = cs_sidebar_setup_tools + +# Fun fact: the stats window shortcut can also be customized (very slightly) +# Due to the added complexity of handling this relative to what is probably zero demand, this will remain unimplemented for the time being +# gui_hooks.stats_dialog_will_show.append(cs_injectCloseShortcut([config_scuts["stats close window"]])) +# The deck options window is another feature that probably won't be implemented unless requested +# gui_hooks.deck_options_did_load.append(cs_injectCloseShortcut([config_scuts["editor deck options close window"]])) + +# Detects all conflicts, regardless of enable status +cs_conflictDetect() + +# Redraws the toolbar with the new shortcuts +mw.toolbar.draw() diff --git a/.local/share/Anki2/addons21/keybindings/meta.json b/.local/share/Anki2/addons21/keybindings/meta.json new file mode 100644 index 0000000..fe824c0 --- /dev/null +++ b/.local/share/Anki2/addons21/keybindings/meta.json @@ -0,0 +1 @@ +{"name": "Customize Keyboard Shortcuts", "mod": 1656483227, "min_point_version": 0, "max_point_version": 45, "branch_index": 0, "disabled": false, "config": {"editor _duplicates": {}, "editor _extras": {"paste custom text": "<nop>"}, "editor _pastes": {}, "editor add card close window": "<default>", "editor add media": "F3", "editor block indent": "<nop>", "editor block outdent": "<nop>", "editor bold": "Ctrl+B", "editor card layout": "Ctrl+L", "editor change col": "F8", "editor change deck": "<nop>", "editor change note type": "Ctrl+N", "editor cloze": "Ctrl+Shift+C", "editor cloze alt": "Ctrl+Shift+Alt+C", "editor cloze forced increment": "<nop>", "editor cloze no increment": "<nop>", "editor confirm add card": "Ctrl+Return", "editor focus tags": "Ctrl+Shift+T", "editor foreground": "F7", "editor html edit": "Ctrl+Shift+X", "editor insert latex": "Ctrl+T, T", "editor insert latex equation": "Ctrl+T, E", "editor insert latex math environment": "Ctrl+T, M", "editor insert mathjax block": "Ctrl+M, E", "editor insert mathjax chemistry": "Ctrl+M, C", "editor insert mathjax inline": "Ctrl+M, M", "editor italic": "Ctrl+I", "editor list insert ordered": "<nop>", "editor list insert unordered": "<nop>", "editor record sound": "F5", "editor remove format": "Ctrl+R", "editor subscript": "Ctrl+=", "editor superscript": "Ctrl++", "editor toggle sticky all": "Shift+F9", "editor toggle sticky current": "F9", "editor underline": "Ctrl+U", "m_toolbox _duplicates": {}, "m_toolbox addons": "Ctrl+Shift+A", "m_toolbox create filtered deck": "F", "m_toolbox export": "Ctrl+E", "m_toolbox import": "Ctrl+Shift+I", "m_toolbox preferences": "Ctrl+P", "m_toolbox quit": "Ctrl+Q", "m_toolbox see documentation": "F1", "m_toolbox study": "/", "m_toolbox switch profile": "Ctrl+Shift+P", "m_toolbox undo": "Ctrl+Z", "main add": "A", "main browse": "B", "main debug": "Ctrl+:", "main deckbrowser": "D", "main stats": "T", "main study": "S", "main sync": "Y", "reviewer _duplicates": {}, "reviewer bury card": "-", "reviewer bury note": "=", "reviewer card info": "I", "reviewer choice 1": "q", "reviewer choice 2": "w", "reviewer choice 3": "3", "reviewer choice 4": "4", "reviewer delete note": "Ctrl+Delete", "reviewer edit current": "E", "reviewer flip card 1": " ", "reviewer flip card 2": "Qt.Key_Return", "reviewer flip card 3": "Qt.Key_Enter", "reviewer mark card": "*", "reviewer more options": "M", "reviewer options menu": "o", "reviewer pause audio": "5", "reviewer play recorded voice": "v", "reviewer previous card info": "Alt+I", "reviewer record voice": "Shift+v", "reviewer replay audio 1": "r", "reviewer replay audio 2": "F5", "reviewer seek backward": "6", "reviewer seek forward": "7", "reviewer set due date": "Ctrl+Shift+D", "reviewer set flag 0": "Ctrl+0", "reviewer set flag 1": "Ctrl+1", "reviewer set flag 2": "Ctrl+2", "reviewer set flag 3": "Ctrl+3", "reviewer set flag 4": "Ctrl+4", "reviewer set flag 5": "Ctrl+5", "reviewer set flag 6": "Ctrl+6", "reviewer set flag 7": "Ctrl+7", "reviewer suspend card": "@", "reviewer suspend note": "!", "window_browser _filters": {"_concat": {}, "_orConcat": {}}, "window_browser add note": "Ctrl+E", "window_browser add tag": "Ctrl+Shift+A", "window_browser change deck": "Ctrl+D", "window_browser change note type": "Ctrl+Shift+M", "window_browser clear unused tags": "<nop>", "window_browser close": "Ctrl+W", "window_browser delete": "Ctrl+Del", "window_browser filter": "Ctrl+Shift+F", "window_browser find": "Ctrl+F", "window_browser find and replace": "Ctrl+Alt+F", "window_browser find duplicates": "<nop>", "window_browser first card": "Home", "window_browser flag_blue": "Ctrl+4", "window_browser flag_green": "Ctrl+3", "window_browser flag_orange": "Ctrl+2", "window_browser flag_red": "Ctrl+1", "window_browser forget card": "Ctrl+Alt+N", "window_browser goto card list": "Ctrl+Shift+L", "window_browser goto next note": "Ctrl+N", "window_browser goto note": "Ctrl+Shift+N", "window_browser goto previous note": "Ctrl+P", "window_browser goto sidebar": "Ctrl+Shift+R", "window_browser guide": "F1", "window_browser info": "Ctrl+Shift+I", "window_browser invert selection": "Ctrl+Alt+S", "window_browser last card": "End", "window_browser manage note types": "<nop>", "window_browser preview": "Ctrl+Shift+P", "window_browser remove current filter": "<nop>", "window_browser remove tag": "Ctrl+Alt+Shift+A", "window_browser reposition": "Ctrl+Shift+S", "window_browser reschedule": "Ctrl+Shift+D", "window_browser save current filter": "<nop>", "window_browser select all": "Ctrl+Alt+A", "window_browser select notes": "<nop>", "window_browser sidebar search": "Alt+1", "window_browser sidebar select": "Alt+2", "window_browser suspend": "Ctrl+J", "window_browser toggle mark": "Ctrl+K", "window_browser undo": "Ctrl+Alt+Z", "\u03a9 custom paste end style": "n", "\u03a9 custom paste extra texts": {}, "\u03a9 custom paste text": "", "\u03a9 enable conflict warning": "y", "\u03a9 enable editor": "y", "\u03a9 enable m_toolbox": "y", "\u03a9 enable main": "y", "\u03a9 enable reviewer": "y", "\u03a9 enable window_browser": "y"}}
\ No newline at end of file diff --git a/.local/share/Anki2/addons21/replay/__init__.py b/.local/share/Anki2/addons21/replay/__init__.py new file mode 100644 index 0000000..62278e7 --- /dev/null +++ b/.local/share/Anki2/addons21/replay/__init__.py @@ -0,0 +1,148 @@ +# -*- mode: Python ; coding: utf-8 -*- +# +# Copyright © 2013–17 Roland Sieker <[email protected]> +# +# License: GNU AGPL, version 3 or later; +# http://www.gnu.org/copyleft/agpl.html + +"""Add-on for Anki 2 to add AnkiDroid-style replay buttons.""" + +import html.parser +import os +import re +import shutil +import urllib.parse + +from bs4 import BeautifulSoup +from PyQt5.QtCore import QUrl +from PyQt5.QtGui import QDesktopServices + + +from anki.cards import Card +from anki.hooks import addHook, wrap +from anki.sound import play +from aqt import mw +from aqt.browser import Browser +from aqt.browser import DataModel +from aqt.clayout import CardLayout +from aqt.reviewer import Reviewer + +__version__ = "3.1.0" + +sound_re = r"\[sound:(.*?)\]" + +hide_class_name = u'browserhide' + + +def svg_css(Card): + """Add the svg button style to the card style""" + return """<style scoped> +.replaybutton span { + display: inline-block; + vertical-align: middle; + padding: 5px; +} + +.replaybutton span svg { + stroke: none; + fill: black; + display: inline; + height: 1em; + width: 1em; + min-width: 12px; + min-height: 12px; +} +</style> +""" + old_css(Card) + + +def play_button_filter( + qa_html, qa_type, dummy_fields, dummy_model, dummy_data, dummy_col): + """ + Filter the questions and answers to add play buttons. + """ + + def add_button(sound): + """Add a button after the match. + + Add a button after the match to replay the audio. The title is + set to "Replay" on the question side to hide information or to + the file name on the answer. + + The filename is first unescaped and then percentage + encoded. For example, an image of a German rock album may go + from “Ö.jpg” to “Ö.jpg” to “%C3%96.jpg”. + + """ + fn = sound.group(1) + # Cut out some escaping of HTML enteties that is done + # automatically by BS4 + unescaped_fn = html.parser.HTMLParser().unescape(fn) + clean_fn = urllib.parse.quote(unescaped_fn) + if 'q' == qa_type: + title = "Replay" + else: + title = fn + return """{orig}<a href='javascript:pycmd("ankiplay{link_fn}");' \ +title="{ttl}" class="replaybutton browserhide"><span><svg viewBox="0 0 32 32">\ +<polygon points="11,25 25,16 11,7"/>Replay</svg></span></a>\ +<span style="display: none;">[sound:{fn}]</span>""".format( + orig=sound.group(0), link_fn=clean_fn, ttl=title, fn=fn) + # The [ ] are the square brackets that we want to + # appear as brackets and not trigger the playing of the + # sound. The span inside the a around the svg is there to + # bring this closer in line with AnkiDroid. + return re.sub(sound_re, add_button, qa_html) + + +def review_link_handler_wrapper(reviewer, url): + """Play the sound or call the original link handler.""" + if url.startswith("ankiplay"): + play(url[8:]) + else: + original_review_link_handler(reviewer, url) + + +def simple_link_handler(url): + """Play the file.""" + if url.startswith("ankiplay"): + play(url[8:]) + else: + QDesktopServices.openUrl(QUrl(url)) + + +def add_clayout_link_handler(clayout, dummy_t): + """Make sure we play the files from the card layout window.""" + clayout.forms[-1]['pform'].frontWeb.setLinkHandler(simple_link_handler) + clayout.forms[-1]['pform'].backWeb.setLinkHandler(simple_link_handler) + + +def add_preview_link_handler(browser): + """Make sure we play the files from the preview window.""" + browser._previewWeb.setLinkHandler(simple_link_handler) + + +def reduce_format_qa(self, text): + """Remove elements with a given class before displaying.""" + soup = BeautifulSoup(text, 'html.parser') + for hide in soup.findAll(True, {'class': re.compile( + '\\b' + hide_class_name + '\\b')}): + hide.extract() + return original_format_qa(self, unicode(soup)) + + +original_review_link_handler = Reviewer._linkHandler +Reviewer._linkHandler = review_link_handler_wrapper + +# TODO: hide stuff from the browser again +#original_format_qa = DataModel.formatQA +#DataModel.formatQA = reduce_format_qa + +old_css = Card.css +Card.css = svg_css + +addHook("mungeQA", play_button_filter) +# Browser._openPreview = wrap(Browser._openPreview, add_preview_link_handler) + +# TODO: fix this +#CardLayout.addTab = wrap(CardLayout.addTab, add_clayout_link_handler) diff --git a/.local/share/Anki2/addons21/replay/meta.json b/.local/share/Anki2/addons21/replay/meta.json new file mode 100644 index 0000000..9e216e8 --- /dev/null +++ b/.local/share/Anki2/addons21/replay/meta.json @@ -0,0 +1 @@ +{"name": "Replay buttons on card", "mod": 1511181519, "min_point_version": 0, "max_point_version": 16, "branch_index": 0, "disabled": false}
\ No newline at end of file |