commit/nvda: 86 new changesets

  • From: commits-noreply@xxxxxxxxxxxxx
  • To: nvda-addons-commits@xxxxxxxxxxxxx
  • Date: Mon, 16 Sep 2013 00:00:58 -0000

86 new commits in nvda:

https://bitbucket.org/nvdaaddonteam/nvda/commits/c98095098c80/
Changeset:   c98095098c80
Branch:      None
User:        jteh
Date:        2013-08-22 14:53:24
Summary:     Initial code for retrieving info about all bound gestures based on 
the current focus. This is far from complete.

Affected #:  1 file

diff --git a/source/inputCore.py b/source/inputCore.py
index 9feaf8f..bd0aa83 100644
--- a/source/inputCore.py
+++ b/source/inputCore.py
@@ -27,6 +27,9 @@ import globalVars
 import languageHandler
 import controlTypes
 
+SCRCAT_KBEMU = _("Emulated system keyboard keys")
+SCRCAT_MISC = _("Miscellaneous")
+
 class NoInputGestureAction(LookupError):
        """Informs that there is no action to execute for a gesture.
        """
@@ -249,6 +252,16 @@ class GlobalGestureMap(object):
                                continue
                        yield cls, scriptName
 
+       def getScriptsForAllGestures(self):
+               """Get all of the scripts and their gestures.
+               @return: The Python class, gesture and script name for each 
mapping;
+                       the script name may be C{None} indicating that the 
gesture should be unbound for this class.
+               @rtype: generator of (class, str, str)
+               """
+               for gesture in self._map:
+                       for cls, scriptName in 
self.getScriptsForGesture(gesture):
+                               yield cls, gesture, scriptName
+
 class InputManager(baseObject.AutoPropertyObject):
        """Manages functionality related to input from the user.
        Input includes key presses on the keyboard, as well as key presses on 
Braille displays, etc.
@@ -379,6 +392,131 @@ class InputManager(baseObject.AutoPropertyObject):
                except NotImplementedError:
                        pass
 
+       def getAllGestureMappings(self, obj=None):
+               if not obj:
+                       obj = api.getFocusObject()
+               return _AllGestureMappingsRetriever(obj).results
+
+class _AllGestureMappingsRetriever(object):
+
+       def __init__(self, obj):
+               self.results = {}
+               self.scriptInfo = {}
+               self.handledGestures = set()
+
+               self.addGlobalMap(manager.userGestureMap, byUser=True)
+               self.addGlobalMap(manager.localeGestureMap)
+               import braille
+               gmap = braille.handler.display.gestureMap
+               if gmap:
+                       self.addGlobalMap(gmap)
+
+               # Global plugins.
+               import globalPluginHandler
+               for plugin in globalPluginHandler.runningPlugins:
+                       self.addObjBindings(plugin)
+
+               # App module.
+               app = obj.appModule
+               if app:
+                       self.addObjBindings(app)
+
+               # Tree interceptor.
+               ti = obj.treeInterceptor
+               if ti:
+                       self.addObjBindings(ti)
+
+               # NVDAObject.
+               self.addObjBindings(obj)
+
+               # Global commands.
+               import globalCommands
+               self.addObjBindings(globalCommands.commands)
+
+       def addResult(self, scriptInfo):
+               self.scriptInfo[scriptInfo.cls, scriptInfo.scriptName] = 
scriptInfo
+               try:
+                       cat = self.results[scriptInfo.category]
+               except KeyError:
+                       cat = self.results[scriptInfo.category] = {}
+               cat[scriptInfo.displayName] = scriptInfo
+
+       def addGlobalMap(self, gmap, byUser=False):
+               for cls, gesture, scriptName in gmap.getScriptsForAllGestures():
+                       key = (cls, gesture)
+                       if key in self.handledGestures:
+                               continue
+                       self.handledGestures.add(key)
+                       if scriptName is None:
+                               # The global map specified that no script 
should execute for this gesture and object.
+                               return
+                       try:
+                               scriptInfo = self.scriptInfo[cls, scriptName]
+                       except KeyError:
+                               if scriptName.startswith("kb:"):
+                                       scriptInfo = 
self.makeKbEmuScriptInfo(cls, scriptName)
+                               else:
+                                       try:
+                                               script = getattr(cls, 
"script_%s" % scriptName)
+                                       except AttributeError:
+                                               continue
+                                       scriptInfo = 
self.makeNormalScriptInfo(cls, scriptName, script)
+                                       if not scriptInfo:
+                                               continue
+                               self.addResult(scriptInfo)
+                       scriptInfo.gestures.append((gesture, byUser))
+
+       def makeKbEmuScriptInfo(self, cls, scriptName):
+               info = AllGesturesScriptInfo(cls, scriptName)
+               info.category = SCRCAT_KBEMU
+               info.displayName = scriptName[3:]
+               return info
+
+       def makeNormalScriptInfo(self, cls, scriptName, script):
+               info = AllGesturesScriptInfo(cls, scriptName)
+               info.category = self.getScriptCategory(cls, script)
+               try:
+                       info.displayName = script.__doc__
+               except AttributeError:
+                       return None
+               return info
+
+       def getScriptCategory(self, cls, script):
+               try:
+                       return script.category
+               except AttributeError:
+                       pass
+               try:
+                       return cls.scriptCategory
+               except AttributeError:
+                       pass
+               return SCRCAT_MISC
+
+       def addObjBindings(self, obj):
+               for gesture, script in obj._gestureMap.iteritems():
+                       scriptName = script.__name__[7:]
+                       cls = self.getScriptCls(script)
+                       try:
+                               scriptInfo = self.scriptInfo[cls, scriptName]
+                       except KeyError:
+                               scriptInfo = self.makeNormalScriptInfo(cls, 
scriptName, script)
+                               self.addResult(scriptInfo)
+                       scriptInfo.gestures.append((gesture, False))
+
+       def getScriptCls(self, script):
+               name = script.__name__
+               for cls in script.im_class.__mro__:
+                       if name in cls.__dict__:
+                               return cls
+
+class AllGesturesScriptInfo(object):
+       __slots__ = ("cls", "scriptName", "category", "displayName", "gestures")
+       
+       def __init__(self, cls, scriptName):
+               self.cls = cls
+               self.scriptName = scriptName
+               self.gestures = []
+
 def normalizeGestureIdentifier(identifier):
        """Normalize a gesture identifier so that it matches other identifiers 
for the same gesture.
        Any items separated by a + sign after the source are considered to be 
of indeterminate order


https://bitbucket.org/nvdaaddonteam/nvda/commits/949f71d815df/
Changeset:   949f71d815df
Branch:      None
User:        jteh
Date:        2013-08-22 15:07:33
Summary:     Fix typo.

Affected #:  1 file

diff --git a/source/inputCore.py b/source/inputCore.py
index bd0aa83..33b85e3 100644
--- a/source/inputCore.py
+++ b/source/inputCore.py
@@ -449,7 +449,7 @@ class _AllGestureMappingsRetriever(object):
                        self.handledGestures.add(key)
                        if scriptName is None:
                                # The global map specified that no script 
should execute for this gesture and object.
-                               return
+                               continue
                        try:
                                scriptInfo = self.scriptInfo[cls, scriptName]
                        except KeyError:


https://bitbucket.org/nvdaaddonteam/nvda/commits/dfe6400934a1/
Changeset:   dfe6400934a1
Branch:      None
User:        jteh
Date:        2013-08-23 03:18:30
Summary:     Add translator comments and code doc for script categories.

Affected #:  1 file

diff --git a/source/inputCore.py b/source/inputCore.py
index 33b85e3..0422486 100644
--- a/source/inputCore.py
+++ b/source/inputCore.py
@@ -27,7 +27,11 @@ import globalVars
 import languageHandler
 import controlTypes
 
+#: Script category for emulated keyboard keys.
+# Translators: The name of a category of NVDA commands.
 SCRCAT_KBEMU = _("Emulated system keyboard keys")
+#: Script category for miscellaneous commands.
+# Translators: The name of a category of NVDA commands.
 SCRCAT_MISC = _("Miscellaneous")
 
 class NoInputGestureAction(LookupError):


https://bitbucket.org/nvdaaddonteam/nvda/commits/f9dbb751407b/
Changeset:   f9dbb751407b
Branch:      None
User:        jteh
Date:        2013-08-23 03:19:28
Summary:     getAllGestureMappings: Fix issues related to scripts with no 
display name.

Affected #:  1 file

diff --git a/source/inputCore.py b/source/inputCore.py
index 0422486..5fc6c13 100644
--- a/source/inputCore.py
+++ b/source/inputCore.py
@@ -479,9 +479,8 @@ class _AllGestureMappingsRetriever(object):
        def makeNormalScriptInfo(self, cls, scriptName, script):
                info = AllGesturesScriptInfo(cls, scriptName)
                info.category = self.getScriptCategory(cls, script)
-               try:
-                       info.displayName = script.__doc__
-               except AttributeError:
+               info.displayName = script.__doc__
+               if not info.displayName:
                        return None
                return info
 
@@ -504,6 +503,8 @@ class _AllGestureMappingsRetriever(object):
                                scriptInfo = self.scriptInfo[cls, scriptName]
                        except KeyError:
                                scriptInfo = self.makeNormalScriptInfo(cls, 
scriptName, script)
+                               if not scriptInfo:
+                                       continue
                                self.addResult(scriptInfo)
                        scriptInfo.gestures.append((gesture, False))
 


https://bitbucket.org/nvdaaddonteam/nvda/commits/8470bd46768a/
Changeset:   8470bd46768a
Branch:      None
User:        jteh
Date:        2013-08-23 03:21:40
Summary:     Initial work on a GUI for input gestures. Right now, it just 
displays the commands in a tree control.

Affected #:  1 file

diff --git a/source/gui/settingsDialogs.py b/source/gui/settingsDialogs.py
index 28d89ea..83530d0 100644
--- a/source/gui/settingsDialogs.py
+++ b/source/gui/settingsDialogs.py
@@ -30,6 +30,7 @@ try:
        import updateCheck
 except RuntimeError:
        updateCheck = None
+import inputCore
 
 class SettingsDialog(wx.Dialog):
        """A settings dialog.
@@ -1510,3 +1511,22 @@ class SpeechSymbolsDialog(SettingsDialog):
                        log.error("Error saving user symbols info: %s" % e)
                
characterProcessing._localeSpeechSymbolProcessors.invalidateLocaleData(self.symbolProcessor.locale)
                super(SpeechSymbolsDialog, self).onOk(evt)
+
+class InputGesturesDialog(SettingsDialog):
+       # Translators: The title of the Input Gestures dialog where the user 
can remap input gestures for commands.
+       title = _("Input Gestures")
+
+       def makeSettings(self, settingsSizer):
+               tree = self.tree = wx.TreeCtrl(self, style=wx.TR_HAS_BUTTONS | 
wx.TR_HIDE_ROOT | wx.TR_SINGLE)
+               self.treeRoot = tree.AddRoot("root")
+               settingsSizer.Add(tree, proportion=7, flag=wx.EXPAND)
+               gestures = inputCore.manager.getAllGestureMappings()
+               for category in sorted(gestures):
+                       parent = tree.AppendItem(self.treeRoot, category)
+                       commands = gestures[category]
+                       for command in sorted(commands):
+                               item = tree.AppendItem(parent, command)
+                               self.tree.SetItemPyData(item, commands[command])
+
+       def postInit(self):
+               self.tree.SetFocus()


https://bitbucket.org/nvdaaddonteam/nvda/commits/b30f96aa3a8d/
Changeset:   b30f96aa3a8d
Branch:      None
User:        jteh
Date:        2013-08-26 09:32:40
Summary:     Add Input gestures item under NVDA Preferences menu.

Affected #:  1 file

diff --git a/source/gui/__init__.py b/source/gui/__init__.py
index 6428d32..55edcde 100644
--- a/source/gui/__init__.py
+++ b/source/gui/__init__.py
@@ -225,6 +225,9 @@ class MainFrame(wx.Frame):
        def onSpeechSymbolsCommand(self, evt):
                self._popupSettingsDialog(SpeechSymbolsDialog)
 
+       def onInputGesturesCommand(self, evt):
+               self._popupSettingsDialog(InputGesturesDialog)
+
        def onAboutCommand(self,evt):
                # Translators: The title of the dialog to show about info for 
NVDA.
                messageBox(versionInfo.aboutMessage, _("About NVDA"), wx.OK)
@@ -342,6 +345,9 @@ class SysTrayIcon(wx.TaskBarIcon):
                        # Translators: The label for the menu item to open 
Punctuation/symbol pronunciation dialog.
                        item = menu_preferences.Append(wx.ID_ANY, 
_("&Punctuation/symbol pronunciation..."))
                        self.Bind(wx.EVT_MENU, frame.onSpeechSymbolsCommand, 
item)
+                       # Translators: The label for the menu item to open the 
Input Gestures dialog.
+                       item = menu_preferences.Append(wx.ID_ANY, _("I&nput 
gestures..."))
+                       self.Bind(wx.EVT_MENU, frame.onInputGesturesCommand, 
item)
                # Translators: The label for Preferences submenu in NVDA menu.
                
self.menu.AppendMenu(wx.ID_ANY,_("&Preferences"),menu_preferences)
 


https://bitbucket.org/nvdaaddonteam/nvda/commits/164f5613181b/
Changeset:   164f5613181b
Branch:      None
User:        jteh
Date:        2013-08-27 04:50:40
Summary:     getAllGestureMappings: Kill unneeded byUser boolean in gestures.

Affected #:  1 file

diff --git a/source/inputCore.py b/source/inputCore.py
index 5fc6c13..7277a25 100644
--- a/source/inputCore.py
+++ b/source/inputCore.py
@@ -408,7 +408,7 @@ class _AllGestureMappingsRetriever(object):
                self.scriptInfo = {}
                self.handledGestures = set()
 
-               self.addGlobalMap(manager.userGestureMap, byUser=True)
+               self.addGlobalMap(manager.userGestureMap)
                self.addGlobalMap(manager.localeGestureMap)
                import braille
                gmap = braille.handler.display.gestureMap
@@ -445,7 +445,7 @@ class _AllGestureMappingsRetriever(object):
                        cat = self.results[scriptInfo.category] = {}
                cat[scriptInfo.displayName] = scriptInfo
 
-       def addGlobalMap(self, gmap, byUser=False):
+       def addGlobalMap(self, gmap):
                for cls, gesture, scriptName in gmap.getScriptsForAllGestures():
                        key = (cls, gesture)
                        if key in self.handledGestures:
@@ -468,7 +468,7 @@ class _AllGestureMappingsRetriever(object):
                                        if not scriptInfo:
                                                continue
                                self.addResult(scriptInfo)
-                       scriptInfo.gestures.append((gesture, byUser))
+                       scriptInfo.gestures.append(gesture)
 
        def makeKbEmuScriptInfo(self, cls, scriptName):
                info = AllGesturesScriptInfo(cls, scriptName)
@@ -506,7 +506,7 @@ class _AllGestureMappingsRetriever(object):
                                if not scriptInfo:
                                        continue
                                self.addResult(scriptInfo)
-                       scriptInfo.gestures.append((gesture, False))
+                       scriptInfo.gestures.append(gesture)
 
        def getScriptCls(self, script):
                name = script.__name__


https://bitbucket.org/nvdaaddonteam/nvda/commits/a7dce3fbe709/
Changeset:   a7dce3fbe709
Branch:      None
User:        jteh
Date:        2013-08-27 05:12:12
Summary:     InputGesturesDialog: Include the gestures in the tree as children 
of the commands.

Affected #:  1 file

diff --git a/source/gui/settingsDialogs.py b/source/gui/settingsDialogs.py
index 83530d0..bc7591f 100644
--- a/source/gui/settingsDialogs.py
+++ b/source/gui/settingsDialogs.py
@@ -1522,11 +1522,14 @@ class InputGesturesDialog(SettingsDialog):
                settingsSizer.Add(tree, proportion=7, flag=wx.EXPAND)
                gestures = inputCore.manager.getAllGestureMappings()
                for category in sorted(gestures):
-                       parent = tree.AppendItem(self.treeRoot, category)
+                       treeCat = tree.AppendItem(self.treeRoot, category)
                        commands = gestures[category]
                        for command in sorted(commands):
-                               item = tree.AppendItem(parent, command)
-                               self.tree.SetItemPyData(item, commands[command])
+                               treeCom = tree.AppendItem(treeCat, command)
+                               commandInfo = commands[command]
+                               tree.SetItemPyData(treeCom, commandInfo)
+                               for gesture in commandInfo.gestures:
+                                       tree.AppendItem(treeCom, gesture)
 
        def postInit(self):
                self.tree.SetFocus()


https://bitbucket.org/nvdaaddonteam/nvda/commits/837a4ccb82f9/
Changeset:   837a4ccb82f9
Branch:      None
User:        jteh
Date:        2013-08-27 09:44:24
Summary:     getAllGestureMappings: Include scripts that aren't bound to any 
gesture.

Affected #:  1 file

diff --git a/source/inputCore.py b/source/inputCore.py
index 7277a25..e895643 100644
--- a/source/inputCore.py
+++ b/source/inputCore.py
@@ -418,24 +418,24 @@ class _AllGestureMappingsRetriever(object):
                # Global plugins.
                import globalPluginHandler
                for plugin in globalPluginHandler.runningPlugins:
-                       self.addObjBindings(plugin)
+                       self.addObj(plugin)
 
                # App module.
                app = obj.appModule
                if app:
-                       self.addObjBindings(app)
+                       self.addObj(app)
 
                # Tree interceptor.
                ti = obj.treeInterceptor
                if ti:
-                       self.addObjBindings(ti)
+                       self.addObj(ti)
 
                # NVDAObject.
-               self.addObjBindings(obj)
+               self.addObj(obj)
 
                # Global commands.
                import globalCommands
-               self.addObjBindings(globalCommands.commands)
+               self.addObj(globalCommands.commands)
 
        def addResult(self, scriptInfo):
                self.scriptInfo[scriptInfo.cls, scriptInfo.scriptName] = 
scriptInfo
@@ -495,25 +495,28 @@ class _AllGestureMappingsRetriever(object):
                        pass
                return SCRCAT_MISC
 
-       def addObjBindings(self, obj):
+       def addObj(self, obj):
+               scripts = {}
+               for cls in obj.__class__.__mro__:
+                       for scriptName, script in cls.__dict__.iteritems():
+                               if not scriptName.startswith("script_"):
+                                       continue
+                               scriptName = scriptName[7:]
+                               try:
+                                       scriptInfo = self.scriptInfo[cls, 
scriptName]
+                               except KeyError:
+                                       scriptInfo = 
self.makeNormalScriptInfo(cls, scriptName, script)
+                                       if not scriptInfo:
+                                               continue
+                                       self.addResult(scriptInfo)
+                               scripts[script] = scriptInfo
                for gesture, script in obj._gestureMap.iteritems():
-                       scriptName = script.__name__[7:]
-                       cls = self.getScriptCls(script)
                        try:
-                               scriptInfo = self.scriptInfo[cls, scriptName]
+                               scriptInfo = scripts[script.__func__]
                        except KeyError:
-                               scriptInfo = self.makeNormalScriptInfo(cls, 
scriptName, script)
-                               if not scriptInfo:
-                                       continue
-                               self.addResult(scriptInfo)
+                               continue
                        scriptInfo.gestures.append(gesture)
 
-       def getScriptCls(self, script):
-               name = script.__name__
-               for cls in script.im_class.__mro__:
-                       if name in cls.__dict__:
-                               return cls
-
 class AllGesturesScriptInfo(object):
        __slots__ = ("cls", "scriptName", "category", "displayName", "gestures")
        


https://bitbucket.org/nvdaaddonteam/nvda/commits/23c95d437dc4/
Changeset:   23c95d437dc4
Branch:      None
User:        jteh
Date:        2013-08-28 05:21:32
Summary:     InputGesturesDialog: Provide Add and Remove buttons that are only 
enabled when appropriate. They don't do anything just yet.

Affected #:  1 file

diff --git a/source/gui/settingsDialogs.py b/source/gui/settingsDialogs.py
index bc7591f..ce9df5c 100644
--- a/source/gui/settingsDialogs.py
+++ b/source/gui/settingsDialogs.py
@@ -1519,7 +1519,9 @@ class InputGesturesDialog(SettingsDialog):
        def makeSettings(self, settingsSizer):
                tree = self.tree = wx.TreeCtrl(self, style=wx.TR_HAS_BUTTONS | 
wx.TR_HIDE_ROOT | wx.TR_SINGLE)
                self.treeRoot = tree.AddRoot("root")
+               tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.onTreeSelect)
                settingsSizer.Add(tree, proportion=7, flag=wx.EXPAND)
+
                gestures = inputCore.manager.getAllGestureMappings()
                for category in sorted(gestures):
                        treeCat = tree.AppendItem(self.treeRoot, category)
@@ -1529,7 +1531,35 @@ class InputGesturesDialog(SettingsDialog):
                                commandInfo = commands[command]
                                tree.SetItemPyData(treeCom, commandInfo)
                                for gesture in commandInfo.gestures:
-                                       tree.AppendItem(treeCom, gesture)
+                                       treeGes = tree.AppendItem(treeCom, 
gesture)
+                                       tree.SetItemPyData(treeGes, gesture)
+
+               sizer = wx.BoxSizer(wx.HORIZONTAL)
+               # Translators: The label of a button to add a gesture in the 
Input Gestures dialog.
+               item = self.addButton = wx.Button(self, label=_("&Add"))
+               item.Bind(wx.EVT_BUTTON, self.onAdd)
+               item.Disable()
+               sizer.Add(item)
+               # Translators: The label of a button to remove a gesture in the 
Input Gestures dialog.
+               item = self.removeButton = wx.Button(self, label=_("&Remove"))
+               item.Bind(wx.EVT_BUTTON, self.onRemove)
+               item.Disable()
+               sizer.Add(item)
+               settingsSizer.Add(sizer)
 
        def postInit(self):
                self.tree.SetFocus()
+
+       def onTreeSelect(self, evt):
+               item = evt.Item
+               data = self.tree.GetItemPyData(item)
+               isCommand = isinstance(data, inputCore.AllGesturesScriptInfo)
+               isGesture = isinstance(data, basestring)
+               self.addButton.Enabled = isCommand or isGesture
+               self.removeButton.Enabled = isGesture
+
+       def onAdd(self, evt):
+               pass
+
+       def onRemove(self, evt):
+               pass


https://bitbucket.org/nvdaaddonteam/nvda/commits/2988806143d7/
Changeset:   2988806143d7
Branch:      None
User:        jteh
Date:        2013-08-28 05:58:42
Summary:     getAllGestureMappings: Account for the user unbinding gestures 
defined on objects.

Affected #:  1 file

diff --git a/source/inputCore.py b/source/inputCore.py
index e895643..21c7ef2 100644
--- a/source/inputCore.py
+++ b/source/inputCore.py
@@ -515,6 +515,10 @@ class _AllGestureMappingsRetriever(object):
                                scriptInfo = scripts[script.__func__]
                        except KeyError:
                                continue
+                       key = (scriptInfo.cls, gesture)
+                       if key in self.handledGestures:
+                               continue
+                       self.handledGestures.add(key)
                        scriptInfo.gestures.append(gesture)
 
 class AllGesturesScriptInfo(object):


https://bitbucket.org/nvdaaddonteam/nvda/commits/716d4040f948/
Changeset:   716d4040f948
Branch:      None
User:        jteh
Date:        2013-08-28 06:00:19
Summary:     InputGesturesDialog: Add code for removing gestures.

GlobalGestureMap grew a remove method to facilitate this.

Affected #:  2 files

diff --git a/source/gui/settingsDialogs.py b/source/gui/settingsDialogs.py
index ce9df5c..35b190d 100644
--- a/source/gui/settingsDialogs.py
+++ b/source/gui/settingsDialogs.py
@@ -1544,6 +1544,7 @@ class InputGesturesDialog(SettingsDialog):
                item = self.removeButton = wx.Button(self, label=_("&Remove"))
                item.Bind(wx.EVT_BUTTON, self.onRemove)
                item.Disable()
+               self.pendingRemoves = set()
                sizer.Add(item)
                settingsSizer.Add(sizer)
 
@@ -1562,4 +1563,23 @@ class InputGesturesDialog(SettingsDialog):
                pass
 
        def onRemove(self, evt):
-               pass
+               treeGes = self.tree.Selection
+               gesture = self.tree.GetItemPyData(treeGes)
+               treeCom = self.tree.GetItemParent(treeGes)
+               scriptInfo = self.tree.GetItemPyData(treeCom)
+               cls = scriptInfo.cls
+               module = cls.__module__
+               className = cls.__name__
+               self.pendingRemoves.add((gesture, module, className, 
scriptInfo.scriptName))
+               self.tree.Delete(treeGes)
+               self.tree.SetFocus()
+
+       def onOk(self, evt):
+               for gesture, module, className, scriptName in 
self.pendingRemoves:
+                       try:
+                               
inputCore.manager.userGestureMap.remove(gesture, module, className, scriptName)
+                       except ValueError:
+                               # The user wants to unbind a gesture they 
didn't define.
+                               inputCore.manager.userGestureMap.add(gesture, 
module, className, None)
+
+               super(InputGesturesDialog, self).onOk(evt)

diff --git a/source/inputCore.py b/source/inputCore.py
index 21c7ef2..232f3fe 100644
--- a/source/inputCore.py
+++ b/source/inputCore.py
@@ -266,6 +266,25 @@ class GlobalGestureMap(object):
                        for cls, scriptName in 
self.getScriptsForGesture(gesture):
                                yield cls, gesture, scriptName
 
+       def remove(self, gesture, module, className, script):
+               """Remove a gesture mapping.
+               @param gesture: The gesture identifier.
+               @type gesture: str
+               @param module: The name of the Python module containing the 
target script.
+               @type module: str
+               @param className: The name of the class in L{module} containing 
the target script.
+               @type className: str
+               @param script: The name of the target script.
+               @type script: str
+               @raise ValueError: If the requested mapping does not exist.
+               """
+               gesture = normalizeGestureIdentifier(gesture)
+               try:
+                       scripts = self._map[gesture]
+               except KeyError:
+                       raise ValueError("Mapping not found")
+               scripts.remove((module, className, script))
+
 class InputManager(baseObject.AutoPropertyObject):
        """Manages functionality related to input from the user.
        Input includes key presses on the keyboard, as well as key presses on 
Braille displays, etc.


https://bitbucket.org/nvdaaddonteam/nvda/commits/3dc9d322925f/
Changeset:   3dc9d322925f
Branch:      None
User:        jteh
Date:        2013-08-29 01:36:05
Summary:     inputCore: Abstract the capturing of gestures (as used by input 
help) so that it can be used for entering gestures in the Input Gestures dialog.

This is still only intended for internal use, as incorrect usage could have 
nasty consequdnces.

Affected #:  1 file

diff --git a/source/inputCore.py b/source/inputCore.py
index 232f3fe..6a65cca 100644
--- a/source/inputCore.py
+++ b/source/inputCore.py
@@ -291,9 +291,10 @@ class InputManager(baseObject.AutoPropertyObject):
        """
 
        def __init__(self):
-               #: Whether input help is enabled, wherein the function of each 
key pressed by the user is reported but not executed.
-               #: @type: bool
-               self.isInputHelpActive = False
+               #: The function to call when capturing gestures.
+               #: If it returns C{False}, normal execution will be prevented.
+               #: @type: callable
+               self._captureFunc = None
                #: The gestures mapped for the NVDA locale.
                #: @type: L{GlobalGestureMap}
                self.localeGestureMap = GlobalGestureMap()
@@ -329,11 +330,13 @@ class InputManager(baseObject.AutoPropertyObject):
                if log.isEnabledFor(log.IO) and not gesture.isModifier:
                        log.io("Input: %s" % gesture.logIdentifier)
 
-               if self.isInputHelpActive:
-                       bypass = getattr(script, "bypassInputHelp", False)
-                       queueHandler.queueFunction(queueHandler.eventQueue, 
self._handleInputHelp, gesture, onlyLog=bypass)
-                       if not bypass:
-                               return
+               if self._captureFunc:
+                       try:
+                               if self._captureFunc(gesture) is False:
+                                       return
+                       except:
+                               log.error("Error in capture function, 
disabling", exc_info=True)
+                               self._captureFunc = None
 
                if gesture.isModifier:
                        raise NoInputGestureAction
@@ -349,6 +352,23 @@ class InputManager(baseObject.AutoPropertyObject):
 
                raise NoInputGestureAction
 
+       def _get_isInputHelpActive(self):
+               """Whether input help is enabled, wherein the function of each 
key pressed by the user is reported but not executed.
+               @rtype: bool
+               """
+               return self._captureFunc == self._inputHelpCaptor
+
+       def _set_isInputHelpActive(self, enable):
+               if enable:
+                       self._captureFunc = self._inputHelpCaptor
+               elif self.isInputHelpActive:
+                       self._captureFunc = None
+
+       def _inputHelpCaptor(self, gesture):
+               bypass = getattr(gesture.script, "bypassInputHelp", False)
+               queueHandler.queueFunction(queueHandler.eventQueue, 
self._handleInputHelp, gesture, onlyLog=bypass)
+               return bypass
+
        def _handleInputHelp(self, gesture, onlyLog=False):
                textList = [gesture.displayName]
                script = gesture.script


https://bitbucket.org/nvdaaddonteam/nvda/commits/44d55637e7b1/
Changeset:   44d55637e7b1
Branch:      None
User:        jteh
Date:        2013-08-29 02:51:30
Summary:     InputGesturesDialog: Add support for adding gestures.

Affected #:  2 files

diff --git a/source/gui/settingsDialogs.py b/source/gui/settingsDialogs.py
index 35b190d..8a76136 100644
--- a/source/gui/settingsDialogs.py
+++ b/source/gui/settingsDialogs.py
@@ -1544,6 +1544,7 @@ class InputGesturesDialog(SettingsDialog):
                item = self.removeButton = wx.Button(self, label=_("&Remove"))
                item.Bind(wx.EVT_BUTTON, self.onRemove)
                item.Disable()
+               self.pendingAdds = set()
                self.pendingRemoves = set()
                sizer.Add(item)
                settingsSizer.Add(sizer)
@@ -1552,7 +1553,7 @@ class InputGesturesDialog(SettingsDialog):
                self.tree.SetFocus()
 
        def onTreeSelect(self, evt):
-               item = evt.Item
+               item = self.tree.Selection
                data = self.tree.GetItemPyData(item)
                isCommand = isinstance(data, inputCore.AllGesturesScriptInfo)
                isGesture = isinstance(data, basestring)
@@ -1560,17 +1561,40 @@ class InputGesturesDialog(SettingsDialog):
                self.removeButton.Enabled = isGesture
 
        def onAdd(self, evt):
-               pass
+               if inputCore.manager._captureFunc:
+                       return
+
+               treeCom = self.tree.Selection
+               scriptInfo = self.tree.GetItemPyData(treeCom)
+               if not isinstance(scriptInfo, inputCore.AllGesturesScriptInfo):
+                       treeCom = self.tree.GetItemParent(treeCom)
+                       scriptInfo = self.tree.GetItemPyData(treeCom)
+               # Translators: The prompt to enter a gesture in the Input 
Gestures dialog.
+               treeGes = self.tree.AppendItem(treeCom, _("Enter input 
gesture:"))
+               self.tree.SelectItem(treeGes)
+               self.tree.SetFocus()
+
+               def addGestureCaptor(gesture):
+                       if gesture.isModifier:
+                               return False
+                       inputCore.manager._captureFunc = None
+                       wx.CallAfter(self._finishAdd, treeGes, scriptInfo, 
gesture)
+                       return False
+               inputCore.manager._captureFunc = addGestureCaptor
+
+       def _finishAdd(self, treeGes, scriptInfo, gesture):
+               gestureId = gesture.logIdentifier
+               self.pendingAdds.add((gestureId, scriptInfo.moduleName, 
scriptInfo.className, scriptInfo.scriptName))
+               self.tree.SetItemText(treeGes, gesture.logIdentifier)
+               self.tree.SetItemPyData(treeGes, gestureId)
+               self.onTreeSelect(None)
 
        def onRemove(self, evt):
                treeGes = self.tree.Selection
                gesture = self.tree.GetItemPyData(treeGes)
                treeCom = self.tree.GetItemParent(treeGes)
                scriptInfo = self.tree.GetItemPyData(treeCom)
-               cls = scriptInfo.cls
-               module = cls.__module__
-               className = cls.__name__
-               self.pendingRemoves.add((gesture, module, className, 
scriptInfo.scriptName))
+               self.pendingRemoves.add((gesture, scriptInfo.moduleName, 
scriptInfo.className, scriptInfo.scriptName))
                self.tree.Delete(treeGes)
                self.tree.SetFocus()
 
@@ -1582,4 +1606,13 @@ class InputGesturesDialog(SettingsDialog):
                                # The user wants to unbind a gesture they 
didn't define.
                                inputCore.manager.userGestureMap.add(gesture, 
module, className, None)
 
+               for gesture, module, className, scriptName in self.pendingAdds:
+                       try:
+                               # The user might have unbound this gesture,
+                               # so remove this override first.
+                               
inputCore.manager.userGestureMap.remove(gesture, module, className, None)
+                       except ValueError:
+                               pass
+                       inputCore.manager.userGestureMap.add(gesture, module, 
className, scriptName)
+
                super(InputGesturesDialog, self).onOk(evt)

diff --git a/source/inputCore.py b/source/inputCore.py
index 6a65cca..c8dae30 100644
--- a/source/inputCore.py
+++ b/source/inputCore.py
@@ -568,6 +568,14 @@ class AllGesturesScriptInfo(object):
                self.scriptName = scriptName
                self.gestures = []
 
+       @property
+       def moduleName(self):
+               return self.cls.__module__
+
+       @property
+       def className(self):
+               return self.cls.__name__
+
 def normalizeGestureIdentifier(identifier):
        """Normalize a gesture identifier so that it matches other identifiers 
for the same gesture.
        Any items separated by a + sign after the source are considered to be 
of indeterminate order


https://bitbucket.org/nvdaaddonteam/nvda/commits/c24f41f8a099/
Changeset:   c24f41f8a099
Branch:      None
User:        jteh
Date:        2013-08-29 04:15:56
Summary:     Save user defined gestures when there are changes and OK is 
pressed in the Input Gestures dialog.

GlobalGestureMap grew a save method to facilitate this.

Affected #:  2 files

diff --git a/source/gui/settingsDialogs.py b/source/gui/settingsDialogs.py
index 8a76136..6b5421b 100644
--- a/source/gui/settingsDialogs.py
+++ b/source/gui/settingsDialogs.py
@@ -1615,4 +1615,14 @@ class InputGesturesDialog(SettingsDialog):
                                pass
                        inputCore.manager.userGestureMap.add(gesture, module, 
className, scriptName)
 
+               if self.pendingAdds or self.pendingRemoves:
+                       # Only save if there is something to save.
+                       try:
+                               inputCore.manager.userGestureMap.save()
+                       except:
+                               log.debugWarning("", exc_info=True)
+                               # Translators: An error displayed when saving 
user defined input gestures fails.
+                               gui.messageBox(_("Error saving user defined 
gestures - probably read only file system."),
+                                       _("Error"), wx.OK | wx.ICON-ERROR)
+
                super(InputGesturesDialog, self).onOk(evt)

diff --git a/source/inputCore.py b/source/inputCore.py
index c8dae30..f96f253 100644
--- a/source/inputCore.py
+++ b/source/inputCore.py
@@ -137,6 +137,9 @@ class GlobalGestureMap(object):
                #: Indicates that the last load or update contained an error.
                #: @type: bool
                self.lastUpdateContainedError = False
+               #: The file name for this gesture map, if any.
+               #: @type: basestring
+               self.fileName = None
                if entries:
                        self.update(entries)
 
@@ -184,6 +187,7 @@ class GlobalGestureMap(object):
                @param filename: The name of the file to load.
                @type: str
                """
+               self.fileName = filename
                try:
                        conf = configobj.ConfigObj(filename, file_error=True, 
encoding="UTF-8")
                except (configobj.ConfigObjError,UnicodeDecodeError), e:
@@ -285,6 +289,40 @@ class GlobalGestureMap(object):
                        raise ValueError("Mapping not found")
                scripts.remove((module, className, script))
 
+       def save(self):
+               """Save this gesture map to disk.
+               @precondition: L{load} must have been called.
+               """
+               if globalVars.appArgs.secure:
+                       return
+               if not self.fileName:
+                       raise ValueError("No file name")
+               out = configobj.ConfigObj()
+               out.filename = self.fileName
+
+               for gesture, scripts in self._map.iteritems():
+                       for module, className, script in scripts:
+                               key = "%s.%s" % (module, className)
+                               try:
+                                       outSect = out[key]
+                               except KeyError:
+                                       out[key] = {}
+                                       outSect = out[key]
+                               if script is None:
+                                       script = "None"
+                               try:
+                                       outVal = outSect[script]
+                               except KeyError:
+                                       # Write the first value as a string so 
configobj doesn't output a comma if there's only one value.
+                                       outVal = outSect[script] = gesture
+                               else:
+                                       if isinstance(outVal, list):
+                                               outVal.append(gesture)
+                                       else:
+                                               outSect[script] = [outVal, 
gesture]
+
+               out.write()
+
 class InputManager(baseObject.AutoPropertyObject):
        """Manages functionality related to input from the user.
        Input includes key presses on the keyboard, as well as key presses on 
Braille displays, etc.


https://bitbucket.org/nvdaaddonteam/nvda/commits/db0fddf2a52d/
Changeset:   db0fddf2a52d
Branch:      None
User:        jteh
Date:        2013-08-29 06:27:06
Summary:     getAllGestureMappings: Include ancestor NVDAObjects, using focus 
ancestors by default.

Affected #:  1 file

diff --git a/source/inputCore.py b/source/inputCore.py
index f96f253..3b70125 100644
--- a/source/inputCore.py
+++ b/source/inputCore.py
@@ -473,14 +473,15 @@ class InputManager(baseObject.AutoPropertyObject):
                except NotImplementedError:
                        pass
 
-       def getAllGestureMappings(self, obj=None):
+       def getAllGestureMappings(self, obj=None, ancestors=None):
                if not obj:
                        obj = api.getFocusObject()
-               return _AllGestureMappingsRetriever(obj).results
+                       ancestors = api.getFocusAncestors()
+               return _AllGestureMappingsRetriever(obj, ancestors).results
 
 class _AllGestureMappingsRetriever(object):
 
-       def __init__(self, obj):
+       def __init__(self, obj, ancestors):
                self.results = {}
                self.scriptInfo = {}
                self.handledGestures = set()
@@ -509,6 +510,8 @@ class _AllGestureMappingsRetriever(object):
 
                # NVDAObject.
                self.addObj(obj)
+               for anc in reversed(ancestors):
+                       self.addObj(anc, isAncestor=True)
 
                # Global commands.
                import globalCommands
@@ -572,12 +575,14 @@ class _AllGestureMappingsRetriever(object):
                        pass
                return SCRCAT_MISC
 
-       def addObj(self, obj):
+       def addObj(self, obj, isAncestor=False):
                scripts = {}
                for cls in obj.__class__.__mro__:
                        for scriptName, script in cls.__dict__.iteritems():
                                if not scriptName.startswith("script_"):
                                        continue
+                               if isAncestor and not getattr(script, 
"canPropagate", False):
+                                       continue
                                scriptName = scriptName[7:]
                                try:
                                        scriptInfo = self.scriptInfo[cls, 
scriptName]


https://bitbucket.org/nvdaaddonteam/nvda/commits/b266daf99ffa/
Changeset:   b266daf99ffa
Branch:      None
User:        mdcurran
Date:        2013-09-02 01:08:06
Summary:     MSHTML VBufBackend: rather than use attachEvent / detachEvent, use 
IConnectionPoint and friends. This *may* be more stable and *may* stop crashes 
after NVDA exit in Internet Explorer.

Affected #:  3 files

diff --git a/nvdaHelper/vbufBackends/mshtml/mshtml.cpp 
b/nvdaHelper/vbufBackends/mshtml/mshtml.cpp
index 8f2adb6..e8590b4 100755
--- a/nvdaHelper/vbufBackends/mshtml/mshtml.cpp
+++ b/nvdaHelper/vbufBackends/mshtml/mshtml.cpp
@@ -561,8 +561,12 @@ inline void fillTextFormattingForNode(IHTMLDOMNode* 
pHTMLDOMNode, VBufStorage_fi
 inline void fillTextFormattingForTextNode(VBufStorage_controlFieldNode_t* 
parentNode, VBufStorage_textFieldNode_t* textNode)
        //text nodes don't support IHTMLElement2 interface, so using style 
information from parent node
        {
-       IHTMLElement2* 
pHTMLElement2=(static_cast<MshtmlVBufStorage_controlFieldNode_t*>(parentNode))->pHTMLElement2;
-       fillTextFormatting_helper(pHTMLElement2,textNode);
+       IHTMLElement2* pHTMLElement2=NULL; 
+       
static_cast<MshtmlVBufStorage_controlFieldNode_t*>(parentNode)->pHTMLDOMNode->QueryInterface(IID_IHTMLElement2,(void**)&pHTMLElement2);
+       if(pHTMLElement2) {
+               fillTextFormatting_helper(pHTMLElement2,textNode);
+               pHTMLElement2->Release();
+       }
 }
 
 const int TABLEHEADER_COLUMN = 0x1;
@@ -1227,11 +1231,11 @@ void MshtmlVBufBackend_t::render(VBufStorage_buffer_t* 
buffer, int docHandle, in
                LOG_DEBUG(L"Error getting document using WM_HTML_GETOBJECT");
                return;
        }
-IHTMLDOMNode* pHTMLDOMNode=NULL;
+       IHTMLDOMNode* pHTMLDOMNode=NULL;
        if(oldNode!=NULL) {
-               IHTMLElement2* 
pHTMLElement2=(static_cast<MshtmlVBufStorage_controlFieldNode_t*>(oldNode))->pHTMLElement2;
-               nhAssert(pHTMLElement2);
-               
pHTMLElement2->QueryInterface(IID_IHTMLDOMNode,(void**)&pHTMLDOMNode);
+               
pHTMLDOMNode=(static_cast<MshtmlVBufStorage_controlFieldNode_t*>(oldNode))->pHTMLDOMNode;
+               nhAssert(pHTMLDOMNode);
+               pHTMLDOMNode->AddRef();
        } else {
                IHTMLDocument3* pHTMLDocument3=NULL;
                
if(ObjectFromLresult(res,IID_IHTMLDocument3,0,(void**)&pHTMLDocument3)!=S_OK) {

diff --git a/nvdaHelper/vbufBackends/mshtml/node.cpp 
b/nvdaHelper/vbufBackends/mshtml/node.cpp
index c7d9b5b..c3f02d1 100755
--- a/nvdaHelper/vbufBackends/mshtml/node.cpp
+++ b/nvdaHelper/vbufBackends/mshtml/node.cpp
@@ -14,8 +14,10 @@ http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 
 #include <list>
 #include <windows.h>
+#include <objbase.h>
 #include <oleidl.h>
 #include <mshtml.h>
+#include <mshtmdid.h>
 #include <common/log.h>
 #include "mshtml.h"
 #include "node.h"
@@ -25,33 +27,71 @@ using namespace std;
 class CDispatchChangeSink : public IDispatch {
        private:
        ULONG refCount;
-       bool hasFired;
+       MshtmlVBufStorage_controlFieldNode_t* storageNode;
+       IConnectionPoint* pConnectionPoint;
+       DWORD dwCookie;
 
        public:
-       MshtmlVBufStorage_controlFieldNode_t* storageNode;
-       bool allowDelete;
 
-       CDispatchChangeSink(MshtmlVBufStorage_controlFieldNode_t* storageNode):
-       refCount(1),
-       hasFired(false),
-       allowDelete(true) {
+       CDispatchChangeSink(MshtmlVBufStorage_controlFieldNode_t* storageNode) :
+       refCount(1), dwCookie(0), pConnectionPoint(NULL) {
                nhAssert(storageNode);
                this->storageNode=storageNode;
                incBackendLibRefCount();
        }
 
+       BOOL connect(IHTMLDOMNode* pHTMLDOMNode, REFIID iid) {
+               if(dwCookie) {
+                       LOG_DEBUGWARNING(L"Already connected");
+                       return false;
+               }
+               IHTMLElement* pHTMLElement=NULL;
+               
pHTMLDOMNode->QueryInterface(IID_IHTMLElement,(void**)&pHTMLElement);
+               if(!pHTMLElement) {
+                       LOG_DEBUGWARNING(L"QueryInterface to IHTMLElement 
failed");
+                       return false;
+               }
+               IConnectionPointContainer* pConnectionPointContainer=NULL;
+               
pHTMLElement->QueryInterface(IID_IConnectionPointContainer,(void**)&pConnectionPointContainer);
+               pHTMLElement->Release();
+               if(!pConnectionPointContainer) {
+                       LOG_DEBUGWARNING(L"QueryInterface to 
IConnectionPointContainer failed");
+                       return false;
+               }
+               IConnectionPoint* pConnectionPoint=NULL;
+               
pConnectionPointContainer->FindConnectionPoint(iid,&pConnectionPoint);
+               pConnectionPointContainer->Release();
+               if(!pConnectionPoint) {
+                       return false;
+               }
+               DWORD dwCookie=0;
+               pConnectionPoint->Advise(this,&dwCookie);
+               if(!dwCookie) {
+                       pConnectionPoint->Release();
+                       return false;
+               }
+               this->pConnectionPoint=pConnectionPoint;
+               this->dwCookie=dwCookie;
+               return true;
+       }
+
+       BOOL disconnect() {
+               if(this->dwCookie==0) return false;
+               this->pConnectionPoint->Unadvise(this->dwCookie);
+               this->dwCookie=0;
+               this->pConnectionPoint->Release();
+               this->pConnectionPoint=NULL;
+               return true;
+       }
+
        ~CDispatchChangeSink() {
+               this->disconnect();
                decBackendLibRefCount();
        }
 
        void onChange() {
-               if(hasFired||allowDelete) {
-                       return;
-               }
-               hasFired=true;
                LOG_DEBUG(L"Marking storage node as invalid");
                
this->storageNode->backend->invalidateSubtree(this->storageNode);
-               LOG_DEBUG(L"Done");
        }
 
        HRESULT STDMETHODCALLTYPE IUnknown::QueryInterface(REFIID riid, void** 
pvpObject) {
@@ -77,28 +117,18 @@ class CDispatchChangeSink : public IDispatch {
                if(this->refCount>0)
                        this->refCount--;
                if(this->refCount==0) {
-                       if (this->allowDelete) {
-                               delete this;
-                       } else {
-                               #ifdef DEBUG
-                               Beep(660,50);
-                               #endif
-                               LOG_DEBUG(L"refCount hit 0 before it should, 
not deleting, node info: " << this->storageNode->getDebugInfo());
-                       }
+                       delete this;
                        return 0;
                }
                return this->refCount;
        }
 
        HRESULT STDMETHODCALLTYPE IDispatch::Invoke(DISPID  dispIdMember, 
REFIID  riid, LCID  lcid, WORD  wFlags, DISPPARAMS FAR*  pDispParams, VARIANT 
FAR*  pVarResult, EXCEPINFO FAR*  pExcepInfo, unsigned int FAR*  puArgErr) {
-               if(dispIdMember==0) {
-                       LOG_DEBUG(L"calling onChange");
+               if(1) { 
//dispIdMember==DISPID_HTMLELEMENTEVENTS2_ONPROPERTYCHANGE||dispIdMember==DISPID_IHTMLELEMENT2_ONPROPERTYCHANGE)
 {
                        this->onChange();
-                       LOG_DEBUG(L"Done, returning S_OK");
                        return S_OK;
                }
-               LOG_DEBUG(L"invoke called with unknown member ID, returning 
E_INVALIDARG");
-               return E_INVALIDARG;
+               return E_FAIL;
        }
 
        HRESULT STDMETHODCALLTYPE  IDispatch::GetTypeInfoCount(UINT* count) {
@@ -238,48 +268,22 @@ class CHTMLChangeSink : public IHTMLChangeSink {
 };
 
 MshtmlVBufStorage_controlFieldNode_t::MshtmlVBufStorage_controlFieldNode_t(int 
docHandle, int ID, bool isBlock, MshtmlVBufBackend_t* backend, IHTMLDOMNode* 
pHTMLDOMNode,const wstring& lang): 
VBufStorage_controlFieldNode_t(docHandle,ID,isBlock), language(lang) {
-       VARIANT_BOOL varBool;
        nhAssert(backend);
        nhAssert(pHTMLDOMNode);
        this->backend=backend;
-       this->pHTMLElement2=NULL;
+       pHTMLDOMNode->AddRef();
+       this->pHTMLDOMNode=pHTMLDOMNode;
        this->propChangeSink=NULL;
        this->loadSink=NULL;
        this->pHTMLChangeSink=NULL;
        this->HTMLChangeSinkCookey=0;
-       
pHTMLDOMNode->QueryInterface(IID_IHTMLElement2,(void**)&(this->pHTMLElement2));
-       if(!this->pHTMLElement2) {
-               LOG_DEBUG(L"Could not queryInterface from IHTMLDOMNode to 
IHTMLElement2");
-       }
-       if(this->pHTMLElement2) {
-               CDispatchChangeSink* propChangeSink=new 
CDispatchChangeSink(this);
-               // It seems that IE 6 sometimes calls Release() once too many 
times.
-               // We don't want propChangeSink to be deleted until we're 
finished with it.
-               propChangeSink->allowDelete=false;
-               
if((pHTMLElement2->attachEvent(L"onpropertychange",propChangeSink,&varBool)==S_OK)&&varBool)
 {
-                       this->propChangeSink=propChangeSink;
-               } else {
-                       LOG_DEBUG(L"Error attaching onPropertyChange event sink 
to IHTMLElement2 at "<<pHTMLElement2);
-                       propChangeSink->allowDelete=true;
-                       propChangeSink->Release();
-               }
-       }
        BSTR nodeName=NULL;
        pHTMLDOMNode->get_nodeName(&nodeName);
-       
if(nodeName!=NULL&&(_wcsicmp(nodeName,L"frame")==0||_wcsicmp(nodeName,L"iframe")==0||_wcsicmp(nodeName,L"img")==0||_wcsicmp(nodeName,L"input")==0))
 {
-               if(this->pHTMLElement2) {
-                       CDispatchChangeSink* loadSink=new 
CDispatchChangeSink(this);
-                       // It seems that IE 6 sometimes calls Release() once 
too many times.
-                       // We don't want loadSink to be deleted until we're 
finished with it.
-                       loadSink->allowDelete=false;
-                       
if((pHTMLElement2->attachEvent(L"onload",loadSink,&varBool)==S_OK)&&varBool) {
-                               this->loadSink=loadSink;
-                       } else {
-                               LOG_DEBUG(L"Error attaching onload event sink 
to IHTMLElement2 at "<<pHTMLElement2);
-                               loadSink->allowDelete=true;
-                               loadSink->Release();
-                       }
-               }
+       CDispatchChangeSink* propChangeSink=new CDispatchChangeSink(this);
+       if(propChangeSink->connect(pHTMLDOMNode,IID_IDispatch)) {
+               this->propChangeSink=propChangeSink;
+       } else {
+               propChangeSink->Release();
        }
        
if(nodeName!=NULL&&(_wcsicmp(nodeName,L"body")==0||_wcsicmp(nodeName,L"frameset")==0))
 {
                IHTMLDOMNode2* pHTMLDOMNode2=NULL;
@@ -317,23 +321,15 @@ 
MshtmlVBufStorage_controlFieldNode_t::MshtmlVBufStorage_controlFieldNode_t(int d
  
 MshtmlVBufStorage_controlFieldNode_t::~MshtmlVBufStorage_controlFieldNode_t() {
        if(this->propChangeSink) {
-               nhAssert(this->pHTMLElement2);
-               
if(pHTMLElement2->detachEvent(L"onpropertychange",this->propChangeSink)!=S_OK) {
-                       LOG_DEBUG(L"Error detaching onpropertychange event sink 
from IHTMLElement2");
+               
if(!(static_cast<CDispatchChangeSink*>(this->propChangeSink)->disconnect())) {
+                       LOG_DEBUGWARNING(L"Failed to stop listening with 
HTMLElementEvents2 for node "<<this->getDebugInfo());
                }
-               
static_cast<CDispatchChangeSink*>(this->propChangeSink)->allowDelete=true;
                this->propChangeSink->Release();
+               this->propChangeSink=NULL;
        }
-       if(this->loadSink) {
-               nhAssert(this->pHTMLElement2);
-               if(pHTMLElement2->detachEvent(L"onload",this->loadSink)!=S_OK) {
-                       LOG_DEBUG(L"Error detaching onload event sink from 
IHTMLElement2");
-               }
-               
static_cast<CDispatchChangeSink*>(this->loadSink)->allowDelete=true;
-               this->loadSink->Release();
-       }
-       if(this->pHTMLElement2) {
-               this->pHTMLElement2->Release();
+       if(this->pHTMLDOMNode) {
+               this->pHTMLDOMNode->Release();
+               this->pHTMLDOMNode=NULL;
        }
        if(this->pHTMLChangeSink) {
                nhAssert(this->pMarkupContainer2);

diff --git a/nvdaHelper/vbufBackends/mshtml/node.h 
b/nvdaHelper/vbufBackends/mshtml/node.h
index 7fc7ae6..ec43b80 100755
--- a/nvdaHelper/vbufBackends/mshtml/node.h
+++ b/nvdaHelper/vbufBackends/mshtml/node.h
@@ -24,7 +24,7 @@ class MshtmlVBufStorage_controlFieldNode_t : public 
VBufStorage_controlFieldNode
 
        public:
        MshtmlVBufBackend_t* backend;
-       IHTMLElement2* pHTMLElement2;
+       IHTMLDOMNode* pHTMLDOMNode;
        IDispatch* propChangeSink;
        IDispatch* loadSink;
        IMarkupContainer2* pMarkupContainer2;


https://bitbucket.org/nvdaaddonteam/nvda/commits/44c554e49489/
Changeset:   44c554e49489
Branch:      None
User:        mdcurran
Date:        2013-09-02 01:08:07
Summary:     MSHTML VBufBackend: only handle dispid_evmeth_onload and 
dispid_evmeth_onpropchange in invoke for our IDispatch implementation that 
handles events. Also call backend->update directly by getting rid of the 
onchange method. These changes should improve performance.

Affected #:  1 file

diff --git a/nvdaHelper/vbufBackends/mshtml/node.cpp 
b/nvdaHelper/vbufBackends/mshtml/node.cpp
index c3f02d1..84f44a8 100755
--- a/nvdaHelper/vbufBackends/mshtml/node.cpp
+++ b/nvdaHelper/vbufBackends/mshtml/node.cpp
@@ -89,11 +89,6 @@ class CDispatchChangeSink : public IDispatch {
                decBackendLibRefCount();
        }
 
-       void onChange() {
-               LOG_DEBUG(L"Marking storage node as invalid");
-               
this->storageNode->backend->invalidateSubtree(this->storageNode);
-       }
-
        HRESULT STDMETHODCALLTYPE IUnknown::QueryInterface(REFIID riid, void** 
pvpObject) {
                if(!pvpObject) return E_INVALIDARG;
                *pvpObject=NULL;
@@ -124,8 +119,8 @@ class CDispatchChangeSink : public IDispatch {
        }
 
        HRESULT STDMETHODCALLTYPE IDispatch::Invoke(DISPID  dispIdMember, 
REFIID  riid, LCID  lcid, WORD  wFlags, DISPPARAMS FAR*  pDispParams, VARIANT 
FAR*  pVarResult, EXCEPINFO FAR*  pExcepInfo, unsigned int FAR*  puArgErr) {
-               if(1) { 
//dispIdMember==DISPID_HTMLELEMENTEVENTS2_ONPROPERTYCHANGE||dispIdMember==DISPID_IHTMLELEMENT2_ONPROPERTYCHANGE)
 {
-                       this->onChange();
+               
if(dispIdMember==DISPID_EVMETH_ONPROPERTYCHANGE||dispIdMember==DISPID_EVMETH_ONLOAD)
 {
+                       
this->storageNode->backend->invalidateSubtree(this->storageNode);
                        return S_OK;
                }
                return E_FAIL;


https://bitbucket.org/nvdaaddonteam/nvda/commits/c1adf89116ed/
Changeset:   c1adf89116ed
Branch:      None
User:        mdcurran
Date:        2013-09-02 01:08:43
Summary:     Merge branch 't3397' into next. Incubates #3397.

Affected #:  3 files

diff --git a/nvdaHelper/vbufBackends/mshtml/mshtml.cpp 
b/nvdaHelper/vbufBackends/mshtml/mshtml.cpp
index 8f2adb6..e8590b4 100755
--- a/nvdaHelper/vbufBackends/mshtml/mshtml.cpp
+++ b/nvdaHelper/vbufBackends/mshtml/mshtml.cpp
@@ -561,8 +561,12 @@ inline void fillTextFormattingForNode(IHTMLDOMNode* 
pHTMLDOMNode, VBufStorage_fi
 inline void fillTextFormattingForTextNode(VBufStorage_controlFieldNode_t* 
parentNode, VBufStorage_textFieldNode_t* textNode)
        //text nodes don't support IHTMLElement2 interface, so using style 
information from parent node
        {
-       IHTMLElement2* 
pHTMLElement2=(static_cast<MshtmlVBufStorage_controlFieldNode_t*>(parentNode))->pHTMLElement2;
-       fillTextFormatting_helper(pHTMLElement2,textNode);
+       IHTMLElement2* pHTMLElement2=NULL; 
+       
static_cast<MshtmlVBufStorage_controlFieldNode_t*>(parentNode)->pHTMLDOMNode->QueryInterface(IID_IHTMLElement2,(void**)&pHTMLElement2);
+       if(pHTMLElement2) {
+               fillTextFormatting_helper(pHTMLElement2,textNode);
+               pHTMLElement2->Release();
+       }
 }
 
 const int TABLEHEADER_COLUMN = 0x1;
@@ -1227,11 +1231,11 @@ void MshtmlVBufBackend_t::render(VBufStorage_buffer_t* 
buffer, int docHandle, in
                LOG_DEBUG(L"Error getting document using WM_HTML_GETOBJECT");
                return;
        }
-IHTMLDOMNode* pHTMLDOMNode=NULL;
+       IHTMLDOMNode* pHTMLDOMNode=NULL;
        if(oldNode!=NULL) {
-               IHTMLElement2* 
pHTMLElement2=(static_cast<MshtmlVBufStorage_controlFieldNode_t*>(oldNode))->pHTMLElement2;
-               nhAssert(pHTMLElement2);
-               
pHTMLElement2->QueryInterface(IID_IHTMLDOMNode,(void**)&pHTMLDOMNode);
+               
pHTMLDOMNode=(static_cast<MshtmlVBufStorage_controlFieldNode_t*>(oldNode))->pHTMLDOMNode;
+               nhAssert(pHTMLDOMNode);
+               pHTMLDOMNode->AddRef();
        } else {
                IHTMLDocument3* pHTMLDocument3=NULL;
                
if(ObjectFromLresult(res,IID_IHTMLDocument3,0,(void**)&pHTMLDocument3)!=S_OK) {

diff --git a/nvdaHelper/vbufBackends/mshtml/node.cpp 
b/nvdaHelper/vbufBackends/mshtml/node.cpp
index c7d9b5b..84f44a8 100755
--- a/nvdaHelper/vbufBackends/mshtml/node.cpp
+++ b/nvdaHelper/vbufBackends/mshtml/node.cpp
@@ -14,8 +14,10 @@ http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 
 #include <list>
 #include <windows.h>
+#include <objbase.h>
 #include <oleidl.h>
 #include <mshtml.h>
+#include <mshtmdid.h>
 #include <common/log.h>
 #include "mshtml.h"
 #include "node.h"
@@ -25,33 +27,66 @@ using namespace std;
 class CDispatchChangeSink : public IDispatch {
        private:
        ULONG refCount;
-       bool hasFired;
+       MshtmlVBufStorage_controlFieldNode_t* storageNode;
+       IConnectionPoint* pConnectionPoint;
+       DWORD dwCookie;
 
        public:
-       MshtmlVBufStorage_controlFieldNode_t* storageNode;
-       bool allowDelete;
 
-       CDispatchChangeSink(MshtmlVBufStorage_controlFieldNode_t* storageNode):
-       refCount(1),
-       hasFired(false),
-       allowDelete(true) {
+       CDispatchChangeSink(MshtmlVBufStorage_controlFieldNode_t* storageNode) :
+       refCount(1), dwCookie(0), pConnectionPoint(NULL) {
                nhAssert(storageNode);
                this->storageNode=storageNode;
                incBackendLibRefCount();
        }
 
-       ~CDispatchChangeSink() {
-               decBackendLibRefCount();
+       BOOL connect(IHTMLDOMNode* pHTMLDOMNode, REFIID iid) {
+               if(dwCookie) {
+                       LOG_DEBUGWARNING(L"Already connected");
+                       return false;
+               }
+               IHTMLElement* pHTMLElement=NULL;
+               
pHTMLDOMNode->QueryInterface(IID_IHTMLElement,(void**)&pHTMLElement);
+               if(!pHTMLElement) {
+                       LOG_DEBUGWARNING(L"QueryInterface to IHTMLElement 
failed");
+                       return false;
+               }
+               IConnectionPointContainer* pConnectionPointContainer=NULL;
+               
pHTMLElement->QueryInterface(IID_IConnectionPointContainer,(void**)&pConnectionPointContainer);
+               pHTMLElement->Release();
+               if(!pConnectionPointContainer) {
+                       LOG_DEBUGWARNING(L"QueryInterface to 
IConnectionPointContainer failed");
+                       return false;
+               }
+               IConnectionPoint* pConnectionPoint=NULL;
+               
pConnectionPointContainer->FindConnectionPoint(iid,&pConnectionPoint);
+               pConnectionPointContainer->Release();
+               if(!pConnectionPoint) {
+                       return false;
+               }
+               DWORD dwCookie=0;
+               pConnectionPoint->Advise(this,&dwCookie);
+               if(!dwCookie) {
+                       pConnectionPoint->Release();
+                       return false;
+               }
+               this->pConnectionPoint=pConnectionPoint;
+               this->dwCookie=dwCookie;
+               return true;
        }
 
-       void onChange() {
-               if(hasFired||allowDelete) {
-                       return;
-               }
-               hasFired=true;
-               LOG_DEBUG(L"Marking storage node as invalid");
-               
this->storageNode->backend->invalidateSubtree(this->storageNode);
-               LOG_DEBUG(L"Done");
+       BOOL disconnect() {
+               if(this->dwCookie==0) return false;
+               this->pConnectionPoint->Unadvise(this->dwCookie);
+               this->dwCookie=0;
+               this->pConnectionPoint->Release();
+               this->pConnectionPoint=NULL;
+               return true;
+       }
+
+       ~CDispatchChangeSink() {
+               this->disconnect();
+               decBackendLibRefCount();
        }
 
        HRESULT STDMETHODCALLTYPE IUnknown::QueryInterface(REFIID riid, void** 
pvpObject) {
@@ -77,28 +112,18 @@ class CDispatchChangeSink : public IDispatch {
                if(this->refCount>0)
                        this->refCount--;
                if(this->refCount==0) {
-                       if (this->allowDelete) {
-                               delete this;
-                       } else {
-                               #ifdef DEBUG
-                               Beep(660,50);
-                               #endif
-                               LOG_DEBUG(L"refCount hit 0 before it should, 
not deleting, node info: " << this->storageNode->getDebugInfo());
-                       }
+                       delete this;
                        return 0;
                }
                return this->refCount;
        }
 
        HRESULT STDMETHODCALLTYPE IDispatch::Invoke(DISPID  dispIdMember, 
REFIID  riid, LCID  lcid, WORD  wFlags, DISPPARAMS FAR*  pDispParams, VARIANT 
FAR*  pVarResult, EXCEPINFO FAR*  pExcepInfo, unsigned int FAR*  puArgErr) {
-               if(dispIdMember==0) {
-                       LOG_DEBUG(L"calling onChange");
-                       this->onChange();
-                       LOG_DEBUG(L"Done, returning S_OK");
+               
if(dispIdMember==DISPID_EVMETH_ONPROPERTYCHANGE||dispIdMember==DISPID_EVMETH_ONLOAD)
 {
+                       
this->storageNode->backend->invalidateSubtree(this->storageNode);
                        return S_OK;
                }
-               LOG_DEBUG(L"invoke called with unknown member ID, returning 
E_INVALIDARG");
-               return E_INVALIDARG;
+               return E_FAIL;
        }
 
        HRESULT STDMETHODCALLTYPE  IDispatch::GetTypeInfoCount(UINT* count) {
@@ -238,48 +263,22 @@ class CHTMLChangeSink : public IHTMLChangeSink {
 };
 
 MshtmlVBufStorage_controlFieldNode_t::MshtmlVBufStorage_controlFieldNode_t(int 
docHandle, int ID, bool isBlock, MshtmlVBufBackend_t* backend, IHTMLDOMNode* 
pHTMLDOMNode,const wstring& lang): 
VBufStorage_controlFieldNode_t(docHandle,ID,isBlock), language(lang) {
-       VARIANT_BOOL varBool;
        nhAssert(backend);
        nhAssert(pHTMLDOMNode);
        this->backend=backend;
-       this->pHTMLElement2=NULL;
+       pHTMLDOMNode->AddRef();
+       this->pHTMLDOMNode=pHTMLDOMNode;
        this->propChangeSink=NULL;
        this->loadSink=NULL;
        this->pHTMLChangeSink=NULL;
        this->HTMLChangeSinkCookey=0;
-       
pHTMLDOMNode->QueryInterface(IID_IHTMLElement2,(void**)&(this->pHTMLElement2));
-       if(!this->pHTMLElement2) {
-               LOG_DEBUG(L"Could not queryInterface from IHTMLDOMNode to 
IHTMLElement2");
-       }
-       if(this->pHTMLElement2) {
-               CDispatchChangeSink* propChangeSink=new 
CDispatchChangeSink(this);
-               // It seems that IE 6 sometimes calls Release() once too many 
times.
-               // We don't want propChangeSink to be deleted until we're 
finished with it.
-               propChangeSink->allowDelete=false;
-               
if((pHTMLElement2->attachEvent(L"onpropertychange",propChangeSink,&varBool)==S_OK)&&varBool)
 {
-                       this->propChangeSink=propChangeSink;
-               } else {
-                       LOG_DEBUG(L"Error attaching onPropertyChange event sink 
to IHTMLElement2 at "<<pHTMLElement2);
-                       propChangeSink->allowDelete=true;
-                       propChangeSink->Release();
-               }
-       }
        BSTR nodeName=NULL;
        pHTMLDOMNode->get_nodeName(&nodeName);
-       
if(nodeName!=NULL&&(_wcsicmp(nodeName,L"frame")==0||_wcsicmp(nodeName,L"iframe")==0||_wcsicmp(nodeName,L"img")==0||_wcsicmp(nodeName,L"input")==0))
 {
-               if(this->pHTMLElement2) {
-                       CDispatchChangeSink* loadSink=new 
CDispatchChangeSink(this);
-                       // It seems that IE 6 sometimes calls Release() once 
too many times.
-                       // We don't want loadSink to be deleted until we're 
finished with it.
-                       loadSink->allowDelete=false;
-                       
if((pHTMLElement2->attachEvent(L"onload",loadSink,&varBool)==S_OK)&&varBool) {
-                               this->loadSink=loadSink;
-                       } else {
-                               LOG_DEBUG(L"Error attaching onload event sink 
to IHTMLElement2 at "<<pHTMLElement2);
-                               loadSink->allowDelete=true;
-                               loadSink->Release();
-                       }
-               }
+       CDispatchChangeSink* propChangeSink=new CDispatchChangeSink(this);
+       if(propChangeSink->connect(pHTMLDOMNode,IID_IDispatch)) {
+               this->propChangeSink=propChangeSink;
+       } else {
+               propChangeSink->Release();
        }
        
if(nodeName!=NULL&&(_wcsicmp(nodeName,L"body")==0||_wcsicmp(nodeName,L"frameset")==0))
 {
                IHTMLDOMNode2* pHTMLDOMNode2=NULL;
@@ -317,23 +316,15 @@ 
MshtmlVBufStorage_controlFieldNode_t::MshtmlVBufStorage_controlFieldNode_t(int d
  
 MshtmlVBufStorage_controlFieldNode_t::~MshtmlVBufStorage_controlFieldNode_t() {
        if(this->propChangeSink) {
-               nhAssert(this->pHTMLElement2);
-               
if(pHTMLElement2->detachEvent(L"onpropertychange",this->propChangeSink)!=S_OK) {
-                       LOG_DEBUG(L"Error detaching onpropertychange event sink 
from IHTMLElement2");
+               
if(!(static_cast<CDispatchChangeSink*>(this->propChangeSink)->disconnect())) {
+                       LOG_DEBUGWARNING(L"Failed to stop listening with 
HTMLElementEvents2 for node "<<this->getDebugInfo());
                }
-               
static_cast<CDispatchChangeSink*>(this->propChangeSink)->allowDelete=true;
                this->propChangeSink->Release();
+               this->propChangeSink=NULL;
        }
-       if(this->loadSink) {
-               nhAssert(this->pHTMLElement2);
-               if(pHTMLElement2->detachEvent(L"onload",this->loadSink)!=S_OK) {
-                       LOG_DEBUG(L"Error detaching onload event sink from 
IHTMLElement2");
-               }
-               
static_cast<CDispatchChangeSink*>(this->loadSink)->allowDelete=true;
-               this->loadSink->Release();
-       }
-       if(this->pHTMLElement2) {
-               this->pHTMLElement2->Release();
+       if(this->pHTMLDOMNode) {
+               this->pHTMLDOMNode->Release();
+               this->pHTMLDOMNode=NULL;
        }
        if(this->pHTMLChangeSink) {
                nhAssert(this->pMarkupContainer2);

diff --git a/nvdaHelper/vbufBackends/mshtml/node.h 
b/nvdaHelper/vbufBackends/mshtml/node.h
index 7fc7ae6..ec43b80 100755
--- a/nvdaHelper/vbufBackends/mshtml/node.h
+++ b/nvdaHelper/vbufBackends/mshtml/node.h
@@ -24,7 +24,7 @@ class MshtmlVBufStorage_controlFieldNode_t : public 
VBufStorage_controlFieldNode
 
        public:
        MshtmlVBufBackend_t* backend;
-       IHTMLElement2* pHTMLElement2;
+       IHTMLDOMNode* pHTMLDOMNode;
        IDispatch* propChangeSink;
        IDispatch* loadSink;
        IMarkupContainer2* pMarkupContainer2;


https://bitbucket.org/nvdaaddonteam/nvda/commits/81cb363df6ee/
Changeset:   81cb363df6ee
Branch:      None
User:        jteh
Date:        2013-09-02 03:51:46
Summary:     In Poedit, messages that are untranslated or fuzzy are again 
indicated with an asterisk and a beep.

This broke due to the removal of the empty control field in display model 
output.
Also, optimise slightly by using UNIT_CHARACTER instead of UINT_LINE, since 
we're only looking at the first character anyway.
Re #3485.

Affected #:  1 file

diff --git a/source/appModules/poedit.py b/source/appModules/poedit.py
index c6e0c69..78c4d34 100644
--- a/source/appModules/poedit.py
+++ b/source/appModules/poedit.py
@@ -1,6 +1,6 @@
 #appModules/poedit.py
 #A part of NonVisual Desktop Access (NVDA)
-#Copyright (C) 2012 Mesar Hameed <mhameed@xxxxxxxxxxxxx>
+#Copyright (C) 2012-2013 Mesar Hameed, NV Access Limited
 #This file is covered by the GNU General Public License.
 #See the file COPYING for more details.
 
@@ -130,10 +130,10 @@ class PoeditListItem(sysListView32.ListItem):
 
        def _get_isBold(self):
                
info=displayModel.DisplayModelTextInfo(self,position=textInfos.POSITION_FIRST)
-               info.expand(textInfos.UNIT_LINE)
+               info.expand(textInfos.UNIT_CHARACTER)
                fields=info.getTextWithFields()
                try:
-                       return fields[1].field['bold']
+                       return fields[0].field['bold']
                except:
                        return False
 


https://bitbucket.org/nvdaaddonteam/nvda/commits/546fa302d905/
Changeset:   546fa302d905
Branch:      None
User:        jteh
Date:        2013-09-02 04:12:38
Summary:     Update messages and translator comments to reflect the rename of 
Automatic comments to Notes for translators in Poedit.

Affected #:  1 file

diff --git a/source/appModules/poedit.py b/source/appModules/poedit.py
index 78c4d34..4c84a64 100644
--- a/source/appModules/poedit.py
+++ b/source/appModules/poedit.py
@@ -73,15 +73,14 @@ class AppModule(appModuleHandler.AppModule):
                                ui.message(obj.name + " " + obj.value)
                        except:
                                # Translators: this message is reported when 
there are no 
-                               # comments to be presented to the user in the 
automatic 
-                               # comments window in poedit.
-                               ui.message(_("No automatic comments."))
+                               # notes for translators to be presented to the 
user in Poedit.
+                               ui.message(_("No notes for translators."))
                else:
                        # Translators: this message is reported when NVDA is 
unable to find 
-                       # the 'automatic comments' window in poedit.
-                       ui.message(_("Could not find automatic comments 
window."))
+                       # the 'Notes for translators' window in poedit.
+                       ui.message(_("Could not find Notes for translators 
window."))
        # Translators: The description of an NVDA command for Poedit.
-       script_reportAutoCommentsWindow.__doc__ = _("Reports any comments in 
the automatic comments window")
+       script_reportAutoCommentsWindow.__doc__ = _("Reports any notes for 
translators")
 
        def script_reportCommentsWindow(self,gesture):
                obj = fetchObject(api.getForegroundObject(), [2, 0, 1, 0, 1, 0, 
1, 0])


https://bitbucket.org/nvdaaddonteam/nvda/commits/59d71f580a42/
Changeset:   59d71f580a42
Branch:      None
User:        jteh
Date:        2013-09-02 04:38:43
Summary:     poedit: Fix reporting of translator added comments broken due to 
changes in Poedit.

Re #3485.

Affected #:  1 file

diff --git a/source/appModules/poedit.py b/source/appModules/poedit.py
index 4c84a64..94e947c 100644
--- a/source/appModules/poedit.py
+++ b/source/appModules/poedit.py
@@ -15,6 +15,9 @@ import textInfos
 import tones
 import ui
 from NVDAObjects.IAccessible import sysListView32
+import windowUtils
+import NVDAObjects.IAccessible
+import winUser
 
 
 def getPath(obj, ancestor):
@@ -83,23 +86,22 @@ class AppModule(appModuleHandler.AppModule):
        script_reportAutoCommentsWindow.__doc__ = _("Reports any notes for 
translators")
 
        def script_reportCommentsWindow(self,gesture):
-               obj = fetchObject(api.getForegroundObject(), [2, 0, 1, 0, 1, 0, 
1, 0])
-               # if it isnt in the normal location, try to find it in the
-               # location of the automatic window.
-               if not obj: 
-                       obj = fetchObject(api.getForegroundObject(), [2, 0, 1, 
0, 1, 0, 0, 0])
-               if obj and obj.windowControlID == 105:
-                       try:
-                               ui.message(obj.name + " " + obj.value)
-                       except:
-                               # Translators: this message is reported when 
there are no
-                               # comments to be presented to the user in the 
translator
-                               # comments window in poedit.
-                               ui.message(_("No comment."))
-               else:
+               try:
+                       obj = NVDAObjects.IAccessible.getNVDAObjectFromEvent(
+                               
windowUtils.findDescendantWindow(api.getForegroundObject().windowHandle, 
visible=True, controlID=104),
+                               winUser.OBJID_CLIENT, 0)
+               except LookupError:
                        # Translators: this message is reported when NVDA is 
unable to find
                        # the 'comments' window in poedit.
                        ui.message(_("Could not find comment window."))
+                       return None
+               try:
+                       ui.message(obj.name + " " + obj.value)
+               except:
+                       # Translators: this message is reported when there are 
no
+                       # comments to be presented to the user in the translator
+                       # comments window in poedit.
+                       ui.message(_("No comment."))
        # Translators: The description of an NVDA command for Poedit.
        script_reportAutoCommentsWindow.__doc__ = _("Reports any comments in 
the comments window")
 


https://bitbucket.org/nvdaaddonteam/nvda/commits/dd9a00180f5c/
Changeset:   dd9a00180f5c
Branch:      None
User:        jteh
Date:        2013-09-02 07:00:38
Summary:     poedit: Fix the labels for the editable text fields in Poedit 
1.5.7.

The control IDs have changed and some of them are duplicated. The label is 
always just above the field on the screen, so rather than using control IDs, 
grab the label object and use that to label the field.
Re #3485.

Affected #:  1 file

diff --git a/source/appModules/poedit.py b/source/appModules/poedit.py
index 94e947c..cc0bcd3 100644
--- a/source/appModules/poedit.py
+++ b/source/appModules/poedit.py
@@ -113,19 +113,17 @@ class AppModule(appModuleHandler.AppModule):
        def chooseNVDAObjectOverlayClasses(self, obj, clsList):
                if "SysListView32" in obj.windowClassName and 
obj.role==controlTypes.ROLE_LISTITEM:
                        clsList.insert(0,PoeditListItem)
-               if obj.role == controlTypes.ROLE_EDITABLETEXT:
-                       if obj.windowControlID == 102:
-                               # Translators: Automatic comments is the name 
of the poedit 
-                               # window that displays comments extracted from 
code.
-                               obj.name =  _("Automatic comments:")
-                       if obj.windowControlID == 104:
-                               # Translators: this is the label for the edit 
area in poedit 
-                               # that contains a translation.
-                               obj.name = _("Translation:")
-                       if obj.windowControlID == 105:
-                               # Translators: 'comments:' is the name of the 
poedit window 
-                               # that displays comments entered by the 
translator.
-                               obj.name = _("Comments:")
+
+       def event_NVDAObject_init(self, obj):
+               if obj.role == controlTypes.ROLE_EDITABLETEXT and 
controlTypes.STATE_MULTILINE in obj.states and obj.isInForeground:
+                       # Oleacc often gets the name wrong.
+                       # The label object is positioned just above the field 
on the screen.
+                       l, t, w, h = obj.location
+                       try:
+                               obj.name = 
NVDAObjects.NVDAObject.objectFromPoint(l + 10, t - 10).name
+                       except AttributeError:
+                               pass
+                       return
 
 class PoeditListItem(sysListView32.ListItem):
 


https://bitbucket.org/nvdaaddonteam/nvda/commits/312b69972c5f/
Changeset:   312b69972c5f
Branch:      None
User:        jteh
Date:        2013-09-02 07:18:14
Summary:     poedit: Fix reporting of notes for translators for Poedit 1.5.7.

Re #3485.

Affected #:  1 file

diff --git a/source/appModules/poedit.py b/source/appModules/poedit.py
index cc0bcd3..3d21ed1 100644
--- a/source/appModules/poedit.py
+++ b/source/appModules/poedit.py
@@ -68,10 +68,15 @@ def fetchObject(obj, path):
 class AppModule(appModuleHandler.AppModule):
 
        def script_reportAutoCommentsWindow(self,gesture):
-               obj = fetchObject(api.getForegroundObject(), [2, 0, 1, 0, 1, 0, 
0, 0])
-               # check the controlid, because in certain situations 
-               # autoComments and comment windows change places.
-               if obj and obj.windowControlID == 102:
+               obj = fetchObject(api.getForegroundObject(), [2, 0, 1, 0, 1, 0, 
1])
+               if obj and obj.windowControlID != 101:
+                       try:
+                               obj = obj.next.firstChild
+                       except AttributeError:
+                               obj = None
+               elif obj:
+                       obj = obj.firstChild
+               if obj:
                        try:
                                ui.message(obj.name + " " + obj.value)
                        except:


https://bitbucket.org/nvdaaddonteam/nvda/commits/c3b74e24281c/
Changeset:   c3b74e24281c
Branch:      None
User:        jteh
Date:        2013-09-02 07:20:10
Summary:     poedit: Fix script docstrings.

Affected #:  1 file

diff --git a/source/appModules/poedit.py b/source/appModules/poedit.py
index 3d21ed1..995618d 100644
--- a/source/appModules/poedit.py
+++ b/source/appModules/poedit.py
@@ -108,7 +108,7 @@ class AppModule(appModuleHandler.AppModule):
                        # comments window in poedit.
                        ui.message(_("No comment."))
        # Translators: The description of an NVDA command for Poedit.
-       script_reportAutoCommentsWindow.__doc__ = _("Reports any comments in 
the comments window")
+       script_reportCommentsWindow.__doc__ = _("Reports any comments in the 
comments window")
 
        __gestures = {
                "kb:control+shift+c": "reportCommentsWindow",


https://bitbucket.org/nvdaaddonteam/nvda/commits/87fab437eeb4/
Changeset:   87fab437eeb4
Branch:      None
User:        jteh
Date:        2013-09-02 07:20:52
Summary:     Update User Guide.

Affected #:  1 file

diff --git a/user_docs/en/userGuide.t2t b/user_docs/en/userGuide.t2t
index 3e1b68d..1091803 100644
--- a/user_docs/en/userGuide.t2t
+++ b/user_docs/en/userGuide.t2t
@@ -554,7 +554,7 @@ Note: The report remaining time shortcut works only with 
the default formatting
 %kc:beginInclude
 || Name | Key | Description |
 | Report Comments Window | control+shift+c | Reports any comments in the 
comments window. |
-| Report automatic comments window | control+shift+a | Reports any comments in 
the automatic comments window. |
+| Report notes for translators | control+shift+a | Reports any notes for 
translators. |
 %kc:endInclude
 
 + Configuring NVDA +


https://bitbucket.org/nvdaaddonteam/nvda/commits/5c5cc2ed89b8/
Changeset:   5c5cc2ed89b8
Branch:      None
User:        mdcurran
Date:        2013-09-02 07:50:44
Summary:     Merge branch 'master' into t1532

Affected #:  38 files

diff --git a/contributors.txt b/contributors.txt
index ed1df41..d18fdd4 100644
--- a/contributors.txt
+++ b/contributors.txt
@@ -133,3 +133,4 @@ Rui Fontes (Tiflotecnia, lda.)
 Kevin Scannell
 Hamid Rezaey
 Bue Vester-Andersen
+Yogesh Kumar

diff --git a/include/minhook b/include/minhook
index ff0888d..e21b54a 160000
--- a/include/minhook
+++ b/include/minhook
@@ -1 +1 @@
-Subproject commit ff0888de0d34399e69155481f70b9e11b771eebe
+Subproject commit e21b54a88190ca477ed73fb7ab24203dfaef28a0

diff --git a/nvdaHelper/minHook/dllmain.cpp b/nvdaHelper/minHook/dllmain.cpp
new file mode 100644
index 0000000..25d6823
--- /dev/null
+++ b/nvdaHelper/minHook/dllmain.cpp
@@ -0,0 +1,28 @@
+/*
+This file is a part of the NVDA project.
+URL: http://www.nvda-project.org/
+Copyright 2006-2010 NVDA contributers.
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License version 2.0, as 
published by
+    the Free Software Foundation.
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+This license can be found at:
+http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+*/
+
+#include <windows.h>
+#include <minhook.h>
+
+BOOL WINAPI DllMain(HINSTANCE hModule,DWORD reason,LPVOID lpReserved) {
+       //Process exiting, we must clean up any pending hooks
+       if(reason==DLL_PROCESS_DETACH&&lpReserved) {
+               if(MH_DisableAllHooks()!=MH_ERROR_NOT_INITIALIZED) {
+                       //Give enough time for all hook functions to complete.
+                       Sleep(250);
+                       MH_Uninitialize();
+               }
+       }
+       return 1;
+}

diff --git a/nvdaHelper/minHook/minHook.def b/nvdaHelper/minHook/minHook.def
deleted file mode 100644
index 75e1b07..0000000
--- a/nvdaHelper/minHook/minHook.def
+++ /dev/null
@@ -1,8 +0,0 @@
-EXPORTS
-       MH_Initialize
-       MH_Uninitialize
-       MH_CreateHook
-       MH_EnableHook
-       MH_DisableHook
-       MH_EnableAllHooks
-       MH_DisableAllHooks

diff --git a/nvdaHelper/minHook/newHook.cpp b/nvdaHelper/minHook/newHook.cpp
deleted file mode 100644
index 06778b8..0000000
--- a/nvdaHelper/minHook/newHook.cpp
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
-This file is a part of the NVDA project.
-URL: http://www.nvda-project.org/
-Copyright 2006-2010 NVDA contributers.
-    This program is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License version 2.0, as 
published by
-    the Free Software Foundation.
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-This license can be found at:
-http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
-*/
-
-#include <windows.h>
-#include "hook.cpp"
-
-using namespace MinHook;
-
-//Based on MH_EnableHook from minHook
-MH_STATUS _doAllHooks(bool enable) {
-       CriticalSection::ScopedLock lock(gCS);
-       if (!gIsInitialized) {
-               return MH_ERROR_NOT_INITIALIZED;
-       }
-       std::vector<uintptr_t> oldIPs;
-       std::vector<uintptr_t> newIPs;
-       for(std::vector<HOOK_ENTRY>::iterator 
hooksIter=gHooks.begin();hooksIter!=gHooks.end();++hooksIter) {
-               
oldIPs.insert(oldIPs.end(),hooksIter->oldIPs.begin(),hooksIter->oldIPs.end());
-               
newIPs.insert(newIPs.end(),hooksIter->newIPs.begin(),hooksIter->newIPs.end());
-       }
-       {
-               ScopedThreadExclusive tex(oldIPs, newIPs);
-               for(std::vector<HOOK_ENTRY>::iterator 
hooksIter=gHooks.begin();hooksIter!=gHooks.end();++hooksIter) {
-                       HOOK_ENTRY*pHook=&(*hooksIter);
-                       if (pHook->isEnabled==enable) continue;
-                       DWORD oldProtect;
-                       if (!VirtualProtect(pHook->pTarget, sizeof(JMP_REL), 
PAGE_EXECUTE_READWRITE, &oldProtect)) {
-                               return MH_ERROR_MEMORY_PROTECT;
-                       }
-                       if(enable) {
-#if defined _M_X64
-                               WriteRelativeJump(pHook->pTarget, 
pHook->pRelay);
-#elif defined _M_IX86
-                               WriteRelativeJump(pHook->pTarget, 
pHook->pDetour);
-#endif
-                       } else {
-                               memcpy(pHook->pTarget, pHook->pBackup, 
sizeof(JMP_REL));
-                       }
-                       VirtualProtect(pHook->pTarget, sizeof(JMP_REL), 
oldProtect, &oldProtect);
-                               pHook->isEnabled = enable;
-               }
-       }
-       return MH_OK;
-}
-
-MH_STATUS MH_EnableAllHooks() {
-       return _doAllHooks(true);
-}
-
-MH_STATUS MH_DisableAllHooks() {
-       return _doAllHooks(false);
-}
-
-BOOL WINAPI DllMain(HINSTANCE hModule,DWORD reason,LPVOID lpReserved) {
-       //Process exiting, we must clean up any pending hooks
-       if(reason==DLL_PROCESS_DETACH&&lpReserved) {
-               if(gIsInitialized) {
-                       MH_DisableAllHooks();
-                       //Give enough time for all hook functions to complete.
-                       Sleep(250);
-                       MH_Uninitialize();
-               }
-       }
-       return 1;
-}

diff --git a/nvdaHelper/minHook/newMinHook.h b/nvdaHelper/minHook/newMinHook.h
deleted file mode 100644
index 831d612..0000000
--- a/nvdaHelper/minHook/newMinHook.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
-This file is a part of the NVDA project.
-URL: http://www.nvda-project.org/
-Copyright 2006-2010 NVDA contributers.
-    This program is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License version 2.0, as 
published by
-    the Free Software Foundation.
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-This license can be found at:
-http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
-*/
-
-#ifndef NVDAHELPER_NEWMINHOOK_H
-#define NVDAHELPER_NEWMINHOOK_H
-
-#include <minhook/libMinHook/minHook.h>
-
-//Some new batch calls
-MH_STATUS MH_EnableAllHooks();
-MH_STATUS MH_DisableAllHooks();
-
-//function pointer typedefs for all minHook functions for use with 
getProcAddress
-typedef MH_STATUS(WINAPI *MH_Initialize_funcType)();
-typedef MH_STATUS(WINAPI *MH_Uninitialize_funcType)();
-typedef MH_STATUS(WINAPI *MH_CreateHook_funcType)(void*,void*,void**);
-typedef MH_STATUS(WINAPI *MH_EnableHook_funcType)(void*);
-typedef MH_STATUS(WINAPI *MH_DisableHook_funcType)(void*);
-typedef MH_STATUS(*MH_EnableAllHooks_funcType)();
-typedef MH_STATUS(*MH_DisableAllHooks_funcType)();
-
-#endif

diff --git a/nvdaHelper/minHook/sconscript b/nvdaHelper/minHook/sconscript
index c076a68..497a242 100644
--- a/nvdaHelper/minHook/sconscript
+++ b/nvdaHelper/minHook/sconscript
@@ -2,19 +2,27 @@ Import([
        'env',
 ])
 
+
+minhookPath=Dir('#include/minhook')
+env=env.Clone(CPPPATH=minhookPath.Dir('include'))
+
 HDESourceFile='HDE64/src/HDE64.c' if env['TARGET_ARCH']=='x86_64' else 
'HDE32/HDE32.c'
 
 sourceFiles=[
        HDESourceFile,
        'buffer.cpp',
        'export.cpp',
+       'hook.cpp',
        'thread.cpp',
        'trampoline.cpp',
 ]
 
-objFiles=[env.Object('_minHook_%s.obj'%x.replace('/','_'),'#/include/minhook/libMinHook/src/%s'%x)
 for x in sourceFiles]
-objFiles.append(env.Object('newHook.cpp',CPPPATH=['#include/minhook/libMinHook/src','${CPPPATH}']))
+objFiles=[env.Object('_minHook_%s.obj'%x.replace('/','_'),minhookPath.File('src/%s'%x))
 for x in sourceFiles]
+objFiles.append('dllmain.cpp')
 
-minHookLib=env.SharedLibrary('minHook',objFiles+['minHook.def'])
+minHookLib=env.SharedLibrary(
+       target='minHook',
+       source=objFiles+[minhookPath.File('dll_resources/minHook.def')],
+)
 
 Return('minHookLib')

diff --git a/nvdaHelper/remote/apiHook.cpp b/nvdaHelper/remote/apiHook.cpp
index 3a5955b..0a3430b 100644
--- a/nvdaHelper/remote/apiHook.cpp
+++ b/nvdaHelper/remote/apiHook.cpp
@@ -17,7 +17,7 @@ http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 #define WIN32_LEAN_AND_MEAN 
 #include <windows.h>
 #include <delayimp.h>
-#include <minHook/newMinHook.h>
+#include <minhook/include/minhook.h>
 #include "nvdaControllerInternal.h"
 #include <common/log.h>
 #include "dllmain.h"
@@ -33,6 +33,15 @@ functionSet_t g_hookedFunctions;
 HMODULE minhookLibHandle=NULL;
 bool error_setNHFP=false;
 
+//function pointer typedefs for all minHook functions for use with 
getProcAddress
+typedef MH_STATUS(WINAPI *MH_Initialize_funcType)();
+typedef MH_STATUS(WINAPI *MH_Uninitialize_funcType)();
+typedef MH_STATUS(WINAPI *MH_CreateHook_funcType)(void*,void*,void**);
+typedef MH_STATUS(WINAPI *MH_EnableHook_funcType)(void*);
+typedef MH_STATUS(WINAPI *MH_DisableHook_funcType)(void*);
+typedef MH_STATUS(*MH_EnableAllHooks_funcType)();
+typedef MH_STATUS(*MH_DisableAllHooks_funcType)();
+
 #define defMHFP(funcName) funcName##_funcType funcName##_fp=NULL
 
 #define setMHFP(funcName) {\

diff --git a/nvdaHelper/remote/winword.cpp b/nvdaHelper/remote/winword.cpp
index 9c2aaf5..f4ddbba 100644
--- a/nvdaHelper/remote/winword.cpp
+++ b/nvdaHelper/remote/winword.cpp
@@ -35,6 +35,7 @@ using namespace std;
 #define wdDISPID_SELECTION_RANGE 400
 #define wdDISPID_SELECTION_SETRANGE 100
 #define wdDISPID_SELECTION_STARTISACTIVE 404
+#define wdDISPID_RANGE_INRANGE 126
 #define wdDISPID_RANGE_STORYTYPE 7
 #define wdDISPID_RANGE_MOVEEND 111
 #define wdDISPID_RANGE_COLLAPSE 101
@@ -47,6 +48,19 @@ using namespace std;
 #define wdDISPID_RANGE_INFORMATION 313
 #define wdDISPID_RANGE_STYLE 151
 #define wdDISPID_RANGE_LANGUAGEID 153
+#define wdDISPID_RANGE_DUPLICATE 6
+#define wdDISPID_RANGE_FORMFIELDS 65
+#define wdDISPID_RANGE_CONTENTCONTROLS 424
+#define wdDISPID_FORMFIELDS_ITEM 0
+#define wdDISPID_FORMFIELD_RANGE 17
+#define wdDISPID_FORMFIELD_TYPE 0
+#define wdDISPID_FORMFIELD_RESULT 10
+#define wdDISPID_FORMFIELD_STATUSTEXT 8
+#define wdDISPID_CONTENTCONTROLS_ITEM 0
+#define wdDISPID_CONTENTCONTROL_RANGE 1
+#define wdDISPID_CONTENTCONTROL_TYPE 5
+#define wdDISPID_CONTENTCONTROL_CHECKED 28
+#define wdDISPID_CONTENTCONTROL_TITLE 12
 #define wdDISPID_STYLE_NAMELOCAL 0
 #define wdDISPID_RANGE_SPELLINGERRORS 316
 #define wdDISPID_SPELLINGERRORS_COUNT 1
@@ -102,6 +116,7 @@ using namespace std;
 
 #define wdCharacter 1
 #define wdWord 2
+#define wdParagraph 4
 #define wdLine 5
 #define wdCharacterFormatting 13
 
@@ -192,6 +207,70 @@ void winword_expandToLine_helper(HWND hwnd, 
winword_expandToLine_args* args) {
        
_com_dispatch_raw_propput(pDispatchApplication,wdDISPID_APPLICATION_SCREENUPDATING,VT_BOOL,true);
 }
 
+BOOL generateFormFieldXML(IDispatch* pDispatchRange, wostringstream& 
XMLStream, int& chunkEnd) {
+       IDispatchPtr pDispatchRange2=NULL;
+       
if(_com_dispatch_raw_propget(pDispatchRange,wdDISPID_RANGE_DUPLICATE,VT_DISPATCH,&pDispatchRange2)!=S_OK||!pDispatchRange2)
 {
+               return false;
+       }
+       
_com_dispatch_raw_method(pDispatchRange2,wdDISPID_RANGE_EXPAND,DISPATCH_METHOD,VT_EMPTY,NULL,L"\x0003",wdParagraph,1);
+       BOOL foundFormField=false;
+       IDispatchPtr pDispatchFormFields=NULL;
+       
_com_dispatch_raw_propget(pDispatchRange2,wdDISPID_RANGE_FORMFIELDS,VT_DISPATCH,&pDispatchFormFields);
+       if(pDispatchFormFields) for(int 
count=1;!foundFormField&&count<100;++count) {
+               IDispatchPtr pDispatchFormField=NULL;
+               
if(_com_dispatch_raw_method(pDispatchFormFields,wdDISPID_FORMFIELDS_ITEM,DISPATCH_METHOD,VT_DISPATCH,&pDispatchFormField,L"\x0003",count)!=S_OK||!pDispatchFormField)
 {
+                       break;
+               }
+               IDispatchPtr pDispatchFormFieldRange=NULL;
+               
if(_com_dispatch_raw_propget(pDispatchFormField,wdDISPID_FORMFIELD_RANGE,VT_DISPATCH,&pDispatchFormFieldRange)!=S_OK||!pDispatchFormFieldRange)
 {
+                       break;
+               }
+               
if(_com_dispatch_raw_method(pDispatchRange,wdDISPID_RANGE_INRANGE,DISPATCH_METHOD,VT_BOOL,&foundFormField,L"\x0009",pDispatchFormFieldRange)!=S_OK||!foundFormField)
 {
+                       continue;
+               }
+               long fieldType=-1;
+               
_com_dispatch_raw_propget(pDispatchFormField,wdDISPID_FORMFIELD_TYPE,VT_I4,&fieldType);
+               BSTR fieldResult=NULL;
+               
_com_dispatch_raw_propget(pDispatchFormField,wdDISPID_FORMFIELD_RESULT,VT_BSTR,&fieldResult);
+               BSTR fieldStatusText=NULL;
+               
_com_dispatch_raw_propget(pDispatchFormField,wdDISPID_FORMFIELD_STATUSTEXT,VT_BSTR,&fieldStatusText);
+               XMLStream<<L"<control wdFieldType=\""<<fieldType<<L"\" 
wdFieldResult=\""<<(fieldResult?fieldResult:L"")<<L"\" 
wdFieldStatusText=\""<<(fieldStatusText?fieldStatusText:L"")<<L"\">";
+               if(fieldResult) SysFreeString(fieldResult);
+               if(fieldStatusText) SysFreeString(fieldStatusText);
+               
_com_dispatch_raw_propget(pDispatchFormFieldRange,wdDISPID_RANGE_END,VT_I4,&chunkEnd);
+               
_com_dispatch_raw_propput(pDispatchRange,wdDISPID_RANGE_END,VT_I4,chunkEnd);
+               break;
+       }
+       if(foundFormField) return true;
+       IDispatchPtr pDispatchContentControls=NULL;
+       
_com_dispatch_raw_propget(pDispatchRange2,wdDISPID_RANGE_CONTENTCONTROLS,VT_DISPATCH,&pDispatchContentControls);
+       if(pDispatchContentControls)for(int 
count=1;!foundFormField&&count<100;++count) {
+               IDispatchPtr pDispatchContentControl=NULL;
+               
if(_com_dispatch_raw_method(pDispatchContentControls,wdDISPID_CONTENTCONTROLS_ITEM,DISPATCH_METHOD,VT_DISPATCH,&pDispatchContentControl,L"\x0003",count)!=S_OK||!pDispatchContentControl)
 {
+                       break;
+               }
+               IDispatchPtr pDispatchContentControlRange=NULL;
+               
if(_com_dispatch_raw_propget(pDispatchContentControl,wdDISPID_CONTENTCONTROL_RANGE,VT_DISPATCH,&pDispatchContentControlRange)!=S_OK||!pDispatchContentControlRange)
 {
+                       break;
+               }
+               
if(_com_dispatch_raw_method(pDispatchRange,wdDISPID_RANGE_INRANGE,DISPATCH_METHOD,VT_BOOL,&foundFormField,L"\x0009",pDispatchContentControlRange)!=S_OK||!foundFormField)
 {
+                       continue;
+               }
+               long fieldType=-1;
+               
_com_dispatch_raw_propget(pDispatchContentControl,wdDISPID_CONTENTCONTROL_TYPE,VT_I4,&fieldType);
+               BOOL fieldChecked=false;
+               
_com_dispatch_raw_propget(pDispatchContentControl,wdDISPID_CONTENTCONTROL_CHECKED,VT_BOOL,&fieldChecked);
+               BSTR fieldTitle=NULL;
+               
_com_dispatch_raw_propget(pDispatchContentControl,wdDISPID_CONTENTCONTROL_TITLE,VT_BSTR,&fieldTitle);
+               XMLStream<<L"<control wdContentControlType=\""<<fieldType<<L"\" 
wdContentControlChecked=\""<<fieldChecked<<L"\" 
wdContentControlTitle=\""<<(fieldTitle?fieldTitle:L"")<<L"\">";
+               if(fieldTitle) SysFreeString(fieldTitle);
+               
_com_dispatch_raw_propget(pDispatchContentControlRange,wdDISPID_RANGE_END,VT_I4,&chunkEnd);
+               
_com_dispatch_raw_propput(pDispatchRange,wdDISPID_RANGE_END,VT_I4,chunkEnd);
+               break;
+       }
+       return foundFormField;
+}
+
 int generateHeadingXML(IDispatch* pDispatchRange, wostringstream& XMLStream) {
        IDispatchPtr pDispatchParagraphs=NULL;
        IDispatchPtr pDispatchParagraph=NULL;
@@ -544,15 +623,27 @@ void winword_getTextInRange_helper(HWND hwnd, 
winword_getTextInRange_args* args)
        int chunkEndOffset=chunkStartOffset;
        int unitsMoved=0;
        BSTR text=NULL;
+       if(initialFormatConfig&formatConfig_reportTables) {
+               
neededClosingControlTagCount+=generateTableXML(pDispatchRange,args->startOffset,args->endOffset,XMLStream);
+       }
+       if(initialFormatConfig&formatConfig_reportHeadings) {
+               
neededClosingControlTagCount+=generateHeadingXML(pDispatchRange,XMLStream);
+       }
+       
generateXMLAttribsForFormatting(pDispatchRange,chunkStartOffset,chunkEndOffset,initialFormatConfig,initialFormatAttribsStream);
+       bool firstLoop=true;
        //Walk the range from the given start to end by characterFormatting or 
word units
        //And grab any text and formatting and generate appropriate xml
-       bool firstLoop=true;
        do {
-               //Move the end by word
-               
if(_com_dispatch_raw_method(pDispatchRange,wdDISPID_RANGE_MOVEEND,DISPATCH_METHOD,VT_I4,&unitsMoved,L"\x0003\x0003",wdWord,1)!=S_OK||unitsMoved<=0)
 {
-                       break;
+               //generated form field xml if in a form field
+               //Also automatically extends the range and chunkEndOffset to 
the end of the field
+               BOOL 
isFormField=generateFormFieldXML(pDispatchRange,XMLStream,chunkEndOffset);
+               if(!isFormField) {
+                       //Move the end by word
+                       
if(_com_dispatch_raw_method(pDispatchRange,wdDISPID_RANGE_MOVEEND,DISPATCH_METHOD,VT_I4,&unitsMoved,L"\x0003\x0003",wdWord,1)!=S_OK||unitsMoved<=0)
 {
+                               break;
+                       }
+                       
_com_dispatch_raw_propget(pDispatchRange,wdDISPID_RANGE_END,VT_I4,&chunkEndOffset);
                }
-               
_com_dispatch_raw_propget(pDispatchRange,wdDISPID_RANGE_END,VT_I4,&chunkEndOffset);
                //Make sure  that the end is not past the requested end after 
the move
                if(chunkEndOffset>(args->endOffset)) {
                        
_com_dispatch_raw_propput(pDispatchRange,wdDISPID_RANGE_END,VT_I4,args->endOffset);
@@ -564,34 +655,29 @@ void winword_getTextInRange_helper(HWND hwnd, 
winword_getTextInRange_args* args)
                        break;
                }
                
_com_dispatch_raw_propget(pDispatchRange,wdDISPID_RANGE_TEXT,VT_BSTR,&text);
+               if(!text) SysAllocString(L"");
                if(text) {
-                       //Force a new chunk before and after control+b (note 
characters)
                        int noteCharOffset=-1;
-                       for(int i=0;text[i]!=L'\0';++i) {
-                               if(text[i]==L'\x0002') {
-                                       noteCharOffset=i;
-                                       if(i==0) text[i]=L' ';
-                                       break;
-                               }
-                       }
-                       bool isNoteChar=(noteCharOffset==0);
-                       if(noteCharOffset==0) noteCharOffset=1;
-                       if(noteCharOffset>0) {
-                               text[noteCharOffset]=L'\0';
-                               
_com_dispatch_raw_method(pDispatchRange,wdDISPID_RANGE_COLLAPSE,DISPATCH_METHOD,VT_EMPTY,NULL,L"\x0003",wdCollapseStart);
-                               
if(_com_dispatch_raw_method(pDispatchRange,wdDISPID_RANGE_MOVEEND,DISPATCH_METHOD,VT_I4,&unitsMoved,L"\x0003\x0003",wdCharacter,noteCharOffset)!=S_OK||unitsMoved<=0)
 {
-                                       break;
-                               }
-                               
_com_dispatch_raw_propget(pDispatchRange,wdDISPID_RANGE_END,VT_I4,&chunkEndOffset);
-                       }
-                       if(firstLoop) {
-                               
if(initialFormatConfig&formatConfig_reportTables) {
-                                       
neededClosingControlTagCount+=generateTableXML(pDispatchRange,args->startOffset,args->endOffset,XMLStream);
+                       bool isNoteChar=false;
+                       if(!isFormField) {
+                               //Force a new chunk before and after control+b 
(note characters)
+                               for(int i=0;text[i]!=L'\0';++i) {
+                                       if(text[i]==L'\x0002') {
+                                               noteCharOffset=i;
+                                               if(i==0) text[i]=L' ';
+                                               break;
+                                       }
                                }
-                               
if(initialFormatConfig&formatConfig_reportHeadings) {
-                                       
neededClosingControlTagCount+=generateHeadingXML(pDispatchRange,XMLStream);
+                               isNoteChar=(noteCharOffset==0);
+                               if(noteCharOffset==0) noteCharOffset=1;
+                               if(noteCharOffset>0) {
+                                       text[noteCharOffset]=L'\0';
+                                       
_com_dispatch_raw_method(pDispatchRange,wdDISPID_RANGE_COLLAPSE,DISPATCH_METHOD,VT_EMPTY,NULL,L"\x0003",wdCollapseStart);
+                                       
if(_com_dispatch_raw_method(pDispatchRange,wdDISPID_RANGE_MOVEEND,DISPATCH_METHOD,VT_I4,&unitsMoved,L"\x0003\x0003",wdCharacter,noteCharOffset)!=S_OK||unitsMoved<=0)
 {
+                                               break;
+                                       }
+                                       
_com_dispatch_raw_propget(pDispatchRange,wdDISPID_RANGE_END,VT_I4,&chunkEndOffset);
                                }
-                               
generateXMLAttribsForFormatting(pDispatchRange,chunkStartOffset,chunkEndOffset,initialFormatConfig,initialFormatAttribsStream);
                        }
                        if(isNoteChar) {
                                
isNoteChar=generateFootnoteEndnoteXML(pDispatchRange,XMLStream,true);
@@ -625,6 +711,7 @@ void winword_getTextInRange_helper(HWND hwnd, 
winword_getTextInRange_args* args)
                        SysFreeString(text);
                        text=NULL;
                        XMLStream<<L"</text>";
+                       if(isFormField) XMLStream<<L"</control>";
                        if(isNoteChar) XMLStream<<L"</control>";
                        if(inlineShapesCount>0) XMLStream<<L"</control>";
                }

diff --git a/nvdaHelper/sconscript b/nvdaHelper/sconscript
index 168b499..585d72d 100644
--- a/nvdaHelper/sconscript
+++ b/nvdaHelper/sconscript
@@ -32,6 +32,6 @@ archClientInstallDirs={
 
 #Build nvdaHelper for needed architectures
 for arch in env['targetArchitectures']: 
-       
archEnv=env.Clone(TARGET_ARCH=arch,tools=['windowsSdk','midl','msrpc','boost','default'])
+       
archEnv=env.Clone(TARGET_ARCH=arch,tools=['windowsSdk','midl','msrpc','default'])
        
archEnv.SConscript('archBuild_sconscript',exports={'env':archEnv,'clientInstallDir':archClientInstallDirs[arch],'libInstallDir':archLibInstallDirs[arch]},variant_dir='build/%s'%arch)
 

diff --git a/readme.txt b/readme.txt
index 236cd17..e5ea569 100644
--- a/readme.txt
+++ b/readme.txt
@@ -42,12 +42,8 @@ General dependencies:
                * Copy the txt2tags Python script to the global Python 
site-packages directory, naming it txt2tags.py.
        * Microsoft Windows SDK, version 7.0: 
http://www.microsoft.com/downloads/en/details.aspx?FamilyID=c17ba869-9671-4330-a63e-1fd44e0e2505&displaylang=en
                * You need to install both the 32 bit and 64 bit libraries and 
tools.
-       * MinHook, version 1.1.0: 
http://www.codeproject.com/KB/winsdk/LibMinHook.aspx
+       * MinHook, rev e21b54a: 
http://www.codeproject.com/KB/winsdk/LibMinHook.aspx
                * This is included as a git submodule
-       * Boost C++ Libraries, version 1.47:
-               * You can download the latest Windows installer from 
http://www.boostpro.com/download
-               * On the components page of the installer, make sure to install 
at least all of the defaults (whatever is already checked).
-               * NVDA only uses the Boost headers; none of the pre-compiled 
libraries are necessary.
        * SCons, version 2.2.0: http://www.scons.org/
                * As the scons command (scons.bat) is installed in to the 
scripts directory inside the directory where you installed Python, it is 
necessary to add the scripts  directory to your path variable so that you can 
run scons from anywhere. The rest of this readme assumes that scons can be run 
in this way.
 

diff --git a/site_scons/site_tools/boost.py b/site_scons/site_tools/boost.py
deleted file mode 100644
index e9cffdb..0000000
--- a/site_scons/site_tools/boost.py
+++ /dev/null
@@ -1,50 +0,0 @@
-###
-#This file is a part of the NVDA project.
-#URL: http://www.nvda-project.org/
-#Copyright 2006-2010 NVDA contributers.
-#This program is free software: you can redistribute it and/or modify
-#it under the terms of the GNU General Public License version 2.0, as 
published by
-#the Free Software Foundation.
-#This program is distributed in the hope that it will be useful,
-#but WITHOUT ANY WARRANTY; without even the implied warranty of
-#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-#This license can be found at:
-#http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
-###
-
-import _winreg
-
-_boostRoot=None
-
-def findBoostRoot():
-       global _boostRoot
-       if _boostRoot is None:
-               try:
-                       
k=_winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE,r'Software\boostpro.com')
-               except:
-                       print "boost tool: can't find bostpro.com registry key"
-                       return None
-               kLen=_winreg.QueryInfoKey(k)[0]
-               if kLen<1:
-                       print "boost tool: no subkeys in boostpro.com registry 
key"
-                       return None
-               subkeyString=_winreg.EnumKey(k,kLen-1)
-               try:
-                       k=_winreg.OpenKey(k,subkeyString)
-               except:
-                       print "boost tool: failed to open %s subkey of 
boostpro.com registry key"%subkeyString
-                       return None
-               try:
-                       boostRoot=_winreg.QueryValueEx(k,"InstallRoot")
-               except:
-                       print "no InstallRoot value in %s subkey of 
boostpro.com registry key"%subkeyString
-                       return None
-               _boostRoot=boostRoot
-       return _boostRoot
-
-def exists():
-       return True if findBoostRoot() is not None else False
-
-def generate(env):
-       boostRoot=findBoostRoot()
-       env.Append(CPPPATH=boostRoot)

diff --git a/source/IAccessibleHandler.py b/source/IAccessibleHandler.py
index 572a564..94db85d 100644
--- a/source/IAccessibleHandler.py
+++ b/source/IAccessibleHandler.py
@@ -302,6 +302,7 @@ IAccessible2StatesToNVDAStates={
        IA2_STATE_MULTI_LINE:controlTypes.STATE_MULTILINE,
        IA2_STATE_ICONIFIED:controlTypes.STATE_ICONIFIED,
        IA2_STATE_EDITABLE:controlTypes.STATE_EDITABLE,
+       IA2_STATE_PINNED:controlTypes.STATE_PINNED,
 }
 
 #A list to store handles received from setWinEventHook, for use with 
unHookWinEvent  

diff --git a/source/NVDAObjects/window/winword.py 
b/source/NVDAObjects/window/winword.py
index d43cdbf..38ee650 100755
--- a/source/NVDAObjects/window/winword.py
+++ b/source/NVDAObjects/window/winword.py
@@ -10,6 +10,7 @@ import comtypes.client
 import comtypes.automation
 import operator
 import locale
+import braille
 import languageHandler
 import ui
 import NVDAHelper
@@ -81,6 +82,19 @@ wdPrimaryFooterStory=9
 wdPrimaryHeaderStory=7
 wdTextFrameStory=5
 
+wdFieldFormTextInput=70
+wdFieldFormCheckBox=71
+wdFieldFormDropDown=83
+wdContentControlRichText=0
+wdContentControlText=1
+wdContentControlPicture=2
+wdContentControlComboBox=3
+wdContentControlDropdownList=4
+wdContentControlBuildingBlockGallery=5
+wdContentControlDate=6
+wdContentControlGroup=7
+wdContentControlCheckBox=8
+
 storyTypeLocalizedLabels={
        wdCommentsStory:_("Comments"),
        wdEndnotesStory:_("Endnotes"),
@@ -94,6 +108,23 @@ storyTypeLocalizedLabels={
        wdTextFrameStory:_("Text frame"),
 }
 
+wdFieldTypesToNVDARoles={
+       wdFieldFormTextInput:controlTypes.ROLE_EDITABLETEXT,
+       wdFieldFormCheckBox:controlTypes.ROLE_CHECKBOX,
+       wdFieldFormDropDown:controlTypes.ROLE_COMBOBOX,
+}
+
+wdContentControlTypesToNVDARoles={
+       wdContentControlRichText:controlTypes.ROLE_EDITABLETEXT,
+       wdContentControlText:controlTypes.ROLE_EDITABLETEXT,
+       wdContentControlPicture:controlTypes.ROLE_GRAPHIC,
+       wdContentControlComboBox:controlTypes.ROLE_COMBOBOX,
+       wdContentControlDropdownList:controlTypes.ROLE_COMBOBOX,
+       wdContentControlDate:controlTypes.ROLE_EDITABLETEXT,
+       wdContentControlGroup:controlTypes.ROLE_GROUPING,
+       wdContentControlCheckBox:controlTypes.ROLE_CHECKBOX,
+}
+
 winwordWindowIid=GUID('{00020962-0000-0000-C000-000000000046}')
 
 
wm_winword_expandToLine=ctypes.windll.user32.RegisterWindowMessageW(u"wm_winword_expandToLine")
@@ -136,7 +167,7 @@ class WordDocumentTextInfo(textInfos.TextInfo):
                lineStart=ctypes.c_int()
                lineEnd=ctypes.c_int()
                
res=NVDAHelper.localLib.nvdaInProcUtils_winword_expandToLine(self.obj.appModule.helperLocalBindingHandle,self.obj.windowHandle,self._rangeObj.start,ctypes.byref(lineStart),ctypes.byref(lineEnd))
-               if res!=0 or (lineStart.value==lineEnd.value==-1): 
+               if res!=0 or lineStart.value==lineEnd.value or 
lineStart.value==-1 or lineEnd.value==-1: 
                        log.debugWarning("winword_expandToLine failed")
                        self._rangeObj.expand(wdParagraph)
                        return
@@ -179,8 +210,6 @@ class WordDocumentTextInfo(textInfos.TextInfo):
                
formatConfig['autoLanguageSwitching']=config.conf['speech'].get('autoLanguageSwitching',False)
                startOffset=self._rangeObj.start
                endOffset=self._rangeObj.end
-               if startOffset==endOffset:
-                       return []
                text=BSTR()
                formatConfigFlags=sum(y for x,y in 
formatConfigFlagsMap.iteritems() if formatConfig.get(x,False))
                
res=NVDAHelper.localLib.nvdaInProcUtils_winword_getTextInRange(self.obj.appModule.helperLocalBindingHandle,self.obj.windowHandle,startOffset,endOffset,formatConfigFlags,ctypes.byref(text))
@@ -226,8 +255,30 @@ class WordDocumentTextInfo(textInfos.TextInfo):
                elif role=="object":
                        role=controlTypes.ROLE_EMBEDDEDOBJECT
                else:
-                       role=controlTypes.ROLE_UNKNOWN
-               field['role']=role
+                       fieldType=int(field.pop('wdFieldType',-1))
+                       if fieldType!=-1:
+                               
role=wdFieldTypesToNVDARoles.get(fieldType,controlTypes.ROLE_UNKNOWN)
+                               if fieldType==wdFieldFormCheckBox and 
int(field.get('wdFieldResult','0'))>0:
+                                       
field['states']=set([controlTypes.STATE_CHECKED])
+                               elif fieldType==wdFieldFormDropDown:
+                                       
field['value']=field.get('wdFieldResult',None)
+                       fieldStatusText=field.pop('wdFieldStatusText',None)
+                       if fieldStatusText:
+                               field['name']=fieldStatusText
+                               field['alwaysReportName']=True
+                       else:
+                               
fieldType=int(field.get('wdContentControlType',-1))
+                               if fieldType!=-1:
+                                       
role=wdContentControlTypesToNVDARoles.get(fieldType,controlTypes.ROLE_UNKNOWN)
+                                       if role==controlTypes.ROLE_CHECKBOX:
+                                               
fieldChecked=bool(int(field.get('wdContentControlChecked','0')))
+                                               if fieldChecked:
+                                                       
field['states']=set([controlTypes.STATE_CHECKED])
+                                       
fieldTitle=field.get('wdContentControlTitle',None)
+                                       if fieldTitle:
+                                               field['name']=fieldTitle
+                                               field['alwaysReportName']=True
+               if role is not None: field['role']=role
                storyType=int(field.pop('wdStoryType',0))
                if storyType:
                        name=storyTypeLocalizedLabels.get(storyType,None)
@@ -413,10 +464,10 @@ class 
WordDocument(EditableTextWithoutAutoSelectDetection, Window):
 
        def script_tab(self,gesture):
                gesture.send()
-               info=self.makeTextInfo(textInfos.POSITION_CARET)
-               if info._rangeObj.tables.count>0:
-                       info.expand(textInfos.UNIT_LINE)
-                       
speech.speakTextInfo(info,reason=controlTypes.REASON_CARET)
+               info=self.makeTextInfo(textInfos.POSITION_SELECTION)
+               if not info.isCollapsed or info._rangeObj.tables.count>0:
+                       
speech.speakTextInfo(info,reason=controlTypes.REASON_FOCUS)
+               braille.handler.handleCaretMove(info)
 
        def _moveInTable(self,row=True,forward=True):
                info=self.makeTextInfo(textInfos.POSITION_CARET)

diff --git a/source/_UIAHandler.py b/source/_UIAHandler.py
index affdf80..96bad4f 100644
--- a/source/_UIAHandler.py
+++ b/source/_UIAHandler.py
@@ -167,7 +167,7 @@ class UIAHandler(COMObject):
                self.clientObject.RemoveAllEventHandlers()
 
        def 
IUIAutomationEventHandler_HandleAutomationEvent(self,sender,eventID):
-               if not self.MTAThreadInitEvent.isSet:
+               if not self.MTAThreadInitEvent.isSet():
                        # UIAHandler hasn't finished initialising yet, so just 
ignore this event.
                        return
                if eventID==UIA_MenuOpenedEventId and 
eventHandler.isPendingEvents("gainFocus"):
@@ -186,7 +186,7 @@ class UIAHandler(COMObject):
                eventHandler.queueEvent(NVDAEventName,obj)
 
        def 
IUIAutomationFocusChangedEventHandler_HandleFocusChangedEvent(self,sender):
-               if not self.MTAThreadInitEvent.isSet:
+               if not self.MTAThreadInitEvent.isSet():
                        # UIAHandler hasn't finished initialising yet, so just 
ignore this event.
                        return
                if not self.isNativeUIAElement(sender):
@@ -211,7 +211,7 @@ class UIAHandler(COMObject):
                eventHandler.queueEvent("gainFocus",obj)
 
        def 
IUIAutomationPropertyChangedEventHandler_HandlePropertyChangedEvent(self,sender,propertyId,newValue):
-               if not self.MTAThreadInitEvent.isSet:
+               if not self.MTAThreadInitEvent.isSet():
                        # UIAHandler hasn't finished initialising yet, so just 
ignore this event.
                        return
                
NVDAEventName=UIAPropertyIdsToNVDAEventNames.get(propertyId,None)

diff --git a/source/appModules/outlook.py b/source/appModules/outlook.py
index 5506930..761f5dc 100644
--- a/source/appModules/outlook.py
+++ b/source/appModules/outlook.py
@@ -86,7 +86,9 @@ class AppModule(appModuleHandler.AppModule):
                windowClassName=obj.windowClassName
                states=obj.states
                controlID=obj.windowControlID
-               if role==controlTypes.ROLE_LISTITEM and 
(windowClassName.startswith("REListBox") or 
windowClassName.startswith("NetUIHWND")):
+               if windowClassName=="REListBox20W" and 
role==controlTypes.ROLE_CHECKBOX:
+                       clsList.insert(0,REListBox20W_CheckBox)
+               elif role==controlTypes.ROLE_LISTITEM and 
(windowClassName.startswith("REListBox") or 
windowClassName.startswith("NetUIHWND")):
                        clsList.insert(0,AutoCompleteListItem)
                if role==controlTypes.ROLE_LISTITEM and 
windowClassName=="OUTEXVLB":
                        clsList.insert(0, AddressBookEntry)
@@ -98,6 +100,16 @@ class AppModule(appModuleHandler.AppModule):
                        elif isinstance(obj,IAccessible) and 
obj.event_objectID==winUser.OBJID_CLIENT and obj.event_childID==0:
                                clsList.insert(0,SuperGridClient2010)
 
+class REListBox20W_CheckBox(IAccessible):
+
+       def script_checkbox(self, gesture):
+               gesture.send()
+               self.event_stateChange()
+
+       __gestures={
+               "kb:space":"checkbox",
+       }
+
 class SuperGridClient2010(IAccessible):
 
        def isDuplicateIAccessibleEvent(self,obj):

diff --git a/source/appModules/zendstudio.py b/source/appModules/zendstudio.py
new file mode 100644
index 0000000..d5ab377
--- /dev/null
+++ b/source/appModules/zendstudio.py
@@ -0,0 +1,5 @@
+"""App module for Zend Studio
+This simply uses the app module for Eclipse.
+"""
+
+from eclipse import *

diff --git a/source/controlTypes.py b/source/controlTypes.py
index b4d92f0..35745c8 100644
--- a/source/controlTypes.py
+++ b/source/controlTypes.py
@@ -183,6 +183,7 @@ STATE_SORTED_ASCENDING=0x100000000
 STATE_SORTED_DESCENDING=0x200000000
 
STATES_SORTED=frozenset([STATE_SORTED,STATE_SORTED_ASCENDING,STATE_SORTED_DESCENDING])
 STATE_HASLONGDESC=0x400000000
+STATE_PINNED=0x800000000
 
 roleLabels={
        # Translators: The word for an unknown control type.
@@ -514,6 +515,8 @@ stateLabels={
        STATE_SORTED_DESCENDING:_("sorted descending"),
        # Translators: a state that denotes that an object (usually a graphic) 
has a long description.
        STATE_HASLONGDESC:_("has long description"),
+       # Translators: a state that denotes that an object is pinned in its 
current location 
+       STATE_PINNED:_("pinned"),
 }
 
 negativeStateLabels={

This diff is so big that we needed to truncate the remainder.

https://bitbucket.org/nvdaaddonteam/nvda/commits/5281be32c51a/
Changeset:   5281be32c51a
Branch:      None
User:        mdcurran
Date:        2013-09-02 08:11:09
Summary:     The Input Gestures dialog can be opened with NVDA+control+i. Re 
#1532

Affected #:  1 file

diff --git a/source/globalCommands.py b/source/globalCommands.py
index d0a3258..c7078cd 100755
--- a/source/globalCommands.py
+++ b/source/globalCommands.py
@@ -1036,6 +1036,11 @@ class GlobalCommands(ScriptableObject):
        # Translators: Input help mode message for apply last saved or default 
settings command.
        script_revertConfiguration.__doc__ = _("Pressing once reverts the 
current configuration to the most recently saved state. Pressing three times 
reverts to factory defaults.")
 
+       def script_activateInputGesturesDialog(self, gesture):
+               wx.CallAfter(gui.mainFrame.onInputGesturesCommand, None)
+       # Translators: Input help mode message for go to Input Gestures dialog 
command.
+       script_activateInputGesturesDialog.__doc__ = _("Shows the NVDA input 
gestures dialog")
+
        def script_activatePythonConsole(self,gesture):
                if globalVars.appArgs.secure:
                        return
@@ -1356,6 +1361,7 @@ class GlobalCommands(ScriptableObject):
                "kb:NVDA+control+o": "activateObjectPresentationDialog",
                "kb:NVDA+control+b": "activateBrowseModeDialog",
                "kb:NVDA+control+d": "activateDocumentFormattingDialog",
+               "kb:NVDA+control+i": "activateInputGesturesDialog",
 
                # Save/reload configuration
                "kb:NVDA+control+c": "saveConfiguration",


https://bitbucket.org/nvdaaddonteam/nvda/commits/8d076275085b/
Changeset:   8d076275085b
Branch:      None
User:        mdcurran
Date:        2013-09-02 08:13:06
Summary:     Give all scripts on virtualBuffers a script category (for the 
input guestures dialog) of 'Browse mode'. Re #1532

Affected #:  1 file

diff --git a/source/virtualBuffers/__init__.py 
b/source/virtualBuffers/__init__.py
index 0cdccd6..37ed488 100644
--- a/source/virtualBuffers/__init__.py
+++ b/source/virtualBuffers/__init__.py
@@ -564,6 +564,9 @@ class ElementsListDialog(wx.Dialog):
 
 class VirtualBuffer(cursorManager.CursorManager, 
treeInterceptorHandler.TreeInterceptor):
 
+       # Translators: the script category for browse mode
+       scriptCategory=_("Browse mode")
+
        REASON_QUICKNAV = "quickNav"
 
        TextInfo=VirtualBufferTextInfo


https://bitbucket.org/nvdaaddonteam/nvda/commits/c9e945183c38/
Changeset:   c9e945183c38
Branch:      None
User:        mdcurran
Date:        2013-09-02 08:42:30
Summary:     Set the scriptCategory of 'Browse mode' on the cursorManager class 
rather than the VirtualBuffer class, so that it also includes NVDA-specific 
text find commands. Re #1532

Affected #:  2 files

diff --git a/source/cursorManager.py b/source/cursorManager.py
index 9ae3b89..4864ebb 100644
--- a/source/cursorManager.py
+++ b/source/cursorManager.py
@@ -32,6 +32,9 @@ class CursorManager(baseObject.ScriptableObject):
        @type selection: L{textInfos.TextInfo}
        """
 
+       # Translators: the script category for browse mode
+       scriptCategory=_("Browse mode")
+
        _lastFindText=""
 
        def __init__(self, *args, **kwargs):

diff --git a/source/virtualBuffers/__init__.py 
b/source/virtualBuffers/__init__.py
index 37ed488..0cdccd6 100644
--- a/source/virtualBuffers/__init__.py
+++ b/source/virtualBuffers/__init__.py
@@ -564,9 +564,6 @@ class ElementsListDialog(wx.Dialog):
 
 class VirtualBuffer(cursorManager.CursorManager, 
treeInterceptorHandler.TreeInterceptor):
 
-       # Translators: the script category for browse mode
-       scriptCategory=_("Browse mode")
-
        REASON_QUICKNAV = "quickNav"
 
        TextInfo=VirtualBufferTextInfo


https://bitbucket.org/nvdaaddonteam/nvda/commits/43d9ae1f03d2/
Changeset:   43d9ae1f03d2
Branch:      None
User:        jteh
Date:        2013-09-02 09:19:52
Summary:     Merge branch 't3485' into next

Incubates #3485.

Affected #:  2 files

diff --git a/source/appModules/poedit.py b/source/appModules/poedit.py
index c6e0c69..995618d 100644
--- a/source/appModules/poedit.py
+++ b/source/appModules/poedit.py
@@ -1,6 +1,6 @@
 #appModules/poedit.py
 #A part of NonVisual Desktop Access (NVDA)
-#Copyright (C) 2012 Mesar Hameed <mhameed@xxxxxxxxxxxxx>
+#Copyright (C) 2012-2013 Mesar Hameed, NV Access Limited
 #This file is covered by the GNU General Public License.
 #See the file COPYING for more details.
 
@@ -15,6 +15,9 @@ import textInfos
 import tones
 import ui
 from NVDAObjects.IAccessible import sysListView32
+import windowUtils
+import NVDAObjects.IAccessible
+import winUser
 
 
 def getPath(obj, ancestor):
@@ -65,44 +68,47 @@ def fetchObject(obj, path):
 class AppModule(appModuleHandler.AppModule):
 
        def script_reportAutoCommentsWindow(self,gesture):
-               obj = fetchObject(api.getForegroundObject(), [2, 0, 1, 0, 1, 0, 
0, 0])
-               # check the controlid, because in certain situations 
-               # autoComments and comment windows change places.
-               if obj and obj.windowControlID == 102:
+               obj = fetchObject(api.getForegroundObject(), [2, 0, 1, 0, 1, 0, 
1])
+               if obj and obj.windowControlID != 101:
+                       try:
+                               obj = obj.next.firstChild
+                       except AttributeError:
+                               obj = None
+               elif obj:
+                       obj = obj.firstChild
+               if obj:
                        try:
                                ui.message(obj.name + " " + obj.value)
                        except:
                                # Translators: this message is reported when 
there are no 
-                               # comments to be presented to the user in the 
automatic 
-                               # comments window in poedit.
-                               ui.message(_("No automatic comments."))
+                               # notes for translators to be presented to the 
user in Poedit.
+                               ui.message(_("No notes for translators."))
                else:
                        # Translators: this message is reported when NVDA is 
unable to find 
-                       # the 'automatic comments' window in poedit.
-                       ui.message(_("Could not find automatic comments 
window."))
+                       # the 'Notes for translators' window in poedit.
+                       ui.message(_("Could not find Notes for translators 
window."))
        # Translators: The description of an NVDA command for Poedit.
-       script_reportAutoCommentsWindow.__doc__ = _("Reports any comments in 
the automatic comments window")
+       script_reportAutoCommentsWindow.__doc__ = _("Reports any notes for 
translators")
 
        def script_reportCommentsWindow(self,gesture):
-               obj = fetchObject(api.getForegroundObject(), [2, 0, 1, 0, 1, 0, 
1, 0])
-               # if it isnt in the normal location, try to find it in the
-               # location of the automatic window.
-               if not obj: 
-                       obj = fetchObject(api.getForegroundObject(), [2, 0, 1, 
0, 1, 0, 0, 0])
-               if obj and obj.windowControlID == 105:
-                       try:
-                               ui.message(obj.name + " " + obj.value)
-                       except:
-                               # Translators: this message is reported when 
there are no
-                               # comments to be presented to the user in the 
translator
-                               # comments window in poedit.
-                               ui.message(_("No comment."))
-               else:
+               try:
+                       obj = NVDAObjects.IAccessible.getNVDAObjectFromEvent(
+                               
windowUtils.findDescendantWindow(api.getForegroundObject().windowHandle, 
visible=True, controlID=104),
+                               winUser.OBJID_CLIENT, 0)
+               except LookupError:
                        # Translators: this message is reported when NVDA is 
unable to find
                        # the 'comments' window in poedit.
                        ui.message(_("Could not find comment window."))
+                       return None
+               try:
+                       ui.message(obj.name + " " + obj.value)
+               except:
+                       # Translators: this message is reported when there are 
no
+                       # comments to be presented to the user in the translator
+                       # comments window in poedit.
+                       ui.message(_("No comment."))
        # Translators: The description of an NVDA command for Poedit.
-       script_reportAutoCommentsWindow.__doc__ = _("Reports any comments in 
the comments window")
+       script_reportCommentsWindow.__doc__ = _("Reports any comments in the 
comments window")
 
        __gestures = {
                "kb:control+shift+c": "reportCommentsWindow",
@@ -112,28 +118,26 @@ class AppModule(appModuleHandler.AppModule):
        def chooseNVDAObjectOverlayClasses(self, obj, clsList):
                if "SysListView32" in obj.windowClassName and 
obj.role==controlTypes.ROLE_LISTITEM:
                        clsList.insert(0,PoeditListItem)
-               if obj.role == controlTypes.ROLE_EDITABLETEXT:
-                       if obj.windowControlID == 102:
-                               # Translators: Automatic comments is the name 
of the poedit 
-                               # window that displays comments extracted from 
code.
-                               obj.name =  _("Automatic comments:")
-                       if obj.windowControlID == 104:
-                               # Translators: this is the label for the edit 
area in poedit 
-                               # that contains a translation.
-                               obj.name = _("Translation:")
-                       if obj.windowControlID == 105:
-                               # Translators: 'comments:' is the name of the 
poedit window 
-                               # that displays comments entered by the 
translator.
-                               obj.name = _("Comments:")
+
+       def event_NVDAObject_init(self, obj):
+               if obj.role == controlTypes.ROLE_EDITABLETEXT and 
controlTypes.STATE_MULTILINE in obj.states and obj.isInForeground:
+                       # Oleacc often gets the name wrong.
+                       # The label object is positioned just above the field 
on the screen.
+                       l, t, w, h = obj.location
+                       try:
+                               obj.name = 
NVDAObjects.NVDAObject.objectFromPoint(l + 10, t - 10).name
+                       except AttributeError:
+                               pass
+                       return
 
 class PoeditListItem(sysListView32.ListItem):
 
        def _get_isBold(self):
                
info=displayModel.DisplayModelTextInfo(self,position=textInfos.POSITION_FIRST)
-               info.expand(textInfos.UNIT_LINE)
+               info.expand(textInfos.UNIT_CHARACTER)
                fields=info.getTextWithFields()
                try:
-                       return fields[1].field['bold']
+                       return fields[0].field['bold']
                except:
                        return False
 

diff --git a/user_docs/en/userGuide.t2t b/user_docs/en/userGuide.t2t
index 7a7ad21..73fdc5f 100644
--- a/user_docs/en/userGuide.t2t
+++ b/user_docs/en/userGuide.t2t
@@ -554,7 +554,7 @@ Note: The report remaining time shortcut works only with 
the default formatting
 %kc:beginInclude
 || Name | Key | Description |
 | Report Comments Window | control+shift+c | Reports any comments in the 
comments window. |
-| Report automatic comments window | control+shift+a | Reports any comments in 
the automatic comments window. |
+| Report notes for translators | control+shift+a | Reports any notes for 
translators. |
 %kc:endInclude
 
 + Configuring NVDA +


https://bitbucket.org/nvdaaddonteam/nvda/commits/f28d3553b633/
Changeset:   f28d3553b633
Branch:      None
User:        mdcurran
Date:        2013-09-03 02:32:03
Summary:     Again move the browse mode script category string, this time into 
inputCore as a constant of SCRCAT_BROWSEMODE. this is so it can also be used by 
globalcommands (toggle pasThrough).

Affected #:  3 files

diff --git a/source/cursorManager.py b/source/cursorManager.py
index 4864ebb..74c2f51 100644
--- a/source/cursorManager.py
+++ b/source/cursorManager.py
@@ -18,6 +18,7 @@ import speech
 import config
 import braille
 import controlTypes
+from inputCore import SCRCAT_BROWSEMODE
 
 class CursorManager(baseObject.ScriptableObject):
        """
@@ -33,7 +34,7 @@ class CursorManager(baseObject.ScriptableObject):
        """
 
        # Translators: the script category for browse mode
-       scriptCategory=_("Browse mode")
+       scriptCategory=SCRCAT_BROWSEMODE
 
        _lastFindText=""
 

diff --git a/source/globalCommands.py b/source/globalCommands.py
index c7078cd..ae2e995 100755
--- a/source/globalCommands.py
+++ b/source/globalCommands.py
@@ -724,6 +724,7 @@ class GlobalCommands(ScriptableObject):
                virtualBuffers.reportPassThrough(vbuf)
        # Translators: Input help mode message for toggle focus and browse mode 
command in web browsing and other situations.
        script_toggleVirtualBufferPassThrough.__doc__=_("Toggles between browse 
mode and focus mode. When in focus mode, keys will pass straight through to the 
application, allowing you to interact directly with a control. When in browse 
mode, you can navigate the document with the cursor, quick navigation keys, 
etc.")
+       
script_toggleVirtualBufferPassThrough.category=inputCore.SCRCAT_BROWSEMODE
 
        def script_quit(self,gesture):
                gui.quit()

diff --git a/source/inputCore.py b/source/inputCore.py
index 3b70125..62236c6 100644
--- a/source/inputCore.py
+++ b/source/inputCore.py
@@ -33,6 +33,9 @@ SCRCAT_KBEMU = _("Emulated system keyboard keys")
 #: Script category for miscellaneous commands.
 # Translators: The name of a category of NVDA commands.
 SCRCAT_MISC = _("Miscellaneous")
+#: Script category for Browse Mode  commands.
+# Translators: The name of a category of NVDA commands.
+SCRCAT_BROWSEMODE = _("Browse mode")
 
 class NoInputGestureAction(LookupError):
        """Informs that there is no action to execute for a gesture.


https://bitbucket.org/nvdaaddonteam/nvda/commits/fba591f91e17/
Changeset:   fba591f91e17
Branch:      None
User:        mdcurran
Date:        2013-09-03 07:15:46
Summary:     Added script categories of mouse, textReview, objectNavigation and 
systemCaret to all related scripts in globalCommands.

Affected #:  1 file

diff --git a/source/globalCommands.py b/source/globalCommands.py
index ae2e995..1a8f4c9 100755
--- a/source/globalCommands.py
+++ b/source/globalCommands.py
@@ -37,6 +37,19 @@ import virtualBuffers
 import characterProcessing
 from baseObject import ScriptableObject
 
+#: Script category for text review  commands.
+# Translators: The name of a category of NVDA commands.
+SCRCAT_TEXTREVIEW = _("Text review")
+#: Script category for Object navigation   commands.
+# Translators: The name of a category of NVDA commands.
+SCRCAT_OBJECTNAVIGATION = _("Object navigation")
+#: Script category for system caret commands.
+# Translators: The name of a category of NVDA commands.
+SCRCAT_SYSTEMCARET = _("System caret")
+#: Script category for mouse commands.
+# Translators: The name of a category of NVDA commands.
+SCRCAT_MOUSE = _("Mouse")
+
 class GlobalCommands(ScriptableObject):
        """Commands that are available at all times, regardless of the current 
focus.
        """
@@ -85,6 +98,7 @@ class GlobalCommands(ScriptableObject):
                        speech.speakSpelling(info.text)
        # Translators: Input help mode message for report current line command.
        script_reportCurrentLine.__doc__=_("Reports the current line under the 
application cursor. Pressing this key twice will spell the current line")
+       script_reportCurrentLine.category=SCRCAT_SYSTEMCARET
 
        def script_leftMouseClick(self,gesture):
                # Translators: Reported when left mouse button is clicked.
@@ -93,6 +107,7 @@ class GlobalCommands(ScriptableObject):
                winUser.mouse_event(winUser.MOUSEEVENTF_LEFTUP,0,0,None,None)
        # Translators: Input help mode message for left mouse click command.
        script_leftMouseClick.__doc__=_("Clicks the left mouse button once at 
the current mouse position")
+       script_leftMouseClick.category=SCRCAT_MOUSE
 
        def script_rightMouseClick(self,gesture):
                # Translators: Reported when right mouse button is clicked.
@@ -101,6 +116,7 @@ class GlobalCommands(ScriptableObject):
                winUser.mouse_event(winUser.MOUSEEVENTF_RIGHTUP,0,0,None,None)
        # Translators: Input help mode message for right mouse click command.
        script_rightMouseClick.__doc__=_("Clicks the right mouse button once at 
the current mouse position")
+       script_rightMouseClick.category=SCRCAT_MOUSE
 
        def script_toggleLeftMouseButton(self,gesture):
                if winUser.getKeyState(winUser.VK_LBUTTON)&32768:
@@ -113,6 +129,7 @@ class GlobalCommands(ScriptableObject):
                        
winUser.mouse_event(winUser.MOUSEEVENTF_LEFTDOWN,0,0,None,None)
        # Translators: Input help mode message for left mouse lock/unlock 
toggle command.
        script_toggleLeftMouseButton.__doc__=_("Locks or unlocks the left mouse 
button")
+       script_toggleLeftMouseButton.category=SCRCAT_MOUSE
 
        def script_toggleRightMouseButton(self,gesture):
                if winUser.getKeyState(winUser.VK_RBUTTON)&32768:
@@ -125,6 +142,7 @@ class GlobalCommands(ScriptableObject):
                        
winUser.mouse_event(winUser.MOUSEEVENTF_RIGHTDOWN,0,0,None,None)
        # Translators: Input help mode message for right mouse lock/unlock 
command.
        script_toggleRightMouseButton.__doc__=_("Locks or unlocks the right 
mouse button")
+       script_toggleRightMouseButton.category=SCRCAT_MOUSE
 
        def script_reportCurrentSelection(self,gesture):
                obj=api.getFocusObject()
@@ -141,6 +159,7 @@ class GlobalCommands(ScriptableObject):
                        speech.speakMessage(_("selected %s")%info.text)
        # Translators: Input help mode message for report current selection 
command.
        script_reportCurrentSelection.__doc__=_("Announces the current 
selection in edit controls and documents. If there is no selection it says so.")
+       script_reportCurrentSelection.category=SCRCAT_SYSTEMCARET
 
        def script_dateTime(self,gesture):
                if scriptHandler.getLastScriptRepeatCount()==0:
@@ -269,6 +288,7 @@ class GlobalCommands(ScriptableObject):
                mouseHandler.executeMouseMoveEvent(x,y)
        # Translators: Input help mode message for move mouse to navigator 
object command.
        script_moveMouseToNavigatorObject.__doc__=_("Moves the mouse pointer to 
the current navigator object")
+       script_moveMouseToNavigatorObject.category=SCRCAT_MOUSE
 
        def script_moveNavigatorObjectToMouse(self,gesture):
                # Translators: Reported when attempting to move the navigator 
object to the object under mouse pointer.
@@ -278,6 +298,7 @@ class GlobalCommands(ScriptableObject):
                speech.speakObject(obj)
        # Translators: Input help mode message for move navigator object to 
mouse command.
        script_moveNavigatorObjectToMouse.__doc__=_("Sets the navigator object 
to the current object under the mouse pointer and speaks it")
+       script_moveNavigatorObjectToMouse.category=SCRCAT_MOUSE
 
        def script_reviewMode_next(self,gesture):
                label=review.nextMode()
@@ -340,6 +361,7 @@ class GlobalCommands(ScriptableObject):
                        
speech.speakObject(curObject,reason=controlTypes.REASON_QUERY)
        # Translators: Input help mode message for report current navigator 
object command.
        script_navigatorObject_current.__doc__=_("Reports the current navigator 
object. Pressing twice spells this information,and pressing three times Copies 
name and value of this  object to the clipboard")
+       script_navigatorObject_current.category=SCRCAT_OBJECTNAVIGATION
 
        def script_navigatorObject_currentDimensions(self,gesture):
                obj=api.getNavigatorObject()
@@ -363,6 +385,7 @@ class GlobalCommands(ScriptableObject):
                ui.message(_("Object edges positioned {left:.1f} per cent from 
left edge of screen, {top:.1f} per cent from top edge of screen, width is 
{width:.1f} per cent of screen, height is {height:.1f} per cent of 
screen").format(left=percentFromLeft,top=percentFromTop,width=percentWidth,height=percentHeight))
        # Translators: Input help mode message for report object dimensions 
command.
        script_navigatorObject_currentDimensions.__doc__=_("Reports the hight, 
width and position of the current navigator object")
+       
script_navigatorObject_currentDimensions.category=SCRCAT_OBJECTNAVIGATION
 
        def script_navigatorObject_toFocus(self,gesture):
                obj=api.getFocusObject()
@@ -376,6 +399,7 @@ class GlobalCommands(ScriptableObject):
                speech.speakObject(obj,reason=controlTypes.REASON_FOCUS)
        # Translators: Input help mode message for move navigator object to 
current focus command.
        script_navigatorObject_toFocus.__doc__=_("Sets the navigator object to 
the current focus, and the review cursor to the position of the caret inside 
it, if possible.")
+       script_navigatorObject_toFocus.category=SCRCAT_OBJECTNAVIGATION
 
        def script_navigatorObject_moveFocus(self,gesture):
                obj=api.getNavigatorObject()
@@ -401,6 +425,7 @@ class GlobalCommands(ScriptableObject):
                        
speech.speakTextInfo(info,reason=controlTypes.REASON_CARET)
        # Translators: Input help mode message for move focus to current 
navigator object command.
        script_navigatorObject_moveFocus.__doc__=_("Pressed once Sets the 
keyboard focus to the navigator object, pressed twice sets the system caret to 
the position of the review cursor")
+       script_navigatorObject_moveFocus.category=SCRCAT_OBJECTNAVIGATION
 
        def script_navigatorObject_parent(self,gesture):
                curObject=api.getNavigatorObject()
@@ -417,6 +442,7 @@ class GlobalCommands(ScriptableObject):
                        speech.speakMessage(_("No containing object"))
        # Translators: Input help mode message for move to parent object 
command.
        script_navigatorObject_parent.__doc__=_("Moves the navigator object to 
the object containing it")
+       script_navigatorObject_parent.category=SCRCAT_OBJECTNAVIGATION
 
        def script_navigatorObject_next(self,gesture):
                curObject=api.getNavigatorObject()
@@ -433,6 +459,7 @@ class GlobalCommands(ScriptableObject):
                        speech.speakMessage(_("No next"))
        # Translators: Input help mode message for move to next object command.
        script_navigatorObject_next.__doc__=_("Moves the navigator object to 
the next object")
+       script_navigatorObject_next.category=SCRCAT_OBJECTNAVIGATION
 
        def script_navigatorObject_previous(self,gesture):
                curObject=api.getNavigatorObject()
@@ -449,6 +476,7 @@ class GlobalCommands(ScriptableObject):
                        speech.speakMessage(_("No previous"))
        # Translators: Input help mode message for move to previous object 
command.
        script_navigatorObject_previous.__doc__=_("Moves the navigator object 
to the previous object")
+       script_navigatorObject_previous.category=SCRCAT_OBJECTNAVIGATION
 
        def script_navigatorObject_firstChild(self,gesture):
                curObject=api.getNavigatorObject()
@@ -465,6 +493,7 @@ class GlobalCommands(ScriptableObject):
                        speech.speakMessage(_("No objects inside"))
        # Translators: Input help mode message for move to first child object 
command.
        script_navigatorObject_firstChild.__doc__=_("Moves the navigator object 
to the first object inside it")
+       script_navigatorObject_firstChild.category=SCRCAT_OBJECTNAVIGATION
 
        def script_review_activate(self,gesture):
                # Translators: a message reported when the action at the 
position of the review cursor or navigator object is performed.
@@ -498,6 +527,7 @@ class GlobalCommands(ScriptableObject):
                ui.message(_("No action"))
        # Translators: Input help mode message for activate current object 
command.
        script_review_activate.__doc__=_("Performs the default action on the 
current navigator object (example: presses it if it is a button).")
+       script_review_activate.category=SCRCAT_OBJECTNAVIGATION
 
        def script_review_top(self,gesture):
                
info=api.getReviewPosition().obj.makeTextInfo(textInfos.POSITION_FIRST)
@@ -507,6 +537,7 @@ class GlobalCommands(ScriptableObject):
                
speech.speakTextInfo(info,unit=textInfos.UNIT_LINE,reason=controlTypes.REASON_CARET)
        # Translators: Input help mode message for move review cursor to top 
line command.
        script_review_top.__doc__=_("Moves the review cursor to the top line of 
the current navigator object and speaks it")
+       script_review_top.category=SCRCAT_TEXTREVIEW
 
        def script_review_previousLine(self,gesture):
                info=api.getReviewPosition().copy()
@@ -521,6 +552,7 @@ class GlobalCommands(ScriptableObject):
                
speech.speakTextInfo(info,unit=textInfos.UNIT_LINE,reason=controlTypes.REASON_CARET)
        # Translators: Input help mode message for move review cursor to 
previous line command.
        script_review_previousLine.__doc__=_("Moves the review cursor to the 
previous line of the current navigator object and speaks it")
+       script_review_previousLine.category=SCRCAT_TEXTREVIEW
 
        def script_review_currentLine(self,gesture):
                info=api.getReviewPosition().copy()
@@ -532,6 +564,7 @@ class GlobalCommands(ScriptableObject):
                        
speech.spellTextInfo(info,useCharacterDescriptions=scriptCount>1)
        # Translators: Input help mode message for read current line under 
review cursor command.
        script_review_currentLine.__doc__=_("Reports the line of the current 
navigator object where the review cursor is situated. If this key is pressed 
twice, the current line will be spelled. Pressing three times will spell the 
line using character descriptions.")
+       script_review_currentLine.category=SCRCAT_TEXTREVIEW
  
        def script_review_nextLine(self,gesture):
                info=api.getReviewPosition().copy()
@@ -546,6 +579,7 @@ class GlobalCommands(ScriptableObject):
                
speech.speakTextInfo(info,unit=textInfos.UNIT_LINE,reason=controlTypes.REASON_CARET)
        # Translators: Input help mode message for move review cursor to next 
line command.
        script_review_nextLine.__doc__=_("Moves the review cursor to the next 
line of the current navigator object and speaks it")
+       script_review_nextLine.category=SCRCAT_TEXTREVIEW
 
        def script_review_bottom(self,gesture):
                
info=api.getReviewPosition().obj.makeTextInfo(textInfos.POSITION_LAST)
@@ -555,6 +589,7 @@ class GlobalCommands(ScriptableObject):
                
speech.speakTextInfo(info,unit=textInfos.UNIT_LINE,reason=controlTypes.REASON_CARET)
        # Translators: Input help mode message for move review cursor to bottom 
line command.
        script_review_bottom.__doc__=_("Moves the review cursor to the bottom 
line of the current navigator object and speaks it")
+       script_review_bottom.category=SCRCAT_TEXTREVIEW
 
        def script_review_previousWord(self,gesture):
                info=api.getReviewPosition().copy()
@@ -570,6 +605,7 @@ class GlobalCommands(ScriptableObject):
                
speech.speakTextInfo(info,reason=controlTypes.REASON_CARET,unit=textInfos.UNIT_WORD)
        # Translators: Input help mode message for move review cursor to 
previous word command.
        script_review_previousWord.__doc__=_("Moves the review cursor to the 
previous word of the current navigator object and speaks it")
+       script_review_previousWord.category=SCRCAT_TEXTREVIEW
 
        def script_review_currentWord(self,gesture):
                info=api.getReviewPosition().copy()
@@ -581,6 +617,7 @@ class GlobalCommands(ScriptableObject):
                        
speech.spellTextInfo(info,useCharacterDescriptions=scriptCount>1)
        # Translators: Input help mode message for report current word under 
review cursor command.
        script_review_currentWord.__doc__=_("Speaks the word of the current 
navigator object where the review cursor is situated. Pressing twice spells the 
word. Pressing three times spells the word using character descriptions")
+       script_review_currentWord.category=SCRCAT_TEXTREVIEW
 
        def script_review_nextWord(self,gesture):
                info=api.getReviewPosition().copy()
@@ -595,6 +632,7 @@ class GlobalCommands(ScriptableObject):
                
speech.speakTextInfo(info,reason=controlTypes.REASON_CARET,unit=textInfos.UNIT_WORD)
        # Translators: Input help mode message for move review cursor to next 
word command.
        script_review_nextWord.__doc__=_("Moves the review cursor to the next 
word of the current navigator object and speaks it")
+       script_review_nextWord.category=SCRCAT_TEXTREVIEW
 
        def script_review_startOfLine(self,gesture):
                info=api.getReviewPosition().copy()
@@ -606,6 +644,7 @@ class GlobalCommands(ScriptableObject):
                
speech.speakTextInfo(info,unit=textInfos.UNIT_CHARACTER,reason=controlTypes.REASON_CARET)
        # Translators: Input help mode message for move review cursor to start 
of current line command.
        script_review_startOfLine.__doc__=_("Moves the review cursor to the 
first character of the line where it is situated in the current navigator 
object and speaks it")
+       script_review_startOfLine.category=SCRCAT_TEXTREVIEW
 
        def script_review_previousCharacter(self,gesture):
                lineInfo=api.getReviewPosition().copy()
@@ -625,6 +664,7 @@ class GlobalCommands(ScriptableObject):
                        
speech.speakTextInfo(charInfo,unit=textInfos.UNIT_CHARACTER,reason=controlTypes.REASON_CARET)
        # Translators: Input help mode message for move review cursor to 
previous character command.
        script_review_previousCharacter.__doc__=_("Moves the review cursor to 
the previous character of the current navigator object and speaks it")
+       script_review_previousCharacter.category=SCRCAT_TEXTREVIEW
 
        def script_review_currentCharacter(self,gesture):
                info=api.getReviewPosition().copy()
@@ -643,6 +683,7 @@ class GlobalCommands(ScriptableObject):
                                
speech.speakTextInfo(info,unit=textInfos.UNIT_CHARACTER,reason=controlTypes.REASON_CARET)
        # Translators: Input help mode message for report current character 
under review cursor command.
        script_review_currentCharacter.__doc__=_("Reports the character of the 
current navigator object where the review cursor is situated. Pressing twice 
reports a description or example of that character. Pressing three times 
reports the numeric value of the character in decimal and hexadecimal")
+       script_review_currentCharacter.category=SCRCAT_TEXTREVIEW
 
        def script_review_nextCharacter(self,gesture):
                lineInfo=api.getReviewPosition().copy()
@@ -662,6 +703,7 @@ class GlobalCommands(ScriptableObject):
                        
speech.speakTextInfo(charInfo,unit=textInfos.UNIT_CHARACTER,reason=controlTypes.REASON_CARET)
        # Translators: Input help mode message for move review cursor to next 
character command.
        script_review_nextCharacter.__doc__=_("Moves the review cursor to the 
next character of the current navigator object and speaks it")
+       script_review_nextCharacter.category=SCRCAT_TEXTREVIEW
 
        def script_review_endOfLine(self,gesture):
                info=api.getReviewPosition().copy()
@@ -674,6 +716,7 @@ class GlobalCommands(ScriptableObject):
                
speech.speakTextInfo(info,unit=textInfos.UNIT_CHARACTER,reason=controlTypes.REASON_CARET)
        # Translators: Input help mode message for move review cursor to end of 
current line command.
        script_review_endOfLine.__doc__=_("Moves the review cursor to the last 
character of the line where it is situated in the current navigator object and 
speaks it")
+       script_review_endOfLine.category=SCRCAT_TEXTREVIEW
 
        def script_speechMode(self,gesture):
                curMode=speech.speechMode
@@ -740,11 +783,13 @@ class GlobalCommands(ScriptableObject):
                sayAllHandler.readText(sayAllHandler.CURSOR_REVIEW)
        # Translators: Input help mode message for say all in review cursor 
command.
        script_review_sayAll.__doc__ = _("reads from the review cursor  up to 
end of current text, moving the review cursor as it goes")
+       script_review_sayAll.category=SCRCAT_TEXTREVIEW
 
        def script_sayAll(self,gesture):
                sayAllHandler.readText(sayAllHandler.CURSOR_CARET)
        # Translators: Input help mode message for say all with system caret 
command.
        script_sayAll.__doc__ = _("reads from the system caret up to the end of 
the text, moving the caret as it goes")
+       script_sayAll.category=SCRCAT_SYSTEMCARET
 
        def script_reportFormatting(self,gesture):
                formatConfig={
@@ -782,6 +827,7 @@ class GlobalCommands(ScriptableObject):
                ui.message(" ".join(textList))
        # Translators: Input help mode message for report formatting command.
        script_reportFormatting.__doc__ = _("Reports formatting info for the 
current review cursor position within a document")
+       script_reportFormatting.category=SCRCAT_TEXTREVIEW
 
        def script_reportCurrentFocus(self,gesture):
                focusObject=api.getFocusObject()
@@ -835,6 +881,7 @@ class GlobalCommands(ScriptableObject):
                ui.message(state)
        # Translators: Input help mode message for toggle mouse tracking 
command.
        script_toggleMouseTracking.__doc__=_("Toggles the reporting of 
information as the mouse moves")
+       script_toggleMouseTracking.category=SCRCAT_MOUSE
 
        def script_title(self,gesture):
                obj=api.getForegroundObject()
@@ -1089,6 +1136,7 @@ class GlobalCommands(ScriptableObject):
                ui.message(_("Start marked"))
        # Translators: Input help mode message for mark review cursor position 
for copy command (that is, marks the current review cursor position as the 
starting point for text to be copied).
        script_review_markStartForCopy.__doc__ = _("Marks the current position 
of the review cursor as the start of text to be copied")
+       script_review_markStartForCopy.category=SCRCAT_TEXTREVIEW
 
        def script_review_copy(self, gesture):
                if not getattr(self, "_copyStartMarker", None):
@@ -1112,6 +1160,7 @@ class GlobalCommands(ScriptableObject):
                self._copyStartMarker = None
        # Translators: Input help mode message for copy selected review cursor 
text to clipboard command.
        script_review_copy.__doc__ = _("Retrieves the text from the previously 
set start marker up to and including the current position of the review cursor 
and copies it to the clipboard")
+       script_review_copy.category=SCRCAT_TEXTREVIEW
 
        def script_braille_scrollBack(self, gesture):
                braille.handler.scrollBack()
@@ -1178,6 +1227,7 @@ class GlobalCommands(ScriptableObject):
                        ui.message(_("no next"))
        # Translators: Input help mode message for a touchscreen gesture.
        script_navigatorObject_nextInFlow.__doc__=_("Moves to the next object 
in a flattened view of the object navigation hierarchy")
+       script_navigatorObject_nextInFlow.category=SCRCAT_OBJECTNAVIGATION
 
        def script_navigatorObject_previousInFlow(self,gesture):
                curObject=api.getNavigatorObject()
@@ -1195,6 +1245,7 @@ class GlobalCommands(ScriptableObject):
                        ui.message(_("no next"))
        # Translators: Input help mode message for a touchscreen gesture.
        script_navigatorObject_previousInFlow.__doc__=_("Moves to the previous 
object in a flattened view of the object navigation hierarchy")
+       script_navigatorObject_previousInFlow.category=SCRCAT_OBJECTNAVIGATION
 
        def script_touch_changeMode(self,gesture):
                mode=touchHandler.handler._curTouchMode


https://bitbucket.org/nvdaaddonteam/nvda/commits/a09af714be5e/
Changeset:   a09af714be5e
Branch:      None
User:        mdcurran
Date:        2013-09-04 01:24:46
Summary:     Added more  script categories to globalCommands and associated 
many more scripts with them. speech, braille, tools, system, focus, config.

Affected #:  1 file

diff --git a/source/globalCommands.py b/source/globalCommands.py
index 1a8f4c9..3ebcba4 100755
--- a/source/globalCommands.py
+++ b/source/globalCommands.py
@@ -49,6 +49,27 @@ SCRCAT_SYSTEMCARET = _("System caret")
 #: Script category for mouse commands.
 # Translators: The name of a category of NVDA commands.
 SCRCAT_MOUSE = _("Mouse")
+#: Script category for mouse commands.
+# Translators: The name of a category of NVDA commands.
+SCRCAT_SPEECH = _("Speech")
+#: Script category for configuration dialogs commands.
+# Translators: The name of a category of NVDA commands.
+SCRCAT_CONFIG = _("Configuration")
+#: Script category for Braille commands.
+# Translators: The name of a category of NVDA commands.
+SCRCAT_BRAILLE = _("Braille")
+#: Script category for tools commands.
+# Translators: The name of a category of NVDA commands.
+SCRCAT_TOOLS = _("Tools")
+#: Script category for touch commands.
+# Translators: The name of a category of NVDA commands.
+SCRCAT_TOUCH = _("Touch screen")
+#: Script category for focus commands.
+# Translators: The name of a category of NVDA commands.
+SCRCAT_FOCUS = _("System focus")
+#: Script category for system status  commands.
+# Translators: The name of a category of NVDA commands.
+SCRCAT_SYSTEM = _("System status")
 
 class GlobalCommands(ScriptableObject):
        """Commands that are available at all times, regardless of the current 
focus.
@@ -169,6 +190,7 @@ class GlobalCommands(ScriptableObject):
                ui.message(text)
        # Translators: Input help mode message for report date and time command.
        script_dateTime.__doc__=_("If pressed once, reports the current time. 
If pressed twice, reports the current date")
+       script_dateTime.category=SCRCAT_SYSTEM
 
        def script_increaseSynthSetting(self,gesture):
                settingName=globalVars.settingsRing.currentSettingName
@@ -180,6 +202,7 @@ class GlobalCommands(ScriptableObject):
                ui.message("%s %s" % (settingName,settingValue))
        # Translators: Input help mode message for increase synth setting value 
command.
        script_increaseSynthSetting.__doc__=_("Increases the currently active 
setting in the synth settings ring")
+       script_increaseSynthSetting.category=SCRCAT_SPEECH
 
        def script_decreaseSynthSetting(self,gesture):
                settingName=globalVars.settingsRing.currentSettingName
@@ -190,6 +213,7 @@ class GlobalCommands(ScriptableObject):
                ui.message("%s %s" % (settingName,settingValue))
        # Translators: Input help mode message for decrease synth setting value 
command.
        script_decreaseSynthSetting.__doc__=_("Decreases the currently active 
setting in the synth settings ring")
+       script_decreaseSynthSetting.category=SCRCAT_SPEECH
 
        def script_nextSynthSetting(self,gesture):
                nextSettingName=globalVars.settingsRing.next()
@@ -200,6 +224,7 @@ class GlobalCommands(ScriptableObject):
                ui.message("%s %s"%(nextSettingName,nextSettingValue))
        # Translators: Input help mode message for next synth setting command.
        script_nextSynthSetting.__doc__=_("Moves to the next available setting 
in the synth settings ring")
+       script_nextSynthSetting.category=SCRCAT_SPEECH
 
        def script_previousSynthSetting(self,gesture):
                previousSettingName=globalVars.settingsRing.previous()
@@ -210,6 +235,7 @@ class GlobalCommands(ScriptableObject):
                ui.message("%s %s"%(previousSettingName,previousSettingValue))
        # Translators: Input help mode message for previous synth setting 
command.
        script_previousSynthSetting.__doc__=_("Moves to the previous available 
setting in the synth settings ring")
+       script_previousSynthSetting.category=SCRCAT_SPEECH
 
        def script_toggleSpeakTypedCharacters(self,gesture):
                if config.conf["keyboard"]["speakTypedCharacters"]:
@@ -223,6 +249,7 @@ class GlobalCommands(ScriptableObject):
                ui.message(state)
        # Translators: Input help mode message for toggle speaked typed 
characters command.
        script_toggleSpeakTypedCharacters.__doc__=_("Toggles on and off the 
speaking of typed characters")
+       script_toggleSpeakTypedCharacters.category=SCRCAT_SPEECH
 
        def script_toggleSpeakTypedWords(self,gesture):
                if config.conf["keyboard"]["speakTypedWords"]:
@@ -236,6 +263,7 @@ class GlobalCommands(ScriptableObject):
                ui.message(state)
        # Translators: Input help mode message for toggle speak typed words 
command.
        script_toggleSpeakTypedWords.__doc__=_("Toggles on and off the speaking 
of typed words")
+       script_toggleSpeakTypedWords.category=SCRCAT_SPEECH
 
        def script_toggleSpeakCommandKeys(self,gesture):
                if config.conf["keyboard"]["speakCommandKeys"]:
@@ -249,6 +277,7 @@ class GlobalCommands(ScriptableObject):
                ui.message(state)
        # Translators: Input help mode message for toggle speak command keys 
command.
        script_toggleSpeakCommandKeys.__doc__=_("Toggles on and off the 
speaking of typed keys, that are not specifically characters")
+       script_toggleSpeakCommandKeys.category=SCRCAT_SPEECH
 
        def script_cycleSpeechSymbolLevel(self,gesture):
                curLevel = config.conf["speech"]["symbolLevel"]
@@ -265,6 +294,7 @@ class GlobalCommands(ScriptableObject):
                ui.message(_("symbol level %s") % name)
        # Translators: Input help mode message for cycle speech symbol level 
command.
        script_cycleSpeechSymbolLevel.__doc__=_("Cycles through speech symbol 
levels which determine what symbols are spoken")
+       script_cycleSpeechSymbolLevel.category=SCRCAT_SPEECH
 
        def script_moveMouseToNavigatorObject(self,gesture):
                obj=api.getNavigatorObject() 
@@ -311,6 +341,7 @@ class GlobalCommands(ScriptableObject):
                        # Translators: reported when there are no other 
available review modes for this object 
                        ui.message(_("No next review mode"))
        script_reviewMode_next.__doc__=_("Switches to the next review mode 
(e.g. object, document or screen) and positions the review position at the 
point of the navigator object")
+       script_reviewMode_next.category=SCRCAT_TEXTREVIEW
 
        def script_reviewMode_previous(self,gesture):
                label=review.nextMode(prev=True)
@@ -323,7 +354,8 @@ class GlobalCommands(ScriptableObject):
                        # Translators: reported when there are no  other 
available review modes for this object 
                        ui.message(_("No previous review mode"))
        script_reviewMode_previous.__doc__=_("Switches to the previous review 
mode (e.g. object, document or screen) and positions the review position at the 
point of the navigator object") 
-
+       script_reviewMode_previous.category=SCRCAT_TEXTREVIEW
+
        def script_navigatorObject_current(self,gesture):
                curObject=api.getNavigatorObject()
                if not isinstance(curObject,NVDAObject):
@@ -736,6 +768,7 @@ class GlobalCommands(ScriptableObject):
                speech.speechMode=newMode
        # Translators: Input help mode message for toggle speech mode command.
        script_speechMode.__doc__=_("Toggles between the speech modes of off, 
beep and talk. When set to off NVDA will not speak anything. If beeps then NVDA 
will simply beep each time it its supposed to speak something. If talk then 
NVDA wil just speak normally.")
+       script_speechMode.category=SCRCAT_SPEECH
 
        def script_moveToParentTreeInterceptor(self,gesture):
                obj=api.getFocusObject()
@@ -754,6 +787,7 @@ class GlobalCommands(ScriptableObject):
                        
wx.CallLater(50,eventHandler.executeEvent,"gainFocus",parent.treeInterceptor.rootNVDAObject)
        # Translators: Input help mode message for move to next document with 
focus command, mostly used in web browsing to move from embedded object to the 
webpage document.
        script_moveToParentTreeInterceptor.__doc__=_("Moves the focus to the 
next closest document that contains the focus")
+       script_moveToParentTreeInterceptor.category=SCRCAT_FOCUS
 
        def script_toggleVirtualBufferPassThrough(self,gesture):
                vbuf = api.getFocusObject().treeInterceptor
@@ -840,6 +874,7 @@ class GlobalCommands(ScriptableObject):
                        speech.speakMessage(_("no focus"))
        # Translators: Input help mode message for report current focus command.
        script_reportCurrentFocus.__doc__ = _("reports the object with focus")
+       script_reportCurrentFocus.category=SCRCAT_FOCUS
 
        def script_reportStatusLine(self,gesture):
                obj = api.getStatusBar()
@@ -868,6 +903,7 @@ class GlobalCommands(ScriptableObject):
                        speech.speakSpelling(text)
        # Translators: Input help mode message for report status line text 
command.
        script_reportStatusLine.__doc__ = _("reads the current application 
status bar and moves the navigator to it")
+       script_reportStatusLine.category=SCRCAT_FOCUS
 
        def script_toggleMouseTracking(self,gesture):
                if config.conf["mouse"]["enableMouseTracking"]:
@@ -901,6 +937,7 @@ class GlobalCommands(ScriptableObject):
                                ui.message(_("%s copied to clipboard")%title)
        # Translators: Input help mode message for report title bar command.
        script_title.__doc__=_("Reports the title of the current application or 
foreground window. If pressed twice, spells the title. If pressed three times, 
copies the title to the clipboard")
+       script_title.category=SCRCAT_FOCUS
 
        def script_speakForeground(self,gesture):
                obj=api.getForegroundObject()
@@ -908,6 +945,7 @@ class GlobalCommands(ScriptableObject):
                        sayAllHandler.readObjects(obj)
        # Translators: Input help mode message for read foreground object 
command (usually the foreground window).
        script_speakForeground.__doc__ = _("speaks the current foreground 
object")
+       script_speakForeground.category=SCRCAT_FOCUS
 
        def script_test_navigatorDisplayModelText(self,gesture):
                obj=api.getNavigatorObject()
@@ -920,6 +958,7 @@ class GlobalCommands(ScriptableObject):
                log.info("Developer info for navigator object:\n%s" % 
"\n".join(obj.devInfo), activateLogViewer=True)
        # Translators: Input help mode message for developer info for current 
navigator object command, used by developers to examine technical info on 
navigator object. This command also serves as a shortcut to open NVDA log 
viewer.
        script_navigatorObject_devInfo.__doc__ = _("Logs information about the 
current navigator object which is useful to developers and activates the log 
viewer so the information can be examined.")
+       script_navigatorObject_devInfo.category=SCRCAT_TOOLS
 
        def script_toggleProgressBarOutput(self,gesture):
                
outputMode=config.conf["presentation"]["progressBarUpdates"]["progressBarOutputMode"]
@@ -1005,6 +1044,7 @@ class GlobalCommands(ScriptableObject):
                ui.message(text)
        # Translators: Input help mode message for report battery status 
command.
        script_say_battery_status.__doc__ = _("reports battery status and time 
remaining if AC is not plugged in")
+       script_say_battery_status.category=SCRCAT_SYSTEM
 
        def script_passNextKeyThrough(self,gesture):
                keyboardHandler.passNextKeyThrough()
@@ -1029,51 +1069,62 @@ class GlobalCommands(ScriptableObject):
                ui.message(message)
        # Translators: Input help mode message for report current program name 
and app module name command.
        script_reportAppModuleInfo.__doc__ = _("Speaks the filename of the 
active application along with the name of the currently loaded appModule")
+       script_reportAppModuleInfo.category=SCRCAT_TOOLS
 
        def script_activateGeneralSettingsDialog(self, gesture):
                wx.CallAfter(gui.mainFrame.onGeneralSettingsCommand, None)
        # Translators: Input help mode message for go to general settings 
dialog command.
        script_activateGeneralSettingsDialog.__doc__ = _("Shows the NVDA 
general settings dialog")
+       script_activateGeneralSettingsDialog.category=SCRCAT_CONFIG
+
 
        def script_activateSynthesizerDialog(self, gesture):
                wx.CallAfter(gui.mainFrame.onSynthesizerCommand, None)
        # Translators: Input help mode message for go to synthesizer dialog 
command.
        script_activateSynthesizerDialog.__doc__ = _("Shows the NVDA 
synthesizer dialog")
+       script_activateSynthesizerDialog.category=SCRCAT_CONFIG
 
        def script_activateVoiceDialog(self, gesture):
                wx.CallAfter(gui.mainFrame.onVoiceCommand, None)
        # Translators: Input help mode message for go to voice settings dialog 
command.
        script_activateVoiceDialog.__doc__ = _("Shows the NVDA voice settings 
dialog")
+       script_activateVoiceDialog.category=SCRCAT_CONFIG
 
        def script_activateKeyboardSettingsDialog(self, gesture):
                wx.CallAfter(gui.mainFrame.onKeyboardSettingsCommand, None)
        # Translators: Input help mode message for go to keyboard settings 
dialog command.
        script_activateKeyboardSettingsDialog.__doc__ = _("Shows the NVDA 
keyboard settings dialog")
+       script_activateKeyboardSettingsDialog.category=SCRCAT_CONFIG
 
        def script_activateMouseSettingsDialog(self, gesture):
                wx.CallAfter(gui.mainFrame.onMouseSettingsCommand, None)
        # Translators: Input help mode message for go to mouse settings dialog 
command.
        script_activateMouseSettingsDialog.__doc__ = _("Shows the NVDA mouse 
settings dialog")
+       script_activateMouseSettingsDialog.category=SCRCAT_CONFIG
 
        def script_activateObjectPresentationDialog(self, gesture):
                wx.CallAfter(gui.mainFrame. onObjectPresentationCommand, None)
        # Translators: Input help mode message for go to object presentation 
dialog command.
        script_activateObjectPresentationDialog.__doc__ = _("Shows the NVDA 
object presentation settings dialog")
+       script_activateObjectPresentationDialog.category=SCRCAT_CONFIG
 
        def script_activateBrowseModeDialog(self, gesture):
                wx.CallAfter(gui.mainFrame.onBrowseModeCommand, None)
        # Translators: Input help mode message for go to browse mode dialog 
command.
        script_activateBrowseModeDialog.__doc__ = _("Shows the NVDA browse mode 
settings dialog")
+       script_activateBrowseModeDialog.category=SCRCAT_CONFIG
 
        def script_activateDocumentFormattingDialog(self, gesture):
                wx.CallAfter(gui.mainFrame.onDocumentFormattingCommand, None)
        # Translators: Input help mode message for go to document formatting 
dialog command.
        script_activateDocumentFormattingDialog.__doc__ = _("Shows the NVDA 
document formatting settings dialog")
+       script_activateDocumentFormattingDialog.category=SCRCAT_CONFIG
 
        def script_saveConfiguration(self,gesture):
                wx.CallAfter(gui.mainFrame.onSaveConfigurationCommand, None)
        # Translators: Input help mode message for save current configuration 
command.
        script_saveConfiguration.__doc__ = _("Saves the current NVDA 
configuration")
+       script_saveConfiguration.category=SCRCAT_CONFIG
 
        def script_revertConfiguration(self,gesture):
                scriptCount=scriptHandler.getLastScriptRepeatCount()
@@ -1083,11 +1134,13 @@ class GlobalCommands(ScriptableObject):
                        
gui.mainFrame.onRevertToDefaultConfigurationCommand(None)
        # Translators: Input help mode message for apply last saved or default 
settings command.
        script_revertConfiguration.__doc__ = _("Pressing once reverts the 
current configuration to the most recently saved state. Pressing three times 
reverts to factory defaults.")
+       script_revertConfiguration.category=SCRCAT_CONFIG
 
        def script_activateInputGesturesDialog(self, gesture):
                wx.CallAfter(gui.mainFrame.onInputGesturesCommand, None)
        # Translators: Input help mode message for go to Input Gestures dialog 
command.
        script_activateInputGesturesDialog.__doc__ = _("Shows the NVDA input 
gestures dialog")
+       script_activateInputGesturesDialog.category=SCRCAT_CONFIG
 
        def script_activatePythonConsole(self,gesture):
                if globalVars.appArgs.secure:
@@ -1099,6 +1152,7 @@ class GlobalCommands(ScriptableObject):
                pythonConsole.activate()
        # Translators: Input help mode message for activate python console 
command.
        script_activatePythonConsole.__doc__ = _("Activates the NVDA Python 
Console, primarily useful for development")
+       script_activatePythonConsole.category=SCRCAT_TOOLS
 
        def script_braille_toggleTether(self, gesture):
                if braille.handler.tether == braille.handler.TETHER_FOCUS:
@@ -1111,6 +1165,7 @@ class GlobalCommands(ScriptableObject):
                ui.message(_("Braille tethered to %s") % tetherMsg)
        # Translators: Input help mode message for toggle braille tether to 
command (tethered means connected to or follows).
        script_braille_toggleTether.__doc__ = _("Toggle tethering of braille 
between the focus and the review position")
+       script_braille_toggleTether.category=SCRCAT_BRAILLE
 
        def script_reportClipboardText(self,gesture):
                try:
@@ -1129,6 +1184,7 @@ class GlobalCommands(ScriptableObject):
                        ui.message(_("The clipboard contains a large portion of 
text. It is %s characters long") % len(text))
        # Translators: Input help mode message for report clipboard text 
command.
        script_reportClipboardText.__doc__ = _("Reports the text on the Windows 
clipboard")
+       script_reportClipboardText.category=SCRCAT_SYSTEM
 
        def script_review_markStartForCopy(self, gesture):
                self._copyStartMarker = api.getReviewPosition().copy()
@@ -1167,34 +1223,40 @@ class GlobalCommands(ScriptableObject):
        # Translators: Input help mode message for a braille command.
        script_braille_scrollBack.__doc__ = _("Scrolls the braille display 
back")
        script_braille_scrollBack.bypassInputHelp = True
+       script_braille_scrollBack.category=SCRCAT_BRAILLE
 
        def script_braille_scrollForward(self, gesture):
                braille.handler.scrollForward()
        # Translators: Input help mode message for a braille command.
        script_braille_scrollForward.__doc__ = _("Scrolls the braille display 
forward")
        script_braille_scrollForward.bypassInputHelp = True
+       script_braille_scrollForward.category=SCRCAT_BRAILLE
 
        def script_braille_routeTo(self, gesture):
                braille.handler.routeTo(gesture.routingIndex)
        # Translators: Input help mode message for a braille command.
        script_braille_routeTo.__doc__ = _("Routes the cursor to or activates 
the object under this braille cell")
+       script_braille_routeTo.category=SCRCAT_BRAILLE
 
        def script_braille_previousLine(self, gesture):
                if braille.handler.buffer.regions: 
                        
braille.handler.buffer.regions[-1].previousLine(start=True)
        # Translators: Input help mode message for a braille command.
        script_braille_previousLine.__doc__ = _("Moves the braille display to 
the previous line")
+       script_braille_previousLine.category=SCRCAT_BRAILLE
 
        def script_braille_nextLine(self, gesture):
                if braille.handler.buffer.regions: 
                        braille.handler.buffer.regions[-1].nextLine()
        # Translators: Input help mode message for a braille command.
        script_braille_nextLine.__doc__ = _("Moves the braille display to the 
next line")
+       script_braille_nextLine.category=SCRCAT_BRAILLE
 
        def script_braille_dots(self, gesture):
                brailleInput.handler.input(gesture.dots)
        # Translators: Input help mode message for a braille command.
        script_braille_dots.__doc__= _("Inputs braille dots via the braille 
keyboard")
+       script_braille_dots.category=SCRCAT_BRAILLE
 
        def script_reloadPlugins(self, gesture):
                import globalPluginHandler
@@ -1205,6 +1267,7 @@ class GlobalCommands(ScriptableObject):
                ui.message(_("Plugins reloaded"))
        # Translators: Input help mode message for reload plugins command.
        script_reloadPlugins.__doc__=_("Reloads app modules and global plugins 
without restarting NVDA, which can be Useful for developers")
+       script_reloadPlugins.category=SCRCAT_TOOLS
 
        def script_navigatorObject_nextInFlow(self,gesture):
                curObject=api.getNavigatorObject()
@@ -1256,16 +1319,20 @@ class GlobalCommands(ScriptableObject):
                ui.message(_("%s mode")%newMode)
        # Translators: Input help mode message for a touchscreen gesture.
        script_touch_changeMode.__doc__=_("cycles between available touch 
modes")
+       script_touch_changeMode.category=SCRCAT_TOUCH
+
 
        def script_touch_newExplore(self,gesture):
                
touchHandler.handler.screenExplorer.moveTo(gesture.tracker.x,gesture.tracker.y,new=True)
        # Translators: Input help mode message for a touchscreen gesture.
        script_touch_newExplore.__doc__=_("Reports the object and content 
directly under your finger")
+       script_touch_newExplore.category=SCRCAT_TOUCH
 
        def script_touch_explore(self,gesture):
                
touchHandler.handler.screenExplorer.moveTo(gesture.tracker.x,gesture.tracker.y)
        # Translators: Input help mode message for a touchscreen gesture.
        script_touch_explore.__doc__=_("Reports the new object or content under 
your finger if different to where your finger was last")
+       script_touch_explore.category=SCRCAT_TOUCH
 
        def script_touch_hoverUp(self,gesture):
                #Specifically for touch typing with onscreen keyboard keys
@@ -1273,6 +1340,7 @@ class GlobalCommands(ScriptableObject):
                import NVDAObjects.UIA
                if isinstance(obj,NVDAObjects.UIA.UIA) and 
obj.UIAElement.cachedClassName=="CRootKey":
                        obj.doAction()
+       script_touch_hoverUp.category=SCRCAT_TOUCH
 
        __gestures = {
                # Basic


https://bitbucket.org/nvdaaddonteam/nvda/commits/4d16dce51182/
Changeset:   4d16dce51182
Branch:      None
User:        mdcurran
Date:        2013-09-04 03:49:43
Summary:     More script categories for globalCommands.

Affected #:  1 file

diff --git a/source/globalCommands.py b/source/globalCommands.py
index 3ebcba4..4221545 100755
--- a/source/globalCommands.py
+++ b/source/globalCommands.py
@@ -70,6 +70,9 @@ SCRCAT_FOCUS = _("System focus")
 #: Script category for system status  commands.
 # Translators: The name of a category of NVDA commands.
 SCRCAT_SYSTEM = _("System status")
+#: Script category for input  commands.
+# Translators: The name of a category of NVDA commands.
+SCRCAT_INPUT = _("Input")
 
 class GlobalCommands(ScriptableObject):
        """Commands that are available at all times, regardless of the current 
focus.
@@ -85,6 +88,8 @@ class GlobalCommands(ScriptableObject):
                ui.message(state)
        # Translators: Input help mode message for toggle input help command.
        script_toggleInputHelp.__doc__=_("Turns input help on or off. When on, 
any input such as pressing a key on the keyboard will tell you what script is 
associated with that input, if any.")
+       script_toggleInputHelp.category=SCRCAT_INPUT
+
 
        def script_toggleCurrentAppSleepMode(self,gesture):
                curFocus=api.getFocusObject()
@@ -981,6 +986,7 @@ class GlobalCommands(ScriptableObject):
                
config.conf["presentation"]["progressBarUpdates"]["progressBarOutputMode"]=outputMode
        # Translators: Input help mode message for toggle progress bar output 
command.
        script_toggleProgressBarOutput.__doc__=_("Toggles between beeps, 
speech, beeps and speech, and off, for reporting progress bar updates")
+       script_toggleProgressBarOutput.category=SCRCAT_SPEECH
 
        def script_toggleReportDynamicContentChanges(self,gesture):
                if config.conf["presentation"]["reportDynamicContentChanges"]:
@@ -994,6 +1000,7 @@ class GlobalCommands(ScriptableObject):
                ui.message(state)
        # Translators: Input help mode message for toggle dynamic content 
changes command.
        script_toggleReportDynamicContentChanges.__doc__=_("Toggles on and off 
the reporting of dynamic content changes, such as new text in dos console 
windows")
+       script_toggleReportDynamicContentChanges.category=SCRCAT_SPEECH
 
        def script_toggleCaretMovesReviewCursor(self,gesture):
                if config.conf["reviewCursor"]["followCaret"]:
@@ -1007,6 +1014,7 @@ class GlobalCommands(ScriptableObject):
                ui.message(state)
        # Translators: Input help mode message for toggle caret moves review 
cursor command.
        script_toggleCaretMovesReviewCursor.__doc__=_("Toggles on and off the 
movement of the review cursor due to the caret moving.")
+       script_toggleCaretMovesReviewCursor.category=SCRCAT_TEXTREVIEW
 
        def script_toggleFocusMovesNavigatorObject(self,gesture):
                if config.conf["reviewCursor"]["followFocus"]:
@@ -1020,6 +1028,7 @@ class GlobalCommands(ScriptableObject):
                ui.message(state)
        # Translators: Input help mode message for toggle focus moves navigator 
object command.
        script_toggleFocusMovesNavigatorObject.__doc__=_("Toggles on and off 
the movement of the navigator object due to focus changes") 
+       script_toggleFocusMovesNavigatorObject.category=SCRCAT_OBJECTNAVIGATION
 
        #added by Rui Batista<ruiandrebatista@xxxxxxxxx> to implement a battery 
status script
        def script_say_battery_status(self,gesture):
@@ -1052,6 +1061,7 @@ class GlobalCommands(ScriptableObject):
                ui.message(_("Pass next key through"))
        # Translators: Input help mode message for pass next key through 
command.
        script_passNextKeyThrough.__doc__=_("The next key that is pressed will 
not be handled at all by NVDA, it will be passed directly through to Windows.")
+       script_passNextKeyThrough.category=SCRCAT_INPUT
 
        def script_reportAppModuleInfo(self,gesture):
                focus=api.getFocusObject()


https://bitbucket.org/nvdaaddonteam/nvda/commits/7caf27df5c80/
Changeset:   7caf27df5c80
Branch:      None
User:        jteh
Date:        2013-09-05 08:10:19
Summary:     inputCore: Provide a way to get display text from a gesture 
identifier.

In order to support this for a particular source, an InputGesture subclass for 
that source must implement a new getDisplayTextForIdentifier class method. That 
InputGesture subclass must also be registered for the source prefix using 
registerGestureSource.
Once this is done, inputCore.getDisplayTextForGestureIdentifier can be used for 
any associated gesture identifiers.

Affected #:  1 file

diff --git a/source/inputCore.py b/source/inputCore.py
index 62236c6..67292d3 100644
--- a/source/inputCore.py
+++ b/source/inputCore.py
@@ -13,6 +13,7 @@ For example, it is used to execute gestures and handle input 
help.
 import sys
 import os
 import itertools
+import weakref
 import configobj
 import baseObject
 import scriptHandler
@@ -124,6 +125,22 @@ class InputGesture(baseObject.AutoPropertyObject):
                """
                return None
 
+       @classmethod
+       def getDisplayTextForIdentifier(cls, identifier):
+               """Get the text to be presented to the user describing a given 
gesture identifier.
+               This should only be called for gesture identifiers associated 
with this class.
+               Most callers will want L{inputCore.getDisplayTextForIdentifier} 
instead.
+               The display text consists of two strings:
+               the gesture's source (e.g. "laptop keyboard")
+               and the specific gesture (e.g. "alt+tab").
+               @param identifier: The normalized gesture identifier in 
question.
+               @type identifier: basestring
+               @return: A tuple of (source, specificGesture).
+               @rtype: tuple of (basestring, basestring)
+               @raise Exception: If no display text can be determined.
+               """
+               raise NotImplementedError
+
 class GlobalGestureMap(object):
        """Maps gestures to scripts anywhere in NVDA.
        This is used to allow users and locales to bind gestures in addition to 
those bound by individual scriptable objects.
@@ -636,6 +653,59 @@ def normalizeGestureIdentifier(identifier):
        main = "+".join(frozenset(main))
        return u"{0}:{1}".format(prefix, main).lower()
 
+#: Maps registered source prefix strings to L{InputGesture} classes.
+gestureSources = weakref.WeakValueDictionary()
+
+def registerGestureSource(source, gestureCls):
+       """Register an input gesture class for a source prefix string.
+       The specified gesture class will be used for queries regarding all 
gesture identifiers with the given source prefix.
+       For example, if "kb" is registered with the C{KeyboardInputGesture} 
class,
+       any queries for "kb:tab" or "kb(desktop):tab" will be directed to the 
C{KeyboardInputGesture} class.
+       If there is no exact match for the source, any parenthesised portion is 
stripped.
+       For example, for "br(baum):d1", if "br(baum)" isn't registered,
+       "br" will be used if it is registered.
+       This registration is used, for example, to get the display text for a 
gesture identifier.
+       @param source: The source prefix for associated gesture identifiers.
+       @type source: basestring
+       @param gestureCls: The input gesture class.
+       @type gestureCls: L{InputGesture}
+       """
+       gestureSources[source] = gestureCls
+
+def _getGestureClsForIdentifier(identifier):
+       """Get the registered gesture class for an identifier.
+       """
+       source = identifier.split(":", 1)[0]
+       try:
+               return gestureSources[source]
+       except KeyError:
+               pass
+       genSource = source.split("(", 1)[0]
+       if genSource:
+               try:
+                       return gestureSources[genSource]
+               except KeyError:
+                       pass
+       raise LookupError("Gesture source not registered: %s" % source)
+
+def getDisplayTextForGestureIdentifier(identifier):
+       """Get the text to be presented to the user describing a given gesture 
identifier.
+       The display text consists of two strings:
+       the gesture's source (e.g. "laptop keyboard")
+       and the specific gesture (e.g. "alt+tab").
+       @param identifier: The normalized gesture identifier in question.
+       @type identifier: basestring
+       @return: A tuple of (source, specificGesture).
+       @rtype: tuple of (basestring, basestring)
+       @raise LookupError: If no display text can be determined.
+       """
+       gcls = _getGestureClsForIdentifier(identifier)
+       try:
+               return gcls.getDisplayTextForIdentifier(identifier)
+       except:
+               raise
+               raise LookupError("Couldn't get display text for identifier: 
%s" % identifier)
+
 #: The singleton input manager instance.
 #: @type: L{InputManager}
 manager = None


https://bitbucket.org/nvdaaddonteam/nvda/commits/99b58fbc54e6/
Changeset:   99b58fbc54e6
Branch:      None
User:        jteh
Date:        2013-09-05 08:15:02
Summary:     Implement support for getting display text for keyboard gesture 
identifiers.

Because this deals with normalized identifiers, all of the key names in the 
keyLabels module had to be converted to entirely lower case. Also, the 
generalized modifiers had to be added to 
KeyboardInputGesture.NORMAL_MODIFIER_KEYS so we have a way to tell they are 
modifiers.

Affected #:  2 files

diff --git a/source/keyLabels.py b/source/keyLabels.py
index 082c35b..3675259 100644
--- a/source/keyLabels.py
+++ b/source/keyLabels.py
@@ -6,45 +6,45 @@
 
 localizedKeyLabels = {
        # Translators: This is the name of the back key found on multimedia 
keyboards for controlling the web-browser.
-       'browserBack': _("back"),
+       'browserback': _("back"),
        # Translators: This is the name of the forward key found on multimedia 
keyboards for controlling the web-browser.
-       'browserForward': _("forward"),
+       'browserforward': _("forward"),
        # Translators: This is the name of the refresh key found on multimedia 
keyboards for controlling the web-browser.
-       'browserRefresh': _("refresh"),
+       'browserrefresh': _("refresh"),
        # Translators: This is the name of the stop key found on multimedia 
keyboards for controlling the web-browser.
-       'browserStop': _("browser stop"),
+       'browserstop': _("browser stop"),
        # Translators: This is the name of the back key found on multimedia 
keyboards to goto the search page of the web-browser.
-       'browserSearch': _("search page"),
+       'browsersearch': _("search page"),
        # Translators: This is the name of the favorites key found on 
multimedia keyboards to open favorites in the web-browser.
-       'browserFavorites': _("favorites"),
+       'browserfavorites': _("favorites"),
        # Translators: This is the name of the home key found on multimedia 
keyboards to goto the home page in the web-browser.
-       'browserHome': _("home page"),
+       'browserhome': _("home page"),
        # Translators: This is the name of the mute key found on multimedia 
keyboards to control playback volume.
-       'volumeMute': _("mute"),
+       'volumemute': _("mute"),
        # Translators: This is the name of the volume down key found on 
multimedia keyboards to reduce playback volume.
-       'volumeDown': _("volume down"),
+       'volumedown': _("volume down"),
        # Translators: This is the name of the volume up key found on 
multimedia keyboards to increase playback volume.
-       'volumeUp': _("volume up"),
+       'volumeup': _("volume up"),
        # Translators: This is the name of the next track key found on 
multimedia keyboards to skip to next track in the mediaplayer.
-       'mediaNextTrack': _("next track"),
+       'medianexttrack': _("next track"),
        # Translators: This is the name of the next track key found on 
multimedia keyboards to skip to next track in the mediaplayer.
-       'mediaPrevTrack': _("previous track"),
+       'mediaprevtrack': _("previous track"),
        # Translators: This is the name of the stop key found on multimedia 
keyboards to stop the current playing track in the mediaplayer.
-       'mediaStop': _("stop"),
+       'mediastop': _("stop"),
        # Translators: This is the name of the play/pause key found on 
multimedia keyboards to play/pause the current playing track in the mediaplayer.
-       'mediaPlayPause': _("play pause"),
+       'mediaplaypause': _("play pause"),
        # Translators: This is the name of the launch email key found on 
multimedia keyboards to open an email client.
-       'launchMail': _("email"),
+       'launchmail': _("email"),
        # Translators: This is the name of the launch mediaplayer key found on 
multimedia keyboards to launch the mediaplayer.
-       'launchMediaPlayer': _("media player"),
+       'launchmediaplayer': _("media player"),
        # Translators: This is the name of the launch custom application 1 key 
found on multimedia keyboards to launch a user-defined application.
-       'launchApp1': _("custom application 1"),
+       'launchapp1': _("custom application 1"),
        # Translators: This is the name of the launch custom application 2 key 
found on multimedia keyboards to launch a user-defined application.
-       'launchApp2': _("custom application 2"),
+       'launchapp2': _("custom application 2"),
        # Translators: This is the name of a key on the keyboard.
        'backspace': _("backspace"),
        # Translators: This is the name of a key on the keyboard.
-       'capsLock': _("caps lock"),
+       'capslock': _("caps lock"),
        # Translators: This is the name of a key on the keyboard.
        'control': _("ctrl"),
        # Translators: This is the name of a key on the keyboard.
@@ -56,15 +56,15 @@ localizedKeyLabels = {
        # Translators: This is the name of a key on the keyboard.
        'enter': _("enter"),
        # Translators: This is the name of a key on the keyboard.
-       'numpadEnter': _("numpad enter"),
+       'numpadenter': _("numpad enter"),
        # Translators: This is the name of a key on the keyboard.
        'escape': _("escape"),
        # Translators: This is the name of a key on the keyboard.
        'space': _("space"),
        # Translators: This is the name of a key on the keyboard.
-       'pageUp': _("page up"),
+       'pageup': _("page up"),
        # Translators: This is the name of a key on the keyboard.
-       'pageDown': _("page down"),
+       'pagedown': _("page down"),
        # Translators: This is the name of a key on the keyboard.
        'end': _("end"),
        # Translators: This is the name of a key on the keyboard.
@@ -72,23 +72,23 @@ localizedKeyLabels = {
        # Translators: This is the name of a key on the keyboard.
        'delete': _("delete"),
        # Translators: This is the name of a key on the keyboard.
-       'numpadDelete': _("numpad delete"),
+       'numpaddelete': _("numpad delete"),
        # Translators: This is the name of a key on the keyboard.
-       'leftArrow': _("left arrow"),
+       'leftarrow': _("left arrow"),
        # Translators: This is the name of a key on the keyboard.
-       'rightArrow': _("right arrow"),
+       'rightarrow': _("right arrow"),
        # Translators: This is the name of a key on the keyboard.
-       'upArrow': _("up arrow"),
+       'uparrow': _("up arrow"),
        # Translators: This is the name of a key on the keyboard.
-       'downArrow': _("down arrow"),
+       'downarrow': _("down arrow"),
        # Translators: This is the name of a key on the keyboard.
        'applications': _("applications"),
        # Translators: This is the name of a key on the keyboard.
-       'numLock': _("num lock"),
+       'numlock': _("num lock"),
        # Translators: This is the name of a key on the keyboard.
-       'printScreen': _("print screen"),
+       'printscreen': _("print screen"),
        # Translators: This is the name of a key on the keyboard.
-       'scrollLock': _("scroll lock"),
+       'scrolllock': _("scroll lock"),
        # Translators: This is the name of a key on the keyboard.
        'numpad4': _("numpad 4"),
        # Translators: This is the name of a key on the keyboard.
@@ -108,29 +108,29 @@ localizedKeyLabels = {
        # Translators: This is the name of a key on the keyboard.
        'numpad5': _("numpad 5"),
        # Translators: This is the name of a key on the keyboard.
-       'numpadDivide': _("numpad divide"),
+       'numpaddivide': _("numpad divide"),
        # Translators: This is the name of a key on the keyboard.
-       'numpadMultiply': _("numpad multiply"),
+       'numpadmultiply': _("numpad multiply"),
        # Translators: This is the name of a key on the keyboard.
-       'numpadMinus': _("numpad minus"),
+       'numpadminus': _("numpad minus"),
        # Translators: This is the name of a key on the keyboard.
-       'numpadPlus': _("numpad plus"),
+       'numpadplus': _("numpad plus"),
        # Translators: This is the name of a key on the keyboard.
-       'leftControl': _("left control"),
+       'leftcontrol': _("left control"),
        # Translators: This is the name of a key on the keyboard.
-       'rightControl': _("right control"),
+       'rightcontrol': _("right control"),
        # Translators: This is the name of a key on the keyboard.
-       'leftWindows': _("left windows"),
+       'leftwindows': _("left windows"),
        # Translators: This is the name of a key on the keyboard.
-       'leftShift': _("left shift"),
+       'leftshift': _("left shift"),
        # Translators: This is the name of a key on the keyboard.
-       'rightShift': _("right shift"),
+       'rightshift': _("right shift"),
        # Translators: This is the name of a key on the keyboard.
-       'leftAlt': _("left alt"),
+       'leftalt': _("left alt"),
        # Translators: This is the name of a key on the keyboard.
-       'rightAlt': _("right alt"),
+       'rightalt': _("right alt"),
        # Translators: This is the name of a key on the keyboard.
-       'rightWindows': _("right windows"),
+       'rightwindows': _("right windows"),
        # Translators: This is the name of a key on the keyboard.
        'break': _("break"),
        # Translators: This is the name of a key on the keyboard.

diff --git a/source/keyboardHandler.py b/source/keyboardHandler.py
index 7ae3e9c..009e7a4 100644
--- a/source/keyboardHandler.py
+++ b/source/keyboardHandler.py
@@ -8,6 +8,7 @@
 """Keyboard support"""
 
 import time
+import re
 import wx
 import winUser
 import vkCodes
@@ -221,12 +222,16 @@ class KeyboardInputGesture(inputCore.InputGesture):
        NORMAL_MODIFIER_KEYS = {
                winUser.VK_LCONTROL: winUser.VK_CONTROL,
                winUser.VK_RCONTROL: winUser.VK_CONTROL,
+               winUser.VK_CONTROL: None,
                winUser.VK_LSHIFT: winUser.VK_SHIFT,
                winUser.VK_RSHIFT: winUser.VK_SHIFT,
+               winUser.VK_SHIFT: None,
                winUser.VK_LMENU: winUser.VK_MENU,
                winUser.VK_RMENU: winUser.VK_MENU,
+               winUser.VK_MENU: None,
                winUser.VK_LWIN: VK_WIN,
                winUser.VK_RWIN: VK_WIN,
+               VK_WIN: None,
        }
 
        #: All possible toggle key vk codes.
@@ -312,7 +317,7 @@ class KeyboardInputGesture(inputCore.InputGesture):
                        key="+".join(self._keyNamesInDisplayOrder))
 
        def _get_displayName(self):
-               return "+".join(localizedKeyLabels.get(key, key) for key in 
self._keyNamesInDisplayOrder)
+               return "+".join(localizedKeyLabels.get(key.lower(), key) for 
key in self._keyNamesInDisplayOrder)
 
        def _get_identifiers(self):
                keyNames = set(self.modifierNames)
@@ -357,7 +362,7 @@ class KeyboardInputGesture(inputCore.InputGesture):
                toggleState = winUser.getKeyState(self.vkCode) & 1
                key = self.mainKeyName
                ui.message(u"{key} {state}".format(
-                       key=localizedKeyLabels.get(key, key),
+                       key=localizedKeyLabels.get(key.lower(), key),
                        state=_("on") if toggleState else _("off")))
 
        def send(self):
@@ -431,3 +436,44 @@ class KeyboardInputGesture(inputCore.InputGesture):
                        raise ValueError
 
                return cls(keys[:-1], vk, 0, ext)
+
+       RE_IDENTIFIER = re.compile(r"^kb(?:\((.+?)\))?:(.*)$")
+       @classmethod
+       def getDisplayTextForIdentifier(cls, identifier):
+               layout, keys = cls.RE_IDENTIFIER.match(identifier).groups()
+               dispSource = None
+               if layout:
+                       try:
+                               # Translators: Used when describing keys on the 
system keyboard with a particular layout.
+                               # %s is replaced with the layout name.
+                               # For example, in English, this might produce 
"laptop keyboard".
+                               dispSource = _("%s keyboard") % 
cls.LAYOUTS[layout]
+                       except KeyError:
+                               pass
+               if not dispSource:
+                       # Translators: Used when describing keys on the system 
keyboard applying to all layouts.
+                       dispSource = _("system keyboard")
+
+               keys = set(keys.split("+"))
+               names = []
+               main = None
+               try:
+                       keys.remove("nvda")
+                       names.append("NVDA")
+               except KeyError:
+                       pass
+               for key in keys:
+                       label = localizedKeyLabels.get(key, key)
+                       vk = vkCodes.byName.get(key)
+                       # vkCodes.byName values are (vk, ext)
+                       if vk:
+                               vk = vk[0]
+                       if vk in cls.NORMAL_MODIFIER_KEYS:
+                               names.append(label)
+                       else:
+                               # The main key must be last, so handle that 
outside the loop.
+                               main = label
+               names.append(main)
+               return dispSource, "+".join(names)
+
+inputCore.registerGestureSource("kb", KeyboardInputGesture)


https://bitbucket.org/nvdaaddonteam/nvda/commits/74c1df1c39f8/
Changeset:   74c1df1c39f8
Branch:      None
User:        jteh
Date:        2013-09-05 08:20:17
Summary:     InputGesturesDialog: Use proper display text when displaying 
gestures if possible.

Affected #:  1 file

diff --git a/source/gui/settingsDialogs.py b/source/gui/settingsDialogs.py
index 6b5421b..a983ebb 100644
--- a/source/gui/settingsDialogs.py
+++ b/source/gui/settingsDialogs.py
@@ -1531,7 +1531,7 @@ class InputGesturesDialog(SettingsDialog):
                                commandInfo = commands[command]
                                tree.SetItemPyData(treeCom, commandInfo)
                                for gesture in commandInfo.gestures:
-                                       treeGes = tree.AppendItem(treeCom, 
gesture)
+                                       treeGes = tree.AppendItem(treeCom, 
self._formatGesture(gesture))
                                        tree.SetItemPyData(treeGes, gesture)
 
                sizer = wx.BoxSizer(wx.HORIZONTAL)
@@ -1552,6 +1552,16 @@ class InputGesturesDialog(SettingsDialog):
        def postInit(self):
                self.tree.SetFocus()
 
+       def _formatGesture(self, identifier):
+               try:
+                       source, main = 
inputCore.getDisplayTextForGestureIdentifier(identifier)
+                       # Translators: Describes a gesture in the Input 
Gestures dialog.
+                       # {main} is replaced with the main part of the gesture; 
e.g. alt+tab.
+                       # {source} is replaced with the gesture's source; e.g. 
laptop keyboard.
+                       return _("{main} ({source})").format(main=main, 
source=source)
+               except LookupError:
+                       return identifier
+
        def onTreeSelect(self, evt):
                item = self.tree.Selection
                data = self.tree.GetItemPyData(item)
@@ -1583,9 +1593,9 @@ class InputGesturesDialog(SettingsDialog):
                inputCore.manager._captureFunc = addGestureCaptor
 
        def _finishAdd(self, treeGes, scriptInfo, gesture):
-               gestureId = gesture.logIdentifier
+               gestureId = gesture.identifiers[0]
                self.pendingAdds.add((gestureId, scriptInfo.moduleName, 
scriptInfo.className, scriptInfo.scriptName))
-               self.tree.SetItemText(treeGes, gesture.logIdentifier)
+               self.tree.SetItemText(treeGes, self._formatGesture(gestureId))
                self.tree.SetItemPyData(treeGes, gestureId)
                self.onTreeSelect(None)
 


https://bitbucket.org/nvdaaddonteam/nvda/commits/e8e103523542/
Changeset:   e8e103523542
Branch:      None
User:        jteh
Date:        2013-09-05 08:26:59
Summary:     Add comment.

Affected #:  1 file

diff --git a/source/keyboardHandler.py b/source/keyboardHandler.py
index 009e7a4..9741fd7 100644
--- a/source/keyboardHandler.py
+++ b/source/keyboardHandler.py
@@ -458,6 +458,7 @@ class KeyboardInputGesture(inputCore.InputGesture):
                names = []
                main = None
                try:
+                       # If present, the NVDA key should appear first.
                        keys.remove("nvda")
                        names.append("NVDA")
                except KeyError:


https://bitbucket.org/nvdaaddonteam/nvda/commits/e659d79e0cb5/
Changeset:   e659d79e0cb5
Branch:      None
User:        jteh
Date:        2013-09-05 08:53:29
Summary:     BrailleDisplayGesture: Base implementation of 
getDisplayTextForIdentifier.

Obviously, this can't make the specific part of the gesture friendlier, but it 
does use the braille display driver's description for the source portion.

Affected #:  1 file

diff --git a/source/braille.py b/source/braille.py
index c798aa8..d5a5764 100644
--- a/source/braille.py
+++ b/source/braille.py
@@ -1611,3 +1611,9 @@ class BrailleDisplayGesture(inputCore.InputGesture):
                if isinstance(display, baseObject.ScriptableObject):
                        return display
                return super(BrailleDisplayGesture, self).scriptableObject
+
+       @classmethod
+       def getDisplayTextForIdentifier(cls, identifier):
+               return handler.display.description, identifier.split(":", 1)[1]
+
+inputCore.registerGestureSource("br", BrailleDisplayGesture)


https://bitbucket.org/nvdaaddonteam/nvda/commits/7dbe5acab31b/
Changeset:   7dbe5acab31b
Branch:      None
User:        pzajda
Date:        2013-09-05 12:42:03
Summary:     The NVDA service now displays a description in the service manager.

Affected #:  1 file

diff --git a/source/nvda_service.py b/source/nvda_service.py
index 1c5297f..5f3a389 100644
--- a/source/nvda_service.py
+++ b/source/nvda_service.py
@@ -327,7 +327,9 @@ def installService(nvdaDir):
        servicePath = os.path.join(nvdaDir, __name__ + ".exe")
        if not os.path.isfile(servicePath):
                raise RuntimeError("Could not find service executable")
-       win32serviceutil.InstallService(None, NVDAService._svc_name_, 
NVDAService._svc_display_name_, startType=win32service.SERVICE_AUTO_START, 
exeName=servicePath)
+       win32serviceutil.InstallService(None, NVDAService._svc_name_, 
NVDAService._svc_display_name_, startType=win32service.SERVICE_AUTO_START, 
exeName=servicePath,
+       # Translators: The NVDA service description
+       description=_(u"Allows NVDA to run on the Windows Logon screen, UAC 
screen and other secure screens."))
 
 def removeService():
        win32serviceutil.RemoveService(NVDAService._svc_name_)


https://bitbucket.org/nvdaaddonteam/nvda/commits/84a743379686/
Changeset:   84a743379686
Branch:      None
User:        jteh
Date:        2013-09-05 12:46:30
Summary:     InputGesturesDialog: When adding a gesture, allow the user to 
choose if there are multiple identifiers; e.g. with and without keyboard layout.

Affected #:  1 file

diff --git a/source/gui/settingsDialogs.py b/source/gui/settingsDialogs.py
index a983ebb..d96fd29 100644
--- a/source/gui/settingsDialogs.py
+++ b/source/gui/settingsDialogs.py
@@ -1588,15 +1588,35 @@ class InputGesturesDialog(SettingsDialog):
                        if gesture.isModifier:
                                return False
                        inputCore.manager._captureFunc = None
-                       wx.CallAfter(self._finishAdd, treeGes, scriptInfo, 
gesture)
+                       wx.CallAfter(self._addCaptured, treeGes, scriptInfo, 
gesture)
                        return False
                inputCore.manager._captureFunc = addGestureCaptor
 
-       def _finishAdd(self, treeGes, scriptInfo, gesture):
-               gestureId = gesture.identifiers[0]
-               self.pendingAdds.add((gestureId, scriptInfo.moduleName, 
scriptInfo.className, scriptInfo.scriptName))
-               self.tree.SetItemText(treeGes, self._formatGesture(gestureId))
-               self.tree.SetItemPyData(treeGes, gestureId)
+       def _addCaptured(self, treeGes, scriptInfo, gesture):
+               gids = gesture.identifiers
+               if len(gids) > 1:
+                       # Multiple choices. Present them in a pop-up menu.
+                       menu = wx.Menu()
+                       for gid in gids:
+                               disp = self._formatGesture(gid)
+                               item = menu.Append(wx.ID_ANY, disp)
+                               self.Bind(wx.EVT_MENU,
+                                       lambda evt: self._addChoice(treeGes, 
scriptInfo, gesture, gid, disp),
+                                       item)
+                       self.PopupMenu(menu)
+                       if not self.tree.GetItemPyData(treeGes):
+                               # No item was selected, so use the first.
+                               self._addChoice(treeGes, scriptInfo, gesture, 
gids[0],
+                                       self._formatGesture(gids[0]))
+                       menu.Destroy()
+               else:
+                       self._addChoice(treeGes, scriptInfo, gesture, gids[0],
+                               self._formatGesture(gids[0]))
+
+       def _addChoice(self, treeGes, scriptInfo, gesture, gid, disp):
+               self.pendingAdds.add((gid, scriptInfo.moduleName, 
scriptInfo.className, scriptInfo.scriptName))
+               self.tree.SetItemText(treeGes, disp)
+               self.tree.SetItemPyData(treeGes, gid)
                self.onTreeSelect(None)
 
        def onRemove(self, evt):


https://bitbucket.org/nvdaaddonteam/nvda/commits/24f3850891b2/
Changeset:   24f3850891b2
Branch:      None
User:        jteh
Date:        2013-09-05 12:49:03
Summary:     KeyboardInputGesture: Change display source for no layout to 
"keyboard, all layouts", which is longer but clearer.

Affected #:  1 file

diff --git a/source/keyboardHandler.py b/source/keyboardHandler.py
index 9741fd7..20709ad 100644
--- a/source/keyboardHandler.py
+++ b/source/keyboardHandler.py
@@ -452,7 +452,7 @@ class KeyboardInputGesture(inputCore.InputGesture):
                                pass
                if not dispSource:
                        # Translators: Used when describing keys on the system 
keyboard applying to all layouts.
-                       dispSource = _("system keyboard")
+                       dispSource = _("keyboard, all layouts")
 
                keys = set(keys.split("+"))
                names = []


https://bitbucket.org/nvdaaddonteam/nvda/commits/f6e8360ef1ea/
Changeset:   f6e8360ef1ea
Branch:      None
User:        jteh
Date:        2013-09-05 13:28:20
Summary:     Don't report all context menus in NVDA's GUI as if they were the 
NVDA menu itself.

Re #3503.

Affected #:  2 files

diff --git a/source/appModules/nvda.py b/source/appModules/nvda.py
index 32d7dad..5d7a9ba 100755
--- a/source/appModules/nvda.py
+++ b/source/appModules/nvda.py
@@ -1,6 +1,6 @@
 #appModules/nvda.py
 #A part of NonVisual Desktop Access (NVDA)
-#Copyright (C) 2008-2011 NV Access Inc
+#Copyright (C) 2008-2013 NV Access Limited
 #This file is covered by the GNU General Public License.
 #See the file COPYING for more details.
 
@@ -8,13 +8,14 @@ import appModuleHandler
 import api
 import controlTypes
 import versionInfo
+import gui
 
 class AppModule(appModuleHandler.AppModule):
 
        def event_NVDAObject_init(self, obj):
                # It seems that context menus always get the name "context" and 
this cannot be overridden.
                # Fudge the name of the NVDA system tray menu to make it more 
friendly.
-               if obj.role == controlTypes.ROLE_POPUPMENU:
+               if gui.mainFrame.sysTrayIcon.isMenuOpen and obj.role == 
controlTypes.ROLE_POPUPMENU:
                        parent = obj.parent
                        if parent and parent.parent==api.getDesktopObject():
                                obj.name=versionInfo.name

diff --git a/source/gui/__init__.py b/source/gui/__init__.py
index 6428d32..9b54763 100644
--- a/source/gui/__init__.py
+++ b/source/gui/__init__.py
@@ -426,6 +426,9 @@ class SysTrayIcon(wx.TaskBarIcon):
                item = self.menu.Append(wx.ID_EXIT, _("E&xit"),_("Exit NVDA"))
                self.Bind(wx.EVT_MENU, frame.onExitCommand, item)
 
+               # We need to know if an item gets activated before its real 
code runs.
+               self.menu.Bind(wx.EVT_MENU, self.onMenuItem)
+               self.isMenuOpen = False
                self.Bind(wx.EVT_TASKBAR_RIGHT_DOWN, self.onActivate)
 
        def Destroy(self):
@@ -434,9 +437,16 @@ class SysTrayIcon(wx.TaskBarIcon):
 
        def onActivate(self, evt):
                mainFrame.prePopup()
+               self.isMenuOpen = True
                self.PopupMenu(self.menu)
+               self.isMenuOpen = False
                mainFrame.postPopup()
 
+       def onMenuItem(self, evt):
+               # An item has been activated, so the menu has been closed.
+               self.isMenuOpen = False
+               evt.Skip()
+
 def initialize():
        global mainFrame
        mainFrame = MainFrame()


https://bitbucket.org/nvdaaddonteam/nvda/commits/3bacefffc160/
Changeset:   3bacefffc160
Branch:      None
User:        jteh
Date:        2013-09-05 13:32:03
Summary:     Merge branch 't3503' into next

Incubates #3503.

Affected #:  2 files

diff --git a/source/appModules/nvda.py b/source/appModules/nvda.py
index 32d7dad..5d7a9ba 100755
--- a/source/appModules/nvda.py
+++ b/source/appModules/nvda.py
@@ -1,6 +1,6 @@
 #appModules/nvda.py
 #A part of NonVisual Desktop Access (NVDA)
-#Copyright (C) 2008-2011 NV Access Inc
+#Copyright (C) 2008-2013 NV Access Limited
 #This file is covered by the GNU General Public License.
 #See the file COPYING for more details.
 
@@ -8,13 +8,14 @@ import appModuleHandler
 import api
 import controlTypes
 import versionInfo
+import gui
 
 class AppModule(appModuleHandler.AppModule):
 
        def event_NVDAObject_init(self, obj):
                # It seems that context menus always get the name "context" and 
this cannot be overridden.
                # Fudge the name of the NVDA system tray menu to make it more 
friendly.
-               if obj.role == controlTypes.ROLE_POPUPMENU:
+               if gui.mainFrame.sysTrayIcon.isMenuOpen and obj.role == 
controlTypes.ROLE_POPUPMENU:
                        parent = obj.parent
                        if parent and parent.parent==api.getDesktopObject():
                                obj.name=versionInfo.name

diff --git a/source/gui/__init__.py b/source/gui/__init__.py
index bdd02c9..9bb778e 100644
--- a/source/gui/__init__.py
+++ b/source/gui/__init__.py
@@ -438,6 +438,9 @@ class SysTrayIcon(wx.TaskBarIcon):
                item = self.menu.Append(wx.ID_EXIT, _("E&xit"),_("Exit NVDA"))
                self.Bind(wx.EVT_MENU, frame.onExitCommand, item)
 
+               # We need to know if an item gets activated before its real 
code runs.
+               self.menu.Bind(wx.EVT_MENU, self.onMenuItem)
+               self.isMenuOpen = False
                self.Bind(wx.EVT_TASKBAR_RIGHT_DOWN, self.onActivate)
 
        def Destroy(self):
@@ -446,9 +449,16 @@ class SysTrayIcon(wx.TaskBarIcon):
 
        def onActivate(self, evt):
                mainFrame.prePopup()
+               self.isMenuOpen = True
                self.PopupMenu(self.menu)
+               self.isMenuOpen = False
                mainFrame.postPopup()
 
+       def onMenuItem(self, evt):
+               # An item has been activated, so the menu has been closed.
+               self.isMenuOpen = False
+               evt.Skip()
+
 def initialize():
        global mainFrame
        mainFrame = MainFrame()


https://bitbucket.org/nvdaaddonteam/nvda/commits/f3604ab4f4ee/
Changeset:   f3604ab4f4ee
Branch:      None
User:        jteh
Date:        2013-09-05 15:22:39
Summary:     User Guide: Add documentation for the Input Gestures dialog.

Affected #:  1 file

diff --git a/user_docs/en/userGuide.t2t b/user_docs/en/userGuide.t2t
index 3e1b68d..87db83c 100644
--- a/user_docs/en/userGuide.t2t
+++ b/user_docs/en/userGuide.t2t
@@ -1056,6 +1056,26 @@ Using the Level field, you can adjust the lowest symbol 
level at which this symb
 
 When you are finished, press the OK button to save your changes or the Cancel 
button to discard them.
 
++++ Input Gestures (NVDA+control+i) +++
+In this dialog, you can customize the input gestures (keys on the keyboard, 
buttons on a braille display, etc.) for NVDA commands.
+
+Only commands that are applicable immediately before the dialog is opened are 
shown.
+Therefore, you should generally open this dialog using its key command so that 
all commands applicable to what you were just doing are shown.
+For example, if you want to customize commands related to browse mode, you 
should press the key command to open the Input Gestures dialog while you are in 
browse mode.
+
+The tree in this dialog lists all of the applicable NVDA commands grouped by 
category.
+Any gestures associated with a command are listed beneath the command.
+
+To add an input gesture to a command, select the command and press the Add 
button.
+Then, perform the input gesture you wish to associate; e.g. press a key on the 
keyboard or a button on a braille display.
+Often, a gesture can be interpreted in more than one way.
+For example, if you pressed a key on the keyboard, you may wish it to be 
specific to the current keyboard layout (e.g. desktop or laptop) or you may 
wish it to apply for all layouts.
+In this case, a menu will appear allowing you to select the desired option.
+
+To remove a gesture from a command, select the gesture and press the Remove 
button.
+
+When you are finished making changes, press the OK button to save them or the 
Cancel button to discard them.
+
 ++ Saving and Reloading the configuration ++
 By default NVDA will automatically save your settings on exit.
 Note, however, that this option can be changed under the general options in 
the preferences menu.


https://bitbucket.org/nvdaaddonteam/nvda/commits/7b244e703b18/
Changeset:   7b244e703b18
Branch:      None
User:        jteh
Date:        2013-09-06 11:20:28
Summary:     nvda_service: cosmetic: Minor tweak to translators comment for 
service and fix indentation.

Affected #:  1 file

diff --git a/source/nvda_service.py b/source/nvda_service.py
index 5f3a389..4f75a2c 100644
--- a/source/nvda_service.py
+++ b/source/nvda_service.py
@@ -328,8 +328,8 @@ def installService(nvdaDir):
        if not os.path.isfile(servicePath):
                raise RuntimeError("Could not find service executable")
        win32serviceutil.InstallService(None, NVDAService._svc_name_, 
NVDAService._svc_display_name_, startType=win32service.SERVICE_AUTO_START, 
exeName=servicePath,
-       # Translators: The NVDA service description
-       description=_(u"Allows NVDA to run on the Windows Logon screen, UAC 
screen and other secure screens."))
+               # Translators: The description of the NVDA service.
+               description=_(u"Allows NVDA to run on the Windows Logon screen, 
UAC screen and other secure screens."))
 
 def removeService():
        win32serviceutil.RemoveService(NVDAService._svc_name_)


https://bitbucket.org/nvdaaddonteam/nvda/commits/63675818548d/
Changeset:   63675818548d
Branch:      None
User:        jteh
Date:        2013-09-06 12:33:27
Summary:     Merge branch 't1213' into next

Incubates #1213.

Affected #:  1 file

diff --git a/source/nvda_service.py b/source/nvda_service.py
index 1c5297f..4f75a2c 100644
--- a/source/nvda_service.py
+++ b/source/nvda_service.py
@@ -327,7 +327,9 @@ def installService(nvdaDir):
        servicePath = os.path.join(nvdaDir, __name__ + ".exe")
        if not os.path.isfile(servicePath):
                raise RuntimeError("Could not find service executable")
-       win32serviceutil.InstallService(None, NVDAService._svc_name_, 
NVDAService._svc_display_name_, startType=win32service.SERVICE_AUTO_START, 
exeName=servicePath)
+       win32serviceutil.InstallService(None, NVDAService._svc_name_, 
NVDAService._svc_display_name_, startType=win32service.SERVICE_AUTO_START, 
exeName=servicePath,
+               # Translators: The description of the NVDA service.
+               description=_(u"Allows NVDA to run on the Windows Logon screen, 
UAC screen and other secure screens."))
 
 def removeService():
        win32serviceutil.RemoveService(NVDAService._svc_name_)


https://bitbucket.org/nvdaaddonteam/nvda/commits/c298ecb4c0a9/
Changeset:   c298ecb4c0a9
Branch:      None
User:        jteh
Date:        2013-09-09 10:37:35
Summary:     Revert "Don't report all context menus in NVDA's GUI as if they 
were the NVDA menu itself."

This implementation could break in some rare cases if a plugin bound a modal 
dialog to a menu item, as this wouldn't get intercepted by the event which sets 
isNVDAMenuOpen to False.
This reverts commit f6e8360ef1ea30d6b9216b5abcdf40db1549f6e0.
Re #3503.

Affected #:  2 files

diff --git a/source/appModules/nvda.py b/source/appModules/nvda.py
index 5d7a9ba..32d7dad 100755
--- a/source/appModules/nvda.py
+++ b/source/appModules/nvda.py
@@ -1,6 +1,6 @@
 #appModules/nvda.py
 #A part of NonVisual Desktop Access (NVDA)
-#Copyright (C) 2008-2013 NV Access Limited
+#Copyright (C) 2008-2011 NV Access Inc
 #This file is covered by the GNU General Public License.
 #See the file COPYING for more details.
 
@@ -8,14 +8,13 @@ import appModuleHandler
 import api
 import controlTypes
 import versionInfo
-import gui
 
 class AppModule(appModuleHandler.AppModule):
 
        def event_NVDAObject_init(self, obj):
                # It seems that context menus always get the name "context" and 
this cannot be overridden.
                # Fudge the name of the NVDA system tray menu to make it more 
friendly.
-               if gui.mainFrame.sysTrayIcon.isMenuOpen and obj.role == 
controlTypes.ROLE_POPUPMENU:
+               if obj.role == controlTypes.ROLE_POPUPMENU:
                        parent = obj.parent
                        if parent and parent.parent==api.getDesktopObject():
                                obj.name=versionInfo.name

diff --git a/source/gui/__init__.py b/source/gui/__init__.py
index 9b54763..6428d32 100644
--- a/source/gui/__init__.py
+++ b/source/gui/__init__.py
@@ -426,9 +426,6 @@ class SysTrayIcon(wx.TaskBarIcon):
                item = self.menu.Append(wx.ID_EXIT, _("E&xit"),_("Exit NVDA"))
                self.Bind(wx.EVT_MENU, frame.onExitCommand, item)
 
-               # We need to know if an item gets activated before its real 
code runs.
-               self.menu.Bind(wx.EVT_MENU, self.onMenuItem)
-               self.isMenuOpen = False
                self.Bind(wx.EVT_TASKBAR_RIGHT_DOWN, self.onActivate)
 
        def Destroy(self):
@@ -437,16 +434,9 @@ class SysTrayIcon(wx.TaskBarIcon):
 
        def onActivate(self, evt):
                mainFrame.prePopup()
-               self.isMenuOpen = True
                self.PopupMenu(self.menu)
-               self.isMenuOpen = False
                mainFrame.postPopup()
 
-       def onMenuItem(self, evt):
-               # An item has been activated, so the menu has been closed.
-               self.isMenuOpen = False
-               evt.Skip()
-
 def initialize():
        global mainFrame
        mainFrame = MainFrame()


https://bitbucket.org/nvdaaddonteam/nvda/commits/fc38daace9cc/
Changeset:   fc38daace9cc
Branch:      None
User:        jteh
Date:        2013-09-09 10:43:26
Summary:     IAccessibleHandler.getIAccIdentity: Support HMENU identity strings 
using AccPropServices.

Affected #:  1 file

diff --git a/source/IAccessibleHandler.py b/source/IAccessibleHandler.py
index 94db85d..200be58 100644
--- a/source/IAccessibleHandler.py
+++ b/source/IAccessibleHandler.py
@@ -870,8 +870,14 @@ def getIAccIdentity(pacc,childID):
        stringPtr,stringSize=IAccIdentityObject.getIdentityString(childID)
        try:
                if accPropServices:
-                       
hwnd,objectID,childID=accPropServices.DecomposeHwndIdentityString(stringPtr,stringSize)
-                       return 
dict(windowHandle=hwnd,objectID=c_int(objectID).value,childID=childID)
+                       try:
+                               
hwnd,objectID,childID=accPropServices.DecomposeHwndIdentityString(stringPtr,stringSize)
+                               return 
dict(windowHandle=hwnd,objectID=c_int(objectID).value,childID=childID)
+                       except COMError:
+                               
hmenu,childID=accPropServices.DecomposeHmenuIdentityString(stringPtr,stringSize)
+                               # hmenu is a wireHMENU, but it seems we can 
just treat this as a number.
+                               # comtypes transparently does this for wireHWND.
+                               return 
dict(menuHandle=cast(hmenu,wintypes.HMENU).value,childID=childID)
                stringPtr=cast(stringPtr,POINTER(c_char*stringSize))
                fields=struct.unpack('IIiI',stringPtr.contents.raw)
                d={}


https://bitbucket.org/nvdaaddonteam/nvda/commits/297d98ae700c/
Changeset:   297d98ae700c
Branch:      None
User:        jteh
Date:        2013-09-09 10:43:52
Summary:     Don't report all context menus in NVDA's GUI as if they were the 
NVDA menu itself.

The NVDA menu is identified by its IAccessible identity. To get this, the GUI 
sets a flag in the nvda app module the first time the NVDA menu is opened so 
that the NVDA app module knows that the next menu it encounters is the NVDA 
menu.
Re #3503.

Affected #:  2 files

diff --git a/source/appModules/nvda.py b/source/appModules/nvda.py
index 32d7dad..0fadb65 100755
--- a/source/appModules/nvda.py
+++ b/source/appModules/nvda.py
@@ -9,15 +9,27 @@ import api
 import controlTypes
 import versionInfo
 
+nvdaMenuIaIdentity = None
+
 class AppModule(appModuleHandler.AppModule):
 
+       def isNvdaMenu(self, obj):
+               global nvdaMenuIaIdentity
+               if obj.IAccessibleIdentity == nvdaMenuIaIdentity:
+                       return True
+               if nvdaMenuIaIdentity is not True:
+                       return False
+               # nvdaMenuIaIdentity is True, so the next menu we encounter is 
the NVDA menu.
+               if obj.role == controlTypes.ROLE_POPUPMENU:
+                       nvdaMenuIaIdentity = obj.IAccessibleIdentity
+                       return True
+               return False
+
        def event_NVDAObject_init(self, obj):
                # It seems that context menus always get the name "context" and 
this cannot be overridden.
                # Fudge the name of the NVDA system tray menu to make it more 
friendly.
-               if obj.role == controlTypes.ROLE_POPUPMENU:
-                       parent = obj.parent
-                       if parent and parent.parent==api.getDesktopObject():
-                               obj.name=versionInfo.name
+               if self.isNvdaMenu(obj):
+                       obj.name=versionInfo.name
 
        def event_gainFocus(self, obj, nextHandler):
                if obj.role == controlTypes.ROLE_UNKNOWN and 
controlTypes.STATE_INVISIBLE in obj.states:

diff --git a/source/gui/__init__.py b/source/gui/__init__.py
index 6428d32..7dfa54a 100644
--- a/source/gui/__init__.py
+++ b/source/gui/__init__.py
@@ -434,7 +434,15 @@ class SysTrayIcon(wx.TaskBarIcon):
 
        def onActivate(self, evt):
                mainFrame.prePopup()
+               import appModules.nvda
+               if not appModules.nvda.nvdaMenuIaIdentity:
+                       # The NVDA app module doesn't know how to identify the 
NVDA menu yet.
+                       # Signal that the NVDA menu has just been opened.
+                       appModules.nvda.nvdaMenuIaIdentity = True
                self.PopupMenu(self.menu)
+               if appModules.nvda.nvdaMenuIaIdentity is True:
+                       # The NVDA menu didn't actually appear for some reason.
+                       appModules.nvda.nvdaMenuIaIdentity = None
                mainFrame.postPopup()
 
 def initialize():


https://bitbucket.org/nvdaaddonteam/nvda/commits/988a466a3e9e/
Changeset:   988a466a3e9e
Branch:      None
User:        jteh
Date:        2013-09-09 11:21:19
Summary:     Merge branch 't3503' into next

Incubates #3503.

Affected #:  3 files

diff --git a/source/IAccessibleHandler.py b/source/IAccessibleHandler.py
index 94db85d..200be58 100644
--- a/source/IAccessibleHandler.py
+++ b/source/IAccessibleHandler.py
@@ -870,8 +870,14 @@ def getIAccIdentity(pacc,childID):
        stringPtr,stringSize=IAccIdentityObject.getIdentityString(childID)
        try:
                if accPropServices:
-                       
hwnd,objectID,childID=accPropServices.DecomposeHwndIdentityString(stringPtr,stringSize)
-                       return 
dict(windowHandle=hwnd,objectID=c_int(objectID).value,childID=childID)
+                       try:
+                               
hwnd,objectID,childID=accPropServices.DecomposeHwndIdentityString(stringPtr,stringSize)
+                               return 
dict(windowHandle=hwnd,objectID=c_int(objectID).value,childID=childID)
+                       except COMError:
+                               
hmenu,childID=accPropServices.DecomposeHmenuIdentityString(stringPtr,stringSize)
+                               # hmenu is a wireHMENU, but it seems we can 
just treat this as a number.
+                               # comtypes transparently does this for wireHWND.
+                               return 
dict(menuHandle=cast(hmenu,wintypes.HMENU).value,childID=childID)
                stringPtr=cast(stringPtr,POINTER(c_char*stringSize))
                fields=struct.unpack('IIiI',stringPtr.contents.raw)
                d={}

diff --git a/source/appModules/nvda.py b/source/appModules/nvda.py
index 5d7a9ba..0fadb65 100755
--- a/source/appModules/nvda.py
+++ b/source/appModules/nvda.py
@@ -1,6 +1,6 @@
 #appModules/nvda.py
 #A part of NonVisual Desktop Access (NVDA)
-#Copyright (C) 2008-2013 NV Access Limited
+#Copyright (C) 2008-2011 NV Access Inc
 #This file is covered by the GNU General Public License.
 #See the file COPYING for more details.
 
@@ -8,17 +8,28 @@ import appModuleHandler
 import api
 import controlTypes
 import versionInfo
-import gui
+
+nvdaMenuIaIdentity = None
 
 class AppModule(appModuleHandler.AppModule):
 
+       def isNvdaMenu(self, obj):
+               global nvdaMenuIaIdentity
+               if obj.IAccessibleIdentity == nvdaMenuIaIdentity:
+                       return True
+               if nvdaMenuIaIdentity is not True:
+                       return False
+               # nvdaMenuIaIdentity is True, so the next menu we encounter is 
the NVDA menu.
+               if obj.role == controlTypes.ROLE_POPUPMENU:
+                       nvdaMenuIaIdentity = obj.IAccessibleIdentity
+                       return True
+               return False
+
        def event_NVDAObject_init(self, obj):
                # It seems that context menus always get the name "context" and 
this cannot be overridden.
                # Fudge the name of the NVDA system tray menu to make it more 
friendly.
-               if gui.mainFrame.sysTrayIcon.isMenuOpen and obj.role == 
controlTypes.ROLE_POPUPMENU:
-                       parent = obj.parent
-                       if parent and parent.parent==api.getDesktopObject():
-                               obj.name=versionInfo.name
+               if self.isNvdaMenu(obj):
+                       obj.name=versionInfo.name
 
        def event_gainFocus(self, obj, nextHandler):
                if obj.role == controlTypes.ROLE_UNKNOWN and 
controlTypes.STATE_INVISIBLE in obj.states:

diff --git a/source/gui/__init__.py b/source/gui/__init__.py
index 9bb778e..94e7881 100644
--- a/source/gui/__init__.py
+++ b/source/gui/__init__.py
@@ -438,9 +438,6 @@ class SysTrayIcon(wx.TaskBarIcon):
                item = self.menu.Append(wx.ID_EXIT, _("E&xit"),_("Exit NVDA"))
                self.Bind(wx.EVT_MENU, frame.onExitCommand, item)
 
-               # We need to know if an item gets activated before its real 
code runs.
-               self.menu.Bind(wx.EVT_MENU, self.onMenuItem)
-               self.isMenuOpen = False
                self.Bind(wx.EVT_TASKBAR_RIGHT_DOWN, self.onActivate)
 
        def Destroy(self):
@@ -449,16 +446,17 @@ class SysTrayIcon(wx.TaskBarIcon):
 
        def onActivate(self, evt):
                mainFrame.prePopup()
-               self.isMenuOpen = True
+               import appModules.nvda
+               if not appModules.nvda.nvdaMenuIaIdentity:
+                       # The NVDA app module doesn't know how to identify the 
NVDA menu yet.
+                       # Signal that the NVDA menu has just been opened.
+                       appModules.nvda.nvdaMenuIaIdentity = True
                self.PopupMenu(self.menu)
-               self.isMenuOpen = False
+               if appModules.nvda.nvdaMenuIaIdentity is True:
+                       # The NVDA menu didn't actually appear for some reason.
+                       appModules.nvda.nvdaMenuIaIdentity = None
                mainFrame.postPopup()
 
-       def onMenuItem(self, evt):
-               # An item has been activated, so the menu has been closed.
-               self.isMenuOpen = False
-               evt.Skip()
-
 def initialize():
        global mainFrame
        mainFrame = MainFrame()


https://bitbucket.org/nvdaaddonteam/nvda/commits/e4481e4b0bbe/
Changeset:   e4481e4b0bbe
Branch:      None
User:        jteh
Date:        2013-09-09 11:26:00
Summary:     User Guide: Remove the Remapping Key Assignments and Other Input 
Gestures advanced section of the User Guide, as this functionality is covered 
by the Input Gestures dialog.

Affected #:  1 file

diff --git a/user_docs/en/userGuide.t2t b/user_docs/en/userGuide.t2t
index 87db83c..94c5221 100644
--- a/user_docs/en/userGuide.t2t
+++ b/user_docs/en/userGuide.t2t
@@ -1785,79 +1785,6 @@ Please see the [BRLTTY key tables documentation 
http://mielke.cc/brltty/doc/driv
 
 + Advanced Topics +
 
-++ Remapping Key Assignments and Other Input Gestures ++
-Users are able to provide or override mappings of input gestures (such as key 
presses) to scripts in a special file in the user's NVDA configuration 
directory.
-This file is called gestures.ini.
-
-This file uses standard ini syntax.
-The file may contain multiple sections and each section may have one or more 
entries.
-
-Each section provides mappings for scripts in a particular Python module and 
class inside NVDA.
-- The section name should be the Python module and class separated by a dot 
(.).
-- The key of each entry is the name of the script to which input gestures 
should be bound.
-Alternatively, you can use None to unbind input gestures from a script to 
which they were previously bound.
-Each entry key can only be listed once per section, including None.
-- The entry value is a comma (,) separated list of gesture identifiers for the 
input gestures that should be bound.
-Gesture identifiers ending in a comma must be enclosed in quotes (" or ').
--
-
-Gesture identifiers consist of a two letter device code; a sub-device, layout 
or mode in brackets; a colon; and then a type-specific string identifying the 
actual input, such as key or touch gesture names.
-- For keyboard gestures, the device code is kb.
-The part in brackets is the keyboard layout and is optional.
-If not specified, the gesture will apply to all keyboard layouts.
-The string after the colon is one or more key names separated by a plus (+) 
sign.
-- For braille display gestures, the device code is br.
-The part in brackets identifies the specific braille display and is mandatory.
-The string after the colon is one or more key names separated by a plus (+) 
sign.
-- For touch gestures, the device code is ts.
-The part in brackets is the touch mode and is optional.
-The string after the colon is a touch gesture; e.g. double_tap, 2fingerflickUp 
or 3finger_tap.
--
-
-In order to discover gesture identifiers, script names and the class and 
module in which they are contained, you can:
-+ Turn on Input Help.
-+ Activate the gesture (press the key, touch the screen, etc.).
-+ Turn off input help.
-+ Activate View log in the NVDA Tools menu.
-+ Examine the recent log entries.
-One of these should provide information about the input gesture you sent, 
including the module.class and script if it is bound to one.
-+
-
-Following is an example of how you could bind NVDA+shift+t to the date time 
script.
-
-To find out the correct script name and module.class for date time, you would 
turn on Input Help and press NVDA+f12 (as this is the current gesture for the 
date time script).
-You would then turn off Input Help and examine the log viewer.
-
-Towards the bottom, you would see:
-
-```
-INFO - inputCore.InputManager._handleInputHelp (13:17:22):
-Input help: gesture kb(desktop):NVDA+f12, bound to script dateTime on 
globalCommands.GlobalCommands
-```
-
-From this, you can see that the script name is dateTime and the module.class 
is globalCommands.GlobalCommands.
-
-If the file does not yet exist, you would create a text file called 
gestures.ini in the user configuration directory and add the following content:
-
-```
-[globalCommands.GlobalCommands]
-       dateTime = kb:NVDA+shift+t
-```
-
-This would bind the key press NVDA+shift+t (in any keyboard layout) to the 
dateTime script.
-
-Note that the original NVDA+f12 binding would still work.
-If you wanted to remove this binding, you would add the following line:
-
-```
-       None = kb:NVDA+f12
-```
-
-Although you are free to have scripts bound to any available key, it may be 
problematic to use the alt key on the keyboard.
-NVDA still sends modifier keys (such as shift, control and alt) to the 
Operating System, even if they eventuate in a script.
-Thus, if you do use alt in a gesture, pressing this key combination may 
activate the menu bar, as well as executing the script.
-Therefore, it is probably best to just use Shift, control and the NVDA 
modifier key as modifiers.
-
 ++ Advanced Customization of Symbol Pronunciation ++
 It is possible to customize the pronunciation of punctuation and other symbols 
beyond what can be done using the [Punctuation/symbol pronunciation 
#SymbolPronunciation] dialog.
 For example, you can specify whether the raw symbol should be sent to the 
synthesizer (e.g. to cause a pause or change in inflection) and you can add 
custom symbols.


https://bitbucket.org/nvdaaddonteam/nvda/commits/d4e14a86bff9/
Changeset:   d4e14a86bff9
Branch:      None
User:        jteh
Date:        2013-09-09 11:28:20
Summary:     Merge branch 't1532' into next

Incubates #1532.

Affected #:  9 files

diff --git a/source/braille.py b/source/braille.py
index 3bb7e4c..f65e311 100644
--- a/source/braille.py
+++ b/source/braille.py
@@ -1616,3 +1616,9 @@ class BrailleDisplayGesture(inputCore.InputGesture):
                if isinstance(display, baseObject.ScriptableObject):
                        return display
                return super(BrailleDisplayGesture, self).scriptableObject
+
+       @classmethod
+       def getDisplayTextForIdentifier(cls, identifier):
+               return handler.display.description, identifier.split(":", 1)[1]
+
+inputCore.registerGestureSource("br", BrailleDisplayGesture)

diff --git a/source/cursorManager.py b/source/cursorManager.py
index 9ae3b89..74c2f51 100644
--- a/source/cursorManager.py
+++ b/source/cursorManager.py
@@ -18,6 +18,7 @@ import speech
 import config
 import braille
 import controlTypes
+from inputCore import SCRCAT_BROWSEMODE
 
 class CursorManager(baseObject.ScriptableObject):
        """
@@ -32,6 +33,9 @@ class CursorManager(baseObject.ScriptableObject):
        @type selection: L{textInfos.TextInfo}
        """
 
+       # Translators: the script category for browse mode
+       scriptCategory=SCRCAT_BROWSEMODE
+
        _lastFindText=""
 
        def __init__(self, *args, **kwargs):

diff --git a/source/globalCommands.py b/source/globalCommands.py
index 1420983..3b9b975 100755
--- a/source/globalCommands.py
+++ b/source/globalCommands.py
@@ -37,6 +37,43 @@ import virtualBuffers
 import characterProcessing
 from baseObject import ScriptableObject
 
+#: Script category for text review  commands.
+# Translators: The name of a category of NVDA commands.
+SCRCAT_TEXTREVIEW = _("Text review")
+#: Script category for Object navigation   commands.
+# Translators: The name of a category of NVDA commands.
+SCRCAT_OBJECTNAVIGATION = _("Object navigation")
+#: Script category for system caret commands.
+# Translators: The name of a category of NVDA commands.
+SCRCAT_SYSTEMCARET = _("System caret")
+#: Script category for mouse commands.
+# Translators: The name of a category of NVDA commands.
+SCRCAT_MOUSE = _("Mouse")
+#: Script category for mouse commands.
+# Translators: The name of a category of NVDA commands.
+SCRCAT_SPEECH = _("Speech")
+#: Script category for configuration dialogs commands.
+# Translators: The name of a category of NVDA commands.
+SCRCAT_CONFIG = _("Configuration")
+#: Script category for Braille commands.
+# Translators: The name of a category of NVDA commands.
+SCRCAT_BRAILLE = _("Braille")
+#: Script category for tools commands.
+# Translators: The name of a category of NVDA commands.
+SCRCAT_TOOLS = _("Tools")
+#: Script category for touch commands.
+# Translators: The name of a category of NVDA commands.
+SCRCAT_TOUCH = _("Touch screen")
+#: Script category for focus commands.
+# Translators: The name of a category of NVDA commands.
+SCRCAT_FOCUS = _("System focus")
+#: Script category for system status  commands.
+# Translators: The name of a category of NVDA commands.
+SCRCAT_SYSTEM = _("System status")
+#: Script category for input  commands.
+# Translators: The name of a category of NVDA commands.
+SCRCAT_INPUT = _("Input")
+
 class GlobalCommands(ScriptableObject):
        """Commands that are available at all times, regardless of the current 
focus.
        """
@@ -51,6 +88,8 @@ class GlobalCommands(ScriptableObject):
                ui.message(state)
        # Translators: Input help mode message for toggle input help command.
        script_toggleInputHelp.__doc__=_("Turns input help on or off. When on, 
any input such as pressing a key on the keyboard will tell you what script is 
associated with that input, if any.")
+       script_toggleInputHelp.category=SCRCAT_INPUT
+
 
        def script_toggleCurrentAppSleepMode(self,gesture):
                curFocus=api.getFocusObject()
@@ -85,6 +124,7 @@ class GlobalCommands(ScriptableObject):
                        speech.speakSpelling(info.text)
        # Translators: Input help mode message for report current line command.
        script_reportCurrentLine.__doc__=_("Reports the current line under the 
application cursor. Pressing this key twice will spell the current line")
+       script_reportCurrentLine.category=SCRCAT_SYSTEMCARET
 
        def script_leftMouseClick(self,gesture):
                # Translators: Reported when left mouse button is clicked.
@@ -93,6 +133,7 @@ class GlobalCommands(ScriptableObject):
                winUser.mouse_event(winUser.MOUSEEVENTF_LEFTUP,0,0,None,None)
        # Translators: Input help mode message for left mouse click command.
        script_leftMouseClick.__doc__=_("Clicks the left mouse button once at 
the current mouse position")
+       script_leftMouseClick.category=SCRCAT_MOUSE
 
        def script_rightMouseClick(self,gesture):
                # Translators: Reported when right mouse button is clicked.
@@ -101,6 +142,7 @@ class GlobalCommands(ScriptableObject):
                winUser.mouse_event(winUser.MOUSEEVENTF_RIGHTUP,0,0,None,None)
        # Translators: Input help mode message for right mouse click command.
        script_rightMouseClick.__doc__=_("Clicks the right mouse button once at 
the current mouse position")
+       script_rightMouseClick.category=SCRCAT_MOUSE
 
        def script_toggleLeftMouseButton(self,gesture):
                if winUser.getKeyState(winUser.VK_LBUTTON)&32768:
@@ -113,6 +155,7 @@ class GlobalCommands(ScriptableObject):
                        
winUser.mouse_event(winUser.MOUSEEVENTF_LEFTDOWN,0,0,None,None)
        # Translators: Input help mode message for left mouse lock/unlock 
toggle command.
        script_toggleLeftMouseButton.__doc__=_("Locks or unlocks the left mouse 
button")
+       script_toggleLeftMouseButton.category=SCRCAT_MOUSE
 
        def script_toggleRightMouseButton(self,gesture):
                if winUser.getKeyState(winUser.VK_RBUTTON)&32768:
@@ -125,6 +168,7 @@ class GlobalCommands(ScriptableObject):
                        
winUser.mouse_event(winUser.MOUSEEVENTF_RIGHTDOWN,0,0,None,None)
        # Translators: Input help mode message for right mouse lock/unlock 
command.
        script_toggleRightMouseButton.__doc__=_("Locks or unlocks the right 
mouse button")
+       script_toggleRightMouseButton.category=SCRCAT_MOUSE
 
        def script_reportCurrentSelection(self,gesture):
                obj=api.getFocusObject()
@@ -141,6 +185,7 @@ class GlobalCommands(ScriptableObject):
                        speech.speakMessage(_("selected %s")%info.text)
        # Translators: Input help mode message for report current selection 
command.
        script_reportCurrentSelection.__doc__=_("Announces the current 
selection in edit controls and documents. If there is no selection it says so.")
+       script_reportCurrentSelection.category=SCRCAT_SYSTEMCARET
 
        def script_dateTime(self,gesture):
                if scriptHandler.getLastScriptRepeatCount()==0:
@@ -150,6 +195,7 @@ class GlobalCommands(ScriptableObject):
                ui.message(text)
        # Translators: Input help mode message for report date and time command.
        script_dateTime.__doc__=_("If pressed once, reports the current time. 
If pressed twice, reports the current date")
+       script_dateTime.category=SCRCAT_SYSTEM
 
        def script_increaseSynthSetting(self,gesture):
                settingName=globalVars.settingsRing.currentSettingName
@@ -161,6 +207,7 @@ class GlobalCommands(ScriptableObject):
                ui.message("%s %s" % (settingName,settingValue))
        # Translators: Input help mode message for increase synth setting value 
command.
        script_increaseSynthSetting.__doc__=_("Increases the currently active 
setting in the synth settings ring")
+       script_increaseSynthSetting.category=SCRCAT_SPEECH
 
        def script_decreaseSynthSetting(self,gesture):
                settingName=globalVars.settingsRing.currentSettingName
@@ -171,6 +218,7 @@ class GlobalCommands(ScriptableObject):
                ui.message("%s %s" % (settingName,settingValue))
        # Translators: Input help mode message for decrease synth setting value 
command.
        script_decreaseSynthSetting.__doc__=_("Decreases the currently active 
setting in the synth settings ring")
+       script_decreaseSynthSetting.category=SCRCAT_SPEECH
 
        def script_nextSynthSetting(self,gesture):
                nextSettingName=globalVars.settingsRing.next()
@@ -181,6 +229,7 @@ class GlobalCommands(ScriptableObject):
                ui.message("%s %s"%(nextSettingName,nextSettingValue))
        # Translators: Input help mode message for next synth setting command.
        script_nextSynthSetting.__doc__=_("Moves to the next available setting 
in the synth settings ring")
+       script_nextSynthSetting.category=SCRCAT_SPEECH
 
        def script_previousSynthSetting(self,gesture):
                previousSettingName=globalVars.settingsRing.previous()
@@ -191,6 +240,7 @@ class GlobalCommands(ScriptableObject):
                ui.message("%s %s"%(previousSettingName,previousSettingValue))
        # Translators: Input help mode message for previous synth setting 
command.
        script_previousSynthSetting.__doc__=_("Moves to the previous available 
setting in the synth settings ring")
+       script_previousSynthSetting.category=SCRCAT_SPEECH
 
        def script_toggleSpeakTypedCharacters(self,gesture):
                if config.conf["keyboard"]["speakTypedCharacters"]:
@@ -204,6 +254,7 @@ class GlobalCommands(ScriptableObject):
                ui.message(state)
        # Translators: Input help mode message for toggle speaked typed 
characters command.
        script_toggleSpeakTypedCharacters.__doc__=_("Toggles on and off the 
speaking of typed characters")
+       script_toggleSpeakTypedCharacters.category=SCRCAT_SPEECH
 
        def script_toggleSpeakTypedWords(self,gesture):
                if config.conf["keyboard"]["speakTypedWords"]:
@@ -217,6 +268,7 @@ class GlobalCommands(ScriptableObject):
                ui.message(state)
        # Translators: Input help mode message for toggle speak typed words 
command.
        script_toggleSpeakTypedWords.__doc__=_("Toggles on and off the speaking 
of typed words")
+       script_toggleSpeakTypedWords.category=SCRCAT_SPEECH
 
        def script_toggleSpeakCommandKeys(self,gesture):
                if config.conf["keyboard"]["speakCommandKeys"]:
@@ -230,6 +282,7 @@ class GlobalCommands(ScriptableObject):
                ui.message(state)
        # Translators: Input help mode message for toggle speak command keys 
command.
        script_toggleSpeakCommandKeys.__doc__=_("Toggles on and off the 
speaking of typed keys, that are not specifically characters")
+       script_toggleSpeakCommandKeys.category=SCRCAT_SPEECH
 
        def script_cycleSpeechSymbolLevel(self,gesture):
                curLevel = config.conf["speech"]["symbolLevel"]
@@ -246,6 +299,7 @@ class GlobalCommands(ScriptableObject):
                ui.message(_("symbol level %s") % name)
        # Translators: Input help mode message for cycle speech symbol level 
command.
        script_cycleSpeechSymbolLevel.__doc__=_("Cycles through speech symbol 
levels which determine what symbols are spoken")
+       script_cycleSpeechSymbolLevel.category=SCRCAT_SPEECH
 
        def script_moveMouseToNavigatorObject(self,gesture):
                obj=api.getNavigatorObject() 
@@ -269,6 +323,7 @@ class GlobalCommands(ScriptableObject):
                mouseHandler.executeMouseMoveEvent(x,y)
        # Translators: Input help mode message for move mouse to navigator 
object command.
        script_moveMouseToNavigatorObject.__doc__=_("Moves the mouse pointer to 
the current navigator object")
+       script_moveMouseToNavigatorObject.category=SCRCAT_MOUSE
 
        def script_moveNavigatorObjectToMouse(self,gesture):
                # Translators: Reported when attempting to move the navigator 
object to the object under mouse pointer.
@@ -278,6 +333,7 @@ class GlobalCommands(ScriptableObject):
                speech.speakObject(obj)
        # Translators: Input help mode message for move navigator object to 
mouse command.
        script_moveNavigatorObjectToMouse.__doc__=_("Sets the navigator object 
to the current object under the mouse pointer and speaks it")
+       script_moveNavigatorObjectToMouse.category=SCRCAT_MOUSE
 
        def script_reviewMode_next(self,gesture):
                label=review.nextMode()
@@ -290,6 +346,7 @@ class GlobalCommands(ScriptableObject):
                        # Translators: reported when there are no other 
available review modes for this object 
                        ui.message(_("No next review mode"))
        script_reviewMode_next.__doc__=_("Switches to the next review mode 
(e.g. object, document or screen) and positions the review position at the 
point of the navigator object")
+       script_reviewMode_next.category=SCRCAT_TEXTREVIEW
 
        def script_reviewMode_previous(self,gesture):
                label=review.nextMode(prev=True)
@@ -302,7 +359,8 @@ class GlobalCommands(ScriptableObject):
                        # Translators: reported when there are no  other 
available review modes for this object 
                        ui.message(_("No previous review mode"))
        script_reviewMode_previous.__doc__=_("Switches to the previous review 
mode (e.g. object, document or screen) and positions the review position at the 
point of the navigator object") 
-
+       script_reviewMode_previous.category=SCRCAT_TEXTREVIEW
+
        def script_navigatorObject_current(self,gesture):
                curObject=api.getNavigatorObject()
                if not isinstance(curObject,NVDAObject):
@@ -340,6 +398,7 @@ class GlobalCommands(ScriptableObject):
                        
speech.speakObject(curObject,reason=controlTypes.REASON_QUERY)
        # Translators: Input help mode message for report current navigator 
object command.
        script_navigatorObject_current.__doc__=_("Reports the current navigator 
object. Pressing twice spells this information,and pressing three times Copies 
name and value of this  object to the clipboard")
+       script_navigatorObject_current.category=SCRCAT_OBJECTNAVIGATION
 
        def script_navigatorObject_currentDimensions(self,gesture):
                obj=api.getNavigatorObject()
@@ -363,6 +422,7 @@ class GlobalCommands(ScriptableObject):
                ui.message(_("Object edges positioned {left:.1f} per cent from 
left edge of screen, {top:.1f} per cent from top edge of screen, width is 
{width:.1f} per cent of screen, height is {height:.1f} per cent of 
screen").format(left=percentFromLeft,top=percentFromTop,width=percentWidth,height=percentHeight))
        # Translators: Input help mode message for report object dimensions 
command.
        script_navigatorObject_currentDimensions.__doc__=_("Reports the hight, 
width and position of the current navigator object")
+       
script_navigatorObject_currentDimensions.category=SCRCAT_OBJECTNAVIGATION
 
        def script_navigatorObject_toFocus(self,gesture):
                obj=api.getFocusObject()
@@ -376,6 +436,7 @@ class GlobalCommands(ScriptableObject):
                speech.speakObject(obj,reason=controlTypes.REASON_FOCUS)
        # Translators: Input help mode message for move navigator object to 
current focus command.
        script_navigatorObject_toFocus.__doc__=_("Sets the navigator object to 
the current focus, and the review cursor to the position of the caret inside 
it, if possible.")
+       script_navigatorObject_toFocus.category=SCRCAT_OBJECTNAVIGATION
 
        def script_navigatorObject_moveFocus(self,gesture):
                obj=api.getNavigatorObject()
@@ -401,6 +462,7 @@ class GlobalCommands(ScriptableObject):
                        
speech.speakTextInfo(info,reason=controlTypes.REASON_CARET)
        # Translators: Input help mode message for move focus to current 
navigator object command.
        script_navigatorObject_moveFocus.__doc__=_("Pressed once Sets the 
keyboard focus to the navigator object, pressed twice sets the system caret to 
the position of the review cursor")
+       script_navigatorObject_moveFocus.category=SCRCAT_OBJECTNAVIGATION
 
        def script_navigatorObject_parent(self,gesture):
                curObject=api.getNavigatorObject()
@@ -417,6 +479,7 @@ class GlobalCommands(ScriptableObject):
                        speech.speakMessage(_("No containing object"))
        # Translators: Input help mode message for move to parent object 
command.
        script_navigatorObject_parent.__doc__=_("Moves the navigator object to 
the object containing it")
+       script_navigatorObject_parent.category=SCRCAT_OBJECTNAVIGATION
 
        def script_navigatorObject_next(self,gesture):
                curObject=api.getNavigatorObject()
@@ -433,6 +496,7 @@ class GlobalCommands(ScriptableObject):
                        speech.speakMessage(_("No next"))
        # Translators: Input help mode message for move to next object command.
        script_navigatorObject_next.__doc__=_("Moves the navigator object to 
the next object")
+       script_navigatorObject_next.category=SCRCAT_OBJECTNAVIGATION
 
        def script_navigatorObject_previous(self,gesture):
                curObject=api.getNavigatorObject()
@@ -449,6 +513,7 @@ class GlobalCommands(ScriptableObject):
                        speech.speakMessage(_("No previous"))
        # Translators: Input help mode message for move to previous object 
command.
        script_navigatorObject_previous.__doc__=_("Moves the navigator object 
to the previous object")
+       script_navigatorObject_previous.category=SCRCAT_OBJECTNAVIGATION
 
        def script_navigatorObject_firstChild(self,gesture):
                curObject=api.getNavigatorObject()
@@ -465,6 +530,7 @@ class GlobalCommands(ScriptableObject):
                        speech.speakMessage(_("No objects inside"))
        # Translators: Input help mode message for move to first child object 
command.
        script_navigatorObject_firstChild.__doc__=_("Moves the navigator object 
to the first object inside it")
+       script_navigatorObject_firstChild.category=SCRCAT_OBJECTNAVIGATION
 
        def script_review_activate(self,gesture):
                # Translators: a message reported when the action at the 
position of the review cursor or navigator object is performed.
@@ -498,6 +564,7 @@ class GlobalCommands(ScriptableObject):
                ui.message(_("No action"))
        # Translators: Input help mode message for activate current object 
command.
        script_review_activate.__doc__=_("Performs the default action on the 
current navigator object (example: presses it if it is a button).")
+       script_review_activate.category=SCRCAT_OBJECTNAVIGATION
 
        def script_review_top(self,gesture):
                
info=api.getReviewPosition().obj.makeTextInfo(textInfos.POSITION_FIRST)
@@ -507,6 +574,7 @@ class GlobalCommands(ScriptableObject):
                
speech.speakTextInfo(info,unit=textInfos.UNIT_LINE,reason=controlTypes.REASON_CARET)
        # Translators: Input help mode message for move review cursor to top 
line command.
        script_review_top.__doc__=_("Moves the review cursor to the top line of 
the current navigator object and speaks it")
+       script_review_top.category=SCRCAT_TEXTREVIEW
 
        def script_review_previousLine(self,gesture):
                info=api.getReviewPosition().copy()
@@ -521,6 +589,7 @@ class GlobalCommands(ScriptableObject):
                
speech.speakTextInfo(info,unit=textInfos.UNIT_LINE,reason=controlTypes.REASON_CARET)
        # Translators: Input help mode message for move review cursor to 
previous line command.
        script_review_previousLine.__doc__=_("Moves the review cursor to the 
previous line of the current navigator object and speaks it")
+       script_review_previousLine.category=SCRCAT_TEXTREVIEW
 
        def script_review_currentLine(self,gesture):
                info=api.getReviewPosition().copy()
@@ -532,6 +601,7 @@ class GlobalCommands(ScriptableObject):
                        
speech.spellTextInfo(info,useCharacterDescriptions=scriptCount>1)
        # Translators: Input help mode message for read current line under 
review cursor command.
        script_review_currentLine.__doc__=_("Reports the line of the current 
navigator object where the review cursor is situated. If this key is pressed 
twice, the current line will be spelled. Pressing three times will spell the 
line using character descriptions.")
+       script_review_currentLine.category=SCRCAT_TEXTREVIEW
  
        def script_review_nextLine(self,gesture):
                info=api.getReviewPosition().copy()
@@ -546,6 +616,7 @@ class GlobalCommands(ScriptableObject):
                
speech.speakTextInfo(info,unit=textInfos.UNIT_LINE,reason=controlTypes.REASON_CARET)
        # Translators: Input help mode message for move review cursor to next 
line command.
        script_review_nextLine.__doc__=_("Moves the review cursor to the next 
line of the current navigator object and speaks it")
+       script_review_nextLine.category=SCRCAT_TEXTREVIEW
 
        def script_review_bottom(self,gesture):
                
info=api.getReviewPosition().obj.makeTextInfo(textInfos.POSITION_LAST)
@@ -555,6 +626,7 @@ class GlobalCommands(ScriptableObject):
                
speech.speakTextInfo(info,unit=textInfos.UNIT_LINE,reason=controlTypes.REASON_CARET)
        # Translators: Input help mode message for move review cursor to bottom 
line command.
        script_review_bottom.__doc__=_("Moves the review cursor to the bottom 
line of the current navigator object and speaks it")
+       script_review_bottom.category=SCRCAT_TEXTREVIEW
 
        def script_review_previousWord(self,gesture):
                info=api.getReviewPosition().copy()
@@ -570,6 +642,7 @@ class GlobalCommands(ScriptableObject):
                
speech.speakTextInfo(info,reason=controlTypes.REASON_CARET,unit=textInfos.UNIT_WORD)
        # Translators: Input help mode message for move review cursor to 
previous word command.
        script_review_previousWord.__doc__=_("Moves the review cursor to the 
previous word of the current navigator object and speaks it")
+       script_review_previousWord.category=SCRCAT_TEXTREVIEW
 
        def script_review_currentWord(self,gesture):
                info=api.getReviewPosition().copy()
@@ -581,6 +654,7 @@ class GlobalCommands(ScriptableObject):
                        
speech.spellTextInfo(info,useCharacterDescriptions=scriptCount>1)
        # Translators: Input help mode message for report current word under 
review cursor command.
        script_review_currentWord.__doc__=_("Speaks the word of the current 
navigator object where the review cursor is situated. Pressing twice spells the 
word. Pressing three times spells the word using character descriptions")
+       script_review_currentWord.category=SCRCAT_TEXTREVIEW
 
        def script_review_nextWord(self,gesture):
                info=api.getReviewPosition().copy()
@@ -595,6 +669,7 @@ class GlobalCommands(ScriptableObject):
                
speech.speakTextInfo(info,reason=controlTypes.REASON_CARET,unit=textInfos.UNIT_WORD)
        # Translators: Input help mode message for move review cursor to next 
word command.
        script_review_nextWord.__doc__=_("Moves the review cursor to the next 
word of the current navigator object and speaks it")
+       script_review_nextWord.category=SCRCAT_TEXTREVIEW
 
        def script_review_startOfLine(self,gesture):
                info=api.getReviewPosition().copy()
@@ -606,6 +681,7 @@ class GlobalCommands(ScriptableObject):
                
speech.speakTextInfo(info,unit=textInfos.UNIT_CHARACTER,reason=controlTypes.REASON_CARET)
        # Translators: Input help mode message for move review cursor to start 
of current line command.
        script_review_startOfLine.__doc__=_("Moves the review cursor to the 
first character of the line where it is situated in the current navigator 
object and speaks it")
+       script_review_startOfLine.category=SCRCAT_TEXTREVIEW
 
        def script_review_previousCharacter(self,gesture):
                lineInfo=api.getReviewPosition().copy()
@@ -625,6 +701,7 @@ class GlobalCommands(ScriptableObject):
                        
speech.speakTextInfo(charInfo,unit=textInfos.UNIT_CHARACTER,reason=controlTypes.REASON_CARET)
        # Translators: Input help mode message for move review cursor to 
previous character command.
        script_review_previousCharacter.__doc__=_("Moves the review cursor to 
the previous character of the current navigator object and speaks it")
+       script_review_previousCharacter.category=SCRCAT_TEXTREVIEW
 
        def script_review_currentCharacter(self,gesture):
                info=api.getReviewPosition().copy()
@@ -643,6 +720,7 @@ class GlobalCommands(ScriptableObject):
                                
speech.speakTextInfo(info,unit=textInfos.UNIT_CHARACTER,reason=controlTypes.REASON_CARET)
        # Translators: Input help mode message for report current character 
under review cursor command.
        script_review_currentCharacter.__doc__=_("Reports the character of the 
current navigator object where the review cursor is situated. Pressing twice 
reports a description or example of that character. Pressing three times 
reports the numeric value of the character in decimal and hexadecimal")
+       script_review_currentCharacter.category=SCRCAT_TEXTREVIEW
 
        def script_review_nextCharacter(self,gesture):
                lineInfo=api.getReviewPosition().copy()
@@ -662,6 +740,7 @@ class GlobalCommands(ScriptableObject):
                        
speech.speakTextInfo(charInfo,unit=textInfos.UNIT_CHARACTER,reason=controlTypes.REASON_CARET)
        # Translators: Input help mode message for move review cursor to next 
character command.
        script_review_nextCharacter.__doc__=_("Moves the review cursor to the 
next character of the current navigator object and speaks it")
+       script_review_nextCharacter.category=SCRCAT_TEXTREVIEW
 
        def script_review_endOfLine(self,gesture):
                info=api.getReviewPosition().copy()
@@ -674,6 +753,7 @@ class GlobalCommands(ScriptableObject):
                
speech.speakTextInfo(info,unit=textInfos.UNIT_CHARACTER,reason=controlTypes.REASON_CARET)
        # Translators: Input help mode message for move review cursor to end of 
current line command.
        script_review_endOfLine.__doc__=_("Moves the review cursor to the last 
character of the line where it is situated in the current navigator object and 
speaks it")
+       script_review_endOfLine.category=SCRCAT_TEXTREVIEW
 
        def script_speechMode(self,gesture):
                curMode=speech.speechMode
@@ -693,6 +773,7 @@ class GlobalCommands(ScriptableObject):
                speech.speechMode=newMode
        # Translators: Input help mode message for toggle speech mode command.
        script_speechMode.__doc__=_("Toggles between the speech modes of off, 
beep and talk. When set to off NVDA will not speak anything. If beeps then NVDA 
will simply beep each time it its supposed to speak something. If talk then 
NVDA wil just speak normally.")
+       script_speechMode.category=SCRCAT_SPEECH
 
        def script_moveToParentTreeInterceptor(self,gesture):
                obj=api.getFocusObject()
@@ -711,6 +792,7 @@ class GlobalCommands(ScriptableObject):
                        
wx.CallLater(50,eventHandler.executeEvent,"gainFocus",parent.treeInterceptor.rootNVDAObject)
        # Translators: Input help mode message for move to next document with 
focus command, mostly used in web browsing to move from embedded object to the 
webpage document.
        script_moveToParentTreeInterceptor.__doc__=_("Moves the focus to the 
next closest document that contains the focus")
+       script_moveToParentTreeInterceptor.category=SCRCAT_FOCUS
 
        def script_toggleVirtualBufferPassThrough(self,gesture):
                vbuf = api.getFocusObject().treeInterceptor
@@ -724,6 +806,7 @@ class GlobalCommands(ScriptableObject):
                virtualBuffers.reportPassThrough(vbuf)
        # Translators: Input help mode message for toggle focus and browse mode 
command in web browsing and other situations.
        script_toggleVirtualBufferPassThrough.__doc__=_("Toggles between browse 
mode and focus mode. When in focus mode, keys will pass straight through to the 
application, allowing you to interact directly with a control. When in browse 
mode, you can navigate the document with the cursor, quick navigation keys, 
etc.")
+       
script_toggleVirtualBufferPassThrough.category=inputCore.SCRCAT_BROWSEMODE
 
        def script_quit(self,gesture):
                gui.quit()
@@ -739,11 +822,13 @@ class GlobalCommands(ScriptableObject):
                sayAllHandler.readText(sayAllHandler.CURSOR_REVIEW)
        # Translators: Input help mode message for say all in review cursor 
command.
        script_review_sayAll.__doc__ = _("reads from the review cursor  up to 
end of current text, moving the review cursor as it goes")
+       script_review_sayAll.category=SCRCAT_TEXTREVIEW
 
        def script_sayAll(self,gesture):
                sayAllHandler.readText(sayAllHandler.CURSOR_CARET)
        # Translators: Input help mode message for say all with system caret 
command.
        script_sayAll.__doc__ = _("reads from the system caret up to the end of 
the text, moving the caret as it goes")
+       script_sayAll.category=SCRCAT_SYSTEMCARET
 
        def script_reportFormatting(self,gesture):
                formatConfig={
@@ -781,6 +866,7 @@ class GlobalCommands(ScriptableObject):
                ui.message(" ".join(textList))
        # Translators: Input help mode message for report formatting command.
        script_reportFormatting.__doc__ = _("Reports formatting info for the 
current review cursor position within a document")
+       script_reportFormatting.category=SCRCAT_TEXTREVIEW
 
        def script_reportCurrentFocus(self,gesture):
                focusObject=api.getFocusObject()
@@ -793,6 +879,7 @@ class GlobalCommands(ScriptableObject):
                        speech.speakMessage(_("no focus"))
        # Translators: Input help mode message for report current focus command.
        script_reportCurrentFocus.__doc__ = _("reports the object with focus")
+       script_reportCurrentFocus.category=SCRCAT_FOCUS
 
        def script_reportStatusLine(self,gesture):
                obj = api.getStatusBar()
@@ -821,6 +908,7 @@ class GlobalCommands(ScriptableObject):
                        speech.speakSpelling(text)
        # Translators: Input help mode message for report status line text 
command.
        script_reportStatusLine.__doc__ = _("reads the current application 
status bar and moves the navigator to it")
+       script_reportStatusLine.category=SCRCAT_FOCUS
 
        def script_toggleMouseTracking(self,gesture):
                if config.conf["mouse"]["enableMouseTracking"]:
@@ -834,6 +922,7 @@ class GlobalCommands(ScriptableObject):
                ui.message(state)
        # Translators: Input help mode message for toggle mouse tracking 
command.
        script_toggleMouseTracking.__doc__=_("Toggles the reporting of 
information as the mouse moves")
+       script_toggleMouseTracking.category=SCRCAT_MOUSE
 
        def script_title(self,gesture):
                obj=api.getForegroundObject()
@@ -853,6 +942,7 @@ class GlobalCommands(ScriptableObject):
                                ui.message(_("%s copied to clipboard")%title)
        # Translators: Input help mode message for report title bar command.
        script_title.__doc__=_("Reports the title of the current application or 
foreground window. If pressed twice, spells the title. If pressed three times, 
copies the title to the clipboard")
+       script_title.category=SCRCAT_FOCUS
 
        def script_speakForeground(self,gesture):
                obj=api.getForegroundObject()
@@ -860,6 +950,7 @@ class GlobalCommands(ScriptableObject):
                        sayAllHandler.readObjects(obj)
        # Translators: Input help mode message for read foreground object 
command (usually the foreground window).
        script_speakForeground.__doc__ = _("speaks the current foreground 
object")
+       script_speakForeground.category=SCRCAT_FOCUS
 
        def script_test_navigatorDisplayModelText(self,gesture):
                obj=api.getNavigatorObject()
@@ -872,6 +963,7 @@ class GlobalCommands(ScriptableObject):
                log.info("Developer info for navigator object:\n%s" % 
"\n".join(obj.devInfo), activateLogViewer=True)
        # Translators: Input help mode message for developer info for current 
navigator object command, used by developers to examine technical info on 
navigator object. This command also serves as a shortcut to open NVDA log 
viewer.
        script_navigatorObject_devInfo.__doc__ = _("Logs information about the 
current navigator object which is useful to developers and activates the log 
viewer so the information can be examined.")
+       script_navigatorObject_devInfo.category=SCRCAT_TOOLS
 
        def script_toggleProgressBarOutput(self,gesture):
                
outputMode=config.conf["presentation"]["progressBarUpdates"]["progressBarOutputMode"]
@@ -894,6 +986,7 @@ class GlobalCommands(ScriptableObject):
                
config.conf["presentation"]["progressBarUpdates"]["progressBarOutputMode"]=outputMode
        # Translators: Input help mode message for toggle progress bar output 
command.
        script_toggleProgressBarOutput.__doc__=_("Toggles between beeps, 
speech, beeps and speech, and off, for reporting progress bar updates")
+       script_toggleProgressBarOutput.category=SCRCAT_SPEECH
 
        def script_toggleReportDynamicContentChanges(self,gesture):
                if config.conf["presentation"]["reportDynamicContentChanges"]:
@@ -907,6 +1000,7 @@ class GlobalCommands(ScriptableObject):
                ui.message(state)
        # Translators: Input help mode message for toggle dynamic content 
changes command.
        script_toggleReportDynamicContentChanges.__doc__=_("Toggles on and off 
the reporting of dynamic content changes, such as new text in dos console 
windows")
+       script_toggleReportDynamicContentChanges.category=SCRCAT_SPEECH
 
        def script_toggleCaretMovesReviewCursor(self,gesture):
                if config.conf["reviewCursor"]["followCaret"]:
@@ -920,6 +1014,7 @@ class GlobalCommands(ScriptableObject):
                ui.message(state)
        # Translators: Input help mode message for toggle caret moves review 
cursor command.
        script_toggleCaretMovesReviewCursor.__doc__=_("Toggles on and off the 
movement of the review cursor due to the caret moving.")
+       script_toggleCaretMovesReviewCursor.category=SCRCAT_TEXTREVIEW
 
        def script_toggleFocusMovesNavigatorObject(self,gesture):
                if config.conf["reviewCursor"]["followFocus"]:
@@ -933,6 +1028,7 @@ class GlobalCommands(ScriptableObject):
                ui.message(state)
        # Translators: Input help mode message for toggle focus moves navigator 
object command.
        script_toggleFocusMovesNavigatorObject.__doc__=_("Toggles on and off 
the movement of the navigator object due to focus changes") 
+       script_toggleFocusMovesNavigatorObject.category=SCRCAT_OBJECTNAVIGATION
 
        #added by Rui Batista<ruiandrebatista@xxxxxxxxx> to implement a battery 
status script
        def script_say_battery_status(self,gesture):
@@ -957,6 +1053,7 @@ class GlobalCommands(ScriptableObject):
                ui.message(text)
        # Translators: Input help mode message for report battery status 
command.
        script_say_battery_status.__doc__ = _("reports battery status and time 
remaining if AC is not plugged in")
+       script_say_battery_status.category=SCRCAT_SYSTEM
 
        def script_passNextKeyThrough(self,gesture):
                keyboardHandler.passNextKeyThrough()
@@ -964,6 +1061,7 @@ class GlobalCommands(ScriptableObject):
                ui.message(_("Pass next key through"))
        # Translators: Input help mode message for pass next key through 
command.
        script_passNextKeyThrough.__doc__=_("The next key that is pressed will 
not be handled at all by NVDA, it will be passed directly through to Windows.")
+       script_passNextKeyThrough.category=SCRCAT_INPUT
 
        def script_reportAppModuleInfo(self,gesture):
                focus=api.getFocusObject()
@@ -981,51 +1079,62 @@ class GlobalCommands(ScriptableObject):
                ui.message(message)
        # Translators: Input help mode message for report current program name 
and app module name command.
        script_reportAppModuleInfo.__doc__ = _("Speaks the filename of the 
active application along with the name of the currently loaded appModule")
+       script_reportAppModuleInfo.category=SCRCAT_TOOLS
 
        def script_activateGeneralSettingsDialog(self, gesture):
                wx.CallAfter(gui.mainFrame.onGeneralSettingsCommand, None)
        # Translators: Input help mode message for go to general settings 
dialog command.
        script_activateGeneralSettingsDialog.__doc__ = _("Shows the NVDA 
general settings dialog")
+       script_activateGeneralSettingsDialog.category=SCRCAT_CONFIG
+
 
        def script_activateSynthesizerDialog(self, gesture):
                wx.CallAfter(gui.mainFrame.onSynthesizerCommand, None)
        # Translators: Input help mode message for go to synthesizer dialog 
command.
        script_activateSynthesizerDialog.__doc__ = _("Shows the NVDA 
synthesizer dialog")
+       script_activateSynthesizerDialog.category=SCRCAT_CONFIG
 
        def script_activateVoiceDialog(self, gesture):
                wx.CallAfter(gui.mainFrame.onVoiceCommand, None)
        # Translators: Input help mode message for go to voice settings dialog 
command.
        script_activateVoiceDialog.__doc__ = _("Shows the NVDA voice settings 
dialog")
+       script_activateVoiceDialog.category=SCRCAT_CONFIG
 
        def script_activateKeyboardSettingsDialog(self, gesture):
                wx.CallAfter(gui.mainFrame.onKeyboardSettingsCommand, None)
        # Translators: Input help mode message for go to keyboard settings 
dialog command.
        script_activateKeyboardSettingsDialog.__doc__ = _("Shows the NVDA 
keyboard settings dialog")
+       script_activateKeyboardSettingsDialog.category=SCRCAT_CONFIG
 
        def script_activateMouseSettingsDialog(self, gesture):
                wx.CallAfter(gui.mainFrame.onMouseSettingsCommand, None)
        # Translators: Input help mode message for go to mouse settings dialog 
command.
        script_activateMouseSettingsDialog.__doc__ = _("Shows the NVDA mouse 
settings dialog")
+       script_activateMouseSettingsDialog.category=SCRCAT_CONFIG
 
        def script_activateObjectPresentationDialog(self, gesture):
                wx.CallAfter(gui.mainFrame. onObjectPresentationCommand, None)
        # Translators: Input help mode message for go to object presentation 
dialog command.
        script_activateObjectPresentationDialog.__doc__ = _("Shows the NVDA 
object presentation settings dialog")
+       script_activateObjectPresentationDialog.category=SCRCAT_CONFIG
 
        def script_activateBrowseModeDialog(self, gesture):
                wx.CallAfter(gui.mainFrame.onBrowseModeCommand, None)
        # Translators: Input help mode message for go to browse mode dialog 
command.
        script_activateBrowseModeDialog.__doc__ = _("Shows the NVDA browse mode 
settings dialog")
+       script_activateBrowseModeDialog.category=SCRCAT_CONFIG
 
        def script_activateDocumentFormattingDialog(self, gesture):
                wx.CallAfter(gui.mainFrame.onDocumentFormattingCommand, None)
        # Translators: Input help mode message for go to document formatting 
dialog command.
        script_activateDocumentFormattingDialog.__doc__ = _("Shows the NVDA 
document formatting settings dialog")
+       script_activateDocumentFormattingDialog.category=SCRCAT_CONFIG
 
        def script_saveConfiguration(self,gesture):
                wx.CallAfter(gui.mainFrame.onSaveConfigurationCommand, None)
        # Translators: Input help mode message for save current configuration 
command.
        script_saveConfiguration.__doc__ = _("Saves the current NVDA 
configuration")
+       script_saveConfiguration.category=SCRCAT_CONFIG
 
        def script_revertConfiguration(self,gesture):
                scriptCount=scriptHandler.getLastScriptRepeatCount()
@@ -1035,6 +1144,13 @@ class GlobalCommands(ScriptableObject):
                        
gui.mainFrame.onRevertToDefaultConfigurationCommand(None)
        # Translators: Input help mode message for apply last saved or default 
settings command.
        script_revertConfiguration.__doc__ = _("Pressing once reverts the 
current configuration to the most recently saved state. Pressing three times 
reverts to factory defaults.")
+       script_revertConfiguration.category=SCRCAT_CONFIG
+
+       def script_activateInputGesturesDialog(self, gesture):
+               wx.CallAfter(gui.mainFrame.onInputGesturesCommand, None)
+       # Translators: Input help mode message for go to Input Gestures dialog 
command.
+       script_activateInputGesturesDialog.__doc__ = _("Shows the NVDA input 
gestures dialog")
+       script_activateInputGesturesDialog.category=SCRCAT_CONFIG
 
        def script_activatePythonConsole(self,gesture):
                if globalVars.appArgs.secure:
@@ -1046,6 +1162,7 @@ class GlobalCommands(ScriptableObject):
                pythonConsole.activate()
        # Translators: Input help mode message for activate python console 
command.
        script_activatePythonConsole.__doc__ = _("Activates the NVDA Python 
Console, primarily useful for development")
+       script_activatePythonConsole.category=SCRCAT_TOOLS
 
        def script_braille_toggleTether(self, gesture):
                if braille.handler.tether == braille.handler.TETHER_FOCUS:
@@ -1058,6 +1175,7 @@ class GlobalCommands(ScriptableObject):
                ui.message(_("Braille tethered to %s") % tetherMsg)
        # Translators: Input help mode message for toggle braille tether to 
command (tethered means connected to or follows).
        script_braille_toggleTether.__doc__ = _("Toggle tethering of braille 
between the focus and the review position")
+       script_braille_toggleTether.category=SCRCAT_BRAILLE
 
        def script_reportClipboardText(self,gesture):
                try:
@@ -1076,6 +1194,7 @@ class GlobalCommands(ScriptableObject):
                        ui.message(_("The clipboard contains a large portion of 
text. It is %s characters long") % len(text))
        # Translators: Input help mode message for report clipboard text 
command.
        script_reportClipboardText.__doc__ = _("Reports the text on the Windows 
clipboard")
+       script_reportClipboardText.category=SCRCAT_SYSTEM
 
        def script_review_markStartForCopy(self, gesture):
                self._copyStartMarker = api.getReviewPosition().copy()
@@ -1083,6 +1202,7 @@ class GlobalCommands(ScriptableObject):
                ui.message(_("Start marked"))
        # Translators: Input help mode message for mark review cursor position 
for copy command (that is, marks the current review cursor position as the 
starting point for text to be copied).
        script_review_markStartForCopy.__doc__ = _("Marks the current position 
of the review cursor as the start of text to be copied")
+       script_review_markStartForCopy.category=SCRCAT_TEXTREVIEW
 
        def script_review_copy(self, gesture):
                if not getattr(self, "_copyStartMarker", None):
@@ -1106,40 +1226,47 @@ class GlobalCommands(ScriptableObject):
                self._copyStartMarker = None
        # Translators: Input help mode message for copy selected review cursor 
text to clipboard command.
        script_review_copy.__doc__ = _("Retrieves the text from the previously 
set start marker up to and including the current position of the review cursor 
and copies it to the clipboard")
+       script_review_copy.category=SCRCAT_TEXTREVIEW
 
        def script_braille_scrollBack(self, gesture):
                braille.handler.scrollBack()
        # Translators: Input help mode message for a braille command.
        script_braille_scrollBack.__doc__ = _("Scrolls the braille display 
back")
        script_braille_scrollBack.bypassInputHelp = True
+       script_braille_scrollBack.category=SCRCAT_BRAILLE
 
        def script_braille_scrollForward(self, gesture):
                braille.handler.scrollForward()
        # Translators: Input help mode message for a braille command.
        script_braille_scrollForward.__doc__ = _("Scrolls the braille display 
forward")
        script_braille_scrollForward.bypassInputHelp = True
+       script_braille_scrollForward.category=SCRCAT_BRAILLE
 
        def script_braille_routeTo(self, gesture):
                braille.handler.routeTo(gesture.routingIndex)
        # Translators: Input help mode message for a braille command.
        script_braille_routeTo.__doc__ = _("Routes the cursor to or activates 
the object under this braille cell")
+       script_braille_routeTo.category=SCRCAT_BRAILLE
 
        def script_braille_previousLine(self, gesture):
                if braille.handler.buffer.regions: 
                        
braille.handler.buffer.regions[-1].previousLine(start=True)
        # Translators: Input help mode message for a braille command.
        script_braille_previousLine.__doc__ = _("Moves the braille display to 
the previous line")
+       script_braille_previousLine.category=SCRCAT_BRAILLE
 
        def script_braille_nextLine(self, gesture):
                if braille.handler.buffer.regions: 
                        braille.handler.buffer.regions[-1].nextLine()
        # Translators: Input help mode message for a braille command.
        script_braille_nextLine.__doc__ = _("Moves the braille display to the 
next line")
+       script_braille_nextLine.category=SCRCAT_BRAILLE
 
        def script_braille_dots(self, gesture):
                brailleInput.handler.input(gesture.dots)
        # Translators: Input help mode message for a braille command.
        script_braille_dots.__doc__= _("Inputs braille dots via the braille 
keyboard")
+       script_braille_dots.category=SCRCAT_BRAILLE
 
        def script_reloadPlugins(self, gesture):
                import globalPluginHandler
@@ -1150,6 +1277,7 @@ class GlobalCommands(ScriptableObject):
                ui.message(_("Plugins reloaded"))
        # Translators: Input help mode message for reload plugins command.
        script_reloadPlugins.__doc__=_("Reloads app modules and global plugins 
without restarting NVDA, which can be Useful for developers")
+       script_reloadPlugins.category=SCRCAT_TOOLS
 
        def script_navigatorObject_nextInFlow(self,gesture):
                curObject=api.getNavigatorObject()
@@ -1172,6 +1300,7 @@ class GlobalCommands(ScriptableObject):
                        ui.message(_("no next"))
        # Translators: Input help mode message for a touchscreen gesture.
        script_navigatorObject_nextInFlow.__doc__=_("Moves to the next object 
in a flattened view of the object navigation hierarchy")
+       script_navigatorObject_nextInFlow.category=SCRCAT_OBJECTNAVIGATION
 
        def script_navigatorObject_previousInFlow(self,gesture):
                curObject=api.getNavigatorObject()
@@ -1189,6 +1318,7 @@ class GlobalCommands(ScriptableObject):
                        ui.message(_("no next"))
        # Translators: Input help mode message for a touchscreen gesture.
        script_navigatorObject_previousInFlow.__doc__=_("Moves to the previous 
object in a flattened view of the object navigation hierarchy")
+       script_navigatorObject_previousInFlow.category=SCRCAT_OBJECTNAVIGATION
 
        def script_touch_changeMode(self,gesture):
                mode=touchHandler.handler._curTouchMode
@@ -1199,16 +1329,20 @@ class GlobalCommands(ScriptableObject):
                ui.message(_("%s mode")%newMode)
        # Translators: Input help mode message for a touchscreen gesture.
        script_touch_changeMode.__doc__=_("cycles between available touch 
modes")
+       script_touch_changeMode.category=SCRCAT_TOUCH
+
 
        def script_touch_newExplore(self,gesture):
                
touchHandler.handler.screenExplorer.moveTo(gesture.tracker.x,gesture.tracker.y,new=True)
        # Translators: Input help mode message for a touchscreen gesture.
        script_touch_newExplore.__doc__=_("Reports the object and content 
directly under your finger")
+       script_touch_newExplore.category=SCRCAT_TOUCH
 
        def script_touch_explore(self,gesture):
                
touchHandler.handler.screenExplorer.moveTo(gesture.tracker.x,gesture.tracker.y)
        # Translators: Input help mode message for a touchscreen gesture.
        script_touch_explore.__doc__=_("Reports the new object or content under 
your finger if different to where your finger was last")
+       script_touch_explore.category=SCRCAT_TOUCH
 
        def script_touch_hoverUp(self,gesture):
                #Specifically for touch typing with onscreen keyboard keys
@@ -1216,6 +1350,7 @@ class GlobalCommands(ScriptableObject):
                import NVDAObjects.UIA
                if isinstance(obj,NVDAObjects.UIA.UIA) and 
obj.UIAElement.cachedClassName=="CRootKey":
                        obj.doAction()
+       script_touch_hoverUp.category=SCRCAT_TOUCH
 
        def script_activateConfigProfilesDialog(self, gesture):
                wx.CallAfter(gui.mainFrame.onConfigProfilesCommand, None)
@@ -1361,6 +1496,7 @@ class GlobalCommands(ScriptableObject):
                "kb:NVDA+control+o": "activateObjectPresentationDialog",
                "kb:NVDA+control+b": "activateBrowseModeDialog",
                "kb:NVDA+control+d": "activateDocumentFormattingDialog",
+               "kb:NVDA+control+i": "activateInputGesturesDialog",
 
                # Configuration management
                "kb:NVDA+control+c": "saveConfiguration",

diff --git a/source/gui/__init__.py b/source/gui/__init__.py
index 94e7881..0324263 100644
--- a/source/gui/__init__.py
+++ b/source/gui/__init__.py
@@ -225,6 +225,9 @@ class MainFrame(wx.Frame):
        def onSpeechSymbolsCommand(self, evt):
                self._popupSettingsDialog(SpeechSymbolsDialog)
 
+       def onInputGesturesCommand(self, evt):
+               self._popupSettingsDialog(InputGesturesDialog)
+
        def onAboutCommand(self,evt):
                # Translators: The title of the dialog to show about info for 
NVDA.
                messageBox(versionInfo.aboutMessage, _("About NVDA"), wx.OK)
@@ -351,6 +354,9 @@ class SysTrayIcon(wx.TaskBarIcon):
                        # Translators: The label for the menu item to open 
Punctuation/symbol pronunciation dialog.
                        item = menu_preferences.Append(wx.ID_ANY, 
_("&Punctuation/symbol pronunciation..."))
                        self.Bind(wx.EVT_MENU, frame.onSpeechSymbolsCommand, 
item)
+                       # Translators: The label for the menu item to open the 
Input Gestures dialog.
+                       item = menu_preferences.Append(wx.ID_ANY, _("I&nput 
gestures..."))
+                       self.Bind(wx.EVT_MENU, frame.onInputGesturesCommand, 
item)
                # Translators: The label for Preferences submenu in NVDA menu.
                
self.menu.AppendMenu(wx.ID_ANY,_("&Preferences"),menu_preferences)
 

diff --git a/source/gui/settingsDialogs.py b/source/gui/settingsDialogs.py
index f4cd6e2..bd10732 100644
--- a/source/gui/settingsDialogs.py
+++ b/source/gui/settingsDialogs.py
@@ -30,6 +30,7 @@ try:
        import updateCheck
 except RuntimeError:
        updateCheck = None
+import inputCore
 
 class SettingsDialog(wx.Dialog):
        """A settings dialog.
@@ -1516,3 +1517,148 @@ class SpeechSymbolsDialog(SettingsDialog):
                        log.error("Error saving user symbols info: %s" % e)
                
characterProcessing._localeSpeechSymbolProcessors.invalidateLocaleData(self.symbolProcessor.locale)
                super(SpeechSymbolsDialog, self).onOk(evt)
+
+class InputGesturesDialog(SettingsDialog):
+       # Translators: The title of the Input Gestures dialog where the user 
can remap input gestures for commands.
+       title = _("Input Gestures")
+
+       def makeSettings(self, settingsSizer):
+               tree = self.tree = wx.TreeCtrl(self, style=wx.TR_HAS_BUTTONS | 
wx.TR_HIDE_ROOT | wx.TR_SINGLE)
+               self.treeRoot = tree.AddRoot("root")
+               tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.onTreeSelect)
+               settingsSizer.Add(tree, proportion=7, flag=wx.EXPAND)
+
+               gestures = inputCore.manager.getAllGestureMappings()
+               for category in sorted(gestures):
+                       treeCat = tree.AppendItem(self.treeRoot, category)
+                       commands = gestures[category]
+                       for command in sorted(commands):
+                               treeCom = tree.AppendItem(treeCat, command)
+                               commandInfo = commands[command]
+                               tree.SetItemPyData(treeCom, commandInfo)
+                               for gesture in commandInfo.gestures:
+                                       treeGes = tree.AppendItem(treeCom, 
self._formatGesture(gesture))
+                                       tree.SetItemPyData(treeGes, gesture)
+
+               sizer = wx.BoxSizer(wx.HORIZONTAL)
+               # Translators: The label of a button to add a gesture in the 
Input Gestures dialog.
+               item = self.addButton = wx.Button(self, label=_("&Add"))
+               item.Bind(wx.EVT_BUTTON, self.onAdd)
+               item.Disable()
+               sizer.Add(item)
+               # Translators: The label of a button to remove a gesture in the 
Input Gestures dialog.
+               item = self.removeButton = wx.Button(self, label=_("&Remove"))
+               item.Bind(wx.EVT_BUTTON, self.onRemove)
+               item.Disable()
+               self.pendingAdds = set()
+               self.pendingRemoves = set()
+               sizer.Add(item)
+               settingsSizer.Add(sizer)
+
+       def postInit(self):
+               self.tree.SetFocus()
+
+       def _formatGesture(self, identifier):
+               try:
+                       source, main = 
inputCore.getDisplayTextForGestureIdentifier(identifier)
+                       # Translators: Describes a gesture in the Input 
Gestures dialog.
+                       # {main} is replaced with the main part of the gesture; 
e.g. alt+tab.
+                       # {source} is replaced with the gesture's source; e.g. 
laptop keyboard.
+                       return _("{main} ({source})").format(main=main, 
source=source)
+               except LookupError:
+                       return identifier
+
+       def onTreeSelect(self, evt):
+               item = self.tree.Selection
+               data = self.tree.GetItemPyData(item)
+               isCommand = isinstance(data, inputCore.AllGesturesScriptInfo)
+               isGesture = isinstance(data, basestring)
+               self.addButton.Enabled = isCommand or isGesture
+               self.removeButton.Enabled = isGesture
+
+       def onAdd(self, evt):
+               if inputCore.manager._captureFunc:
+                       return
+
+               treeCom = self.tree.Selection
+               scriptInfo = self.tree.GetItemPyData(treeCom)
+               if not isinstance(scriptInfo, inputCore.AllGesturesScriptInfo):
+                       treeCom = self.tree.GetItemParent(treeCom)
+                       scriptInfo = self.tree.GetItemPyData(treeCom)
+               # Translators: The prompt to enter a gesture in the Input 
Gestures dialog.
+               treeGes = self.tree.AppendItem(treeCom, _("Enter input 
gesture:"))
+               self.tree.SelectItem(treeGes)
+               self.tree.SetFocus()
+
+               def addGestureCaptor(gesture):
+                       if gesture.isModifier:
+                               return False
+                       inputCore.manager._captureFunc = None
+                       wx.CallAfter(self._addCaptured, treeGes, scriptInfo, 
gesture)
+                       return False
+               inputCore.manager._captureFunc = addGestureCaptor
+
+       def _addCaptured(self, treeGes, scriptInfo, gesture):
+               gids = gesture.identifiers
+               if len(gids) > 1:
+                       # Multiple choices. Present them in a pop-up menu.
+                       menu = wx.Menu()
+                       for gid in gids:
+                               disp = self._formatGesture(gid)
+                               item = menu.Append(wx.ID_ANY, disp)
+                               self.Bind(wx.EVT_MENU,
+                                       lambda evt: self._addChoice(treeGes, 
scriptInfo, gesture, gid, disp),
+                                       item)
+                       self.PopupMenu(menu)
+                       if not self.tree.GetItemPyData(treeGes):
+                               # No item was selected, so use the first.
+                               self._addChoice(treeGes, scriptInfo, gesture, 
gids[0],
+                                       self._formatGesture(gids[0]))
+                       menu.Destroy()
+               else:
+                       self._addChoice(treeGes, scriptInfo, gesture, gids[0],
+                               self._formatGesture(gids[0]))
+
+       def _addChoice(self, treeGes, scriptInfo, gesture, gid, disp):
+               self.pendingAdds.add((gid, scriptInfo.moduleName, 
scriptInfo.className, scriptInfo.scriptName))
+               self.tree.SetItemText(treeGes, disp)
+               self.tree.SetItemPyData(treeGes, gid)
+               self.onTreeSelect(None)
+
+       def onRemove(self, evt):
+               treeGes = self.tree.Selection
+               gesture = self.tree.GetItemPyData(treeGes)
+               treeCom = self.tree.GetItemParent(treeGes)
+               scriptInfo = self.tree.GetItemPyData(treeCom)
+               self.pendingRemoves.add((gesture, scriptInfo.moduleName, 
scriptInfo.className, scriptInfo.scriptName))
+               self.tree.Delete(treeGes)
+               self.tree.SetFocus()
+
+       def onOk(self, evt):
+               for gesture, module, className, scriptName in 
self.pendingRemoves:
+                       try:
+                               
inputCore.manager.userGestureMap.remove(gesture, module, className, scriptName)
+                       except ValueError:
+                               # The user wants to unbind a gesture they 
didn't define.
+                               inputCore.manager.userGestureMap.add(gesture, 
module, className, None)
+
+               for gesture, module, className, scriptName in self.pendingAdds:
+                       try:
+                               # The user might have unbound this gesture,
+                               # so remove this override first.
+                               
inputCore.manager.userGestureMap.remove(gesture, module, className, None)
+                       except ValueError:
+                               pass
+                       inputCore.manager.userGestureMap.add(gesture, module, 
className, scriptName)
+
+               if self.pendingAdds or self.pendingRemoves:
+                       # Only save if there is something to save.
+                       try:
+                               inputCore.manager.userGestureMap.save()
+                       except:
+                               log.debugWarning("", exc_info=True)
+                               # Translators: An error displayed when saving 
user defined input gestures fails.
+                               gui.messageBox(_("Error saving user defined 
gestures - probably read only file system."),
+                                       _("Error"), wx.OK | wx.ICON-ERROR)
+
+               super(InputGesturesDialog, self).onOk(evt)

diff --git a/source/inputCore.py b/source/inputCore.py
index 9feaf8f..67292d3 100644
--- a/source/inputCore.py
+++ b/source/inputCore.py
@@ -13,6 +13,7 @@ For example, it is used to execute gestures and handle input 
help.
 import sys
 import os
 import itertools
+import weakref
 import configobj
 import baseObject
 import scriptHandler
@@ -27,6 +28,16 @@ import globalVars
 import languageHandler
 import controlTypes
 
+#: Script category for emulated keyboard keys.
+# Translators: The name of a category of NVDA commands.
+SCRCAT_KBEMU = _("Emulated system keyboard keys")
+#: Script category for miscellaneous commands.
+# Translators: The name of a category of NVDA commands.
+SCRCAT_MISC = _("Miscellaneous")
+#: Script category for Browse Mode  commands.
+# Translators: The name of a category of NVDA commands.
+SCRCAT_BROWSEMODE = _("Browse mode")
+
 class NoInputGestureAction(LookupError):
        """Informs that there is no action to execute for a gesture.
        """
@@ -114,6 +125,22 @@ class InputGesture(baseObject.AutoPropertyObject):
                """
                return None
 
+       @classmethod
+       def getDisplayTextForIdentifier(cls, identifier):
+               """Get the text to be presented to the user describing a given 
gesture identifier.
+               This should only be called for gesture identifiers associated 
with this class.
+               Most callers will want L{inputCore.getDisplayTextForIdentifier} 
instead.
+               The display text consists of two strings:
+               the gesture's source (e.g. "laptop keyboard")
+               and the specific gesture (e.g. "alt+tab").
+               @param identifier: The normalized gesture identifier in 
question.
+               @type identifier: basestring
+               @return: A tuple of (source, specificGesture).
+               @rtype: tuple of (basestring, basestring)
+               @raise Exception: If no display text can be determined.
+               """
+               raise NotImplementedError
+
 class GlobalGestureMap(object):
        """Maps gestures to scripts anywhere in NVDA.
        This is used to allow users and locales to bind gestures in addition to 
those bound by individual scriptable objects.
@@ -130,6 +157,9 @@ class GlobalGestureMap(object):
                #: Indicates that the last load or update contained an error.
                #: @type: bool
                self.lastUpdateContainedError = False
+               #: The file name for this gesture map, if any.
+               #: @type: basestring
+               self.fileName = None
                if entries:
                        self.update(entries)
 
@@ -177,6 +207,7 @@ class GlobalGestureMap(object):
                @param filename: The name of the file to load.
                @type: str
                """
+               self.fileName = filename
                try:
                        conf = configobj.ConfigObj(filename, file_error=True, 
encoding="UTF-8")
                except (configobj.ConfigObjError,UnicodeDecodeError), e:
@@ -249,15 +280,79 @@ class GlobalGestureMap(object):
                                continue
                        yield cls, scriptName
 
+       def getScriptsForAllGestures(self):
+               """Get all of the scripts and their gestures.
+               @return: The Python class, gesture and script name for each 
mapping;
+                       the script name may be C{None} indicating that the 
gesture should be unbound for this class.
+               @rtype: generator of (class, str, str)
+               """
+               for gesture in self._map:
+                       for cls, scriptName in 
self.getScriptsForGesture(gesture):
+                               yield cls, gesture, scriptName
+
+       def remove(self, gesture, module, className, script):
+               """Remove a gesture mapping.
+               @param gesture: The gesture identifier.
+               @type gesture: str
+               @param module: The name of the Python module containing the 
target script.
+               @type module: str
+               @param className: The name of the class in L{module} containing 
the target script.
+               @type className: str
+               @param script: The name of the target script.
+               @type script: str
+               @raise ValueError: If the requested mapping does not exist.
+               """
+               gesture = normalizeGestureIdentifier(gesture)
+               try:
+                       scripts = self._map[gesture]
+               except KeyError:
+                       raise ValueError("Mapping not found")
+               scripts.remove((module, className, script))
+
+       def save(self):
+               """Save this gesture map to disk.
+               @precondition: L{load} must have been called.
+               """
+               if globalVars.appArgs.secure:
+                       return
+               if not self.fileName:
+                       raise ValueError("No file name")
+               out = configobj.ConfigObj()
+               out.filename = self.fileName
+
+               for gesture, scripts in self._map.iteritems():
+                       for module, className, script in scripts:
+                               key = "%s.%s" % (module, className)
+                               try:
+                                       outSect = out[key]
+                               except KeyError:
+                                       out[key] = {}
+                                       outSect = out[key]
+                               if script is None:
+                                       script = "None"
+                               try:
+                                       outVal = outSect[script]
+                               except KeyError:
+                                       # Write the first value as a string so 
configobj doesn't output a comma if there's only one value.
+                                       outVal = outSect[script] = gesture
+                               else:
+                                       if isinstance(outVal, list):
+                                               outVal.append(gesture)
+                                       else:
+                                               outSect[script] = [outVal, 
gesture]
+
+               out.write()
+
 class InputManager(baseObject.AutoPropertyObject):
        """Manages functionality related to input from the user.
        Input includes key presses on the keyboard, as well as key presses on 
Braille displays, etc.
        """
 
        def __init__(self):
-               #: Whether input help is enabled, wherein the function of each 
key pressed by the user is reported but not executed.
-               #: @type: bool
-               self.isInputHelpActive = False
+               #: The function to call when capturing gestures.
+               #: If it returns C{False}, normal execution will be prevented.
+               #: @type: callable
+               self._captureFunc = None
                #: The gestures mapped for the NVDA locale.
                #: @type: L{GlobalGestureMap}
                self.localeGestureMap = GlobalGestureMap()
@@ -293,11 +388,13 @@ class InputManager(baseObject.AutoPropertyObject):
                if log.isEnabledFor(log.IO) and not gesture.isModifier:
                        log.io("Input: %s" % gesture.logIdentifier)
 
-               if self.isInputHelpActive:
-                       bypass = getattr(script, "bypassInputHelp", False)
-                       queueHandler.queueFunction(queueHandler.eventQueue, 
self._handleInputHelp, gesture, onlyLog=bypass)
-                       if not bypass:
-                               return
+               if self._captureFunc:
+                       try:
+                               if self._captureFunc(gesture) is False:
+                                       return
+                       except:
+                               log.error("Error in capture function, 
disabling", exc_info=True)
+                               self._captureFunc = None
 
                if gesture.isModifier:
                        raise NoInputGestureAction
@@ -313,6 +410,23 @@ class InputManager(baseObject.AutoPropertyObject):
 
                raise NoInputGestureAction
 
+       def _get_isInputHelpActive(self):
+               """Whether input help is enabled, wherein the function of each 
key pressed by the user is reported but not executed.
+               @rtype: bool
+               """
+               return self._captureFunc == self._inputHelpCaptor
+
+       def _set_isInputHelpActive(self, enable):
+               if enable:
+                       self._captureFunc = self._inputHelpCaptor
+               elif self.isInputHelpActive:
+                       self._captureFunc = None
+
+       def _inputHelpCaptor(self, gesture):
+               bypass = getattr(gesture.script, "bypassInputHelp", False)
+               queueHandler.queueFunction(queueHandler.eventQueue, 
self._handleInputHelp, gesture, onlyLog=bypass)
+               return bypass
+
        def _handleInputHelp(self, gesture, onlyLog=False):
                textList = [gesture.displayName]
                script = gesture.script
@@ -379,6 +493,152 @@ class InputManager(baseObject.AutoPropertyObject):
                except NotImplementedError:
                        pass
 
+       def getAllGestureMappings(self, obj=None, ancestors=None):
+               if not obj:
+                       obj = api.getFocusObject()
+                       ancestors = api.getFocusAncestors()
+               return _AllGestureMappingsRetriever(obj, ancestors).results
+
+class _AllGestureMappingsRetriever(object):
+
+       def __init__(self, obj, ancestors):
+               self.results = {}
+               self.scriptInfo = {}
+               self.handledGestures = set()
+
+               self.addGlobalMap(manager.userGestureMap)
+               self.addGlobalMap(manager.localeGestureMap)
+               import braille
+               gmap = braille.handler.display.gestureMap
+               if gmap:
+                       self.addGlobalMap(gmap)
+
+               # Global plugins.
+               import globalPluginHandler
+               for plugin in globalPluginHandler.runningPlugins:
+                       self.addObj(plugin)
+
+               # App module.
+               app = obj.appModule
+               if app:
+                       self.addObj(app)
+
+               # Tree interceptor.
+               ti = obj.treeInterceptor
+               if ti:
+                       self.addObj(ti)
+
+               # NVDAObject.
+               self.addObj(obj)
+               for anc in reversed(ancestors):
+                       self.addObj(anc, isAncestor=True)
+
+               # Global commands.
+               import globalCommands
+               self.addObj(globalCommands.commands)
+
+       def addResult(self, scriptInfo):
+               self.scriptInfo[scriptInfo.cls, scriptInfo.scriptName] = 
scriptInfo
+               try:
+                       cat = self.results[scriptInfo.category]
+               except KeyError:
+                       cat = self.results[scriptInfo.category] = {}
+               cat[scriptInfo.displayName] = scriptInfo
+
+       def addGlobalMap(self, gmap):
+               for cls, gesture, scriptName in gmap.getScriptsForAllGestures():
+                       key = (cls, gesture)
+                       if key in self.handledGestures:
+                               continue
+                       self.handledGestures.add(key)
+                       if scriptName is None:
+                               # The global map specified that no script 
should execute for this gesture and object.
+                               continue
+                       try:
+                               scriptInfo = self.scriptInfo[cls, scriptName]
+                       except KeyError:
+                               if scriptName.startswith("kb:"):
+                                       scriptInfo = 
self.makeKbEmuScriptInfo(cls, scriptName)
+                               else:
+                                       try:
+                                               script = getattr(cls, 
"script_%s" % scriptName)
+                                       except AttributeError:
+                                               continue
+                                       scriptInfo = 
self.makeNormalScriptInfo(cls, scriptName, script)
+                                       if not scriptInfo:
+                                               continue
+                               self.addResult(scriptInfo)
+                       scriptInfo.gestures.append(gesture)
+
+       def makeKbEmuScriptInfo(self, cls, scriptName):
+               info = AllGesturesScriptInfo(cls, scriptName)
+               info.category = SCRCAT_KBEMU
+               info.displayName = scriptName[3:]
+               return info
+
+       def makeNormalScriptInfo(self, cls, scriptName, script):
+               info = AllGesturesScriptInfo(cls, scriptName)
+               info.category = self.getScriptCategory(cls, script)
+               info.displayName = script.__doc__
+               if not info.displayName:
+                       return None
+               return info
+
+       def getScriptCategory(self, cls, script):
+               try:
+                       return script.category
+               except AttributeError:
+                       pass
+               try:
+                       return cls.scriptCategory
+               except AttributeError:
+                       pass
+               return SCRCAT_MISC
+
+       def addObj(self, obj, isAncestor=False):
+               scripts = {}
+               for cls in obj.__class__.__mro__:
+                       for scriptName, script in cls.__dict__.iteritems():
+                               if not scriptName.startswith("script_"):
+                                       continue
+                               if isAncestor and not getattr(script, 
"canPropagate", False):
+                                       continue
+                               scriptName = scriptName[7:]
+                               try:
+                                       scriptInfo = self.scriptInfo[cls, 
scriptName]
+                               except KeyError:
+                                       scriptInfo = 
self.makeNormalScriptInfo(cls, scriptName, script)
+                                       if not scriptInfo:
+                                               continue
+                                       self.addResult(scriptInfo)
+                               scripts[script] = scriptInfo
+               for gesture, script in obj._gestureMap.iteritems():
+                       try:
+                               scriptInfo = scripts[script.__func__]
+                       except KeyError:
+                               continue
+                       key = (scriptInfo.cls, gesture)
+                       if key in self.handledGestures:
+                               continue
+                       self.handledGestures.add(key)
+                       scriptInfo.gestures.append(gesture)
+
+class AllGesturesScriptInfo(object):
+       __slots__ = ("cls", "scriptName", "category", "displayName", "gestures")
+       
+       def __init__(self, cls, scriptName):
+               self.cls = cls
+               self.scriptName = scriptName
+               self.gestures = []
+
+       @property
+       def moduleName(self):
+               return self.cls.__module__
+
+       @property
+       def className(self):
+               return self.cls.__name__
+
 def normalizeGestureIdentifier(identifier):
        """Normalize a gesture identifier so that it matches other identifiers 
for the same gesture.
        Any items separated by a + sign after the source are considered to be 
of indeterminate order
@@ -393,6 +653,59 @@ def normalizeGestureIdentifier(identifier):
        main = "+".join(frozenset(main))
        return u"{0}:{1}".format(prefix, main).lower()
 
+#: Maps registered source prefix strings to L{InputGesture} classes.
+gestureSources = weakref.WeakValueDictionary()
+
+def registerGestureSource(source, gestureCls):
+       """Register an input gesture class for a source prefix string.
+       The specified gesture class will be used for queries regarding all 
gesture identifiers with the given source prefix.
+       For example, if "kb" is registered with the C{KeyboardInputGesture} 
class,
+       any queries for "kb:tab" or "kb(desktop):tab" will be directed to the 
C{KeyboardInputGesture} class.
+       If there is no exact match for the source, any parenthesised portion is 
stripped.
+       For example, for "br(baum):d1", if "br(baum)" isn't registered,
+       "br" will be used if it is registered.
+       This registration is used, for example, to get the display text for a 
gesture identifier.
+       @param source: The source prefix for associated gesture identifiers.
+       @type source: basestring
+       @param gestureCls: The input gesture class.
+       @type gestureCls: L{InputGesture}
+       """
+       gestureSources[source] = gestureCls
+
+def _getGestureClsForIdentifier(identifier):
+       """Get the registered gesture class for an identifier.
+       """
+       source = identifier.split(":", 1)[0]
+       try:
+               return gestureSources[source]
+       except KeyError:
+               pass
+       genSource = source.split("(", 1)[0]
+       if genSource:
+               try:
+                       return gestureSources[genSource]
+               except KeyError:
+                       pass
+       raise LookupError("Gesture source not registered: %s" % source)
+
+def getDisplayTextForGestureIdentifier(identifier):
+       """Get the text to be presented to the user describing a given gesture 
identifier.
+       The display text consists of two strings:
+       the gesture's source (e.g. "laptop keyboard")
+       and the specific gesture (e.g. "alt+tab").
+       @param identifier: The normalized gesture identifier in question.
+       @type identifier: basestring
+       @return: A tuple of (source, specificGesture).
+       @rtype: tuple of (basestring, basestring)
+       @raise LookupError: If no display text can be determined.
+       """
+       gcls = _getGestureClsForIdentifier(identifier)
+       try:
+               return gcls.getDisplayTextForIdentifier(identifier)
+       except:
+               raise
+               raise LookupError("Couldn't get display text for identifier: 
%s" % identifier)
+
 #: The singleton input manager instance.
 #: @type: L{InputManager}
 manager = None

diff --git a/source/keyLabels.py b/source/keyLabels.py
index 082c35b..3675259 100644
--- a/source/keyLabels.py
+++ b/source/keyLabels.py
@@ -6,45 +6,45 @@
 
 localizedKeyLabels = {
        # Translators: This is the name of the back key found on multimedia 
keyboards for controlling the web-browser.
-       'browserBack': _("back"),
+       'browserback': _("back"),
        # Translators: This is the name of the forward key found on multimedia 
keyboards for controlling the web-browser.
-       'browserForward': _("forward"),
+       'browserforward': _("forward"),
        # Translators: This is the name of the refresh key found on multimedia 
keyboards for controlling the web-browser.
-       'browserRefresh': _("refresh"),
+       'browserrefresh': _("refresh"),
        # Translators: This is the name of the stop key found on multimedia 
keyboards for controlling the web-browser.
-       'browserStop': _("browser stop"),
+       'browserstop': _("browser stop"),
        # Translators: This is the name of the back key found on multimedia 
keyboards to goto the search page of the web-browser.
-       'browserSearch': _("search page"),
+       'browsersearch': _("search page"),
        # Translators: This is the name of the favorites key found on 
multimedia keyboards to open favorites in the web-browser.
-       'browserFavorites': _("favorites"),
+       'browserfavorites': _("favorites"),
        # Translators: This is the name of the home key found on multimedia 
keyboards to goto the home page in the web-browser.
-       'browserHome': _("home page"),
+       'browserhome': _("home page"),
        # Translators: This is the name of the mute key found on multimedia 
keyboards to control playback volume.
-       'volumeMute': _("mute"),
+       'volumemute': _("mute"),
        # Translators: This is the name of the volume down key found on 
multimedia keyboards to reduce playback volume.
-       'volumeDown': _("volume down"),
+       'volumedown': _("volume down"),
        # Translators: This is the name of the volume up key found on 
multimedia keyboards to increase playback volume.
-       'volumeUp': _("volume up"),
+       'volumeup': _("volume up"),
        # Translators: This is the name of the next track key found on 
multimedia keyboards to skip to next track in the mediaplayer.
-       'mediaNextTrack': _("next track"),
+       'medianexttrack': _("next track"),
        # Translators: This is the name of the next track key found on 
multimedia keyboards to skip to next track in the mediaplayer.
-       'mediaPrevTrack': _("previous track"),
+       'mediaprevtrack': _("previous track"),
        # Translators: This is the name of the stop key found on multimedia 
keyboards to stop the current playing track in the mediaplayer.
-       'mediaStop': _("stop"),
+       'mediastop': _("stop"),
        # Translators: This is the name of the play/pause key found on 
multimedia keyboards to play/pause the current playing track in the mediaplayer.
-       'mediaPlayPause': _("play pause"),
+       'mediaplaypause': _("play pause"),
        # Translators: This is the name of the launch email key found on 
multimedia keyboards to open an email client.
-       'launchMail': _("email"),
+       'launchmail': _("email"),
        # Translators: This is the name of the launch mediaplayer key found on 
multimedia keyboards to launch the mediaplayer.
-       'launchMediaPlayer': _("media player"),
+       'launchmediaplayer': _("media player"),
        # Translators: This is the name of the launch custom application 1 key 
found on multimedia keyboards to launch a user-defined application.
-       'launchApp1': _("custom application 1"),
+       'launchapp1': _("custom application 1"),
        # Translators: This is the name of the launch custom application 2 key 
found on multimedia keyboards to launch a user-defined application.
-       'launchApp2': _("custom application 2"),
+       'launchapp2': _("custom application 2"),
        # Translators: This is the name of a key on the keyboard.
        'backspace': _("backspace"),
        # Translators: This is the name of a key on the keyboard.
-       'capsLock': _("caps lock"),
+       'capslock': _("caps lock"),
        # Translators: This is the name of a key on the keyboard.
        'control': _("ctrl"),
        # Translators: This is the name of a key on the keyboard.
@@ -56,15 +56,15 @@ localizedKeyLabels = {
        # Translators: This is the name of a key on the keyboard.
        'enter': _("enter"),
        # Translators: This is the name of a key on the keyboard.
-       'numpadEnter': _("numpad enter"),
+       'numpadenter': _("numpad enter"),
        # Translators: This is the name of a key on the keyboard.
        'escape': _("escape"),
        # Translators: This is the name of a key on the keyboard.
        'space': _("space"),
        # Translators: This is the name of a key on the keyboard.
-       'pageUp': _("page up"),
+       'pageup': _("page up"),
        # Translators: This is the name of a key on the keyboard.
-       'pageDown': _("page down"),
+       'pagedown': _("page down"),
        # Translators: This is the name of a key on the keyboard.
        'end': _("end"),
        # Translators: This is the name of a key on the keyboard.
@@ -72,23 +72,23 @@ localizedKeyLabels = {
        # Translators: This is the name of a key on the keyboard.
        'delete': _("delete"),
        # Translators: This is the name of a key on the keyboard.
-       'numpadDelete': _("numpad delete"),
+       'numpaddelete': _("numpad delete"),
        # Translators: This is the name of a key on the keyboard.
-       'leftArrow': _("left arrow"),
+       'leftarrow': _("left arrow"),
        # Translators: This is the name of a key on the keyboard.
-       'rightArrow': _("right arrow"),
+       'rightarrow': _("right arrow"),
        # Translators: This is the name of a key on the keyboard.
-       'upArrow': _("up arrow"),
+       'uparrow': _("up arrow"),
        # Translators: This is the name of a key on the keyboard.
-       'downArrow': _("down arrow"),
+       'downarrow': _("down arrow"),
        # Translators: This is the name of a key on the keyboard.
        'applications': _("applications"),
        # Translators: This is the name of a key on the keyboard.
-       'numLock': _("num lock"),
+       'numlock': _("num lock"),
        # Translators: This is the name of a key on the keyboard.
-       'printScreen': _("print screen"),
+       'printscreen': _("print screen"),
        # Translators: This is the name of a key on the keyboard.
-       'scrollLock': _("scroll lock"),
+       'scrolllock': _("scroll lock"),
        # Translators: This is the name of a key on the keyboard.
        'numpad4': _("numpad 4"),
        # Translators: This is the name of a key on the keyboard.
@@ -108,29 +108,29 @@ localizedKeyLabels = {
        # Translators: This is the name of a key on the keyboard.
        'numpad5': _("numpad 5"),
        # Translators: This is the name of a key on the keyboard.
-       'numpadDivide': _("numpad divide"),
+       'numpaddivide': _("numpad divide"),
        # Translators: This is the name of a key on the keyboard.
-       'numpadMultiply': _("numpad multiply"),
+       'numpadmultiply': _("numpad multiply"),
        # Translators: This is the name of a key on the keyboard.
-       'numpadMinus': _("numpad minus"),
+       'numpadminus': _("numpad minus"),
        # Translators: This is the name of a key on the keyboard.
-       'numpadPlus': _("numpad plus"),
+       'numpadplus': _("numpad plus"),
        # Translators: This is the name of a key on the keyboard.
-       'leftControl': _("left control"),
+       'leftcontrol': _("left control"),
        # Translators: This is the name of a key on the keyboard.
-       'rightControl': _("right control"),
+       'rightcontrol': _("right control"),
        # Translators: This is the name of a key on the keyboard.
-       'leftWindows': _("left windows"),
+       'leftwindows': _("left windows"),
        # Translators: This is the name of a key on the keyboard.
-       'leftShift': _("left shift"),
+       'leftshift': _("left shift"),
        # Translators: This is the name of a key on the keyboard.
-       'rightShift': _("right shift"),
+       'rightshift': _("right shift"),
        # Translators: This is the name of a key on the keyboard.
-       'leftAlt': _("left alt"),
+       'leftalt': _("left alt"),
        # Translators: This is the name of a key on the keyboard.
-       'rightAlt': _("right alt"),
+       'rightalt': _("right alt"),
        # Translators: This is the name of a key on the keyboard.
-       'rightWindows': _("right windows"),
+       'rightwindows': _("right windows"),
        # Translators: This is the name of a key on the keyboard.
        'break': _("break"),
        # Translators: This is the name of a key on the keyboard.

diff --git a/source/keyboardHandler.py b/source/keyboardHandler.py
index ed7090b..139eed2 100644
--- a/source/keyboardHandler.py
+++ b/source/keyboardHandler.py
@@ -8,6 +8,7 @@
 """Keyboard support"""
 
 import time
+import re
 import wx
 import winUser
 import vkCodes
@@ -252,12 +253,16 @@ class KeyboardInputGesture(inputCore.InputGesture):
        NORMAL_MODIFIER_KEYS = {
                winUser.VK_LCONTROL: winUser.VK_CONTROL,
                winUser.VK_RCONTROL: winUser.VK_CONTROL,
+               winUser.VK_CONTROL: None,
                winUser.VK_LSHIFT: winUser.VK_SHIFT,
                winUser.VK_RSHIFT: winUser.VK_SHIFT,
+               winUser.VK_SHIFT: None,
                winUser.VK_LMENU: winUser.VK_MENU,
                winUser.VK_RMENU: winUser.VK_MENU,
+               winUser.VK_MENU: None,
                winUser.VK_LWIN: VK_WIN,
                winUser.VK_RWIN: VK_WIN,
+               VK_WIN: None,
        }
 
        #: All possible toggle key vk codes.
@@ -352,7 +357,7 @@ class KeyboardInputGesture(inputCore.InputGesture):
                        # Translators: Reported for an unknown key press.
                        # %s will be replaced with the key code.
                        _("unknown %s") % key[8:] if key.startswith("unknown_")
-                       else localizedKeyLabels.get(key, key) for key in 
self._keyNamesInDisplayOrder)
+                       else localizedKeyLabels.get(key.lower(), key) for key 
in self._keyNamesInDisplayOrder)
 
        def _get_identifiers(self):
                keyNames = set(self.modifierNames)
@@ -407,7 +412,7 @@ class KeyboardInputGesture(inputCore.InputGesture):
                toggleState = winUser.getKeyState(self.vkCode) & 1
                key = self.mainKeyName
                ui.message(u"{key} {state}".format(
-                       key=localizedKeyLabels.get(key, key),
+                       key=localizedKeyLabels.get(key.lower(), key),
                        state=_("on") if toggleState else _("off")))
 
        def send(self):
@@ -481,3 +486,45 @@ class KeyboardInputGesture(inputCore.InputGesture):
                        raise ValueError
 
                return cls(keys[:-1], vk, 0, ext)
+
+       RE_IDENTIFIER = re.compile(r"^kb(?:\((.+?)\))?:(.*)$")
+       @classmethod
+       def getDisplayTextForIdentifier(cls, identifier):
+               layout, keys = cls.RE_IDENTIFIER.match(identifier).groups()
+               dispSource = None
+               if layout:
+                       try:
+                               # Translators: Used when describing keys on the 
system keyboard with a particular layout.
+                               # %s is replaced with the layout name.
+                               # For example, in English, this might produce 
"laptop keyboard".
+                               dispSource = _("%s keyboard") % 
cls.LAYOUTS[layout]
+                       except KeyError:
+                               pass
+               if not dispSource:
+                       # Translators: Used when describing keys on the system 
keyboard applying to all layouts.
+                       dispSource = _("keyboard, all layouts")
+
+               keys = set(keys.split("+"))
+               names = []
+               main = None
+               try:
+                       # If present, the NVDA key should appear first.
+                       keys.remove("nvda")
+                       names.append("NVDA")
+               except KeyError:
+                       pass
+               for key in keys:
+                       label = localizedKeyLabels.get(key, key)
+                       vk = vkCodes.byName.get(key)
+                       # vkCodes.byName values are (vk, ext)
+                       if vk:
+                               vk = vk[0]
+                       if vk in cls.NORMAL_MODIFIER_KEYS:
+                               names.append(label)
+                       else:
+                               # The main key must be last, so handle that 
outside the loop.
+                               main = label
+               names.append(main)
+               return dispSource, "+".join(names)
+
+inputCore.registerGestureSource("kb", KeyboardInputGesture)

diff --git a/user_docs/en/userGuide.t2t b/user_docs/en/userGuide.t2t
index 73fdc5f..d07d9ec 100644
--- a/user_docs/en/userGuide.t2t
+++ b/user_docs/en/userGuide.t2t
@@ -1057,6 +1057,26 @@ Using the Level field, you can adjust the lowest symbol 
level at which this symb
 
 When you are finished, press the OK button to save your changes or the Cancel 
button to discard them.
 
++++ Input Gestures (NVDA+control+i) +++
+In this dialog, you can customize the input gestures (keys on the keyboard, 
buttons on a braille display, etc.) for NVDA commands.
+
+Only commands that are applicable immediately before the dialog is opened are 
shown.
+Therefore, you should generally open this dialog using its key command so that 
all commands applicable to what you were just doing are shown.
+For example, if you want to customize commands related to browse mode, you 
should press the key command to open the Input Gestures dialog while you are in 
browse mode.
+
+The tree in this dialog lists all of the applicable NVDA commands grouped by 
category.
+Any gestures associated with a command are listed beneath the command.
+
+To add an input gesture to a command, select the command and press the Add 
button.
+Then, perform the input gesture you wish to associate; e.g. press a key on the 
keyboard or a button on a braille display.
+Often, a gesture can be interpreted in more than one way.
+For example, if you pressed a key on the keyboard, you may wish it to be 
specific to the current keyboard layout (e.g. desktop or laptop) or you may 
wish it to apply for all layouts.
+In this case, a menu will appear allowing you to select the desired option.
+
+To remove a gesture from a command, select the gesture and press the Remove 
button.
+
+When you are finished making changes, press the OK button to save them or the 
Cancel button to discard them.
+
 ++ Saving and Reloading the configuration ++
 By default NVDA will automatically save your settings on exit.
 Note, however, that this option can be changed under the general options in 
the preferences menu.
@@ -1814,79 +1834,6 @@ Please see the [BRLTTY key tables documentation 
http://mielke.cc/brltty/doc/driv
 
 + Advanced Topics +
 
-++ Remapping Key Assignments and Other Input Gestures ++
-Users are able to provide or override mappings of input gestures (such as key 
presses) to scripts in a special file in the user's NVDA configuration 
directory.
-This file is called gestures.ini.
-
-This file uses standard ini syntax.
-The file may contain multiple sections and each section may have one or more 
entries.
-
-Each section provides mappings for scripts in a particular Python module and 
class inside NVDA.
-- The section name should be the Python module and class separated by a dot 
(.).
-- The key of each entry is the name of the script to which input gestures 
should be bound.
-Alternatively, you can use None to unbind input gestures from a script to 
which they were previously bound.
-Each entry key can only be listed once per section, including None.
-- The entry value is a comma (,) separated list of gesture identifiers for the 
input gestures that should be bound.
-Gesture identifiers ending in a comma must be enclosed in quotes (" or ').
--
-
-Gesture identifiers consist of a two letter device code; a sub-device, layout 
or mode in brackets; a colon; and then a type-specific string identifying the 
actual input, such as key or touch gesture names.
-- For keyboard gestures, the device code is kb.
-The part in brackets is the keyboard layout and is optional.
-If not specified, the gesture will apply to all keyboard layouts.
-The string after the colon is one or more key names separated by a plus (+) 
sign.
-- For braille display gestures, the device code is br.
-The part in brackets identifies the specific braille display and is mandatory.
-The string after the colon is one or more key names separated by a plus (+) 
sign.
-- For touch gestures, the device code is ts.
-The part in brackets is the touch mode and is optional.
-The string after the colon is a touch gesture; e.g. double_tap, 2fingerflickUp 
or 3finger_tap.
--
-
-In order to discover gesture identifiers, script names and the class and 
module in which they are contained, you can:
-+ Turn on Input Help.
-+ Activate the gesture (press the key, touch the screen, etc.).
-+ Turn off input help.
-+ Activate View log in the NVDA Tools menu.
-+ Examine the recent log entries.
-One of these should provide information about the input gesture you sent, 
including the module.class and script if it is bound to one.
-+
-
-Following is an example of how you could bind NVDA+shift+t to the date time 
script.
-
-To find out the correct script name and module.class for date time, you would 
turn on Input Help and press NVDA+f12 (as this is the current gesture for the 
date time script).
-You would then turn off Input Help and examine the log viewer.
-
-Towards the bottom, you would see:
-
-```
-INFO - inputCore.InputManager._handleInputHelp (13:17:22):
-Input help: gesture kb(desktop):NVDA+f12, bound to script dateTime on 
globalCommands.GlobalCommands
-```
-
-From this, you can see that the script name is dateTime and the module.class 
is globalCommands.GlobalCommands.
-
-If the file does not yet exist, you would create a text file called 
gestures.ini in the user configuration directory and add the following content:
-
-```
-[globalCommands.GlobalCommands]
-       dateTime = kb:NVDA+shift+t
-```
-
-This would bind the key press NVDA+shift+t (in any keyboard layout) to the 
dateTime script.
-
-Note that the original NVDA+f12 binding would still work.
-If you wanted to remove this binding, you would add the following line:
-
-```
-       None = kb:NVDA+f12
-```
-
-Although you are free to have scripts bound to any available key, it may be 
problematic to use the alt key on the keyboard.
-NVDA still sends modifier keys (such as shift, control and alt) to the 
Operating System, even if they eventuate in a script.
-Thus, if you do use alt in a gesture, pressing this key combination may 
activate the menu bar, as well as executing the script.
-Therefore, it is probably best to just use Shift, control and the NVDA 
modifier key as modifiers.
-
 ++ Advanced Customization of Symbol Pronunciation ++
 It is possible to customize the pronunciation of punctuation and other symbols 
beyond what can be done using the [Punctuation/symbol pronunciation 
#SymbolPronunciation] dialog.
 For example, you can specify whether the raw symbol should be sent to the 
synthesizer (e.g. to cause a pause or change in inflection) and you can add 
custom symbols.


https://bitbucket.org/nvdaaddonteam/nvda/commits/3e2585ace020/
Changeset:   3e2585ace020
Branch:      None
User:        jteh
Date:        2013-09-09 11:39:04
Summary:     ProfilesDialog: Rename (none) to (normal configuration) for 
clarity. Also fix wording concerning this in the User Guide.

Affected #:  2 files

diff --git a/source/gui/configProfiles.py b/source/gui/configProfiles.py
index cec5aa2..e23ebc9 100644
--- a/source/gui/configProfiles.py
+++ b/source/gui/configProfiles.py
@@ -33,9 +33,9 @@ class ProfilesDialog(wx.Dialog):
                sizer = wx.BoxSizer(wx.HORIZONTAL)
                # Translators: The label of the profile list in the 
Configuration Profiles dialog.
                sizer.Add(wx.StaticText(self, label=_("&Profile")))
-               # Translators: Indicates that no configuration profile is 
selected.
-               # In this case, the user's normal configuration will be used.
-               profiles = [_("(none)")]
+               # Translators: The item to select the user's normal 
configuration
+               # in the profile list in the Configuration Profiles dialog.
+               profiles = [_("(normal configuration)")]
                profiles.extend(config.conf.listProfiles())
                item = self.profileList = wx.Choice(self, choices=profiles)
                item.Bind(wx.EVT_CHOICE, self.onProfileListChoice)

diff --git a/user_docs/en/userGuide.t2t b/user_docs/en/userGuide.t2t
index 9f70b08..276e262 100644
--- a/user_docs/en/userGuide.t2t
+++ b/user_docs/en/userGuide.t2t
@@ -1092,7 +1092,7 @@ You can manually activate the selected profile by 
pressing the Activate button.
 Once a profile is manually activated, any settings in that profile override 
the settings in your normal configuration.
 Also, any settings you change will be saved in that profile.
 
-To return to your normal configuration, select "(none)" in the profile list 
and activate it.
+To return to your normal configuration, select "(normal configuration)" in the 
profile list and press the Activate button.
 
 You can create a new profile by pressing the New button.
 To rename or delete a profile, press the Rename or Delete buttons, 
respectively.


https://bitbucket.org/nvdaaddonteam/nvda/commits/22da85a3c9a3/
Changeset:   22da85a3c9a3
Branch:      None
User:        jteh
Date:        2013-09-09 12:11:02
Summary:     ProfilesDialog: Rename Activate button to Activate/edit to make it 
clearer that this means you will also be editing the profile.

Affected #:  2 files

diff --git a/source/gui/configProfiles.py b/source/gui/configProfiles.py
index e23ebc9..41c888b 100644
--- a/source/gui/configProfiles.py
+++ b/source/gui/configProfiles.py
@@ -48,8 +48,8 @@ class ProfilesDialog(wx.Dialog):
                mainSizer.Add(sizer)
 
                sizer = wx.BoxSizer(wx.HORIZONTAL)
-               # Translators: The label of a button to activate the selected 
profile.
-               item = wx.Button(self, label=_("&Activate"))
+               # Translators: The label of a button to activate (and therefore 
also edit) the selected profile.
+               item = wx.Button(self, label=_("&Activate/edit"))
                item.Bind(wx.EVT_BUTTON, self.onActivate)
                sizer.Add(item)
                self.AffirmativeId = item.Id

diff --git a/user_docs/en/userGuide.t2t b/user_docs/en/userGuide.t2t
index 276e262..5d6eb14 100644
--- a/user_docs/en/userGuide.t2t
+++ b/user_docs/en/userGuide.t2t
@@ -1088,11 +1088,11 @@ You can also do this using a key command:
 
 The first control in this dialog is the profile list from which you can select 
one of the available profiles.
 When you open the dialog, the last manually activated profile is selected.
-You can manually activate the selected profile by pressing the Activate button.
+You can manually activate the selected profile by pressing the Activate/edit 
button.
 Once a profile is manually activated, any settings in that profile override 
the settings in your normal configuration.
 Also, any settings you change will be saved in that profile.
 
-To return to your normal configuration, select "(normal configuration)" in the 
profile list and press the Activate button.
+To return to your normal configuration, select "(normal configuration)" in the 
profile list and press the Activate/edit button.
 
 You can create a new profile by pressing the New button.
 To rename or delete a profile, press the Rename or Delete buttons, 
respectively.


https://bitbucket.org/nvdaaddonteam/nvda/commits/4ea8af1cfb6a/
Changeset:   4ea8af1cfb6a
Branch:      None
User:        jteh
Date:        2013-09-09 15:33:02
Summary:     ProfilesDialog: When a new profile is created and thus selected, 
make sure the Triggers, Rename and Delete buttons always get enabled.

Previously, they weren't enabled if (normal configuration) was selected before 
creating the new profile.

Affected #:  1 file

diff --git a/source/gui/configProfiles.py b/source/gui/configProfiles.py
index 41c888b..3006ec1 100644
--- a/source/gui/configProfiles.py
+++ b/source/gui/configProfiles.py
@@ -129,6 +129,7 @@ class ProfilesDialog(wx.Dialog):
                        return
                self.profileList.Append(name)
                self.profileList.Selection = self.profileList.Count - 1
+               self.onProfileListChoice(None)
                self.profileList.SetFocus()
 
        def onDelete(self, evt):


https://bitbucket.org/nvdaaddonteam/nvda/commits/fd48169ab214/
Changeset:   fd48169ab214
Branch:      None
User:        jteh
Date:        2013-09-10 04:48:08
Summary:     TriggersDialog: Don't disable the say all option if it is already 
associated with another profile. Instead, warn the user if they enable it that 
it will be removed from the other profile.

Affected #:  2 files

diff --git a/source/gui/configProfiles.py b/source/gui/configProfiles.py
index 3006ec1..4f000ac 100644
--- a/source/gui/configProfiles.py
+++ b/source/gui/configProfiles.py
@@ -232,9 +232,6 @@ class TriggersDialog(wx.Dialog):
                item = self.sayAllToggle = wx.CheckBox(self, label=_("&Say 
all"))
                if "sayAll" in triggers:
                        item.Value = True
-               elif "sayAll" in config.conf["profileTriggers"]:
-                       # This trigger is associated with another profile 
already.
-                       item.Disable()
                mainSizer.Add(item)
 
                item = wx.Button(self, wx.ID_CLOSE, label=_("&Close"))
@@ -248,13 +245,23 @@ class TriggersDialog(wx.Dialog):
 
        def onClose(self, evt):
                triggers = config.conf["profileTriggers"]
+               try:
+                       trigOnOther = triggers["sayAll"] != self.profile
+               except KeyError:
+                       trigOnOther = False
                if self.sayAllToggle.Value:
+                       if trigOnOther and gui.messageBox(
+                               # Translators: A confirmation prompt that might 
be displayed when closing the configuration profile triggers dialog.
+                               _("Say all is already associated with another 
profile.\n"
+                                       "If you continue, it will be removed 
from the other profile and associated with this one.\n"
+                                       "Are you sure you want to continue?"),
+                               # Translators: The title of a confirmation 
prompt.
+                               _("Confirm"), wx.YES | wx.NO | wx.ICON_WARNING, 
self
+                       ) == wx.NO:
+                               return
                        triggers["sayAll"] = self.profile
-               elif self.sayAllToggle.Enabled:
-                       try:
-                               del triggers["sayAll"]
-                       except KeyError:
-                               pass
+               elif not trigOnOther:
+                       del triggers["sayAll"]
 
                self.Parent.Enable()
                self.Destroy()

diff --git a/user_docs/en/userGuide.t2t b/user_docs/en/userGuide.t2t
index 5d6eb14..78d8130 100644
--- a/user_docs/en/userGuide.t2t
+++ b/user_docs/en/userGuide.t2t
@@ -1104,7 +1104,6 @@ Pressing the Triggers button in the Configuration 
Profiles dialog allows you to
 If there is a manually activated profile, other profiles can still be 
activated due to triggers, but any settings in the manually activated profile 
will override them.
 
 A specific trigger can only be associated with a single profile, but a single 
profile can be associated with several triggers.
-If a trigger is already associated with another profile, its option won't be 
available.
 
 The Applications grouping allows you to specify applications that should 
trigger the profile when you switch to them.
 Applications are specified by the name of their executable file (without the 
extension), which might be different to the actual name of the application.


https://bitbucket.org/nvdaaddonteam/nvda/commits/a930d33f8f14/
Changeset:   a930d33f8f14
Branch:      None
User:        jteh
Date:        2013-09-10 07:42:49
Summary:     Refactor app module lose/gain focus behaviour.

Previously, app module lose/gainFocus events and triggers occurred in an 
indeterminate order. Now, they occur ordered from the outermost app module for 
gain focus and the innermost app module for lose focus.
Also, all trigger exits and enters occur between the loseFocus and gainFocus 
events. This means that they can be optimised so that the profile switch is 
only handled once, which may be a lot faster because it can eliminate redundant 
synth/braille display switching.
ConfigManager.atomicProfileSwitch was added to facilitate the switching 
optimisation.

Affected #:  3 files

diff --git a/source/api.py b/source/api.py
index 31ea63d..1c4285d 100644
--- a/source/api.py
+++ b/source/api.py
@@ -66,7 +66,7 @@ Before overriding the last object, this function calls 
event_loseFocus on the ob
        #add the old focus to the old focus ancestors, but only if its not None 
(is none at NVDA initialization)
        if globalVars.focusObject: 
                oldFocusLine.append(globalVars.focusObject)
-       oldAppModuleSet=set(o.appModule for o in oldFocusLine if o and 
o.appModule)
+       oldAppModules=[o.appModule for o in oldFocusLine if o and o.appModule]
        ancestors=[]
        tempObj=obj
        matchedOld=False
@@ -106,13 +106,10 @@ Before overriding the last object, this function calls 
event_loseFocus on the ob
                container=tempObj.container
                tempObj.container=container # Cache the parent.
                tempObj=container
+       newAppModules=[o.appModule for o in ancestors if o and o.appModule]
        #Remove the final new ancestor as this will be the new focus object
        del ancestors[-1]
-       newAppModuleSet=set(o.appModule for o in ancestors+[obj] if o and 
o.appModule)
-       for removedMod in oldAppModuleSet-newAppModuleSet:
-               appModuleHandler.handleLoseFocus(removedMod)
-       for addedMod in newAppModuleSet-oldAppModuleSet:
-               appModuleHandler.handleGainFocus(addedMod)
+       appModuleHandler.handleAppSwitch(oldAppModules,newAppModules)
        try:
                treeInterceptorHandler.cleanup()
        except watchdog.CallCancelled:

diff --git a/source/appModuleHandler.py b/source/appModuleHandler.py
index 1efaeae..ed73446 100644
--- a/source/appModuleHandler.py
+++ b/source/appModuleHandler.py
@@ -177,20 +177,51 @@ def terminate():
                        log.exception("Error terminating app module %r" % app)
        runningTable.clear()
 
-def handleLoseFocus(mod):
-       if not mod.sleepMode and hasattr(mod,'event_appModule_loseFocus'):
-               try:
-                       mod.event_appModule_loseFocus()
-               except watchdog.CallCancelled:
-                       pass
-       mod._configProfileTrigger.exit()
-       mod._configProfileTrigger = None
+def handleAppSwitch(oldMods, newMods):
+       newModsSet = set(newMods)
+       processed = set()
+       nextStage = []
+
+       # Determine all apps that are losing focus and fire appropriate events.
+       for mod in reversed(oldMods):
+               if mod in processed:
+                       # This app has already been handled.
+                       continue
+               processed.add(mod)
+               if mod in newModsSet:
+                       # This app isn't losing focus.
+                       continue
+               processed.add(mod)
+               # This app is losing focus.
+               nextStage.append(mod)
+               if not mod.sleepMode and 
hasattr(mod,'event_appModule_loseFocus'):
+                       try:
+                               mod.event_appModule_loseFocus()
+                       except watchdog.CallCancelled:
+                               pass
+
+       with config.conf.atomicProfileSwitch():
+               # Exit triggers for apps that lost focus.
+               for mod in nextStage:
+                       mod._configProfileTrigger.exit()
+                       mod._configProfileTrigger = None
+
+               nextStage = []
+               # Determine all apps that are gaining focus and enter triggers.
+               for mod in newMods:
+                       if mod in processed:
+                               # This app isn't gaining focus or it has 
already been handled.
+                               continue
+                       processed.add(mod)
+                       # This app is gaining focus.
+                       nextStage.append(mod)
+                       trigger = mod._configProfileTrigger = 
AppProfileTrigger(mod.appName)
+                       trigger.enter()
 
-def handleGainFocus(mod):
-       trigger = mod._configProfileTrigger = AppProfileTrigger(mod.appName)
-       trigger.enter()
-       if not mod.sleepMode and hasattr(mod,'event_appModule_gainFocus'):
-               mod.event_appModule_gainFocus()
+       # Fire appropriate events for apps gaining focus.
+       for mod in nextStage:
+               if not mod.sleepMode and 
hasattr(mod,'event_appModule_gainFocus'):
+                       mod.event_appModule_gainFocus()
 
 #base class for appModules
 class AppModule(baseObject.ScriptableObject):

diff --git a/source/config/__init__.py b/source/config/__init__.py
index b78f135..d4e335e 100644
--- a/source/config/__init__.py
+++ b/source/config/__init__.py
@@ -9,6 +9,7 @@ import os
 import sys
 from cStringIO import StringIO
 import itertools
+import contextlib
 from configobj import ConfigObj, ConfigObjError
 from validate import Validator
 from logHandler import log
@@ -408,11 +409,14 @@ class ConfigManager(object):
                self.editProfileIndex = None
                self.validator = Validator()
                self.rootSection = None
+               self._shouldHandleProfileSwitch = True
                self._initBaseConf()
                #: The names of all profiles that have been modified since they 
were last saved.
                self._dirtyProfiles = set()
 
        def _handleProfileSwitch(self):
+               if not self._shouldHandleProfileSwitch:
+                       return
                init = self.rootSection is None
                # Reset the cache.
                self.rootSection = AggregatedSection(self, (), self.spec, 
self.profiles)
@@ -675,6 +679,22 @@ class ConfigManager(object):
                self.profiles.remove(profile)
                self._handleProfileSwitch()
 
+       @contextlib.contextmanager
+       def atomicProfileSwitch(self):
+               """Indicate that multiple profile switches should be treated as 
one.
+               This is useful when multiple triggers may be exited/entered at 
once;
+               e.g. when switching applications.
+               While multiple switches aren't harmful, they might take longer;
+               e.g. unnecessarily switching speech synthesizers or braille 
displays.
+               This is a context manager to be used with the C{with} statement.
+               """
+               self._shouldHandleProfileSwitch = False
+               try:
+                       yield
+               finally:
+                       self._shouldHandleProfileSwitch = True
+                       self._handleProfileSwitch()
+
 class AggregatedSection(object):
        """A view of a section of configuration which aggregates settings from 
all active profiles.
        """


https://bitbucket.org/nvdaaddonteam/nvda/commits/1abb8b9365b1/
Changeset:   1abb8b9365b1
Branch:      None
User:        jteh
Date:        2013-09-11 08:01:58
Summary:     Fix severe truncation of speech (even up to several lines) with 
say all when a profile using a different synth is triggered.

Say all needs to wait for the synth to actually finish speaking before it exits 
the trigger.

Affected #:  1 file

diff --git a/source/sayAllHandler.py b/source/sayAllHandler.py
index f26321e..1a3043f 100644
--- a/source/sayAllHandler.py
+++ b/source/sayAllHandler.py
@@ -146,7 +146,7 @@ def readTextHelper_generator(cursor):
                                        if cursor!=CURSOR_CARET or 
config.conf["reviewCursor"]["followCaret"]:
                                                api.setReviewPosition(updater)
                        elif not keepReading and 
lastReceivedIndex==lastSentIndex:
-                               # All text has been spoken.
+                               # All text has been sent to the synth.
                                # Turn the page and start again if the object 
supports it.
                                if 
isinstance(reader.obj,textInfos.DocumentWithPageTurns):
                                        try:
@@ -163,6 +163,20 @@ def readTextHelper_generator(cursor):
                                yield
                        yield
 
+               # Wait until the synth has actually finished speaking.
+               # Otherwise, if there is a triggered profile with a different 
synth,
+               # we will switch too early and truncate speech (even up to 
several lines).
+               # Send another index and wait for it.
+               index=lastSentIndex+1
+               speech.speak([speech.IndexCommand(index)])
+               while speech.getLastSpeechIndex()<index:
+                       yield
+                       yield
+               # Some synths say they've handled the index slightly sooner 
than they actually have,
+               # so wait a bit longer.
+               for i in xrange(30):
+                       yield
+
 class SayAllProfileTrigger(config.ProfileTrigger):
        """A configuration profile trigger for when say all is in progress.
        """


https://bitbucket.org/nvdaaddonteam/nvda/commits/eeadf3ca9de0/
Changeset:   eeadf3ca9de0
Branch:      None
User:        jteh
Date:        2013-09-12 03:21:12
Summary:     Fix an issue where nothing was spoken if a single key (e.g. an 
arrow key) was pressed during say all and say all used a different speech synth.

This occurred because the say all trigger was being exited after the text in 
response to the key was spoken instead of before. The reason was that a 
reference to the say all generator was being held longer than it should in 
queueHandler.pumpall, so Python couldn't destroy the generator until after the 
queue had been pumped.
Now, pumpAll only holds the reference as long as it needs to.

Affected #:  1 file

diff --git a/source/queueHandler.py b/source/queueHandler.py
index 4005245..0db66da 100644
--- a/source/queueHandler.py
+++ b/source/queueHandler.py
@@ -73,4 +73,6 @@ def pumpAll():
                except:
                        log.exception("error in generator %d"%ID)
                        del generators[ID]
+               # Lose our reference so Python can destroy the generator if 
appropriate.
+               del gen
        flushQueue(eventQueue)


https://bitbucket.org/nvdaaddonteam/nvda/commits/c9cdfbb81a34/
Changeset:   c9cdfbb81a34
Branch:      None
User:        mdcurran
Date:        2013-09-12 03:21:58
Summary:     Merge branch 'master' into next

Affected #:  1 file

diff --git a/user_docs/en/changes.t2t b/user_docs/en/changes.t2t
index b2e7147..43fa530 100644
--- a/user_docs/en/changes.t2t
+++ b/user_docs/en/changes.t2t
@@ -11,6 +11,7 @@
  - They can also be activated automatically due to triggers such as switching 
to a particular application.
  - See the User Guide for details.
 - Form fields are now reported in Microsoft word documents. (#2295)
+- NVDA can now announce revision information in Microsoft Word when Track 
Changes is enabled. Note that Report editor revisions in NVDA's document 
settings dialog (off by default) must be enabled also for them to be announced. 
(#1670)
 
 
 == Bug Fixes ==


https://bitbucket.org/nvdaaddonteam/nvda/commits/201a4ecf5a20/
Changeset:   201a4ecf5a20
Branch:      None
User:        jteh
Date:        2013-09-12 03:42:42
Summary:     Merge branch 'master' into t1532

Affected #:  9 files

diff --git a/nvdaHelper/remote/winword.cpp b/nvdaHelper/remote/winword.cpp
index f4ddbba..07ac388 100644
--- a/nvdaHelper/remote/winword.cpp
+++ b/nvdaHelper/remote/winword.cpp
@@ -36,6 +36,10 @@ using namespace std;
 #define wdDISPID_SELECTION_SETRANGE 100
 #define wdDISPID_SELECTION_STARTISACTIVE 404
 #define wdDISPID_RANGE_INRANGE 126
+#define wdDISPID_RANGE_DUPLICATE 6
+#define wdDISPID_RANGE_REVISIONS 150
+#define wdDISPID_REVISIONS_ITEM 0
+#define wdDISPID_REVISION_TYPE 4
 #define wdDISPID_RANGE_STORYTYPE 7
 #define wdDISPID_RANGE_MOVEEND 111
 #define wdDISPID_RANGE_COLLAPSE 101
@@ -154,6 +158,7 @@ using namespace std;
 #define formatConfig_reportComments 4096
 #define formatConfig_reportHeadings 8192
 #define formatConfig_reportLanguage 16384
+#define formatConfig_reportRevisions 32768
 
 #define formatConfig_fontFlags 
(formatConfig_reportFontName|formatConfig_reportFontSize|formatConfig_reportFontAttributes|formatConfig_reportColor)
 #define formatConfig_initialFormatFlags 
(formatConfig_reportPage|formatConfig_reportLineNumber|formatConfig_reportTables|formatConfig_reportHeadings)
@@ -288,6 +293,25 @@ int generateHeadingXML(IDispatch* pDispatchRange, 
wostringstream& XMLStream) {
        return 1;
 }
 
+int getRevisionType(IDispatch* pDispatchOrigRange) {
+       IDispatchPtr pDispatchRange=NULL;
+       //If range is not duplicated here, revisions collection represents 
revisions at the start of the range when it was first created
+       
if(_com_dispatch_raw_propget(pDispatchOrigRange,wdDISPID_RANGE_DUPLICATE,VT_DISPATCH,&pDispatchRange)!=S_OK||!pDispatchRange)
 {
+               return 0;
+       }
+       IDispatchPtr pDispatchRevisions=NULL;
+       
if(_com_dispatch_raw_propget(pDispatchRange,wdDISPID_RANGE_REVISIONS,VT_DISPATCH,&pDispatchRevisions)!=S_OK||!pDispatchRevisions)
 {
+               return 0;
+       }
+       IDispatchPtr pDispatchRevision=NULL;
+       
if(_com_dispatch_raw_method(pDispatchRevisions,wdDISPID_REVISIONS_ITEM,DISPATCH_METHOD,VT_DISPATCH,&pDispatchRevision,L"\x0003",1)!=S_OK||!pDispatchRevision)
 {
+               return 0;
+       }
+       long revisionType=0;
+       
_com_dispatch_raw_propget(pDispatchRevision,wdDISPID_REVISION_TYPE,VT_I4,&revisionType);
+       return revisionType;
+}
+
 int getHyperlinkCount(IDispatch* pDispatchRange) {
        IDispatchPtr pDispatchHyperlinks=NULL;
        int count=0;
@@ -441,6 +465,10 @@ void generateXMLAttribsForFormatting(IDispatch* 
pDispatchRange, int startOffset,
        
if((formatConfig&formatConfig_reportLinks)&&getHyperlinkCount(pDispatchRange)>0)
 {
                formatAttribsStream<<L"link=\"1\" ";
        }
+       if(formatConfig&formatConfig_reportRevisions) {
+               long revisionType=getRevisionType(pDispatchRange);
+               formatAttribsStream<<L"wdRevisionType=\""<<revisionType<<L"\" ";
+       }
        if(formatConfig&formatConfig_reportStyle) {
                IDispatchPtr pDispatchStyle=NULL;
                
if(_com_dispatch_raw_propget(pDispatchRange,wdDISPID_RANGE_STYLE,VT_DISPATCH,&pDispatchStyle)==S_OK&&pDispatchStyle)
 {
@@ -634,6 +662,7 @@ void winword_getTextInRange_helper(HWND hwnd, 
winword_getTextInRange_args* args)
        //Walk the range from the given start to end by characterFormatting or 
word units
        //And grab any text and formatting and generate appropriate xml
        do {
+               int curDisabledFormatConfig=0;
                //generated form field xml if in a form field
                //Also automatically extends the range and chunkEndOffset to 
the end of the field
                BOOL 
isFormField=generateFormFieldXML(pDispatchRange,XMLStream,chunkEndOffset);
@@ -666,6 +695,10 @@ void winword_getTextInRange_helper(HWND hwnd, 
winword_getTextInRange_args* args)
                                                noteCharOffset=i;
                                                if(i==0) text[i]=L' ';
                                                break;
+                                       }  else 
if(text[i]==L'\x0007'&&(chunkEndOffset-chunkStartOffset)==1) {
+                                               text[i]=L'\0';
+                                               //Collecting revision info does 
not work on cell delimiters
+                                               
curDisabledFormatConfig|=formatConfig_reportRevisions;
                                        }
                                }
                                isNoteChar=(noteCharOffset==0);
@@ -693,9 +726,9 @@ void winword_getTextInRange_helper(HWND hwnd, 
winword_getTextInRange_args* args)
                                }
                                
_com_dispatch_raw_propget(pDispatchRange,wdDISPID_RANGE_END,VT_I4,&chunkEndOffset);
                        }
-                       XMLStream<<L"<text ";
+                       XMLStream<<L"<text 
_startOffset=\""<<chunkStartOffset<<L"\" _endOffset=\""<<chunkEndOffset<<L"\" ";
                        XMLStream<<initialFormatAttribsStream.str();
-                       
generateXMLAttribsForFormatting(pDispatchRange,chunkStartOffset,chunkEndOffset,formatConfig,XMLStream);
+                       
generateXMLAttribsForFormatting(pDispatchRange,chunkStartOffset,chunkEndOffset,formatConfig&(~curDisabledFormatConfig),XMLStream);
                        XMLStream<<L">";
                        if(firstLoop) {
                                formatConfig&=(~formatConfig_reportLists);

diff --git a/source/NVDAObjects/window/winword.py 
b/source/NVDAObjects/window/winword.py
index 38ee650..9106ae6 100755
--- a/source/NVDAObjects/window/winword.py
+++ b/source/NVDAObjects/window/winword.py
@@ -95,6 +95,47 @@ wdContentControlDate=6
 wdContentControlGroup=7
 wdContentControlCheckBox=8
 
+wdNoRevision=0
+wdRevisionInsert=1
+wdRevisionDelete=2
+wdRevisionProperty=3
+wdRevisionParagraphNumber=4
+wdRevisionDisplayField=5
+wdRevisionReconcile=6
+wdRevisionConflict=7
+wdRevisionStyle=8
+wdRevisionReplace=9
+wdRevisionParagraphProperty=10
+wdRevisionTableProperty=11
+wdRevisionSectionProperty=12
+wdRevisionStyleDefinition=13
+wdRevisionMovedFrom=14
+wdRevisionMovedTo=15
+wdRevisionCellInsertion=16
+wdRevisionCellDeletion=17
+wdRevisionCellMerge=18
+
+wdRevisionTypeLabels={
+       wdRevisionInsert:_("insertion"),
+       wdRevisionDelete:_("deletion"),
+       wdRevisionProperty:_("property"),
+       wdRevisionParagraphNumber:_("paragraph number"),
+       wdRevisionDisplayField:_("display field"),
+       wdRevisionReconcile:_("reconcile"),
+       wdRevisionConflict:_("conflict"),
+       wdRevisionStyle:_("style"),
+       wdRevisionReplace:_("replace"),
+       wdRevisionParagraphProperty:_("paragraph property"),
+       wdRevisionTableProperty:_("table property"),
+       wdRevisionSectionProperty:_("section property"),
+       wdRevisionStyleDefinition:_("style definition"),
+       wdRevisionMovedFrom:_("moved from"),
+       wdRevisionMovedTo:_("moved to"),
+       wdRevisionCellInsertion:_("cell insertion"),
+       wdRevisionCellDeletion:_("cell deletion"),
+       wdRevisionCellMerge:_("cell merge"),
+}
+
 storyTypeLocalizedLabels={
        wdCommentsStory:_("Comments"),
        wdEndnotesStory:_("Endnotes"),
@@ -159,6 +200,7 @@ formatConfigFlagsMap={
        "reportComments":4096,
        "reportHeadings":8192,
        "autoLanguageSwitching":16384,  
+       "reportRevisions":32768,
 }
 
 class WordDocumentTextInfo(textInfos.TextInfo):
@@ -205,6 +247,7 @@ class WordDocumentTextInfo(textInfos.TextInfo):
                        raise NotImplementedError("position: %s"%position)
 
        def getTextWithFields(self,formatConfig=None):
+               extraDetail=formatConfig.get('extraDetail',False) if 
formatConfig else False
                if not formatConfig:
                        formatConfig=config.conf['documentFormatting']
                
formatConfig['autoLanguageSwitching']=config.conf['speech'].get('autoLanguageSwitching',False)
@@ -223,7 +266,7 @@ class WordDocumentTextInfo(textInfos.TextInfo):
                                if isinstance(field,textInfos.ControlField):
                                        
item.field=self._normalizeControlField(field)
                                elif isinstance(field,textInfos.FormatField):
-                                       
item.field=self._normalizeFormatField(field)
+                                       
item.field=self._normalizeFormatField(field,extraDetail=extraDetail)
                        elif index>0 and isinstance(item,basestring) and 
item.isspace():
                                 #2047: don't expose language for whitespace as 
its incorrect for east-asian languages 
                                lastItem=commandList[index-1]
@@ -288,7 +331,25 @@ class WordDocumentTextInfo(textInfos.TextInfo):
                                field['role']=controlTypes.ROLE_FRAME
                return field
 
-       def _normalizeFormatField(self,field):
+       def _normalizeFormatField(self,field,extraDetail=False):
+               _startOffset=int(field.pop('_startOffset'))
+               _endOffset=int(field.pop('_endOffset'))
+               revisionType=int(field.pop('wdRevisionType',0))
+               revisionLabel=wdRevisionTypeLabels.get(revisionType,None)
+               if revisionLabel:
+                       if extraDetail:
+                               try:
+                                       r=self.obj.WinwordSelectionObject.range
+                                       r.setRange(_startOffset,_endOffset)
+                                       rev=r.revisions.item(1)
+                                       author=rev.author
+                                       date=rev.date
+                               except COMError:
+                                       author=_("unknown author")
+                                       date=_("unknown date")
+                               field['revision']=_("{revisionType} by 
{revisionAuthor} on 
{revisionDate}").format(revisionType=revisionLabel,revisionAuthor=author,revisionDate=date)
+                       else:
+                               field['revision']=revisionLabel
                color=field.pop('color',None)
                if color is not None:
                        field['color']=colors.RGB.fromCOLORREF(int(color))      
        

diff --git a/source/config/__init__.py b/source/config/__init__.py
index 6bc5d65..5e0a4ee 100644
--- a/source/config/__init__.py
+++ b/source/config/__init__.py
@@ -144,6 +144,7 @@ confspec = ConfigObj(StringIO(
        reportFontName = boolean(default=false)
        reportFontSize = boolean(default=false)
        reportFontAttributes = boolean(default=false)
+       reportRevisions = boolean(default=false)
        reportColor = boolean(default=False)
        reportAlignment = boolean(default=false)
        reportStyle = boolean(default=false)

diff --git a/source/globalCommands.py b/source/globalCommands.py
index 4221545..eb5f3c2 100755
--- a/source/globalCommands.py
+++ b/source/globalCommands.py
@@ -833,7 +833,7 @@ class GlobalCommands(ScriptableObject):
        def script_reportFormatting(self,gesture):
                formatConfig={
                        "detectFormatAfterCursor":False,
-                       
"reportFontName":True,"reportFontSize":True,"reportFontAttributes":True,"reportColor":True,
+                       
"reportFontName":True,"reportFontSize":True,"reportFontAttributes":True,"reportColor":True,"reportRevisions":False,
                        
"reportStyle":True,"reportAlignment":True,"reportSpellingErrors":True,
                        
"reportPage":False,"reportLineNumber":False,"reportTables":False,
                        
"reportLinks":False,"reportHeadings":False,"reportLists":False,

diff --git a/source/gui/settingsDialogs.py b/source/gui/settingsDialogs.py
index d96fd29..3dadf3e 100644
--- a/source/gui/settingsDialogs.py
+++ b/source/gui/settingsDialogs.py
@@ -1011,6 +1011,11 @@ class DocumentFormattingDialog(SettingsDialog):
                settingsSizer.Add(self.colorCheckBox,border=10,flag=wx.BOTTOM)
                # Translators: This is the label for a checkbox in the
                # document formatting settings dialog.
+               
self.revisionsCheckBox=wx.CheckBox(self,wx.NewId(),label=_("Report &editor 
revisions"))
+               
self.revisionsCheckBox.SetValue(config.conf["documentFormatting"]["reportRevisions"])
+               
settingsSizer.Add(self.revisionsCheckBox,border=10,flag=wx.BOTTOM)
+               # Translators: This is the label for a checkbox in the
+               # document formatting settings dialog.
                self.styleCheckBox=wx.CheckBox(self,wx.NewId(),label=_("Report 
st&yle"))
                
self.styleCheckBox.SetValue(config.conf["documentFormatting"]["reportStyle"])
                settingsSizer.Add(self.styleCheckBox,border=10,flag=wx.BOTTOM)
@@ -1090,6 +1095,7 @@ class DocumentFormattingDialog(SettingsDialog):
                
config.conf["documentFormatting"]["reportFontSize"]=self.fontSizeCheckBox.IsChecked()
                
config.conf["documentFormatting"]["reportFontAttributes"]=self.fontAttrsCheckBox.IsChecked()
                
config.conf["documentFormatting"]["reportColor"]=self.colorCheckBox.IsChecked()
+               
config.conf["documentFormatting"]["reportRevisions"]=self.revisionsCheckBox.IsChecked()
                
config.conf["documentFormatting"]["reportAlignment"]=self.alignmentCheckBox.IsChecked()
                
config.conf["documentFormatting"]["reportStyle"]=self.styleCheckBox.IsChecked()
                
config.conf["documentFormatting"]["reportSpellingErrors"]=self.spellingErrorsCheckBox.IsChecked()

diff --git a/source/keyboardHandler.py b/source/keyboardHandler.py
index 20709ad..9780548 100644
--- a/source/keyboardHandler.py
+++ b/source/keyboardHandler.py
@@ -146,6 +146,13 @@ def 
internal_keyDownEvent(vkCode,scanCode,extended,injected):
                try:
                        inputCore.manager.executeGesture(gesture)
                        trappedKeys.add(keyCode)
+                       if 
canModifiersPerformAction(gesture.generalizedModifiers):
+                               # #3472: These modifiers can perform an action 
if pressed alone
+                               # and we've just consumed the main key.
+                               # Send special reserved vkcode (0xff) to at 
least notify the app's key state that something happendd.
+                               # This allows alt and windows to be bound to 
scripts and
+                               # stops control+shift from switching keyboard 
layouts in cursorManager selection scripts.
+                               KeyboardInputGesture((),0xff,0,False).send()
                        return False
                except inputCore.NoInputGestureAction:
                        if gesture.isNVDAModifierKey:
@@ -213,6 +220,30 @@ def getInputHkl():
                thread = 0
        return winUser.user32.GetKeyboardLayout(thread)
 
+def canModifiersPerformAction(modifiers):
+       """Determine whether given generalized modifiers can perform an action 
if pressed alone.
+       For example, alt activates the menu bar if it isn't modifying another 
key.
+       """
+       if inputCore.manager.isInputHelpActive:
+               return False
+       control = shift = other = False
+       for vk, ext in modifiers:
+               if vk in (winUser.VK_MENU, VK_WIN):
+                       # Alt activates the menu bar.
+                       # Windows activates the Start Menu.
+                       return True
+               elif vk == winUser.VK_CONTROL:
+                       control = True
+               elif vk == winUser.VK_SHIFT:
+                       shift = True
+               elif (vk, ext) not in trappedKeys :
+                       # Trapped modifiers aren't relevant.
+                       other = True
+       if control and shift and not other:
+               # Shift+control switches keyboard layouts.
+               return True
+       return False
+
 class KeyboardInputGesture(inputCore.InputGesture):
        """A key pressed on the traditional system keyboard.
        """

diff --git a/source/speech.py b/source/speech.py
index 9d8cf9f..8278fba 100755
--- a/source/speech.py
+++ b/source/speech.py
@@ -559,6 +559,9 @@ def 
speakTextInfo(info,useCache=True,formatConfig=None,unit=None,reason=controlT
        extraDetail=unit in (textInfos.UNIT_CHARACTER,textInfos.UNIT_WORD)
        if not formatConfig:
                formatConfig=config.conf["documentFormatting"]
+       if extraDetail:
+               formatConfig=formatConfig.copy()
+               formatConfig['extraDetail']=True
        reportIndentation=unit==textInfos.UNIT_LINE and 
formatConfig["reportLineIndentation"]
 
        speechSequence=[]
@@ -1060,6 +1063,15 @@ def 
getFormatFieldSpeech(attrs,attrsCache=None,formatConfig=None,unit=None,extra
                        # %s will be replaced with the line number.
                        text=_("line %s")%lineNumber
                        textList.append(text)
+       if  formatConfig["reportRevisions"]:
+               revision=attrs.get("revision")
+               oldRevision=attrsCache.get("revision") if attrsCache is not 
None else None
+               if (revision or oldRevision is not None) and 
revision!=oldRevision:
+                       # Translators: Reported when text is revised.
+                       text=(_("revised %s"%revision) if revision
+                               # Translators: Reported when text is not 
revised.
+                               else _("unrevised"))
+                       textList.append(text)
        if  formatConfig["reportFontAttributes"]:
                bold=attrs.get("bold")
                oldBold=attrsCache.get("bold") if attrsCache is not None else 
None

diff --git a/user_docs/en/changes.t2t b/user_docs/en/changes.t2t
index ce9ebdf..3188ffb 100644
--- a/user_docs/en/changes.t2t
+++ b/user_docs/en/changes.t2t
@@ -7,12 +7,15 @@
 
 == New Features ==
 - Form fields are now reported in Microsoft word documents. (#2295)
+- NVDA can now announce revision information in Microsoft Word when Track 
Changes is enabled. Note that Report editor revisions in NVDA's document 
settings dialog (off by default) must be enabled also for them to be announced. 
(#1670)
 
 
 == Bug Fixes ==
 - Zend Studio now functions the same as Eclipse. (#3420)
 - The changed state of certain checkboxes in the Microsoft Outlook 2010 
message rules dialog are now reported automatically. (#3063)
 - NVDA will now report the pinned state for pinned controls such as tabs in 
Mozilla Firefox. (#3372)
+- It is now possible to bind scripts to keyboard gestures containing Alt 
and/or Windows keys as modifiers. Previously, if this was done, performing the 
script would cause the Start Menu or menu bar to be activated. (#3472)
+- Selecting text in browse mode documents (e.g. using control+shift+end) no 
longer causes the keyboard layout to be switched on systems with multiple 
keyboard layouts installed. (#3472)
 
 
 = 2013.2 =

diff --git a/user_docs/en/userGuide.t2t b/user_docs/en/userGuide.t2t
index 94c5221..1ae185a 100644
--- a/user_docs/en/userGuide.t2t
+++ b/user_docs/en/userGuide.t2t
@@ -996,6 +996,7 @@ You can configure reporting of:
 - Font attributes
 - Text alignment
 - Colors
+- editor revisions
 - Text style
 - Spelling errors
 - Page numbers


https://bitbucket.org/nvdaaddonteam/nvda/commits/acbd6d40e551/
Changeset:   acbd6d40e551
Branch:      None
User:        jteh
Date:        2013-09-12 03:43:20
Summary:     Merge branch 't1532' into next. No changes to tt1532 specific 
code, just merged with master.

Affected #:  1 file

diff --git a/user_docs/en/changes.t2t b/user_docs/en/changes.t2t
index 43fa530..0bcb632 100644
--- a/user_docs/en/changes.t2t
+++ b/user_docs/en/changes.t2t
@@ -18,6 +18,8 @@
 - Zend Studio now functions the same as Eclipse. (#3420)
 - The changed state of certain checkboxes in the Microsoft Outlook 2010 
message rules dialog are now reported automatically. (#3063)
 - NVDA will now report the pinned state for pinned controls such as tabs in 
Mozilla Firefox. (#3372)
+- It is now possible to bind scripts to keyboard gestures containing Alt 
and/or Windows keys as modifiers. Previously, if this was done, performing the 
script would cause the Start Menu or menu bar to be activated. (#3472)
+- Selecting text in browse mode documents (e.g. using control+shift+end) no 
longer causes the keyboard layout to be switched on systems with multiple 
keyboard layouts installed. (#3472)
 
 
 = 2013.2 =


https://bitbucket.org/nvdaaddonteam/nvda/commits/84ba1652e329/
Changeset:   84ba1652e329
Branch:      None
User:        mdcurran
Date:        2013-09-12 03:47:17
Summary:     Merge branch 'master' into next

Affected #:  1 file

diff --git a/user_docs/en/changes.t2t b/user_docs/en/changes.t2t
index 0bcb632..1d56024 100644
--- a/user_docs/en/changes.t2t
+++ b/user_docs/en/changes.t2t
@@ -12,6 +12,7 @@
  - See the User Guide for details.
 - Form fields are now reported in Microsoft word documents. (#2295)
 - NVDA can now announce revision information in Microsoft Word when Track 
Changes is enabled. Note that Report editor revisions in NVDA's document 
settings dialog (off by default) must be enabled also for them to be announced. 
(#1670)
+- Dropdown lists in Microsoft Excel 2003 through 2010 are now announced when 
opened and navigated around. (#3382)
 
 
 == Bug Fixes ==


https://bitbucket.org/nvdaaddonteam/nvda/commits/b120c93442d5/
Changeset:   b120c93442d5
Branch:      None
User:        jteh
Date:        2013-09-12 03:48:49
Summary:     Merge branch 't667' into next

Incubates #667.

Affected #:  7 files

diff --git a/source/api.py b/source/api.py
index 6a27064..9dd0d98 100644
--- a/source/api.py
+++ b/source/api.py
@@ -66,7 +66,7 @@ Before overriding the last object, this function calls 
event_loseFocus on the ob
        #add the old focus to the old focus ancestors, but only if its not None 
(is none at NVDA initialization)
        if globalVars.focusObject: 
                oldFocusLine.append(globalVars.focusObject)
-       oldAppModuleSet=set(o.appModule for o in oldFocusLine if o and 
o.appModule)
+       oldAppModules=[o.appModule for o in oldFocusLine if o and o.appModule]
        ancestors=[]
        tempObj=obj
        matchedOld=False
@@ -106,13 +106,10 @@ Before overriding the last object, this function calls 
event_loseFocus on the ob
                container=tempObj.container
                tempObj.container=container # Cache the parent.
                tempObj=container
+       newAppModules=[o.appModule for o in ancestors if o and o.appModule]
        #Remove the final new ancestor as this will be the new focus object
        del ancestors[-1]
-       newAppModuleSet=set(o.appModule for o in ancestors+[obj] if o and 
o.appModule)
-       for removedMod in oldAppModuleSet-newAppModuleSet:
-               appModuleHandler.handleLoseFocus(removedMod)
-       for addedMod in newAppModuleSet-oldAppModuleSet:
-               appModuleHandler.handleGainFocus(addedMod)
+       appModuleHandler.handleAppSwitch(oldAppModules,newAppModules)
        try:
                treeInterceptorHandler.cleanup()
        except watchdog.CallCancelled:

diff --git a/source/appModuleHandler.py b/source/appModuleHandler.py
index 1efaeae..ed73446 100644
--- a/source/appModuleHandler.py
+++ b/source/appModuleHandler.py
@@ -177,20 +177,51 @@ def terminate():
                        log.exception("Error terminating app module %r" % app)
        runningTable.clear()
 
-def handleLoseFocus(mod):
-       if not mod.sleepMode and hasattr(mod,'event_appModule_loseFocus'):
-               try:
-                       mod.event_appModule_loseFocus()
-               except watchdog.CallCancelled:
-                       pass
-       mod._configProfileTrigger.exit()
-       mod._configProfileTrigger = None
+def handleAppSwitch(oldMods, newMods):
+       newModsSet = set(newMods)
+       processed = set()
+       nextStage = []
+
+       # Determine all apps that are losing focus and fire appropriate events.
+       for mod in reversed(oldMods):
+               if mod in processed:
+                       # This app has already been handled.
+                       continue
+               processed.add(mod)
+               if mod in newModsSet:
+                       # This app isn't losing focus.
+                       continue
+               processed.add(mod)
+               # This app is losing focus.
+               nextStage.append(mod)
+               if not mod.sleepMode and 
hasattr(mod,'event_appModule_loseFocus'):
+                       try:
+                               mod.event_appModule_loseFocus()
+                       except watchdog.CallCancelled:
+                               pass
+
+       with config.conf.atomicProfileSwitch():
+               # Exit triggers for apps that lost focus.
+               for mod in nextStage:
+                       mod._configProfileTrigger.exit()
+                       mod._configProfileTrigger = None
+
+               nextStage = []
+               # Determine all apps that are gaining focus and enter triggers.
+               for mod in newMods:
+                       if mod in processed:
+                               # This app isn't gaining focus or it has 
already been handled.
+                               continue
+                       processed.add(mod)
+                       # This app is gaining focus.
+                       nextStage.append(mod)
+                       trigger = mod._configProfileTrigger = 
AppProfileTrigger(mod.appName)
+                       trigger.enter()
 
-def handleGainFocus(mod):
-       trigger = mod._configProfileTrigger = AppProfileTrigger(mod.appName)
-       trigger.enter()
-       if not mod.sleepMode and hasattr(mod,'event_appModule_gainFocus'):
-               mod.event_appModule_gainFocus()
+       # Fire appropriate events for apps gaining focus.
+       for mod in nextStage:
+               if not mod.sleepMode and 
hasattr(mod,'event_appModule_gainFocus'):
+                       mod.event_appModule_gainFocus()
 
 #base class for appModules
 class AppModule(baseObject.ScriptableObject):

diff --git a/source/config/__init__.py b/source/config/__init__.py
index 570f2b4..86b73dc 100644
--- a/source/config/__init__.py
+++ b/source/config/__init__.py
@@ -9,6 +9,7 @@ import os
 import sys
 from cStringIO import StringIO
 import itertools
+import contextlib
 from configobj import ConfigObj, ConfigObjError
 from validate import Validator
 from logHandler import log
@@ -409,11 +410,14 @@ class ConfigManager(object):
                self.editProfileIndex = None
                self.validator = Validator()
                self.rootSection = None
+               self._shouldHandleProfileSwitch = True
                self._initBaseConf()
                #: The names of all profiles that have been modified since they 
were last saved.
                self._dirtyProfiles = set()
 
        def _handleProfileSwitch(self):
+               if not self._shouldHandleProfileSwitch:
+                       return
                init = self.rootSection is None
                # Reset the cache.
                self.rootSection = AggregatedSection(self, (), self.spec, 
self.profiles)
@@ -676,6 +680,22 @@ class ConfigManager(object):
                self.profiles.remove(profile)
                self._handleProfileSwitch()
 
+       @contextlib.contextmanager
+       def atomicProfileSwitch(self):
+               """Indicate that multiple profile switches should be treated as 
one.
+               This is useful when multiple triggers may be exited/entered at 
once;
+               e.g. when switching applications.
+               While multiple switches aren't harmful, they might take longer;
+               e.g. unnecessarily switching speech synthesizers or braille 
displays.
+               This is a context manager to be used with the C{with} statement.
+               """
+               self._shouldHandleProfileSwitch = False
+               try:
+                       yield
+               finally:
+                       self._shouldHandleProfileSwitch = True
+                       self._handleProfileSwitch()
+
 class AggregatedSection(object):
        """A view of a section of configuration which aggregates settings from 
all active profiles.
        """

diff --git a/source/gui/configProfiles.py b/source/gui/configProfiles.py
index cec5aa2..4f000ac 100644
--- a/source/gui/configProfiles.py
+++ b/source/gui/configProfiles.py
@@ -33,9 +33,9 @@ class ProfilesDialog(wx.Dialog):
                sizer = wx.BoxSizer(wx.HORIZONTAL)
                # Translators: The label of the profile list in the 
Configuration Profiles dialog.
                sizer.Add(wx.StaticText(self, label=_("&Profile")))
-               # Translators: Indicates that no configuration profile is 
selected.
-               # In this case, the user's normal configuration will be used.
-               profiles = [_("(none)")]
+               # Translators: The item to select the user's normal 
configuration
+               # in the profile list in the Configuration Profiles dialog.
+               profiles = [_("(normal configuration)")]
                profiles.extend(config.conf.listProfiles())
                item = self.profileList = wx.Choice(self, choices=profiles)
                item.Bind(wx.EVT_CHOICE, self.onProfileListChoice)
@@ -48,8 +48,8 @@ class ProfilesDialog(wx.Dialog):
                mainSizer.Add(sizer)
 
                sizer = wx.BoxSizer(wx.HORIZONTAL)
-               # Translators: The label of a button to activate the selected 
profile.
-               item = wx.Button(self, label=_("&Activate"))
+               # Translators: The label of a button to activate (and therefore 
also edit) the selected profile.
+               item = wx.Button(self, label=_("&Activate/edit"))
                item.Bind(wx.EVT_BUTTON, self.onActivate)
                sizer.Add(item)
                self.AffirmativeId = item.Id
@@ -129,6 +129,7 @@ class ProfilesDialog(wx.Dialog):
                        return
                self.profileList.Append(name)
                self.profileList.Selection = self.profileList.Count - 1
+               self.onProfileListChoice(None)
                self.profileList.SetFocus()
 
        def onDelete(self, evt):
@@ -231,9 +232,6 @@ class TriggersDialog(wx.Dialog):
                item = self.sayAllToggle = wx.CheckBox(self, label=_("&Say 
all"))
                if "sayAll" in triggers:
                        item.Value = True
-               elif "sayAll" in config.conf["profileTriggers"]:
-                       # This trigger is associated with another profile 
already.
-                       item.Disable()
                mainSizer.Add(item)
 
                item = wx.Button(self, wx.ID_CLOSE, label=_("&Close"))
@@ -247,13 +245,23 @@ class TriggersDialog(wx.Dialog):
 
        def onClose(self, evt):
                triggers = config.conf["profileTriggers"]
+               try:
+                       trigOnOther = triggers["sayAll"] != self.profile
+               except KeyError:
+                       trigOnOther = False
                if self.sayAllToggle.Value:
+                       if trigOnOther and gui.messageBox(
+                               # Translators: A confirmation prompt that might 
be displayed when closing the configuration profile triggers dialog.
+                               _("Say all is already associated with another 
profile.\n"
+                                       "If you continue, it will be removed 
from the other profile and associated with this one.\n"
+                                       "Are you sure you want to continue?"),
+                               # Translators: The title of a confirmation 
prompt.
+                               _("Confirm"), wx.YES | wx.NO | wx.ICON_WARNING, 
self
+                       ) == wx.NO:
+                               return
                        triggers["sayAll"] = self.profile
-               elif self.sayAllToggle.Enabled:
-                       try:
-                               del triggers["sayAll"]
-                       except KeyError:
-                               pass
+               elif not trigOnOther:
+                       del triggers["sayAll"]
 
                self.Parent.Enable()
                self.Destroy()

diff --git a/source/queueHandler.py b/source/queueHandler.py
index 4005245..0db66da 100644
--- a/source/queueHandler.py
+++ b/source/queueHandler.py
@@ -73,4 +73,6 @@ def pumpAll():
                except:
                        log.exception("error in generator %d"%ID)
                        del generators[ID]
+               # Lose our reference so Python can destroy the generator if 
appropriate.
+               del gen
        flushQueue(eventQueue)

diff --git a/source/sayAllHandler.py b/source/sayAllHandler.py
index f26321e..1a3043f 100644
--- a/source/sayAllHandler.py
+++ b/source/sayAllHandler.py
@@ -146,7 +146,7 @@ def readTextHelper_generator(cursor):
                                        if cursor!=CURSOR_CARET or 
config.conf["reviewCursor"]["followCaret"]:
                                                api.setReviewPosition(updater)
                        elif not keepReading and 
lastReceivedIndex==lastSentIndex:
-                               # All text has been spoken.
+                               # All text has been sent to the synth.
                                # Turn the page and start again if the object 
supports it.
                                if 
isinstance(reader.obj,textInfos.DocumentWithPageTurns):
                                        try:
@@ -163,6 +163,20 @@ def readTextHelper_generator(cursor):
                                yield
                        yield
 
+               # Wait until the synth has actually finished speaking.
+               # Otherwise, if there is a triggered profile with a different 
synth,
+               # we will switch too early and truncate speech (even up to 
several lines).
+               # Send another index and wait for it.
+               index=lastSentIndex+1
+               speech.speak([speech.IndexCommand(index)])
+               while speech.getLastSpeechIndex()<index:
+                       yield
+                       yield
+               # Some synths say they've handled the index slightly sooner 
than they actually have,
+               # so wait a bit longer.
+               for i in xrange(30):
+                       yield
+
 class SayAllProfileTrigger(config.ProfileTrigger):
        """A configuration profile trigger for when say all is in progress.
        """

diff --git a/user_docs/en/userGuide.t2t b/user_docs/en/userGuide.t2t
index d07d9ec..7667d21 100644
--- a/user_docs/en/userGuide.t2t
+++ b/user_docs/en/userGuide.t2t
@@ -1109,11 +1109,11 @@ You can also do this using a key command:
 
 The first control in this dialog is the profile list from which you can select 
one of the available profiles.
 When you open the dialog, the last manually activated profile is selected.
-You can manually activate the selected profile by pressing the Activate button.
+You can manually activate the selected profile by pressing the Activate/edit 
button.
 Once a profile is manually activated, any settings in that profile override 
the settings in your normal configuration.
 Also, any settings you change will be saved in that profile.
 
-To return to your normal configuration, select "(none)" in the profile list 
and activate it.
+To return to your normal configuration, select "(normal configuration)" in the 
profile list and press the Activate/edit button.
 
 You can create a new profile by pressing the New button.
 To rename or delete a profile, press the Rename or Delete buttons, 
respectively.
@@ -1125,7 +1125,6 @@ Pressing the Triggers button in the Configuration 
Profiles dialog allows you to
 If there is a manually activated profile, other profiles can still be 
activated due to triggers, but any settings in the manually activated profile 
will override them.
 
 A specific trigger can only be associated with a single profile, but a single 
profile can be associated with several triggers.
-If a trigger is already associated with another profile, its option won't be 
available.
 
 The Applications grouping allows you to specify applications that should 
trigger the profile when you switch to them.
 Applications are specified by the name of their executable file (without the 
extension), which might be different to the actual name of the application.


https://bitbucket.org/nvdaaddonteam/nvda/commits/1909092e051f/
Changeset:   1909092e051f
Branch:      None
User:        mdcurran
Date:        2013-09-12 03:53:36
Summary:     Merge branch 'master' into next

Affected #:  0 files



https://bitbucket.org/nvdaaddonteam/nvda/commits/2dac7393334e/
Changeset:   2dac7393334e
Branch:      None
User:        mdcurran
Date:        2013-09-12 03:58:06
Summary:     Merge branch 'master' into next

Affected #:  0 files



https://bitbucket.org/nvdaaddonteam/nvda/commits/d9f65203d29d/
Changeset:   d9f65203d29d
Branch:      None
User:        jteh
Date:        2013-09-12 08:16:34
Summary:     gui.MainFrame: Make the focus and focus ancestors before the last 
popup available via prevFocus and prevFocusAncestors attributes.

This makes it possible for dialogs brought up with the NVDA menu to be context 
sensitive.

Affected #:  1 file

diff --git a/source/gui/__init__.py b/source/gui/__init__.py
index 6428d32..5e1e0ec 100644
--- a/source/gui/__init__.py
+++ b/source/gui/__init__.py
@@ -87,6 +87,14 @@ class MainFrame(wx.Frame):
                super(MainFrame, self).__init__(None, wx.ID_ANY, 
versionInfo.name, size=(1,1), style=style)
                self.Bind(wx.EVT_CLOSE, self.onExitCommand)
                self.sysTrayIcon = SysTrayIcon(self)
+               #: The focus before the last popup or C{None} if unknown.
+               #: This is only valid before L{prePopup} is called,
+               #: so it should be used as early as possible in any popup that 
needs it.
+               #: @type: L{NVDAObject}
+               self.prevFocus = None
+               #: The focus ancestors before the last popup or C{None} if 
unknown.
+               #: @type: list of L{NVDAObject}
+               self.prevFocusAncestors = None
                # This makes Windows return to the previous foreground window 
and also seems to allow NVDA to be brought to the foreground.
                self.Show()
                self.Hide()
@@ -107,7 +115,12 @@ class MainFrame(wx.Frame):
                L{postPopup} should be called after the dialog or menu has been 
shown.
                @postcondition: A dialog or menu may be shown.
                """
-               if 
winUser.getWindowThreadProcessID(winUser.getForegroundWindow())[0] != 
os.getpid():
+               nvdaPid = os.getpid()
+               focus = api.getFocusObject()
+               if focus.processID != nvdaPid:
+                       self.prevFocus = focus
+                       self.prevFocusAncestors = api.getFocusAncestors()
+               if 
winUser.getWindowThreadProcessID(winUser.getForegroundWindow())[0] != nvdaPid:
                        # This process is not the foreground process, so bring 
it to the foreground.
                        self.Raise()
 
@@ -115,6 +128,8 @@ class MainFrame(wx.Frame):
                """Clean up after a popup dialog or menu.
                This should be called after a dialog or menu was popped up for 
the user.
                """
+               self.prevFocus = None
+               self.prevFocusAncestors = None
                if not winUser.isWindowVisible(winUser.getForegroundWindow()):
                        # The current foreground window is invisible, so we 
want to return to the previous foreground window.
                        # Showing and hiding our main window seems to achieve 
this.


https://bitbucket.org/nvdaaddonteam/nvda/commits/6f613f43b3e9/
Changeset:   6f613f43b3e9
Branch:      None
User:        jteh
Date:        2013-09-12 08:17:18
Summary:     Merge branch 'nvdaMenuSaveState' into t1532

Affected #:  6 files

diff --git a/source/NVDAObjects/window/__init__.py 
b/source/NVDAObjects/window/__init__.py
index f2d78b2..4cd8451 100644
--- a/source/NVDAObjects/window/__init__.py
+++ b/source/NVDAObjects/window/__init__.py
@@ -84,6 +84,9 @@ An NVDAObject for a window
                if windowClassName=="EXCEL7" and (relation=='focus' or 
isinstance(relation,tuple)): 
                        from . import excel
                        yield excel.ExcelCell 
+               if windowClassName=="EXCEL:":
+                       from .excel import ExcelDropdown as newCls
+                       yield newCls
                import JABHandler
                if JABHandler.isJavaWindow(windowHandle):
                        import NVDAObjects.JAB

diff --git a/source/NVDAObjects/window/excel.py 
b/source/NVDAObjects/window/excel.py
index c68ff94..a87bae6 100755
--- a/source/NVDAObjects/window/excel.py
+++ b/source/NVDAObjects/window/excel.py
@@ -16,6 +16,7 @@ import colors
 import eventHandler
 import gui
 import winUser
+from displayModel import DisplayModelTextInfo
 import controlTypes
 from . import Window
 from .. import NVDAObjectTextInfo
@@ -168,6 +169,22 @@ class ExcelCell(ExcelBase):
                if rowHeaderColumn and columnNumber>rowHeaderColumn:
                        return 
self.excelCellObject.parent.cells(rowNumber,rowHeaderColumn).text
 
+       def _get_dropdown(self):
+               w=winUser.getAncestor(self.windowHandle,winUser.GA_ROOT)
+               if not w: return
+               obj=Window(windowHandle=w,chooseBestAPI=False)
+               if not obj: return
+               prev=obj.previous
+               if not prev or prev.windowClassName!='EXCEL:': return
+               return prev
+
+       def script_openDropdown(self,gesture):
+               gesture.send()
+               d=self.dropdown
+               if d:
+                       d.parent=self
+                       eventHandler.queueEvent("gainFocus",d)
+
        def script_setColumnHeaderRow(self,gesture):
                scriptCount=scriptHandler.getLastScriptRepeatCount()
                tableID=self.tableID
@@ -290,6 +307,7 @@ class ExcelCell(ExcelBase):
        __gestures = {
                "kb:NVDA+shift+c": "setColumnHeaderRow",
                "kb:NVDA+shift+r": "setRowHeaderColumn",
+               "kb:alt+downArrow":"openDropdown",
        }
 
 class ExcelSelection(ExcelBase):
@@ -321,3 +339,107 @@ class ExcelSelection(ExcelBase):
                if position==textInfos.POSITION_SELECTION:
                        position=textInfos.POSITION_ALL
                return super(ExcelSelection,self).makeTextInfo(position)
+
+class ExcelDropdownItem(Window):
+
+       firstChild=None
+       lastChild=None
+       children=[]
+       role=controlTypes.ROLE_LISTITEM
+
+       def __init__(self,parent=None,name=None,states=None,index=None):
+               self.name=name
+               self.states=states
+               self.parent=parent
+               self.index=index
+               
super(ExcelDropdownItem,self).__init__(windowHandle=parent.windowHandle)
+
+       def _get_previous(self):
+               newIndex=self.index-1
+               if newIndex>=0:
+                       return self.parent.children[newIndex]
+
+       def _get_next(self):
+               newIndex=self.index+1
+               if newIndex<self.parent.childCount:
+                       return self.parent.children[newIndex]
+
+       def _get_positionInfo(self):
+               return 
{'indexInGroup':self.index+1,'similarItemsInGroup':self.parent.childCount,}
+
+class ExcelDropdown(Window):
+
+       @classmethod
+       def kwargsFromSuper(cls,kwargs,relation=None):
+               return kwargs
+
+       role=controlTypes.ROLE_LIST
+       excelCell=None
+
+       def _get__highlightColors(self):
+               
background=colors.RGB.fromCOLORREF(winUser.user32.GetSysColor(13))
+               
foreground=colors.RGB.fromCOLORREF(winUser.user32.GetSysColor(14))
+               self._highlightColors=(background,foreground)
+               return self._highlightColors
+
+       def _get_children(self):
+               children=[]
+               index=0
+               states=set()
+               for item in 
DisplayModelTextInfo(self,textInfos.POSITION_ALL).getTextWithFields(): 
+                       if isinstance(item,textInfos.FieldCommand) and 
item.command=="formatChange":
+                               states=set([controlTypes.STATE_SELECTABLE])
+                               foreground=item.field.get('color',None)
+                               
background=item.field.get('background-color',None)
+                               if 
(background,foreground)==self._highlightColors: 
+                                       states.add(controlTypes.STATE_SELECTED)
+                       if isinstance(item,basestring):
+                               
obj=ExcelDropdownItem(parent=self,name=item,states=states,index=index)
+                               children.append(obj)
+                               index+=1
+               return children
+
+       def _get_childCount(self):
+               return len(self.children)
+
+       def _get_firstChild(self):
+               return self.children[0]
+       def _get_selection(self):
+               for child in self.children:
+                       if controlTypes.STATE_SELECTED in child.states:
+                               return child
+
+       def script_selectionChange(self,gesture):
+               gesture.send()
+               newFocus=self.selection or self
+               if eventHandler.lastQueuedFocusObject is newFocus: return
+               eventHandler.queueEvent("gainFocus",newFocus)
+       script_selectionChange.canPropagate=True
+
+       def script_closeDropdown(self,gesture):
+               gesture.send()
+               eventHandler.queueEvent("gainFocus",self.parent)
+       script_closeDropdown.canPropagate=True
+
+       __gestures={
+               "kb:downArrow":"selectionChange",
+               "kb:upArrow":"selectionChange",
+               "kb:leftArrow":"selectionChange",
+               "kb:rightArrow":"selectionChange",
+               "kb:home":"selectionChange",
+               "kb:end":"selectionChange",
+               "kb:escape":"closeDropdown",
+               "kb:enter":"closeDropdown",
+               "kb:space":"closeDropdown",
+       }
+
+       def event_gainFocus(self):
+               child=self.selection
+               if not child and self.childCount>0:
+                       child=self.children[0]
+               if child:
+                       eventHandler.queueEvent("focusEntered",self)
+                       eventHandler.queueEvent("gainFocus",child)
+               else:
+                       super(ExcelDropdown,self).event_gainFocus()
+                       

diff --git a/source/_UIAHandler.py b/source/_UIAHandler.py
index 96bad4f..989ccd1 100644
--- a/source/_UIAHandler.py
+++ b/source/_UIAHandler.py
@@ -130,11 +130,11 @@ class UIAHandler(COMObject):
                        raise self.MTAThreadInitException
 
        def terminate(self):
-               
MTAThreadHandle=HANDLE(windll.kernel32.OpenThread(self.MTAThread.ident,False,winKernel.SYNCHRONIZE))
+               
MTAThreadHandle=HANDLE(windll.kernel32.OpenThread(winKernel.SYNCHRONIZE,False,self.MTAThread.ident))
                self.MTAThreadStopEvent.set()
-               index=c_int()
-               #Wait for the MTAA thread to die (while still message pumping)
-               
windll.user32.MsgWaitForMultipleObjects(1,byref(MTAThreadHandle),False,5000,0)
+               #Wait for the MTA thread to die (while still message pumping)
+               if 
windll.user32.MsgWaitForMultipleObjects(1,byref(MTAThreadHandle),False,200,0)!=0:
+                       log.debugWarning("Timeout or error while waiting for 
UIAHandler MTA thread")
                windll.kernel32.CloseHandle(MTAThreadHandle)
                del self.MTAThread
 

diff --git a/source/gui/__init__.py b/source/gui/__init__.py
index 55edcde..fa206bc 100644
--- a/source/gui/__init__.py
+++ b/source/gui/__init__.py
@@ -87,6 +87,14 @@ class MainFrame(wx.Frame):
                super(MainFrame, self).__init__(None, wx.ID_ANY, 
versionInfo.name, size=(1,1), style=style)
                self.Bind(wx.EVT_CLOSE, self.onExitCommand)
                self.sysTrayIcon = SysTrayIcon(self)
+               #: The focus before the last popup or C{None} if unknown.
+               #: This is only valid before L{prePopup} is called,
+               #: so it should be used as early as possible in any popup that 
needs it.
+               #: @type: L{NVDAObject}
+               self.prevFocus = None
+               #: The focus ancestors before the last popup or C{None} if 
unknown.
+               #: @type: list of L{NVDAObject}
+               self.prevFocusAncestors = None
                # This makes Windows return to the previous foreground window 
and also seems to allow NVDA to be brought to the foreground.
                self.Show()
                self.Hide()
@@ -107,7 +115,12 @@ class MainFrame(wx.Frame):
                L{postPopup} should be called after the dialog or menu has been 
shown.
                @postcondition: A dialog or menu may be shown.
                """
-               if 
winUser.getWindowThreadProcessID(winUser.getForegroundWindow())[0] != 
os.getpid():
+               nvdaPid = os.getpid()
+               focus = api.getFocusObject()
+               if focus.processID != nvdaPid:
+                       self.prevFocus = focus
+                       self.prevFocusAncestors = api.getFocusAncestors()
+               if 
winUser.getWindowThreadProcessID(winUser.getForegroundWindow())[0] != nvdaPid:
                        # This process is not the foreground process, so bring 
it to the foreground.
                        self.Raise()
 
@@ -115,6 +128,8 @@ class MainFrame(wx.Frame):
                """Clean up after a popup dialog or menu.
                This should be called after a dialog or menu was popped up for 
the user.
                """
+               self.prevFocus = None
+               self.prevFocusAncestors = None
                if not winUser.isWindowVisible(winUser.getForegroundWindow()):
                        # The current foreground window is invisible, so we 
want to return to the previous foreground window.
                        # Showing and hiding our main window seems to achieve 
this.

diff --git a/source/logHandler.py b/source/logHandler.py
index 2f6c7e6..0a380ec 100755
--- a/source/logHandler.py
+++ b/source/logHandler.py
@@ -24,36 +24,6 @@ EVENT_E_ALL_SUBSCRIBERS_FAILED = -2147220991
 RPC_E_CALL_REJECTED = -2147418111
 RPC_E_DISCONNECTED = -2147417848
 
-moduleCache={}
-
-def makeModulePathFromFilePath(path):
-       """calculates the pythonic dotted module path from a file path of a 
python module.
-       @param path: the relative or absolute path to the module
-       @type path: string
-       @returns: the Pythonic dotted module path 
-       @rtype: string
-       """
-       if path in moduleCache:
-               return moduleCache[path]
-       modPathList = []
-       # Work through the path components from right to left.
-       curPath = path
-       while curPath:
-               curPath, curPathCom = os.path.split(curPath)
-               if not curPathCom:
-                       break
-               curPathCom = os.path.splitext(curPathCom)[0]
-               # __init__ is the root module of a package, so skip it.
-               if curPathCom != "__init__":
-                       modPathList.insert(0, curPathCom)
-               if curPath in sys.path:
-                       # curPath is in the Python search path, so the Pythonic 
module path is relative to curPath.
-                       break
-       modulePath = ".".join(modPathList)
-       if modulePath:
-               moduleCache[path] = modulePath
-       return modulePath
- 
 def getCodePath(f):
        """Using a frame object, gets its module path (relative to the current 
directory).[className.[funcName]]
        @param f: the frame object to use
@@ -61,7 +31,19 @@ def getCodePath(f):
        @returns: the dotted module.class.attribute path
        @rtype: string
        """
-       path=makeModulePathFromFilePath(os.path.relpath(f.f_code.co_filename))
+       fn=f.f_code.co_filename
+       if fn[0] != "<" and os.path.isabs(fn) and not fn.startswith(sys.path[0] 
+ "\\"):
+               # This module is external because:
+               # the code comes from a file (fn doesn't begin with "<");
+               # it has an absolute file path (code bundled in binary builds 
reports relative paths); and
+               # it is not part of NVDA's Python code (not beneath 
sys.path[0]).
+               path="external:"
+       else:
+               path=""
+       try:
+               path+=f.f_globals["__name__"]
+       except KeyError:
+               path+=fn
        funcName=f.f_code.co_name
        if funcName.startswith('<'):
                funcName=""

diff --git a/user_docs/en/changes.t2t b/user_docs/en/changes.t2t
index 3188ffb..b16889d 100644
--- a/user_docs/en/changes.t2t
+++ b/user_docs/en/changes.t2t
@@ -8,6 +8,7 @@
 == New Features ==
 - Form fields are now reported in Microsoft word documents. (#2295)
 - NVDA can now announce revision information in Microsoft Word when Track 
Changes is enabled. Note that Report editor revisions in NVDA's document 
settings dialog (off by default) must be enabled also for them to be announced. 
(#1670)
+- Dropdown lists in Microsoft Excel 2003 through 2010 are now announced when 
opened and navigated around. (#3382)
 
 
 == Bug Fixes ==


https://bitbucket.org/nvdaaddonteam/nvda/commits/329e27302a89/
Changeset:   329e27302a89
Branch:      None
User:        jteh
Date:        2013-09-12 08:59:35
Summary:     If the Input Gestures dialog is opened from the NVDA menu, it will 
now use the focus before the NVDA menu was opened.

Previously, you always had to use NVDA+control+i if you wanted to access 
context specific gestures.

Affected #:  2 files

diff --git a/source/gui/settingsDialogs.py b/source/gui/settingsDialogs.py
index 3dadf3e..84aab0c 100644
--- a/source/gui/settingsDialogs.py
+++ b/source/gui/settingsDialogs.py
@@ -1528,7 +1528,7 @@ class InputGesturesDialog(SettingsDialog):
                tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.onTreeSelect)
                settingsSizer.Add(tree, proportion=7, flag=wx.EXPAND)
 
-               gestures = inputCore.manager.getAllGestureMappings()
+               gestures = 
inputCore.manager.getAllGestureMappings(obj=gui.mainFrame.prevFocus, 
ancestors=gui.mainFrame.prevFocusAncestors)
                for category in sorted(gestures):
                        treeCat = tree.AppendItem(self.treeRoot, category)
                        commands = gestures[category]

diff --git a/user_docs/en/userGuide.t2t b/user_docs/en/userGuide.t2t
index 1ae185a..485c0ea 100644
--- a/user_docs/en/userGuide.t2t
+++ b/user_docs/en/userGuide.t2t
@@ -1061,8 +1061,7 @@ When you are finished, press the OK button to save your 
changes or the Cancel bu
 In this dialog, you can customize the input gestures (keys on the keyboard, 
buttons on a braille display, etc.) for NVDA commands.
 
 Only commands that are applicable immediately before the dialog is opened are 
shown.
-Therefore, you should generally open this dialog using its key command so that 
all commands applicable to what you were just doing are shown.
-For example, if you want to customize commands related to browse mode, you 
should press the key command to open the Input Gestures dialog while you are in 
browse mode.
+For example, if you want to customize commands related to browse mode, you 
should open the Input Gestures dialog while you are in browse mode.
 
 The tree in this dialog lists all of the applicable NVDA commands grouped by 
category.
 Any gestures associated with a command are listed beneath the command.


https://bitbucket.org/nvdaaddonteam/nvda/commits/2633f7e19366/
Changeset:   2633f7e19366
Branch:      None
User:        jteh
Date:        2013-09-12 09:04:15
Summary:     Merge branch 't1532' into next

Incubates #1532.

Affected #:  3 files

diff --git a/source/gui/__init__.py b/source/gui/__init__.py
index 0324263..bb64610 100644
--- a/source/gui/__init__.py
+++ b/source/gui/__init__.py
@@ -87,6 +87,14 @@ class MainFrame(wx.Frame):
                super(MainFrame, self).__init__(None, wx.ID_ANY, 
versionInfo.name, size=(1,1), style=style)
                self.Bind(wx.EVT_CLOSE, self.onExitCommand)
                self.sysTrayIcon = SysTrayIcon(self)
+               #: The focus before the last popup or C{None} if unknown.
+               #: This is only valid before L{prePopup} is called,
+               #: so it should be used as early as possible in any popup that 
needs it.
+               #: @type: L{NVDAObject}
+               self.prevFocus = None
+               #: The focus ancestors before the last popup or C{None} if 
unknown.
+               #: @type: list of L{NVDAObject}
+               self.prevFocusAncestors = None
                # This makes Windows return to the previous foreground window 
and also seems to allow NVDA to be brought to the foreground.
                self.Show()
                self.Hide()
@@ -107,7 +115,12 @@ class MainFrame(wx.Frame):
                L{postPopup} should be called after the dialog or menu has been 
shown.
                @postcondition: A dialog or menu may be shown.
                """
-               if 
winUser.getWindowThreadProcessID(winUser.getForegroundWindow())[0] != 
os.getpid():
+               nvdaPid = os.getpid()
+               focus = api.getFocusObject()
+               if focus.processID != nvdaPid:
+                       self.prevFocus = focus
+                       self.prevFocusAncestors = api.getFocusAncestors()
+               if 
winUser.getWindowThreadProcessID(winUser.getForegroundWindow())[0] != nvdaPid:
                        # This process is not the foreground process, so bring 
it to the foreground.
                        self.Raise()
 
@@ -115,6 +128,8 @@ class MainFrame(wx.Frame):
                """Clean up after a popup dialog or menu.
                This should be called after a dialog or menu was popped up for 
the user.
                """
+               self.prevFocus = None
+               self.prevFocusAncestors = None
                if not winUser.isWindowVisible(winUser.getForegroundWindow()):
                        # The current foreground window is invisible, so we 
want to return to the previous foreground window.
                        # Showing and hiding our main window seems to achieve 
this.

diff --git a/source/gui/settingsDialogs.py b/source/gui/settingsDialogs.py
index bd10732..e6b8ce3 100644
--- a/source/gui/settingsDialogs.py
+++ b/source/gui/settingsDialogs.py
@@ -1528,7 +1528,7 @@ class InputGesturesDialog(SettingsDialog):
                tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.onTreeSelect)
                settingsSizer.Add(tree, proportion=7, flag=wx.EXPAND)
 
-               gestures = inputCore.manager.getAllGestureMappings()
+               gestures = 
inputCore.manager.getAllGestureMappings(obj=gui.mainFrame.prevFocus, 
ancestors=gui.mainFrame.prevFocusAncestors)
                for category in sorted(gestures):
                        treeCat = tree.AppendItem(self.treeRoot, category)
                        commands = gestures[category]

diff --git a/user_docs/en/userGuide.t2t b/user_docs/en/userGuide.t2t
index 7667d21..adb3262 100644
--- a/user_docs/en/userGuide.t2t
+++ b/user_docs/en/userGuide.t2t
@@ -1061,8 +1061,7 @@ When you are finished, press the OK button to save your 
changes or the Cancel bu
 In this dialog, you can customize the input gestures (keys on the keyboard, 
buttons on a braille display, etc.) for NVDA commands.
 
 Only commands that are applicable immediately before the dialog is opened are 
shown.
-Therefore, you should generally open this dialog using its key command so that 
all commands applicable to what you were just doing are shown.
-For example, if you want to customize commands related to browse mode, you 
should press the key command to open the Input Gestures dialog while you are in 
browse mode.
+For example, if you want to customize commands related to browse mode, you 
should open the Input Gestures dialog while you are in browse mode.
 
 The tree in this dialog lists all of the applicable NVDA commands grouped by 
category.
 Any gestures associated with a command are listed beneath the command.


https://bitbucket.org/nvdaaddonteam/nvda/commits/685c52188c98/
Changeset:   685c52188c98
Branch:      None
User:        jteh
Date:        2013-09-13 06:05:51
Summary:     Fix undesirable speech interruption with some synths whenever the 
focus changed.

atomicProfileSwitch was calling _handleProfileSwitch at the end even if there 
was no profile switch. appModuleHandler.handleAppSwitch (which is called for 
every focus change) uses atomicProfileSwitch even if the app doesn't change, so 
this meant _handleProfileSwitch was being called for every focus change. 
_handleProfileSwitch resets synth settings and some synths cancel speech if the 
voice is reset.
Re #3524.

Affected #:  1 file

diff --git a/source/config/__init__.py b/source/config/__init__.py
index d4e335e..3a0087b 100644
--- a/source/config/__init__.py
+++ b/source/config/__init__.py
@@ -410,12 +410,14 @@ class ConfigManager(object):
                self.validator = Validator()
                self.rootSection = None
                self._shouldHandleProfileSwitch = True
+               self._pendingHandleProfileSwitch = False
                self._initBaseConf()
                #: The names of all profiles that have been modified since they 
were last saved.
                self._dirtyProfiles = set()
 
        def _handleProfileSwitch(self):
                if not self._shouldHandleProfileSwitch:
+                       self._pendingHandleProfileSwitch = True
                        return
                init = self.rootSection is None
                # Reset the cache.
@@ -693,7 +695,9 @@ class ConfigManager(object):
                        yield
                finally:
                        self._shouldHandleProfileSwitch = True
-                       self._handleProfileSwitch()
+                       if self._pendingHandleProfileSwitch:
+                               self._handleProfileSwitch()
+                               self._pendingHandleProfileSwitch = False
 
 class AggregatedSection(object):
        """A view of a section of configuration which aggregates settings from 
all active profiles.


https://bitbucket.org/nvdaaddonteam/nvda/commits/349ff4ee78ce/
Changeset:   349ff4ee78ce
Branch:      None
User:        jteh
Date:        2013-09-13 08:09:45
Summary:     When switching config profiles, don't apply voice settings that 
haven't actually changed.

Some synths may interrupt speech or perform slowly when some settings are 
applied, so it's best to apply only those that are necessary.

Affected #:  1 file

diff --git a/source/synthDriverHandler.py b/source/synthDriverHandler.py
index f0ab46a..4a78623 100644
--- a/source/synthDriverHandler.py
+++ b/source/synthDriverHandler.py
@@ -107,7 +107,7 @@ def handleConfigProfileSwitch():
        if conf["synth"] != _curSynth.name:
                setSynth(conf["synth"])
                return
-       _curSynth.loadSettings()
+       _curSynth.loadSettings(onlyChanged=True)
 
 class SynthSetting(object):
        """Represents a synthesizer setting such as voice or variant.
@@ -452,24 +452,28 @@ class SynthDriver(baseObject.AutoPropertyObject):
                for setting in self.supportedSettings:
                        conf[setting.name]=getattr(self,setting.name)
 
-       def loadSettings(self):
+       def loadSettings(self, onlyChanged=False):
                c=config.conf["speech"][self.name]
                if self.isSupported("voice"):
                        voice=c.get("voice",None)
-                       try:
-                               changeVoice(self,voice)
-                       except:
-                               log.warning("Invalid voice: %s" % voice)
-                               # Update the configuration with the correct 
voice.
-                               c["voice"]=self.voice
-                               # We need to call changeVoice here so that 
required initialisation can be performed.
-                               changeVoice(self,self.voice)
-               else:
+                       if not onlyChanged or self.voice!=voice:
+                               try:
+                                       changeVoice(self,voice)
+                               except:
+                                       log.warning("Invalid voice: %s" % voice)
+                                       # Update the configuration with the 
correct voice.
+                                       c["voice"]=self.voice
+                                       # We need to call changeVoice here so 
that required initialisation can be performed.
+                                       changeVoice(self,self.voice)
+               elif not onlyChanged:
                        changeVoice(self,None)
                for s in self.supportedSettings:
                        if s.name=="voice" or c[s.name] is None:
                                continue
-                       setattr(self,s.name,c[s.name])
+                       val=c[s.name]
+                       if onlyChanged and getattr(self,s.name)==val:
+                               continue
+                       setattr(self,s.name,val)
 
        def _get_initialSettingsRingSetting (self):
                if not self.isSupported("rate") and 
len(self.supportedSettings)>0:


https://bitbucket.org/nvdaaddonteam/nvda/commits/64c198882fd3/
Changeset:   64c198882fd3
Branch:      None
User:        jteh
Date:        2013-09-13 08:51:56
Summary:     Merge branch 't667' into next

Incubates #667. Fixes #3524.

Affected #:  2 files

diff --git a/source/config/__init__.py b/source/config/__init__.py
index 86b73dc..1bf36dc 100644
--- a/source/config/__init__.py
+++ b/source/config/__init__.py
@@ -411,12 +411,14 @@ class ConfigManager(object):
                self.validator = Validator()
                self.rootSection = None
                self._shouldHandleProfileSwitch = True
+               self._pendingHandleProfileSwitch = False
                self._initBaseConf()
                #: The names of all profiles that have been modified since they 
were last saved.
                self._dirtyProfiles = set()
 
        def _handleProfileSwitch(self):
                if not self._shouldHandleProfileSwitch:
+                       self._pendingHandleProfileSwitch = True
                        return
                init = self.rootSection is None
                # Reset the cache.
@@ -694,7 +696,9 @@ class ConfigManager(object):
                        yield
                finally:
                        self._shouldHandleProfileSwitch = True
-                       self._handleProfileSwitch()
+                       if self._pendingHandleProfileSwitch:
+                               self._handleProfileSwitch()
+                               self._pendingHandleProfileSwitch = False
 
 class AggregatedSection(object):
        """A view of a section of configuration which aggregates settings from 
all active profiles.

diff --git a/source/synthDriverHandler.py b/source/synthDriverHandler.py
index f0ab46a..4a78623 100644
--- a/source/synthDriverHandler.py
+++ b/source/synthDriverHandler.py
@@ -107,7 +107,7 @@ def handleConfigProfileSwitch():
        if conf["synth"] != _curSynth.name:
                setSynth(conf["synth"])
                return
-       _curSynth.loadSettings()
+       _curSynth.loadSettings(onlyChanged=True)
 
 class SynthSetting(object):
        """Represents a synthesizer setting such as voice or variant.
@@ -452,24 +452,28 @@ class SynthDriver(baseObject.AutoPropertyObject):
                for setting in self.supportedSettings:
                        conf[setting.name]=getattr(self,setting.name)
 
-       def loadSettings(self):
+       def loadSettings(self, onlyChanged=False):
                c=config.conf["speech"][self.name]
                if self.isSupported("voice"):
                        voice=c.get("voice",None)
-                       try:
-                               changeVoice(self,voice)
-                       except:
-                               log.warning("Invalid voice: %s" % voice)
-                               # Update the configuration with the correct 
voice.
-                               c["voice"]=self.voice
-                               # We need to call changeVoice here so that 
required initialisation can be performed.
-                               changeVoice(self,self.voice)
-               else:
+                       if not onlyChanged or self.voice!=voice:
+                               try:
+                                       changeVoice(self,voice)
+                               except:
+                                       log.warning("Invalid voice: %s" % voice)
+                                       # Update the configuration with the 
correct voice.
+                                       c["voice"]=self.voice
+                                       # We need to call changeVoice here so 
that required initialisation can be performed.
+                                       changeVoice(self,self.voice)
+               elif not onlyChanged:
                        changeVoice(self,None)
                for s in self.supportedSettings:
                        if s.name=="voice" or c[s.name] is None:
                                continue
-                       setattr(self,s.name,c[s.name])
+                       val=c[s.name]
+                       if onlyChanged and getattr(self,s.name)==val:
+                               continue
+                       setattr(self,s.name,val)
 
        def _get_initialSettingsRingSetting (self):
                if not self.isSupported("rate") and 
len(self.supportedSettings)>0:


https://bitbucket.org/nvdaaddonteam/nvda/commits/23b20c22fbcf/
Changeset:   23b20c22fbcf
Branch:      None
User:        jteh
Date:        2013-09-14 00:43:13
Summary:     TriggersDialog: Don't barf when say all is unchecked and the say 
all trigger isn't associated with any other profile.

Re #667.

Affected #:  1 file

diff --git a/source/gui/configProfiles.py b/source/gui/configProfiles.py
index 4f000ac..f8cf839 100644
--- a/source/gui/configProfiles.py
+++ b/source/gui/configProfiles.py
@@ -261,7 +261,10 @@ class TriggersDialog(wx.Dialog):
                                return
                        triggers["sayAll"] = self.profile
                elif not trigOnOther:
-                       del triggers["sayAll"]
+                       try:
+                               del triggers["sayAll"]
+                       except KeyError:
+                               pass
 
                self.Parent.Enable()
                self.Destroy()


https://bitbucket.org/nvdaaddonteam/nvda/commits/9f660291af00/
Changeset:   9f660291af00
Branch:      None
User:        jteh
Date:        2013-09-14 00:43:37
Summary:     Merge branch 't667' into next

Incubates #667.

Affected #:  1 file

diff --git a/source/gui/configProfiles.py b/source/gui/configProfiles.py
index 4f000ac..f8cf839 100644
--- a/source/gui/configProfiles.py
+++ b/source/gui/configProfiles.py
@@ -261,7 +261,10 @@ class TriggersDialog(wx.Dialog):
                                return
                        triggers["sayAll"] = self.profile
                elif not trigOnOther:
-                       del triggers["sayAll"]
+                       try:
+                               del triggers["sayAll"]
+                       except KeyError:
+                               pass
 
                self.Parent.Enable()
                self.Destroy()


https://bitbucket.org/nvdaaddonteam/nvda/commits/c9b1b235d492/
Changeset:   c9b1b235d492
Branch:      None
User:        jteh
Date:        2013-09-15 16:15:18
Summary:     Fix problems when attempting to move to columns in list views in 
NVDA's GUI.

The nvda app module was trying to use IAccessible properties without first 
checking whether it was dealing with an IAccessible object.
Fixes #3527.

Affected #:  1 file

diff --git a/source/appModules/nvda.py b/source/appModules/nvda.py
index 0fadb65..b1edb76 100755
--- a/source/appModules/nvda.py
+++ b/source/appModules/nvda.py
@@ -8,6 +8,7 @@ import appModuleHandler
 import api
 import controlTypes
 import versionInfo
+from NVDAObjects.IAccessible import IAccessible
 
 nvdaMenuIaIdentity = None
 
@@ -15,6 +16,8 @@ class AppModule(appModuleHandler.AppModule):
 
        def isNvdaMenu(self, obj):
                global nvdaMenuIaIdentity
+               if not isinstance(obj, IAccessible):
+                       return False
                if obj.IAccessibleIdentity == nvdaMenuIaIdentity:
                        return True
                if nvdaMenuIaIdentity is not True:


https://bitbucket.org/nvdaaddonteam/nvda/commits/f717c9b01783/
Changeset:   f717c9b01783
Branch:      None
User:        jteh
Date:        2013-09-15 16:16:50
Summary:     Merge branch 't3503' into next

Incubates #3503. Re #3527.

Affected #:  1 file

diff --git a/source/appModules/nvda.py b/source/appModules/nvda.py
index 0fadb65..b1edb76 100755
--- a/source/appModules/nvda.py
+++ b/source/appModules/nvda.py
@@ -8,6 +8,7 @@ import appModuleHandler
 import api
 import controlTypes
 import versionInfo
+from NVDAObjects.IAccessible import IAccessible
 
 nvdaMenuIaIdentity = None
 
@@ -15,6 +16,8 @@ class AppModule(appModuleHandler.AppModule):
 
        def isNvdaMenu(self, obj):
                global nvdaMenuIaIdentity
+               if not isinstance(obj, IAccessible):
+                       return False
                if obj.IAccessibleIdentity == nvdaMenuIaIdentity:
                        return True
                if nvdaMenuIaIdentity is not True:


https://bitbucket.org/nvdaaddonteam/nvda/commits/a66853216d95/
Changeset:   a66853216d95
Branch:      None
User:        jteh
Date:        2013-09-15 17:30:43
Summary:     If an unknow command line argument is passed to the NVDA 
distribution package, NVDA no longer displays error message dialogs endlessly.

This occurred because command line errors are signalled with exit code 2, but 
the same exit code was also being used to signal NVDA restarts to the launcher.
Now, restarts are signalled to the launcher using exit code 3.
Authors: Patrick ZAJDA <patrick@xxxxxxxx>, James Teh <jamie@xxxxxxxxxxxx>
Re #3463.

Affected #:  2 files

diff --git a/launcher/nvdaLauncher.nsi b/launcher/nvdaLauncher.nsi
index bea1fe3..4b7de09 100644
--- a/launcher/nvdaLauncher.nsi
+++ b/launcher/nvdaLauncher.nsi
@@ -82,8 +82,8 @@ ${GetParameters} $0
 Banner::destroy
 exec:
 execWait "$PLUGINSDIR\app\nvda_noUIAccess.exe $0 -r --launcher" $1
-;If exit code is 2 then execute again (restart)
-intcmp $1 2 exec +1
+;If exit code is 3 then execute again (restart)
+intcmp $1 3 exec +1
 SectionEnd
 
 var hmci

diff --git a/source/core.py b/source/core.py
index c179cc1..80b6b9e 100644
--- a/source/core.py
+++ b/source/core.py
@@ -46,7 +46,7 @@ def restart():
        """Restarts NVDA by starting a new copy with -r."""
        if globalVars.appArgs.launcher:
                import wx
-               globalVars.exitCode=2
+               globalVars.exitCode=3
                wx.GetApp().ExitMainLoop()
                return
        import subprocess


https://bitbucket.org/nvdaaddonteam/nvda/commits/c41367a2cc5a/
Changeset:   c41367a2cc5a
Branch:      None
User:        jteh
Date:        2013-09-15 17:34:57
Summary:     Merge branch 't3463' into next

Incubates #3463.

Affected #:  2 files

diff --git a/launcher/nvdaLauncher.nsi b/launcher/nvdaLauncher.nsi
index bea1fe3..4b7de09 100644
--- a/launcher/nvdaLauncher.nsi
+++ b/launcher/nvdaLauncher.nsi
@@ -82,8 +82,8 @@ ${GetParameters} $0
 Banner::destroy
 exec:
 execWait "$PLUGINSDIR\app\nvda_noUIAccess.exe $0 -r --launcher" $1
-;If exit code is 2 then execute again (restart)
-intcmp $1 2 exec +1
+;If exit code is 3 then execute again (restart)
+intcmp $1 3 exec +1
 SectionEnd
 
 var hmci

diff --git a/source/core.py b/source/core.py
index 937225c..6377487 100644
--- a/source/core.py
+++ b/source/core.py
@@ -55,7 +55,7 @@ def restart():
        """Restarts NVDA by starting a new copy with -r."""
        if globalVars.appArgs.launcher:
                import wx
-               globalVars.exitCode=2
+               globalVars.exitCode=3
                wx.GetApp().ExitMainLoop()
                return
        import subprocess


https://bitbucket.org/nvdaaddonteam/nvda/commits/07826b32b69e/
Changeset:   07826b32b69e
Branch:      None
User:        mdcurran
Date:        2013-09-16 01:26:33
Summary:     added translator comments for MS word revision types. Re #1670

Affected #:  1 file

diff --git a/source/NVDAObjects/window/winword.py 
b/source/NVDAObjects/window/winword.py
index 9106ae6..a2e7830 100755
--- a/source/NVDAObjects/window/winword.py
+++ b/source/NVDAObjects/window/winword.py
@@ -116,23 +116,41 @@ wdRevisionCellDeletion=17
 wdRevisionCellMerge=18
 
 wdRevisionTypeLabels={
+       # Translators: a Microsoft Word revision type (inserted content) 
        wdRevisionInsert:_("insertion"),
+       # Translators: a Microsoft Word revision type (deleted content) 
        wdRevisionDelete:_("deletion"),
+       # Translators: a Microsoft Word revision type (changed content 
property, e.g. font, color)
        wdRevisionProperty:_("property"),
+       # Translators: a Microsoft Word revision type (changed paragraph number)
        wdRevisionParagraphNumber:_("paragraph number"),
+       # Translators: a Microsoft Word revision type (display field)
        wdRevisionDisplayField:_("display field"),
+       # Translators: a Microsoft Word revision type (reconcile) 
        wdRevisionReconcile:_("reconcile"),
+       # Translators: a Microsoft Word revision type (conflicting revision)
        wdRevisionConflict:_("conflict"),
+       # Translators: a Microsoft Word revision type (style change)
        wdRevisionStyle:_("style"),
+       # Translators: a Microsoft Word revision type (replaced content) 
        wdRevisionReplace:_("replace"),
+       # Translators: a Microsoft Word revision type (changed paragraph 
property, e.g. alignment)
        wdRevisionParagraphProperty:_("paragraph property"),
+       # Translators: a Microsoft Word revision type (table)
        wdRevisionTableProperty:_("table property"),
+       # Translators: a Microsoft Word revision type (section property) 
        wdRevisionSectionProperty:_("section property"),
+       # Translators: a Microsoft Word revision type (style definition)
        wdRevisionStyleDefinition:_("style definition"),
+       # Translators: a Microsoft Word revision type (moved from)
        wdRevisionMovedFrom:_("moved from"),
+       # Translators: a Microsoft Word revision type (moved to)
        wdRevisionMovedTo:_("moved to"),
+       # Translators: a Microsoft Word revision type (inserted table cell)
        wdRevisionCellInsertion:_("cell insertion"),
+       # Translators: a Microsoft Word revision type (deleted table cell)
        wdRevisionCellDeletion:_("cell deletion"),
+       # Translators: a Microsoft Word revision type (merged table cells)
        wdRevisionCellMerge:_("cell merge"),
 }
 


https://bitbucket.org/nvdaaddonteam/nvda/commits/107b6c9e4ba9/
Changeset:   107b6c9e4ba9
Branch:      None
User:        mhameed
Date:        2013-09-16 01:34:49
Summary:     l10n

From translation svn rev:11460.

Affected #:  43 files

This diff is so big that we needed to truncate the remainder.

https://bitbucket.org/nvdaaddonteam/nvda/commits/b911f6553fef/
Changeset:   b911f6553fef
Branch:      None
User:        mhameed
Date:        2013-09-16 01:34:49
Summary:     Add new Polish translator to contributors.

Affected #:  1 file

diff --git a/contributors.txt b/contributors.txt
index d18fdd4..83bf595 100644
--- a/contributors.txt
+++ b/contributors.txt
@@ -134,3 +134,4 @@ Kevin Scannell
 Hamid Rezaey
 Bue Vester-Andersen
 Yogesh Kumar
+Grzegorz Zlotowicz


https://bitbucket.org/nvdaaddonteam/nvda/commits/5b906832d7d5/
Changeset:   5b906832d7d5
Branch:      None
User:        mhameed
Date:        2013-09-16 01:34:50
Summary:     add Farsi translator to contributors.

Affected #:  1 file

diff --git a/contributors.txt b/contributors.txt
index 83bf595..8e64bbc 100644
--- a/contributors.txt
+++ b/contributors.txt
@@ -131,6 +131,7 @@ Juan Pablo Martínez Cortés
 Marat Suleymanov
 Rui Fontes (Tiflotecnia, lda.)
 Kevin Scannell
+Ali Aslani
 Hamid Rezaey
 Bue Vester-Andersen
 Yogesh Kumar


https://bitbucket.org/nvdaaddonteam/nvda/commits/d4d3744bf628/
Changeset:   d4d3744bf628
Branch:      next
User:        mhameed
Date:        2013-09-16 01:59:46
Summary:     Merge branch 'master' into next

Affected #:  46 files

diff --git a/contributors.txt b/contributors.txt
index d18fdd4..8e64bbc 100644
--- a/contributors.txt
+++ b/contributors.txt
@@ -131,6 +131,8 @@ Juan Pablo Martínez Cortés
 Marat Suleymanov
 Rui Fontes (Tiflotecnia, lda.)
 Kevin Scannell
+Ali Aslani
 Hamid Rezaey
 Bue Vester-Andersen
 Yogesh Kumar
+Grzegorz Zlotowicz

diff --git a/source/NVDAObjects/window/winword.py 
b/source/NVDAObjects/window/winword.py
index 4f66617..b99c0b8 100755
--- a/source/NVDAObjects/window/winword.py
+++ b/source/NVDAObjects/window/winword.py
@@ -115,23 +115,41 @@ wdRevisionCellDeletion=17
 wdRevisionCellMerge=18
 
 wdRevisionTypeLabels={
+       # Translators: a Microsoft Word revision type (inserted content) 
        wdRevisionInsert:_("insertion"),
+       # Translators: a Microsoft Word revision type (deleted content) 
        wdRevisionDelete:_("deletion"),
+       # Translators: a Microsoft Word revision type (changed content 
property, e.g. font, color)
        wdRevisionProperty:_("property"),
+       # Translators: a Microsoft Word revision type (changed paragraph number)
        wdRevisionParagraphNumber:_("paragraph number"),
+       # Translators: a Microsoft Word revision type (display field)
        wdRevisionDisplayField:_("display field"),
+       # Translators: a Microsoft Word revision type (reconcile) 
        wdRevisionReconcile:_("reconcile"),
+       # Translators: a Microsoft Word revision type (conflicting revision)
        wdRevisionConflict:_("conflict"),
+       # Translators: a Microsoft Word revision type (style change)
        wdRevisionStyle:_("style"),
+       # Translators: a Microsoft Word revision type (replaced content) 
        wdRevisionReplace:_("replace"),
+       # Translators: a Microsoft Word revision type (changed paragraph 
property, e.g. alignment)
        wdRevisionParagraphProperty:_("paragraph property"),
+       # Translators: a Microsoft Word revision type (table)
        wdRevisionTableProperty:_("table property"),
+       # Translators: a Microsoft Word revision type (section property) 
        wdRevisionSectionProperty:_("section property"),
+       # Translators: a Microsoft Word revision type (style definition)
        wdRevisionStyleDefinition:_("style definition"),
+       # Translators: a Microsoft Word revision type (moved from)
        wdRevisionMovedFrom:_("moved from"),
+       # Translators: a Microsoft Word revision type (moved to)
        wdRevisionMovedTo:_("moved to"),
+       # Translators: a Microsoft Word revision type (inserted table cell)
        wdRevisionCellInsertion:_("cell insertion"),
+       # Translators: a Microsoft Word revision type (deleted table cell)
        wdRevisionCellDeletion:_("cell deletion"),
+       # Translators: a Microsoft Word revision type (merged table cells)
        wdRevisionCellMerge:_("cell merge"),
 }
 

This diff is so big that we needed to truncate the remainder.

Repository URL: https://bitbucket.org/nvdaaddonteam/nvda/

--

This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.

Other related posts:

  • » commit/nvda: 86 new changesets - commits-noreply