1 new commit in RapidSettings: https://bitbucket.org/nvdaaddonteam/rapidsettings/commits/d3638bea60da/ Changeset: d3638bea60da Branch: master User: ABuffEr Date: 2015-03-17 14:01:05+00:00 Summary: Various bug fixes, updated readme and template Affected #: 10 files diff --git a/addon/globalPlugins/rapidSettings/__init__.py b/addon/globalPlugins/rapidSettings/__init__.py index 2ae647e..516bfdd 100644 --- a/addon/globalPlugins/rapidSettings/__init__.py +++ b/addon/globalPlugins/rapidSettings/__init__.py @@ -11,7 +11,7 @@ from msg import message as msg import queueHandler import ui from tones import beep -from logHandler import log +#from logHandler import log import addonHandler addonHandler.initTranslation() @@ -21,7 +21,7 @@ _addonSummary = _curAddon.manifest['summary'] _scriptCategory = unicode(_addonSummary) # the global variables to store current settings dialog instance -# and a application focused object before script_showSettingsTree execution +# and an application focused object before script_showSettingsTree execution ins = curAppObj = None class SettingsTree(wx.TreeCtrl): @@ -97,7 +97,7 @@ class SettingsTree(wx.TreeCtrl): # list of all wx controls in the dialog children = ins.GetChildren() # loop to prepare names for tree items, based on combobox, editbox, checkbox and slider - for n in range(0,len(children)): + for n in xrange(0,len(children)): child = children[n] name = None if child.IsEnabled(): @@ -107,13 +107,13 @@ class SettingsTree(wx.TreeCtrl): # the colon presence is very inconsistent sep = ' ' if child.GetName().endswith(':') else ': ' # concatenate name, separator and current value of combobox - name = sep.join([msg(child.GetName()), msg(child.GetLabel())]) + name = sep.join([msg(child.GetName()), msg(child.GetStringSelection())]) else: # ...but some other has "choice" as name, so we take the staticText label, the previous control sep = ' ' if children[n-1].GetLabel().endswith(':') else ': ' - name = sep.join([msg(children[n-1].GetLabel()), msg(child.GetLabel())]) + name = sep.join([msg(children[n-1].GetLabel()), msg(child.GetStringSelection())]) elif child.ClassName == u'wxTextCtrl': - name = ': '.join([msg(children[n-1].GetLabel()), child.GetLabel()]) + name = ': '.join([msg(children[n-1].GetLabel()), child.GetValue()]) elif child.ClassName == u'wxCheckBox': name = ': '.join([msg(child.GetLabel()), msg("on" if child.GetValue() else "off")]) elif child.ClassName == u'wxSlider': @@ -325,8 +325,16 @@ class SettingsTreeDialog(SettingsDialog): self.originalTriggersValue = self.modifiedTriggersValue = config.conf.profileTriggersEnabled # hide controls associated with tree items self.disableItems() + # bind onEscape on all items, to avoid problems pressing escape + for item in self.GetChildren(): + item.Bind(wx.EVT_KEY_UP, self.onEscape) + # remember ok and cancel button +# self.Bind(wx.EVT_KEY_UP, self.onEscape, id=wx.ID_OK) +# self.Bind(wx.EVT_KEY_UP, self.onEscape, id=wx.ID_CANCEL) # trace modified settings self.changedItems = [] + # useful to fix problem closing context menu in onCancel + self.contextMenu = None def listProfiles(self): """Build the list for profiles combo""" @@ -376,33 +384,33 @@ class SettingsTreeDialog(SettingsDialog): def viewPopupMenu(self, event): """Create context menu, calling preparePopupMenu, and view it""" - popupMenu = self.preparePopupMenu() - self.PopupMenu(popupMenu) + self.contextMenu = self.preparePopupMenu() + self.PopupMenu(self.contextMenu) def preparePopupMenu(self): """Prepare context menu items according to current profile""" popupMenu = wx.Menu() toggleManualProfileMI = popupMenu.Append(wx.NewId(), msg("Manual activate")) - self.Bind(wx.EVT_MENU, self.toggleManualProfile, toggleManualProfileMI) + popupMenu.Bind(wx.EVT_MENU, self.toggleManualProfile, toggleManualProfileMI) newProfileMI = popupMenu.Append(wx.NewId(), msg("&New", amp=True)) - self.Bind(wx.EVT_MENU, self.newProfile, newProfileMI) + popupMenu.Bind(wx.EVT_MENU, self.newProfile, newProfileMI) renameProfileMI = popupMenu.Append(wx.NewId(), msg("&Rename", amp=True)) - self.Bind(wx.EVT_MENU, self.renameProfile, renameProfileMI) + popupMenu.Bind(wx.EVT_MENU, self.renameProfile, renameProfileMI) deleteProfileMI = popupMenu.Append(wx.NewId(), msg("&Delete", amp=True)) - self.Bind(wx.EVT_MENU, self.deleteProfile, deleteProfileMI) + popupMenu.Bind(wx.EVT_MENU, self.deleteProfile, deleteProfileMI) toggleTriggersMI = popupMenu.AppendCheckItem(wx.NewId(), msg("Temporarily d&isable all triggers", amp=True)) toggleTriggersMI.Check(not self.modifiedTriggersValue) - self.Bind(wx.EVT_MENU, self.toggleTriggers, toggleTriggersMI) - # FIXME: escape should close menu, but close dialog - # Translators: a label of menu item to close menu itself - closeMenuMI = popupMenu.Append(wx.NewId(), _("&Close menu")) - self.Bind(wx.EVT_MENU, self.closePopupMenu, closeMenuMI) - curProfile = self.profileCombo.GetLabel() + popupMenu.Bind(wx.EVT_MENU, self.toggleTriggers, toggleTriggersMI) + curProfile = self.profileCombo.GetStringSelection() if curProfile == msg("(normal configuration)"): + toggleManualProfileMI.Enable(False) renameProfileMI.Enable(False) deleteProfileMI.Enable(False) else: - (cName, cStates) = curProfile.rsplit(' (', 1) + try: + cName, cStates = curProfile.rsplit(' (', 1) + except: + cName = curProfile.rsplit(' (', 1) # if yes, it is manual or triggered if self.originalProfile != msg("(normal configuration)"): (oName, oStates) = self.originalProfile.rsplit(' (', 1) @@ -412,7 +420,7 @@ class SettingsTreeDialog(SettingsDialog): # or it is normal configuration, and currently selected profile was already # candidate to be manually activated, so now it can be only deactivated elif cName == self.keepProfile: - toggleManualProfileMI.SetText(msg("Manual deactivate")) + toggleManualProfileMI.SetText(msg("Manual deactivate")) return popupMenu def postInit(self): @@ -475,6 +483,11 @@ class SettingsTreeDialog(SettingsDialog): self.slider.Bind(wx.EVT_COMMAND_SCROLL, lambda event, item=slider: self.onItemChange(event, item)) self.slider.Enable() + def onEscape(self, event): + if event.GetKeyCode() == wx.WXK_ESCAPE: + self.onCancel(event) +# event.StopPropagation() + def onInputSearch(self, event): """Process input in search field, call search method on the tree""" input = self.search.GetValue() @@ -500,7 +513,7 @@ class SettingsTreeDialog(SettingsDialog): # useful for language and variant comboboxes in voice section item.GetEventHandler().ProcessEvent(event) # request a update to relative tree item - self.tree.updateLabel(self.combo.GetLabel()) + self.tree.updateLabel(self.combo.GetStringSelection()) elif item.ClassName == u'wxTextCtrl': if self.edit.GetValue() == self.editOriginalValue: self.changedItems.remove(item) @@ -607,7 +620,7 @@ class SettingsTreeDialog(SettingsDialog): def toggleManualProfile(self, event): """Indicate which profile must be kept manually active after dialog closing""" toggleManualProfileMI = event.GetEventObject().FindItemById(event.GetId()) - name = self.profileCombo.GetLabel().rsplit(' (', 1)[0] + name = self.profileCombo.GetStringSelection().rsplit(' (', 1)[0] if toggleManualProfileMI.GetText() == msg("Manual activate"): self.keepProfile = name else: @@ -618,11 +631,11 @@ class SettingsTreeDialog(SettingsDialog): """Show new profile creation dialog""" # collapse and request to eventually save changes self.tree.CollapseAll() + self.onOk(wx.EVT_BUTTON) profDlg = self.getProfileDialog() NewProfileDialog(profDlg).Show() # "destroy" ProfilesDialog instance, for coherence reasons ProfilesDialog._instance = None - self.onOk(wx.EVT_BUTTON) def renameProfile(self, event): """Show profile renaming dialog""" @@ -646,10 +659,12 @@ class SettingsTreeDialog(SettingsDialog): profDlg.onDelete(wx.EVT_BUTTON) # "destroy" ProfilesDialog instance, for coherence reasons ProfilesDialog._instance = None - # update dialog title and profileCombo + # update dialog title, tree and profileCombo + self.tree.find = '' + self.tree.initSections(self.tree.prepareClassList()) + self.updateProfileCombo() self.title = self.prepareTitle() self.SetTitle(self.title) - self.updateProfileCombo() def toggleTriggers(self, event): """Indicate if triggers must be kept activated or deactivated after dialog closing""" @@ -673,11 +688,6 @@ class SettingsTreeDialog(SettingsDialog): # Temporary solution part 2: close dialog self.onOk(wx.EVT_BUTTON) - def closePopupMenu(self, event): - """Close context menu""" - # see FIXME comment in preparePopupMenu - event.GetEventObject().Destroy() - def onOk(self, event): """Call onOk(event) on phantom dialog instance, if this exists, and the same on superclass, plus many various actions""" if ins is not None and "wxPyDeadObject" not in str(ins.__class__): @@ -687,7 +697,12 @@ class SettingsTreeDialog(SettingsDialog): # disable an eventual profile manually activated (to modify) config.conf.manualActivateProfile(None) else: - (name, states) = self.originalProfile.rsplit(' (', 1) + try: + (name, states) = self.originalProfile.rsplit(' (', 1) + # changing NVDA language and pressing return, we have only name (normal configuration), so: + except: + name = self.originalProfile.rsplit(' (', 1) + states = [] # if initial profile is manual, otherwise if msg("manual") in states: try: @@ -719,6 +734,10 @@ class SettingsTreeDialog(SettingsDialog): def onCancel(self, event): """Call onCancel(event) on phantom dialog instance, if this exists, and the same on superclass""" + if self.contextMenu != None and "wxPyDeadObject" not in str(self.contextMenu.__class__): + self.contextMenu.Destroy() + self.profileCombo.SetFocus() + return if ins is not None and "wxPyDeadObject" not in str(ins.__class__): ins.onCancel(event) # if triggers is originally enabled @@ -741,7 +760,7 @@ class SettingsTreeDialog(SettingsDialog): # simply manually deactivate an eventual profile manually activated (to modify it) config.conf.manualActivateProfile(None) # if initial is normal configuration and is not currently selected profile in profileCombo - elif self.originalProfile != self.profileCombo.GetLabel(): + elif self.originalProfile != self.profileCombo.GetStringSelection(): # disable an eventual profile manually activated (to modify) config.conf.manualActivateProfile(None) super(SettingsTreeDialog, self).onCancel(event) diff --git a/addon/locale/it/LC_MESSAGES/nvda.po b/addon/locale/it/LC_MESSAGES/nvda.po index 66eaa42..3471a9a 100644 --- a/addon/locale/it/LC_MESSAGES/nvda.po +++ b/addon/locale/it/LC_MESSAGES/nvda.po @@ -7,15 +7,15 @@ msgid "" msgstr "" "Project-Id-Version: 'rapidSettings' '2.0dev'\n" "Report-Msgid-Bugs-To: 'nvda-translations@xxxxxxxxxxxxx'\n" -"POT-Creation-Date: 2014-03-15 14:47+0100\n" -"PO-Revision-Date: 2014-03-15 14:49+0100\n" +"POT-Creation-Date: 2015-03-17 14:55+0100\n" +"PO-Revision-Date: 2015-03-17 14:56+0100\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: Alberto Buffolino <a.buffolino@xxxxxxxxx>\n" "Language: it\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Generator: Poedit 1.6.4\n" +"X-Generator: Poedit 1.6.5\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Poedit-Basepath: .\n" @@ -41,20 +41,14 @@ msgstr "" "La finestra di dialogo profili di configurazione è aperta. Prima chiudila, " "per favore." -#. FIXME: escape should close menu, but close dialog -#. Translators: a label of menu item to close menu itself -#: addon\globalPlugins\rapidSettings\__init__.py:398 -msgid "&Close menu" -msgstr "&Chiudi menu" - #. Translators: the message for save changes -#: addon\globalPlugins\rapidSettings\__init__.py:758 +#: addon\globalPlugins\rapidSettings\__init__.py:777 #, python-brace-format msgid "Do you want to save changes for {section} section?" msgstr "Vuoi salvare i cambiamenti per la sezione {section}?" #. Translators: the description for the settings tree script. -#: addon\globalPlugins\rapidSettings\__init__.py:784 +#: addon\globalPlugins\rapidSettings\__init__.py:803 msgid "" "Presents a tree of all settings sections, which you can expand to modify all " "NVDA options" @@ -76,3 +70,6 @@ msgid "" msgstr "" "Permette di accedere a tutte le impostazioni in una singola finestra, e " "ricercare fra di esse." + +#~ msgid "&Close menu" +#~ msgstr "&Chiudi menu" diff --git a/buildVars.py b/buildVars.py index 19eb7c5..77ff8d7 100644 --- a/buildVars.py +++ b/buildVars.py @@ -11,19 +11,21 @@ addon_info = { # for previously unpublished addons, please follow the community guidelines at: # https://bitbucket.org/nvdaaddonteam/todo/raw/master/guideLines.txt # add-on Name, internal for nvda - "addon-name" : "rapidSettings", + "addon_name" : "rapidSettings", # 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. - "addon-summary" : _("Rapid Settings"), + "addon_summary" : _("Rapid Settings"), # Add-on description # Translators: Long description to be shown for this add-on on add-on information from add-ons manager - "addon-description" : _("""Lets you to access to all settings in a single window, and search among them."""), + "addon_description" : _("""Lets you to access to all settings in a single window, and search among them."""), # version - "addon-version" : "2.0dev", + "addon_version" : "2.0dev", # Author(s) - "addon-author" : u"Alberto Buffolino <a.buffolino@xxxxxxxxx>", + "addon_author" : u"Alberto Buffolino <a.buffolino@xxxxxxxxx>", # URL for the add-on documentation support - "addon-url" : None + "addon_url" : None, + # Documentation file name + "addon_docFileName" : "readme.html", } @@ -34,7 +36,7 @@ import os.path pythonSources = [os.path.join("addon", "globalPlugins", "rapidSettings", "*.py")] # Files that contain strings for translation. Usually your python sources -i18nSources = pythonSources + ["buildVars.py", "docHandler.py"] +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. diff --git a/docHandler.py b/docHandler.py deleted file mode 100644 index b514fe4..0000000 --- a/docHandler.py +++ /dev/null @@ -1,68 +0,0 @@ -# -*- coding: UTF-8 -*- - -# docHandler: module for managing addons documentation -# See: http://community.nvda-project.org/ticket/2694 - -import os -import languageHandler -import addonHandler -import globalPluginHandler -import gui -import wx - -addonHandler.initTranslation() - -_addonDir = os.path.join(os.path.dirname(__file__), "..").decode("mbcs") # The root of an addon folder -_docFileName = "readme.html" # The name of an addon documentation file -_curAddon = addonHandler.Addon(_addonDir) # Addon instance -_addonSummary = _curAddon.manifest['summary'] -_addonVersion = _curAddon.manifest['version'] -_addonName = _curAddon.manifest['name'] - -def getDocFolder(addonDir=_addonDir): - langs = [languageHandler.getLanguage(), "en"] - for lang in langs: - docFolder = os.path.join(addonDir, "doc", lang) - if os.path.isdir(docFolder): - return docFolder - if "_" in lang: - tryLang = lang.split("_")[0] - docFolder = os.path.join(addonDir, "doc", tryLang) - if os.path.isdir(docFolder): - return docFolder - if tryLang == "en": - break - if lang == "en": - break - return None - -def getDocPath(docFileName=_docFileName): - docPath = getDocFolder() - if docPath is not None: - docFile = os.path.join(docPath, docFileName) - if os.path.isfile(docFile): - docPath = docFile - return docPath - -def openDocPath(): - try: - os.startfile(getDocPath()) - except WindowsError: - pass - -class GlobalPlugin(globalPluginHandler.GlobalPlugin): - - def __init__(self): - super(globalPluginHandler.GlobalPlugin, self).__init__() - self.help = gui.mainFrame.sysTrayIcon.helpMenu - self.helpItem = self.help.Append(wx.ID_ANY, u"{summary} {version}".format(summary=_addonSummary, version=_addonVersion), _addonName) - gui.mainFrame.sysTrayIcon.Bind(wx.EVT_MENU, self.onHelp, self.helpItem) - - def onHelp(self, evt): - openDocPath() - - def terminate(self): - try: - self.help.RemoveItem(self.helpItem) - except wx.PyDeadObjectError: - pass diff --git a/manifest-translated.ini.tpl b/manifest-translated.ini.tpl index 4750635..c06aa84 100644 --- a/manifest-translated.ini.tpl +++ b/manifest-translated.ini.tpl @@ -1,2 +1,2 @@ -summary = "{addon-summary}" -description = """{addon-description}""" +summary = "{addon_summary}" +description = """{addon_description}""" diff --git a/manifest.ini.tpl b/manifest.ini.tpl index 8093a3f..7de43bf 100644 --- a/manifest.ini.tpl +++ b/manifest.ini.tpl @@ -1,6 +1,7 @@ -name = {addon-name} -summary = "{addon-summary}" -description = """{addon-description}""" -author = "{addon-author}" -url = {addon-url} -version = {addon-version} +name = {addon_name} +summary = "{addon_summary}" +description = """{addon_description}""" +author = "{addon_author}" +url = {addon_url} +version = {addon_version} +docFileName = {addon_docFileName} diff --git a/readme.md b/readme.md index 96577b7..e4a69f5 100644 --- a/readme.md +++ b/readme.md @@ -25,6 +25,7 @@ You can also search in all settings, using the specific field before the tree; t ## Changes for 2.0 ## * Profile management implementation. +* Various bug fixes. ## Changes for 1.0 ## @@ -33,11 +34,8 @@ You can also search in all settings, using the specific field before the tree; t ### Bugs or problems still present ### * Braille remains on a blank line when search field it cleaned automatically. -* If you press escape on a combobox, editbox, radio button or slider, the event is not processed by main dialog, generating errors. -* If you press escape on context menu, dialog is closed (not context menu). * It's not possible to use return on a tree item to simulate click on ok button (see code for details). * Enable or disable triggers from context menu requires to close dialog for coherence reasons (see code for details). * When you select a Braille display that requires port selection, the port combobox is not shown immediately. -* If you get an error in Braille display selection (display not found), after pressing ok the original dialog appears. [1]: http://addons.nvda-project.org/files/get.php?file=rs-dev \ No newline at end of file diff --git a/sconstruct b/sconstruct index 1d54d6e..b77cfe6 100644 --- a/sconstruct +++ b/sconstruct @@ -1,5 +1,5 @@ # NVDA add-on template SCONSTRUCT file -#Copyright (C) 2012 Rui Batista <ruiandrebatista@xxxxxxxxx> +#Copyright (C) 2012, 2014 Rui Batista <ruiandrebatista@xxxxxxxxx> #This file is covered by the GNU General Public License. #See the file COPYING.txt for more details. @@ -8,7 +8,6 @@ import gettext import os import os.path import zipfile -import configobj import buildVars @@ -16,7 +15,7 @@ import buildVars def md2html(source, dest): import markdown lang = os.path.basename(os.path.dirname(source)).replace('_', '-') - title="{addonSummary} {addonVersion}".format(addonSummary=buildVars.addon_info["addon-summary"], addonVersion=buildVars.addon_info["addon-version"]) + title="{addonSummary} {addonVersion}".format(addonSummary=buildVars.addon_info["addon_summary"], addonVersion=buildVars.addon_info["addon_version"]) headerDic = { "[[!meta title=\"": "# ", "\"]]": " #", @@ -52,10 +51,11 @@ def mdTool(env): ) env['BUILDERS']['markdown']=mdBuilder -env = Environment(ENV=os.environ, tools=[mdTool]) +env = Environment(ENV=os.environ, tools=['gettexttool', mdTool]) +env.Append(**buildVars.addon_info) -addonFile = env.File("{addon-name}-{addon-version}.nvda-addon".format(**buildVars.addon_info)) +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, @@ -79,31 +79,7 @@ env['BUILDERS']['NVDAAddon'] = Builder(generator=addonGenerator) env['BUILDERS']['NVDAManifest'] = Builder(generator=manifestGenerator) env['BUILDERS']['NVDATranslatedManifest'] = Builder(generator=translatedManifestGenerator) -env['BUILDERS']['gettextMoFile']=env.Builder( - action=env.Action(["msgfmt -o $TARGETS $SOURCES"], lambda t, s, e : "Compiling translation %s" % s[0]), - suffix=".mo", - src_suffix=".po" -) - -env['BUILDERS']['gettextPotFile']=env.Builder( - action=env.Action(["xgettext --msgid-bugs-address='%s' --package-name='%s' --package-version='%s' -c -o $TARGETS $SOURCES" % - ("nvda-translations@xxxxxxxxxxxxx", buildVars.addon_info['addon-name'], buildVars.addon_info['addon-version']) - ], lambda t, s, e : "Generating pot file %s" % t[0]), - suffix=".pot") -env['BUILDERS']['gettextMergePotFile']=env.Builder( - action=env.Action(["xgettext --msgid-bugs-address='%s' --package-name='%s' --package-version='%s' --omit-header --no-location -c -o $TARGETS $SOURCES" % - ("nvda-translations@xxxxxxxxxxxxx", buildVars.addon_info['addon-name'], buildVars.addon_info['addon-version']) - ], lambda t, s, e : "Generating pot file %s" % t[0]), - suffix=".pot") - def createAddonHelp(dir): - if not os.path.isfile("docHandler.py"): - return - plugindir = os.path.join(dir, "globalPlugins") - docFilename = "{addonName}_docHandler.py".format(addonName=buildVars.addon_info["addon-name"]) - docPath = os.path.join(plugindir, docFilename) - docFileTarget = env.Command(docPath, "docHandler.py", Copy("$TARGET", "$SOURCE")) - env.Depends(addon, docFileTarget) docsDir = os.path.join(dir, "doc") if os.path.isfile("style.css"): cssPath = os.path.join(docsDir, "style.css") @@ -139,7 +115,7 @@ def generateManifest(source, dest): def generateTranslatedManifest(source, language, out): _ = gettext.translation("nvda", localedir=os.path.join("addon", "locale"), languages=[language]).ugettext vars = {} - for var in ("addon-summary", "addon-description"): + 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() @@ -168,7 +144,7 @@ for file in pythonFiles: env.Depends(addon, file) #Convert markdown files to html -createAddonHelp("addon") # We need at least doc in English and should append an item to Help menu +createAddonHelp("addon") # We need at least doc in English and should enable the Help button for the add-on in Add-ons Manager for mdFile in env.Glob(os.path.join('addon', 'doc', '*', '*.md')): htmlFile = env.markdown(mdFile) env.Depends(htmlFile, mdFile) @@ -176,10 +152,16 @@ for mdFile in env.Glob(os.path.join('addon', 'doc', '*', '*.md')): # Pot target i18nFiles = expandGlobs(buildVars.i18nSources) -pot = env.gettextPotFile("%s.pot" % "{addon-name}".format(**buildVars.addon_info), i18nFiles) +gettextvars={ + 'gettext_package_bugs_address' : 'nvda-translations@xxxxxxxxxxxxx', + '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("%s-merge.pot" % "{addon-name}".format(**buildVars.addon_info), i18nFiles) +mergePot = env.gettextMergePotFile("${addon_name}-merge.pot", i18nFiles, **gettextvars) env.Alias('mergePot', mergePot) env.Depends(mergePot, i18nFiles) diff --git a/sconstruct.bak b/sconstruct.bak new file mode 100644 index 0000000..1d54d6e --- /dev/null +++ b/sconstruct.bak @@ -0,0 +1,190 @@ +# NVDA add-on template SCONSTRUCT file +#Copyright (C) 2012 Rui Batista <ruiandrebatista@xxxxxxxxx> +#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 configobj + +import buildVars + + +def md2html(source, dest): + import markdown + lang = os.path.basename(os.path.dirname(source)).replace('_', '-') + title="{addonSummary} {addonVersion}".format(addonSummary=buildVars.addon_info["addon-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.iteritems(): + mdText = mdText.replace(k, v, 1) + htmlText = markdown.markdown(mdText) + with codecs.open(dest, "w", "utf-8") as f: + f.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + + "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"\n" + + " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\";>\n" + + "<html xmlns=\"http://www.w3.org/1999/xhtml\"; xml:lang=\"%s\" lang=\"%s\">\n" % (lang, lang) + + "<head>\n" + + "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"/>\n" + + "<link rel=\"stylesheet\" type=\"text/css\" href=\"../style.css\" media=\"screen\"/>\n" + + "<title>%s</title>\n" % title + + "</head>\n<body>\n" + ) + f.write(htmlText) + f.write("\n</body>\n</html>") + +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 + +env = Environment(ENV=os.environ, tools=[mdTool]) + + +addonFile = env.File("{addon-name}-{addon-version}.nvda-addon".format(**buildVars.addon_info)) + +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) + +env['BUILDERS']['gettextMoFile']=env.Builder( + action=env.Action(["msgfmt -o $TARGETS $SOURCES"], lambda t, s, e : "Compiling translation %s" % s[0]), + suffix=".mo", + src_suffix=".po" +) + +env['BUILDERS']['gettextPotFile']=env.Builder( + action=env.Action(["xgettext --msgid-bugs-address='%s' --package-name='%s' --package-version='%s' -c -o $TARGETS $SOURCES" % + ("nvda-translations@xxxxxxxxxxxxx", buildVars.addon_info['addon-name'], buildVars.addon_info['addon-version']) + ], lambda t, s, e : "Generating pot file %s" % t[0]), + suffix=".pot") +env['BUILDERS']['gettextMergePotFile']=env.Builder( + action=env.Action(["xgettext --msgid-bugs-address='%s' --package-name='%s' --package-version='%s' --omit-header --no-location -c -o $TARGETS $SOURCES" % + ("nvda-translations@xxxxxxxxxxxxx", buildVars.addon_info['addon-name'], buildVars.addon_info['addon-version']) + ], lambda t, s, e : "Generating pot file %s" % t[0]), + suffix=".pot") + +def createAddonHelp(dir): + if not os.path.isfile("docHandler.py"): + return + plugindir = os.path.join(dir, "globalPlugins") + docFilename = "{addonName}_docHandler.py".format(addonName=buildVars.addon_info["addon-name"]) + docPath = os.path.join(plugindir, docFilename) + docFileTarget = env.Command(docPath, "docHandler.py", Copy("$TARGET", "$SOURCE")) + env.Depends(addon, docFileTarget) + 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, "en", "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): + with codecs.open(source, "r", "utf-8") as f: + manifest_template = f.read() + manifest = manifest_template.format(**buildVars.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]).ugettext + 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 +createAddonHelp("addon") # We need at least doc in English and should append an item to Help menu +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) +pot = env.gettextPotFile("%s.pot" % "{addon-name}".format(**buildVars.addon_info), i18nFiles) +env.Alias('pot', pot) +env.Depends(pot, i18nFiles) +mergePot = env.gettextMergePotFile("%s-merge.pot" % "{addon-name}".format(**buildVars.addon_info), i18nFiles) +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")) + +env.Depends(addon, manifest) +env.Default(addon) diff --git a/site_scons/site_tools/gettexttool/__init__.py b/site_scons/site_tools/gettexttool/__init__.py new file mode 100644 index 0000000..fa3a937 --- /dev/null +++ b/site_scons/site_tools/gettexttool/__init__.py @@ -0,0 +1,49 @@ +""" 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' " + "-c -o $TARGET $SOURCES" +) + +def generate(env): + env.SetDefault(gettext_package_bugs_address="example@xxxxxxxxxxx") + 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 " + 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") + Repository URL: https://bitbucket.org/nvdaaddonteam/rapidsettings/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email.