Index: test/keystrokes/gtk-demo/learn_mode.py
===================================================================
--- test/keystrokes/gtk-demo/learn_mode.py	(revision 3554)
+++ test/keystrokes/gtk-demo/learn_mode.py	(working copy)
@@ -57,9 +57,9 @@
 sequence.append(KeyComboAction("KP_5"))
 sequence.append(utils.AssertPresentationAction(
     "Orca command: flat review current word",
-    ["BRAILLE LINE:  'Speaks or spells the current flat review item or word.'",
-     "     VISIBLE:  'Speaks or spells the current fla', cursor=0",
-     "SPEECH OUTPUT: 'Speaks or spells the current flat review item or word.'"]))
+    ["BRAILLE LINE:  'Speaks the current flat review item or word.'",
+     "     VISIBLE:  'Speaks the current flat review i', cursor=0",
+     "SPEECH OUTPUT: 'Speaks the current flat review item or word.'"]))
 
 sequence.append(utils.StartRecordingAction())
 sequence.append(TypeAction("5"))
Index: test/keystrokes/gtk-demo/role_text_multiline_navigation.py
===================================================================
--- test/keystrokes/gtk-demo/role_text_multiline_navigation.py	(revision 3554)
+++ test/keystrokes/gtk-demo/role_text_multiline_navigation.py	(working copy)
@@ -729,6 +729,8 @@
      "     VISIBLE:  'This is only  $l', cursor=6",
      "SPEECH OUTPUT: 'is'"]))
 
+sequence.append(PauseAction(1000))
+
 sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("KP_5"))
 sequence.append(KeyComboAction("KP_5"))
@@ -742,6 +744,8 @@
      "SPEECH OUTPUT: 'i'",
      "SPEECH OUTPUT: 's'"]))
 
+sequence.append(PauseAction(1000))
+
 sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("KP_5"))
 sequence.append(KeyComboAction("KP_5"))
@@ -769,6 +773,8 @@
      "SPEECH OUTPUT: 'This is only ",
      "'"]))
 
+sequence.append(PauseAction(1000))
+
 sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("KP_8"))
 sequence.append(KeyComboAction("KP_8"))
@@ -796,6 +802,8 @@
      "SPEECH OUTPUT: '",
      "'"]))
 
+sequence.append(PauseAction(1000))
+
 sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("KP_8"))
 sequence.append(KeyComboAction("KP_8"))
@@ -849,34 +857,22 @@
      "     VISIBLE:  'This is only  $l', cursor=6",
      "SPEECH OUTPUT: 'i'"]))
 
-sequence.append(utils.StartRecordingAction())
-sequence.append(KeyComboAction("KP_2"))
-sequence.append(KeyComboAction("KP_2"))
-sequence.append(utils.AssertPresentationAction(
-    "KP_2 2X to spell 'i'",
-    ["BRAILLE LINE:  'This is only  $l'",
-     "     VISIBLE:  'This is only  $l', cursor=6",
-     "BRAILLE LINE:  'This is only  $l'",
-     "     VISIBLE:  'This is only  $l', cursor=6",
-     "SPEECH OUTPUT: 'i'",
-     "SPEECH OUTPUT: 'i'"]))
+sequence.append(PauseAction(1000))
 
 sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("KP_2"))
 sequence.append(KeyComboAction("KP_2"))
-sequence.append(KeyComboAction("KP_2"))
 sequence.append(utils.AssertPresentationAction(
-    "KP_2 3X to military spell 'i'",
+    "KP_2 2X to military spell 'i'",
     ["BRAILLE LINE:  'This is only  $l'",
      "     VISIBLE:  'This is only  $l', cursor=6",
      "BRAILLE LINE:  'This is only  $l'",
      "     VISIBLE:  'This is only  $l', cursor=6",
-     "BRAILLE LINE:  'This is only  $l'",
-     "     VISIBLE:  'This is only  $l', cursor=6",
      "SPEECH OUTPUT: 'i'",
-     "SPEECH OUTPUT: 'i'",
      "SPEECH OUTPUT: 'india'"]))
 
