[orca] Begin cleaning up Live Region support in Gecko
- From: Joanmarie Diggs <joanied src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [orca] Begin cleaning up Live Region support in Gecko
- Date: Wed, 3 Jun 2015 00:41:43 +0000 (UTC)
commit 1e505b9d4222350c716964ef1ba65cfcfdb788ad
Author: Joanmarie Diggs <jdiggs igalia com>
Date: Tue Jun 2 20:41:06 2015 -0400
Begin cleaning up Live Region support in Gecko
src/orca/liveregions.py | 202 ++++++++++++++--------------
src/orca/script.py | 8 +-
src/orca/scripts/toolkits/Gecko/keymaps.py | 23 ---
src/orca/scripts/toolkits/Gecko/script.py | 85 +++----------
src/orca/structural_navigation.py | 9 +-
5 files changed, 134 insertions(+), 193 deletions(-)
---
diff --git a/src/orca/liveregions.py b/src/orca/liveregions.py
index c0dfa52..e00c6c6 100644
--- a/src/orca/liveregions.py
+++ b/src/orca/liveregions.py
@@ -4,9 +4,15 @@ import pyatspi
import time
from gi.repository import GLib
+from . import cmdnames
+from . import keybindings
from . import messages
+from . import input_event
from . import orca_state
from . import speech
+from . import settings_manager
+
+_settingsManager = settings_manager.getManager()
# define 'live' property types
LIVE_OFF = -1
@@ -58,47 +64,6 @@ class PriorityQueue:
myfilter = lambda item: item[0] > priority
self.queue = list(filter(myfilter, self.queue))
- def clumpContents(self):
- """ Combines messages with the same 'label' by appending newer
- 'content' and removing the newer message. This operation is only
- applied to the next dequeued message for performance reasons and is
- often applied in conjunction with filterContents() """
- if len(self.queue):
- newqueue = []
- newqueue.append(self.queue[0])
- targetlabels = newqueue[0][2]['labels']
- targetcontent = newqueue[0][2]['content']
- for i in range(1, len(self.queue)):
- if self.queue[i][2]['labels'] == targetlabels:
- newqueue[0][2]['content'].extend \
- (self.queue[i][2]['content'])
- else:
- newqueue.append(self.queue[i])
-
- self.queue = newqueue
-
- def filterContents(self):
- """ Combines utterances by eliminating repeated utterances and
- utterances that are part of other utterances. """
- if len(self.queue[0][2]['content']) > 1:
- oldcontent = self.queue[0][2]['content']
- newcontent = [oldcontent[0]]
-
- for i in range(1, len(oldcontent)):
- found = False
- for j in range(len(newcontent)):
- if oldcontent[i].find(newcontent[j]) != -1 \
- or newcontent[j].find(oldcontent[i]) != -1:
- if len(oldcontent[i]) > len(newcontent[j]):
- newcontent[j] = oldcontent[i]
- found = True
- break
-
- if not found:
- newcontent.append(oldcontent[i])
-
- self.queue[0][2]['content'] = newcontent
-
def __len__(self):
""" Return the length of the queue """
return len(self.queue)
@@ -110,6 +75,9 @@ class LiveRegionManager:
# message priority queue
self.msg_queue = PriorityQueue()
+ self.inputEventHandlers = self._getInputEventHandlers()
+ self.keyBindings = self._getKeyBindings()
+
# Message cache. Used to store up to 9 previous messages so user can
# review if desired.
self.msg_cache = []
@@ -137,6 +105,65 @@ class LiveRegionManager:
script.bookmarks.addSaveObserver(self.bookmarkSaveHandler)
script.bookmarks.addLoadObserver(self.bookmarkLoadHandler)
+ def _getInputEventHandlers(self):
+ handlers = {}
+
+ handlers["advanceLivePoliteness"] = \
+ input_event.InputEventHandler(
+ self.advancePoliteness,
+ cmdnames.LIVE_REGIONS_ADVANCE_POLITENESS)
+
+ handlers["setLivePolitenessOff"] = \
+ input_event.InputEventHandler(
+ self.setLivePolitenessOff,
+ cmdnames.LIVE_REGIONS_SET_POLITENESS_OFF)
+
+ handlers["monitorLiveRegions"] = \
+ input_event.InputEventHandler(
+ self.toggleMonitoring,
+ cmdnames.LIVE_REGIONS_MONITOR)
+
+ handlers["reviewLiveAnnouncement"] = \
+ input_event.InputEventHandler(
+ self.reviewLiveAnnouncement,
+ cmdnames.LIVE_REGIONS_REVIEW)
+
+ return handlers
+
+ def _getKeyBindings(self):
+ keyBindings = keybindings.KeyBindings()
+
+ keyBindings.add(
+ keybindings.KeyBinding(
+ "backslash",
+ keybindings.defaultModifierMask,
+ keybindings.NO_MODIFIER_MASK,
+ self.inputEventHandlers.get("advanceLivePoliteness")))
+
+ keyBindings.add(
+ keybindings.KeyBinding(
+ "backslash",
+ keybindings.defaultModifierMask,
+ keybindings.SHIFT_MODIFIER_MASK,
+ self.inputEventHandlers.get("setLivePolitenessOff")))
+
+ keyBindings.add(
+ keybindings.KeyBinding(
+ "backslash",
+ keybindings.defaultModifierMask,
+ keybindings.ORCA_SHIFT_MODIFIER_MASK,
+ self.inputEventHandlers.get("monitorLiveRegions")))
+
+ for key in ["F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9"]:
+ keyBindings.add(
+ keybindings.KeyBinding(
+ key,
+ keybindings.defaultModifierMask,
+ keybindings.ORCA_MODIFIER_MASK,
+ self.inputEventHandlers.get("reviewLiveAnnouncement")))
+
+ return keyBindings
+
def reset(self):
# First we will purge our politeness override dictionary of LIVE_NONE
# objects that are not registered for this page
@@ -190,24 +217,9 @@ class LiveRegionManager:
purging the message queue and outputting any queued messages that
were queued up in the handleEvent() method.
"""
- # If there are messages in the queue, we are monitoring, and we are not
- # currently speaking then speak queued message.
- # Note: Do all additional work within if statement to prevent
- # it from being done for each event loop callback
- # Note: isSpeaking() returns False way too early. A strategy using
- # a message length (in secs) could be used but don't forget many
- # parameters such as rate,expanded text and others must be considered.
- if len(self.msg_queue) > 0 \
- and not speech.isSpeaking() \
- and orca_state.lastInputEvent \
- and time.time() - orca_state.lastInputEvent.time > 1:
- # House cleaning on the message queue.
- # First we will purge the queue of old messages
+
+ if len(self.msg_queue) > 0:
self.msg_queue.purgeByKeepAlive()
- # Next, we will filter the messages
- self.msg_queue.clumpContents()
- self.msg_queue.filterContents()
- # Let's get our queued information
politeness, timestamp, message, obj = self.msg_queue.dequeue()
# Form output message. No need to repeat labels and content.
# TODO: really needs to be tested in real life cases. Perhaps
@@ -244,8 +256,14 @@ class LiveRegionManager:
retval.append(self._script.bookmarks.pathToObj(objectid))
return retval
- def advancePoliteness(self, obj):
+ def advancePoliteness(self, script, inputEvent):
"""Advance the politeness level of the given object"""
+
+ if not _settingsManager.getSetting('inferLiveRegions'):
+ self._script.presentMessage(messages.LIVE_REGIONS_OFF)
+ return
+
+ obj = orca_state.locusOfFocus
utterances = []
objectid = self._getObjectId(obj)
uri = self._script.bookmarks.getURIKey()
@@ -282,16 +300,27 @@ class LiveRegionManager:
self._script.speakContents(self._script.utilities.getObjectContentsAtOffset(
self.lastliveobj, 0))
- def reviewLiveAnnouncement(self, msgnum):
+ def reviewLiveAnnouncement(self, script, inputEvent):
"""Speak the given number cached message"""
+
+ msgnum = int(inputEvent.event_string[1:])
+ if not _settingsManager.getSetting('inferLiveRegions'):
+ self._script.presentMessage(messages.LIVE_REGIONS_OFF)
+ return
+
if msgnum > len(self.msg_cache):
self._script.presentMessage(messages.LIVE_REGIONS_NO_MESSAGE)
else:
self._script.presentMessage(self.msg_cache[-msgnum])
- def setLivePolitenessOff(self):
+ def setLivePolitenessOff(self, script, inputEvent):
"""User toggle to set all live regions to LIVE_OFF or back to their
original politeness."""
+
+ if not _settingsManager.getSetting('inferLiveRegions'):
+ self._script.presentMessage(messages.LIVE_REGIONS_OFF)
+ return
+
# start at the document frame
docframe = self._script.utilities.documentFrame()
# get the URI of the page. It is used as a partial key.
@@ -439,8 +468,10 @@ class LiveRegionManager:
else:
return None
- # Get the labeling information now that we have good content.
- labels = self._getLabelsAsUtterances(event.source)
+ # Proper live regions typically come with proper aria labels. These
+ # labels are typically exposed as names. Failing that, descriptions.
+ # Looking for actual labels seems a non-performant waste of time.
+ labels = [event.source.name, event.source.description]
# instantly send out notify messages
if 'channel' in attrs and attrs['channel'] == 'notify':
@@ -463,42 +494,6 @@ class LiveRegionManager:
if len(self.msg_cache) > CACHE_SIZE:
self.msg_cache.pop(0)
- def _getLabelsAsUtterances(self, obj):
- """Get the labels for a given object"""
- # try the Gecko label getter first
- uttstring = self._script.utilities.displayedLabel(obj)
- if uttstring:
- return [uttstring.strip()]
- # often we see a table cell. I'll implement my own label getter
- elif obj.getRole() == pyatspi.ROLE_TABLE_CELL \
- and obj.parent.childCount > 1:
- # We will try the table interface first for it's parent
- try:
- itable = obj.parent.queryTable()
- # I'm in a table, now what row are we in? Look in the first
- # columm of that row.
- #
- # Note: getRowHeader() fails for most markup. We will use the
- # relation when the markup is good (when getRowHeader() works)
- # so we won't see this code in those cases.
- index = self._script.utilities.cellIndex(obj)
- row = itable.getRowAtIndex(index)
- header = itable.getAccessibleAt(row, 0)
- # expand the header
- return [self._script.utilities.expandEOCs(header).strip()]
- except NotImplementedError:
- pass
-
- # Last ditch effort is to see if our parent is a table row <tr>
- # element.
- parentattrs = self._getAttrDictionary(obj.parent)
- if 'tag' in parentattrs and parentattrs['tag'] == 'TR':
- return [self._script.utilities.expandEOCs( \
- obj.parent.getChildAtIndex(0)).strip()]
-
- # Sorry, no valid labels found
- return []
-
def _getLiveType(self, obj):
"""Returns the live politeness setting for a given object. Also,
registers LIVE_NONE objects in politeness overrides when monitoring."""
@@ -577,3 +572,12 @@ class LiveRegionManager:
except Exception:
raise LookupError
obj = obj.parent
+
+ def toggleMonitoring(self, script, inputEvent):
+ if not _settingsManager.getSetting('inferLiveRegions'):
+ _settingsManager.setSetting('inferLiveRegions', True)
+ self._script.presentMessage(messages.LIVE_REGIONS_MONITORING_ON)
+ else:
+ _settingsManager.setSetting('inferLiveRegions', False)
+ self.flushMessages()
+ self._script.presentMessage(messages.LIVE_REGIONS_MONITORING_OFF)
diff --git a/src/orca/script.py b/src/orca/script.py
index f0a4fea..dd76772 100644
--- a/src/orca/script.py
+++ b/src/orca/script.py
@@ -100,6 +100,9 @@ class Script:
self.utilities = self.getUtilities()
self.labelInference = self.getLabelInference()
self.structuralNavigation = self.getStructuralNavigation()
+ self.bookmarks = self.getBookmarks()
+ self.liveRegionManager = self.getLiveRegionManager()
+
self.chat = self.getChat()
self.inputEventHandlers = {}
self.pointOfReference = {}
@@ -113,7 +116,6 @@ class Script:
self.generatorCache = {}
self.eventCache = {}
self.whereAmI = self.getWhereAmI()
- self.bookmarks = self.getBookmarks()
self.spellcheck = self.getSpellCheck()
self.voices = settings.voices
self.tutorialGenerator = self.getTutorialGenerator()
@@ -245,6 +247,10 @@ class Script:
types = self.getEnabledStructuralNavigationTypes()
return structural_navigation.StructuralNavigation(self, types)
+ def getLiveRegionManager(self):
+ """Returns the live region support for this script."""
+ return None
+
def useStructuralNavigationModel(self):
"""Returns True if we should use structural navigation. Most
scripts will have no need to override this. Gecko does however
diff --git a/src/orca/scripts/toolkits/Gecko/keymaps.py b/src/orca/scripts/toolkits/Gecko/keymaps.py
index 471aa24..933070d 100644
--- a/src/orca/scripts/toolkits/Gecko/keymaps.py
+++ b/src/orca/scripts/toolkits/Gecko/keymaps.py
@@ -58,29 +58,6 @@ arrowKeymap = (
)
commonKeymap = (
-
- # keybindings to provide chat room message history.
- ("F1", defaultModifierMask, ORCA_MODIFIER_MASK, "reviewLiveAnnouncement"),
- ("F2", defaultModifierMask, ORCA_MODIFIER_MASK, "reviewLiveAnnouncement"),
- ("F3", defaultModifierMask, ORCA_MODIFIER_MASK, "reviewLiveAnnouncement"),
- ("F4", defaultModifierMask, ORCA_MODIFIER_MASK, "reviewLiveAnnouncement"),
- ("F5", defaultModifierMask, ORCA_MODIFIER_MASK, "reviewLiveAnnouncement"),
- ("F6", defaultModifierMask, ORCA_MODIFIER_MASK, "reviewLiveAnnouncement"),
- ("F7", defaultModifierMask, ORCA_MODIFIER_MASK, "reviewLiveAnnouncement"),
- ("F8", defaultModifierMask, ORCA_MODIFIER_MASK, "reviewLiveAnnouncement"),
- ("F9", defaultModifierMask, ORCA_MODIFIER_MASK, "reviewLiveAnnouncement"),
-
- # misc
-
- ("backslash", defaultModifierMask, SHIFT_MODIFIER_MASK,
- "setLivePolitenessOff"),
-
- ("backslash", defaultModifierMask, ORCA_SHIFT_MODIFIER_MASK,
- "monitorLiveRegions"),
-
- ("backslash", defaultModifierMask, NO_MODIFIER_MASK,
- "advanceLivePoliteness"),
-
("F12", defaultModifierMask, ORCA_MODIFIER_MASK,
"toggleCaretNavigationHandler"),
diff --git a/src/orca/scripts/toolkits/Gecko/script.py b/src/orca/scripts/toolkits/Gecko/script.py
index b53634b..3722c2d 100644
--- a/src/orca/scripts/toolkits/Gecko/script.py
+++ b/src/orca/scripts/toolkits/Gecko/script.py
@@ -121,12 +121,6 @@ class Script(default.Script):
Script.goBeginningOfLine,
Script.goEndOfLine]
- self._liveRegionFunctions = \
- [Script.setLivePolitenessOff,
- Script.advanceLivePoliteness,
- Script.monitorLiveRegions,
- Script.reviewLiveAnnouncement]
-
if _settingsManager.getSetting('caretNavigationEnabled') == None:
_settingsManager.setSetting('caretNavigationEnabled', True)
if _settingsManager.getSetting('sayAllOnLoad') == None:
@@ -155,9 +149,6 @@ class Script(default.Script):
#
self._madeFindAnnouncement = False
- # Create the live region manager and start the message manager
- self.liveMngr = liveregions.LiveRegionManager(self)
-
# For really large objects, a call to getAttributes can take up to
# two seconds! This is a Firefox bug. We'll try to improve things
# by storing attributes.
@@ -293,6 +284,11 @@ class Script(default.Script):
return enabledTypes
+ def getLiveRegionManager(self):
+ """Returns the live region support for this script."""
+
+ return liveregions.LiveRegionManager(self)
+
def getStructuralNavigation(self):
"""Returns the 'structural navigation' class for this script.
"""
@@ -309,6 +305,8 @@ class Script(default.Script):
self.inputEventHandlers.update(\
self.structuralNavigation.inputEventHandlers)
+ self.inputEventHandlers.update(self.liveRegionManager.inputEventHandlers)
+
self.inputEventHandlers["goNextCharacterHandler"] = \
input_event.InputEventHandler(
Script.goNextCharacter,
@@ -359,26 +357,6 @@ class Script(default.Script):
Script.goEndOfLine,
cmdnames.CARET_NAVIGATION_LINE_END)
- self.inputEventHandlers["advanceLivePoliteness"] = \
- input_event.InputEventHandler(
- Script.advanceLivePoliteness,
- cmdnames.LIVE_REGIONS_ADVANCE_POLITENESS)
-
- self.inputEventHandlers["setLivePolitenessOff"] = \
- input_event.InputEventHandler(
- Script.setLivePolitenessOff,
- cmdnames.LIVE_REGIONS_SET_POLITENESS_OFF)
-
- self.inputEventHandlers["monitorLiveRegions"] = \
- input_event.InputEventHandler(
- Script.monitorLiveRegions,
- cmdnames.LIVE_REGIONS_MONITOR)
-
- self.inputEventHandlers["reviewLiveAnnouncement"] = \
- input_event.InputEventHandler(
- Script.reviewLiveAnnouncement,
- cmdnames.LIVE_REGIONS_REVIEW)
-
self.inputEventHandlers["toggleCaretNavigationHandler"] = \
input_event.InputEventHandler(
Script.toggleCaretNavigation,
@@ -446,6 +424,10 @@ class Script(default.Script):
for keyBinding in bindings.keyBindings:
keyBindings.add(keyBinding)
+ liveRegionBindings = self.liveRegionManager.keyBindings
+ for keyBinding in liveRegionBindings.keyBindings:
+ keyBindings.add(keyBinding)
+
return keyBindings
def getAppPreferencesGUI(self):
@@ -662,9 +644,7 @@ class Script(default.Script):
self._lastCommandWasCaretNav = consumes
self._lastCommandWasStructNav = False
self._lastCommandWasMouseButton = False
- elif handler \
- and (handler.function in self.structuralNavigation.functions \
- or handler.function in self._liveRegionFunctions):
+ elif handler and handler.function in self.structuralNavigation.functions:
consumes = self.useStructuralNavigationModel()
self._lastCommandWasCaretNav = False
self._lastCommandWasStructNav = consumes
@@ -681,9 +661,7 @@ class Script(default.Script):
self._lastCommandWasCaretNav = consumes
self._lastCommandWasStructNav = False
self._lastCommandWasMouseButton = False
- elif handler \
- and (handler.function in self.structuralNavigation.functions \
- or handler.function in self._liveRegionFunctions):
+ elif handler and handler.function in self.structuralNavigation.functions:
consumes = self.useStructuralNavigationModel()
self._lastCommandWasCaretNav = False
self._lastCommandWasStructNav = consumes
@@ -1098,7 +1076,7 @@ class Script(default.Script):
if self.utilities.handleAsLiveRegion(event):
msg = "INFO: Event to be handled as live region"
debug.println(debug.LEVEL_INFO, msg)
- self.liveMngr.handleEvent(event)
+ self.liveRegionManager.handleEvent(event)
return True
if self._loadingDocumentContent:
@@ -1155,7 +1133,7 @@ class Script(default.Script):
msg = "INFO: Updating loading state and resetting live regions"
debug.println(debug.LEVEL_INFO, msg)
self._loadingDocumentContent = False
- self.liveMngr.reset()
+ self.liveRegionManager.reset()
return True
def onDocumentLoadStopped(self, event):
@@ -1293,7 +1271,7 @@ class Script(default.Script):
if event.source.getRole() == pyatspi.ROLE_FRAME:
msg = "INFO: Flusing messages from live region manager"
debug.println(debug.LEVEL_INFO, msg)
- self.liveMngr.flushMessages()
+ self.liveRegionManager.flushMessages()
return True
@@ -1367,7 +1345,7 @@ class Script(default.Script):
if self.utilities.handleAsLiveRegion(event):
msg = "INFO: Event to be handled as live region"
debug.println(debug.LEVEL_INFO, msg)
- self.liveMngr.handleEvent(event)
+ self.liveRegionManager.handleEvent(event)
return True
if self.utilities.eventIsEOCAdded(event):
@@ -2049,35 +2027,6 @@ class Script(default.Script):
self.utilities.setCaretPosition(obj, characterOffset)
self.presentLine(obj, characterOffset)
- def advanceLivePoliteness(self, inputEvent):
- """Advances live region politeness level."""
- if _settingsManager.getSetting('inferLiveRegions'):
- self.liveMngr.advancePoliteness(orca_state.locusOfFocus)
- else:
- self.presentMessage(messages.LIVE_REGIONS_OFF)
-
- def monitorLiveRegions(self, inputEvent):
- if not _settingsManager.getSetting('inferLiveRegions'):
- _settingsManager.setSetting('inferLiveRegions', True)
- self.presentMessage(messages.LIVE_REGIONS_MONITORING_ON)
- else:
- _settingsManager.setSetting('inferLiveRegions', False)
- self.liveMngr.flushMessages()
- self.presentMessage(messages.LIVE_REGIONS_MONITORING_OFF)
-
- def setLivePolitenessOff(self, inputEvent):
- if _settingsManager.getSetting('inferLiveRegions'):
- self.liveMngr.setLivePolitenessOff()
- else:
- self.presentMessage(messages.LIVE_REGIONS_OFF)
-
- def reviewLiveAnnouncement(self, inputEvent):
- if _settingsManager.getSetting('inferLiveRegions'):
- self.liveMngr.reviewLiveAnnouncement( \
- int(inputEvent.event_string[1:]))
- else:
- self.presentMessage(messages.LIVE_REGIONS_OFF)
-
def enableStickyFocusMode(self, inputEvent):
self._inFocusMode = True
self._focusModeIsSticky = True
diff --git a/src/orca/structural_navigation.py b/src/orca/structural_navigation.py
index ee9a1cd..4584428 100644
--- a/src/orca/structural_navigation.py
+++ b/src/orca/structural_navigation.py
@@ -222,6 +222,8 @@ class StructuralNavigationObject:
modifiers,
self.inputEventHandlers[handlerName]))
+ self.functions.append(self.showList)
+
# Set up the "at level" handlers (e.g. to navigate among headings
# at the specified level).
#
@@ -280,6 +282,8 @@ class StructuralNavigationObject:
modifiers,
self.inputEventHandlers[handlerName]))
+ self.functions.append(handler)
+
# Set up the "directional" handlers (e.g. for table cells. Live
# region support has a handler to go to the last live region,
# so we'll handle that here as well).
@@ -713,7 +717,7 @@ class StructuralNavigation:
# #
#########################################################################
- def toggleStructuralNavigation(self, script, inputEvent):
+ def toggleStructuralNavigation(self, script, inputEvent, presentMessage=True):
"""Toggles structural navigation keys."""
self.enabled = not self.enabled
@@ -724,7 +728,8 @@ class StructuralNavigation:
string = messages.STRUCTURAL_NAVIGATION_KEYS_OFF
debug.println(debug.LEVEL_CONFIGURATION, string)
- self._script.presentMessage(string)
+ if presentMessage:
+ self._script.presentMessage(string)
#########################################################################
# #
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]