├── manifest-translated.ini.tpl ├── .gitignore ├── manifest.ini.tpl ├── .gitattributes ├── style.css ├── addon ├── globalPlugins │ └── quickNotetaker │ │ ├── addonConfig.py │ │ ├── constants.py │ │ ├── __init__.py │ │ ├── notesManager.py │ │ ├── settingsPanel.py │ │ ├── helpers.py │ │ └── dialogs.py ├── doc │ └── zh_CN │ │ └── readme.md └── locale │ ├── zh_CN │ └── LC_MESSAGES │ │ └── nvda.po │ ├── he │ └── LC_MESSAGES │ │ └── nvda.po │ ├── ar │ └── LC_MESSAGES │ │ └── nvda.po │ ├── de │ └── LC_MESSAGES │ │ └── nvda.po │ └── ru │ └── LC_MESSAGES │ └── nvda.po ├── site_scons └── site_tools │ └── gettexttool │ └── __init__.py ├── todo.md ├── sconstruct ├── readme.md └── COPYING.txt /manifest-translated.ini.tpl: -------------------------------------------------------------------------------- 1 | summary = "{addon_summary}" 2 | description = """{addon_description}""" 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | addon/doc/*.css 2 | addon/doc/en/ 3 | *_docHandler.py 4 | *.html 5 | *.ini 6 | *.mo 7 | *.pot 8 | *.py[co] 9 | *.nvda-addon 10 | .sconsign.dblite 11 | -------------------------------------------------------------------------------- /manifest.ini.tpl: -------------------------------------------------------------------------------- 1 | name = {addon_name} 2 | summary = "{addon_summary}" 3 | description = """{addon_description}""" 4 | author = "{addon_author}" 5 | url = {addon_url} 6 | version = {addon_version} 7 | docFileName = {addon_docFileName} 8 | minimumNVDAVersion = {addon_minimumNVDAVersion} 9 | lastTestedNVDAVersion = {addon_lastTestedNVDAVersion} 10 | updateChannel = {addon_updateChannel} 11 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set default behaviour, in case users don't have core.autocrlf set. 2 | * text=auto 3 | 4 | # Try to ensure that po files in the repo does not include 5 | # source code line numbers. 6 | # Every person expected to commit po files should change their personal config file as described here: 7 | # https://mail.gnome.org/archives/kupfer-list/2010-June/msg00002.html 8 | *.po filter=cleanpo 9 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | body { 3 | font-family : Verdana, Arial, Helvetica, Sans-serif; 4 | line-height: 1.2em; 5 | } 6 | h1, h2 {text-align: center} 7 | dt { 8 | font-weight : bold; 9 | float : left; 10 | width: 10%; 11 | clear: left 12 | } 13 | dd { 14 | margin : 0 0 0.4em 0; 15 | float : left; 16 | width: 90%; 17 | display: block; 18 | } 19 | p { clear : both; 20 | } 21 | a { text-decoration : underline; 22 | } 23 | :active { 24 | text-decoration : none; 25 | } 26 | a:focus, a:hover {outline: solid} 27 | -------------------------------------------------------------------------------- /addon/globalPlugins/quickNotetaker/addonConfig.py: -------------------------------------------------------------------------------- 1 | # addonConfig.py 2 | # -*- coding: utf-8 -*- 3 | # A part from Quick Notetaker add-on 4 | # Copyright (C) 2022 NV Access Limited, Mohammad Suliman, Eilana Benish 5 | # This file is covered by the GNU General Public License. 6 | # See the file COPYING for more details. 7 | 8 | import config 9 | import os 10 | import wx 11 | 12 | 13 | def initialize(): 14 | configSpec = { 15 | "notesDocumentsPath": f"string(default={os.path.normpath(os.path.expanduser('~/documents/quickNotetaker'))})", 16 | "askWhereToSaveDocx": "boolean(default=False)", 17 | "openFileAfterCreation": "boolean(default=False)", 18 | "captureActiveWindowTitle": "boolean(default=True)", 19 | "rememberTakerSizeAndPos": "boolean(default=False)", 20 | "autoAlignText": "boolean(default=true)", 21 | "takerXPos": f"integer(default={wx.DefaultPosition.x})", 22 | "takerYPos": f"integer(default={wx.DefaultPosition.y})", 23 | "takerWidth": "integer(default=500)", 24 | "takerHeight": "integer(default=500)", 25 | } 26 | config.conf.spec["quickNotetaker"] = configSpec 27 | 28 | 29 | def getValue(key): 30 | return config.conf["quickNotetaker"][key] 31 | 32 | 33 | def setValue(key, value): 34 | config.conf["quickNotetaker"][key] = value 35 | -------------------------------------------------------------------------------- /addon/globalPlugins/quickNotetaker/constants.py: -------------------------------------------------------------------------------- 1 | # constants.py 2 | # -*- coding: utf-8 -*- 3 | # A part from Quick Notetaker add-on 4 | # Copyright (C) 2022 NV Access Limited, Mohammad Suliman, Eilana Benish 5 | # This file is covered by the GNU General Public License. 6 | # See the file COPYING for more details. 7 | 8 | import globalVars 9 | import os 10 | 11 | CONFIG_PATH = globalVars.appArgs.configPath 12 | 13 | QUICK_NOTETAKER_PATH = os.path.join( 14 | CONFIG_PATH, "addons", "quickNotetaker", "globalPlugins") 15 | 16 | QUICK_NOTETAKER_PATH_DEV = os.path.join( 17 | CONFIG_PATH, "scratchpad", "globalPlugins", "quickNotetaker") 18 | 19 | # Remember to comment out in production 20 | # QUICK_NOTETAKER_PATH = QUICK_NOTETAKER_PATH_DEV 21 | 22 | DATA_DIR_PATH = os.path.join(CONFIG_PATH, "Quick Notetaker data") 23 | 24 | DATA_FILE_PATH = os.path.join(DATA_DIR_PATH, "notes.json") 25 | 26 | PANDOC_PATH = os.path.join( 27 | QUICK_NOTETAKER_PATH, "quickNotetaker", "lib", "pandoc-3.1.2", "pandoc") 28 | 29 | PANDOC_PATH_DEV = os.path.join( 30 | QUICK_NOTETAKER_PATH_DEV, "lib", "pandoc-3.1.2", "pandoc") 31 | 32 | # Remember to comment out in production 33 | # PANDOC_PATH = PANDOC_PATH_DEV 34 | 35 | TEMP_FILES_PATH = os.path.join(QUICK_NOTETAKER_PATH, "tempFiles") 36 | 37 | DEFAULT_DOCUMENTS_PATH = os.path.normpath( 38 | os.path.expanduser("~/documents/quickNotetaker")) 39 | -------------------------------------------------------------------------------- /site_scons/site_tools/gettexttool/__init__.py: -------------------------------------------------------------------------------- 1 | """ This tool allows generation of gettext .mo compiled files, pot files from source code files 2 | and pot files for merging. 3 | 4 | Three new builders are added into the constructed environment: 5 | 6 | - gettextMoFile: generates .mo file from .pot file using msgfmt. 7 | - gettextPotFile: Generates .pot file from source code files. 8 | - gettextMergePotFile: Creates a .pot file appropriate for merging into existing .po files. 9 | 10 | To properly configure get text, define the following variables: 11 | 12 | - gettext_package_bugs_address 13 | - gettext_package_name 14 | - gettext_package_version 15 | 16 | 17 | """ 18 | from SCons.Action import Action 19 | 20 | 21 | def exists(env): 22 | return True 23 | 24 | 25 | XGETTEXT_COMMON_ARGS = ( 26 | "--msgid-bugs-address='$gettext_package_bugs_address' " 27 | "--package-name='$gettext_package_name' " 28 | "--package-version='$gettext_package_version' " 29 | "--keyword=pgettext:1c,2 " 30 | "-c -o $TARGET $SOURCES" 31 | ) 32 | 33 | 34 | def generate(env): 35 | env.SetDefault(gettext_package_bugs_address="example@example.com") 36 | env.SetDefault(gettext_package_name="") 37 | env.SetDefault(gettext_package_version="") 38 | 39 | env['BUILDERS']['gettextMoFile'] = env.Builder( 40 | action=Action("msgfmt -o $TARGET $SOURCE", "Compiling translation $SOURCE"), 41 | suffix=".mo", 42 | src_suffix=".po" 43 | ) 44 | 45 | env['BUILDERS']['gettextPotFile'] = env.Builder( 46 | action=Action("xgettext " + XGETTEXT_COMMON_ARGS, "Generating pot file $TARGET"), 47 | suffix=".pot") 48 | 49 | env['BUILDERS']['gettextMergePotFile'] = env.Builder( 50 | action=Action( 51 | "xgettext " + "--omit-header --no-location " + XGETTEXT_COMMON_ARGS, 52 | "Generating pot file $TARGET" 53 | ), 54 | suffix=".pot" 55 | ) 56 | -------------------------------------------------------------------------------- /todo.md: -------------------------------------------------------------------------------- 1 | ## Users suggestions 2 | 3 | Those are suggestions by users we document here. The suggestions haven't been discussed internally, so there is no guarantee they will make their way to production. 4 | 5 | * Allow the notes to be tagged. Example tags might be: personal, education, miscellaneous, etc. 6 | * Allow the notes to be organized according to their tags. Maybe a tree view can be used, maybe something else. 7 | * Find a mechanism to allow the user to select a text and then create a note using a key stroke without the need to open any other interface. To achieve this, I think that copying to the clipboard is a mandatory intermediate stage so the add-on can access the selected text. 8 | * Create a mechanism to get the page number the user was reading when they created the note. This info can be found in the status bar. Note however that each program might has a different method to obtain this data, so we need to take into consideration all popular programs and maybe handle each one separately. 9 | * Make the place where we save the notes themself configurable. Currently the notes are saved in NVDA's directory in app data, and the user has no option to change this. Having this option configurable will allow the user to save the notes in an online service such as One Drive or Dropbox for example, so the notes can be synced between different devices. 10 | * Create a mechanism to sync notes between different devices. Note that this feature will be very tricky to implement because of the complexity, and the feasibility of this as well when taking into account that NVDA may not ship with the required libraries. 11 | * Add support for markdown extensions, especially the popular ones such as those that support tables and checkboxes. 12 | * Make the label of the control to specify the place where to save the notes documents emphasize that this corresponds to the Word documents for the notes, and not the notes themself. Also we need to combine this with the option to let the user to choose where the notes themselves are stored, and to create an intuitive UX for this. 13 | 14 | ## Features we plan to investigate 15 | 16 | * Create an option to allow to send the note as email directly using the add-on interface. That is: we may have a button in the interface to open the default email program with the email body as the note content, and the subject as the title. The user needs to fil out the to field, maybe CC and BCC fields also. The first thing to check if this is technically possible. 17 | * Allow the user to press a key combination to start to record NVDA speech in a new note. The user will have another key combination to stop recording. The note maybe saved automatically without displaying any interface, or the Notetaker interface will be opened with the recorded speech as the note content so the user can edit. Maybe this behavior needs to be configurable. 18 | -------------------------------------------------------------------------------- /addon/globalPlugins/quickNotetaker/__init__.py: -------------------------------------------------------------------------------- 1 | # __init__.py 2 | # -*- coding: utf-8 -*- 3 | # A part from Quick Notetaker add-on 4 | # Copyright (C) 2022 NV Access Limited, Mohammad Suliman, Eilana Benish 5 | # This file is covered by the GNU General Public License. 6 | # See the file COPYING for more details. 7 | 8 | from logHandler import log 9 | import globalPluginHandler 10 | from scriptHandler import script 11 | import gui 12 | from . import dialogs 13 | from .dialogs import NoteTakerDialog, NotesManagerDialog 14 | from .settingsPanel import QuickNotetakerPanel 15 | from . import notesManager 16 | import os 17 | from .constants import TEMP_FILES_PATH 18 | from . import addonConfig 19 | import api 20 | import addonHandler 21 | 22 | 23 | addonHandler.initTranslation() 24 | 25 | 26 | class GlobalPlugin(globalPluginHandler.GlobalPlugin): 27 | 28 | def __init__(self, *args, **kwargs): 29 | super(GlobalPlugin, self).__init__(*args, **kwargs) 30 | addonConfig.initialize() 31 | notesManager.initialize() 32 | try: 33 | os.mkdir(addonConfig.getValue("notesDocumentsPath")) 34 | except FileNotFoundError: 35 | # The user has no documents directory 36 | # Create the add-on documents folder in the user root folder instead 37 | addonConfig.setValue("notesDocumentsPath", os.path.expanduser("~\\QuickNotetaker")) 38 | os.mkdir(addonConfig.getValue("notesDocumentsPath")) 39 | except FileExistsError: 40 | pass 41 | try: 42 | os.mkdir(TEMP_FILES_PATH) 43 | except FileExistsError: 44 | pass 45 | gui.settingsDialogs.NVDASettingsDialog.categoryClasses.append( 46 | QuickNotetakerPanel) 47 | 48 | def terminate(self): 49 | super(GlobalPlugin, self).terminate() 50 | gui.settingsDialogs.NVDASettingsDialog.categoryClasses.remove( 51 | QuickNotetakerPanel) 52 | if not os.path.isdir(TEMP_FILES_PATH): 53 | return 54 | for file in os.listdir(TEMP_FILES_PATH): 55 | os.remove(os.path.join(TEMP_FILES_PATH, file)) 56 | 57 | # Translators: the name of the add-on category in input gestures 58 | scriptCategory=_("Quick Notetaker") 59 | 60 | @ script( 61 | # Translators: the description for the command to open the notetaker dialog 62 | description=_("Shows the Notetaker interface for writing a new note"), 63 | gesture="kb:NVDA+alt+n" 64 | ) 65 | def script_showNoteTakerUI(self, gesture): 66 | noteTitle=None 67 | if addonConfig.getValue("captureActiveWindowTitle"): 68 | noteTitle=api.getForegroundObject().name 69 | gui.mainFrame.prePopup() 70 | dialogs.noteTakerInstance=NoteTakerDialog(noteTitle=noteTitle) 71 | dialogs.noteTakerInstance.Show() 72 | gui.mainFrame.postPopup() 73 | 74 | @ script( 75 | description=_( 76 | # Translators: the description for the command to open the Notes Manager 77 | "Shows the Notes Manager interface for viewing and managing notes"), 78 | gesture="kb:NVDA+alt+v" 79 | ) 80 | def script_showNotesManagerDialogUI(self, gesture): 81 | gui.mainFrame.prePopup() 82 | dialogs.notesManagerInstance=NotesManagerDialog() 83 | dialogs.notesManagerInstance.Show() 84 | gui.mainFrame.postPopup() 85 | -------------------------------------------------------------------------------- /addon/globalPlugins/quickNotetaker/notesManager.py: -------------------------------------------------------------------------------- 1 | # notesManager.py 2 | # -*- coding: utf-8 -*- 3 | # A part from Quick Notetaker add-on 4 | # Copyright (C) 2022 NV Access Limited, Mohammad Suliman, Eilana Benish 5 | # This file is covered by the GNU General Public License. 6 | # See the file COPYING for more details. 7 | 8 | import os 9 | from logHandler import log 10 | from datetime import datetime 11 | import json 12 | from .constants import DATA_DIR_PATH, DATA_FILE_PATH 13 | from .helpers import getTitle 14 | 15 | 16 | class note(object): 17 | """A class that represents a single note""" 18 | 19 | def __init__(self, id="", title="", content="", lastEdited="", lastEditedStamp="", docxPath=""): 20 | if not id: 21 | self.id = datetime.now().strftime("%Y%m%d%H%M%S") 22 | self.title = getTitle(content) 23 | self.content = content 24 | self.lastEdited = self.prettyFormat(datetime.now()) 25 | self.lastEditedStamp = datetime.now().strftime("%Y%m%d%H%M%S") 26 | self.docxPath = docxPath 27 | else: 28 | self.id = id 29 | self.title = title 30 | self.content = content 31 | self.lastEdited = lastEdited 32 | self.lastEditedStamp = lastEditedStamp 33 | self.docxPath = docxPath 34 | 35 | def updateNote(self, newContent, docxPath): 36 | if newContent is not None: #and self.content != newContent: 37 | # for now, allow the user to edit the content even though the content hasn't changed to be able to overcome the title bug present in the first release 38 | self.title = getTitle(newContent) 39 | self.content = newContent 40 | self.lastEdited = self.prettyFormat(datetime.now()) 41 | self.lastEditedStamp = datetime.now().strftime("%Y%m%d%H%M%S") 42 | if docxPath is not None and self.docxPath != docxPath: 43 | self.docxPath = docxPath 44 | 45 | def prettyFormat(self, datetime): 46 | return f"{datetime.strftime('%d')}/{datetime.strftime('%m')}/{datetime.strftime('%Y')}, {datetime.strftime('%H')}:{datetime.strftime('%M')}" 47 | 48 | 49 | def initialize(): 50 | if os.path.isfile(DATA_FILE_PATH): 51 | return 52 | log.debug(f"Creating a new file {os.path.abspath(DATA_FILE_PATH)}") 53 | try: 54 | if not os.path.isdir(DATA_DIR_PATH): 55 | os.mkdir(DATA_DIR_PATH) 56 | except: 57 | log.error("Can't create the data file directory!") 58 | raise 59 | try: 60 | with open(DATA_FILE_PATH, mode="x", encoding="utf8") as file: 61 | file.write("[]") 62 | except: 63 | log.error("Can't create data file") 64 | raise 65 | 66 | 67 | def loadAllNotes(): 68 | with open(DATA_FILE_PATH, mode="r", encoding="utf8") as file: 69 | allNotes = json.load(file, object_hook=deserializeNote) 70 | return allNotes 71 | 72 | 73 | def deserializeNote(dict): 74 | deserializedNote = note( 75 | dict['id'], dict['title'], dict['content'], dict['lastEdited'], dict['lastEditedStamp'], dict['docxPath']) 76 | return deserializedNote 77 | 78 | 79 | def _dumpAllNotes(allNotes): 80 | # Backup the file content 81 | with open(DATA_FILE_PATH, mode="r", encoding="utf8") as file: 82 | allContent = file.read() 83 | # Sort all notes according to the last edited stamp in descending order 84 | allNotes.sort(key=lambda note: note.lastEditedStamp, reverse=True) 85 | try: 86 | with open(DATA_FILE_PATH, mode="w", encoding="utf8") as file: 87 | json.dump([note.__dict__ for note in allNotes], 88 | file, indent=4, ensure_ascii=False) 89 | except: 90 | with open(dataFilePath, mode="w", encoding="utf8") as file: 91 | file.write(allContent) 92 | raise 93 | 94 | 95 | def saveNewNote(noteContent, docxPath=""): 96 | newNote = note(content=noteContent, docxPath=docxPath) 97 | allNotes = loadAllNotes() 98 | allNotes.append(newNote) 99 | _dumpAllNotes(allNotes) 100 | 101 | 102 | def deleteNote(noteID): 103 | allNotes = loadAllNotes() 104 | for note in allNotes: 105 | if note.id == noteID: 106 | allNotes.remove(note) 107 | break 108 | _dumpAllNotes(allNotes) 109 | 110 | 111 | def updateNote(noteID, newContent=None, docxPath=None): 112 | allNotes = loadAllNotes() 113 | for note in allNotes: 114 | if note.id == noteID: 115 | note.updateNote(newContent, docxPath) 116 | break 117 | _dumpAllNotes(allNotes) 118 | -------------------------------------------------------------------------------- /addon/globalPlugins/quickNotetaker/settingsPanel.py: -------------------------------------------------------------------------------- 1 | # settings.py 2 | # -*- coding: utf-8 -*- 3 | # A part from Quick Notetaker add-on 4 | # Copyright (C) 2022 NV Access Limited, Mohammad Suliman, Eilana Benish 5 | # This file is covered by the GNU General Public License. 6 | # See the file COPYING for more details. 7 | 8 | import os 9 | import gui 10 | import wx 11 | from . import addonConfig 12 | import addonHandler 13 | 14 | addonHandler.initTranslation() 15 | 16 | 17 | class QuickNotetakerPanel(gui.SettingsPanel): 18 | 19 | # Translators: the title of the Quick Notetaker panel in NVDA's settings 20 | title = _("Quick Notetaker") 21 | 22 | def makeSettings(self, settingsSizer): 23 | sHelper = gui.guiHelper.BoxSizerHelper(self, sizer=settingsSizer) 24 | # Translators: the label of the control in Quick Notetaker settings panel for choosing a default folder where the add-on documents will be saved 25 | directoryGroupText = _("Default documents directory:") 26 | groupSizer = wx.StaticBoxSizer( 27 | wx.VERTICAL, self, label=directoryGroupText) 28 | groupHelper = sHelper.addItem( 29 | gui.guiHelper.BoxSizerHelper(self, sizer=groupSizer)) 30 | groupBox = groupSizer.GetStaticBox() 31 | # Translators: the label of a button to browse for a directory 32 | browseText = _("Browse...") 33 | dirDialogTitle = _( 34 | # Translators: The title of the dialog presented when browsing for the directory where quick notetaker documents will be stored 35 | "Select a default directory where the documents of Quick Notetaker will be stored") 36 | directoryPathHelper = gui.guiHelper.PathSelectionHelper( 37 | groupBox, browseText, dirDialogTitle) 38 | directoryEntryControl = groupHelper.addItem(directoryPathHelper) 39 | self.documentDirectoryEdit = directoryEntryControl.pathControl 40 | self.documentDirectoryEdit.Value = addonConfig.getValue( 41 | "notesDocumentsPath") 42 | askWhereToSaveDocxText = _( 43 | # Translators: the label of a check box in Quick Notetaker settings panel 44 | "Ask me each time &where to save the note's corresponding Microsoft Word document") 45 | self.askWhereToSaveDocxCheckbox = sHelper.addItem( 46 | wx.CheckBox(self, label=askWhereToSaveDocxText)) 47 | self.askWhereToSaveDocxCheckbox.Value = addonConfig.getValue( 48 | "askWhereToSaveDocx") 49 | openFileAfterCreationText = _( 50 | # Translators: the label of a check box in Quick Notetaker settings panel 51 | "&Open the note's corresponding Microsoft Word document after saving or updating") 52 | self.openAfterCreationCheckbox = sHelper.addItem( 53 | wx.CheckBox(self, label=openFileAfterCreationText)) 54 | self.openAfterCreationCheckbox.Value = addonConfig.getValue( 55 | "openFileAfterCreation") 56 | captureActiveWindowTitleText = _( 57 | # Translators: the label of a check box in Quick Notetaker settings panel 58 | "&Capture the active window title when creating a new note") 59 | self.captureActiveWindowTitleCheckbox = sHelper.addItem( 60 | wx.CheckBox(self, label=captureActiveWindowTitleText)) 61 | self.captureActiveWindowTitleCheckbox.Value = addonConfig.getValue( 62 | "captureActiveWindowTitle") 63 | rememberTakerSizeAndPosText = _( 64 | # Translators: the label of a check box in Quick Notetaker settings panel 65 | "&Remember the note taker window size and position") 66 | self.rememberTakerSizeAndPosCheckbox = sHelper.addItem( 67 | wx.CheckBox(self, label=rememberTakerSizeAndPosText)) 68 | self.rememberTakerSizeAndPosCheckbox.Value = addonConfig.getValue( 69 | "rememberTakerSizeAndPos") 70 | autoAlignTextText = _( 71 | # Translators: the label of a check box in Quick Notetaker settings panel 72 | "Au&to align text when editing notes (relevant for RTL languages)") 73 | self.autoAlignTextCheckbox = sHelper.addItem( 74 | wx.CheckBox(self, label=autoAlignTextText)) 75 | self.autoAlignTextCheckbox.Value = addonConfig.getValue( 76 | "autoAlignText") 77 | 78 | def onSave(self): 79 | addonConfig.setValue( 80 | "notesDocumentsPath", 81 | os.path.normpath(self.documentDirectoryEdit.Value)) 82 | addonConfig.setValue( 83 | "askWhereToSaveDocx", 84 | self.askWhereToSaveDocxCheckbox.Value) 85 | addonConfig.setValue( 86 | "openFileAfterCreation", 87 | self.openAfterCreationCheckbox.Value) 88 | addonConfig.setValue( 89 | "captureActiveWindowTitle", 90 | self.captureActiveWindowTitleCheckbox.Value) 91 | addonConfig.setValue( 92 | "rememberTakerSizeAndPos", 93 | self.rememberTakerSizeAndPosCheckbox.Value) 94 | addonConfig.setValue( 95 | "autoAlignText", 96 | self.autoAlignTextCheckbox.Value) 97 | -------------------------------------------------------------------------------- /addon/doc/zh_CN/readme.md: -------------------------------------------------------------------------------- 1 | # NVDA 快捷笔记插件 2 | 3 | 快捷笔记插件是一个很好的工具,可以随时随地在用户使用的任何应用程序中快速而轻松地撰写笔记。 4 | 例如,无论用户是在观看视频,还是在参加Zoom、Teams 或 Google 会议,都可一件创建笔记。 5 | 只需按下 NVDA + Alt + n 组合键,屏幕的左上角就会出现一个浮动窗口。 6 | 随后就可以在新弹出的笔记窗口中创建笔记了。 7 | 8 | 在您每次创建笔记时都可以自动捕捉当前窗口的标题作为笔记的一部分,当然这是可选设置。 9 | 利用该特性,后续您可以通过笔记的标题从而得知这个笔记是在什么情况下创建的。 10 | 用户可以在插件的设置面板中调整该设置以决定插件是否自动捕捉当前窗口标题。 11 | 12 | ## Notetaker 对话框 13 | 14 | - 笔记编辑区。 15 | 16 | 当打开记事本界面时,焦点将处于这个编辑区。支持用Markdown(一种标记语言,可以轻松转换为 HTML格式)来撰写笔记内容。 17 | 想了解更多关于 Markdown 的更多信息,请访问[Markdown指南页面](https://www.markdownguide.org/)。 18 | 19 | - 预览笔记:在一个HTML窗口中查看已撰写的笔记。 20 | 21 | - 复制:将笔记内容复制到剪贴板上。 22 | 23 | - 复制 HTML 代码:复制笔记内容的 HTML 代码。对于那些用 Markdown 写作的人来说这是个很有用的功能。 24 | 25 | - 一个复选框,可以将笔记保存为 Microsoft Word 文档,或者更新相应的笔记(如果存在)。 26 | 27 | - 一个保存并关闭按钮。 28 | 29 | - 一个丢弃按钮,在需要时丢弃修改。当存在未保存的修改时,会向用户显示一条警告信息,询问您是否要退出并放弃所有更改。 30 | 31 | ## 笔记管理器界面 32 | 33 | ### 打开和关闭 34 | 35 | - NVDA + Alt + V 可以打开笔记管理器界面。 36 | 37 | - 使用 Escape 键或窗口底部的关闭按钮可以关闭该界面。 38 | 39 | ### 笔记列表 40 | 41 | 笔记是以表格的形式呈现的,其中包括。 42 | 43 | 1. 笔记的标题。如果笔记内容中不是自动补货的窗口标题,那么会将笔记内容的第一行是为标题。 44 | 45 | 2. 最后依次编辑的时间戳。 46 | 47 | 3. 笔记内容的预览文本。 48 | 49 | ### 笔记管理器界面中的可用选项 50 | 51 | - 查看笔记:在一个HTML窗口中查看笔记。 52 | 53 | - 编辑笔记:打开笔记,并在 Notetaker 界面中进行编辑。 54 | 55 | - 复制笔记:将笔记内容复制到剪贴板上。 56 | 57 | - 创建 Microsoft Word 文档。创建一个Microsoft Word文档 58 | 59 | - 在 Microsoft Word 中打开:打开该笔记的 Microsoft Word 版文档。 60 | 61 | - 复制 HTML 代码:复制该笔记的HTML代码。对于那些使用 Markdown 写作的人来说这是个很有用的功能 62 | 63 | - 删除笔记:在删除笔记前会显示一个警告。 64 | 65 | - 新建笔记:从这个界面可以进入Notetaker界面创建新笔记。 66 | 67 | - 打开设置:从这里也可以打开插件的设置。 68 | 69 | - 关闭:关闭该窗口。 70 | 71 | ## 插件的设置 72 | 73 | 插件设置是 NVDA 设置界面的一部分。 74 | 若想转到该设置,可使用 NVDA + n 打开 NVDA 菜单。 75 | 选择“选项” > “设置” 然后向下找到“快捷笔记”类别。 76 | 77 | 在设置界面,用户可以: 78 | 79 | - 默认文档目录:选择保存笔记的默认目录,快捷笔记文档将保存在该目录下。 80 | 可以点击“浏览”按钮以更改该目录。 81 | 82 | - 每次都询问我 Microsoft Word 文档保存到何处:一个复选框(默认情况下未选中)--显示选择文档位置的对话框。以便在笔记的每次保存或更新操作时让用户选择文档的保存位置。 83 | 84 | - 保存或更新后,打开笔记对应的Microsoft Word文档。一个复选框(默认情况下未选中)--允许用户选择是否在保存或更新操作后打开Microsoft Word文档。 85 | 86 | - 新建笔记时使用所聚焦窗口的标题作为笔记标题:一个复选框(默认选中)--允许捕捉用户在创建笔记时的当前窗口标题。此标题也会成为 Microsoft Word 文档的标题(如果有的话)。 87 | 88 | - 记住笔记窗口的大小和位置:一个复选框(默认情况下未选中)--创建或编辑笔记时记住上次笔记窗口的大小和位置。 89 | - 编辑笔记时自动对齐文本 (适用于从右到左的语言环境): 90 | 一个复选框(默认选中)--控制文本的对齐方式,这取决于您的语言环境,例如,如果使用的语言是阿拉伯语或希伯来语,选中此选项时将右对齐,如果语言是英语或法语,选中此选项时文本将左对齐。 91 | 92 | ## 键盘快捷键 93 | 94 | - NVDA + Alt + n:打开 Notetaker 界面。 95 | 96 | - NVDA + Alt + v:打开笔记管理器界面。 97 | 98 | ### 不同界面中的键盘快捷键(英文) 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 |
InterfaceCommandKeyboard shortcut
NotetakerFocus the note edit areaAlt + n
NotetakerAlign text to the rightControl + r
NotetakerAlign text to the leftControl + l
NotetakerPreview note in an HTML windowAlt + r
NotetakerCopyAlt + p
NotetakerCopy HTML codeAlt + h
NotetakerSave note as a Microsoft Word documentAlt + w
NotetakerUpdate the note corresponding Microsoft Word documentAlt + w
NotetakerSave and closeAlt + s
NotetakerDiscardAlt + d
NotetakerOpen notes ManagerAlt + m
Notes ManagerView noteAlt + v
Notes ManagerEdit noteAlt + e
Notes ManagerCopy noteAlt + p
Notes ManagerOpen in Microsoft Word (if such a document exists)Alt + o
Notes ManagerCreate a word document for a saved noteAlt + w
Notes ManagerCopy HTML codeAlt + h
Notes ManagerDelete noteAlt + d
Notes ManagerNew noteAlt + n
Notes ManagerOpen settingsAlt + s
Notes ManagerClose the interfaceAlt + c
The settings interfaceAsk me each time where to save the note's corresponding Microsoft Word documentAlt + w
The settings interfaceOpen the note's corresponding Microsoft Word document after saving or updatingAlt + o
The settings interfaceCapture the active window title when creating a new noteAlt + c
The settings interfaceRemember the note taker window size and positionAlt + r
The settings interfaceAuto align text when editing notes (relevant for RTL languages)Alt + t
246 | 247 | ## 致谢 248 | 249 | - 插件与 Pandoc 密不可分, Pandoc 是一个很棒的工具,它能够在不同格式之间转换文档。没有此工具插件将无法实现现有功能。有关 Pandoc 的更多信息请[访问 Pandoc 主页](https://pandoc.org/)。 250 | 251 | - 插件还依赖于一个名为 Python Markdown 的包,关于此包的更多信息请访问[Github 页面](https://github.com/trentm/python-markdown2)。 252 | 253 | - 非常感谢 NV Access、插件作者以及贡献者!插件的很多设计灵感源于你们的作品和贡献,所以让我们一起保持这个伟大社区的活跃吧! 254 | -------------------------------------------------------------------------------- /sconstruct: -------------------------------------------------------------------------------- 1 | # NVDA add-on template SCONSTRUCT file 2 | # Copyright (C) 2012-2021 Rui Batista, Noelia Martinez, Joseph Lee 3 | # This file is covered by the GNU General Public License. 4 | # See the file COPYING.txt for more details. 5 | 6 | import codecs 7 | import gettext 8 | import os 9 | import os.path 10 | import zipfile 11 | import sys 12 | 13 | # While names imported below are available by default in every SConscript 14 | # Linters aren't aware about them. 15 | # To avoid Flake8 F821 warnings about them they are imported explicitly. 16 | # When using other Scons functions please add them to the line below. 17 | from SCons.Script import BoolVariable, Builder, Copy, Environment, Variables 18 | 19 | sys.dont_write_bytecode = True 20 | 21 | # Bytecode should not be written for build vars module to keep the repository root folder clean. 22 | import buildVars # NOQA: E402 23 | 24 | 25 | def md2html(source, dest): 26 | import markdown 27 | # Use extensions if defined. 28 | mdExtensions = buildVars.markdownExtensions 29 | lang = os.path.basename(os.path.dirname(source)).replace('_', '-') 30 | localeLang = os.path.basename(os.path.dirname(source)) 31 | try: 32 | _ = gettext.translation("nvda", localedir=os.path.join("addon", "locale"), languages=[localeLang]).gettext 33 | summary = _(buildVars.addon_info["addon_summary"]) 34 | except Exception: 35 | summary = buildVars.addon_info["addon_summary"] 36 | title = "{addonSummary} {addonVersion}".format( 37 | addonSummary=summary, addonVersion=buildVars.addon_info["addon_version"] 38 | ) 39 | headerDic = { 40 | "[[!meta title=\"": "# ", 41 | "\"]]": " #", 42 | } 43 | with codecs.open(source, "r", "utf-8") as f: 44 | mdText = f.read() 45 | for k, v in headerDic.items(): 46 | mdText = mdText.replace(k, v, 1) 47 | htmlText = markdown.markdown(mdText, extensions=mdExtensions) 48 | # Optimization: build resulting HTML text in one go instead of writing parts separately. 49 | docText = "\n".join([ 50 | "", 51 | "" % lang, 52 | "", 53 | "" 54 | "", 55 | "", 56 | "%s" % title, 57 | "\n", 58 | htmlText, 59 | "\n" 60 | ]) 61 | with codecs.open(dest, "w", "utf-8") as f: 62 | f.write(docText) 63 | 64 | 65 | def mdTool(env): 66 | mdAction = env.Action( 67 | lambda target, source, env: md2html(source[0].path, target[0].path), 68 | lambda target, source, env: 'Generating % s' % target[0], 69 | ) 70 | mdBuilder = env.Builder( 71 | action=mdAction, 72 | suffix='.html', 73 | src_suffix='.md', 74 | ) 75 | env['BUILDERS']['markdown'] = mdBuilder 76 | 77 | 78 | vars = Variables() 79 | vars.Add("version", "The version of this build", buildVars.addon_info["addon_version"]) 80 | vars.Add(BoolVariable("dev", "Whether this is a daily development version", False)) 81 | vars.Add("channel", "Update channel for this build", buildVars.addon_info["addon_updateChannel"]) 82 | 83 | env = Environment(variables=vars, ENV=os.environ, tools=['gettexttool', mdTool]) 84 | env.Append(**buildVars.addon_info) 85 | 86 | if env["dev"]: 87 | import datetime 88 | buildDate = datetime.datetime.now() 89 | year, month, day = str(buildDate.year), str(buildDate.month), str(buildDate.day) 90 | env["addon_version"] = "".join([year, month.zfill(2), day.zfill(2), "-dev"]) 91 | env["channel"] = "dev" 92 | elif env["version"] is not None: 93 | env["addon_version"] = env["version"] 94 | if "channel" in env and env["channel"] is not None: 95 | env["addon_updateChannel"] = env["channel"] 96 | 97 | buildVars.addon_info["addon_version"] = env["addon_version"] 98 | buildVars.addon_info["addon_updateChannel"] = env["addon_updateChannel"] 99 | 100 | addonFile = env.File("${addon_name}-${addon_version}.nvda-addon") 101 | 102 | 103 | def addonGenerator(target, source, env, for_signature): 104 | action = env.Action( 105 | lambda target, source, env: createAddonBundleFromPath(source[0].abspath, target[0].abspath) and None, 106 | lambda target, source, env: "Generating Addon %s" % target[0] 107 | ) 108 | return action 109 | 110 | 111 | def manifestGenerator(target, source, env, for_signature): 112 | action = env.Action( 113 | lambda target, source, env: generateManifest(source[0].abspath, target[0].abspath) and None, 114 | lambda target, source, env: "Generating manifest %s" % target[0] 115 | ) 116 | return action 117 | 118 | 119 | def translatedManifestGenerator(target, source, env, for_signature): 120 | dir = os.path.abspath(os.path.join(os.path.dirname(str(source[0])), "..")) 121 | lang = os.path.basename(dir) 122 | action = env.Action( 123 | lambda target, source, env: generateTranslatedManifest(source[1].abspath, lang, target[0].abspath) and None, 124 | lambda target, source, env: "Generating translated manifest %s" % target[0] 125 | ) 126 | return action 127 | 128 | 129 | env['BUILDERS']['NVDAAddon'] = Builder(generator=addonGenerator) 130 | env['BUILDERS']['NVDAManifest'] = Builder(generator=manifestGenerator) 131 | env['BUILDERS']['NVDATranslatedManifest'] = Builder(generator=translatedManifestGenerator) 132 | 133 | 134 | def createAddonHelp(dir): 135 | docsDir = os.path.join(dir, "doc") 136 | if os.path.isfile("style.css"): 137 | cssPath = os.path.join(docsDir, "style.css") 138 | cssTarget = env.Command(cssPath, "style.css", Copy("$TARGET", "$SOURCE")) 139 | env.Depends(addon, cssTarget) 140 | if os.path.isfile("readme.md"): 141 | readmePath = os.path.join(docsDir, buildVars.baseLanguage, "readme.md") 142 | readmeTarget = env.Command(readmePath, "readme.md", Copy("$TARGET", "$SOURCE")) 143 | env.Depends(addon, readmeTarget) 144 | 145 | 146 | def createAddonBundleFromPath(path, dest): 147 | """ Creates a bundle from a directory that contains an addon manifest file.""" 148 | basedir = os.path.abspath(path) 149 | with zipfile.ZipFile(dest, 'w', zipfile.ZIP_DEFLATED) as z: 150 | # FIXME: the include/exclude feature may or may not be useful. Also python files can be pre-compiled. 151 | for dir, dirnames, filenames in os.walk(basedir): 152 | relativePath = os.path.relpath(dir, basedir) 153 | for filename in filenames: 154 | pathInBundle = os.path.join(relativePath, filename) 155 | absPath = os.path.join(dir, filename) 156 | if pathInBundle not in buildVars.excludedFiles: 157 | z.write(absPath, pathInBundle) 158 | return dest 159 | 160 | 161 | def generateManifest(source, dest): 162 | addon_info = buildVars.addon_info 163 | with codecs.open(source, "r", "utf-8") as f: 164 | manifest_template = f.read() 165 | manifest = manifest_template.format(**addon_info) 166 | with codecs.open(dest, "w", "utf-8") as f: 167 | f.write(manifest) 168 | 169 | 170 | def generateTranslatedManifest(source, language, out): 171 | _ = gettext.translation("nvda", localedir=os.path.join("addon", "locale"), languages=[language]).gettext 172 | vars = {} 173 | for var in ("addon_summary", "addon_description"): 174 | vars[var] = _(buildVars.addon_info[var]) 175 | with codecs.open(source, "r", "utf-8") as f: 176 | manifest_template = f.read() 177 | result = manifest_template.format(**vars) 178 | with codecs.open(out, "w", "utf-8") as f: 179 | f.write(result) 180 | 181 | 182 | def expandGlobs(files): 183 | return [f for pattern in files for f in env.Glob(pattern)] 184 | 185 | 186 | addon = env.NVDAAddon(addonFile, env.Dir('addon')) 187 | 188 | langDirs = [f for f in env.Glob(os.path.join("addon", "locale", "*"))] 189 | 190 | # Allow all NVDA's gettext po files to be compiled in source/locale, and manifest files to be generated 191 | for dir in langDirs: 192 | poFile = dir.File(os.path.join("LC_MESSAGES", "nvda.po")) 193 | moFile = env.gettextMoFile(poFile) 194 | env.Depends(moFile, poFile) 195 | translatedManifest = env.NVDATranslatedManifest( 196 | dir.File("manifest.ini"), 197 | [moFile, os.path.join("manifest-translated.ini.tpl")] 198 | ) 199 | env.Depends(translatedManifest, ["buildVars.py"]) 200 | env.Depends(addon, [translatedManifest, moFile]) 201 | 202 | pythonFiles = expandGlobs(buildVars.pythonSources) 203 | for file in pythonFiles: 204 | env.Depends(addon, file) 205 | 206 | # Convert markdown files to html 207 | # We need at least doc in English and should enable the Help button for the add-on in Add-ons Manager 208 | createAddonHelp("addon") 209 | for mdFile in env.Glob(os.path.join('addon', 'doc', '*', '*.md')): 210 | htmlFile = env.markdown(mdFile) 211 | env.Depends(htmlFile, mdFile) 212 | env.Depends(addon, htmlFile) 213 | 214 | # Pot target 215 | i18nFiles = expandGlobs(buildVars.i18nSources) 216 | gettextvars = { 217 | 'gettext_package_bugs_address': 'nvda-translations@groups.io', 218 | 'gettext_package_name': buildVars.addon_info['addon_name'], 219 | 'gettext_package_version': buildVars.addon_info['addon_version'] 220 | } 221 | 222 | pot = env.gettextPotFile("${addon_name}.pot", i18nFiles, **gettextvars) 223 | env.Alias('pot', pot) 224 | env.Depends(pot, i18nFiles) 225 | mergePot = env.gettextMergePotFile("${addon_name}-merge.pot", i18nFiles, **gettextvars) 226 | env.Alias('mergePot', mergePot) 227 | env.Depends(mergePot, i18nFiles) 228 | 229 | # Generate Manifest path 230 | manifest = env.NVDAManifest(os.path.join("addon", "manifest.ini"), os.path.join("manifest.ini.tpl")) 231 | # Ensure manifest is rebuilt if buildVars is updated. 232 | env.Depends(manifest, "buildVars.py") 233 | 234 | env.Depends(addon, manifest) 235 | env.Default(addon) 236 | env.Clean(addon, ['.sconsign.dblite', 'addon/doc/' + buildVars.baseLanguage + '/']) 237 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Quick Notetaker add-on for NVDA 2 | 3 | The Quick Notetaker add-on is a wonderful tool which allows writing 4 | notes quickly and easily anytime and from any app the user is using. 5 | Whether the user is watching a video for example, or participating in a 6 | meeting on Zoom, teams or Google meet, they can easily and smoothly open 7 | the notetaker and take a note. In order to create a quick note, NVDA + 8 | Alt + n key combination can be used, a floating window appears at the 9 | top left corner of the screen, so the note can be typed there. 10 | 11 | Every note that is being Created can optionally get the active window 12 | title, and as such, the note content can get the context in which this 13 | note was created by having the note title as the active window title the 14 | user was using. This behavior can be changed from the add-on settings, 15 | where the user can decide whether the active window title is captured 16 | when creating a new note. 17 | 18 | ## The Notetaker dialog 19 | 20 | - The note edit area: When opening the Notetaker interface the focus 21 | will be in this edit area. Writing with Markdown (a markup language 22 | to easily produce HTML content) is supported also. For more info on 23 | Markdown visit [the Markdown guide 24 | page](https://www.markdownguide.org/). 25 | 26 | - Preview note: to view the note in an HTML window. 27 | 28 | - Copy: to copy the note as is to the clipboard. 29 | 30 | - Copy HTML code: to copy the HTML code representing the note. A 31 | useful feature for those who write in Markdown. 32 | 33 | - A checkbox to allow saving the note as Microsoft Word also, or updating the corresponding one if it exists. 34 | 35 | - A save and close button. 36 | 37 | - A discard button to discard changes when desired. When unsaved changes exist, a warning message is displayed to the user asking if they are sure they want to exit and discard their changes. 38 | 39 | ## The Notes Manager interface 40 | 41 | ### Opening and closing this interface 42 | 43 | - NVDA + Alt + v will launch the Notes Manager interface. 44 | 45 | - Using either the Escape key or the close button found at the bottom 46 | of this window will close this interface. 47 | 48 | ### The notes list 49 | 50 | The notes are organized in a tabular list which includes: 51 | 52 | 1. The note title: If the note hasn’t got the active window title, the 53 | first line of the note will be the note title displayed in this 54 | list. 55 | 56 | 2. Last edited time stamp. 57 | 58 | 3. A preview text of the note content. 59 | 60 | ### The options available in Notes Manager interface 61 | 62 | - View note: to view the note in an HTML window. 63 | 64 | - Edit note: opens the note to be edited using Notetaker interface. 65 | 66 | - Copy note: copies the note content as is to the clipboard. 67 | 68 | - Create a Microsoft Word document: Creates a Microsoft Word document 69 | representing this note in case it has no such document. 70 | 71 | - Open in Microsoft Word: opens the Microsoft Word document attached 72 | to this note in case it has a one. 73 | 74 | - Copy HTML code: copies the HTML code representing this note. A 75 | useful feature for those who write in Markdown. 76 | 77 | - Delete note: displays a warning before performing the note deletion. 78 | 79 | - New note: the Notetaker interface can be reached from this interface 80 | to create a new note. 81 | 82 | - Open settings: opening the add-on settings is also possible from 83 | here. 84 | 85 | - Close: to close the window. 86 | 87 | ## The add-on settings 88 | 89 | The add-on settings are a part of NVDA’s settings interface. To reach 90 | those settings, the user needs to open the NVDA menu using NVDA key + n, 91 | choose preferences > settings, and then arrow down until reaching 92 | Quick Notetaker category. 93 | 94 | Using the settings interface the user can: 95 | 96 | - Default documents directory: to choose the default directory where 97 | Quick Notetaker documents will be saved. The user can press the 98 | “Browse” button to change the path of this directory. 99 | 100 | - Ask me each time where to save the note's corresponding Microsoft 101 | Word document: a checkbox (not checked by default) – to show the 102 | dialog for choosing the location where the document will be saved on 103 | each save or update operation for the note’s Microsoft Word document 104 | if such a one exists. 105 | 106 | - Open the note's corresponding Microsoft Word document after saving 107 | or updating: a checkbox (not checked by default) – to allow the user 108 | to choose whether the Microsoft Word document will be opened after a 109 | save or update operation in case the note has such document. 110 | 111 | - Capture the active window title when creating a new note: a checkbox 112 | (checked by default) – to allow the note to get the active window 113 | title the user was using when they created the note. This title will 114 | be also the title of the Microsoft Word document for the note in 115 | case it has a one. 116 | 117 | - Remember the note taker window size and position: a checkbox (not 118 | checked by default) – to tell the add-on to remember the size and 119 | the position of the Notetaker dialog when creating or editing a 120 | note. As such, when the user opens the dialog next time, the 121 | position and the size will be the same as the last time the dialog 122 | was used. The default position of this dialog is at the top left 123 | corner of the screen. 124 | 125 | - Auto align text when editing notes (relevant for RTL languages): a 126 | checkbox (checked by default) – to control whether the text when 127 | creating or editing a note should be auto aligned according to the 128 | language used. This is mostly relevant for right to left languages. 129 | For example, if the language used is Arabic or Hebrew, then the text 130 | will be right aligned when this option is chosen, if the language is 131 | English or French, then the text will be left aligned. 132 | 133 | ## Keyboard shortcuts 134 | 135 | - NVDA + Alt + n: to open the Notetaker interface. 136 | 137 | - NVDA + Alt + v: to open the Notes Manager interface. 138 | 139 | ### Keyboard shortcuts in the different interfaces 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 |
InterfaceCommandKeyboard shortcut
NotetakerFocus the note edit areaAlt + n
NotetakerAlign text to the rightControl + r
NotetakerAlign text to the leftControl + l
NotetakerPreview note in an HTML windowAlt + r
NotetakerCopyAlt + p
NotetakerCopy HTML codeAlt + h
NotetakerSave note as a Microsoft Word documentAlt + w
NotetakerUpdate the note corresponding Microsoft Word documentAlt + w
NotetakerSave and closeAlt + s
NotetakerDiscardAlt + d
NotetakerOpen notes ManagerAlt + m
Notes ManagerView noteAlt + v
Notes ManagerEdit noteAlt + e
Notes ManagerCopy noteAlt + p
Notes ManagerOpen in Microsoft Word (if such a document exists)Alt + o
Notes ManagerCreate a word document for a saved noteAlt + w
Notes ManagerCopy HTML codeAlt + h
Notes ManagerDelete noteAlt + d
Notes ManagerNew noteAlt + n
Notes ManagerOpen settingsAlt + s
Notes ManagerClose the interfaceAlt + c
The settings interfaceAsk me each time where to save the note's corresponding Microsoft Word documentAlt + w
The settings interfaceOpen the note's corresponding Microsoft Word document after saving or updatingAlt + o
The settings interfaceCapture the active window title when creating a new noteAlt + c
The settings interfaceRemember the note taker window size and positionAlt + r
The settings interfaceAuto align text when editing notes (relevant for RTL languages)Alt + t
287 | 288 | ## Acknowledgements 289 | 290 | - The add-on comes bundled with Pandoc, a wonderful tool which allows 291 | converting documents between different formats. Without this tool 292 | the add-on won’t be able to offer the capabilities it offers. For 293 | more info on Pandoc [visit the Pandoc 294 | homepage](https://pandoc.org/). 295 | 296 | - The add-on also relies on a Python Markdown package called 297 | markdown2. For more info on this package [visit the package GitHub 298 | page](https://github.com/trentm/python-markdown2). 299 | 300 | - A great thanks to NV Access, add-on authors, and contributors! 301 | Several parts of the add-on were inspired by your magnificent work 302 | and effort, so please keep up this brilliant ecosystem and the 303 | cooperation. 304 | -------------------------------------------------------------------------------- /addon/globalPlugins/quickNotetaker/helpers.py: -------------------------------------------------------------------------------- 1 | # helpers.py 2 | # -*- coding: utf-8 -*- 3 | # A part from Quick Notetaker add-on 4 | # Copyright (C) 2022 NV Access Limited, Mohammad Suliman, Eilana Benish 5 | # This file is covered by the GNU General Public License. 6 | # See the file COPYING for more details. 7 | 8 | from .constants import PANDOC_PATH, TEMP_FILES_PATH, DEFAULT_DOCUMENTS_PATH 9 | import subprocess 10 | import re 11 | import wx 12 | from .lib.markdown2 import markdown 13 | import threading 14 | import os 15 | from . import addonConfig 16 | from logHandler import log 17 | import unicodedata 18 | from enum import Enum 19 | from urllib.parse import urlparse 20 | import addonHandler 21 | 22 | addonHandler.initTranslation() 23 | 24 | 25 | def saveAsWord(mdContent, filePath, callback, *args): 26 | saveThread = threading.Thread(target=_saveAsWord, args=( 27 | mdContent, filePath, callback, *args), daemon=True) 28 | saveThread.start() 29 | 30 | 31 | def _saveAsWord(mdContent, filePath, callBack, *args): 32 | title = getTitle(mdContent) 33 | if not os.path.isdir(TEMP_FILES_PATH): 34 | os.mkdir(TEMP_FILES_PATH) 35 | with open(f"{TEMP_FILES_PATH}/{title}.md", mode="w+", encoding="utf8") as input: 36 | input.write(handleMdContent(mdContent)) 37 | dirWasChanged = False 38 | if filePath == "": 39 | outputFilePath, dirWasChanged = _findAvailablePath( 40 | addonConfig.getValue("notesDocumentsPath"), title, "docx") 41 | else: 42 | outputFilePath = filePath 43 | outputFilePath, result = _runPandocCommand( 44 | title, outputFilePath, isRtlDocument(mdContent)) 45 | dirWasChanged = dirWasChanged or result 46 | if callBack: 47 | callBack(outputFilePath, dirWasChanged, mdContent, *args) 48 | if addonConfig.getValue("openFileAfterCreation"): 49 | os.startfile(outputFilePath) 50 | 51 | 52 | def _runPandocCommand(fileTitle, outputFilePath, isHtmlDocument): 53 | pandocArgs = [PANDOC_PATH, "-f", "markdown", "-t", 54 | "docx", "-s", "-i", f"{TEMP_FILES_PATH}/{fileTitle}.md"] 55 | if isHtmlDocument: 56 | pandocArgs.extend(["-V", "dir[=rtl]"]) 57 | pandocArgs.append("-o") 58 | startupInfo = subprocess.STARTUPINFO() 59 | startupInfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW 60 | pandocArgs.append(outputFilePath) 61 | try: 62 | subprocess.run(pandocArgs, check=True, startupinfo=startupInfo) 63 | return outputFilePath, False 64 | except subprocess.CalledProcessError: 65 | pandocArgs.remove(outputFilePath) 66 | if not os.path.isdir(addonConfig.getValue("notesDocumentsPath")): 67 | os.mkdir(addonConfig.getValue("notesDocumentsPath")) 68 | log.debug( 69 | "the specified directory name is invalid! Reverting to the user default one.") 70 | outputFilePath = os.path.join(addonConfig.getValue( 71 | "notesDocumentsPath"), f"{fileTitle}.docx") 72 | pandocArgs.append(outputFilePath) 73 | try: 74 | subprocess.run(pandocArgs, check=True, startupinfo=startupInfo) 75 | return outputFilePath, True 76 | except subprocess.CalledProcessError: 77 | pandocArgs.remove(outputFilePath) 78 | if not os.path.isdir(DEFAULT_DOCUMENTS_PATH): 79 | os.mkdir(DEFAULT_DOCUMENTS_PATH) 80 | log.debug( 81 | "The specified directory name is invalid! Reverting to the add-on default one.") 82 | outputFilePath = os.path.join(DEFAULT_DOCUMENTS_PATH, f"{fileTitle}.docx") 83 | pandocArgs.append(outputFilePath) 84 | try: 85 | subprocess.run(pandocArgs, check=True, startupinfo=startupInfo) 86 | return outputFilePath, True 87 | except subprocess.CalledProcessError: 88 | raise 89 | 90 | 91 | def openInWord(filePath, callback, *args): 92 | openThread = threading.Thread( 93 | target=_openInWord, args=(filePath, callback, *args,), daemon=True) 94 | openThread.start() 95 | 96 | 97 | def _openInWord(filePath, callback, *args): 98 | result = False 99 | try: 100 | os.startfile(filePath) 101 | result = True 102 | except: 103 | pass 104 | if callback: 105 | callback(result, *args) 106 | 107 | 108 | def _findAvailablePath(dirName, fileTitle, extension): 109 | """Finds available file path if the given one is already used. 110 | We need this to avoid over riding existing files content""" 111 | dirWasChanged = False 112 | if not os.path.isdir(dirName): 113 | try: 114 | os.mkdir(dirName) 115 | except: 116 | log.debug( 117 | "The user default directory name is invalid! Reverting to the user default one.") 118 | if not os.path.isdir(DEFAULT_DOCUMENTS_PATH): 119 | os.mkdir(DEFAULT_DOCUMENTS_PATH) 120 | dirName = DEFAULT_DOCUMENTS_PATH 121 | dirWasChanged = True 122 | candidatePath = os.path.join(dirName, f"{fileTitle}.{extension}") 123 | if not os.path.isfile(candidatePath): 124 | return candidatePath, dirWasChanged 125 | for i in range(50): 126 | candidatePath = os.path.join( 127 | dirName, f"{fileTitle} ({i + 1}).{extension}") 128 | if not os.path.isfile(candidatePath): 129 | return candidatePath, dirWasChanged 130 | 131 | 132 | #: A regex for matching a URL 133 | #: Taken from https://gist.githubusercontent.com/nishad/ff5d02394afaf8cca5818f023fb88a21/raw/cc631328b9bfc0750379847ecbe415b4df69aa67/urlmarker.py 134 | urlPatternText =\ 135 | r"""(?i)\b((?:https?:(?:/{1,3}|[a-z0-9%])|[a-z0-9.\-]+[.](?:com|net|org|edu|gov|mil|aero|asia|biz|cat|coop|info|int|jobs|mobi|museum|name|post|pro|tel|travel|xxx|ac|ad|ae|af|ag|ai|al|am|an|ao|aq|ar|as|at|au|aw|ax|az|ba|bb|bd|be|bf|bg|bh|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co|cr|cs|cu|cv|cx|cy|cz|dd|de|dj|dk|dm|do|dz|ec|ee|eg|eh|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gg|gh|gi|gl|gm|gn|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|in|io|iq|ir|is|it|je|jm|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|me|mg|mh|mk|ml|mm|mn|mo|mp|mq|mr|ms|mt|mu|mv|mw|mx|my|mz|na|nc|ne|nf|ng|ni|nl|no|np|nr|nu|nz|om|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|ps|pt|pw|py|qa|re|ro|rs|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|Ja|sk|sl|sm|sn|so|sr|ss|st|su|sv|sx|sy|sz|tc|td|tf|tg|th|tj|tk|tl|tm|tn|to|tp|tr|tt|tv|tw|tz|ua|ug|uk|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|ye|yt|yu|za|zm|zw)/)(?:[^\s()<>{}\[\]]+|\([^\s()]*?\([^\s()]+\)[^\s()]*?\)|\([^\s]+?\))+(?:\([^\s()]*?\([^\s()]+\)[^\s()]*?\)|\([^\s]+?\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’])|(?:(?)+", " ", htmlText) 192 | # Remove extra spaces if any 193 | extracted = _removeExtraSpaces(extracted) 194 | return extracted 195 | 196 | 197 | def _removeExtraSpaces(text): 198 | text = re.sub(r" +", " ", text) 199 | text = text.strip() 200 | return text 201 | 202 | 203 | def _isRtlParagraph(paragraph): 204 | """determines if the given paragraph is rtl by relying on the first none html text letter of the paragraph""" 205 | rtlClasses = ["R", "AL", "AN"] 206 | # Delete HTML markup if it exists 207 | paragraphWithoutHtml = retrieveTextFromHtml(paragraph) 208 | lettersOnly = re.sub(r"\W+", "", paragraphWithoutHtml) 209 | if not lettersOnly: 210 | return None 211 | if unicodedata.bidirectional(lettersOnly[0]) in rtlClasses: 212 | return True 213 | else: 214 | return False 215 | 216 | 217 | def _makeRtl(content): 218 | """Makes the text rtl if needed by wrapping each paragraph starting with RTL char with a div which has dir = rtl""" 219 | paragraphs = content.split("\n\n") 220 | result = [] 221 | for paragraph in paragraphs: 222 | if _isRtlParagraph(paragraph): 223 | result.append( 224 | '
\n%s\n
' % paragraph) 225 | else: 226 | result.append('
\n%s\n
' % paragraph) 227 | return "\n\n".join(result) 228 | 229 | 230 | def isRtlDocument(content): 231 | """Determines if the document is RTL by checking if the document has atleast an rtl paragraph. If it has, then the document is rtl""" 232 | paragraphs = content.split("\n\n") 233 | for paragraph in paragraphs: 234 | if _isRtlParagraph(paragraph): 235 | return True 236 | return False 237 | 238 | 239 | class Align(Enum): 240 | ALIGN_TO_LEFT = 1 241 | ALIGN_TO_RIGHT = 2 242 | NO_CHANGE = 3 243 | 244 | 245 | def handleTextAlignment(text, currentAlignment): 246 | """Determines the alignment of the text relying on the first letter found in the text""" 247 | rtlClasses = ["R", "AL", "AN"] 248 | # Delete HTML markup if it exists 249 | lettersOnly = re.sub(r"\W+", "", text) 250 | if not lettersOnly or not addonConfig.getValue("autoAlignText"): 251 | return Align.NO_CHANGE 252 | if unicodedata.bidirectional(lettersOnly[0]) in rtlClasses and currentAlignment != wx.Layout_RightToLeft: 253 | return Align.ALIGN_TO_RIGHT 254 | elif not unicodedata.bidirectional(lettersOnly[0]) in rtlClasses and currentAlignment != wx.Layout_LeftToRight: 255 | return Align.ALIGN_TO_LEFT 256 | else: 257 | return Align.NO_CHANGE 258 | -------------------------------------------------------------------------------- /addon/locale/zh_CN/LC_MESSAGES/nvda.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the 'quickNotetaker' package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: 'quickNotetaker' '1.0'\n" 9 | "Report-Msgid-Bugs-To: 'nvda-translations@groups.io'\n" 10 | "POT-Creation-Date: 2021-10-08 21:49+0800\n" 11 | "PO-Revision-Date: 2021-10-08 21:50+0800\n" 12 | "Last-Translator: Mohammad Suliman \n" 13 | "Language-Team: Cary-rowen \n" 14 | "Language: zh\n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=UTF-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | "Plural-Forms: nplurals=1; plural=0;\n" 19 | "X-Generator: Poedit 2.4.3\n" 20 | 21 | #. Translators: the name of the add-on category in input gestures 22 | #. Translators: the title of the Quick Notetaker panel in NVDA's settings 23 | #. Add-on summary, usually the user visible name of the addon. 24 | #. Translators: Summary for this add-on 25 | #. to be shown on installation and add-on information found in Add-ons Manager. 26 | #: addon\globalPlugins\quickNotetaker\__init__.py:58 27 | #: addon\globalPlugins\quickNotetaker\settingsPanel.py:20 buildVars.py:23 28 | msgid "Quick Notetaker" 29 | msgstr "快捷笔记" 30 | 31 | #. Translators: the description for the command to open the notetaker dialog 32 | #: addon\globalPlugins\quickNotetaker\__init__.py:62 33 | msgid "Shows the Notetaker interface for writing a new note" 34 | msgstr "显示新建笔记界面,用于创建新的笔记。" 35 | 36 | #. Translators: the description for the command to open the Notes Manager 37 | #: addon\globalPlugins\quickNotetaker\__init__.py:77 38 | msgid "Shows the Notes Manager interface for viewing and managing notes" 39 | msgstr "显示笔记管理器界面,用于查看和管理笔记" 40 | 41 | #. Translators: the title for the Quick Notetaker Notetaker window 42 | #: addon\globalPlugins\quickNotetaker\dialogs.py:77 43 | msgid "Notetaker - Quick Notetaker" 44 | msgstr "Notetaker - 快捷笔记" 45 | 46 | #. Translators: a lable of a button in Notetaker dialog 47 | #: addon\globalPlugins\quickNotetaker\dialogs.py:92 48 | msgid "Open Notes &Manager..." 49 | msgstr "打开笔记管理器(&M)..." 50 | 51 | #. Translators: a lable of a button in Notetaker dialog 52 | #: addon\globalPlugins\quickNotetaker\dialogs.py:101 53 | msgid "P&review note..." 54 | msgstr "预览笔记(&P)..." 55 | 56 | #. Translators: a lable of a button in Notetaker dialog 57 | #: addon\globalPlugins\quickNotetaker\dialogs.py:104 58 | msgid "Co&py" 59 | msgstr "复制(&C)" 60 | 61 | #. Translators: a lable of a button in Notetaker dialog 62 | #. Translaters: The lable of a button in Notes Manager dialog 63 | #: addon\globalPlugins\quickNotetaker\dialogs.py:109 64 | #: addon\globalPlugins\quickNotetaker\dialogs.py:392 65 | msgid "Copy &HTML code" 66 | msgstr "复制 HTML 代码(&H)..." 67 | 68 | #. Translators: The lable of the note content edit area in Notetaker dialog 69 | #: addon\globalPlugins\quickNotetaker\dialogs.py:115 70 | msgid "&Note content:" 71 | msgstr "笔记内容(&N)" 72 | 73 | #: addon\globalPlugins\quickNotetaker\dialogs.py:135 74 | msgid "Update the corresponding Microsoft &Word document also" 75 | msgstr "更新相应的 Microsoft Word 文档(&W)" 76 | 77 | #. Translators: The label of the check box in Notetaker dialog when creating a new note or when editing an existing note with no Word document attached to it 78 | #: addon\globalPlugins\quickNotetaker\dialogs.py:138 79 | msgid "Save as Microsoft &Word document also" 80 | msgstr "另存为 Microsoft Word 文档(&W)" 81 | 82 | #. Translators: a lable of a button in Notetaker dialog 83 | #: addon\globalPlugins\quickNotetaker\dialogs.py:148 84 | msgid "&Save and close" 85 | msgstr "保存并关闭(&S)" 86 | 87 | #. Translators: a lable of a button in Notetaker dialog 88 | #: addon\globalPlugins\quickNotetaker\dialogs.py:155 89 | msgid "&Discard" 90 | msgstr "放弃(&D)" 91 | 92 | #. Translators: The message which asks the user whether they want to exit and discard changes in Notetaker dialog 93 | #: addon\globalPlugins\quickNotetaker\dialogs.py:200 94 | msgid "Are you sure you want to exit and discard changes?" 95 | msgstr "您确定要退出并放弃所有更改吗?" 96 | 97 | #. Translators: The title of the message which asks the user whether they want to exit and discard changes in Notetaker dialog 98 | #. Translators: the title of the message telling the user that opening Notes Manager wasn't possible 99 | #. Translators: the title of the message telling the user that editing the note wasn't possible 100 | #. Translators: the title of the message telling the user that opening Notetaker wasn't possible 101 | #. Translators: the title of the message shown to the user when the note attached Word document is no longer available. 102 | #. This message is displayed when trying to open the note's Word document from the Notes Manager dialog 103 | #. Translators: the title of the warning messaged shown to the user when they try to delete a note from Notes Manager 104 | #. Translators: the title of the message telling the user that the directory they tried to save the document in is no longer available. 105 | #. See the message body for more details 106 | #: addon\globalPlugins\quickNotetaker\dialogs.py:202 107 | #: addon\globalPlugins\quickNotetaker\dialogs.py:224 108 | #: addon\globalPlugins\quickNotetaker\dialogs.py:482 109 | #: addon\globalPlugins\quickNotetaker\dialogs.py:506 110 | #: addon\globalPlugins\quickNotetaker\dialogs.py:609 111 | #: addon\globalPlugins\quickNotetaker\dialogs.py:622 112 | #: addon\globalPlugins\quickNotetaker\dialogs.py:659 113 | msgid "Warning" 114 | msgstr "警告" 115 | 116 | #. Translators: the message shown to the user when opening Notes Manager is not possible because a one is already opened 117 | #: addon\globalPlugins\quickNotetaker\dialogs.py:222 118 | msgid "Couldn't open Notes Manager! A Notes Manager window is already opened." 119 | msgstr "无法重复打开笔记管理器,另一个笔记管理器窗口已打开。" 120 | 121 | #. Translators: The message which tells the user that copying the note was successful 122 | #. Translators: the message telling the user that copying the note was successful 123 | #: addon\globalPlugins\quickNotetaker\dialogs.py:260 124 | #: addon\globalPlugins\quickNotetaker\dialogs.py:268 125 | #: addon\globalPlugins\quickNotetaker\dialogs.py:637 126 | #: addon\globalPlugins\quickNotetaker\dialogs.py:647 127 | msgid "Copied to clipboard!" 128 | msgstr "已复制到剪贴板!" 129 | 130 | #. Translators: The title of the Notes Manager dialog 131 | #: addon\globalPlugins\quickNotetaker\dialogs.py:327 132 | msgid "Notes Manager - Quick Notetaker" 133 | msgstr "笔记管理器 - 快捷笔记" 134 | 135 | #. Translators: the label of the notes list in Notes Manager dialog 136 | #: addon\globalPlugins\quickNotetaker\dialogs.py:339 137 | msgid "No&tes:" 138 | msgstr "笔记(&N)" 139 | 140 | #. Translators: the name of the first column in the notes list in Notes Manager dialog 141 | #: addon\globalPlugins\quickNotetaker\dialogs.py:355 142 | msgid "Title" 143 | msgstr "标题" 144 | 145 | #. Translators: the name of the second column in the notes list in Notes Manager dialog 146 | #: addon\globalPlugins\quickNotetaker\dialogs.py:358 147 | msgid "Last Edited" 148 | msgstr "上次编辑" 149 | 150 | #. Translators: the name of the third column in the notes list in Notes Manager dialog 151 | #: addon\globalPlugins\quickNotetaker\dialogs.py:360 152 | msgid "Preview" 153 | msgstr "预览" 154 | 155 | #. Translaters: The lable of a button in Notes Manager dialog 156 | #: addon\globalPlugins\quickNotetaker\dialogs.py:368 157 | msgid "&View note..." 158 | msgstr "查看笔记(&V)..." 159 | 160 | #. Translaters: The lable of a button in Notes Manager dialog 161 | #: addon\globalPlugins\quickNotetaker\dialogs.py:374 162 | msgid "&Edit note..." 163 | msgstr "编辑笔记(&E)..." 164 | 165 | #. Translaters: The lable of a button in Notes Manager dialog 166 | #: addon\globalPlugins\quickNotetaker\dialogs.py:380 167 | msgid "Co&py note" 168 | msgstr "复制笔记(&P)..." 169 | 170 | #. Translaters: The lable of the open in Word button in Notes Manager dialog in case the note has a Word document attached to it 171 | #. Translators: the lable of the open in word button in Notes Manager dialog in case the note has a Word document attached to it 172 | #: addon\globalPlugins\quickNotetaker\dialogs.py:386 173 | #: addon\globalPlugins\quickNotetaker\dialogs.py:532 174 | msgid "&Open in Microsoft Word..." 175 | msgstr "在 Microsoft Word 中打开(&O)..." 176 | 177 | #. Translaters: The lable of a button in Notes Manager dialog 178 | #: addon\globalPlugins\quickNotetaker\dialogs.py:398 179 | msgid "&Delete note..." 180 | msgstr "删除笔记(&D)..." 181 | 182 | #. Translators: the label of a button in Notes Manager dialog 183 | #: addon\globalPlugins\quickNotetaker\dialogs.py:412 184 | msgid "&New note..." 185 | msgstr "新建笔记(&N)..." 186 | 187 | #: addon\globalPlugins\quickNotetaker\dialogs.py:418 188 | msgid "Open &settings..." 189 | msgstr "打开设置(&S)..." 190 | 191 | #. Translaters: The lable of a button in Notes Manager dialog 192 | #: addon\globalPlugins\quickNotetaker\dialogs.py:433 193 | msgid "&Close" 194 | msgstr "关闭(&C)..." 195 | 196 | #. Translators: the message shown to the user when editing the note is not possible 197 | #: addon\globalPlugins\quickNotetaker\dialogs.py:480 198 | msgid "" 199 | "Couldn't edit note! An open Notetaker window with unsaved changes is present." 200 | msgstr "无法编辑该笔记!另一份笔记尚未保存。" 201 | 202 | #. Translators: the message shown to the user when opening Notetaker is not possible because a one is already opened 203 | #: addon\globalPlugins\quickNotetaker\dialogs.py:504 204 | msgid "Couldn't open Notetaker! A Notetaker window is already opened." 205 | msgstr "无法重复开启!另一个笔记窗口已打开。" 206 | 207 | #. Translators: the lable of the open in word button in Notes Manager dialog in case the note has no Word document attached to it 208 | #: addon\globalPlugins\quickNotetaker\dialogs.py:535 209 | msgid "Create Microsoft &Word document" 210 | msgstr "创建 Microsoft Word 文档(&W)..." 211 | 212 | #. Translators: the message shown to the user when the note attached Word document is no longer available. 213 | #. This message is displayed when trying to open the note's Word document from the Notes Manager dialog 214 | #: addon\globalPlugins\quickNotetaker\dialogs.py:606 215 | msgid "" 216 | "A document with the specified name was not found! You can create a new one " 217 | "so you would be able to view this note as a Microsoft Word document." 218 | msgstr "" 219 | "未找到具有指定名称的文档!您可以创建一个新的笔记,以便您可以将此笔记作为 " 220 | "Microsoft Word 文档查看。" 221 | 222 | #. Translators: the warning messaged shown to the user when they try to delete a note from Notes Manager 223 | #: addon\globalPlugins\quickNotetaker\dialogs.py:620 224 | msgid "Are you sure you want to delete this note?" 225 | msgstr "你确定要删除这份笔记么?" 226 | 227 | #. Translators: the message which tells the user that the directory they tried to save the file in is no longer available, 228 | #. so the file was saved in the user default one if this was possible. 229 | #. If not, the file was saved in the quick Notetaker directory in documents folder 230 | #: addon\globalPlugins\quickNotetaker\dialogs.py:656 231 | msgid "" 232 | "The saved path for the Microsoft Word document no longer exists! The " 233 | "document was saved in the default directory for the ad-on!" 234 | msgstr "" 235 | "Microsoft Word 文档的保存路径不存在!该文档已保存在插件的默认保存目录中!" 236 | 237 | #. Translators: The title of the dialog which allows the user to choose the folder where they want to save the note's corresponding Word document. 238 | #. This dialog is displayed to the user if the option of "Ask me each time where to save the note's corresponding Word document" in quick Notetaker settings is checked 239 | #: addon\globalPlugins\quickNotetaker\dialogs.py:667 240 | msgid "Select the folder where the document will be saved" 241 | msgstr "选择要保存文档的文件夹" 242 | 243 | #. Translators: the title given to a note if it has no title 244 | #: addon\globalPlugins\quickNotetaker\helpers.py:165 245 | msgid "Untitled" 246 | msgstr "无标题" 247 | 248 | #. Translators: the label of the control in Quick Notetaker settings panel for choosing a default folder where the add-on documents will be saved 249 | #: addon\globalPlugins\quickNotetaker\settingsPanel.py:25 250 | msgid "Default documents directory:" 251 | msgstr "默认文档目录:" 252 | 253 | #. Translators: the label of a button to browse for a directory 254 | #: addon\globalPlugins\quickNotetaker\settingsPanel.py:32 255 | msgid "Browse..." 256 | msgstr "浏览..." 257 | 258 | #. Translators: The title of the dialog presented when browsing for the directory where quick notetaker documents will be stored 259 | #: addon\globalPlugins\quickNotetaker\settingsPanel.py:35 260 | msgid "" 261 | "Select a default directory where the documents of Quick Notetaker will be " 262 | "stored" 263 | msgstr "选择用于保存笔记文档的默认文件夹" 264 | 265 | #. Translators: the label of a check box in Quick Notetaker settings panel 266 | #: addon\globalPlugins\quickNotetaker\settingsPanel.py:44 267 | msgid "" 268 | "Ask me each time &where to save the note's corresponding Microsoft Word " 269 | "document" 270 | msgstr "每次都询问我 Microsoft Word 文档保存到何处?" 271 | 272 | #. Translators: the label of a check box in Quick Notetaker settings panel 273 | #: addon\globalPlugins\quickNotetaker\settingsPanel.py:51 274 | msgid "" 275 | "&Open the note's corresponding Microsoft Word document after saving or " 276 | "updating" 277 | msgstr "保存或更新后打开笔记对应的 Microsoft Word 文档(&S)" 278 | 279 | #. Translators: the label of a check box in Quick Notetaker settings panel 280 | #: addon\globalPlugins\quickNotetaker\settingsPanel.py:58 281 | msgid "&Capture the active window title when creating a new note" 282 | msgstr "新建笔记时使用所聚焦窗口的标题作为笔记标题(&C)" 283 | 284 | #. Translators: the label of a check box in Quick Notetaker settings panel 285 | #: addon\globalPlugins\quickNotetaker\settingsPanel.py:65 286 | msgid "&Remember the note taker window size and position" 287 | msgstr "记住笔记窗口的大小和位置(&R)" 288 | 289 | #. Translators: the label of a check box in Quick Notetaker settings panel 290 | #: addon\globalPlugins\quickNotetaker\settingsPanel.py:72 291 | msgid "Au&to align text when editing notes (relevant for RTL languages)" 292 | msgstr "编辑笔记时自动对齐文本 (适用于从右到左的语言环境)" 293 | 294 | #. Add-on description 295 | #. Translators: Long description to be shown for this add-on on add-on information from add-ons manager 296 | #: buildVars.py:26 297 | msgid "" 298 | "A note taker for NVDA. Allows the user to create, edit, view, manage and " 299 | "export notes to different formats." 300 | msgstr "" 301 | "用于 NVDA 的快捷笔记插件。该插件可以让用户创建、编辑、查看、管理和导出不同格" 302 | "式的笔记。" 303 | -------------------------------------------------------------------------------- /addon/locale/he/LC_MESSAGES/nvda.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the 'quickNotetaker' package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: 'quickNotetaker' '1.0'\n" 10 | "Report-Msgid-Bugs-To: 'nvda-translations@groups.io'\n" 11 | "POT-Creation-Date: 2021-09-21 10:40+0300\n" 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 13 | "Last-Translator: Eilana Benish , Mohammad Suliman \n" 14 | "Language-Team: Eilana Benish , Mohammad Suliman , YEAR. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: 'quickNotetaker' '1.0'\n" 10 | "Report-Msgid-Bugs-To: 'nvda-translations@groups.io'\n" 11 | "POT-Creation-Date: 2021-09-21 12:07+0300\n" 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 13 | "Last-Translator: Mohammad Suliman \n" 14 | "Language-Team: Mohammad Suliman \n" 15 | "Language: ar\n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | 20 | #. Translators: the name of the add-on category in input gestures 21 | #. Translators: the title of the Quick Notetaker panel in NVDA's settings 22 | #. Add-on summary, usually the user visible name of the addon. 23 | #. Translators: Summary for this add-on 24 | #. to be shown on installation and add-on information found in Add-ons Manager. 25 | #: addon\globalPlugins\quickNotetaker\__init__.py:47 26 | #: addon\globalPlugins\quickNotetaker\settingsPanel.py:18 buildVars.py:23 27 | msgid "Quick Notetaker" 28 | msgstr "Quick Notetaker" 29 | 30 | #. Translators: the description for the command to open the Notetaker dialog 31 | #: addon\globalPlugins\quickNotetaker\__init__.py:51 32 | msgid "Shows the Notetaker interface for writing a new note" 33 | msgstr "يعرض واجهة مدون الملاحظات لكتابة ملاحظة جديدة" 34 | 35 | #. Translators: the description for the command to open the Notes Manager 36 | #: addon\globalPlugins\quickNotetaker\__init__.py:66 37 | msgid "Shows the Note Viewer interface for viewing and managing notes" 38 | msgstr "يعرض واجهة مدير الملاحظات لمشاهدة وادارة الملاحظات" 39 | 40 | #. Translators: the title for the Quick Notetaker Notetaker window 41 | #: addon\globalPlugins\quickNotetaker\dialogs.py:74 42 | msgid "Notetaker - Quick Notetaker" 43 | msgstr "مدون ملاحظات - Quick Notetaker" 44 | 45 | #. Translators: a lable of a button in Notetaker dialog 46 | #: addon\globalPlugins\quickNotetaker\dialogs.py:89 47 | msgid "Open Notes &Manager..." 48 | msgstr "&فتح مدير الملاحظات..." 49 | 50 | #. Translators: a lable of a button in Notetaker dialog 51 | #: addon\globalPlugins\quickNotetaker\dialogs.py:98 52 | msgid "P&review note..." 53 | msgstr "&عرض مسبق للملاحظة..." 54 | 55 | #. Translators: a lable of a button in Notetaker dialog 56 | #: addon\globalPlugins\quickNotetaker\dialogs.py:101 57 | msgid "Co&py" 58 | msgstr "&نسخ" 59 | 60 | #. Translators: a lable of a button in Notetaker dialog 61 | #. Translaters: The lable of a button in Notes Manager dialog 62 | #: addon\globalPlugins\quickNotetaker\dialogs.py:106 63 | #: addon\globalPlugins\quickNotetaker\dialogs.py:361 64 | msgid "Copy &HTML code" 65 | msgstr "نسخ &شيفرة HTML" 66 | 67 | #. Translators: The lable of the note content edit area in Notetaker dialog 68 | #: addon\globalPlugins\quickNotetaker\dialogs.py:112 69 | msgid "&Note content:" 70 | msgstr "&محتوى الملاحظة:" 71 | 72 | #. Translators: The label of the check box in Notetaker dialog when editing a note which has Word document attached to it 73 | #: addon\globalPlugins\quickNotetaker\dialogs.py:129 74 | msgid "Update the corresponding Microsoft &Word document also" 75 | msgstr "&قم بتحديث ملف ال Microsoft Word المرفق للملاحظة أيضا" 76 | 77 | #. Translators: The label of the check box in Notetaker dialog when creating a new note or when editing an existing note with no Word document attached to it 78 | #: addon\globalPlugins\quickNotetaker\dialogs.py:132 79 | msgid "Save as Microsoft &Word document also" 80 | msgstr "&قم بالحفظ كملف Microsoft Word أيضا" 81 | 82 | #. Translators: a lable of a button in Notetaker dialog 83 | #: addon\globalPlugins\quickNotetaker\dialogs.py:142 84 | msgid "&Save and close" 85 | msgstr "&حفظ وإغلاق" 86 | 87 | #. Translators: a lable of a button in Notetaker dialog 88 | #: addon\globalPlugins\quickNotetaker\dialogs.py:149 89 | msgid "&Discard" 90 | msgstr "&تجاهل" 91 | 92 | #. Translators: The message which asks the user whether they want to exit and discard changes in Notetaker dialog 93 | #: addon\globalPlugins\quickNotetaker\dialogs.py:195 94 | msgid "Are you sure you want to exit and discard changes?" 95 | msgstr "هل انت متأكد انه برغبتك الخروج وتجاهل التغييرات؟" 96 | 97 | #. Translators: The title of the message which asks the user whether they want to exit and discard changes in Notetaker dialog 98 | #. Translators: the title of the message telling the user that opening Notes Manager wasn't possible 99 | #. Translators: the title of the message telling the user that editing the note wasn't possible 100 | #. Translators: the title of the message telling the user that opening Notetaker wasn't possible 101 | #. Translators: the title of the message shown to the user when the note attached Word document is no longer available. 102 | #. This message is displayed when trying to open the note's Word document from the Notes Manager dialog 103 | #. Translators: the title of the warning messaged shown to the user when they try to delete a note from Notes Manager 104 | #. Translators: the title of the message telling the user that the directory they tried to save the document in is no longer available. 105 | #. See the message body for more details 106 | #: addon\globalPlugins\quickNotetaker\dialogs.py:199 107 | #: addon\globalPlugins\quickNotetaker\dialogs.py:221 108 | #: addon\globalPlugins\quickNotetaker\dialogs.py:477 109 | #: addon\globalPlugins\quickNotetaker\dialogs.py:501 110 | #: addon\globalPlugins\quickNotetaker\dialogs.py:611 111 | #: addon\globalPlugins\quickNotetaker\dialogs.py:624 112 | #: addon\globalPlugins\quickNotetaker\dialogs.py:661 113 | msgid "Warning" 114 | msgstr "تحذير" 115 | 116 | #. Translators: the message shown to the user when opening Notes Manager is not possible because a one is already opened 117 | #: addon\globalPlugins\quickNotetaker\dialogs.py:219 118 | msgid "Couldn't open Notes Manager! A Notes Manager window is already opened." 119 | msgstr "لم يتمكن عرض مدير الملاحظات! هناك شباك مدير ملاحظات معروض مسبقا." 120 | 121 | #. Translators: The message which tells the user that copying the note was successful 122 | #. Translators: the message telling the user that copying the note was successful 123 | #: addon\globalPlugins\quickNotetaker\dialogs.py:231 124 | #: addon\globalPlugins\quickNotetaker\dialogs.py:238 125 | #: addon\globalPlugins\quickNotetaker\dialogs.py:605 126 | #: addon\globalPlugins\quickNotetaker\dialogs.py:614 127 | msgid "Copied to clipboard!" 128 | msgstr "نسخ للحافظة" 129 | 130 | #. Translators: The title of the Notes Manager dialog 131 | #: addon\globalPlugins\quickNotetaker\dialogs.py:296 132 | msgid "Notes Manager - Quick Notetaker" 133 | msgstr "مدير الملاحظات - Quick Notetaker" 134 | 135 | #. Translators: the label of the notes list in Notes Manager dialog 136 | #: addon\globalPlugins\quickNotetaker\dialogs.py:308 137 | msgid "No&tes:" 138 | msgstr "&الملاحظات" 139 | 140 | #. Translators: the name of the first column in the notes list in Notes Manager dialog 141 | #: addon\globalPlugins\quickNotetaker\dialogs.py:324 142 | msgid "Title" 143 | msgstr "عنوان" 144 | 145 | #. Translators: the name of the second column in the notes list in Notes Manager dialog 146 | #: addon\globalPlugins\quickNotetaker\dialogs.py:327 147 | msgid "Last Edited" 148 | msgstr "آخر تحرير" 149 | 150 | #. Translators: the name of the third column in the notes list in Notes Manager dialog 151 | #: addon\globalPlugins\quickNotetaker\dialogs.py:329 152 | msgid "Preview" 153 | msgstr "عرض مسبق" 154 | 155 | #. Translaters: The lable of a button in Notes Manager dialog 156 | #: addon\globalPlugins\quickNotetaker\dialogs.py:337 157 | msgid "&View note..." 158 | msgstr "&عرض الملاحظة..." 159 | 160 | #. Translaters: The lable of a button in Notes Manager dialog 161 | #: addon\globalPlugins\quickNotetaker\dialogs.py:343 162 | msgid "&Edit note..." 163 | msgstr "&تحرير الملاحظة..." 164 | 165 | #. Translaters: The lable of a button in Notes Manager dialog 166 | #: addon\globalPlugins\quickNotetaker\dialogs.py:349 167 | msgid "Co&py note" 168 | msgstr "&نسخ الملاحظة" 169 | 170 | #. Translaters: The lable of the open in Word button in Notes Manager dialog in case the note has a Word document attached to it 171 | #. Translators: the lable of the open in word button in Notes Manager dialog in case the note has a Word document attached to it 172 | #: addon\globalPlugins\quickNotetaker\dialogs.py:355 173 | #: addon\globalPlugins\quickNotetaker\dialogs.py:495 174 | msgid "&Open in Microsoft Word..." 175 | msgstr "ال&فتح بواسطة Microsoft Word..." 176 | 177 | #. Translaters: The lable of a button in Notes Manager dialog 178 | #: addon\globalPlugins\quickNotetaker\dialogs.py:367 179 | msgid "&Delete note..." 180 | msgstr "&محو الملاحظة..." 181 | 182 | #. Translators: the label of a button in Notes Manager dialog 183 | #: addon\globalPlugins\quickNotetaker\dialogs.py:381 184 | msgid "&New note..." 185 | msgstr "ملاحظة &جديدة..." 186 | 187 | #. Translaters: The lable of a button in Notes Manager dialog 188 | #: addon\globalPlugins\quickNotetaker\dialogs.py:386 189 | msgid "Open &settings..." 190 | msgstr "فتح الإع&دادات..." 191 | 192 | #. Translaters: The lable of a button in Notes Manager dialog 193 | #: addon\globalPlugins\quickNotetaker\dialogs.py:401 194 | msgid "&Close" 195 | msgstr "إ&غلاق" 196 | 197 | #. Translators: the title given to a note if it has no title 198 | #: addon\globalPlugins\quickNotetaker\helpers.py:128 199 | msgid "Untitled" 200 | msgstr "بلا عنوان" 201 | 202 | #. Translators: the message shown to the user when editing the note is not possible 203 | #: addon\globalPlugins\quickNotetaker\dialogs.py:454 204 | msgid "" 205 | "Couldn't edit note! An open Notetaker window with unsaved changes is present." 206 | msgstr "لا يمكن تحرير الملاحظة! هناك شباك مدون ملاحظات مع تغييرات غير محفوظة لا يزال مفتوحة." 207 | 208 | #. Translators: the message shown to the user when opening Notetaker is not possible because a one is already opened 209 | #: addon\globalPlugins\quickNotetaker\dialogs.py:499 210 | msgid "Couldn't open Notetaker! A Notetaker window is already opened." 211 | msgstr "لم يتمكن عرض مدون الملاحظات! هناك شباك مدون ملاحظات معروض مسبقا." 212 | 213 | #. Translators: the lable of the open in word button in Notes Manager dialog in case the note has no Word document attached to it 214 | #: addon\globalPlugins\quickNotetaker\dialogs.py:498 215 | msgid "Create Microsoft &Word document" 216 | msgstr "إنشاء مل&ف Microsoft Word" 217 | 218 | #. Translators: the message shown to the user when the note attached Word document is no longer available. 219 | #. This message is displayed when trying to open the note's Word document from the Notes Manager dialog 220 | #: addon\globalPlugins\quickNotetaker\dialogs.py:574 221 | msgid "" 222 | "A document with the specified name was not found! You can create a new one " 223 | "so you would be able to view this note as a Microsoft Word document." 224 | msgstr "لم يتم العثور على ملف بهذا الإسم! يمكنك انشاء ملف جديد حتى تتمكن من مشاهدة الملاحظة كملف Microsoft Word." 225 | 226 | #. Translators: the warning messaged shown to the user when they try to delete a note from Notes Manager 227 | #: addon\globalPlugins\quickNotetaker\dialogs.py:588 228 | msgid "Are you sure you want to delete this note?" 229 | msgstr "هل انت متأكد انك ترغب بمحو الملاحظة؟" 230 | 231 | #. Translators: the message which tells the user that the directory they tried to save the file in is no longer available, 232 | #. so the file was saved in the user default one if this was possible. 233 | #. If not, the file was saved in the quick Notetaker directory in documents folder 234 | #: addon\globalPlugins\quickNotetaker\dialogs.py:623 235 | msgid "" 236 | "The saved path for the Microsoft Word document no longer exists! The " 237 | "document was saved in the default directory for the ad-on!" 238 | msgstr "مسار ملف ال Microsoft Word المحفوظ لهذه الملاحظة لم يعد متوفرا! تم حفظ المستند في المجلد الإفتراضي للاضافة!" 239 | 240 | #. Translators: The title of the dialog which allows the user to choose the folder where they want to save the note's corresponding Word document. 241 | #. This dialog is displayed to the user if the option of "Ask me each time where to save the note's corresponding Word document" in quick Notetaker settings is checked 242 | #: addon\globalPlugins\quickNotetaker\dialogs.py:634 243 | msgid "Select the folder where the document will be saved" 244 | msgstr "قم بإختيار المجلد الذي سيحفظ فيه المستند" 245 | 246 | #. Translators: the label of the control in Quick Notetaker settings panel for choosing a default folder where the add-on documents will be saved 247 | #: addon\globalPlugins\quickNotetaker\settingsPanel.py:23 248 | msgid "Default documents directory:" 249 | msgstr "المجلد الإفتراضي للمستندات" 250 | 251 | #. Translators: the label of a button to browse for a directory 252 | #: addon\globalPlugins\quickNotetaker\settingsPanel.py:30 253 | msgid "Browse..." 254 | msgstr "استعراض..." 255 | 256 | #. Translators: The title of the dialog presented when browsing for the directory where quick Notetaker documents will be stored 257 | #: addon\globalPlugins\quickNotetaker\settingsPanel.py:33 258 | msgid "" 259 | "Select a default directory where the documents of Quick Notetaker will be " 260 | "stored" 261 | msgstr "قم بإختيار المجلد الإفتراضي حيث سيتم حفظ مستندات Quick Notetaker" 262 | 263 | #. Translators: the label of a check box in Quick Notetaker settings panel 264 | #: addon\globalPlugins\quickNotetaker\settingsPanel.py:42 265 | msgid "" 266 | "Ask me each time &where to save the note's corresponding Microsoft Word " 267 | "document" 268 | msgstr "إسألني &كل مرة اين سيتم حفظ ملف ال Microsoft Word المرفق للملاحظة" 269 | 270 | #. Translators: the label of a check box in Quick Notetaker settings panel 271 | #: addon\globalPlugins\quickNotetaker\settingsPanel.py:49 272 | msgid "" 273 | "&Open the note's corresponding Microsoft Word document after saving or " 274 | "updating" 275 | msgstr "&قم بفتح ملف ال Microsoft Word بعد الحفظ أو التحديث" 276 | 277 | #. Translators: the label of a check box in Quick Notetaker settings panel 278 | #: addon\globalPlugins\quickNotetaker\settingsPanel.py:56 279 | msgid "&Capture the active window title when creating a new note" 280 | msgstr "قم بإست&خدام عنوان الشباك النشط كعنوان الملاحظة حين إنشاء ملاحظة جديدة" 281 | 282 | #. Translators: the label of a check box in Quick Notetaker settings panel 283 | #: addon\globalPlugins\quickNotetaker\settingsPanel.py:63 284 | msgid "&Remember the note taker window size and position" 285 | msgstr "ت&ذكر موقع وحجم شباك مدون الملاحظات" 286 | 287 | #. Translators: the label of a check box in Quick Notetaker settings panel 288 | #: addon\globalPlugins\quickNotetaker\settingsPanel.py:70 289 | msgid "Au&to align text when editing notes (relevant for RTL languages)" 290 | msgstr "قم بمحاذاة ال&نص بشكل تلقائي حين تحرير الملاحظات (هام عند الكتابة بلغات من اليمين الى اليسار)" 291 | 292 | #. Add-on description 293 | #. Translators: Long description to be shown for this add-on on add-on information from add-ons manager 294 | #: buildVars.py:26 295 | msgid "" 296 | "A note taker for NVDA. Allows the user to create, edit, view, manage and " 297 | "export notes to different formats." 298 | msgstr "مدون ملاحظات ل NVDA. يمكن المستخدم من إنشاء، تحرير، عرض، إدارة وتصدير الملاحظات الى صيغ مختلفة." 299 | -------------------------------------------------------------------------------- /addon/locale/de/LC_MESSAGES/nvda.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the quickNotetaker package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: quickNotetaker 1.0\n" 10 | "Report-Msgid-Bugs-To: nvda-translations@groups.io\n" 11 | "POT-Creation-Date: 2022-02-02 20:19+0100\n" 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 13 | "Last-Translator: Benjamin Hofer \n" 14 | "Language-Team: Benjamin Hofer \n" 15 | "Language: de\n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | 20 | #. Translators: the name of the add-on category in input gestures 21 | #. Translators: the title of the Quick Notetaker panel in NVDA's settings 22 | #. Add-on summary, usually the user visible name of the addon. 23 | #. Translators: Summary for this add-on 24 | #. to be shown on installation and add-on information found in Add-ons Manager. 25 | #: addon/globalPlugins/quickNotetaker/__init__.py:58 26 | #: addon/globalPlugins/quickNotetaker/settingsPanel.py:20 buildVars.py:23 27 | msgid "Quick Notetaker" 28 | msgstr "Quick Notetaker" 29 | 30 | #. Translators: the description for the command to open the notetaker dialog 31 | #: addon/globalPlugins/quickNotetaker/__init__.py:62 32 | msgid "Shows the Notetaker interface for writing a new note" 33 | msgstr "Zeigt die Notizbuch-Oberfläche zum Schreiben einer neuen Notiz an" 34 | 35 | #. Translators: the description for the command to open the Notes Manager 36 | #: addon/globalPlugins/quickNotetaker/__init__.py:77 37 | msgid "Shows the Notes Manager interface for viewing and managing notes" 38 | msgstr "Zeigt die Oberfläche des Notizbuchs zum Anzeigen und Verwalten von Notizen an" 39 | 40 | #. Translators: the title for the Quick Notetaker Notetaker window 41 | #: addon/globalPlugins/quickNotetaker/dialogs.py:77 42 | msgid "Notetaker - Quick Notetaker" 43 | msgstr "Notizbuch - Quick Notetaker" 44 | 45 | #. Translators: a lable of a button in Notetaker dialog 46 | #: addon/globalPlugins/quickNotetaker/dialogs.py:92 47 | msgid "Open Notes &Manager..." 48 | msgstr "Notizbuch öffnen..." 49 | 50 | #. Translators: a lable of a button in Notetaker dialog 51 | #: addon/globalPlugins/quickNotetaker/dialogs.py:101 52 | msgid "P&review note..." 53 | msgstr "Notizvo&rschau..." 54 | 55 | #. Translators: a lable of a button in Notetaker dialog 56 | #: addon/globalPlugins/quickNotetaker/dialogs.py:104 57 | msgid "Co&py" 58 | msgstr "Ko&pieren" 59 | 60 | #. Translators: a lable of a button in Notetaker dialog 61 | #. Translaters: The lable of a button in Notes Manager dialog 62 | #: addon/globalPlugins/quickNotetaker/dialogs.py:109 63 | #: addon/globalPlugins/quickNotetaker/dialogs.py:392 64 | msgid "Copy &HTML code" 65 | msgstr "&HTML-Code kopieren..." 66 | 67 | #. Translators: The lable of the note content edit area in Notetaker dialog 68 | #: addon/globalPlugins/quickNotetaker/dialogs.py:115 69 | msgid "&Note content:" 70 | msgstr "Inhalt der &Notiz:" 71 | 72 | #: addon/globalPlugins/quickNotetaker/dialogs.py:135 73 | msgid "Update the corresponding Microsoft &Word document also" 74 | msgstr "Auch das zugehörige Microsoft &Word-Dokument aktualisieren" 75 | 76 | #. Translators: The label of the check box in Notetaker dialog when creating a new note or when editing an existing note with no Word document attached to it 77 | #: addon/globalPlugins/quickNotetaker/dialogs.py:138 78 | msgid "Save as Microsoft &Word document also" 79 | msgstr "Auch als Microsoft &Word-Dokument speichern" 80 | 81 | #. Translators: a lable of a button in Notetaker dialog 82 | #: addon/globalPlugins/quickNotetaker/dialogs.py:148 83 | msgid "&Save and close" 84 | msgstr "&Speichern und schließen" 85 | 86 | #. Translators: a lable of a button in Notetaker dialog 87 | #: addon/globalPlugins/quickNotetaker/dialogs.py:155 88 | msgid "&Discard" 89 | msgstr "Verwerfen" 90 | 91 | #. Translators: The message which asks the user whether they want to exit and discard changes in Notetaker dialog 92 | #: addon/globalPlugins/quickNotetaker/dialogs.py:200 93 | msgid "Are you sure you want to exit and discard changes?" 94 | msgstr "Sind Sie sicher, dass Sie das Notizbuch schließen und Ihre Änderungen verwerfen möchten?" 95 | 96 | #. Translators: The title of the message which asks the user whether they want to exit and discard changes in Notetaker dialog 97 | #. Translators: the title of the message telling the user that opening Notes Manager wasn't possible 98 | #. Translators: the title of the message telling the user that editing the note wasn't possible 99 | #. Translators: the title of the message telling the user that opening Notetaker wasn't possible 100 | #. Translators: the title of the message shown to the user when the note attached Word document is no longer available. 101 | #. This message is displayed when trying to open the note's Word document from the Notes Manager dialog 102 | #. Translators: the title of the warning messaged shown to the user when they try to delete a note from Notes Manager 103 | #. Translators: the title of the message telling the user that the directory they tried to save the document in is no longer available. 104 | #. See the message body for more details 105 | #: addon/globalPlugins/quickNotetaker/dialogs.py:202 106 | #: addon/globalPlugins/quickNotetaker/dialogs.py:224 107 | #: addon/globalPlugins/quickNotetaker/dialogs.py:482 108 | #: addon/globalPlugins/quickNotetaker/dialogs.py:506 109 | #: addon/globalPlugins/quickNotetaker/dialogs.py:609 110 | #: addon/globalPlugins/quickNotetaker/dialogs.py:622 111 | #: addon/globalPlugins/quickNotetaker/dialogs.py:659 112 | msgid "Warning" 113 | msgstr "Warnung" 114 | 115 | #. Translators: the message shown to the user when opening Notes Manager is not possible because a one is already opened 116 | #: addon/globalPlugins/quickNotetaker/dialogs.py:222 117 | msgid "Couldn't open Notes Manager! A Notes Manager window is already opened." 118 | msgstr "Das Notizbuch konnte nicht geöffnet werden!" 119 | 120 | #. Translators: The message which tells the user that copying the note was successful 121 | #. Translators: the message telling the user that copying the note was successful 122 | #: addon/globalPlugins/quickNotetaker/dialogs.py:260 123 | #: addon/globalPlugins/quickNotetaker/dialogs.py:268 124 | #: addon/globalPlugins/quickNotetaker/dialogs.py:637 125 | #: addon/globalPlugins/quickNotetaker/dialogs.py:647 126 | msgid "Copied to clipboard!" 127 | msgstr "In Zwischenablage kopiert" 128 | 129 | #. Translators: The title of the Notes Manager dialog 130 | #: addon/globalPlugins/quickNotetaker/dialogs.py:327 131 | msgid "Notes Manager - Quick Notetaker" 132 | msgstr "Notizbuch - Quick Notetaker" 133 | 134 | #. Translators: the label of the notes list in Notes Manager dialog 135 | #: addon/globalPlugins/quickNotetaker/dialogs.py:339 136 | msgid "No&tes:" 137 | msgstr "No&tizen:" 138 | 139 | #. Translators: the name of the first column in the notes list in Notes Manager dialog 140 | #: addon/globalPlugins/quickNotetaker/dialogs.py:355 141 | msgid "Title" 142 | msgstr "Titel" 143 | 144 | #. Translators: the name of the second column in the notes list in Notes Manager dialog 145 | #: addon/globalPlugins/quickNotetaker/dialogs.py:358 146 | msgid "Last Edited" 147 | msgstr "Zuletzt geändert" 148 | 149 | #. Translators: the name of the third column in the notes list in Notes Manager dialog 150 | #: addon/globalPlugins/quickNotetaker/dialogs.py:360 151 | msgid "Preview" 152 | msgstr "Vorschau" 153 | 154 | #. Translaters: The lable of a button in Notes Manager dialog 155 | #: addon/globalPlugins/quickNotetaker/dialogs.py:368 156 | msgid "&View note..." 157 | msgstr "Notiz anzeigen.." 158 | 159 | #. Translaters: The lable of a button in Notes Manager dialog 160 | #: addon/globalPlugins/quickNotetaker/dialogs.py:374 161 | msgid "&Edit note..." 162 | msgstr "Notiz b&earbeiten..." 163 | 164 | #. Translaters: The lable of a button in Notes Manager dialog 165 | #: addon/globalPlugins/quickNotetaker/dialogs.py:380 166 | msgid "Co&py note" 167 | msgstr "Notiz ko&pieren" 168 | 169 | #. Translaters: The lable of the open in Word button in Notes Manager dialog in case the note has a Word document attached to it 170 | #. Translators: the lable of the open in word button in Notes Manager dialog in case the note has a Word document attached to it 171 | #: addon/globalPlugins/quickNotetaker/dialogs.py:386 172 | #: addon/globalPlugins/quickNotetaker/dialogs.py:532 173 | msgid "&Open in Microsoft Word..." 174 | msgstr "In Microsoft Word öffnen..." 175 | 176 | #. Translaters: The lable of a button in Notes Manager dialog 177 | #: addon/globalPlugins/quickNotetaker/dialogs.py:398 178 | msgid "&Delete note..." 179 | msgstr "Notiz löschen..." 180 | 181 | #. Translators: the label of a button in Notes Manager dialog 182 | #: addon/globalPlugins/quickNotetaker/dialogs.py:412 183 | msgid "&New note..." 184 | msgstr "&Neue Notiz..." 185 | 186 | #: addon/globalPlugins/quickNotetaker/dialogs.py:418 187 | msgid "Open &settings..." 188 | msgstr "Ein&stellungen öffnen..." 189 | 190 | #. Translaters: The lable of a button in Notes Manager dialog 191 | #: addon/globalPlugins/quickNotetaker/dialogs.py:433 192 | msgid "&Close" 193 | msgstr "Schließen" 194 | 195 | #. Translators: the message shown to the user when editing the note is not possible 196 | #: addon/globalPlugins/quickNotetaker/dialogs.py:480 197 | msgid "" 198 | "Couldn't edit note! An open Notetaker window with unsaved changes is present." 199 | msgstr "" 200 | "Die Notiz kann nicht bearbeitet werden, da noch ein Notizbuch mit ungespeicherten Änderungen offen ist." 201 | 202 | #. Translators: the message shown to the user when opening Notetaker is not possible because a one is already opened 203 | #: addon/globalPlugins/quickNotetaker/dialogs.py:504 204 | msgid "Couldn't open Notetaker! A Notetaker window is already opened." 205 | msgstr "Das Notizbuch kann nicht geöffnet werden. Es ist bereits ein Notizbuch-Fenster geöffnet." 206 | 207 | #. Translators: the lable of the open in word button in Notes Manager dialog in case the note has no Word document attached to it 208 | #: addon/globalPlugins/quickNotetaker/dialogs.py:535 209 | msgid "Create Microsoft &Word document" 210 | msgstr "Microsoft &Word-Dokument erstellen" 211 | 212 | #. Translators: the message shown to the user when the note attached Word document is no longer available. 213 | #. This message is displayed when trying to open the note's Word document from the Notes Manager dialog 214 | #: addon/globalPlugins/quickNotetaker/dialogs.py:606 215 | msgid "" 216 | "A document with the specified name was not found! You can create a new one " 217 | "so you would be able to view this note as a Microsoft Word document." 218 | msgstr "" 219 | "Ein Dokument mit dem angegebenen Namen wurde nicht gefunden! Sie können ein neues Dokument erstellen, " 220 | "und damit diese Notiz als Microsoft Word-Dokument anzeigen können." 221 | 222 | #. Translators: the warning messaged shown to the user when they try to delete a note from Notes Manager 223 | #: addon/globalPlugins/quickNotetaker/dialogs.py:620 224 | msgid "Are you sure you want to delete this note?" 225 | msgstr "Möchten Sie diese Notiz wirklich löschen?" 226 | 227 | #. Translators: the message which tells the user that the directory they tried to save the file in is no longer available, 228 | #. so the file was saved in the user default one if this was possible. 229 | #. If not, the file was saved in the quick Notetaker directory in documents folder 230 | #: addon/globalPlugins/quickNotetaker/dialogs.py:656 231 | msgid "" 232 | "The saved path for the Microsoft Word document no longer exists! The " 233 | "document was saved in the default directory for the ad-on!" 234 | msgstr "" 235 | "Der gespeicherte Pfad für das Microsoft Word-Dokument existiert nicht mehr! Das " 236 | "Dokument wurde im Standardverzeichnis für das Add-On gespeichert!" 237 | 238 | #. Translators: The title of the dialog which allows the user to choose the folder where they want to save the note's corresponding Word document. 239 | #. This dialog is displayed to the user if the option of "Ask me each time where to save the note's corresponding Word document" in quick Notetaker settings is checked 240 | #: addon/globalPlugins/quickNotetaker/dialogs.py:667 241 | msgid "Select the folder where the document will be saved" 242 | msgstr "Wählen Sie das Verzeichnis aus, in dem das Dokument gespeichert werden soll" 243 | 244 | #. Translators: the title given to a note if it has no title 245 | #: addon/globalPlugins/quickNotetaker/helpers.py:165 246 | msgid "Untitled" 247 | msgstr "Unbenannt" 248 | 249 | #. Translators: the label of the control in Quick Notetaker settings panel for choosing a default folder where the add-on documents will be saved 250 | #: addon/globalPlugins/quickNotetaker/settingsPanel.py:25 251 | msgid "Default documents directory:" 252 | msgstr "Standardverzeichnis für Dokumente:" 253 | 254 | #. Translators: the label of a button to browse for a directory 255 | #: addon/globalPlugins/quickNotetaker/settingsPanel.py:32 256 | msgid "Browse..." 257 | msgstr "Durchsuchen..." 258 | 259 | #. Translators: The title of the dialog presented when browsing for the directory where quick notetaker documents will be stored 260 | #: addon/globalPlugins/quickNotetaker/settingsPanel.py:35 261 | msgid "" 262 | "Select a default directory where the documents of Quick Notetaker will be " 263 | "stored" 264 | msgstr "" 265 | "Wählen Sie ein Standardverzeichnis, in dem die Dokumente von Quick Notetaker " 266 | "gespeichert werden" 267 | 268 | #. Translators: the label of a check box in Quick Notetaker settings panel 269 | #: addon/globalPlugins/quickNotetaker/settingsPanel.py:44 270 | msgid "" 271 | "Ask me each time &where to save the note's corresponding Microsoft Word " 272 | "document" 273 | msgstr "" 274 | "Immer fragen, &wo das entsprechende Microsoft Word-Dokument der Notiz gespeichert " 275 | "werden soll" 276 | 277 | #. Translators: the label of a check box in Quick Notetaker settings panel 278 | #: addon/globalPlugins/quickNotetaker/settingsPanel.py:51 279 | msgid "" 280 | "&Open the note's corresponding Microsoft Word document after saving or " 281 | "updating" 282 | msgstr "" 283 | "Das Microsoft Word-Dokument nach dem Speichern oder Aktualisieren der " 284 | "Notiz öffnen" 285 | 286 | #. Translators: the label of a check box in Quick Notetaker settings panel 287 | #: addon/globalPlugins/quickNotetaker/settingsPanel.py:58 288 | msgid "&Capture the active window title when creating a new note" 289 | msgstr "Beim Erstellen einer neuen Notiz den aktuellen Fenstertitel als Name verwenden" 290 | 291 | #. Translators: the label of a check box in Quick Notetaker settings panel 292 | #: addon/globalPlugins/quickNotetaker/settingsPanel.py:65 293 | msgid "&Remember the note taker window size and position" 294 | msgstr "Fenstergröße und -position für das Notizbuch-Fenster beibehalten" 295 | 296 | #. Translators: the label of a check box in Quick Notetaker settings panel 297 | #: addon/globalPlugins/quickNotetaker/settingsPanel.py:72 298 | msgid "Au&to align text when editing notes (relevant for RTL languages)" 299 | msgstr "Au&tomatisches Au&srichten von Text beim Bearbeiten von Notizen (relevant für RTL-Sprachen)" 300 | 301 | #. Add-on description 302 | #. Translators: Long description to be shown for this add-on on add-on information from add-ons manager 303 | #: buildVars.py:26 304 | msgid "" 305 | "A note taker for NVDA. Allows the user to create, edit, view, manage and " 306 | "export notes to different formats." 307 | msgstr "" 308 | "Ein Notizbuch für NVDA. Ermöglicht dem Benutzer das Erstellen, Bearbeiten, Anzeigen, Verwalten und " 309 | "Exportieren von Notizen." 310 | -------------------------------------------------------------------------------- /addon/locale/ru/LC_MESSAGES/nvda.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the 'quickNotetaker' package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: 'quickNotetaker' '1.1'\n" 9 | "Report-Msgid-Bugs-To: 'nvda-translations@groups.io'\n" 10 | "POT-Creation-Date: 2023-03-07 18:44+0400\n" 11 | "PO-Revision-Date: 2023-03-13 21:39+0400\n" 12 | "Last-Translator: Dragan Ratkovich\n" 13 | "Language-Team: \n" 14 | "Language: ru\n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=UTF-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " 19 | "n%10<=4 && (n%100<12 || n%100>14) ? 1 : 2);\n" 20 | "X-Generator: Poedit 3.2.2\n" 21 | 22 | #. Translators: the name of the add-on category in input gestures 23 | #. Translators: the title of the Quick Notetaker panel in NVDA's settings 24 | #. Add-on summary, usually the user visible name of the addon. 25 | #. Translators: Summary for this add-on 26 | #. to be shown on installation and add-on information found in Add-ons Manager. 27 | #: addon\globalPlugins\quickNotetaker\__init__.py:58 28 | #: addon\globalPlugins\quickNotetaker\settingsPanel.py:20 buildVars.py:23 29 | msgid "Quick Notetaker" 30 | msgstr "Быстрый блокнот" 31 | 32 | #. Translators: the description for the command to open the notetaker dialog 33 | #: addon\globalPlugins\quickNotetaker\__init__.py:62 34 | msgid "Shows the Notetaker interface for writing a new note" 35 | msgstr "Показывает интерфейс блокнота для написания новой заметки" 36 | 37 | #. Translators: the description for the command to open the Notes Manager 38 | #: addon\globalPlugins\quickNotetaker\__init__.py:77 39 | msgid "Shows the Notes Manager interface for viewing and managing notes" 40 | msgstr "" 41 | "Показывает интерфейс диспетчера заметок для просмотра и управления заметками" 42 | 43 | #. Translators: the title for the Quick Notetaker Notetaker window 44 | #: addon\globalPlugins\quickNotetaker\dialogs.py:77 45 | msgid "Notetaker - Quick Notetaker" 46 | msgstr "Блокнот - Быстрый блокнот" 47 | 48 | #. Translators: a lable of a button in Notetaker dialog 49 | #: addon\globalPlugins\quickNotetaker\dialogs.py:92 50 | msgid "Open Notes &Manager..." 51 | msgstr "Открыть &менеджер заметок..." 52 | 53 | #. Translators: a lable of a button in Notetaker dialog 54 | #: addon\globalPlugins\quickNotetaker\dialogs.py:101 55 | msgid "P&review note..." 56 | msgstr "Предварительный &просмотр заметки..." 57 | 58 | #. Translators: a lable of a button in Notetaker dialog 59 | #: addon\globalPlugins\quickNotetaker\dialogs.py:104 60 | msgid "Co&py" 61 | msgstr "&Копировать" 62 | 63 | #. Translators: a lable of a button in Notetaker dialog 64 | #. Translaters: The lable of a button in Notes Manager dialog 65 | #: addon\globalPlugins\quickNotetaker\dialogs.py:109 66 | #: addon\globalPlugins\quickNotetaker\dialogs.py:392 67 | msgid "Copy &HTML code" 68 | msgstr "Скопировать &HTML-код" 69 | 70 | #. Translators: The lable of the note content edit area in Notetaker dialog 71 | #: addon\globalPlugins\quickNotetaker\dialogs.py:115 72 | msgid "&Note content:" 73 | msgstr "Содержание &заметки:" 74 | 75 | #: addon\globalPlugins\quickNotetaker\dialogs.py:135 76 | msgid "Update the corresponding Microsoft &Word document also" 77 | msgstr "Также обновить соответствующий документ Microsoft &Word" 78 | 79 | #. Translators: The label of the check box in Notetaker dialog when creating a new note or when editing an existing note with no Word document attached to it 80 | #: addon\globalPlugins\quickNotetaker\dialogs.py:138 81 | msgid "Save as Microsoft &Word document also" 82 | msgstr "Также сохранить как документ Microsoft &Word" 83 | 84 | #. Translators: a lable of a button in Notetaker dialog 85 | #: addon\globalPlugins\quickNotetaker\dialogs.py:148 86 | msgid "&Save and close" 87 | msgstr "&Сохранить и закрыть" 88 | 89 | #. Translators: a lable of a button in Notetaker dialog 90 | #: addon\globalPlugins\quickNotetaker\dialogs.py:155 91 | msgid "&Discard" 92 | msgstr "&Отменить" 93 | 94 | #. Translators: The message which asks the user whether they want to exit and discard changes in Notetaker dialog 95 | #: addon\globalPlugins\quickNotetaker\dialogs.py:200 96 | msgid "Are you sure you want to exit and discard changes?" 97 | msgstr "Вы уверены, что хотите выйти и отменить изменения?" 98 | 99 | #. Translators: The title of the message which asks the user whether they want to exit and discard changes in Notetaker dialog 100 | #. Translators: the title of the message telling the user that opening Notes Manager wasn't possible 101 | #. Translators: the title of the message telling the user that editing the note wasn't possible 102 | #. Translators: the title of the message telling the user that opening Notetaker wasn't possible 103 | #. Translators: the title of the message shown to the user when the note attached Word document is no longer available. 104 | #. This message is displayed when trying to open the note's Word document from the Notes Manager dialog 105 | #. Translators: the title of the warning messaged shown to the user when they try to delete a note from Notes Manager 106 | #. Translators: the title of the message telling the user that the directory they tried to save the document in is no longer available. 107 | #. See the message body for more details 108 | #: addon\globalPlugins\quickNotetaker\dialogs.py:202 109 | #: addon\globalPlugins\quickNotetaker\dialogs.py:224 110 | #: addon\globalPlugins\quickNotetaker\dialogs.py:482 111 | #: addon\globalPlugins\quickNotetaker\dialogs.py:506 112 | #: addon\globalPlugins\quickNotetaker\dialogs.py:609 113 | #: addon\globalPlugins\quickNotetaker\dialogs.py:622 114 | #: addon\globalPlugins\quickNotetaker\dialogs.py:659 115 | msgid "Warning" 116 | msgstr "Предупреждение" 117 | 118 | #. Translators: the message shown to the user when opening Notes Manager is not possible because a one is already opened 119 | #: addon\globalPlugins\quickNotetaker\dialogs.py:222 120 | msgid "Couldn't open Notes Manager! A Notes Manager window is already opened." 121 | msgstr "" 122 | "Не удалось открыть менеджер заметок! Окно менеджера заметок уже открыто." 123 | 124 | #. Translators: The message which tells the user that copying the note was successful 125 | #. Translators: the message telling the user that copying the note was successful 126 | #: addon\globalPlugins\quickNotetaker\dialogs.py:260 127 | #: addon\globalPlugins\quickNotetaker\dialogs.py:268 128 | #: addon\globalPlugins\quickNotetaker\dialogs.py:637 129 | #: addon\globalPlugins\quickNotetaker\dialogs.py:647 130 | msgid "Copied to clipboard!" 131 | msgstr "Скопировано в буфер обмена!" 132 | 133 | #. Translators: The title of the Notes Manager dialog 134 | #: addon\globalPlugins\quickNotetaker\dialogs.py:327 135 | msgid "Notes Manager - Quick Notetaker" 136 | msgstr "Менеджер заметок - Быстрый блокнот" 137 | 138 | #. Translators: the label of the notes list in Notes Manager dialog 139 | #: addon\globalPlugins\quickNotetaker\dialogs.py:339 140 | msgid "No&tes:" 141 | msgstr "&Заметки:" 142 | 143 | #. Translators: the name of the first column in the notes list in Notes Manager dialog 144 | #: addon\globalPlugins\quickNotetaker\dialogs.py:355 145 | msgid "Title" 146 | msgstr "Название" 147 | 148 | #. Translators: the name of the second column in the notes list in Notes Manager dialog 149 | #: addon\globalPlugins\quickNotetaker\dialogs.py:358 150 | msgid "Last Edited" 151 | msgstr "Последнее редактирование" 152 | 153 | #. Translators: the name of the third column in the notes list in Notes Manager dialog 154 | #: addon\globalPlugins\quickNotetaker\dialogs.py:360 155 | msgid "Preview" 156 | msgstr "Предварительный просмотр" 157 | 158 | #. Translaters: The lable of a button in Notes Manager dialog 159 | #: addon\globalPlugins\quickNotetaker\dialogs.py:368 160 | msgid "&View note..." 161 | msgstr "&Посмотреть заметку..." 162 | 163 | #. Translaters: The lable of a button in Notes Manager dialog 164 | #: addon\globalPlugins\quickNotetaker\dialogs.py:374 165 | msgid "&Edit note..." 166 | msgstr "&Редактировать заметку..." 167 | 168 | #. Translaters: The lable of a button in Notes Manager dialog 169 | #: addon\globalPlugins\quickNotetaker\dialogs.py:380 170 | msgid "Co&py note" 171 | msgstr "&Копировать заметку" 172 | 173 | #. Translaters: The lable of the open in Word button in Notes Manager dialog in case the note has a Word document attached to it 174 | #. Translators: the lable of the open in word button in Notes Manager dialog in case the note has a Word document attached to it 175 | #: addon\globalPlugins\quickNotetaker\dialogs.py:386 176 | #: addon\globalPlugins\quickNotetaker\dialogs.py:532 177 | msgid "&Open in Microsoft Word..." 178 | msgstr "Открыть в Microsoft &Word..." 179 | 180 | #. Translaters: The lable of a button in Notes Manager dialog 181 | #: addon\globalPlugins\quickNotetaker\dialogs.py:398 182 | msgid "&Delete note..." 183 | msgstr "&Удалить заметку..." 184 | 185 | #. Translators: the label of a button in Notes Manager dialog 186 | #: addon\globalPlugins\quickNotetaker\dialogs.py:412 187 | msgid "&New note..." 188 | msgstr "&Новая заметка..." 189 | 190 | #: addon\globalPlugins\quickNotetaker\dialogs.py:418 191 | msgid "Open &settings..." 192 | msgstr "Открыть нас&тройки..." 193 | 194 | #. Translaters: The lable of a button in Notes Manager dialog 195 | #: addon\globalPlugins\quickNotetaker\dialogs.py:433 196 | msgid "&Close" 197 | msgstr "&Закрыть" 198 | 199 | #. Translators: the message shown to the user when editing the note is not possible 200 | #: addon\globalPlugins\quickNotetaker\dialogs.py:480 201 | msgid "" 202 | "Couldn't edit note! An open Notetaker window with unsaved changes is present." 203 | msgstr "" 204 | "Не удалось отредактировать заметку! Присутствует открытое окно блокнота с " 205 | "несохраненными изменениями." 206 | 207 | #. Translators: the message shown to the user when opening Notetaker is not possible because a one is already opened 208 | #: addon\globalPlugins\quickNotetaker\dialogs.py:504 209 | msgid "Couldn't open Notetaker! A Notetaker window is already opened." 210 | msgstr "Не могу открыть блокнот! Окно блокнота уже открыто." 211 | 212 | #. Translators: the lable of the open in word button in Notes Manager dialog in case the note has no Word document attached to it 213 | #: addon\globalPlugins\quickNotetaker\dialogs.py:535 214 | msgid "Create Microsoft &Word document" 215 | msgstr "Создать документ Microsoft &Word" 216 | 217 | #. Translators: the message shown to the user when the note attached Word document is no longer available. 218 | #. This message is displayed when trying to open the note's Word document from the Notes Manager dialog 219 | #: addon\globalPlugins\quickNotetaker\dialogs.py:606 220 | msgid "" 221 | "A document with the specified name was not found! You can create a new one " 222 | "so you would be able to view this note as a Microsoft Word document." 223 | msgstr "" 224 | "Документ с указанным именем не найден! Вы можете создать новую, чтобы иметь " 225 | "возможность просматривать эту заметку как документ Microsoft Word." 226 | 227 | #. Translators: the warning messaged shown to the user when they try to delete a note from Notes Manager 228 | #: addon\globalPlugins\quickNotetaker\dialogs.py:620 229 | msgid "Are you sure you want to delete this note?" 230 | msgstr "Вы уверены, что хотите удалить эту заметку?" 231 | 232 | #. Translators: the message which tells the user that the directory they tried to save the file in is no longer available, 233 | #. so the file was saved in the user default one if this was possible. 234 | #. If not, the file was saved in the quick Notetaker directory in documents folder 235 | #: addon\globalPlugins\quickNotetaker\dialogs.py:656 236 | msgid "" 237 | "The saved path for the Microsoft Word document no longer exists! The " 238 | "document was saved in the default directory for the ad-on!" 239 | msgstr "" 240 | "Сохраненный путь к документу Microsoft Word больше не существует! Документ " 241 | "был сохранен в каталоге по умолчанию для дополнения!" 242 | 243 | #. Translators: The title of the dialog which allows the user to choose the folder where they want to save the note's corresponding Word document. 244 | #. This dialog is displayed to the user if the option of "Ask me each time where to save the note's corresponding Word document" in quick Notetaker settings is checked 245 | #: addon\globalPlugins\quickNotetaker\dialogs.py:667 246 | msgid "Select the folder where the document will be saved" 247 | msgstr "Выберите папку, в которой будет сохранен документ" 248 | 249 | #. Translators: the title given to a note if it has no title 250 | #: addon\globalPlugins\quickNotetaker\helpers.py:164 251 | msgid "Untitled" 252 | msgstr "Без названия" 253 | 254 | #. Translators: the label of the control in Quick Notetaker settings panel for choosing a default folder where the add-on documents will be saved 255 | #: addon\globalPlugins\quickNotetaker\settingsPanel.py:25 256 | msgid "Default documents directory:" 257 | msgstr "Каталог документов по умолчанию:" 258 | 259 | #. Translators: the label of a button to browse for a directory 260 | #: addon\globalPlugins\quickNotetaker\settingsPanel.py:32 261 | msgid "Browse..." 262 | msgstr "Обзор..." 263 | 264 | #. Translators: The title of the dialog presented when browsing for the directory where quick notetaker documents will be stored 265 | #: addon\globalPlugins\quickNotetaker\settingsPanel.py:35 266 | msgid "" 267 | "Select a default directory where the documents of Quick Notetaker will be " 268 | "stored" 269 | msgstr "" 270 | "Выберите каталог по умолчанию, в котором будут храниться документы быстрого " 271 | "блокнота" 272 | 273 | #. Translators: the label of a check box in Quick Notetaker settings panel 274 | #: addon\globalPlugins\quickNotetaker\settingsPanel.py:44 275 | msgid "" 276 | "Ask me each time &where to save the note's corresponding Microsoft Word " 277 | "document" 278 | msgstr "" 279 | "Спрашивать каждый раз &где сохранить соответствующий документ Microsoft Word " 280 | "для заметки" 281 | 282 | #. Translators: the label of a check box in Quick Notetaker settings panel 283 | #: addon\globalPlugins\quickNotetaker\settingsPanel.py:51 284 | msgid "" 285 | "&Open the note's corresponding Microsoft Word document after saving or " 286 | "updating" 287 | msgstr "" 288 | "&Открыть соответствующий документ Microsoft Word после сохранения или " 289 | "обновления" 290 | 291 | #. Translators: the label of a check box in Quick Notetaker settings panel 292 | #: addon\globalPlugins\quickNotetaker\settingsPanel.py:58 293 | msgid "&Capture the active window title when creating a new note" 294 | msgstr "&Захватить заголовок активного окна при создании новой заметки" 295 | 296 | #. Translators: the label of a check box in Quick Notetaker settings panel 297 | #: addon\globalPlugins\quickNotetaker\settingsPanel.py:65 298 | msgid "&Remember the note taker window size and position" 299 | msgstr "Запомнить &размер и положение окна заметки" 300 | 301 | #. Translators: the label of a check box in Quick Notetaker settings panel 302 | #: addon\globalPlugins\quickNotetaker\settingsPanel.py:72 303 | msgid "Au&to align text when editing notes (relevant for RTL languages)" 304 | msgstr "" 305 | "&Автоматическое выравнивание текста при редактировании заметок (актуально " 306 | "для языков с письмом справа налево)" 307 | 308 | #. Add-on description 309 | #. Translators: Long description to be shown for this add-on on add-on information from add-ons manager 310 | #: buildVars.py:26 311 | msgid "" 312 | "A note taker for NVDA. Allows the user to create, edit, view, manage and " 313 | "export notes to different formats." 314 | msgstr "" 315 | "Блокнот для NVDA. Позволяет пользователю создавать, редактировать, " 316 | "просматривать, управлять и экспортировать заметки в различные форматы." 317 | -------------------------------------------------------------------------------- /COPYING.txt: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Library General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License 307 | along with this program; if not, write to the Free Software 308 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 309 | 310 | 311 | Also add information on how to contact you by electronic and paper mail. 312 | 313 | If the program is interactive, make it output a short notice like this 314 | when it starts in an interactive mode: 315 | 316 | Gnomovision version 69, Copyright (C) year name of author 317 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 318 | This is free software, and you are welcome to redistribute it 319 | under certain conditions; type `show c' for details. 320 | 321 | The hypothetical commands `show w' and `show c' should show the appropriate 322 | parts of the General Public License. Of course, the commands you use may 323 | be called something other than `show w' and `show c'; they could even be 324 | mouse-clicks or menu items--whatever suits your program. 325 | 326 | You should also get your employer (if you work as a programmer) or your 327 | school, if any, to sign a "copyright disclaimer" for the program, if 328 | necessary. Here is a sample; alter the names: 329 | 330 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 331 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 332 | 333 | , 1 April 1989 334 | Ty Coon, President of Vice 335 | 336 | This General Public License does not permit incorporating your program into 337 | proprietary programs. If your program is a subroutine library, you may 338 | consider it more useful to permit linking proprietary applications with the 339 | library. If this is what you want to do, use the GNU Library General 340 | Public License instead of this License. 341 | -------------------------------------------------------------------------------- /addon/globalPlugins/quickNotetaker/dialogs.py: -------------------------------------------------------------------------------- 1 | # dialogs.py 2 | # -*- coding: utf-8 -*- 3 | # A part from Quick Notetaker add-on 4 | # Copyright (C) 2022 NV Access Limited, Mohammad Suliman, Eilana Benish 5 | # This file is covered by the GNU General Public License. 6 | # See the file COPYING for more details. 7 | 8 | import wx 9 | import gui 10 | from gui import guiHelper 11 | from gui import nvdaControls 12 | from gui.dpiScalingHelper import DpiScalingHelperMixin, DpiScalingHelperMixinWithoutInit 13 | from gui.settingsDialogs import NVDASettingsDialog 14 | from logHandler import log 15 | import ui 16 | from .lib.markdown2 import markdown 17 | import weakref 18 | import api 19 | import re 20 | from . import notesManager 21 | from .helpers import * 22 | from . import addonConfig 23 | from .settingsPanel import QuickNotetakerPanel 24 | import addonHandler 25 | 26 | 27 | addonHandler.initTranslation() 28 | 29 | 30 | #: Stores a Notes Manager instance if it exists 31 | notesManagerInstance = None 32 | 33 | 34 | #: Stores a Notes Taker instance if it exists 35 | noteTakerInstance = None 36 | 37 | 38 | class NoteTakerDialog(wx.Dialog): 39 | 40 | @classmethod 41 | def _instance(cls): 42 | """ type: () -> NoteTakerDialog 43 | return None until this is replaced with a weakref.ref object. Then the instance is retrieved 44 | with by treating that object as a callable. 45 | """ 46 | return None 47 | 48 | def __new__(cls, *args, **kwargs): 49 | instance = NoteTakerDialog._instance() 50 | if instance is None: 51 | return super(NoteTakerDialog, cls).__new__(cls, *args, **kwargs) 52 | return instance 53 | 54 | def _getDialogSizeAndPosition(self): 55 | dialogSize = wx.Size(500, 500) 56 | dialogPos = wx.DefaultPosition 57 | if addonConfig.getValue("rememberTakerSizeAndPos"): 58 | log.debug( 59 | "Setting Quick Notetaker Notetaker window position and size") 60 | dialogSize = wx.Size( 61 | addonConfig.getValue("takerWidth"), 62 | addonConfig.getValue("takerHeight") 63 | ) 64 | dialogPos = wx.Point( 65 | x=addonConfig.getValue("takerXPos"), 66 | y=addonConfig.getValue("takerYPos") 67 | ) 68 | return dialogSize, dialogPos 69 | 70 | def __init__(self, currentNote=None, noteTitle=None): 71 | if NoteTakerDialog._instance() is not None: 72 | return 73 | NoteTakerDialog._instance = weakref.ref(self) 74 | 75 | dialogSize, dialogPos = self._getDialogSizeAndPosition() 76 | # Translators: the title for the Quick Notetaker Notetaker window 77 | title = _("Notetaker - Quick Notetaker") 78 | if noteTitle: 79 | title = f"{noteTitle} - {title}" 80 | 81 | super().__init__( 82 | gui.mainFrame, 83 | title=title, 84 | size=dialogSize, 85 | pos=dialogPos, 86 | style=wx.CAPTION | wx.CLOSE_BOX | wx.RESIZE_BORDER | wx.STAY_ON_TOP 87 | ) 88 | 89 | mainSizer = wx.BoxSizer(wx.VERTICAL) 90 | sHelper = guiHelper.BoxSizerHelper(self, wx.VERTICAL) 91 | # Translators: a lable of a button in Notetaker dialog 92 | openManagerButton = wx.Button(self, label=_("Open Notes &Manager...")) 93 | openManagerButton.Bind(wx.EVT_BUTTON, self.onOpenManager) 94 | sHelper.addItem(openManagerButton, flag=wx.ALIGN_CENTER) 95 | if notesManagerInstance: 96 | openManagerButton.Disable() 97 | buttonsHelper = guiHelper.ButtonHelper(wx.HORIZONTAL) 98 | preViewButton = buttonsHelper.addButton( 99 | self, 100 | # Translators: a lable of a button in Notetaker dialog 101 | label=_("P&review note...")) 102 | preViewButton.Bind(wx.EVT_BUTTON, self.onPreview) 103 | # Translators: a lable of a button in Notetaker dialog 104 | copyButton = buttonsHelper.addButton(self, label=_("Co&py")) 105 | copyButton.Bind(wx.EVT_BUTTON, self.onCopy) 106 | copyHtmlButton = buttonsHelper.addButton( 107 | self, 108 | # Translators: a lable of a button in Notetaker dialog 109 | label=_("Copy &HTML code")) 110 | copyHtmlButton.Bind(wx.EVT_BUTTON, self.onCopyAsHtml) 111 | sHelper.addItem(buttonsHelper.sizer) 112 | 113 | sizer = wx.BoxSizer(wx.VERTICAL) 114 | # Translators: The lable of the note content edit area in Notetaker dialog 115 | label = wx.StaticText(self, label=_("&Note content:")) 116 | sizer.Add(label, flag=wx.ALIGN_CENTER_HORIZONTAL) 117 | sizer.AddSpacer(guiHelper.SPACE_BETWEEN_ASSOCIATED_CONTROL_VERTICAL) 118 | self.noteEditArea = wx.TextCtrl( 119 | self, style=wx.TE_RICH2 | wx.TE_MULTILINE) 120 | self.noteEditArea.Bind(wx.EVT_TEXT, self.onCharacter) 121 | self.noteEditArea.Bind(wx.EVT_KEY_UP, self.onKeyUp) 122 | sizer.Add(self.noteEditArea, proportion=1, flag=wx.EXPAND) 123 | sHelper.addItem(sizer, proportion=1, flag=wx.EXPAND) 124 | if noteTitle: 125 | self.noteEditArea.SetValue(noteTitle + "\n\n") 126 | self.noteEditArea.SetInsertionPointEnd() 127 | self.currentNote = currentNote 128 | if self.currentNote: 129 | self.noteEditArea.SetValue(self.currentNote.content) 130 | self.noteEditArea.SetFocus() 131 | 132 | if self.currentNote and self.currentNote.docxPath: 133 | # Translators: The label of the check box in Notetaker dialog when editing a note which has Word document attached to it 134 | checkboxText = _( 135 | "Update the corresponding Microsoft &Word document also") 136 | else: 137 | # Translators: The label of the check box in Notetaker dialog when creating a new note or when editing an existing note with no Word document attached to it 138 | checkboxText = _("Save as Microsoft &Word document also") 139 | self.saveAswordCheckBox = sHelper.addItem( 140 | wx.CheckBox(self, label=checkboxText)) 141 | if self.currentNote and self.currentNote.docxPath: 142 | self.saveAswordCheckBox.Value = True 143 | buttons = guiHelper.ButtonHelper(wx.HORIZONTAL) 144 | saveButton = buttons.addButton( 145 | self, 146 | id=wx.ID_OK, 147 | # Translators: a lable of a button in Notetaker dialog 148 | label=_("&Save and close")) 149 | saveButton.SetDefault() 150 | saveButton.Bind(wx.EVT_BUTTON, self.onsaveChanges) 151 | discardButton = buttons.addButton( 152 | self, 153 | id=wx.ID_CLOSE, 154 | # Translators: a lable of a button in Notetaker dialog 155 | label=_("&Discard")) 156 | discardButton.Bind(wx.EVT_BUTTON, lambda evt: self.Close()) 157 | sHelper.addDialogDismissButtons(buttons, True) 158 | mainSizer.Add(sHelper.sizer, proportion=1, flag=wx.EXPAND) 159 | self.SetSizer(mainSizer) 160 | self.Bind(wx.EVT_CLOSE, self.onDiscard) 161 | self.Bind(wx.EVT_WINDOW_DESTROY, self.onDestroy) 162 | self.EscapeId = wx.ID_CLOSE 163 | 164 | def onDestroy(self, evt): 165 | global noteTakerInstance 166 | noteTakerInstance = None 167 | evt.Skip() 168 | 169 | def onPreview(self, evt): 170 | mdContent = self.noteEditArea.GetValue() 171 | mdContent = handleMdContent(mdContent) 172 | htmlContent = markdown(mdContent, extras=["markdown-in-html"]) 173 | title = getTitle(mdContent) 174 | ui.browseableMessage(htmlContent, title, True) 175 | 176 | def onsaveChanges(self, evt): 177 | newContent = self.noteEditArea.GetValue() 178 | if self.saveAswordCheckBox.Value: 179 | self.saveAsWord(newContent) 180 | return 181 | if self.currentNote: 182 | notesManager.updateNote(self.currentNote.id, newContent) 183 | else: 184 | notesManager.saveNewNote(newContent) 185 | self._savePositionInformation() 186 | self._clean() 187 | 188 | def onDiscard(self, evt): 189 | textAreaContent = self.noteEditArea.GetValue() 190 | if not textAreaContent: 191 | self._savePositionInformation() 192 | self._clean() 193 | return 194 | if self.currentNote and self.currentNote.content == textAreaContent: 195 | self._savePositionInformation() 196 | self._clean() 197 | return 198 | res = gui.messageBox( 199 | # Translators: The message which asks the user whether they want to exit and discard changes in Notetaker dialog 200 | _("Are you sure you want to exit and discard changes?"), 201 | # Translators: The title of the message which asks the user whether they want to exit and discard changes in Notetaker dialog 202 | _("Warning"), 203 | style=wx.YES_NO | wx.NO_DEFAULT | wx.CANCEL, 204 | parent=self) 205 | if res == wx.YES: 206 | self._savePositionInformation() 207 | self._clean() 208 | 209 | def _savePositionInformation(self): 210 | position = self.GetPosition() 211 | addonConfig.setValue("takerXPos", position.x) 212 | addonConfig.setValue("takerYPos", position.y) 213 | size = self.GetSize() 214 | addonConfig.setValue("takerWidth", size.width) 215 | addonConfig.setValue("takerHeight", size.height) 216 | 217 | def onOpenManager(self, evt): 218 | global notesManagerInstance 219 | if notesManagerInstance: 220 | gui.messageBox( 221 | # Translators: the message shown to the user when opening Notes Manager is not possible because a one is already opened 222 | _("Couldn't open Notes Manager! A Notes Manager window is already opened."), 223 | # Translators: the title of the message telling the user that opening Notes Manager wasn't possible 224 | _("Warning"), 225 | style=wx.ICON_WARNING | wx.OK, 226 | parent=self 227 | ) 228 | return 229 | gui.mainFrame.prePopup() 230 | notesManagerInstance = NotesManagerDialog() 231 | notesManagerInstance.Show() 232 | gui.mainFrame.postPopup() 233 | 234 | def onKeyUp(self, evt): 235 | if evt.GetModifiers() == wx.MOD_CONTROL: 236 | if evt.GetKeyCode() == ord("R"): 237 | self.noteEditArea.SetLayoutDirection(wx.Layout_RightToLeft) 238 | elif evt.GetKeyCode() == ord("L"): 239 | self.noteEditArea.SetLayoutDirection(wx.Layout_LeftToRight) 240 | evt.Skip() 241 | 242 | def onCharacter(self, evt): 243 | content = self.noteEditArea.GetValue() 244 | if not addonConfig.getValue("autoAlignText"): 245 | evt.Skip() 246 | return 247 | res = handleTextAlignment( 248 | content, self.noteEditArea.GetLayoutDirection()) 249 | if res == Align.ALIGN_TO_LEFT: 250 | self.noteEditArea.SetLayoutDirection(wx.Layout_LeftToRight) 251 | elif res == Align.ALIGN_TO_RIGHT: 252 | self.noteEditArea.SetLayoutDirection(wx.Layout_RightToLeft) 253 | evt.Skip() 254 | 255 | def onCopy(self, evt): 256 | content = self.noteEditArea.GetValue() 257 | res = api.copyToClip(content, False) 258 | if res == True: 259 | # Translators: The message which tells the user that copying the note was successful 260 | ui.message(_("Copied to clipboard!")) 261 | 262 | def onCopyAsHtml(self, evt): 263 | content = self.noteEditArea.GetValue() 264 | res = api.copyToClip( 265 | markdown(content, extras=["markdown-in-html"]), False) 266 | if res == True: 267 | # Translators: The message which tells the user that copying the note was successful 268 | ui.message(_("Copied to clipboard!")) 269 | 270 | def saveAsWord(self, newContent): 271 | docxPath = "" 272 | if self.currentNote and self.currentNote.docxPath: 273 | docxPath = self.currentNote.docxPath 274 | elif addonConfig.getValue("askWhereToSaveDocx"): 275 | docxPath = askUserWhereToSave(self, newContent) 276 | if docxPath is None: 277 | return 278 | saveAsWord( 279 | newContent, 280 | docxPath, 281 | self._saveAsWordCallback, 282 | self.currentNote.id if self.currentNote else None 283 | ) 284 | self._savePositionInformation() 285 | self._clean() 286 | 287 | def _saveAsWordCallback(self, outputFilePath, dirWasChanged, mdContent, noteID): 288 | if noteID: 289 | notesManager.updateNote(noteID, mdContent, outputFilePath) 290 | else: 291 | notesManager.saveNewNote(mdContent, outputFilePath) 292 | notifyDirWasChanged(dirWasChanged) 293 | if notesManagerInstance: 294 | notesManagerInstance.refreshAllNotesList( 295 | notesManagerInstance.notesList.GetFirstSelected()) 296 | 297 | def _clean(self): 298 | self.DestroyChildren() 299 | self.Destroy() 300 | 301 | 302 | class NotesManagerDialog( 303 | DpiScalingHelperMixinWithoutInit, 304 | wx.Dialog # wxPython does not seem to call base class initializer, put last in MRO 305 | ): 306 | 307 | @classmethod 308 | def _instance(cls): 309 | """ type: () -> NotesManagerDialog 310 | return None until this is replaced with a weakref.ref object. Then the instance is retrieved 311 | with by treating that object as a callable. 312 | """ 313 | return None 314 | 315 | def __new__(cls, *args, **kwargs): 316 | instance = NotesManagerDialog._instance() 317 | if instance is None: 318 | return super(NotesManagerDialog, cls).__new__(cls, *args, **kwargs) 319 | return instance 320 | 321 | def __init__(self): 322 | if NotesManagerDialog._instance() is not None: 323 | return 324 | NotesManagerDialog._instance = weakref.ref(self) 325 | 326 | # Translators: The title of the Notes Manager dialog 327 | title = _("Notes Manager - Quick Notetaker") 328 | super().__init__( 329 | gui.mainFrame, 330 | title=title, 331 | style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.MAXIMIZE_BOX, 332 | ) 333 | mainSizer = wx.BoxSizer(wx.VERTICAL) 334 | firstTextSizer = wx.BoxSizer(wx.VERTICAL) 335 | listAndButtonsSizerHelper = guiHelper.BoxSizerHelper( 336 | self, sizer=wx.BoxSizer(wx.HORIZONTAL)) 337 | 338 | # Translators: the label of the notes list in Notes Manager dialog 339 | entriesLabel = _("No&tes:") 340 | firstTextSizer.Add(wx.StaticText(self, label=entriesLabel)) 341 | mainSizer.Add( 342 | firstTextSizer, 343 | border=guiHelper.BORDER_FOR_DIALOGS, 344 | flag=wx.TOP | wx.LEFT | wx.RIGHT 345 | ) 346 | self.notesList = listAndButtonsSizerHelper.addItem( 347 | nvdaControls.AutoWidthColumnListCtrl( 348 | parent=self, 349 | style=wx.LC_REPORT | wx.LC_SINGLE_SEL, 350 | ), 351 | flag=wx.EXPAND, 352 | proportion=1, 353 | ) 354 | # Translators: the name of the first column in the notes list in Notes Manager dialog 355 | self.notesList.InsertColumn(0, _("Title"), width=self.scaleSize(200)) 356 | self.notesList.InsertColumn( 357 | # Translators: the name of the second column in the notes list in Notes Manager dialog 358 | 1, _("Last Edited"), width=self.scaleSize(100)) 359 | # Translators: the name of the third column in the notes list in Notes Manager dialog 360 | self.notesList.InsertColumn(2, _("Preview"), width=self.scaleSize(400)) 361 | self.notesList.Bind(wx.EVT_LIST_ITEM_FOCUSED, self.onListItemSelected) 362 | 363 | # this is the group of buttons that affects the currently selected note 364 | entryButtonsHelper = guiHelper.ButtonHelper(wx.VERTICAL) 365 | self.viewButton = entryButtonsHelper.addButton( 366 | self, 367 | # Translaters: The lable of a button in Notes Manager dialog 368 | label=_("&View note...")) 369 | self.viewButton.Disable() 370 | self.viewButton.Bind(wx.EVT_BUTTON, self.onView) 371 | self.editButton = entryButtonsHelper.addButton( 372 | self, 373 | # Translaters: The lable of a button in Notes Manager dialog 374 | label=_("&Edit note...")) 375 | self.editButton.Disable() 376 | self.editButton.Bind(wx.EVT_BUTTON, self.onEdit) 377 | self.copyButton = entryButtonsHelper.addButton( 378 | self, 379 | # Translaters: The lable of a button in Notes Manager dialog 380 | label=_("Co&py note")) 381 | self.copyButton.Disable() 382 | self.copyButton.Bind(wx.EVT_BUTTON, self.onCopy) 383 | self.openInWordButton = entryButtonsHelper.addButton( 384 | self, 385 | # Translaters: The lable of the open in Word button in Notes Manager dialog in case the note has a Word document attached to it 386 | label=_("&Open in Microsoft Word...")) 387 | self.openInWordButton.Disable() 388 | self.openInWordButton.Bind(wx.EVT_BUTTON, self.onOpenInWord) 389 | self.copyHtmlButton = entryButtonsHelper.addButton( 390 | self, 391 | # Translaters: The lable of a button in Notes Manager dialog 392 | label=_("Copy &HTML code")) 393 | self.copyHtmlButton.Disable() 394 | self.copyHtmlButton.Bind(wx.EVT_BUTTON, self.onCopyAsHtml) 395 | self.deleteButton = entryButtonsHelper.addButton( 396 | self, 397 | # Translaters: The lable of a button in Notes Manager dialog 398 | label=_("&Delete note...")) 399 | self.deleteButton.Disable() 400 | self.deleteButton.Bind(wx.EVT_BUTTON, self.onDelete) 401 | listAndButtonsSizerHelper.addItem(entryButtonsHelper.sizer) 402 | 403 | mainSizer.Add( 404 | listAndButtonsSizerHelper.sizer, 405 | border=guiHelper.BORDER_FOR_DIALOGS, 406 | flag=wx.ALL | wx.EXPAND, 407 | proportion=1, 408 | ) 409 | 410 | generalActions = guiHelper.ButtonHelper(wx.HORIZONTAL) 411 | # Translators: the label of a button in Notes Manager dialog 412 | newNoteButton = generalActions.addButton(self, label=_("&New note...")) 413 | newNoteButton.Bind(wx.EVT_BUTTON, self.onNewNote) 414 | if noteTakerInstance: 415 | newNoteButton.Disable() 416 | # Translaters: The lable of a button in Notes Manager dialog 417 | openSettingsButton = generalActions.addButton( 418 | self, label=_("Open &settings...")) 419 | openSettingsButton.Bind(wx.EVT_BUTTON, self.onSettings) 420 | mainSizer.Add( 421 | generalActions.sizer, 422 | border=guiHelper.BORDER_FOR_DIALOGS, 423 | flag=wx.LEFT | wx.RIGHT 424 | ) 425 | 426 | mainSizer.Add( 427 | wx.StaticLine(self), 428 | border=guiHelper.BORDER_FOR_DIALOGS, 429 | flag=wx.ALL | wx.EXPAND 430 | ) 431 | 432 | # Translaters: The lable of a button in Notes Manager dialog 433 | closeButton = wx.Button(self, label=_("&Close"), id=wx.ID_CLOSE) 434 | closeButton.Bind(wx.EVT_BUTTON, lambda evt: self.Close()) 435 | mainSizer.Add( 436 | closeButton, 437 | border=guiHelper.BORDER_FOR_DIALOGS, 438 | flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.CENTER | wx.ALIGN_RIGHT 439 | ) 440 | self.Bind(wx.EVT_CLOSE, self.onClose) 441 | self.EscapeId = wx.ID_CLOSE 442 | 443 | mainSizer.Fit(self) 444 | self.SetSizer(mainSizer) 445 | 446 | self.refreshAllNotesList() 447 | self.SetMinSize(mainSizer.GetMinSize()) 448 | self.SetSize(self.scaleSize((763, 509))) 449 | self.CentreOnScreen() 450 | self.notesList.SetFocus() 451 | self.Bind(wx.EVT_ACTIVATE, self.onActivate) 452 | self.Bind(wx.EVT_WINDOW_DESTROY, self.onDestroy) 453 | 454 | def onActivate(self, evt): 455 | if evt.GetActive(): 456 | self.refreshAllNotesList(self.notesList.GetFirstSelected()) 457 | evt.Skip() 458 | 459 | def onDestroy(self, evt): 460 | global notesManagerInstance 461 | notesManagerInstance = None 462 | evt.Skip() 463 | 464 | def onView(self, evt): 465 | curNote = self._getCurrentNote() 466 | if not curNote: 467 | return 468 | content = handleMdContent(curNote.content) 469 | contentAsHtml = markdown(content, extras=["markdown-in-html"]) 470 | ui.browseableMessage(contentAsHtml, curNote.title, True) 471 | 472 | def onEdit(self, evt): 473 | curNote = self._getCurrentNote() 474 | if not curNote: 475 | return 476 | global noteTakerInstance 477 | if noteTakerInstance: 478 | gui.messageBox( 479 | # Translators: the message shown to the user when editing the note is not possible 480 | _("Couldn't edit note! An open Notetaker window with unsaved changes is present."), 481 | # Translators: the title of the message telling the user that editing the note wasn't possible 482 | _("Warning"), 483 | style=wx.ICON_WARNING | wx.OK, 484 | parent=self 485 | ) 486 | return 487 | gui.mainFrame.prePopup() 488 | noteTakerInstance = NoteTakerDialog(currentNote=curNote) 489 | noteTakerInstance.Show() 490 | gui.mainFrame.postPopup() 491 | 492 | def _getCurrentNote(self): 493 | index = self.notesList.GetFirstSelected() 494 | if index < 0: 495 | return 496 | curNote = notesManager.loadAllNotes()[index] 497 | return curNote 498 | 499 | def onNewNote(self, evt): 500 | global noteTakerInstance 501 | if noteTakerInstance: 502 | gui.messageBox( 503 | # Translators: the message shown to the user when opening Notetaker is not possible because a one is already opened 504 | _("Couldn't open Notetaker! A Notetaker window is already opened."), 505 | # Translators: the title of the message telling the user that opening Notetaker wasn't possible 506 | _("Warning"), 507 | style=wx.ICON_WARNING | wx.OK, 508 | parent=self 509 | ) 510 | return 511 | gui.mainFrame.prePopup() 512 | noteTakerInstance = NoteTakerDialog() 513 | noteTakerInstance.Show() 514 | gui.mainFrame.postPopup() 515 | 516 | def onSettings(self, evt): 517 | gui.mainFrame._popupSettingsDialog( 518 | NVDASettingsDialog, QuickNotetakerPanel) 519 | 520 | def onClose(self, evt): 521 | self.DestroyChildren() 522 | self.Destroy() 523 | evt.Skip() 524 | 525 | def onListItemSelected(self, evt): 526 | self.viewButton.Enable() 527 | self.editButton.Enable() 528 | self.copyButton.Enable() 529 | curNote = self._getCurrentNote() 530 | if curNote and curNote.docxPath: 531 | # Translators: the lable of the open in word button in Notes Manager dialog in case the note has a Word document attached to it 532 | label = _("&Open in Microsoft Word...") 533 | else: 534 | # Translators: the lable of the open in word button in Notes Manager dialog in case the note has no Word document attached to it 535 | label = _("Create Microsoft &Word document") 536 | if addonConfig.getValue("askWhereToSaveDocx"): 537 | label += "..." 538 | self.openInWordButton.SetLabel(label) 539 | self.openInWordButton.Enable() 540 | self.copyHtmlButton.Enable() 541 | self.deleteButton.Enable() 542 | 543 | def refreshAllNotesList(self, activeIndex=0): 544 | self.notesList.DeleteAllItems() 545 | for note in notesManager.loadAllNotes(): 546 | self.notesList.Append(( 547 | note.title, 548 | note.lastEdited, 549 | getPreviewText(note.content) 550 | )) 551 | # Select the given active note or the first one if not given 552 | allNotesLen = len(notesManager.loadAllNotes()) 553 | if allNotesLen > 0: 554 | if activeIndex == -1: 555 | activeIndex = allNotesLen - 1 556 | elif activeIndex < 0 or activeIndex >= allNotesLen: 557 | activeIndex = 0 558 | self.notesList.Select(activeIndex, on=1) 559 | self.notesList.SetItemState( 560 | activeIndex, wx.LIST_STATE_FOCUSED, wx.LIST_STATE_FOCUSED) 561 | else: 562 | self.viewButton.Disable() 563 | self.editButton.Disable() 564 | self.copyButton.Disable() 565 | self.openInWordButton.Disable() 566 | self.copyHtmlButton.Disable() 567 | self.deleteButton.Disable() 568 | 569 | def onOpenInWord(self, evt): 570 | curNote = self._getCurrentNote() 571 | if curNote and curNote.docxPath: 572 | openInWord( 573 | curNote.docxPath, 574 | self._openInWordCallback, 575 | curNote.id 576 | ) 577 | return 578 | if not curNote: 579 | return 580 | docxPath = "" 581 | if addonConfig.getValue("askWhereToSaveDocx"): 582 | docxPath = askUserWhereToSave(self, curNote.content) 583 | if docxPath is None: 584 | return 585 | saveAsWord( 586 | curNote.content, 587 | docxPath, 588 | self._saveAsWordCallback, 589 | curNote.id 590 | ) 591 | 592 | def _saveAsWordCallback(self, outputFilePath, dirWasChanged, mdContent, noteID): 593 | notesManager.updateNote(noteID, docxPath=outputFilePath) 594 | notifyDirWasChanged(dirWasChanged) 595 | if not self: 596 | return 597 | self.refreshAllNotesList(self.notesList.GetFirstSelected()) 598 | 599 | def _openInWordCallback(self, hasSucceeded, noteID): 600 | if hasSucceeded: 601 | return 602 | notesManager.updateNote(noteID, docxPath="") 603 | gui.messageBox( 604 | # Translators: the message shown to the user when the note attached Word document is no longer available. 605 | # This message is displayed when trying to open the note's Word document from the Notes Manager dialog 606 | _("A document with the specified name was not found! You can create a new one so you would be able to view this note as a Microsoft Word document."), 607 | # Translators: the title of the message shown to the user when the note attached Word document is no longer available. 608 | # This message is displayed when trying to open the note's Word document from the Notes Manager dialog 609 | _("Warning"), 610 | style=wx.ICON_WARNING | wx.OK, 611 | parent=gui.mainFrame 612 | ) 613 | 614 | def onDelete(self, evt): 615 | curNote = self._getCurrentNote() 616 | if not curNote: 617 | return 618 | res = gui.messageBox( 619 | # Translators: the warning messaged shown to the user when they try to delete a note from Notes Manager 620 | _("Are you sure you want to delete this note?"), 621 | # Translators: the title of the warning messaged shown to the user when they try to delete a note from Notes Manager 622 | _("Warning"), 623 | style=wx.YES_NO | wx.NO_DEFAULT, 624 | parent=self) 625 | if res != wx.YES: 626 | return 627 | notesManager.deleteNote(curNote.id) 628 | self.refreshAllNotesList(self.notesList.GetFirstSelected()) 629 | 630 | def onCopy(self, evt): 631 | curNote = self._getCurrentNote() 632 | if not curNote: 633 | return 634 | res = api.copyToClip(curNote.content, False) 635 | if res == True: 636 | # Translators: the message telling the user that copying the note was successful 637 | ui.message(_("Copied to clipboard!")) 638 | 639 | def onCopyAsHtml(self, evt): 640 | curNote = self._getCurrentNote() 641 | if not curNote: 642 | return 643 | res = api.copyToClip( 644 | markdown(curNote.content, extras=["markdown-in-html"]), False) 645 | if res: 646 | # Translators: the message telling the user that copying the note was successful 647 | ui.message(_("Copied to clipboard!")) 648 | 649 | 650 | def notifyDirWasChanged(dirWasChanged): 651 | if dirWasChanged: 652 | gui.messageBox( 653 | # Translators: the message which tells the user that the directory they tried to save the file in is no longer available, 654 | # so the file was saved in the user default one if this was possible. 655 | # If not, the file was saved in the quick Notetaker directory in documents folder 656 | _("The saved path for the Microsoft Word document no longer exists! The document was saved in the default directory for the ad-on!"), 657 | # Translators: the title of the message telling the user that the directory they tried to save the document in is no longer available. 658 | # See the message body for more details 659 | _("Warning"), 660 | style=wx.ICON_WARNING | wx.OK, 661 | parent=gui.mainFrame) 662 | 663 | 664 | def askUserWhereToSave(parent, noteContent): 665 | # Translators: The title of the dialog which allows the user to choose the folder where they want to save the note's corresponding Word document. 666 | # This dialog is displayed to the user if the option of "Ask me each time where to save the note's corresponding Word document" in quick Notetaker settings is checked 667 | with wx.DirDialog(parent, _("Select the folder where the document will be saved"), defaultPath=addonConfig.getValue("notesDocumentsPath")) as d: 668 | if d.ShowModal() == wx.ID_OK: 669 | return f"{d.Path}/{getTitle(noteContent)}.docx" 670 | else: 671 | return None 672 | --------------------------------------------------------------------------------