+sequence.append(PauseAction(1000))
+
 sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("KP_1"))
 sequence.append(utils.AssertPresentationAction(
Index: src/orca/scripts/StarOffice.py
===================================================================
--- src/orca/scripts/StarOffice.py	(revision 3554)
+++ src/orca/scripts/StarOffice.py	(working copy)
@@ -58,9 +58,9 @@
 
         where_am_I.WhereAmI.__init__(self, script)
 
-    def _speakTableCell(self, obj, doubleClick):
+    def _speakTableCell(self, obj, basicOnly):
         """Given the nature of OpenOffice Calc, Orca should override the
-        default KP_Enter behavior when the item with focus is a cell
+        default whereAmI behavior when the item with focus is a cell
         within Calc. In this instance, the following information should
         be spoken/displayed:
 
@@ -97,18 +97,18 @@
                       utterances)
         speech.speakUtterances(utterances)
 
-    def _speakParagraph(self, obj, doubleClick):
+    def _speakParagraph(self, obj, basicOnly):
         """OpenOffice Calc cells have the role "paragraph" when
         they are being edited.
         """
 
         top = self._script.getTopLevel(obj)
         if top and top.name.endswith(" Calc"):
-            self._speakCalc(obj, doubleClick)
+            self._speakCalc(obj, basicOnly)
         elif top and top.name.endswith(" Writer"):
-            self._speakText(obj, doubleClick)
+            self._speakText(obj, basicOnly)
 
-    def _speakCalc(self, obj, doubleClick):
+    def _speakCalc(self, obj, basicOnly):
         """Speak a OpenOffice Calc cell.
         """
 
@@ -118,7 +118,7 @@
         # No way to get cell coordinates?
 
         [textContents, startOffset, endOffset, selected] = \
-            self._getTextContents(obj, doubleClick)
+            self._getTextContents(obj, basicOnly)
         text = textContents
         utterances.append(text)
         if selected:
@@ -169,12 +169,10 @@
                       utterances)
         speech.speakUtterances(utterances)
 
-    def _speakTitleOrStatus(self, obj, doubleClick):
-        """Speak the title or status bar contents.
+    def speakTitle(self, obj):
+        """Speak the title bar.
 
-        Calc-Specific Handling: If pressed a single time while focus is
-        on a cell within OpenOffice Calc, Orca will speak the following
-        information:
+        Calc-Specific Handling: 
 
         1. The contents of the title bar of the application main window
         2. The title of the current worksheet
@@ -185,30 +183,46 @@
 
         top = self._script.getTopLevel(obj)
         if top and not top.name.endswith(" Calc"):
-            return where_am_I.WhereAmI._speakTitleOrStatus(self,
-                                                           obj,
-                                                           doubleClick)
+            return where_am_I.WhereAmI.speakTitle(self, obj)
+
         utterances = []
 
         mylist = self._getCalcFrameAndSheet(obj)
-        if doubleClick:
-            if mylist[0]:
-                self._statusBar = None
-                self._getStatusBar(mylist[0])
-                if self._statusBar:
-                    self._speakCalcStatusBar()
-        else:
-            if mylist[0]:
-                text = self.getObjLabelAndName(mylist[0])
-                utterances.append(text)
-            if mylist[1]:
-                text = self.getObjLabelAndName(mylist[1])
-                utterances.append(text)
+        if mylist[0]:
+            text = self.getObjLabelAndName(mylist[0])
+            utterances.append(text)
+        if mylist[1]:
+            text = self.getObjLabelAndName(mylist[1])
+            utterances.append(text)
 
-            debug.println(self._debugLevel,
-                          "Calc titlebar and sheet utterances=%s" % utterances)
-            speech.speakUtterances(utterances)
+        debug.println(self._debugLevel,
+                      "Calc titlebar and sheet utterances=%s" % utterances)
+        speech.speakUtterances(utterances)
 
+    def speakStatusBar(self, obj):
+        """Speak the status bar contents.
+
+        Note that if the application with focus is Calc, but a cell does not
+        have focus, the default behavior should be used.
+        """
+
+        top = self._script.getTopLevel(obj)
+        if top and not top.name.endswith(" Calc"):
+            return where_am_I.WhereAmI.speakStatusBar(self, obj)
+
+        utterances = []
+
+        mylist = self._getCalcFrameAndSheet(obj)
+        if mylist[0]:
+            self._statusBar = None
+            self._getStatusBar(mylist[0])
+            if self._statusBar:
+                self._speakCalcStatusBar()
+
+        debug.println(self._debugLevel,
+                      "Calc status bar utterances=%s" % utterances)
+        speech.speakUtterances(utterances)
+
 class BrailleGenerator(braillegenerator.BrailleGenerator):
     """Overrides _getBrailleRegionsForTableCellRow so that , when we are
     in a spread sheet, we can braille the dynamic row and column headers
@@ -773,11 +787,6 @@
         self.lastStartOff = -1
         self.lastEndOff = -1
 
-        # Used to determine if the user has double-clicked the dynamic
-        # row/column hotkeys.
-
-        self.lastDynamicEvent = None
-
         # Used to determine whether the caret has moved to a new paragraph.
         #
         self.currentParagraph = None
@@ -883,6 +892,14 @@
                 _("Set the row to use as dynamic column headers " \
                   "when speaking calc cells."))
 
+        self.inputEventHandlers["clearDynamicColumnHeadersHandler"] = \
+            input_event.InputEventHandler(
+                Script.clearDynamicColumnHeaders,
+                # Translators: Orca allows you to dynamically define which
+                # row of a spreadsheet or table counts as column headers.
+                #
+                _("Clears the dynamic column headers."))
+
         self.inputEventHandlers["setDynamicRowHeadersHandler"] = \
             input_event.InputEventHandler(
                 Script.setDynamicRowHeaders,
@@ -892,6 +909,14 @@
                 _("Set the column to use as dynamic row headers " \
                   "to use when speaking calc cells."))
 
+        self.inputEventHandlers["clearDynamicRowHeadersHandler"] = \
+            input_event.InputEventHandler(
+                Script.clearDynamicRowHeaders,
+                # Translators: Orca allows you to dynamically define which
+                # column of a spreadsheet or table counts as row headers.
+                #
+                _("Clears the dynamic row headers"))
+
     def getKeyBindings(self):
         """Defines the key bindings for this script. Setup the default
         key bindings, then add one in for reading the input line.
@@ -913,15 +938,33 @@
                 "r",
                 1 << settings.MODIFIER_ORCA,
                 1 << settings.MODIFIER_ORCA,
-                self.inputEventHandlers["setDynamicColumnHeadersHandler"]))
+                self.inputEventHandlers["setDynamicColumnHeadersHandler"],
+                1))
 
         keyBindings.add(
             keybindings.KeyBinding(
+                "r",
+                1 << settings.MODIFIER_ORCA,
+                1 << settings.MODIFIER_ORCA,
+                self.inputEventHandlers["clearDynamicColumnHeadersHandler"],
+                2))
+
+        keyBindings.add(
+            keybindings.KeyBinding(
                 "c",
                 1 << settings.MODIFIER_ORCA,
                 1 << settings.MODIFIER_ORCA,
-                self.inputEventHandlers["setDynamicRowHeadersHandler"]))
+                self.inputEventHandlers["setDynamicRowHeadersHandler"],
+                1))
 
+        keyBindings.add(
+            keybindings.KeyBinding(
+                "c",
+                1 << settings.MODIFIER_ORCA,
+                1 << settings.MODIFIER_ORCA,
+                self.inputEventHandlers["clearDynamicRowHeadersHandler"],
+                2))
+
         return keyBindings
 
     def getAppPreferencesGUI(self):
@@ -1409,40 +1452,48 @@
         Once the user has defined the row, it will be used to first speak
         this header when moving between columns.
 
-        A "double-click" of the Insert-c hotkey, will clear the dynamic
-        header column.
-
         Arguments:
         - inputEvent: if not None, the input event that caused this action.
         """
 
         debug.println(self.debugLevel, "StarOffice.setDynamicColumnHeaders.")
 
-        clickCount = self.getClickCount(self.lastDynamicEvent, inputEvent)
         table = self.getTable(orca_state.locusOfFocus)
         if table:
             row = self.getTableRow(orca_state.locusOfFocus)
-            if clickCount == 2:
-                try:
-                    del self.dynamicColumnHeaders[table]
-                    # Translators: Orca allows you to dynamically define which
-                    # row of a spreadsheet or table counts as column headers.
-                    #
-                    line = _("Dynamic column header cleared.")
-                    speech.stop()
-                    speech.speak(line)
-                    braille.displayMessage(line)
-                except:
-                    pass
-            else:
-                self.dynamicColumnHeaders[table] = row
+            self.dynamicColumnHeaders[table] = row
+            # Translators: Orca allows you to dynamically define which
+            # row of a spreadsheet or table counts as column headers.
+            #
+            line = _("Dynamic column header set for row %d") % (row+1)
+            speech.speak(line)
+            braille.displayMessage(line)
+
+        return True
+
+    def clearDynamicColumnHeaders(self, inputEvent):
+        """Clear the dynamic header column.
+
+        Arguments:
+        - inputEvent: if not None, the input event that caused this action.
+        """
+
+        debug.println(self.debugLevel, "StarOffice.clearDynamicColumnHeaders.")
+
+        table = self.getTable(orca_state.locusOfFocus)
+        if table:
+            row = self.getTableRow(orca_state.locusOfFocus)
+            try:
+                del self.dynamicColumnHeaders[table]
                 # Translators: Orca allows you to dynamically define which
                 # row of a spreadsheet or table counts as column headers.
                 #
-                line = _("Dynamic column header set for row ") + str(row+1)
+                line = _("Dynamic column header cleared.")
+                speech.stop()
                 speech.speak(line)
                 braille.displayMessage(line)
-        self.lastDynamicEvent = inputEvent
+            except:
+                pass
 
         return True
 
@@ -1477,43 +1528,50 @@
         Once the user has defined the column, it will be used to first speak
         this header when moving between rows.
 
-        A "double-click" of the Insert-r hotkey, will clear the dynamic
-        header row.
-
         Arguments:
         - inputEvent: if not None, the input event that caused this action.
         """
 
         debug.println(self.debugLevel, "StarOffice.setDynamicRowHeaders.")
 
-        clickCount = self.getClickCount(self.lastDynamicEvent, inputEvent)
         table = self.getTable(orca_state.locusOfFocus)
         if table:
             column = self.getTableColumn(orca_state.locusOfFocus)
-            if clickCount == 2:
-                try:
-                    del self.dynamicRowHeaders[table]
-                    # Translators: Orca allows you to dynamically define which
-                    # column of a spreadsheet or table counts as row headers.
-                    #
-                    line = _("Dynamic row header cleared.")
-                    speech.stop()
-                    speech.speak(line)
-                    braille.displayMessage(line)
-                except:
-                    pass
-            else:
-                self.dynamicRowHeaders[table] = column
+            self.dynamicRowHeaders[table] = column
+            # Translators: Orca allows you to dynamically define which
+            # column of a spreadsheet or table counts as row headers.
+            #
+            line = _("Dynamic row header set for column %s") \
+                   % self.columnConvert(column+1)
+            speech.speak(line)
+            braille.displayMessage(line)
+
+        return True
+
+    def clearDynamicRowHeaders(self, inputEvent):
+        """Clear the dynamic row headers.
+
+        Arguments:
+        - inputEvent: if not None, the input event that caused this action.
+        """
+
+        debug.println(self.debugLevel, "StarOffice.clearDynamicRowHeaders.")
+
+        table = self.getTable(orca_state.locusOfFocus)
+        if table:
+            column = self.getTableColumn(orca_state.locusOfFocus)
+            try:
+                del self.dynamicRowHeaders[table]
                 # Translators: Orca allows you to dynamically define which
                 # column of a spreadsheet or table counts as row headers.
                 #
-                line = _("Dynamic row header set for column %s") \
-                       % self.columnConvert(column+1)
+                line = _("Dynamic row header cleared.")
+                speech.stop()
                 speech.speak(line)
                 braille.displayMessage(line)
+            except:
+                pass
 
-        self.lastDynamicEvent = inputEvent
-
         return True
 
     def readMisspeltWord(self, event, pane):
Index: src/orca/flat_review.py
===================================================================
--- src/orca/flat_review.py	(revision 3554)
+++ src/orca/flat_review.py	(working copy)
@@ -702,7 +702,7 @@
                 zone = line.zones[zoneToCheck]
                 currentLineIndex = lineToCheck
                 currentZoneIndex = zoneToCheck
-                if caretOffset:
+                if caretOffset and zone.words:
                     currentWordIndex = len(zone.words) - 1
                     currentCharIndex = \
                           zone.words[currentWordIndex].length - 1
@@ -942,6 +942,22 @@
             textZones = self.splitTextIntoZones(
                 accessible, string, startOffset, cliprect)
 
+            # We need to account for the fact that newlines at the end of 
+            # text are treated as being on the same line when they in fact
+            # are a whole separate blank line.  So, we check for this and
+            # make up a new text zone for these cases.  See bug 434654.
+            #
+            if (endOffset == length) and (string[-1:] == "\n"):
+                [x, y, width, height] = text.getRangeExtents(startOffset, 
+                                                             endOffset,
+                                                             0)
+                if not textZones:
+                    textZones = []
+                textZones.append(TextZone(accessible,
+                                          endOffset,
+                                          "",
+                                          x, y + height, 0, height))
+
             if textZones:
                 zones.extend(textZones)
             elif len(zones):
Index: src/orca/orca_prefs.py
===================================================================
--- src/orca/orca_prefs.py	(revision 3554)
+++ src/orca/orca_prefs.py	(working copy)
@@ -31,8 +31,10 @@
 import settings
 
 # The same fields than in orca_gui_prefs.py:
-(HANDLER, DESCRIP, MOD_MASK1, MOD_USED1, KEY1, OLDTEXT1, TEXT1, \
- MOD_MASK2, MOD_USED2, KEY2, OLDTEXT2, TEXT2, MODIF, EDITABLE) = range(14)
+#
+(HANDLER, DESCRIP, MOD_MASK1, MOD_USED1, KEY1, CLICK_COUNT1, OLDTEXT1, \
+ TEXT1, MOD_MASK2, MOD_USED2, KEY2, CLICK_COUNT2, OLDTEXT2, TEXT2, MODIF, \
+ EDITABLE) = range(16)
 
 (ACTUAL, REPLACEMENT) = range(2)
 
@@ -502,8 +504,14 @@
             else:
                 prefs.writelines("      0,\n")
                 prefs.writelines("      0,\n")
-            prefs.writelines('      script.inputEventHandlers["' + \
-                             str(tupl[HANDLER]) +'"]))\n\n')
+            if (tupl[CLICK_COUNT1] == "1"):
+                prefs.writelines('      script.inputEventHandlers["' + \
+                                 str(tupl[HANDLER]) +'"]))\n\n')
+            else:
+                prefs.writelines('      script.inputEventHandlers["' + \
+                                 str(tupl[HANDLER]) +'"],\n')
+                prefs.writelines("      " + str(tupl[CLICK_COUNT1])  + \
+                                 "))\n\n")
 
         if (tupl[TEXT2]):
             prefs.writelines("   keyB.add(orca.keybindings.KeyBinding(\n")
@@ -514,8 +522,14 @@
             else:
                 prefs.writelines("      0,\n")
                 prefs.writelines("      0,\n")
-            prefs.writelines('      script.inputEventHandlers["' + \
-                             str(tupl[HANDLER]) +'"]))\n\n')
+            if (tupl[CLICK_COUNT2] == "1"):
+                prefs.writelines('      script.inputEventHandlers["' + \
+                                 str(tupl[HANDLER]) +'"]))\n\n')
+            else:
+                prefs.writelines('      script.inputEventHandlers["' + \
+                                 str(tupl[HANDLER]) +'"],\n')
+                prefs.writelines("      " + str(tupl[CLICK_COUNT2])  + \
+                                 "))\n\n")
 
     def _writeKeyBindingsPostamble(self, prefs):
         """Writes the postamble to the user-settings.py keyBindings section."""
@@ -536,7 +550,7 @@
             iterChild = treeModel.iter_children(thisIter)
             while iterChild != None:
                 values = treeModel.get(iterChild,
-                                       0,1,2,3,4,5,6,7,8,9,10,11,12,13)
+                                       0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15)
                 if values[MODIF]:
                     self._writeKeyBinding(prefs, values)
                 iterChild = treeModel.iter_next(iterChild)
Index: src/orca/default.py
===================================================================
--- src/orca/default.py	(revision 3554)
+++ src/orca/default.py	(working copy)
@@ -170,9 +170,9 @@
                 #
                 _("Speaks entire document."))
 
-        self.inputEventHandlers["whereAmIHandler"] = \
+        self.inputEventHandlers["whereAmIBasicHandler"] = \
             input_event.InputEventHandler(
-                Script.getWhereAmIInfo,
+                Script.whereAmIBasic,
                 # Translators: the "Where am I" feature of Orca allows
                 # a user to press a key and then have information
                 # about their current context spoken and brailled to
@@ -180,18 +180,36 @@
                 # name of the current pushbutton with focus as well as
                 # its mnemonic.
                 #
-                _("Performs the where am I operation."))
+                _("Performs the basic where am I operation."))
 
-        self.inputEventHandlers["getTitleOrStatusHandler"] = \
+        self.inputEventHandlers["whereAmIDetailedHandler"] = \
             input_event.InputEventHandler(
-                Script.getTitleOrStatus,
-                # Translators: Orca has a command that will speak
-                # a window's title if pressed once.  If pressed
-                # two times quickly the contents of the status bar
-                # will be spoken instead.
+                Script.whereAmIDetailed,
+                # Translators: the "Where am I" feature of Orca allows
+                # a user to press a key and then have information
+                # about their current context spoken and brailled to
+                # them.  For example, the information may include the
+                # name of the current pushbutton with focus as well as
+                # its mnemonic.
                 #
-                _("Speaks the title bar or status bar."))
+                _("Performs the detailed where am I operation."))
 
+        self.inputEventHandlers["getTitleHandler"] = \
+            input_event.InputEventHandler(
+                Script.getTitle,
+                # Translators: This command will cause the window's
+                # title to be spoken.
+                #
+                _("Speaks the title bar."))
+
+        self.inputEventHandlers["getStatusBarHandler"] = \
+            input_event.InputEventHandler(
+                Script.getStatusBar,
+                # Translators: This command will cause the window's
+                # status bar contents to be spoken.
+                #
+                _("Speaks the status bar."))
+
         self.inputEventHandlers["findHandler"] = \
             input_event.InputEventHandler(
                 orca.showFindGUI,
@@ -282,10 +300,42 @@
                 # buttons, labels, etc.) as a sequence of words in a
                 # sequence of lines.  The flat review feature allows
                 # the user to explore this text by the {previous,next}
-                # {line,word,character}.
+                # {line,word,character}.  This particular command will
+                # cause Orca to speak the current line.
                 #
                 _("Speaks the current flat review line."))
 
+        self.inputEventHandlers["reviewSpellCurrentLineHandler"] = \
+            input_event.InputEventHandler(
+                Script.reviewSpellCurrentLine,
+                # Translators: the 'flat review' feature of Orca
+                # allows the blind user to explore the text in a
+                # window in a 2D fashion.  That is, Orca treats all
+                # the text from all objects in a window (e.g.,
+                # buttons, labels, etc.) as a sequence of words in a
+                # sequence of lines.  The flat review feature allows
+                # the user to explore this text by the {previous,next}
+                # {line,word,character}. This particular command will
+                # cause Orca to spell the current line.
+                #
+                _("Spells the current flat review line."))
+
+        self.inputEventHandlers["reviewPhoneticCurrentLineHandler"] = \
+            input_event.InputEventHandler(
+                Script.reviewPhoneticCurrentLine,
+                # Translators: the 'flat review' feature of Orca
+                # allows the blind user to explore the text in a
+                # window in a 2D fashion.  That is, Orca treats all
+                # the text from all objects in a window (e.g.,
+                # buttons, labels, etc.) as a sequence of words in a
+                # sequence of lines.  The flat review feature allows
+                # the user to explore this text by the {previous,next}
+                # {line,word,character}. This particular command will
+                # cause Orca to "phonetically spell" the current line,
+                # saying "Alpha" for "a", "Bravo" for "b" and so on.
+                #
+                _("Phonetically spells the current flat review line."))
+
         self.inputEventHandlers["reviewNextLineHandler"] = \
             input_event.InputEventHandler(
                 Script.reviewNextLine,
@@ -357,12 +407,42 @@
                 # buttons, labels, etc.) as a sequence of words in a
                 # sequence of lines.  The flat review feature allows
                 # the user to explore this text by the {previous,next}
-                # {line,word,character}.  The 'speaks' means it will
-                # speak the word.  The 'spells' means it will spell
-                # out a word letter by letter.
+                # {line,word,character}.  This command will speak the
+                # current word or item.
                 #
-                _("Speaks or spells the current flat review item or word."))
+                _("Speaks the current flat review item or word."))
 
+        self.inputEventHandlers["reviewSpellCurrentItemHandler"] = \
+            input_event.InputEventHandler(
+                Script.reviewSpellCurrentItem,
+                # Translators: the 'flat review' feature of Orca
+                # allows the blind user to explore the text in a
+                # window in a 2D fashion.  That is, Orca treats all
+                # the text from all objects in a window (e.g.,
+                # buttons, labels, etc.) as a sequence of words in a
+                # sequence of lines.  The flat review feature allows
+                # the user to explore this text by the {previous,next}
+                # {line,word,character}.  This command will spell out
+                # the current word or item letter by letter.
+                #
+                _("Spells the current flat review item or word."))
+
+        self.inputEventHandlers["reviewPhoneticCurrentItemHandler"] = \
+            input_event.InputEventHandler(
+                Script.reviewPhoneticCurrentItem,
+                # Translators: the 'flat review' feature of Orca
+                # allows the blind user to explore the text in a
+                # window in a 2D fashion.  That is, Orca treats all
+                # the text from all objects in a window (e.g.,
+                # buttons, labels, etc.) as a sequence of words in a
+                # sequence of lines.  The flat review feature allows
+                # the user to explore this text by the {previous,next}
+                # {line,word,character}.  This command will spell out
+                # the current word or item phonetically, saying "Alpha"
+                # for "a", "Bravo" for "b" and so on.
+                #
+                _("Phonetically spells the current flat review item or word."))
+
         self.inputEventHandlers["reviewCurrentAccessibleHandler"] = \
             input_event.InputEventHandler(
                 Script.reviewCurrentAccessible,
@@ -460,6 +540,25 @@
                 #
                 _("Speaks the current flat review character."))
 
+        self.inputEventHandlers["reviewSpellCurrentCharacterHandler"] = \
+            input_event.InputEventHandler(
+                Script.reviewSpellCurrentCharacter,
+                # Translators: the 'flat review' feature of Orca
+                # allows the blind user to explore the text in a
+                # window in a 2D fashion.  That is, Orca treats all
+                # the text from all objects in a window (e.g.,
+                # buttons, labels, etc.) as a sequence of words in a
+                # sequence of lines.  The flat review feature allows
+                # the user to explore this text by the {previous,next}
+                # {line,word,character}.  Previous will go backwards
+                # in the window until you reach the top (i.e., it will
+                # wrap across lines if necessary).  This command will
+                # cause Orca to speak a phonetic representation of the
+                # character currently being reviewed, saying "Alpha"
+                # for "a", "Bravo" for "b" and so on.
+                #
+                _("Phonetically speaks the current flat review character."))
+
         self.inputEventHandlers["reviewNextCharacterHandler"] = \
             input_event.InputEventHandler(
                 Script.reviewNextCharacter,
@@ -894,17 +993,35 @@
                 "KP_Enter",
                 orcaModMask,
                 0,
-                self.inputEventHandlers["whereAmIHandler"]))
+                self.inputEventHandlers["whereAmIBasicHandler"],
+                1))
 
         keyBindings.add(
             keybindings.KeyBinding(
                 "KP_Enter",
                 orcaModMask,
+                0,
+                self.inputEventHandlers["whereAmIDetailedHandler"],
+                2))
+
+        keyBindings.add(
+            keybindings.KeyBinding(
+                "KP_Enter",
                 orcaModMask,
-                self.inputEventHandlers["getTitleOrStatusHandler"]))
+                orcaModMask,
+                self.inputEventHandlers["getTitleHandler"],
+                1))
 
         keyBindings.add(
             keybindings.KeyBinding(
+                "KP_Enter",
+                orcaModMask,
+                orcaModMask,
+                self.inputEventHandlers["getStatusBarHandler"],
+                2))
+
+        keyBindings.add(
+            keybindings.KeyBinding(
                 "KP_Delete",
                 orcaModMask,
                 0,
@@ -957,17 +1074,51 @@
                 "KP_8",
                 0,
                 0,
-                self.inputEventHandlers["reviewCurrentLineHandler"]))
+                self.inputEventHandlers["reviewCurrentLineHandler"],
+                1))
 
         keyBindings.add(
             keybindings.KeyBinding(
+                "KP_8",
+                0,
+                0,
+                self.inputEventHandlers["reviewSpellCurrentLineHandler"],
+                2))
+
+        keyBindings.add(
+            keybindings.KeyBinding(
+                "KP_8",
+                0,
+                0,
+                self.inputEventHandlers["reviewPhoneticCurrentLineHandler"],
+                3))
+
+        keyBindings.add(
+            keybindings.KeyBinding(
                 "KP_Up",
                 0,
                 0,
-                self.inputEventHandlers["reviewCurrentLineHandler"]))
+                self.inputEventHandlers["reviewCurrentLineHandler"],
+                1))
 
         keyBindings.add(
             keybindings.KeyBinding(
+                "KP_Up",
+                0,
+                0,
+                self.inputEventHandlers["reviewSpellCurrentLineHandler"],
+                2))
+
+        keyBindings.add(
+            keybindings.KeyBinding(
+                "KP_Up",
+                0,
+                0,
+                self.inputEventHandlers["reviewPhoneticCurrentLineHandler"],
+                3))
+
+        keyBindings.add(
+            keybindings.KeyBinding(
                 "KP_9",
                 orcaModMask,
                 0,
@@ -1027,17 +1178,51 @@
                 "KP_5",
                 orcaModMask,
                 0,
-                self.inputEventHandlers["reviewCurrentItemHandler"]))
+                self.inputEventHandlers["reviewCurrentItemHandler"],
+                1))
 
         keyBindings.add(
             keybindings.KeyBinding(
+                "KP_5",
+                orcaModMask,
+                0,
+                self.inputEventHandlers["reviewSpellCurrentItemHandler"],
+                2))
+
+        keyBindings.add(
+            keybindings.KeyBinding(
+                "KP_5",
+                orcaModMask,
+                0,
+                self.inputEventHandlers["reviewPhoneticCurrentItemHandler"],
+                3))
+
+        keyBindings.add(
+            keybindings.KeyBinding(
                 "KP_Begin",
                 orcaModMask,
                 0,
-                self.inputEventHandlers["reviewCurrentItemHandler"]))
+                self.inputEventHandlers["reviewCurrentItemHandler"],
+                1))
 
         keyBindings.add(
             keybindings.KeyBinding(
+                "KP_Begin",
+                orcaModMask,
+                0,
+                self.inputEventHandlers["reviewSpellCurrentItemHandler"],
+                2))
+
+        keyBindings.add(
+            keybindings.KeyBinding(
+                "KP_Begin",
+                orcaModMask,
+                0,
+                self.inputEventHandlers["reviewPhoneticCurrentItemHandler"],
+                3))
+
+        keyBindings.add(
+            keybindings.KeyBinding(
                 "KP_5",
                 orcaModMask,
                 orcaModMask,
@@ -1111,17 +1296,35 @@
                 "KP_2",
                 0,
                 0,
-                self.inputEventHandlers["reviewCurrentCharacterHandler"]))
+                self.inputEventHandlers["reviewCurrentCharacterHandler"],
+                1))
 
         keyBindings.add(
             keybindings.KeyBinding(
+                "KP_2",
+                0,
+                0,
+                self.inputEventHandlers["reviewSpellCurrentCharacterHandler"],
+                2))
+
+        keyBindings.add(
+            keybindings.KeyBinding(
                 "KP_Down",
                 0,
                 0,
-                self.inputEventHandlers["reviewCurrentCharacterHandler"]))
+                self.inputEventHandlers["reviewCurrentCharacterHandler"],
+                1))
 
         keyBindings.add(
             keybindings.KeyBinding(
+                "KP_Down",
+                0,
+                0,
+                self.inputEventHandlers["reviewSpellCurrentCharacterHandler"],
+                2))
+
+        keyBindings.add(
+            keybindings.KeyBinding(
                 "KP_3",
                 0,
                 0,
@@ -1179,17 +1382,35 @@
                 "Return",
                 orcaModMask,
                 orcaModMask,
-                self.inputEventHandlers["whereAmIHandler"]))
+                self.inputEventHandlers["whereAmIBasicHandler"],
+                1))
 
         keyBindings.add(
             keybindings.KeyBinding(
+                "Return",
+                orcaModMask,
+                orcaModMask,
+                self.inputEventHandlers["whereAmIDetailedHandler"],
+                2))
+
+        keyBindings.add(
+            keybindings.KeyBinding(
                 "slash",
                 orcaModMask,
                 orcaModMask,
-                self.inputEventHandlers["getTitleOrStatusHandler"]))
+                self.inputEventHandlers["getTitleHandler"],
+                1))
 
         keyBindings.add(
             keybindings.KeyBinding(
+                "slash",
+                orcaModMask,
+                orcaModMask,
+                self.inputEventHandlers["getStatusBarHandler"],
+                2))
+
+        keyBindings.add(
+            keybindings.KeyBinding(
                 "bracketleft",
                 orcaModMask,
                 orcaModMask,
@@ -1228,10 +1449,27 @@
                 "i",
                 orcaModMask,
                 orcaModMask,
-                self.inputEventHandlers["reviewCurrentLineHandler"]))
+                self.inputEventHandlers["reviewCurrentLineHandler"],
+                1))
 
         keyBindings.add(
             keybindings.KeyBinding(
+                "i",
+                orcaModMask,
+                orcaModMask,
+                self.inputEventHandlers["reviewSpellCurrentLineHandler"],
+                2))
+
+        keyBindings.add(
+            keybindings.KeyBinding(
+                "i",
+                orcaModMask,
+                orcaModMask,
+                self.inputEventHandlers["reviewPhoneticCurrentLineHandler"],
+                3))
+
+        keyBindings.add(
+            keybindings.KeyBinding(
                 "o",
                 orcaControlModMask,
                 orcaModMask,
@@ -1263,13 +1501,30 @@
                 "k",
                 orcaControlModMask,
                 orcaModMask,
-                self.inputEventHandlers["reviewCurrentItemHandler"]))
+                self.inputEventHandlers["reviewCurrentItemHandler"],
+                1))
 
         keyBindings.add(
             keybindings.KeyBinding(
                 "k",
                 orcaControlModMask,
+                orcaModMask,
+                self.inputEventHandlers["reviewSpellCurrentItemHandler"],
+                2))
+
+        keyBindings.add(
+            keybindings.KeyBinding(
+                "k",
                 orcaControlModMask,
+                orcaModMask,
+                self.inputEventHandlers["reviewPhoneticCurrentItemHandler"],
+                3))
+
+        keyBindings.add(
+            keybindings.KeyBinding(
+                "k",
+                orcaControlModMask,
+                orcaControlModMask,
                 self.inputEventHandlers["reviewCurrentAccessibleHandler"]))
 
         keyBindings.add(
@@ -1305,10 +1560,19 @@
                 "comma",
                 orcaModMask,
                 orcaModMask,
-                self.inputEventHandlers["reviewCurrentCharacterHandler"]))
+                self.inputEventHandlers["reviewCurrentCharacterHandler"],
+                1))
 
         keyBindings.add(
             keybindings.KeyBinding(
+                "comma",
+                orcaModMask,
+                orcaModMask,
+                self.inputEventHandlers["reviewSpellCurrentCharacterHandler"],
+                2))
+
+        keyBindings.add(
+            keybindings.KeyBinding(
                 "period",
                 orcaModMask,
                 orcaModMask,
@@ -1675,7 +1939,7 @@
 
 
     def sayAll(self, inputEvent):
-        clickCount = self.getClickCount(self.lastSayAllEvent, inputEvent)
+        clickCount = self.getClickCount()
         doubleClick = clickCount == 2
         self.lastSayAllEvent = inputEvent
 
@@ -2077,36 +2341,49 @@
             braille.displayMessage(text)
             speech.speak(text)
 
-    def doWhereAmI(self, inputEvent, statusOrTitle):
+    def doWhereAmI(self, inputEvent, basicOnly):
         """Peforms the whereAmI operation.
 
         Arguments:
         - inputEvent:     The original inputEvent
-        - titleOrStatus:  If true, the user is interested in the window's
-                          title or status bar.
         """
 
         obj = orca_state.locusOfFocus
         self.updateBraille(obj)
 
-        clickCount = self.getClickCount(self.lastWhereAmIEvent, inputEvent)
-        doubleClick = clickCount == 2
-        self.lastWhereAmIEvent = inputEvent
+        return self.whereAmI.whereAmI(obj, basicOnly)
 
-        return self.whereAmI.whereAmI(obj, doubleClick, statusOrTitle)
+    def whereAmIBasic(self, inputEvent):
+        """Speaks basic information about the current object of interest.
+        """
 
-    def getWhereAmIInfo(self, inputEvent):
-        """Speaks information about the current object of interest."""
+        self.doWhereAmI(inputEvent, True)
 
+    def whereAmIDetailed(self, inputEvent):
+        """Speaks detailed/custom information about the current object of
+        interest.
+        """
+
         self.doWhereAmI(inputEvent, False)
 
-    def getTitleOrStatus(self, inputEvent):
-        """Speaks the window title for a single click; speaks the contents
-        of the status bar for a double click.
+    def getTitle(self, inputEvent):
+        """Speaks the title of the window with focus.
         """
 
-        self.doWhereAmI(inputEvent, True)
+        obj = orca_state.locusOfFocus
+        self.updateBraille(obj)
 
+        return self.whereAmI.speakTitle(orca_state.locusOfFocus)
+
+    def getStatusBar(self, inputEvent):
+        """Speaks the contents of the status bar of the window with focus.
+        """
+
+        obj = orca_state.locusOfFocus
+        self.updateBraille(obj)
+
+        return self.whereAmI.speakStatusBar(orca_state.locusOfFocus)
+
     def findCommonAncestor(self, a, b):
         """Finds the common ancestor between Accessible a and Accessible b.
 
@@ -3968,21 +4245,36 @@
         return True
 
     def reviewCurrentLine(self, inputEvent):
-        """Presents the current flat review line via braille and speech."""
+        """Brailles and speaks the current flat review line."""
 
-        clickCount = self.getClickCount(self.lastReviewCurrentEvent,
-                                           inputEvent)
-        self._reviewCurrentLine(inputEvent, clickCount)
+        self._reviewCurrentLine(inputEvent, 1)
         self.lastReviewCurrentEvent = inputEvent
 
         return True
 
-    def _reviewCurrentLine(self, inputEvent, clickCount=1):
+    def reviewSpellCurrentLine(self, inputEvent):
+        """Brailles and spells the current flat review line."""
+
+        self._reviewCurrentLine(inputEvent, 2)
+        self.lastReviewCurrentEvent = inputEvent
+
+        return True
+
+    def reviewPhoneticCurrentLine(self, inputEvent):
+        """Brailles and phonetically spells the current flat review line."""
+
+        self._reviewCurrentLine(inputEvent, 3)
+        self.lastReviewCurrentEvent = inputEvent
+
+        return True
+
+    def _reviewCurrentLine(self, inputEvent, speechType=1):
         """Presents the current flat review line via braille and speech.
 
         Arguments:
         - inputEvent - the current input event.
-        - clickCount - number of times the user has "clicked" the current key.
+        - speechType - the desired presentation: speak (1), spell (2), or
+                       phonetic (3)
         """
 
         context = self.getFlatReviewContext()
@@ -4007,11 +4299,11 @@
                 # user has navigated to a line with only whitespace on it.
                 #
                 speech.speak(_("white space"))
-            elif lineString.isupper() and (clickCount < 2 or clickCount > 3):
+            elif lineString.isupper() and (speechType < 2 or speechType > 3):
                 speech.speak(lineString, self.voices[settings.UPPERCASE_VOICE])
-            elif clickCount == 2:
+            elif speechType == 2:
                 self.spellCurrentItem(lineString)
-            elif clickCount == 3:
+            elif speechType == 3:
                 self.phoneticSpellCurrentItem(lineString)
             else:
                 lineString = self.adjustForRepeats(lineString)
@@ -4093,18 +4385,29 @@
         return True
 
     def reviewCurrentItem(self, inputEvent, targetCursorCell=0):
-        """Speak/Braille the current item to the user. A "double-click"
-        of this key will cause the word to be spelt. A "triple-click"
-        will cause the word to be phonetically spelt.
-        """
+        """Brailles and speaks the current item to the user."""
 
-        clickCount = self.getClickCount(self.lastReviewCurrentEvent,
-                                           inputEvent)
-        self._reviewCurrentItem(inputEvent, targetCursorCell, clickCount)
+        self._reviewCurrentItem(inputEvent, targetCursorCell, 1)
         self.lastReviewCurrentEvent = inputEvent
 
         return True
 
+    def reviewSpellCurrentItem(self, inputEvent, targetCursorCell=0):
+        """Brailles and spells the current item to the user."""
+
+        self._reviewCurrentItem(inputEvent, targetCursorCell, 2)
+        self.lastReviewCurrentEvent = inputEvent
+
+        return True
+
+    def reviewPhoneticCurrentItem(self, inputEvent, targetCursorCell=0):
+        """Brailles and phonetically spells the current item to the user."""
+
+        self._reviewCurrentItem(inputEvent, targetCursorCell, 3)
+        self.lastReviewCurrentEvent = inputEvent
+
+        return True
+
     def spellCurrentItem(self, itemString):
         """Spell the current flat review word or line.
 
@@ -4120,13 +4423,14 @@
                 speech.speak(character.encode("UTF-8"))
 
     def _reviewCurrentItem(self, inputEvent, targetCursorCell=0,
-                           clickCount=1):
+                           speechType=1):
         """Presents the current item to the user.
 
         Arguments:
         - inputEvent - the current input event.
         - targetCursorCell - if non-zero, the target braille cursor cell.
-        - clickCount - number of times the user has "clicked" the current key.
+        - speechType - the desired presentation: speak (1), spell (2), or
+                       phonetic (3).
         """
 
         context = self.getFlatReviewContext()
