1# Orca 2# 3# Copyright 2016 Igalia, S.L. 4# Author: Joanmarie Diggs <jdiggs@igalia.com> 5# 6# This library is free software; you can redistribute it and/or 7# modify it under the terms of the GNU Lesser General Public 8# License as published by the Free Software Foundation; either 9# version 2.1 of the License, or (at your option) any later version. 10# 11# This library is distributed in the hope that it will be useful, 12# but WITHOUT ANY WARRANTY; without even the implied warranty of 13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14# Lesser General Public License for more details. 15# 16# You should have received a copy of the GNU Lesser General Public 17# License along with this library; if not, write to the 18# Free Software Foundation, Inc., Franklin Street, Fifth Floor, 19# Boston MA 02110-1301 USA. 20 21__id__ = "$Id$" 22__version__ = "$Revision$" 23__date__ = "$Date$" 24__copyright__ = "Copyright (c) 2016 Igalia, S.L." 25__license__ = "LGPL" 26 27from orca import debug 28from orca import orca 29from orca import orca_state 30from orca import speech 31from orca.scripts import default 32 33from .braille_generator import BrailleGenerator 34from .speech_generator import SpeechGenerator 35from .script_utilities import Utilities 36 37 38class Script(default.Script): 39 40 def __init__(self, app): 41 super().__init__(app) 42 self.presentIfInactive = False 43 44 def deactivate(self): 45 """Called when this script is deactivated.""" 46 47 self.utilities.clearCache() 48 super().deactivate() 49 50 def getBrailleGenerator(self): 51 """Returns the braille generator for this script.""" 52 53 return BrailleGenerator(self) 54 55 def getSpeechGenerator(self): 56 """Returns the speech generator for this script.""" 57 58 return SpeechGenerator(self) 59 60 def getUtilities(self): 61 """Returns the utilites for this script.""" 62 63 return Utilities(self) 64 65 def onFocus(self, event): 66 """Callback for focus: accessibility events.""" 67 68 # https://bugzilla.gnome.org/show_bug.cgi?id=748311 69 orca.setLocusOfFocus(event, event.source) 70 71 def onTextDeleted(self, event): 72 """Callback for object:text-changed:delete accessibility events.""" 73 74 if self.utilities.treatEventAsNoise(event): 75 msg = "TERMINAL: Deletion is believed to be noise" 76 debug.println(debug.LEVEL_INFO, msg, True) 77 return 78 79 super().onTextDeleted(event) 80 81 def onTextInserted(self, event): 82 """Callback for object:text-changed:insert accessibility events.""" 83 84 if not self.utilities.treatEventAsCommand(event): 85 msg = "TERMINAL: Passing along event to default script." 86 debug.println(debug.LEVEL_INFO, msg, True) 87 super().onTextInserted(event) 88 return 89 90 msg = "TERMINAL: Insertion is believed to be due to terminal command" 91 debug.println(debug.LEVEL_INFO, msg, True) 92 93 self.updateBraille(event.source) 94 95 newString = self.utilities.insertedText(event) 96 if len(newString) == 1: 97 self.speakCharacter(newString) 98 else: 99 voice = self.speechGenerator.voice(string=newString) 100 speech.speak(newString, voice) 101 102 if self.flatReviewContext: 103 return 104 105 try: 106 text = event.source.queryText() 107 except: 108 pass 109 else: 110 self._saveLastCursorPosition(event.source, text.caretOffset) 111 self.utilities.updateCachedTextSelection(event.source) 112 113 def presentKeyboardEvent(self, event): 114 if orca_state.learnModeEnabled or not event.isPrintableKey(): 115 return super().presentKeyboardEvent(event) 116 117 if event.isPressedKey(): 118 return False 119 120 self._sayAllIsInterrupted = False 121 self.utilities.clearCachedCommandState() 122 if event.shouldEcho == False or event.isOrcaModified() or event.isCharacterEchoable(): 123 return False 124 125 # We have no reliable way of knowing a password is being entered into 126 # a terminal -- other than the fact that the text typed isn't there. 127 try: 128 text = event.getObject().queryText() 129 offset = text.caretOffset 130 prevChar = text.getText(offset - 1, offset) 131 char = text.getText(offset, offset + 1) 132 except: 133 return False 134 135 string = event.event_string 136 if string not in [prevChar, "space", char]: 137 return False 138 139 msg = "TERMINAL: Presenting keyboard event %s" % string 140 debug.println(debug.LEVEL_INFO, msg, True) 141 142 voice = self.speechGenerator.voice(string=string) 143 speech.speakKeyEvent(event, voice) 144 return True 145 146 def skipObjectEvent(self, event): 147 if event.type == "object:text-changed:insert": 148 return False 149 150 newEvent, newTime = None, 0 151 if event.type == "object:text-changed:delete": 152 if self.utilities.isBackSpaceCommandTextDeletionEvent(event): 153 return False 154 155 newEvent, newTime = self.eventCache.get("object:text-changed:insert", [None, 0]) 156 157 if newEvent is None or newEvent.source != event.source: 158 return super().skipObjectEvent(event) 159 160 if event.detail1 != newEvent.detail1: 161 return False 162 163 data = "\n%s%s" % (" " * 11, str(newEvent).replace("\t", " " * 11)) 164 msg = "TERMINAL: Skipping due to more recent event at offset%s" % data 165 debug.println(debug.LEVEL_INFO, msg, True) 166 return True 167