~stomato463/+junk/nvdajp

« back to all changes in this revision

Viewing changes to source/editableText.py

  • Committer: Masataka Shinke
  • Date: 2011-10-25 12:35:26 UTC
  • mfrom: (4185 jpmain)
  • mto: This revision was merged to the branch mainline in revision 4211.
  • Revision ID: mshinke@users.sourceforge.jp-20111025123526-ze527a2rl3z0g2ky
lp:~nishimotz/nvdajp/main : 4185 をマージ

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#editableText.py
 
2
#A part of NonVisual Desktop Access (NVDA)
 
3
#This file is covered by the GNU General Public License.
 
4
#See the file COPYING for more details.
 
5
#Copyright (C) 2006-2010 Michael Curran <mick@kulgan.net>, James Teh <jamie@jantrid.net>
 
6
 
 
7
"""Common support for editable text.
 
8
"""
 
9
 
 
10
import time
 
11
import api
 
12
from baseObject import ScriptableObject
 
13
import braille
 
14
import speech
 
15
import config
 
16
import eventHandler
 
17
from scriptHandler import isScriptWaiting
 
18
import textInfos
 
19
 
 
20
class EditableText(ScriptableObject):
 
21
        """Provides scripts to report appropriately when moving the caret in editable text fields.
 
22
        This does not handle the selection change keys.
 
23
        To have selection changes reported, the object must notify of selection changes.
 
24
        If the object supports selection but does not notify of selection changes, L{EditableTextWithoutAutoSelectDetection} should be used instead.
 
25
        
 
26
        If the object notifies of selection changes, the following should be done:
 
27
                * When the object gains focus, L{initAutoSelectDetection} must be called.
 
28
                * When the object notifies of a possible selection change, L{detectPossibleSelectionChange} must be called.
 
29
                * Optionally, if the object notifies of changes to its content, L{hasContentChangedSinceLastSelection} should be set to C{True}.
 
30
        @ivar hasContentChangedSinceLastSelection: Whether the content has changed since the last selection occurred.
 
31
        @type hasContentChangedSinceLastSelection: bool
 
32
        """
 
33
 
 
34
        #: Whether to fire caretMovementFailed events when the caret doesn't move in response to a caret movement key.
 
35
        shouldFireCaretMovementFailedEvents = False
 
36
 
 
37
        def _hasCaretMoved(self, bookmark, retryInterval=0.01, timeout=0.03):
 
38
                elapsed = 0
 
39
                while elapsed < timeout:
 
40
                        if isScriptWaiting():
 
41
                                return False
 
42
                        api.processPendingEvents(processEventQueue=False)
 
43
                        if eventHandler.isPendingEvents("gainFocus"):
 
44
                                return True
 
45
                        #The caret may stop working as the focus jumps, we want to stay in the while loop though
 
46
                        try:
 
47
                                newBookmark = self.makeTextInfo(textInfos.POSITION_CARET).bookmark
 
48
                        except (RuntimeError,NotImplementedError):
 
49
                                pass
 
50
                        else:
 
51
                                if newBookmark!=bookmark:
 
52
                                        return True
 
53
                        time.sleep(retryInterval)
 
54
                        elapsed += retryInterval
 
55
                return False
 
56
 
 
57
        def _caretScriptPostMovedHelper(self, speakUnit):
 
58
                if isScriptWaiting():
 
59
                        return
 
60
                try:
 
61
                        info = self.makeTextInfo(textInfos.POSITION_CARET)
 
62
                except:
 
63
                        return
 
64
                if config.conf["reviewCursor"]["followCaret"] and api.getNavigatorObject() is self:
 
65
                        api.setReviewPosition(info.copy())
 
66
                if speakUnit:
 
67
                        info.expand(speakUnit)
 
68
                        speech.speakTextInfo(info, unit=speakUnit, reason=speech.REASON_CARET)
 