@@ -4158,15 +4462,14 @@
                     # user has navigated to a line with only whitespace on it.
                     #
                     speech.speak(_("white space"))
-                elif wordString.isupper() \
-                     and (clickCount < 2 or clickCount > 3):
+                elif wordString.isupper() and speechType == 1:
                     speech.speak(wordString,
                                  self.voices[settings.UPPERCASE_VOICE])
-                elif clickCount == 2:
+                elif speechType == 2:
                     self.spellCurrentItem(wordString)
-                elif clickCount == 3:
+                elif speechType == 3:
                     self.phoneticSpellCurrentItem(wordString)
-                else:
+                elif speechType == 1:
                     wordString = self.adjustForRepeats(wordString)
                     speech.speak(wordString)
 
@@ -4221,22 +4524,29 @@
         return True
 
     def reviewCurrentCharacter(self, inputEvent):
-        """Presents the current flat review character via braille and speech.
+        """Brailles and speaks the current flat review character."""
+
+        self._reviewCurrentCharacter(inputEvent, 1)
+        self.lastReviewCurrentEvent = inputEvent
+
+        return True
+
+    def reviewSpellCurrentCharacter(self, inputEvent):
+        """Brailles and 'spells' (phonetically) the current flat review
+        character.
         """
 
-        clickCount = self.getClickCount(self.lastReviewCurrentEvent,
-                                           inputEvent)
-        self._reviewCurrentCharacter(inputEvent, clickCount)
+        self._reviewCurrentCharacter(inputEvent, 2)
         self.lastReviewCurrentEvent = inputEvent
 
         return True
 
-    def _reviewCurrentCharacter(self, inputEvent, clickCount=1):
+    def _reviewCurrentCharacter(self, inputEvent, speechType=1):
         """Presents the current flat review character via braille and speech.
 
         Arguments:
         - inputEvent - the current input event.
