Module displayModel
[hide private]
[frames] | no frames]

Source Code for Module displayModel

  1  from ctypes import * 
  2  from ctypes.wintypes import RECT 
  3  from comtypes import BSTR 
  4  import math 
  5  import colors 
  6  import XMLFormatting 
  7  import api 
  8  import winUser 
  9  import NVDAHelper 
 10  import textInfos 
 11  from textInfos.offsets import OffsetsTextInfo 
 12  import watchdog 
 13   
 14  _getWindowTextInRect=None 
 15  _requestTextChangeNotificationsForWindow=None 
 16  #: Objects that have registered for text change notifications. 
 17  _textChangeNotificationObjs=[] 
 18   
19 -def initialize():
20 global _getWindowTextInRect,_requestTextChangeNotificationsForWindow 21 _getWindowTextInRect=CFUNCTYPE(c_long,c_long,c_long,c_int,c_int,c_int,c_int,c_int,c_int,c_bool,POINTER(BSTR),POINTER(BSTR))(('displayModel_getWindowTextInRect',NVDAHelper.localLib),((1,),(1,),(1,),(1,),(1,),(1,),(1,),(1,),(1,),(2,),(2,))) 22 _requestTextChangeNotificationsForWindow=NVDAHelper.localLib.displayModel_requestTextChangeNotificationsForWindow
23
24 -def getWindowTextInRect(bindingHandle, windowHandle, left, top, right, bottom,minHorizontalWhitespace,minVerticalWhitespace,useXML=False):
25 text, cpBuf = watchdog.cancellableExecute(_getWindowTextInRect, bindingHandle, windowHandle, left, top, right, bottom,minHorizontalWhitespace,minVerticalWhitespace,useXML) 26 if not text or not cpBuf: 27 return u"",[] 28 29 characterRects = [] 30 cpBufIt = iter(cpBuf) 31 for cp in cpBufIt: 32 characterRects.append((ord(cp), ord(next(cpBufIt)), ord(next(cpBufIt)), ord(next(cpBufIt)))) 33 return text, characterRects
34
35 -def requestTextChangeNotifications(obj, enable):
36 """Request or cancel notifications for when the display text changes in an NVDAObject. 37 A textChange event (event_textChange) will be fired on the object when its text changes. 38 Note that this event does not provide any information about the changed text itself. 39 It is important to request that notifications be cancelled when you no longer require them or when the object is no longer in use, 40 as otherwise, resources will not be released. 41 @param obj: The NVDAObject for which text change notifications are desired. 42 @type obj: NVDAObject 43 @param enable: C{True} to enable notifications, C{False} to disable them. 44 @type enable: bool 45 """ 46 if not enable: 47 _textChangeNotificationObjs.remove(obj) 48 watchdog.cancellableExecute(_requestTextChangeNotificationsForWindow, obj.appModule.helperLocalBindingHandle, obj.windowHandle, enable) 49 if enable: 50 _textChangeNotificationObjs.append(obj)
51
52 -def textChangeNotify(windowHandle, left, top, right, bottom):
53 for obj in _textChangeNotificationObjs: 54 if windowHandle == obj.windowHandle: 55 # It is safe to call this event from this RPC thread. 56 # This avoids an extra core cycle. 57 obj.event_textChange()
58
59 -class DisplayModelTextInfo(OffsetsTextInfo):
60 61 minHorizontalWhitespace=8 62 minVerticalWhitespace=32 63 64 _cache__textAndRects = True
65 - def _get__textAndRects(self,useXML=False):
66 try: 67 left, top, width, height = self.obj.location 68 except TypeError: 69 # No location; nothing we can do. 70 return u"", [] 71 return getWindowTextInRect(self.obj.appModule.helperLocalBindingHandle, self.obj.windowHandle, left, top, left + width, top + height,self.minHorizontalWhitespace,self.minVerticalWhitespace,useXML)
72
73 - def _getStoryText(self):
74 return self._textAndRects[0]
75
76 - def _getStoryLength(self):
77 return len(self._getStoryText())
78
79 - def _getTextRange(self, start, end):
80 return self._getStoryText()[start:end]
81
82 - def getTextWithFields(self,formatConfig=None):
83 start=self._startOffset 84 end=self._endOffset 85 if start==end: 86 return u"" 87 text=self._get__textAndRects(useXML=True)[0] 88 if not text: 89 return u"" 90 text="<control>%s</control>"%text 91 commandList=XMLFormatting.XMLTextParser().parse(text) 92 #Strip unwanted commands and text from the start and the end to honour the requested offsets 93 stringOffset=0 94 for index in xrange(len(commandList)-1): 95 command=commandList[index] 96 if isinstance(command,basestring): 97 stringLen=len(command) 98 if (stringOffset+stringLen)<=start: 99 stringOffset+=stringLen 100 else: 101 del commandList[1:index-1] 102 commandList[2]=command[start-stringOffset:] 103 break 104 end=end-start 105 stringOffset=0 106 for index in xrange(1,len(commandList)-1): 107 command=commandList[index] 108 if isinstance(command,basestring): 109 stringLen=len(command) 110 if (stringOffset+stringLen)<end: 111 stringOffset+=stringLen 112 else: 113 commandList[index]=command[0:end-stringOffset] 114 del commandList[index+1:-1] 115 break 116 for item in commandList: 117 if isinstance(item,textInfos.FieldCommand) and isinstance(item.field,textInfos.FormatField): 118 self._normalizeFormatField(item.field) 119 return commandList
120
121 - def _normalizeFormatField(self,field):
122 field['bold']=True if field.get('bold')=="true" else False 123 field['italic']=True if field.get('italic')=="true" else False 124 field['underline']=True if field.get('underline')=="true" else False 125 color=field.get('color') 126 if color is not None: 127 field['color']=colors.RGB.fromCOLORREF(int(color)) 128 bkColor=field.get('background-color') 129 if bkColor is not None: 130 field['background-color']=colors.RGB.fromCOLORREF(int(bkColor))
131 132
133 - def _getPointFromOffset(self, offset):
134 text,rects=self._textAndRects 135 if not text or not rects or offset>=len(rects): 136 raise LookupError 137 x,y=rects[offset][:2] 138 return textInfos.Point(x, y)
139
140 - def _getOffsetFromPoint(self, x, y):
141 for charOffset, (charLeft, charTop, charRight, charBottom) in enumerate(self._textAndRects[1]): 142 if charLeft<=x<charRight and charTop<=y<charBottom: 143 return charOffset 144 raise LookupError
145
146 - def _getClosestOffsetFromPoint(self,x,y):
147 #Enumerate the character rectangles 148 a=enumerate(self._textAndRects[1]) 149 #Convert calculate center points for all the rectangles 150 b=((charOffset,(charLeft+(charRight-charLeft)/2,charTop+(charBottom-charTop)/2)) for charOffset,(charLeft,charTop,charRight,charBottom) in a) 151 #Calculate distances from all center points to the given x and y 152 #But place the distance before the character offset, to make sorting by distance easier 153 c=((math.sqrt(abs(x-cx)**2+abs(y-cy)**2),charOffset) for charOffset,(cx,cy) in b) 154 #produce a static list of distances and character offsets, sorted by distance 155 d=sorted(c) 156 #Return the lowest offset with the shortest distance 157 return d[0][1] if len(d)>0 else 0
158
159 - def _getNVDAObjectFromOffset(self,offset):
160 try: 161 p=self._getPointFromOffset(offset) 162 except (NotImplementedError,LookupError): 163 return self.obj 164 obj=api.getDesktopObject().objectFromPoint(p.x,p.y) 165 from NVDAObjects.window import Window 166 if not obj or not isinstance(obj,Window) or not winUser.isDescendantWindow(self.obj.windowHandle,obj.windowHandle): 167 return self.obj 168 return obj
169
170 - def _getOffsetsFromNVDAObject(self,obj):
171 l=obj.location 172 if not l: 173 raise RuntimeError 174 x=l[0]+(l[2]/2) 175 y=l[1]+(l[3]/2) 176 offset=self._getClosestOffsetFromPoint(x,y) 177 return offset,offset
178
179 - def _get_clipboardText(self):
180 return super(DisplayModelTextInfo,self).clipboardText.replace('\0',' ')
181
182 -class EditableTextDisplayModelTextInfo(DisplayModelTextInfo):
183 184 minHorizontalWhitespace=1 185 minVerticalWhitespace=4 186
187 - def _getCaretOffset(self):
188 caretRect = winUser.getGUIThreadInfo(self.obj.windowThreadID).rcCaret 189 objLocation=self.obj.location 190 objRect=RECT(objLocation[0],objLocation[1],objLocation[0]+objLocation[2],objLocation[1]+objLocation[3]) 191 tempPoint = winUser.POINT() 192 tempPoint.x=caretRect.left 193 tempPoint.y=caretRect.top 194 winUser.user32.ClientToScreen(self.obj.windowHandle, byref(tempPoint)) 195 caretRect.left=max(objRect.left,tempPoint.x) 196 caretRect.top=max(objRect.top,tempPoint.y) 197 tempPoint.x=caretRect.right 198 tempPoint.y=caretRect.bottom 199 winUser.user32.ClientToScreen(self.obj.windowHandle, byref(tempPoint)) 200 caretRect.right=min(objRect.right,tempPoint.x) 201 caretRect.bottom=min(objRect.bottom,tempPoint.y) 202 import speech 203 for charOffset, (charLeft, charTop, charRight, charBottom) in enumerate(self._textAndRects[1]): 204 #speech.speakMessage("caret %d,%d char %d,%d"%(caretRect.top,caretRect.bottom,charTop,charBottom)) 205 if caretRect.left>=charLeft and caretRect.right<=charRight and ((caretRect.top<=charTop and caretRect.bottom>=charBottom) or (caretRect.top>=charTop and caretRect.bottom<=charBottom)): 206 return charOffset 207 raise RuntimeError
208
209 - def _setCaretOffset(self,offset):
210 rects=self._textAndRects[1] 211 if offset>=len(rects): 212 raise RuntimeError("offset %d out of range") 213 left,top,right,bottom=rects[offset] 214 x=left #+(right-left)/2 215 y=top+(bottom-top)/2 216 oldX,oldY=winUser.getCursorPos() 217 winUser.setCursorPos(x,y) 218 winUser.mouse_event(winUser.MOUSEEVENTF_LEFTDOWN,0,0,None,None) 219 winUser.mouse_event(winUser.MOUSEEVENTF_LEFTUP,0,0,None,None) 220 winUser.setCursorPos(oldX,oldY)
221
222 - def _getSelectionOffsets(self):
223 offset=self._getCaretOffset() 224 return offset,offset
225
226 - def _setSelectionOffsets(self,start,end):
227 if start!=end: 228 raise TypeError("Expanded selections not supported") 229 self._setCaretOffset(start)
230