69
 
 
70
        def _caretMovementScriptHelper(self, gesture, unit):
 
71
                try:
 
72
                        info=self.makeTextInfo(textInfos.POSITION_CARET)
 
73
                except:
 
74
                        gesture.send()
 
75
                        return
 
76
                bookmark=info.bookmark
 
77
                gesture.send()
 
78
                if not self._hasCaretMoved(bookmark) and self.shouldFireCaretMovementFailedEvents:
 
79
                        eventHandler.executeEvent("caretMovementFailed", self, gesture=gesture)
 
80
                self._caretScriptPostMovedHelper(unit)
 
81
 
 
82
        def script_caret_moveByLine(self,gesture):
 
83
                self._caretMovementScriptHelper(gesture, textInfos.UNIT_LINE)
 
84
 
 
85
        def script_caret_moveByCharacter(self,gesture):
 
86
                self._caretMovementScriptHelper(gesture, textInfos.UNIT_CHARACTER)
 
87
 
 
88
        def script_caret_moveByWord(self,gesture):
 
89
                self._caretMovementScriptHelper(gesture, textInfos.UNIT_WORD)
 
90
 
 
91
        def script_caret_moveByParagraph(self,gesture):
 
92
                self._caretMovementScriptHelper(gesture, textInfos.UNIT_PARAGRAPH)
 
93
 
 
94
        def _backspaceScriptHelper(self,unit,gesture):
 
95
                try:
 
96
                        oldInfo=self.makeTextInfo(textInfos.POSITION_CARET)
 
97
                except:
 
98
                        gesture.send()
 
99
                        return
 
100
                oldBookmark=oldInfo.bookmark
 
101
                testInfo=oldInfo.copy()
 
102
                res=testInfo.move(textInfos.UNIT_CHARACTER,-1)
 
103
                if res<0:
 
104
                        testInfo.expand(unit)
 
105
                        delChunk=testInfo.text
 
106
                else:
 
107
                        delChunk=""
 
108
                gesture.send()
 
109
                if not self._hasCaretMoved(oldBookmark):
 
110
                        return
 
111
                if len(delChunk)>1:
 
112
                        speech.speakMessage(delChunk)
 
113
                else:
 
114
                        speech.speakSpelling(delChunk)
 
115
                self._caretScriptPostMovedHelper(None)
 
116
 
 
117
        def script_caret_backspaceCharacter(self,gesture):
 
118
                self._backspaceScriptHelper(textInfos.UNIT_CHARACTER,gesture)
 
119
 
 
120
        def script_caret_backspaceWord(self,gesture):
 
121
                self._backspaceScriptHelper(textInfos.UNIT_WORD,gesture)
 
122
 
 
123
        def script_caret_delete(self,gesture):
 
124
                try:
 
125
                        info=self.makeTextInfo(textInfos.POSITION_CARET)
 
126
                except:
 
127
                        gesture.send()
 
128
                        return
 
129
                bookmark=info.bookmark
 
130
                gesture.send()
 
131
                # We'll try waiting for the caret to move, but we don't care if it doesn't.
 
132
                self._hasCaretMoved(bookmark)
 
133
                self._caretScriptPostMovedHelper(textInfos.UNIT_CHARACTER)
 
134
                braille.handler.handleCaretMove(self)
 
135
 
 
136
        __gestures = {
 
137
                "kb:upArrow": "caret_moveByLine",
 
138
                "kb:downArrow": "caret_moveByLine",
 
139
                "kb:leftArrow": "caret_moveByCharacter",
 
140
                "kb:rightArrow": "caret_moveByCharacter",
 
141
                "kb:pageUp": "caret_moveByLine",
 
142
                "kb:pageDown": "caret_moveByLine",
 
143
                "kb:control+leftArrow": "caret_moveByWord",
 
144
                "kb:control+rightArrow": "caret_moveByWord",
 
145
                "kb:control+upArrow": "caret_moveByParagraph",
 
146
                "kb:control+downArrow": "caret_moveByParagraph",
 
147
                "kb:home": "caret_moveByCharacter",
 
148
                "kb:end": "caret_moveByCharacter",
 
149
                "kb:control+home": "caret_moveByLine",
 
150
                "kb:control+end": "caret_moveByLine",
 
151
                "kb:delete": "caret_delete",
 
152
                "kb:numpadDelete": "caret_delete",
 
153
                "kb:backspace": "caret_backspaceCharacter",
 
154
                "kb:control+backspace": "caret_backspaceWord",
 
155
        }
 
156
 
 
157
        def initAutoSelectDetection(self):
 
158
                """Initialise automatic detection of selection changes.
 
159
                This should be called when the object gains focus.
 
160
                """
 
161
                try:
 
162
                        self._lastSelectionPos=self.makeTextInfo(textInfos.POSITION_SELECTION)
 
163
                except:
 
164
                        self._lastSelectionPos=None
 
165
                self.hasContentChangedSinceLastSelection=False
 
166
 
 
167
        def detectPossibleSelectionChange(self):
 
168
                """Detects if the selection has been changed, and if so it speaks the change.
 
169
                """
 
170
                try:
 
171
                        newInfo=self.makeTextInfo(textInfos.POSITION_SELECTION)
 
172
                except:
 
173
                        # Just leave the old selection, which is usually better than nothing.
 
174
                        return
 
175
                oldInfo=getattr(self,'_lastSelectionPos',None)
 
176
                self._lastSelectionPos=newInfo.copy()
 
177
                if not oldInfo:
 
178
                        # There's nothing we can do, but at least the last selection will be right next time.
 
179
                        return
 
180
                hasContentChanged=getattr(self,'hasContentChangedSinceLastSelection',False)
 
181
                self.hasContentChangedSinceLastSelection=False
 
182
                speech.speakSelectionChange(oldInfo,newInfo,generalize=hasContentChanged)
 
183
 
 
184
class EditableTextWithoutAutoSelectDetection(EditableText):
 
185
        """In addition to L{EditableText}, provides scripts to report appropriately when the selection changes.
 
186
        This should be used when an object does not notify of selection changes.
 
187
        """
 
188
 
 
189
        def script_caret_changeSelection(self,gesture):
 
190
                try:
 
191
                        oldInfo=self.makeTextInfo(textInfos.POSITION_SELECTION)
 
192
                except:
 
193
                        gesture.send()
 
194
                        return
 
195
                gesture.send()
 
196
                if isScriptWaiting() or eventHandler.isPendingEvents("gainFocus"):
 
197
                        return
 
198
                api.processPendingEvents(processEventQueue=False)
 
199
                try:
 
200
                        newInfo=self.makeTextInfo(textInfos.POSITION_SELECTION)
 
201
                except:
 
202
                        return
 
203
                speech.speakSelectionChange(oldInfo,newInfo)
 
204
 
 
205
        __changeSelectionGestures = (
 
206
                "kb:shift+upArrow",
 
207
                "kb:shift+downArrow",
 
208
                "kb:shift+leftArrow",
 
209
                "kb:shift+rightArrow",
 
210
                "kb:shift+pageUp",
 
211
                "kb:shift+pageDown",
 
212
                "kb:shift+control+leftArrow",
 
213
                "kb:shift+control+rightArrow",
 
214
                "kb:shift+control+upArrow",
 
215
                "kb:shift+control+downArrow",
 
216
                "kb:shift+home",
 
217
                "kb:shift+end",
 
218
                "kb:shift+control+home",
 
219
                "kb:shift+control+end",
 
220
                "kb:control+a",
 
221
        )
 
222
 
 
223
        def initClass(self):
 
224
                for gesture in self.__changeSelectionGestures:
 
225
                        self.bindGesture(gesture, "caret_changeSelection")