~stomato463/+junk/nvdajp

« back to all changes in this revision

Viewing changes to source/textInfos/offsets.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
1
#textInfos/offsets.py
2
2
#A part of NonVisual Desktop Access (NVDA)
3
 
#Copyright (C) 2006-2007 NVDA Contributors <http://www.nvda-project.org/>
4
3
#This file is covered by the GNU General Public License.
5
4
#See the file COPYING for more details.
 
5
#Copyright (C) 2006 Michael Curran <mick@kulgan.net>, James Teh <jamie@jantrid.net>
6
6
 
7
7
import re
 
8
import ctypes
 
9
import NVDAHelper
 
10
import config
8
11
import textInfos
9
12
 
10
13
class Offsets(object):
127
130
        return offset
128
131
 
129
132
class OffsetsTextInfo(textInfos.TextInfo):
 
133
        """An abstract TextInfo for text implementations which represent ranges using numeric offsets relative to the start of the text.
 
134
        In such implementations, the start of the text is represented by 0 and the end is the length of the entire text.
 
135
        
 
136
        All subclasses must implement L{_getStoryLength}.
 
137
        Aside from this, there are two possible implementations:
 
138
                * If the underlying text implementation does not support retrieval of line offsets, L{_getStoryText} should be implemented.
 
139
                In this case, the base implementation of L{_getLineOffsets} will retrieve the entire text of the object and use text searching algorithms to find line offsets.
 
140
                This is very inefficient and should be avoided if possible.
 
141
                * Otherwise, subclasses must implement at least L{_getTextRange} and L{_getLineOffsets}.
 
142
                Retrieval of other offsets (e.g. L{_getWordOffsets}) should also be implemented if possible for greatest accuracy and efficiency.
 
143
        
 
144
        If a caret and/or selection should be supported, L{_getCaretOffset} and/or L{_getSelectionOffsets} should be implemented, respectively.
 
145
        To support conversion from/to screen points (e.g. for mouse tracking), L{_getOffsetFromPoint}/L{_getPointFromOffset} should be implemented.
 
146
        """
 
147
 
 
148
        detectFormattingAfterCursorMaybeSlow=True #: honours documentFormatting config option if true - set to false if this is not at all slow.
 
149
        useUniscribe=True #Use uniscribe to calculate word offsets etc
 
150
 
130
151
 
131
152
        def __eq__(self,other):
132
153
                if self is other or (isinstance(other,OffsetsTextInfo) and self._startOffset==other._startOffset and self._endOffset==other._endOffset):
150
171
                raise NotImplementedError
151
172
 
152
173
        def _getStoryText(self):
 
174
                """Retrieve the entire text of the object.
 
175
                @return: The entire text of the object.
 
176
                @rtype: unicode
 
177
                """
153
178
                raise NotImplementedError
154
179
 
155
180
        def _getTextRange(self,start,end):
 
181
                """Retrieve the text in a given offset range.
 
182
                @param start: The start offset.
 
183
                @type start: int
 
184
                @param end: The end offset (exclusive).
 
185
                @type end: int
 
186
                @return: The text contained in the requested range.
 
187
                @rtype: unicode
 
188
                """
156
189
                raise NotImplementedError
157
190
 
158
191
        def _getFormatFieldAndOffsets(self,offset,formatConfig,calculateOffsets=True):
 
192
                """Retrieve the formatting information for a given offset and the offsets spanned by that field.
 
193
                Subclasses must override this if support for text formatting is desired.
 
194
                The base implementation associates text with line numbers if possible.
 
195
                """
159
196
                formatField=textInfos.FormatField()
160
197
                startOffset,endOffset=self._startOffset,self._endOffset
161
198
                if formatConfig["reportLineNumber"]:
172
209
        def _getWordOffsets(self,offset):
173
210
                lineStart,lineEnd=self._getLineOffsets(offset)
174
211
                lineText=self._getTextRange(lineStart,lineEnd)
 
212
                #Convert NULL and non-breaking space to space to make sure that words will break on them
 
213
                lineText=lineText.translate({0:u' ',0xa0:u' '})
 
214
                if self.useUniscribe:
 
215
                        start=ctypes.c_int()
 
216
                        end=ctypes.c_int()
 
