From 2d963325cbd583a0c10c50a35a18743d1d8112f9 Mon Sep 17 00:00:00 2001 From: Danstiv Date: Wed, 14 Jul 2021 14:26:19 +0700 Subject: [PATCH] Initial commit --- .gitattributes | 8 + .gitignore | 10 + addon/appModules/bvssh.py | 6 + addon/doc/ru/readme.md | 11 + addon/locale/ru/LC_MESSAGES/nvda.po | 30 +++ buildVars.py | 72 ++++++ manifest-translated.ini.tpl | 2 + manifest.ini.tpl | 10 + readme.md | 11 + sconstruct | 236 ++++++++++++++++++ site_scons/site_tools/gettexttool/__init__.py | 55 ++++ style.css | 26 ++ 12 files changed, 477 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 addon/appModules/bvssh.py create mode 100644 addon/doc/ru/readme.md create mode 100644 addon/locale/ru/LC_MESSAGES/nvda.po create mode 100644 buildVars.py create mode 100644 manifest-translated.ini.tpl create mode 100644 manifest.ini.tpl create mode 100644 readme.md create mode 100644 sconstruct create mode 100644 site_scons/site_tools/gettexttool/__init__.py create mode 100644 style.css diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..a1a1316 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,8 @@ +# Set default behaviour, in case users don't have core.autocrlf set. +* text=auto + +# Try to ensure that po files in the repo does not include +# source code line numbers. +# Every person expected to commit po files should change their personal config file as described here: +# https://mail.gnome.org/archives/kupfer-list/2010-June/msg00002.html +*.po filter=cleanpo diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a304182 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +addon/doc/*.css +addon/doc/en/ +*_docHandler.py +*.html +*.ini +*.mo +*.pot +*.py[co] +*.nvda-addon +.sconsign.dblite diff --git a/addon/appModules/bvssh.py b/addon/appModules/bvssh.py new file mode 100644 index 0000000..83aab87 --- /dev/null +++ b/addon/appModules/bvssh.py @@ -0,0 +1,6 @@ +import appModuleHandler +import NVDAObjects.IAccessible.sysListView32 +class AppModule(appModuleHandler.AppModule): + def event_NVDAObject_init(self, obj): + if isinstance(obj, NVDAObjects.IAccessible.sysListView32.List) and obj.name=='List1': + obj.name = obj.parent.next.name diff --git a/addon/doc/ru/readme.md b/addon/doc/ru/readme.md new file mode 100644 index 0000000..15e55b3 --- /dev/null +++ b/addon/doc/ru/readme.md @@ -0,0 +1,11 @@ +# Bitvise helper + +Улучшает доступность программы Bitvise SSH Client.
+ +1. Подписаны списки "Local Files" и "Remote Files". + +# Журнал изменений + +## 0.2 + +Добавлен перевод и документация. diff --git a/addon/locale/ru/LC_MESSAGES/nvda.po b/addon/locale/ru/LC_MESSAGES/nvda.po new file mode 100644 index 0000000..b5c2225 --- /dev/null +++ b/addon/locale/ru/LC_MESSAGES/nvda.po @@ -0,0 +1,30 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the bitviseHelper package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: bitviseHelper 0.2\n" +"Report-Msgid-Bugs-To: nvda-translations@groups.io\n" +"POT-Creation-Date: 2021-07-14 14:00+0700\n" +"PO-Revision-Date: 2021-07-14 14:03+0700\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 2.4.3\n" +"Last-Translator: Danstiv \n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : 2);\n" +"Language: ru\n" + +#. Add-on summary, usually the user visible name of the addon. +#. Translators: Summary for this add-on +#. to be shown on installation and add-on information found in Add-ons Manager. +msgid "Bitvise helper" +msgstr "Bitvise helper" + +#. Add-on description +#. Translators: Long description to be shown for this add-on on add-on information from add-ons manager +msgid "Improves accessibility of Bitvise SSH Client program." +msgstr "Улучшает доступность программы Bitvise SSH Client." diff --git a/buildVars.py b/buildVars.py new file mode 100644 index 0000000..f635af4 --- /dev/null +++ b/buildVars.py @@ -0,0 +1,72 @@ +# -*- coding: UTF-8 -*- + +# Build customizations +# Change this file instead of sconstruct or manifest files, whenever possible. + + +# Since some strings in `addon_info` are translatable, +# we need to include them in the .po files. +# Gettext recognizes only strings given as parameters to the `_` function. +# To avoid initializing translations in this module we simply roll our own "fake" `_` function +# which returns whatever is given to it as an argument. +def _(arg): + return arg + + +# Add-on information variables +addon_info = { + # add-on Name/identifier, internal for NVDA + "addon_name": "bitviseHelper", + # Add-on summary, usually the user visible name of the addon. + # Translators: Summary for this add-on + # to be shown on installation and add-on information found in Add-ons Manager. + "addon_summary": _("Bitvise helper"), + # Add-on description + # Translators: Long description to be shown for this add-on on add-on information from add-ons manager + "addon_description": _("""Improves accessibility of Bitvise SSH Client program."""), + # version + "addon_version": "0.2", + # Author(s) + "addon_author": "Danstiv ", + # URL for the add-on documentation support + "addon_url": "https://github.com/danstiv/bitviseHelper", + # Documentation file name + "addon_docFileName": "readme.html", + # Minimum NVDA version supported (e.g. "2018.3.0", minor version is optional) + "addon_minimumNVDAVersion": None, + # Last NVDA version supported/tested (e.g. "2018.4.0", ideally more recent than minimum version) + "addon_lastTestedNVDAVersion": 2021.1, + # Add-on update channel (default is None, denoting stable releases, + # and for development releases, use "dev".) + # Do not change unless you know what you are doing! + "addon_updateChannel": None, +} + +# Define the python files that are the sources of your add-on. +# You can either list every file (using ""/") as a path separator, +# or use glob expressions. +# For example to include all files with a ".py" extension from the "globalPlugins" dir of your add-on +# the list can be written as follows: +# pythonSources = ["addon/globalPlugins/*.py"] +# For more information on SCons Glob expressions please take a look at: +# https://scons.org/doc/production/HTML/scons-user/apd.html +pythonSources = ['addon/appModules/bitviseHelper/*.py'] + +# Files that contain strings for translation. Usually your python sources +i18nSources = pythonSources + ["buildVars.py"] + +# Files that will be ignored when building the nvda-addon file +# Paths are relative to the addon directory, not to the root directory of your addon sources. +excludedFiles = [] + +# Base language for the NVDA add-on +# If your add-on is written in a language other than english, modify this variable. +# For example, set baseLanguage to "es" if your add-on is primarily written in spanish. +baseLanguage = "en" + +# Markdown extensions for add-on documentation +# Most add-ons do not require additional Markdown extensions. +# If you need to add support for markup such as tables, fill out the below list. +# Extensions string must be of the form "markdown.extensions.extensionName" +# e.g. "markdown.extensions.tables" to add tables. +markdownExtensions = [] diff --git a/manifest-translated.ini.tpl b/manifest-translated.ini.tpl new file mode 100644 index 0000000..c06aa84 --- /dev/null +++ b/manifest-translated.ini.tpl @@ -0,0 +1,2 @@ +summary = "{addon_summary}" +description = """{addon_description}""" diff --git a/manifest.ini.tpl b/manifest.ini.tpl new file mode 100644 index 0000000..d44355d --- /dev/null +++ b/manifest.ini.tpl @@ -0,0 +1,10 @@ +name = {addon_name} +summary = "{addon_summary}" +description = """{addon_description}""" +author = "{addon_author}" +url = {addon_url} +version = {addon_version} +docFileName = {addon_docFileName} +minimumNVDAVersion = {addon_minimumNVDAVersion} +lastTestedNVDAVersion = {addon_lastTestedNVDAVersion} +updateChannel = {addon_updateChannel} diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..71e37ff --- /dev/null +++ b/readme.md @@ -0,0 +1,11 @@ +# Bitvise helper + +Improves accessibility of Bitvise SSH Client program.
+ +1. Labeled "Local Files" and "Remote Files" lists. + +# Change log + +## 0.2 + +Added translation and documentation. diff --git a/sconstruct b/sconstruct new file mode 100644 index 0000000..ab61654 --- /dev/null +++ b/sconstruct @@ -0,0 +1,236 @@ +# NVDA add-on template SCONSTRUCT file +# Copyright (C) 2012-2021 Rui Batista, Noelia Martinez, Joseph Lee +# This file is covered by the GNU General Public License. +# See the file COPYING.txt for more details. + +import codecs +import gettext +import os +import os.path +import zipfile +import sys + +# While names imported below are available by default in every SConscript +# Linters aren't aware about them. +# To avoid Flake8 F821 warnings about them they are imported explicitly. +# When using other Scons functions please add them to the line below. +from SCons.Script import BoolVariable, Builder, Copy, Environment, Variables + +sys.dont_write_bytecode = True + +# Bytecode should not be written for build vars module to keep the repository root folder clean. +import buildVars # NOQA: E402 + + +def md2html(source, dest): + import markdown + # Use extensions if defined. + mdExtensions = buildVars.markdownExtensions + lang = os.path.basename(os.path.dirname(source)).replace('_', '-') + localeLang = os.path.basename(os.path.dirname(source)) + try: + _ = gettext.translation("nvda", localedir=os.path.join("addon", "locale"), languages=[localeLang]).gettext + summary = _(buildVars.addon_info["addon_summary"]) + except Exception: + summary = buildVars.addon_info["addon_summary"] + title = "{addonSummary} {addonVersion}".format( + addonSummary=summary, addonVersion=buildVars.addon_info["addon_version"] + ) + headerDic = { + "[[!meta title=\"": "# ", + "\"]]": " #", + } + with codecs.open(source, "r", "utf-8") as f: + mdText = f.read() + for k, v in headerDic.items(): + mdText = mdText.replace(k, v, 1) + htmlText = markdown.markdown(mdText, extensions=mdExtensions) + # Optimization: build resulting HTML text in one go instead of writing parts separately. + docText = "\n".join([ + "", + "" % lang, + "", + "" + "", + "", + "%s" % title, + "\n", + htmlText, + "\n" + ]) + with codecs.open(dest, "w", "utf-8") as f: + f.write(docText) + + +def mdTool(env): + mdAction = env.Action( + lambda target, source, env: md2html(source[0].path, target[0].path), + lambda target, source, env: 'Generating % s' % target[0], + ) + mdBuilder = env.Builder( + action=mdAction, + suffix='.html', + src_suffix='.md', + ) + env['BUILDERS']['markdown'] = mdBuilder + + +vars = Variables() +vars.Add("version", "The version of this build", buildVars.addon_info["addon_version"]) +vars.Add(BoolVariable("dev", "Whether this is a daily development version", False)) +vars.Add("channel", "Update channel for this build", buildVars.addon_info["addon_updateChannel"]) + +env = Environment(variables=vars, ENV=os.environ, tools=['gettexttool', mdTool]) +env.Append(**buildVars.addon_info) + +if env["dev"]: + import datetime + buildDate = datetime.datetime.now() + year, month, day = str(buildDate.year), str(buildDate.month), str(buildDate.day) + env["addon_version"] = "".join([year, month.zfill(2), day.zfill(2), "-dev"]) + env["channel"] = "dev" +elif env["version"] is not None: + env["addon_version"] = env["version"] +if "channel" in env and env["channel"] is not None: + env["addon_updateChannel"] = env["channel"] + +buildVars.addon_info["addon_version"] = env["addon_version"] +buildVars.addon_info["addon_updateChannel"] = env["addon_updateChannel"] + +addonFile = env.File("${addon_name}-${addon_version}.nvda-addon") + + +def addonGenerator(target, source, env, for_signature): + action = env.Action( + lambda target, source, env: createAddonBundleFromPath(source[0].abspath, target[0].abspath) and None, + lambda target, source, env: "Generating Addon %s" % target[0] + ) + return action + + +def manifestGenerator(target, source, env, for_signature): + action = env.Action( + lambda target, source, env: generateManifest(source[0].abspath, target[0].abspath) and None, + lambda target, source, env: "Generating manifest %s" % target[0] + ) + return action + + +def translatedManifestGenerator(target, source, env, for_signature): + dir = os.path.abspath(os.path.join(os.path.dirname(str(source[0])), "..")) + lang = os.path.basename(dir) + action = env.Action( + lambda target, source, env: generateTranslatedManifest(source[1].abspath, lang, target[0].abspath) and None, + lambda target, source, env: "Generating translated manifest %s" % target[0] + ) + return action + + +env['BUILDERS']['NVDAAddon'] = Builder(generator=addonGenerator) +env['BUILDERS']['NVDAManifest'] = Builder(generator=manifestGenerator) +env['BUILDERS']['NVDATranslatedManifest'] = Builder(generator=translatedManifestGenerator) + + +def createAddonHelp(dir): + docsDir = os.path.join(dir, "doc") + if os.path.isfile("style.css"): + cssPath = os.path.join(docsDir, "style.css") + cssTarget = env.Command(cssPath, "style.css", Copy("$TARGET", "$SOURCE")) + env.Depends(addon, cssTarget) + if os.path.isfile("readme.md"): + readmePath = os.path.join(docsDir, buildVars.baseLanguage, "readme.md") + readmeTarget = env.Command(readmePath, "readme.md", Copy("$TARGET", "$SOURCE")) + env.Depends(addon, readmeTarget) + + +def createAddonBundleFromPath(path, dest): + """ Creates a bundle from a directory that contains an addon manifest file.""" + basedir = os.path.abspath(path) + with zipfile.ZipFile(dest, 'w', zipfile.ZIP_DEFLATED) as z: + # FIXME: the include/exclude feature may or may not be useful. Also python files can be pre-compiled. + for dir, dirnames, filenames in os.walk(basedir): + relativePath = os.path.relpath(dir, basedir) + for filename in filenames: + pathInBundle = os.path.join(relativePath, filename) + absPath = os.path.join(dir, filename) + if pathInBundle not in buildVars.excludedFiles: + z.write(absPath, pathInBundle) + return dest + + +def generateManifest(source, dest): + addon_info = buildVars.addon_info + with codecs.open(source, "r", "utf-8") as f: + manifest_template = f.read() + manifest = manifest_template.format(**addon_info) + with codecs.open(dest, "w", "utf-8") as f: + f.write(manifest) + + +def generateTranslatedManifest(source, language, out): + _ = gettext.translation("nvda", localedir=os.path.join("addon", "locale"), languages=[language]).gettext + vars = {} + for var in ("addon_summary", "addon_description"): + vars[var] = _(buildVars.addon_info[var]) + with codecs.open(source, "r", "utf-8") as f: + manifest_template = f.read() + result = manifest_template.format(**vars) + with codecs.open(out, "w", "utf-8") as f: + f.write(result) + + +def expandGlobs(files): + return [f for pattern in files for f in env.Glob(pattern)] + + +addon = env.NVDAAddon(addonFile, env.Dir('addon')) + +langDirs = [f for f in env.Glob(os.path.join("addon", "locale", "*"))] + +# Allow all NVDA's gettext po files to be compiled in source/locale, and manifest files to be generated +for dir in langDirs: + poFile = dir.File(os.path.join("LC_MESSAGES", "nvda.po")) + moFile = env.gettextMoFile(poFile) + env.Depends(moFile, poFile) + translatedManifest = env.NVDATranslatedManifest( + dir.File("manifest.ini"), + [moFile, os.path.join("manifest-translated.ini.tpl")] + ) + env.Depends(translatedManifest, ["buildVars.py"]) + env.Depends(addon, [translatedManifest, moFile]) + +pythonFiles = expandGlobs(buildVars.pythonSources) +for file in pythonFiles: + env.Depends(addon, file) + +# Convert markdown files to html +# We need at least doc in English and should enable the Help button for the add-on in Add-ons Manager +createAddonHelp("addon") +for mdFile in env.Glob(os.path.join('addon', 'doc', '*', '*.md')): + htmlFile = env.markdown(mdFile) + env.Depends(htmlFile, mdFile) + env.Depends(addon, htmlFile) + +# Pot target +i18nFiles = expandGlobs(buildVars.i18nSources) +gettextvars = { + 'gettext_package_bugs_address': 'nvda-translations@groups.io', + 'gettext_package_name': buildVars.addon_info['addon_name'], + 'gettext_package_version': buildVars.addon_info['addon_version'] +} + +pot = env.gettextPotFile("${addon_name}.pot", i18nFiles, **gettextvars) +env.Alias('pot', pot) +env.Depends(pot, i18nFiles) +mergePot = env.gettextMergePotFile("${addon_name}-merge.pot", i18nFiles, **gettextvars) +env.Alias('mergePot', mergePot) +env.Depends(mergePot, i18nFiles) + +# Generate Manifest path +manifest = env.NVDAManifest(os.path.join("addon", "manifest.ini"), os.path.join("manifest.ini.tpl")) +# Ensure manifest is rebuilt if buildVars is updated. +env.Depends(manifest, "buildVars.py") + +env.Depends(addon, manifest) +env.Default(addon) +env.Clean(addon, ['.sconsign.dblite', 'addon/doc/' + buildVars.baseLanguage + '/']) diff --git a/site_scons/site_tools/gettexttool/__init__.py b/site_scons/site_tools/gettexttool/__init__.py new file mode 100644 index 0000000..6583b81 --- /dev/null +++ b/site_scons/site_tools/gettexttool/__init__.py @@ -0,0 +1,55 @@ +""" This tool allows generation of gettext .mo compiled files, pot files from source code files +and pot files for merging. + +Three new builders are added into the constructed environment: + +- gettextMoFile: generates .mo file from .pot file using msgfmt. +- gettextPotFile: Generates .pot file from source code files. +- gettextMergePotFile: Creates a .pot file appropriate for merging into existing .po files. + +To properly configure get text, define the following variables: + +- gettext_package_bugs_address +- gettext_package_name +- gettext_package_version + + +""" +from SCons.Action import Action + + +def exists(env): + return True + + +XGETTEXT_COMMON_ARGS = ( + "--msgid-bugs-address='$gettext_package_bugs_address' " + "--package-name='$gettext_package_name' " + "--package-version='$gettext_package_version' " + "--keyword=pgettext:1c,2 " + "-c -o $TARGET $SOURCES" +) + + +def generate(env): + env.SetDefault(gettext_package_bugs_address="example@example.com") + env.SetDefault(gettext_package_name="") + env.SetDefault(gettext_package_version="") + + env['BUILDERS']['gettextMoFile'] = env.Builder( + action=Action("msgfmt -o $TARGET $SOURCE", "Compiling translation $SOURCE"), + suffix=".mo", + src_suffix=".po" + ) + + env['BUILDERS']['gettextPotFile'] = env.Builder( + action=Action("xgettext --no-location " + XGETTEXT_COMMON_ARGS, "Generating pot file $TARGET"), + suffix=".pot") + + env['BUILDERS']['gettextMergePotFile'] = env.Builder( + action=Action( + "xgettext " + "--omit-header --no-location " + XGETTEXT_COMMON_ARGS, + "Generating pot file $TARGET" + ), + suffix=".pot" + ) diff --git a/style.css b/style.css new file mode 100644 index 0000000..90f99d1 --- /dev/null +++ b/style.css @@ -0,0 +1,26 @@ +@charset "utf-8"; +body { +font-family : Verdana, Arial, Helvetica, Sans-serif; +line-height: 1.2em; +} +h1, h2 {text-align: center} +dt { +font-weight : bold; +float : left; +width: 10%; +clear: left +} +dd { +margin : 0 0 0.4em 0; +float : left; +width: 90%; +display: block; +} +p { clear : both; +} +a { text-decoration : underline; +} +:active { +text-decoration : none; +} +a:focus, a:hover {outline: solid}