[orca] Fix for bgo#620299 - Orca does not treat editable document frames as entries
- From: Joanmarie Diggs <joanied src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [orca] Fix for bgo#620299 - Orca does not treat editable document frames as entries
- Date: Fri, 11 Jun 2010 19:15:46 +0000 (UTC)
commit 4c64737e2f24e72cc8f0ec515864a887094e2793
Author: Joanmarie Diggs <joanmarie diggs gmail com>
Date: Fri Jun 11 15:14:31 2010 -0400
Fix for bgo#620299 - Orca does not treat editable document frames as entries
.../scripts/apps/Thunderbird/script_utilities.py | 10 ++++
.../scripts/toolkits/Gecko/braille_generator.py | 7 +++
src/orca/scripts/toolkits/Gecko/script.py | 45 +++++++++--------
.../scripts/toolkits/Gecko/script_utilities.py | 22 ++++++++
.../scripts/toolkits/Gecko/speech_generator.py | 5 ++
.../toolkits/Gecko/structural_navigation.py | 53 ++++++++++++++++++++
src/orca/structural_navigation.py | 13 +++--
7 files changed, 129 insertions(+), 26 deletions(-)
---
diff --git a/src/orca/scripts/apps/Thunderbird/script_utilities.py b/src/orca/scripts/apps/Thunderbird/script_utilities.py
index 07d73fd..8ceac80 100644
--- a/src/orca/scripts/apps/Thunderbird/script_utilities.py
+++ b/src/orca/scripts/apps/Thunderbird/script_utilities.py
@@ -75,6 +75,16 @@ class Utilities(Gecko.Utilities):
return None
+ def isEntry(self, obj):
+ """Returns True if we should treat this object as an entry."""
+
+ return obj and obj.getRole() == pyatspi.ROLE_ENTRY
+
+ def isPasswordText(self, obj):
+ """Returns True if we should treat this object as password text."""
+
+ return obj and obj.getRole() == pyatspi.ROLE_PASSWORD_TEXT
+
#########################################################################
# #
# Utilities for working with the accessible text interface #
diff --git a/src/orca/scripts/toolkits/Gecko/braille_generator.py b/src/orca/scripts/toolkits/Gecko/braille_generator.py
index 9a3760b..6a729d6 100644
--- a/src/orca/scripts/toolkits/Gecko/braille_generator.py
+++ b/src/orca/scripts/toolkits/Gecko/braille_generator.py
@@ -245,6 +245,11 @@ class BrailleGenerator(braille_generator.BrailleGenerator):
self._script.isAriaWidget(obj) \
or ((obj.getRole() == pyatspi.ROLE_LIST) \
and (not obj.getState().contains(pyatspi.STATE_FOCUSABLE)))
+
+ oldRole = None
+ if self._script.utilities.isEntry(obj):
+ oldRole = self._overrideRole(pyatspi.ROLE_ENTRY, args)
+
# Treat menu items in collapsed combo boxes as if the combo box
# had focus. This will make things more consistent with how we
# present combo boxes outside of Gecko.
@@ -259,4 +264,6 @@ class BrailleGenerator(braille_generator.BrailleGenerator):
generateBraille(self, obj, **args))
del args['includeContext']
del args['useDefaultFormatting']
+ if oldRole:
+ self._restoreRole(oldRole, args)
return result
diff --git a/src/orca/scripts/toolkits/Gecko/script.py b/src/orca/scripts/toolkits/Gecko/script.py
index be16a28..c5eeeda 100644
--- a/src/orca/scripts/toolkits/Gecko/script.py
+++ b/src/orca/scripts/toolkits/Gecko/script.py
@@ -1284,7 +1284,7 @@ class Script(default.Script):
# caret moved event for some object within the document
# frame.
#
- if eventSourceRole != pyatspi.ROLE_ENTRY \
+ if not self.utilities.isEntry(event.source) \
and self.utilities.isSameObject(
event.source, orca_state.locusOfFocus):
return
@@ -1351,7 +1351,7 @@ class Script(default.Script):
# onCaretMoved will handle.
#
if eventSourceInDocument and not self.isAriaWidget(event.source):
- if event.source.getRole() != pyatspi.ROLE_ENTRY:
+ if not self.utilities.isEntry(event.source):
[obj, characterOffset] = \
self.findFirstCaretContext(event.source, event.detail1)
else:
@@ -1595,7 +1595,8 @@ class Script(default.Script):
# caret context for the document frame. If we succeed, then
# we set the focus on the object that's holding the caret.
#
- if eventSourceRole == pyatspi.ROLE_DOCUMENT_FRAME:
+ if eventSourceRole == pyatspi.ROLE_DOCUMENT_FRAME \
+ and not event.source.getState().contains(pyatspi.STATE_EDITABLE):
try:
[obj, characterOffset] = self.getCaretContext()
state = obj.getState()
@@ -2202,9 +2203,9 @@ class Script(default.Script):
role = obj.getRole()
if (not len(string) and role != pyatspi.ROLE_PARAGRAPH) \
- or role in [pyatspi.ROLE_ENTRY,
- pyatspi.ROLE_PASSWORD_TEXT,
- pyatspi.ROLE_LINK]:
+ or self.utilities.isEntry(obj) \
+ or self.utilities.isPasswordText(obj) \
+ or role == pyatspi.ROLE_LINK:
[regions, fRegion] = \
self.brailleGenerator.generateBraille(obj)
@@ -2330,8 +2331,7 @@ class Script(default.Script):
# things, however, we can defer to the default scripts.
#
- if not self.inDocumentContent() \
- or obj.getRole() == pyatspi.ROLE_ENTRY:
+ if not self.inDocumentContent() or self.utilities.isEntry(obj):
default.Script.sayCharacter(self, obj)
return
@@ -2386,7 +2386,7 @@ class Script(default.Script):
wordContents = self.getWordContentsAtOffset(obj, characterOffset)
[textObj, startOffset, endOffset, word] = wordContents[0]
self.speakMisspelledIndicator(textObj, startOffset)
- if obj.getRole() != pyatspi.ROLE_ENTRY:
+ if not self.utilities.isEntry(textObj):
self.speakContents(wordContents)
else:
word = self.utilities.substring(textObj, startOffset, endOffset)
@@ -2399,8 +2399,7 @@ class Script(default.Script):
# EMBEDDED_OBJECT_CHARACTER model of Gecko. For all other
# things, however, we can defer to the default scripts.
#
- if not self.inDocumentContent() or \
- obj.getRole() == pyatspi.ROLE_ENTRY:
+ if not self.inDocumentContent() or self.utilities.isEntry(obj):
default.Script.sayLine(self, obj)
return
@@ -2674,7 +2673,7 @@ class Script(default.Script):
weHandleIt = True
obj = orca_state.locusOfFocus
- if obj and (obj.getRole() == pyatspi.ROLE_ENTRY):
+ if self.utilities.isEntry(obj):
text = obj.queryText()
length = text.characterCount
caretOffset = text.caretOffset
@@ -3236,6 +3235,7 @@ class Script(default.Script):
formRoles = [pyatspi.ROLE_CHECK_BOX,
pyatspi.ROLE_RADIO_BUTTON,
pyatspi.ROLE_COMBO_BOX,
+ pyatspi.ROLE_DOCUMENT_FRAME,
pyatspi.ROLE_LIST,
pyatspi.ROLE_ENTRY,
pyatspi.ROLE_PASSWORD_TEXT,
@@ -3246,6 +3246,9 @@ class Script(default.Script):
and state.contains(pyatspi.STATE_FOCUSABLE) \
and state.contains(pyatspi.STATE_SENSITIVE)
+ if obj.getRole() == pyatspi.ROLE_DOCUMENT_FRAME:
+ isField = isField and state.contains(pyatspi.STATE_EDITABLE)
+
return isField
def isUselessObject(self, obj):
@@ -3631,7 +3634,7 @@ class Script(default.Script):
# of several objects, so we'll examine the strings of what
# we've got and pop off the ones that match.
#
- elif role == pyatspi.ROLE_ENTRY:
+ elif self.utilities.isEntry(item[0]):
labelGuess = self.guessLabelFromLine(item[0])
index = len(lineContents) - 1
while labelGuess and index >= 0:
@@ -4237,7 +4240,7 @@ class Script(default.Script):
if text:
unicodeText = self.utilities.unicodeText(obj)
if characterOffset >= len(unicodeText):
- if obj.getRole() != pyatspi.ROLE_ENTRY:
+ if not self.utilities.isEntry(obj):
return [obj, -1]
else:
# We're at the end of an entry. If we return -1,
@@ -4760,12 +4763,9 @@ class Script(default.Script):
# We'll let the default script handle entries and other entry-like
# things (e.g. the text portion of a dojo spin button).
#
- isEntry = obj.getState().contains(pyatspi.STATE_EDITABLE) \
- or obj.getRole() in [pyatspi.ROLE_ENTRY,
- pyatspi.ROLE_TEXT,
- pyatspi.ROLE_PASSWORD_TEXT]
-
- if not self.inDocumentContent(obj) or isEntry:
+ if not self.inDocumentContent(obj) \
+ or self.utilities.isEntry(obj) \
+ or self.utilities.isPasswordText(obj):
return default.Script.getTextLineAtCaret(self, obj, offset)
# Find the current line.
@@ -5281,7 +5281,8 @@ class Script(default.Script):
# role.
#
if not len(string) \
- or role in [pyatspi.ROLE_ENTRY, pyatspi.ROLE_PASSWORD_TEXT]:
+ or self.utilities.isEntry(obj) \
+ or self.utilities.isPasswordText(obj):
utterance = self.speechGenerator.generateSpeech(obj)
else:
utterance = [string]
@@ -5365,7 +5366,7 @@ class Script(default.Script):
if character and character != self.EMBEDDED_OBJECT_CHARACTER:
speech.speakCharacter(character,
self.getACSS(obj, character))
- elif obj.getRole() != pyatspi.ROLE_ENTRY:
+ elif not self.utilities.isEntry(obj):
# We won't have a character if we move to the end of an
# entry (in which case we're not on a character and therefore
# have nothing to say), or when we hit a component with no
diff --git a/src/orca/scripts/toolkits/Gecko/script_utilities.py b/src/orca/scripts/toolkits/Gecko/script_utilities.py
index b189591..7e872b8 100644
--- a/src/orca/scripts/toolkits/Gecko/script_utilities.py
+++ b/src/orca/scripts/toolkits/Gecko/script_utilities.py
@@ -213,6 +213,23 @@ class Utilities(script_utilities.Utilities):
return None
+ def isEntry(self, obj):
+ """Returns True if we should treat this object as an entry."""
+
+ if not obj:
+ return False
+
+ if obj.getRole() == pyatspi.ROLE_ENTRY:
+ return True
+
+ if obj.getState().contains(pyatspi.STATE_EDITABLE) \
+ and obj.getRole() in [pyatspi.ROLE_DOCUMENT_FRAME,
+ pyatspi.ROLE_PARAGRAPH,
+ pyatspi.ROLE_TEXT]:
+ return True
+
+ return False
+
def isLayoutOnly(self, obj):
"""Returns True if the given object is for layout purposes only."""
@@ -224,6 +241,11 @@ class Utilities(script_utilities.Utilities):
else:
return script_utilities.Utilities.isLayoutOnly(self, obj)
+ def isPasswordText(self, obj):
+ """Returns True if we should treat this object as password text."""
+
+ return obj and obj.getRole() == pyatspi.ROLE_PASSWORD_TEXT
+
def isReadOnlyTextArea(self, obj):
"""Returns True if obj is a text entry area that is read only."""
diff --git a/src/orca/scripts/toolkits/Gecko/speech_generator.py b/src/orca/scripts/toolkits/Gecko/speech_generator.py
index 18355e3..f4669c4 100644
--- a/src/orca/scripts/toolkits/Gecko/speech_generator.py
+++ b/src/orca/scripts/toolkits/Gecko/speech_generator.py
@@ -499,6 +499,11 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
result.extend(speech_generator.SpeechGenerator.\
generateSpeech(self, obj, **args))
self._restoreRole(oldRole, args)
+ elif self._script.utilities.isEntry(obj):
+ oldRole = self._overrideRole(pyatspi.ROLE_ENTRY, args)
+ result.extend(speech_generator.SpeechGenerator.\
+ generateSpeech(self, obj, **args))
+ self._restoreRole(oldRole, args)
# ARIA widgets get treated like regular default widgets.
#
else:
diff --git a/src/orca/scripts/toolkits/Gecko/structural_navigation.py b/src/orca/scripts/toolkits/Gecko/structural_navigation.py
index 8bd1298..8560650 100644
--- a/src/orca/scripts/toolkits/Gecko/structural_navigation.py
+++ b/src/orca/scripts/toolkits/Gecko/structural_navigation.py
@@ -232,3 +232,56 @@ class GeckoStructuralNavigation(structural_navigation.StructuralNavigation):
eocs = float(string.count(embeddedObjectChar))
# print "Guess #2", string, eocs/len(string)
return eocs/len(string) < 0.005
+
+ ########################
+ # #
+ # Entries #
+ # #
+ ########################
+
+ def _entryPredicate(self, obj, arg=None):
+ """The predicate to be used for verifying that the object
+ obj is an entry.
+
+ Arguments:
+ - obj: the accessible object under consideration.
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ isMatch = False
+ if self._script.utilities.isEntry(obj) \
+ or self._script.utilities.isPasswordText(obj):
+ state = obj.getState()
+ isMatch = state.contains(pyatspi.STATE_FOCUSABLE) \
+ and state.contains(pyatspi.STATE_SENSITIVE) \
+ and state.contains(pyatspi.STATE_EDITABLE)
+
+ return isMatch
+
+ ########################
+ # #
+ # Form Fields #
+ # #
+ ########################
+
+ def _formFieldPredicate(self, obj, arg=None):
+ """The predicate to be used for verifying that the object
+ obj is a form field.
+
+ Arguments:
+ - obj: the accessible object under consideration.
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ isMatch = False
+ if obj and obj.getRole() in self.FORM_ROLES:
+ state = obj.getState()
+ isMatch = state.contains(pyatspi.STATE_FOCUSABLE) \
+ and state.contains(pyatspi.STATE_SENSITIVE)
+
+ if obj.getRole() == pyatspi.ROLE_DOCUMENT_FRAME:
+ isMatch = isMatch and state.contains(pyatspi.STATE_EDITABLE)
+
+ return isMatch
diff --git a/src/orca/structural_navigation.py b/src/orca/structural_navigation.py
index 4385fa8..7dbf299 100644
--- a/src/orca/structural_navigation.py
+++ b/src/orca/structural_navigation.py
@@ -465,6 +465,7 @@ class StructuralNavigation:
FORM_ROLES = [pyatspi.ROLE_CHECK_BOX,
pyatspi.ROLE_RADIO_BUTTON,
pyatspi.ROLE_COMBO_BOX,
+ pyatspi.ROLE_DOCUMENT_FRAME, # rich text editing
pyatspi.ROLE_LIST,
pyatspi.ROLE_ENTRY,
pyatspi.ROLE_PASSWORD_TEXT,
@@ -2438,7 +2439,8 @@ class StructuralNavigation:
the criteria (e.g. the level of a heading).
"""
- role = [pyatspi.ROLE_ENTRY,
+ role = [pyatspi.ROLE_DOCUMENT_FRAME,
+ pyatspi.ROLE_ENTRY,
pyatspi.ROLE_PASSWORD_TEXT,
pyatspi.ROLE_TEXT]
roleMatch = collection.MATCH_ANY
@@ -2450,7 +2452,8 @@ class StructuralNavigation:
states=state,
matchStates=stateMatch,
roles=role,
- matchRoles=roleMatch)
+ matchRoles=roleMatch,
+ applyPredicate=True)
def _entryPredicate(self, obj, arg=None):
"""The predicate to be used for verifying that the object
@@ -2463,7 +2466,8 @@ class StructuralNavigation:
"""
isMatch = False
- if obj and obj.getRole() in [pyatspi.ROLE_ENTRY,
+ if obj and obj.getRole() in [pyatspi.ROLE_DOCUMENT_FRAME,
+ pyatspi.ROLE_ENTRY,
pyatspi.ROLE_PASSWORD_TEXT,
pyatspi.ROLE_TEXT]:
state = obj.getState()
@@ -2547,7 +2551,8 @@ class StructuralNavigation:
states=state,
matchStates=stateMatch,
roles=role,
- matchRoles=roleMatch)
+ matchRoles=roleMatch,
+ applyPredicate=True)
def _formFieldPredicate(self, obj, arg=None):
"""The predicate to be used for verifying that the object
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]