-        - clickCount - number of times the user has "clicked" the current key.
+        - speechType - the desired presentation: speak (1) or phonetic (2)
         """
 
         context = self.getFlatReviewContext()
@@ -4262,9 +4572,7 @@
                     # user has navigated to an empty line.
                     #
                     speech.speak(_("blank"))
-                elif clickCount == 2:
-                    self.spellCurrentItem(charString)
-                elif clickCount == 3:
+                elif speechType == 2:
                     self.phoneticSpellCurrentItem(charString)
                 elif charString.decode("UTF-8").isupper():
                     speech.speakCharacter(charString,
@@ -5134,35 +5442,6 @@
         else:
             return obj
 
-    def getClickCount(self, lastInputEvent, inputEvent):
-        """Return the count of the number of clicks a user has made to one
-        of the keys on the keyboard.
-
-        Arguments:
-        - lastInputEvent: the last input event.
-        - inputEvent: the current input event.
-        """
-
-        if not isinstance(inputEvent, input_event.KeyboardEvent):
-            orca_state.clickCount = 0
-            return orca_state.clickCount
-
-        if not isinstance(lastInputEvent, input_event.KeyboardEvent):
-            orca_state.clickCount = 1
-            return orca_state.clickCount
-
-        if (lastInputEvent.hw_code != inputEvent.hw_code) or \
-           (lastInputEvent.modifiers != inputEvent.modifiers):
-            orca_state.clickCount = 1
-            return orca_state.clickCount
-
-        if (inputEvent.time - lastInputEvent.time) < \
-                settings.doubleClickTimeout:
-            orca_state.clickCount += 1
-        else:
-            orca_state.clickCount = 1
-        return orca_state.clickCount
-
     def isDesiredFocusedItem(self, obj, rolesList):
         """Called to determine if the given object and it's hierarchy of
            parent objects, each have the desired roles.