217
                        #uniscribe does some strange things when you give it a string  with not more than two alphanumeric chars in a row.
 
218
                        #Inject two alphanumeric characters at the end to fix this 
 
219
                        lineText+="xx"
 
220
                        if NVDAHelper.localLib.calculateWordOffsets(lineText,len(lineText),offset-lineStart,ctypes.byref(start),ctypes.byref(end)):
 
221
                                return start.value+lineStart,min(end.value+lineStart,lineEnd)
 
222
                #Fall back to the older word offsets detection that only breaks on non alphanumeric
175
223
                start=findStartOfWord(lineText,offset-lineStart)+lineStart
176
224
                end=findEndOfWord(lineText,offset-lineStart)+lineStart
177
225
                return [start,end]
178
226
 
179
227
        def _getLineNumFromOffset(self,offset):
180
 
                raise NotImplementedError
 
228
                return None
 
229
 
181
230
 
182
231
        def _getLineOffsets(self,offset):
183
232
                text=self._getStoryText()
198
247
        def _getOffsetFromPoint(self,x,y):
199
248
                raise NotImplementedError
200
249
 
 
250
        def _getNVDAObjectFromOffset(self,offset):
 
251
                raise NotImplementedError
 
252
 
 
253
        def _getOffsetsFromNVDAObject(self,obj):
 
254
                raise NotImplementedError
 
255
 
201
256
        def __init__(self,obj,position):
 
257
                """Constructor.
 
258
                Subclasses may extend this to perform implementation specific initialisation, calling their superclass method afterwards.
 
259
                """
202
260
                super(OffsetsTextInfo,self).__init__(obj,position)
 
261
                from NVDAObjects import NVDAObject
203
262
                if isinstance(position,textInfos.Point):
204
263
                        offset=self._getOffsetFromPoint(position.x,position.y)
205
264
                        position=Offsets(offset,offset)
 
265
                elif isinstance(position,NVDAObject):
 
266
                        start,end=self._getOffsetsFromNVDAObject(position)
 
267
                        position=textInfos.offsets.Offsets(start,end)
206
268
                if position==textInfos.POSITION_FIRST:
207
269
                        self._startOffset=self._endOffset=0
208
270
                elif position==textInfos.POSITION_LAST:
220
282
                else:
221
283
                        raise NotImplementedError("position: %s not supported"%position)
222
284
 
 
285
        def _get_NVDAObjectAtStart(self):
 
286
                return self._getNVDAObjectFromOffset(self._startOffset)
 
287
 
223
288
        def _getUnitOffsets(self,unit,offset):
224
289
                if unit==textInfos.UNIT_CHARACTER:
225
290
                        offsetsFunc=self._getCharacterOffsets
231
296
                        offsetsFunc=self._getParagraphOffsets
232
297
                elif unit==textInfos.UNIT_READINGCHUNK:
233
298
                        offsetsFunc=self._getReadingChunkOffsets
 
299
                elif unit==textInfos.UNIT_STORY:
 
300
                        return 0,self._getStoryLength()
234
301
                else:
235
302
                        raise ValueError("unknown unit: %s"%unit)
236
303
                return offsetsFunc(offset)
292
359
        def getTextWithFields(self,formatConfig=None):
293
360
                if not formatConfig:
294
361
                        formatConfig=config.conf["documentFormatting"]
295
 
                if not formatConfig['detectFormatAfterCursor']:
 
362
                if self.detectFormattingAfterCursorMaybeSlow and not formatConfig['detectFormatAfterCursor']:
296
363
                        field,(boundStart,boundEnd)=self._getFormatFieldAndOffsets(self._startOffset,formatConfig,calculateOffsets=False)
297
364
                        text=self.text
298
365
                        return [textInfos.FieldCommand('formatChange',field),text]
377
444
                else:
378
445
                        # Start searching one past the start to avoid finding the current match.
379
446
                        inText=self._getTextRange(self._startOffset+1,self._getStoryLength())
380
 
                m=re.search(re.escape(text),inText,re.IGNORECASE)
 
447
                m=re.search(re.escape(text),inText,(0 if caseSensitive else re.IGNORECASE)|re.UNICODE)
381
448
                if not m:
382
449
                        return False
383
450
                if reverse: