[orca] Fix for bug #645465 Say All needs to be implemented for WebKitGtk-based apps
- From: Joanmarie Diggs <joanied src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [orca] Fix for bug #645465 Say All needs to be implemented for WebKitGtk-based apps
- Date: Mon, 21 Mar 2011 22:28:11 +0000 (UTC)
commit 6af52fb7186ce51e70d6b720108fc5a276eabdcd
Author: Joanmarie Diggs <joanmarie diggs gmail com>
Date: Mon Mar 21 11:52:52 2011 -0400
Fix for bug #645465 Say All needs to be implemented for WebKitGtk-based apps
src/orca/scripts/toolkits/WebKitGtk/script.py | 117 ++++++++++++++++++++
.../scripts/toolkits/WebKitGtk/script_utilities.py | 51 +++++++++
.../scripts/toolkits/WebKitGtk/speech_generator.py | 9 ++
3 files changed, 177 insertions(+), 0 deletions(-)
---
diff --git a/src/orca/scripts/toolkits/WebKitGtk/script.py b/src/orca/scripts/toolkits/WebKitGtk/script.py
index 2742a78..335f172 100644
--- a/src/orca/scripts/toolkits/WebKitGtk/script.py
+++ b/src/orca/scripts/toolkits/WebKitGtk/script.py
@@ -29,7 +29,10 @@ import pyatspi
import pyatspi.utils as utils
import orca.scripts.default as default
+import orca.input_event as input_event
import orca.orca as orca
+import orca.settings as settings
+import orca.speechserver as speechserver
import orca.orca_state as orca_state
import orca.speech as speech
from orca.orca_i18n import _
@@ -39,6 +42,8 @@ from braille_generator import BrailleGenerator
from speech_generator import SpeechGenerator
from script_utilities import Utilities
+_settingsManager = getattr(orca, '_settingsManager')
+
########################################################################
# #
# The WebKitGtk script class. #
@@ -84,6 +89,19 @@ class Script(default.Script):
self.inputEventHandlers.update(
self.structuralNavigation.inputEventHandlers)
+ self.inputEventHandlers["sayAllHandler"] = \
+ input_event.InputEventHandler(
+ Script.sayAll,
+ # Translators: the Orca "SayAll" command allows the
+ # user to press a key and have the entire document in
+ # a window be automatically spoken to the user. If
+ # the user presses any key during a SayAll operation,
+ # the speech will be interrupted and the cursor will
+ # be positioned at the point where the speech was
+ # interrupted.
+ #
+ _("Speaks entire document."))
+
def getKeyBindings(self):
"""Defines the key bindings for this script. Setup the default
key bindings, then add one in for reading the input line.
@@ -343,3 +361,102 @@ class Script(default.Script):
break
return child, index
+
+ def sayAll(self, inputEvent):
+ """Speaks the contents of the document beginning with the present
+ location. Overridden in this script because the sayAll could have
+ been started on an object without text (such as an image).
+ """
+
+ if not self.utilities.isWebKitGtk(orca_state.locusOfFocus):
+ return default.Script.sayAll(self, inputEvent)
+
+ speech.sayAll(self.textLines(orca_state.locusOfFocus),
+ self.__sayAllProgressCallback)
+
+ return True
+
+ def getTextSegments(self, obj, boundary, offset=0):
+ segments = []
+ text = obj.queryText()
+ length = text.characterCount
+ string, start, end = text.getTextAtOffset(offset, boundary)
+ while string and offset < length:
+ string = self.utilities.adjustForRepeats(string)
+ voice = self.speechGenerator.getVoiceForString(obj, string)
+ string = self.utilities.adjustForLinks(obj, string, start)
+ segments.append([string, start, end, voice])
+ offset = end
+ string, start, end = text.getTextAtOffset(offset, boundary)
+
+ return segments
+
+ def textLines(self, obj):
+ """Creates a generator that can be used to iterate over each line
+ of a text object, starting at the caret offset.
+
+ Arguments:
+ - obj: an Accessible that has a text specialization
+
+ Returns an iterator that produces elements of the form:
+ [SayAllContext, acss], where SayAllContext has the text to be
+ spoken and acss is an ACSS instance for speaking the text.
+ """
+
+ document = utils.findAncestor(
+ obj, lambda x: x.getRole() == pyatspi.ROLE_DOCUMENT_FRAME)
+ allTextObjs = utils.findAllDescendants(
+ document, lambda x: 'Text' in utils.listInterfaces(x))
+ allTextObjs = allTextObjs[allTextObjs.index(obj):len(allTextObjs)]
+ textObjs = filter(lambda x: x.parent not in allTextObjs, allTextObjs)
+ if not textObjs:
+ return
+
+ boundary = pyatspi.TEXT_BOUNDARY_LINE_START
+ sayAllStyle = _settingsManager.getSetting('sayAllStyle')
+ if sayAllStyle == settings.SAYALL_STYLE_SENTENCE:
+ boundary = pyatspi.TEXT_BOUNDARY_SENTENCE_START
+
+ offset = textObjs[0].queryText().caretOffset
+ for textObj in textObjs:
+ textSegments = self.getTextSegments(textObj, boundary, offset)
+ roleInfo = self.speechGenerator.getRoleName(textObj)
+ if roleInfo:
+ roleName, voice = roleInfo
+ textSegments.append([roleName, 0, -1, voice])
+
+ for (string, start, end, voice) in textSegments:
+ yield [speechserver.SayAllContext(textObj, string, start, end),
+ voice]
+
+ offset = 0
+
+ def __sayAllProgressCallback(self, context, progressType):
+ if progressType == speechserver.SayAllContext.PROGRESS:
+ return
+
+ obj = context.obj
+ orca.setLocusOfFocus(None, obj, notifyScript=False)
+
+ offset = context.currentOffset
+ text = obj.queryText()
+
+ if progressType == speechserver.SayAllContext.INTERRUPTED:
+ text.setCaretOffset(offset)
+ return
+
+ # SayAllContext.COMPLETED doesn't necessarily mean done with SayAll;
+ # just done with the current object. If we're still in SayAll, we do
+ # not want to set the caret (and hence set focus) in a link we just
+ # passed by.
+ try:
+ hypertext = obj.queryHypertext()
+ except NotImplementedError:
+ pass
+ else:
+ linkCount = hypertext.getNLinks()
+ links = [hypertext.getLink(x) for x in range(linkCount)]
+ if filter(lambda l: l.startIndex <= offset <= l.endIndex, links):
+ return
+
+ text.setCaretOffset(offset)
diff --git a/src/orca/scripts/toolkits/WebKitGtk/script_utilities.py b/src/orca/scripts/toolkits/WebKitGtk/script_utilities.py
index 97e3cd9..879291b 100644
--- a/src/orca/scripts/toolkits/WebKitGtk/script_utilities.py
+++ b/src/orca/scripts/toolkits/WebKitGtk/script_utilities.py
@@ -84,3 +84,54 @@ class Utilities(script_utilities.Utilities):
and not state.contains(pyatspi.STATE_EDITABLE)
return readOnly
+
+ def adjustForLinks(self, obj, line, startOffset):
+ """Adjust line to include the word "link" after any hypertext links.
+ Overridden here to deal with parents containing children which in
+ turn contain the links and implement the hypertext interface.
+
+ Arguments:
+ - obj: the accessible object that this line came from.
+ - line: the string to adjust for links.
+ - startOffset: the caret offset at the start of the line.
+
+ Returns: a new line adjusted to add the speaking of "link" after
+ text which is also a link.
+ """
+
+ adjustedLine = script_utilities.Utilities.adjustForLinks(
+ self, obj, line, startOffset)
+
+ roles = [pyatspi.ROLE_LIST_ITEM]
+ if not obj.getRole() in roles or not obj.childCount:
+ return adjustedLine
+
+ child = obj[0]
+ try:
+ hypertext = obj.queryHypertext()
+ nLinks = hypertext.getNLinks()
+ except NotImplementedError:
+ nLinks = 0
+
+ if not nLinks:
+ try:
+ hypertext = child.queryHypertext()
+ nLinks = hypertext.getNLinks()
+ except NotImplementedError:
+ pass
+
+ if not nLinks:
+ return adjustedLine
+
+ try:
+ objText = obj.queryText()
+ childText = child.queryText()
+ except NotImplementedError:
+ return adjustedLine
+
+ adjustment = objText.characterCount - childText.characterCount
+ if adjustment:
+ adjustedLine = script_utilities.Utilities.adjustForLinks(
+ self, child, line, startOffset - adjustment)
+
+ return adjustedLine
diff --git a/src/orca/scripts/toolkits/WebKitGtk/speech_generator.py b/src/orca/scripts/toolkits/WebKitGtk/speech_generator.py
index bb6c464..03bc798 100644
--- a/src/orca/scripts/toolkits/WebKitGtk/speech_generator.py
+++ b/src/orca/scripts/toolkits/WebKitGtk/speech_generator.py
@@ -28,6 +28,7 @@ __license__ = "LGPL"
import pyatspi
import orca.rolenames as rolenames
+import orca.settings as settings
import orca.speech_generator as speech_generator
from orca.orca_i18n import _
@@ -44,6 +45,13 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
def __init__(self, script):
speech_generator.SpeechGenerator.__init__(self, script)
+ def getVoiceForString(self, obj, string, **args):
+ voice = settings.voices[settings.DEFAULT_VOICE]
+ if string.decode("UTF-8").isupper():
+ voice = settings.voices[settings.UPPERCASE_VOICE]
+
+ return voice
+
def _generateRoleName(self, obj, **args):
result = []
acss = self.voice(speech_generator.SYSTEM)
@@ -55,6 +63,7 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
doNotSpeak.extend([pyatspi.ROLE_FORM,
pyatspi.ROLE_LABEL,
pyatspi.ROLE_MENU_ITEM,
+ pyatspi.ROLE_LIST_ITEM,
pyatspi.ROLE_PARAGRAPH,
pyatspi.ROLE_SECTION,
pyatspi.ROLE_TABLE_CELL])
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]