Index: src/orca/orca.py
===================================================================
--- src/orca/orca.py	(revision 3554)
+++ src/orca/orca.py	(working copy)
@@ -553,6 +553,34 @@
 
         speech.speakKeyEvent(event_string, eventType)
 
+def _setClickCount(inputEvent):
+    """Sets the count of the number of clicks a user has made to one
+    of the non-modifier keys on the keyboard.
+
+    Arguments:
+    - inputEvent: the current input event.
+    """
+
+    lastInputEvent = orca_state.lastNonModifierKeyEvent
+
+    if inputEvent.type == pyatspi.KEY_RELEASED_EVENT:
+        pass
+    elif not isinstance(inputEvent, KeyboardEvent):
+        orca_state.clickCount = 0
+    elif not isinstance(lastInputEvent, KeyboardEvent):
+        orca_state.clickCount = 1
+    elif (lastInputEvent.hw_code != inputEvent.hw_code) or \
+         (lastInputEvent.modifiers != inputEvent.modifiers):
+        orca_state.clickCount = 1
+    elif (inputEvent.time - lastInputEvent.time) < \
+           settings.doubleClickTimeout:
+        # Cap the possible number of clicks at 3.
+        #
+        if orca_state.clickCount < 3:
+            orca_state.clickCount += 1
+    else:
+        orca_state.clickCount = 1
+
 def _processKeyCaptured(event):
     """Called when a new key event arrives and orca_state.capturingKeys=True.
     (used for key bindings redefinition)
@@ -563,17 +591,6 @@
            or isLockingKey(event.event_string):
             return True
         else:
-            # We want to capture this event, after first doing a little
-            # clean up.  Eliminate modifiers we're not interested in.
-            #
-            mask = (1 << settings.MODIFIER_ORCA |
-                    1 << pyatspi.MODIFIER_ALT |
-                    1 << pyatspi.MODIFIER_SHIFT |
-                    1 << pyatspi.MODIFIER_CONTROL |
-                    1 << pyatspi.MODIFIER_META2 |
-                    1 << pyatspi.MODIFIER_META3)
-            event.modifiers = event.modifiers & mask
-
             # We want the keyname rather than the printable character.
             # If it's not on the keypad, get the name of the unshifted
             # character. (i.e. "1" instead of "!")
@@ -581,7 +598,8 @@
             keymap = gtk.gdk.keymap_get_default()
             entries = keymap.get_entries_for_keycode(event.hw_code)
             event.event_string = gtk.gdk.keyval_name(entries[0][0])
-            if event.event_string.startswith("KP"):
+            if event.event_string.startswith("KP") and \
+               event.event_string != "KP_Enter":
                 name = gtk.gdk.keyval_name(entries[1][0])
                 if name.startswith("KP"):
                     event.event_string = name
@@ -679,6 +697,19 @@
         keyboardEvent.modifiers = keyboardEvent.modifiers \
                                   | (1 << settings.MODIFIER_ORCA)
 
+    orca_state.lastInputEvent = keyboardEvent
+
+    # If this is a key event for a non-modifier key, save a handle to it.
+    # This is needed to help determine user actions when a multi-key chord
+    # has been pressed, and we might get the key events in different orders.
+    # See comment #15 of bug #435201 for more details.  We also want to
+    # store the "click count" for the purpose of supporting keybindings
+    # with unique behaviors when double- or triple-clicked.
+    #
+    if not isModifierKey(keyboardEvent.event_string):
+        _setClickCount(keyboardEvent)
+        orca_state.lastNonModifierKeyEvent = keyboardEvent
+
     # Orca gets first stab at the event.  Then, the presenter gets
     # a shot. [[[TODO: WDW - might want to let the presenter try first?
     # The main reason this is staying as is is that we may not want
@@ -694,12 +725,10 @@
                 consumed = _PRESENTATION_MANAGERS[_currentPresentationManager].\
                            processKeyboardEvent(keyboardEvent)
             if (not consumed) and settings.learnModeEnabled:
-                if keyboardEvent.type \
-                    == pyatspi.KEY_PRESSED_EVENT:
-                    clickCount = orca_state.activeScript.getClickCount(\
-                                                orca_state.lastInputEvent,
-                                                keyboardEvent)
-                    if clickCount == 2:
+                if keyboardEvent.type == pyatspi.KEY_PRESSED_EVENT:
+                    clickCount = orca_state.activeScript.getClickCount()
+                    if isPrintableKey(keyboardEvent.event_string) \
+                       and clickCount == 2:
                         orca_state.activeScript.phoneticSpellCurrentItem(\
                             keyboardEvent.event_string)
                     else:
@@ -714,16 +743,6 @@
     except:
         debug.printException(debug.LEVEL_SEVERE)
 
-    orca_state.lastInputEvent = keyboardEvent
-
-    # If this is a key event for a non-modifier key, save a handle to it.
-    # This is needed to help determine user actions when a multi-key chord
-    # has been pressed, and we might get the key events in different orders.
-    # See comment #15 of bug #435201 for more details.
-    #
-    if not isModifierKey(keyboardEvent.event_string):
-        orca_state.lastNonModifierKeyEvent = keyboardEvent
-
     return consumed or isOrcaModifier
 
 ########################################################################
Index: src/orca/Gecko.py
===================================================================
--- src/orca/Gecko.py	(revision 3554)
+++ src/orca/Gecko.py	(working copy)
@@ -28,7 +28,7 @@
 # 
 # pylint: disable-msg=E1103
 
-"""Custom script for Gecko toolkit.  NOT WORKING WELL AT THE MOMENT.
+"""Custom script for Gecko toolkit.
 Please refer to the following URL for more information on the AT-SPI
 implementation in Gecko:
 http://developer.mozilla.org/en/docs/Accessibility/ATSPI_Support
@@ -1389,14 +1389,13 @@
         where_am_I.WhereAmI.__init__(self, script)
         self._script = script
         
-    def whereAmI(self, obj, doubleClick, statusOrTitle):
-        """Calls the base class method for single clicks and Gecko specific
-        information presentation methods for double clicks
+    def whereAmI(self, obj, basicOnly):
+        """Calls the base class method for basic information and Gecko
+        specific presentation methods for detailed/custom information.
         """
 
-        if not doubleClick or statusOrTitle \
-           or not self._script.inDocumentContent(obj):
-            where_am_I.WhereAmI.whereAmI(self, obj, doubleClick, statusOrTitle)
+        if basicOnly or not self._script.inDocumentContent(obj):
+            where_am_I.WhereAmI.whereAmI(self, obj, basicOnly)
             self._script.liveMngr.outputLiveRegionDescription(obj)
         else:
             try:
@@ -1806,12 +1805,8 @@
 ########################################################################
 
 class Script(default.Script):
-    """The script for Firefox.
+    """The script for Firefox."""
 
-    NOTE: THIS IS UNDER DEVELOPMENT AND DOES NOT PROVIDE ANY COMPELLING
-    ACCESS TO FIREFOX AT THIS POINT.
-    """
-
     ####################################################################
     #                                                                  #
     # Overridden Script Methods                                        #
Index: src/orca/where_am_I.py
===================================================================
--- src/orca/where_am_I.py	(revision 3554)
+++ src/orca/where_am_I.py	(working copy)
@@ -50,7 +50,7 @@
         self._defaultButton = None
         self._lastAttributeString = ""
 
-    def whereAmI(self, obj, doubleClick, titleOrStatus):
+    def whereAmI(self, obj, basicOnly):
         """Speaks information about the current object of interest, including
         the object itself, which window it is in, which application, which
         workspace, etc.
@@ -74,8 +74,7 @@
            \n  parent label= %s \
            \n  parent name=%s \
            \n  parent role=%s \
-           \n  double-click=%s \
-           \n  titleOrStatus=%s" % \
+           \n  basicOnly=%s" % \
             (self._getObjLabel(obj),
              self._getObjName(obj),
              obj.getRoleName(),
@@ -83,78 +82,72 @@
              self._getObjLabel(obj.parent),
              self._getObjName(obj.parent),
              obj.parent.getRoleName(),
-             doubleClick,
-             titleOrStatus))
+             basicOnly))
 
         role = obj.getRole()
 
-        if titleOrStatus:
-            self._speakTitleOrStatus(obj, doubleClick)
-            return True
+        toolbar = self._getToolbar(obj)
+        if toolbar:
+            self._speakToolbar(toolbar)
 
-        else:
-            toolbar = self._getToolbar(obj)
-            if toolbar:
-                self._speakToolbar(toolbar)
-
         if role == pyatspi.ROLE_CHECK_BOX:
-            self._speakCheckBox(obj, doubleClick)
+            self._speakCheckBox(obj, basicOnly)
 
         elif role == pyatspi.ROLE_RADIO_BUTTON:
-            self._speakRadioButton(obj, doubleClick)
+            self._speakRadioButton(obj, basicOnly)
 
         elif role == pyatspi.ROLE_COMBO_BOX:
-            self._speakComboBox(obj, doubleClick)
+            self._speakComboBox(obj, basicOnly)
 
         elif role == pyatspi.ROLE_SPIN_BUTTON:
-            self._speakSpinButton(obj, doubleClick)
+            self._speakSpinButton(obj, basicOnly)
 
         elif role == pyatspi.ROLE_PUSH_BUTTON:
-            self._speakPushButton(obj, doubleClick)
+            self._speakPushButton(obj, basicOnly)
 
         elif role == pyatspi.ROLE_SLIDER:
-            self._speakSlider(obj, doubleClick)
+            self._speakSlider(obj, basicOnly)
 
         elif role in [pyatspi.ROLE_MENU,
                       pyatspi.ROLE_MENU_ITEM,
                       pyatspi.ROLE_CHECK_MENU_ITEM,
                       pyatspi.ROLE_RADIO_MENU_ITEM]:
-            self._speakMenuItem(obj, doubleClick)
+            self._speakMenuItem(obj, basicOnly)
 
         elif role == pyatspi.ROLE_PAGE_TAB:
-            self._speakPageTab(obj, doubleClick)
+            self._speakPageTab(obj, basicOnly)
 
         elif role in [pyatspi.ROLE_TEXT,
                       pyatspi.ROLE_TERMINAL]:
-            self._speakText(obj, doubleClick)
+            self._speakText(obj, basicOnly)
 
         elif role == pyatspi.ROLE_TABLE_CELL:
-            self._speakTableCell(obj, doubleClick)
+            self._speakTableCell(obj, basicOnly)
 
         elif role == pyatspi.ROLE_LIST_ITEM:
-            self._speakListItem(obj, doubleClick)
+            self._speakListItem(obj, basicOnly)
 
         elif role == pyatspi.ROLE_PARAGRAPH:
-            self._speakParagraph(obj, doubleClick)
+            self._speakParagraph(obj, basicOnly)
 
         elif role == pyatspi.ROLE_ICON:
-            self._speakIconPanel(obj, doubleClick)
+            self._speakIconPanel(obj, basicOnly)
             
         elif role == pyatspi.ROLE_LINK:
-            self._speakLink(obj, doubleClick)
+            self._speakLink(obj, basicOnly)
 
         elif role == pyatspi.ROLE_TOGGLE_BUTTON:
-            self._speakToggleButton(obj, doubleClick)
+            self._speakToggleButton(obj, basicOnly)
 
         else:
-            self._speakGenericObject(obj, doubleClick)
+            self._speakGenericObject(obj, basicOnly)
 
-        if not doubleClick:
+        if basicOnly:
             self._speakObjDescription(obj)
 
         return True
 
-    def _speakCheckBox(self, obj, doubleClick):
+    def _speakCheckBox(self, obj, basicOnly):
         """Checkboxes present the following information
         (an example is 'Enable speech, checkbox checked, Alt E'):
         1. label
@@ -176,7 +169,7 @@
                       % utterances)
         speech.speakUtterances(utterances)
 
-    def _speakRadioButton(self, obj, doubleClick):
+    def _speakRadioButton(self, obj, basicOnly):
         """Radio Buttons present the following information (an example is
         'Punctuation Level, Some, Radio button, selected, item 2 of 4, Alt M'):
 
@@ -224,7 +217,7 @@
                       utterances)
         speech.speakUtterances(utterances)
 
-    def _speakComboBox(self, obj, doubleClick):
+    def _speakComboBox(self, obj, basicOnly):
         """Comboboxes present the following information (an example is
         'Speech system: combo box, GNOME Speech Services, item 1 of 1,
         Alt S'):
@@ -256,7 +249,7 @@
                       utterances)
         speech.speakUtterances(utterances)
 
-    def _speakSpinButton(self, obj, doubleClick):
+    def _speakSpinButton(self, obj, basicOnly):
         """Spin Buttons present the following information (an example is
         'Scale factor: spin button, 4.00, Alt F'):
 
@@ -283,7 +276,7 @@
                       utterances)
         speech.speakUtterances(utterances)
 
-    def _speakPushButton(self, obj, doubleClick):
+    def _speakPushButton(self, obj, basicOnly):
         """ Push Buttons present the following information (an example is
         'Apply button, Alt A'):
 
@@ -306,7 +299,7 @@
                       utterances)
         speech.speakUtterances(utterances)
 
-    def _speakSlider(self, obj, doubleClick):
+    def _speakSlider(self, obj, basicOnly):
         """Sliders present the following information (examples include
         'Pitch slider, 5.0, 56%'; 'Volume slider, 9.0, 100%'):
 
@@ -337,7 +330,7 @@
                       utterances)
         speech.speakUtterances(utterances)
 
-    def _speakMenuItem(self, obj, doubleClick):
+    def _speakMenuItem(self, obj, basicOnly):
         """Menu items present the following information (examples include
         'File menu, Open..., Control + O, item 2 of 20, O', 'File menu,
         Wizards Menu, item 4 of 20, W'):
@@ -408,7 +401,7 @@
                       utterances)
         speech.speakUtterances(utterances)
 
-    def _speakPageTab(self, obj, doubleClick):
+    def _speakPageTab(self, obj, basicOnly):
         """Tabs in a Tab List present the following information (an example
         is 'Tab list, braille page, item 2 of 5'):
 
@@ -438,7 +431,7 @@
                       utterances)
         speech.speakUtterances(utterances)
 
-    def _speakText(self, obj, doubleClick):
+    def _speakText(self, obj, basicOnly):
         """Text boxes present the following information (an example is
         'Source display: text, blank, Alt O'):
 
@@ -463,8 +456,8 @@
         utterances.append(text)
 
         [textContents, startOffset, endOffset, selected] = \
-                       self._getTextContents(obj, doubleClick)
-        if doubleClick:
+                       self._getTextContents(obj, basicOnly)
+        if not basicOnly:
             # Speak character attributes.
             textContents = \
                 self._insertAttributes(obj, startOffset,
@@ -478,7 +471,7 @@
                       utterances)
         speech.speakUtterances(utterances)
 
-        if doubleClick:
+        if not basicOnly:
             settings.verbalizePunctuationStyle = savedStyle
 
         utterances = []
@@ -498,7 +491,7 @@
                       utterances)
         speech.speakUtterances(utterances)
 
-    def _speakTableCell(self, obj, doubleClick):
+    def _speakTableCell(self, obj, basicOnly):
         """Tree Tables present the following information (an example is
         'Tree table, Mike Pedersen, item 8 of 10, tree level 2'):
 
@@ -528,7 +521,7 @@
         speech.speakUtterances(utterances)
 
         utterances = []
-        if doubleClick:
+        if not basicOnly:
             table = parent.queryTable()
             row = table.getRowAtIndex(
               orca_state.locusOfFocus.getIndexInParent())
@@ -547,7 +540,7 @@
         # Speak the remaining items.
         utterances = []
 
-        if not doubleClick:
+        if basicOnly:
             try:
                 table = parent.queryTable()
             except NotImplementedError:
@@ -590,7 +583,7 @@
                       utterances)
         speech.speakUtterances(utterances)
 
-    def _speakListItem(self, obj, doubleClick):
+    def _speakListItem(self, obj, basicOnly):
         """List items should be treated like tree cells:
 
         1. label, if any
@@ -651,12 +644,12 @@
                       utterances)
         speech.speakUtterances(utterances)
 
-    def _speakParagraph(self, obj, doubleClick):
+    def _speakParagraph(self, obj, basicOnly):
         """Speak a paragraph object.
         """
-        self._speakText(obj, doubleClick)
+        self._speakText(obj, basicOnly)
 
-    def _speakIconPanel(self, obj, doubleClick):
+    def _speakIconPanel(self, obj, basicOnly):
         """Speak the contents of the pane containing this icon. The
         number of icons in the pane is spoken. Plus the total number of
         selected icons. Plus the name of each of the selected icons.
@@ -700,19 +693,19 @@
         itemString = _("on item %d of %d") % (currentItem, childCount)
         utterances.append(itemString)
 
-        if doubleClick:
+        if not basicOnly:
             for i in range(0, len(selectedItems)):
                 utterances.append(self.getObjLabelAndName(selectedItems[i]))
 
         speech.speakUtterances(utterances)
         
-    def _speakLink(self, obj, doubleClick):
+    def _speakLink(self, obj, basicOnly):
         """Speaks information about a link including protocol, domain 
         comparisons and size of file if possible
         
         Arguments:
         - obj: the icon object that currently has focus.
-        - doubleClick: was it a doubleclick event?
+        - basicOnly: True if the user is performing a standard/basic whereAmI.
         """
         
         # get the URI for the link of interest and parse it.
@@ -772,7 +765,7 @@
 
         speech.speakUtterances([linkoutput, domainoutput, sizeoutput])
 
-    def _speakToggleButton(self, obj, doubleClick):
+    def _speakToggleButton(self, obj, basicOnly):
         """Speak toggle button information:
            1. Name/Label
            2. Role
@@ -832,7 +825,7 @@
             #
             return _('%.2f megabytes') % (float(size) * .000001) 
 
-    def _speakGenericObject(self, obj, doubleClick):
+    def _speakGenericObject(self, obj, basicOnly):
         """Speak a generic object; one not specifically handled by
         other methods.
         """
@@ -1164,15 +1157,15 @@
 
         return [textContents, startOffset, endOffset]
 
-    def getTextSelections(self, obj, doubleClick):
+    def getTextSelections(self, obj, basicOnly):
         """Get all the text applicable text selections for the given object.
-        If the user doubleclicked, look to see if there are any previous 
-        or next text objects that also have selected text and add in their 
-        text contents.
+        If the user is doing a detailed whereAmI, look to see if there are
+        any previous or next text objects that also have selected text and
+        add in their text contents.
 
         Arguments:
         - obj: the text object to start extracting the selected text from.
-        - doubleClick: True if the user double-clicked the "where am I" key.
+        - basicOnly: True if the user is performing a standard/basic whereAmI.
 
         Returns: all the selected text contents plus the start and end
         offsets within the text for the given object.
@@ -1186,7 +1179,7 @@
             [textContents, startOffset, endOffset] = \
                                             self._getTextSelection(obj)
 
-        if doubleClick:
+        if not basicOnly:
             current = obj
             morePossibleSelections = True
             while morePossibleSelections:
@@ -1308,7 +1301,7 @@
 
         return [currentSelected, otherSelected]
 
-    def _getTextContents(self, obj, doubleClick):
+    def _getTextContents(self, obj, basicOnly):
         """Returns utterences for text.
 
         A. if no text on the current line is selected, the current line
@@ -1328,11 +1321,11 @@
             (caretOffset, nSelections))
 
         [current, other] = self._hasTextSelections(obj)
-        if (doubleClick and (current or other)) or \
-           (not doubleClick and current):
+        if (not basicOnly and (current or other)) or \
+           (basicOnly and current):
             selected = True
             [textContents, startOffset, endOffset] = \
-                                  self.getTextSelections(obj, doubleClick)
+                                  self.getTextSelections(obj, basicOnly)
         else:
             # Get the line containing the caret
             #
@@ -1455,44 +1448,52 @@
 
         return dictionary
 
-    def _speakTitleOrStatus(self, obj, doubleClick):
-        """When pressed a single time, Orca will speak the following
-        information:
+    def speakTitle(self, obj):
+        """Orca will speak the following information:
 
         1. The contents of the title bar of the application main window
         2. If in a dialog box within an application, the contents of the
         title bar of the dialog box.
         3. Orca will pause briefly between these two pieces of information
         so that the speech user can distinguish each.
-
-        If pressed twice, Orca will speak the status bar.
         """
 
         utterances = []
 
         results = self._getFrameAndDialog(obj)
 
-        if doubleClick:
-            if results[0]:
-                self._statusBar = None
-                self._getStatusBar(results[0])
-                if self._statusBar:
-                    self._speakStatusBar()
-            window = results[1] or results[0]
-            if window:
-                self._speakDefaultButton(window)
-        else:
-            if results[0]:
-                text = self.getObjLabelAndName(results[0])
-                utterances.append(text)
-            if results[1]:
-                text = self.getObjLabelAndName(results[1])
-                utterances.append(text)
+        if results[0]:
+            text = self.getObjLabelAndName(results[0])
+            utterances.append(text)
+        if results[1]:
+            text = self.getObjLabelAndName(results[1])
+            utterances.append(text)
 
-            debug.println(self._debugLevel, "titlebar utterances=%s" % \
-                          utterances)
-            speech.speakUtterances(utterances)
+        debug.println(self._debugLevel, "titlebar utterances=%s" % \
+                      utterances)
+        speech.speakUtterances(utterances)
 
+    def speakStatusBar(self, obj):
+        """Speak the contents of the status bar of the window with focus.
+        """
+ 
+        utterances = []
+ 
+        results = self._getFrameAndDialog(obj)
+
+        if results[0]:
+            self._statusBar = None
+            self._getStatusBar(results[0])
+            if self._statusBar:
+                self._speakStatusBar()
+        window = results[1] or results[0]
+        if window:
+            self._speakDefaultButton(window)
+
+        debug.println(self._debugLevel, "status bar utterances=%s" % \
+                      utterances)
+        speech.speakUtterances(utterances)
+
     def _getFrameAndDialog(self, obj):
         """Returns the frame and (possibly) the dialog containing
         the object.
Index: src/orca/input_event.py
===================================================================
--- src/orca/input_event.py	(revision 3554)
+++ src/orca/input_event.py	(working copy)
@@ -76,6 +76,16 @@
             if value < 32:
                 event_string = chr(value + 0x40)
 
+        # Eliminate modifiers we're not interested in.
+        #
+        mask = (1 << settings.MODIFIER_ORCA |
+                1 << pyatspi.MODIFIER_ALT |
+                1 << pyatspi.MODIFIER_SHIFT |
+                1 << pyatspi.MODIFIER_CONTROL |
+                1 << pyatspi.MODIFIER_META2 |
+                1 << pyatspi.MODIFIER_META3)
+        event.modifiers = event.modifiers & mask
+
         InputEvent.__init__(self, KEYBOARD_EVENT)
         self.type = event.type
         self.hw_code = event.hw_code
Index: src/orca/script.py
===================================================================
--- src/orca/script.py	(revision 3554)
+++ src/orca/script.py	(working copy)
@@ -261,7 +261,14 @@
         - appState: an object obtained from getAppState
         """
         return
-        
+
+    def getClickCount(self):
+        """Return the count of the number of clicks a user has made to one
+        of the keys on the keyboard.
+        """
+
+        return orca_state.clickCount
+
     # [[[WDW - There is a circular reference going on somewhere (see
     # bug 333168).  In the presence of this reference, the existence
     # of a __del__ method prevents the garbage collector from
@@ -319,6 +326,7 @@
 
         Returns True if the event is of interest.
         """
+
         user_bindings = None
         user_bindings_map = settings.keyBindingsMap
         if user_bindings_map.has_key(self.__module__):
Index: src/orca/keybindings.py
===================================================================
--- src/orca/keybindings.py	(revision 3554)
+++ src/orca/keybindings.py	(working copy)
@@ -39,6 +39,7 @@
 import pyatspi
 import debug
 import settings
+import orca_state
 
 from orca_i18n import _           # for gettext support
 
@@ -200,7 +201,8 @@
     and the InputEventHandler.
     """
 
-    def __init__(self, keysymstring, modifier_mask, modifiers, handler):
+    def __init__(self, keysymstring, modifier_mask, modifiers, handler,
+                 click_count = 1):
         """Creates a new key binding.
 
         Arguments:
@@ -219,6 +221,7 @@
         self.modifier_mask = modifier_mask
         self.modifiers = modifiers
         self.handler = handler
+        self.click_count = click_count
         self.keycode = None
 
     def matches(self, keycode, modifiers):
@@ -249,10 +252,12 @@
     def __str__(self):
         result = "[\n"
         for keyBinding in self.keyBindings:
-            result += "  [%x %x %s %s]\n" % (keyBinding.modifier_mask,
-                                             keyBinding.modifiers,
-                                             keyBinding.keysymstring,
-                                             keyBinding.handler.description)
+            result += "  [%x %x %s %d %s]\n" % \
+                      (keyBinding.modifier_mask,
+                       keyBinding.modifiers,
+                       keyBinding.keysymstring,
+                       keyBinding.click_count,
+                       keyBinding.handler.description)
         result += "]"
         return result
     
@@ -283,10 +288,12 @@
         """Return True if keyBinding is already in self.keyBindings.
 
            The typeOfSearch can be:
-              "strict":      matches description, modifiers, key
+              "strict":      matches description, modifiers, key, and
+                             click count
               "description": matches only description.
-              "keys":        matches the modifiers, key, and modifier mask
-              "keysNoMask":  matches the modifiers and key only
+              "keys":        matches the modifiers, key, and modifier mask,
+                             and click count
+              "keysNoMask":  matches the modifiers, key, and click count
         """
 
         hasIt = False
@@ -300,7 +307,9 @@
                     and (keyBinding.modifier_mask \
                          == newKeyBinding.modifier_mask) \
                     and (keyBinding.modifiers \
-                         == newKeyBinding.modifiers):
+                         == newKeyBinding.modifiers) \
+                    and (keyBinding.click_count \
+                         == newKeyBinding.click_count):
                     hasIt = True
             elif typeOfSearch == "description":
                 if keyBinding.handler.description \
@@ -312,13 +321,17 @@
                     and (keyBinding.modifier_mask \
                          == newKeyBinding.modifier_mask) \
                     and (keyBinding.modifiers \
-                         == newKeyBinding.modifiers):
+                         == newKeyBinding.modifiers) \
+                    and (keyBinding.click_count \
+                         == newKeyBinding.click_count):
                     hasIt = True
             elif typeOfSearch == "keysNoMask":
                 if (keyBinding.keysymstring \
                     == newKeyBinding.keysymstring) \
                     and (keyBinding.modifiers \
-                         == newKeyBinding.modifiers):
+                         == newKeyBinding.modifiers) \
+                    and (keyBinding.click_count \
+                         == newKeyBinding.click_count):
                     hasIt = True
 
         return hasIt
@@ -327,16 +340,32 @@
         """Returns the input handler of the key binding that matches the
         given keycode and modifiers, or None if no match exists.
         """
-        handler = None
+
+        if not orca_state.activeScript:
+            return None
+
+        candidates = []
+        clickCount = orca_state.activeScript.getClickCount()
         for keyBinding in self.keyBindings:
             if keyBinding.matches(keyboardEvent.hw_code, \
                                   keyboardEvent.modifiers):
-                handler = keyBinding.handler
-                if keyBinding.modifier_mask == keyboardEvent.modifiers:
-                    break
+                if keyBinding.modifier_mask == keyboardEvent.modifiers and \
+                   keyBinding.click_count == clickCount:
+                    return keyBinding.handler
+                candidates.append(keyBinding)
 
-        return handler
+        # If we're still here, we don't have an exact match. Prefer
+        # the one whose click count is closest to, but does not exceed,
+        # the actual click count.
+        #
+        candidates.sort(cmp=lambda x, y: x.click_count - y.click_count,
+                        reverse=True)
+        for candidate in candidates:
+            if candidate.click_count <= clickCount:
+                return candidate.handler
 
+        return None
+
     def consumeKeyboardEvent(self, script, keyboardEvent):
         """Attempts to consume the given keyboard event.  If these
         keybindings have a handler for the given keyboardEvent, it is
Index: src/orca/orca_gui_prefs.py
===================================================================
--- src/orca/orca_gui_prefs.py	(revision 3554)
+++ src/orca/orca_gui_prefs.py	(working copy)
@@ -51,8 +51,9 @@
 
 from orca_i18n import _  # for gettext support
 
-(HANDLER, DESCRIP, MOD_MASK1, MOD_USED1, KEY1, OLDTEXT1, TEXT1, \
- MOD_MASK2, MOD_USED2, KEY2, OLDTEXT2, TEXT2, MODIF, EDITABLE) = range(14)
+(HANDLER, DESCRIP, MOD_MASK1, MOD_USED1, KEY1, CLICK_COUNT1, OLDTEXT1, \
+ TEXT1, MOD_MASK2, MOD_USED2, KEY2, CLICK_COUNT2, OLDTEXT2, TEXT2, MODIF, \
+ EDITABLE) = range(16)
 
 (NAME, IS_SPOKEN, IS_BRAILLED, VALUE) = range(4)
 
@@ -151,11 +152,13 @@
             gobject.TYPE_STRING,  # Modifier mask 1
             gobject.TYPE_STRING,  # Used Modifiers 1
             gobject.TYPE_STRING,  # Modifier key name 1
+            gobject.TYPE_STRING,  # Click count 1
             gobject.TYPE_STRING,  # Original Text of the Key Binding Shown 1
             gobject.TYPE_STRING,  # Text of the Key Binding Shown 1
             gobject.TYPE_STRING,  # Modifier mask 2
             gobject.TYPE_STRING,  # Used Modifiers 2
             gobject.TYPE_STRING,  # Modifier key name 2
+            gobject.TYPE_STRING,  # Click count 2
             gobject.TYPE_STRING,  # Original Text of the Key Binding Shown 2
             gobject.TYPE_STRING,  # Text of the Key Binding Shown 2
             gobject.TYPE_BOOLEAN, # Key Modified by User
@@ -219,6 +222,16 @@
         column.set_sort_column_id(KEY1)
         self.keyBindView.append_column(column)
 
+        # CLICK_COUNT1 - invisble column
+        #
+        column = gtk.TreeViewColumn("ClickCount1",
+                                    gtk.CellRendererText(),
+                                    text=CLICK_COUNT1)
+        column.set_resizable(True)
+        column.set_visible(False)
+        column.set_sort_column_id(CLICK_COUNT1)
+        self.keyBindView.append_column(column)
+
         # OLDTEXT1 - invisble column which will store a copy of the
         # original keybinding in TEXT1 prior to the Apply or OK
         # buttons being pressed.  This will prevent automatic
@@ -243,7 +256,7 @@
         rendererText.connect('edited',
                              self.editedKey,
                              self.keyBindingsModel,
-                             MOD_MASK1, MOD_USED1, KEY1, TEXT1)
+                             MOD_MASK1, MOD_USED1, KEY1, CLICK_COUNT1, TEXT1)
 
         # Translators: Key Binding is a table column header where
         # the cells in the column represent keyboard combinations
@@ -285,6 +298,16 @@
         column.set_sort_column_id(KEY2)
         self.keyBindView.append_column(column)
 
+        # CLICK_COUNT2 - invisble column
+        #
+        column = gtk.TreeViewColumn("ClickCount2",
+                                    gtk.CellRendererText(),
+                                    text=CLICK_COUNT2)
+        column.set_resizable(True)
+        column.set_visible(False)
+        column.set_sort_column_id(CLICK_COUNT2)
+        self.keyBindView.append_column(column)
+
         # OLDTEXT2 - invisble column which will store a copy of the
         # original keybinding in TEXT1 prior to the Apply or OK
         # buttons being pressed.  This will prevent automatic
@@ -309,7 +332,7 @@
         rendererText.connect('edited',
                              self.editedKey,
                              self.keyBindingsModel,
-                             MOD_MASK2, MOD_USED2, KEY2, TEXT2)
+                             MOD_MASK2, MOD_USED2, KEY2, CLICK_COUNT2, TEXT2)
 
         # Translators: Alternate is a table column header where
         # the cells in the column represent keyboard combinations
@@ -1905,6 +1928,30 @@
 
         return None
 
+    def _clickCountToString(self, clickCount):
+        """Given a numeric clickCount, returns a string for inclusion
+        in the list of keybindings.
+
+        Argument:
+        - clickCount: the number of clicks associated with the keybinding.
+        """
+
+        clickCountString = ""
+        if clickCount == 2:
+            # Translators: Orca keybindings support double
+            # and triple "clicks" or key presses, similar to
+            # using a mouse. 
+            #
+            clickCountString = " " + _("(double click)")
+        elif clickCount == 3:
+            # Translators: Orca keybindings support double
+            # and triple "clicks" or key presses, similar to
+            # using a mouse. 
+            #
+            clickCountString = " " + _("(triple click)")
+
+        return clickCountString
+
     def _addAlternateKeyBinding(self, kb):
         """Adds an alternate keybinding to the existing handler and
         returns true.  In case it doesn't exist yet, just returns
@@ -1926,12 +1973,15 @@
                     if not kb.keysymstring:
                         text = None
                     else:
+                        clickCount = self._clickCountToString(kb.click_count)
                         text = keybindings.getModifierNames(kb.modifiers) \
-                               + kb.keysymstring
+                               + kb.keysymstring \
+                               + clickCount
                     model.set(iterChild,
                               MOD_MASK2, kb.modifier_mask,
                               MOD_USED2, kb.modifiers,
                               KEY2, kb.keysymstring,
+                              CLICK_COUNT2, kb.click_count,
                               OLDTEXT2, text,
                               TEXT2, text)
                 iterChild = model.iter_next(iterChild)
@@ -1961,18 +2011,21 @@
             if not kb.keysymstring:
                 text = None
             else:
+                clickCount = self._clickCountToString(kb.click_count)
                 text = keybindings.getModifierNames(kb.modifiers) \
-                       + kb.keysymstring
+                       + kb.keysymstring \
+                       + clickCount
             model.set (myiter,
-                       HANDLER,   handl,
-                       DESCRIP,   kb.handler.description,
-                       MOD_MASK1, kb.modifier_mask,
-                       MOD_USED1, kb.modifiers,
-                       KEY1,      kb.keysymstring,
-                       OLDTEXT1,  text,
-                       TEXT1,     text,
-                       MODIF,     modif,
-                       EDITABLE,  True)
+                       HANDLER,      handl,
+                       DESCRIP,      kb.handler.description,
+                       MOD_MASK1,    kb.modifier_mask,
+                       MOD_USED1,    kb.modifiers,
+                       KEY1,         kb.keysymstring,
+                       CLICK_COUNT1, kb.click_count,
+                       OLDTEXT1,     text,
+                       TEXT1,        text,
+                       MODIF,        modif,
+                       EDITABLE,     True)
             return myiter
         else:
             return None
@@ -3296,12 +3349,15 @@
             speech.speak(_("Key binding deleted. Press enter to confirm."))
             return True
 
+        clickCount = orca_state.clickCount
         self.newBinding = keybindings.KeyBinding(keyName,
                                                  captured.modifiers,
                                                  captured.modifiers,
-                                                 None)
+                                                 None,
+                                                 clickCount)
         modifierNames = keybindings.getModifierNames(captured.modifiers)
-        newString = modifierNames + keyName
+        clickCount = self._clickCountToString(clickCount)
+        newString = modifierNames + keyName + clickCount
         description = self.pendingKeyBindings.get(newString)
         if description is None \
            and self.kbindings.hasKeyBinding(self.newBinding, "keysNoMask"):
@@ -3328,7 +3384,7 @@
         return True
             
     def editedKey(self, cell, path, new_text, treeModel, 
-                  modMask, modUsed, key, text):
+                  modMask, modUsed, key, click_count, text):
         """The user changed the key for a Keybinding: update the model of
         the treeview.
         """
@@ -3341,15 +3397,18 @@
         try:
             string = self.newBinding.keysymstring
             mods = self.newBinding.modifiers
+            clickCount = self.newBinding.click_count
         except:
             string = None
             mods = 0
+            clickCount = 1
 
         treeModel.set(myiter,
                       modMask, mods,
                       modUsed, mods,
                       key, string,
                       text, new_text,
+                      click_count, clickCount,
                       MODIF, modified)
         speech.stop()
         if new_text:
