Package NVDAObjects :: Package window :: Module edit
[hide private]
[frames] | no frames]

Source Code for Module NVDAObjects.window.edit

  1  #A part of NonVisual Desktop Access (NVDA) 
  2  #Copyright (C) 2006-2008 NVDA Contributors <http://www.nvda-project.org/> 
  3  #This file is covered by the GNU General Public License. 
  4  #See the file COPYING for more details. 
  5   
  6  import locale 
  7  import sys 
  8  import comtypes.client 
  9  import struct 
 10  import ctypes 
 11  import pythoncom 
 12  import win32clipboard 
 13  import oleTypes 
 14  import colors 
 15  import globalVars 
 16  import eventHandler 
 17  import comInterfaces.tom 
 18  from logHandler import log 
 19  import config 
 20  import speech 
 21  import winKernel 
 22  import api 
 23  import winUser 
 24  import textInfos.offsets 
 25  from keyboardHandler import KeyboardInputGesture 
 26  from scriptHandler import isScriptWaiting 
 27  import IAccessibleHandler 
 28  import controlTypes 
 29  from . import Window 
 30  from .. import NVDAObjectTextInfo 
 31  from ..behaviors import EditableTextWithAutoSelectDetection 
 32  import braille 
 33  import watchdog 
 34   
 35  selOffsetsAtLastCaretEvent=None 
 36   
 37  #Edit control window messages 
 38  EM_GETSEL=176 
 39  EM_SETSEL=177 
 40  EM_SCROLLCARET=0xb7 
 41  EM_GETLINE=196 
 42  EM_GETLINECOUNT=186 
 43  EM_LINEFROMCHAR=201 
 44  EM_LINEINDEX=187 
 45  EM_LINELENGTH=193 
 46  EM_POSFROMCHAR=214  
 47  EM_CHARFROMPOS=215 
 48  EM_GETFIRSTVISIBLELINE=0x0ce 
 49  #Rich edit messages 
 50  EM_EXGETSEL=winUser.WM_USER+52 
 51  EM_EXLINEFROMCHAR=winUser.WM_USER+54 
 52  EM_EXSETSEL=winUser.WM_USER+55 
 53  EM_GETCHARFORMAT=winUser.WM_USER+58 
 54  EM_GETPARAFORMAT=winUser.WM_USER+61 
 55  EM_GETTEXTRANGE=winUser.WM_USER+75 
 56  EM_FINDWORDBREAK=winUser.WM_USER+76 
 57  #Rich edit 2.0 messages 
 58  EM_GETTEXTEX=winUser.WM_USER+94 
 59  EM_GETTEXTLENGTHEX=winUser.WM_USER+95 
 60  #Rich edit 4.0 messages 
 61  EM_GETPAGE=winUser.WM_USER+228 
 62   
 63  LF_FACESIZE=32 
 64   
 65  #structures 
 66   
67 -class PointLStruct(ctypes.Structure):
68 _fields_=[ 69 ('x',ctypes.c_long), 70 ('y',ctypes.c_long), 71 ]
72
73 -class CharRangeStruct(ctypes.Structure):
74 _fields_=[ 75 ('cpMin',ctypes.c_long), 76 ('cpMax',ctypes.c_long), 77 ]
78
79 -class TextRangeUStruct(ctypes.Structure):
80 _fields_=[ 81 ('chrg',CharRangeStruct), 82 ('lpstrText',ctypes.c_wchar_p), 83 ]
84
85 -class TextRangeAStruct(ctypes.Structure):
86 _fields_=[ 87 ('chrg',CharRangeStruct), 88 ('lpstrText',ctypes.c_char_p), 89 ]
90 91 CFM_LINK=0x20 92 CFE_AUTOBACKCOLOR=0x4000000 93 CFE_AUTOCOLOR=0x40000000 94 CFE_BOLD=1 95 CFE_ITALIC=2 96 CFE_UNDERLINE=4 97 CFE_STRIKEOUT=8 98 CFE_PROTECTED=16 99 CFE_SUBSCRIPT=0x00010000 # Superscript and subscript are 100 CFE_SUPERSCRIPT=0x00020000 # mutually exclusive 101 102 SCF_SELECTION=0x1 103
104 -class CharFormat2WStruct(ctypes.Structure):
105 _fields_=[ 106 ('cbSize',ctypes.c_uint), 107 ('dwMask',ctypes.wintypes.DWORD), 108 ('dwEffects',ctypes.wintypes.DWORD), 109 ('yHeight',ctypes.c_long), 110 ('yOffset',ctypes.c_long), 111 ('crTextColor',ctypes.wintypes.COLORREF), 112 ('bCharSet',ctypes.c_byte), 113 ('bPitchAndFamily',ctypes.c_byte), 114 ('szFaceName',ctypes.c_wchar*LF_FACESIZE), 115 ('wWeight',ctypes.wintypes.WORD), 116 ('sSpacing',ctypes.c_short), 117 ('crBackColor',ctypes.wintypes.COLORREF), 118 ('lcid',ctypes.wintypes.LCID), 119 ('dwReserved',ctypes.wintypes.DWORD), 120 ('sStyle',ctypes.c_short), 121 ('wKerning',ctypes.wintypes.WORD), 122 ('bUnderlineType',ctypes.c_byte), 123 ('bAnimation',ctypes.c_byte), 124 ('bRevAuthor',ctypes.c_byte), 125 ('bReserved1',ctypes.c_byte), 126 ]
127
128 -class CharFormat2AStruct(ctypes.Structure):
129 _fields_=CharFormat2WStruct._fields_[:] 130 _fields_[8]=('szFaceName',ctypes.c_char*LF_FACESIZE)
131
132 -class getTextExStruct(ctypes.Structure):
133 _fields_=[ 134 ('cb',ctypes.wintypes.DWORD), 135 ('flags',ctypes.wintypes.DWORD), 136 ('codepage',ctypes.c_uint), 137 ('lpDefaultChar',ctypes.wintypes.LPCSTR), 138 ('lpUsedDefChar',ctypes.c_void_p), 139 ]
140
141 -class getTextLengthExStruct(ctypes.Structure):
142 _fields_=[ 143 ('flags',ctypes.wintypes.DWORD), 144 ('codepage',ctypes.c_uint), 145 ]
146 147 #getTextEx flags 148 GT_DEFAULT=0 149 GT_USECRLF=1 150 GT_SELECTION=2 151 GT_RAWTEXT=4 152 GT_NOHIDDENTEXT=8 153 154 #getTextLengthEx flags 155 GTL_DEFAULT=0 156 GTL_USECRLF=1 157 GTL_PRECISE=2 158 GTL_CLOSE=4 159 GTL_NUMCHARS=8 160 GTL_NUMBYTES=16 161 162 #findWordbreak constants 163 WB_CLASSIFY=3 164 WB_MOVEWORDLEFT=4 165 WB_MOVEWORDRIGHT=5 166 WB_LEFTBREAK=6 167 WB_RIGHTBREAK=7 168
169 -class EditTextInfo(textInfos.offsets.OffsetsTextInfo):
170
171 - def _getPointFromOffset(self,offset):
172 if self.obj.editAPIVersion==1: 173 processHandle=self.obj.processHandle 174 internalP=winKernel.virtualAllocEx(processHandle,None,ctypes.sizeof(PointLStruct),winKernel.MEM_COMMIT,winKernel.PAGE_READWRITE) 175 try: 176 p=PointLStruct(0,0) 177 winKernel.writeProcessMemory(processHandle,internalP,ctypes.byref(p),ctypes.sizeof(p),None) 178 watchdog.cancellableSendMessage(self.obj.windowHandle,EM_POSFROMCHAR,internalP,offset) 179 winKernel.readProcessMemory(processHandle,internalP,ctypes.byref(p),ctypes.sizeof(p),None) 180 finally: 181 winKernel.virtualFreeEx(processHandle,internalP,0,winKernel.MEM_RELEASE) 182 point=textInfos.Point(p.x,p.y) 183 else: 184 res=watchdog.cancellableSendMessage(self.obj.windowHandle,EM_POSFROMCHAR,offset,None) 185 point=textInfos.Point(winUser.LOWORD(res),winUser.HIWORD(res)) 186 (left,top,width,height)=self.obj.location 187 if point.x and point.y: 188 point.x=point.x+left 189 point.y=point.y+top 190 return point 191 else: 192 raise NotImplementedError
193
194 - def _getOffsetFromPoint(self,x,y):
195 (left,top,width,height)=self.obj.location 196 if self.obj.editAPIVersion>=1: 197 processHandle=self.obj.processHandle 198 internalP=winKernel.virtualAllocEx(processHandle,None,ctypes.sizeof(PointLStruct),winKernel.MEM_COMMIT,winKernel.PAGE_READWRITE) 199 try: 200 p=PointLStruct(x-left,y-top) 201 winKernel.writeProcessMemory(processHandle,internalP,ctypes.byref(p),ctypes.sizeof(p),None) 202 offset=watchdog.cancellableSendMessage(self.obj.windowHandle,EM_CHARFROMPOS,0,internalP) 203 finally: 204 winKernel.virtualFreeEx(processHandle,internalP,0,winKernel.MEM_RELEASE) 205 else: 206 p=(x-left)+((y-top)<<16) 207 offset=watchdog.cancellableSendMessage(self.obj.windowHandle,EM_CHARFROMPOS,0,p)&0xffff 208 return offset
209
210 - def _getCharFormat(self,offset):
211 oldSel=self._getSelectionOffsets() 212 if oldSel!=(offset,offset+1): 213 self._setSelectionOffsets(offset,offset+1) 214 if self.obj.isWindowUnicode: 215 charFormatStruct=CharFormat2WStruct 216 else: 217 charFormatStruct=CharFormat2AStruct 218 charFormat=charFormatStruct() 219 charFormat.cbSize=ctypes.sizeof(charFormatStruct) 220 processHandle=self.obj.processHandle 221 internalCharFormat=winKernel.virtualAllocEx(processHandle,None,ctypes.sizeof(charFormat),winKernel.MEM_COMMIT,winKernel.PAGE_READWRITE) 222 try: 223 winKernel.writeProcessMemory(processHandle,internalCharFormat,ctypes.byref(charFormat),ctypes.sizeof(charFormat),None) 224 watchdog.cancellableSendMessage(self.obj.windowHandle,EM_GETCHARFORMAT,SCF_SELECTION, internalCharFormat) 225 winKernel.readProcessMemory(processHandle,internalCharFormat,ctypes.byref(charFormat),ctypes.sizeof(charFormat),None) 226 finally: 227 winKernel.virtualFreeEx(processHandle,internalCharFormat,0,winKernel.MEM_RELEASE) 228 if oldSel!=(offset,offset+1): 229 self._setSelectionOffsets(oldSel[0],oldSel[1]) 230 return charFormat
231
232 - def _getFormatFieldAndOffsets(self,offset,formatConfig,calculateOffsets=True):
233 #Basic edit fields do not support formatting at all 234 if self.obj.editAPIVersion<1: 235 return super(EditTextInfo,self)._getFormatFieldAndOffsets(offset,formatConfig,calculateOffsets=calculateOffsets) 236 if calculateOffsets: 237 startOffset,endOffset=self._getWordOffsets(offset) 238 else: 239 startOffset,endOffset=self._startOffset,self._endOffset 240 formatField=textInfos.FormatField() 241 charFormat=None 242 if formatConfig["reportFontName"]: 243 if charFormat is None: charFormat=self._getCharFormat(offset) 244 formatField["font-name"]=charFormat.szFaceName 245 if formatConfig["reportFontSize"]: 246 if charFormat is None: charFormat=self._getCharFormat(offset) 247 formatField["font-size"]="%spt"%(charFormat.yHeight/20) 248 if formatConfig["reportFontAttributes"]: 249 if charFormat is None: charFormat=self._getCharFormat(offset) 250 formatField["bold"]=bool(charFormat.dwEffects&CFE_BOLD) 251 formatField["italic"]=bool(charFormat.dwEffects&CFE_ITALIC) 252 formatField["underline"]=bool(charFormat.dwEffects&CFE_UNDERLINE) 253 formatField["strikethrough"]=bool(charFormat.dwEffects&CFE_STRIKEOUT) 254 if charFormat.dwEffects&CFE_SUBSCRIPT: 255 formatField["text-position"]="sub" 256 elif charFormat.dwEffects&CFE_SUPERSCRIPT: 257 formatField["text-position"]="super" 258 if formatConfig["reportColor"]: 259 if charFormat is None: charFormat=self._getCharFormat(offset) 260 formatField["color"]=colors.RGB.fromCOLORREF(charFormat.crTextColor) if not charFormat.dwEffects&CFE_AUTOCOLOR else _("default color") 261 formatField["background-color"]=colors.RGB.fromCOLORREF(charFormat.crBackColor) if not charFormat.dwEffects&CFE_AUTOBACKCOLOR else _("default color") 262 if formatConfig["reportLineNumber"]: 263 formatField["line-number"]=self._getLineNumFromOffset(offset)+1 264 if formatConfig["reportLinks"]: 265 if charFormat is None: charFormat=self._getCharFormat(offset) 266 formatField["link"]=bool(charFormat.dwEffects&CFM_LINK) 267 return formatField,(startOffset,endOffset)
268
269 - def _getSelectionOffsets(self):
270 if self.obj.editAPIVersion>=1: 271 charRange=CharRangeStruct() 272 processHandle=self.obj.processHandle 273 internalCharRange=winKernel.virtualAllocEx(processHandle,None,ctypes.sizeof(charRange),winKernel.MEM_COMMIT,winKernel.PAGE_READWRITE) 274 try: 275 watchdog.cancellableSendMessage(self.obj.windowHandle,EM_EXGETSEL,0, internalCharRange) 276 winKernel.readProcessMemory(processHandle,internalCharRange,ctypes.byref(charRange),ctypes.sizeof(charRange),None) 277 finally: 278 winKernel.virtualFreeEx(processHandle,internalCharRange,0,winKernel.MEM_RELEASE) 279 return (charRange.cpMin,charRange.cpMax) 280 else: 281 start=ctypes.c_uint() 282 end=ctypes.c_uint() 283 res=watchdog.cancellableSendMessage(self.obj.windowHandle,EM_GETSEL,ctypes.byref(start),ctypes.byref(end)) 284 return start.value,end.value
285
286 - def _setSelectionOffsets(self,start,end):
287 if self.obj.editAPIVersion>=1: 288 charRange=CharRangeStruct() 289 charRange.cpMin=start 290 charRange.cpMax=end 291 processHandle=self.obj.processHandle 292 internalCharRange=winKernel.virtualAllocEx(processHandle,None,ctypes.sizeof(charRange),winKernel.MEM_COMMIT,winKernel.PAGE_READWRITE) 293 try: 294 winKernel.writeProcessMemory(processHandle,internalCharRange,ctypes.byref(charRange),ctypes.sizeof(charRange),None) 295 watchdog.cancellableSendMessage(self.obj.windowHandle,EM_EXSETSEL,0, internalCharRange) 296 finally: 297 winKernel.virtualFreeEx(processHandle,internalCharRange,0,winKernel.MEM_RELEASE) 298 else: 299 watchdog.cancellableSendMessage(self.obj.windowHandle,EM_SETSEL,start,end) 300 #Make sure the Window is always scrolled to the caret 301 watchdog.cancellableSendMessage(self.obj.windowHandle,EM_SCROLLCARET,0,0)
302
303 - def _getCaretOffset(self):
304 return self._getSelectionOffsets()[0]
305
306 - def _setCaretOffset(self,offset):
307 return self._setSelectionOffsets(offset,offset)
308
309 - def _getStoryText(self):
310 if controlTypes.STATE_PROTECTED in self.obj.states: 311 return '*'*(self._getStoryLength()-1) 312 return self.obj.windowText
313
314 - def _getStoryLength(self):
315 if self.obj.editAPIVersion>=2: 316 info=getTextLengthExStruct() 317 info.flags=GTL_NUMCHARS 318 if self.obj.isWindowUnicode: 319 info.codepage=1200 320 else: 321 info.codepage=0 322 processHandle=self.obj.processHandle 323 internalInfo=winKernel.virtualAllocEx(processHandle,None,ctypes.sizeof(info),winKernel.MEM_COMMIT,winKernel.PAGE_READWRITE) 324 try: 325 winKernel.writeProcessMemory(processHandle,internalInfo,ctypes.byref(info),ctypes.sizeof(info),None) 326 textLen=watchdog.cancellableSendMessage(self.obj.windowHandle,EM_GETTEXTLENGTHEX,internalInfo,0) 327 finally: 328 winKernel.virtualFreeEx(processHandle,internalInfo,0,winKernel.MEM_RELEASE) 329 return textLen+1 330 else: 331 return watchdog.cancellableSendMessage(self.obj.windowHandle,winUser.WM_GETTEXTLENGTH,0,0)+1
332
333 - def _getLineCount(self):
334 return watchdog.cancellableSendMessage(self.obj.windowHandle,EM_GETLINECOUNT,0,0)
335
336 - def _getTextRange(self,start,end):
337 if self.obj.editAPIVersion>=2: 338 bufLen=((end-start)+1)*2 339 if self.obj.isWindowUnicode: 340 textRange=TextRangeUStruct() 341 else: 342 textRange=TextRangeAStruct() 343 textRange.chrg.cpMin=start 344 textRange.chrg.cpMax=end 345 processHandle=self.obj.processHandle 346 internalBuf=winKernel.virtualAllocEx(processHandle,None,bufLen,winKernel.MEM_COMMIT,winKernel.PAGE_READWRITE) 347 try: 348 textRange.lpstrText=internalBuf 349 internalTextRange=winKernel.virtualAllocEx(processHandle,None,ctypes.sizeof(textRange),winKernel.MEM_COMMIT,winKernel.PAGE_READWRITE) 350 try: 351 winKernel.writeProcessMemory(processHandle,internalTextRange,ctypes.byref(textRange),ctypes.sizeof(textRange),None) 352 res=watchdog.cancellableSendMessage(self.obj.windowHandle,EM_GETTEXTRANGE,0,internalTextRange) 353 finally: 354 winKernel.virtualFreeEx(processHandle,internalTextRange,0,winKernel.MEM_RELEASE) 355 buf=(ctypes.c_byte*bufLen)() 356 winKernel.readProcessMemory(processHandle,internalBuf,buf,bufLen,None) 357 finally: 358 winKernel.virtualFreeEx(processHandle,internalBuf,0,winKernel.MEM_RELEASE) 359 if self.obj.isWindowUnicode or (res>1 and (buf[res]!=0 or buf[res+1]!=0)): 360 return ctypes.cast(buf,ctypes.c_wchar_p).value 361 else: 362 return unicode(ctypes.cast(buf,ctypes.c_char_p).value, errors="replace", encoding=locale.getlocale()[1]) 363 else: 364 return self._getStoryText()[start:end]
365
366 - def _getWordOffsets(self,offset):
367 if self.obj.editAPIVersion>=2: 368 start=watchdog.cancellableSendMessage(self.obj.windowHandle,EM_FINDWORDBREAK,WB_MOVEWORDLEFT,offset) 369 end=watchdog.cancellableSendMessage(self.obj.windowHandle,EM_FINDWORDBREAK,WB_MOVEWORDRIGHT,start) 370 if end<=offset: 371 start=end 372 end=watchdog.cancellableSendMessage(self.obj.windowHandle,EM_FINDWORDBREAK,WB_MOVEWORDRIGHT,offset) 373 return (start,end) 374 elif sys.getwindowsversion().major<6: #Implementation of standard edit field wordbreak behaviour (only breaks on space) 375 lineStart,lineEnd=self._getLineOffsets(offset) 376 if offset>=lineEnd: 377 return offset,offset+1 378 lineText=self._getTextRange(lineStart,lineEnd) 379 lineTextLen=len(lineText) 380 relativeOffset=offset-lineStart 381 if relativeOffset>=lineTextLen: 382 return offset,offset+1 383 #cariage returns are always treeted as a word by themselves 384 if lineText[relativeOffset] in ['\r','\n']: 385 return offset,offset+1 386 #Find the start of the word (possibly moving through space to get to the word first) 387 tempOffset=relativeOffset 388 while tempOffset>=0 and lineText[tempOffset].isspace(): 389 tempOffset-=1 390 while tempOffset>=0 and not lineText[tempOffset].isspace(): 391 tempOffset-=1 392 tempOffset+=1 393 start=lineStart+tempOffset 394 startOnSpace=True if tempOffset<lineTextLen and lineText[tempOffset].isspace() else False 395 #Find the end of the word and trailing space 396 tempOffset=relativeOffset 397 if startOnSpace: 398 while tempOffset<lineTextLen and lineText[tempOffset].isspace(): 399 tempOffset+=1 400 while tempOffset<lineTextLen and not lineText[tempOffset].isspace(): 401 tempOffset+=1 402 while tempOffset<lineTextLen and lineText[tempOffset].isspace(): 403 tempOffset+=1 404 end=lineStart+tempOffset 405 return start,end 406 else: 407 if self._getTextRange(offset,offset+1) in ['\r','\n']: 408 return offset,offset+1 409 else: 410 return super(EditTextInfo,self)._getWordOffsets(offset)
411 412
413 - def _getLineNumFromOffset(self,offset):
414 if self.obj.editAPIVersion>=1: 415 res=watchdog.cancellableSendMessage(self.obj.windowHandle,EM_EXLINEFROMCHAR,0,offset) 416 return res 417 else: 418 return watchdog.cancellableSendMessage(self.obj.windowHandle,EM_LINEFROMCHAR,offset,0)
419
420 - def _getLineOffsets(self,offset):
421 lineNum=self._getLineNumFromOffset(offset) 422 start=watchdog.cancellableSendMessage(self.obj.windowHandle,EM_LINEINDEX,lineNum,0) 423 length=watchdog.cancellableSendMessage(self.obj.windowHandle,EM_LINELENGTH,offset,0) 424 end=start+length 425 #If we just seem to get invalid line info, calculate manually 426 if start<=0 and end<=0 and lineNum<=0 and self._getLineCount()<=0 and self._getStoryLength()>0: 427 return super(EditTextInfo,self)._getLineOffsets(offset) 428 #Some edit controls that show both line feed and carage return can give a length not including the line feed 429 if end<=offset: 430 end=offset+1 431 #edit controls lye about their line length 432 limit=self._getStoryLength() 433 while self._getLineNumFromOffset(end)==lineNum and end<limit: 434 end+=1 435 return (start,end)
436
437 - def _getParagraphOffsets(self,offset):
438 return self._getLineOffsets(offset)
439 440 ITextDocumentUnitsToNVDAUnits={ 441 comInterfaces.tom.tomCharacter:textInfos.UNIT_CHARACTER, 442 comInterfaces.tom.tomWord:textInfos.UNIT_WORD, 443 comInterfaces.tom.tomLine:textInfos.UNIT_LINE, 444 comInterfaces.tom.tomSentence:textInfos.UNIT_SENTENCE, 445 comInterfaces.tom.tomParagraph:textInfos.UNIT_PARAGRAPH, 446 comInterfaces.tom.tomStory:textInfos.UNIT_STORY, 447 } 448 449 NVDAUnitsToITextDocumentUnits={ 450 textInfos.UNIT_CHARACTER:comInterfaces.tom.tomCharacter, 451 textInfos.UNIT_WORD:comInterfaces.tom.tomWord, 452 textInfos.UNIT_LINE:comInterfaces.tom.tomLine, 453 textInfos.UNIT_SENTENCE:comInterfaces.tom.tomSentence, 454 textInfos.UNIT_PARAGRAPH:comInterfaces.tom.tomParagraph, 455 textInfos.UNIT_STORY:comInterfaces.tom.tomStory, 456 textInfos.UNIT_READINGCHUNK:comInterfaces.tom.tomSentence, 457 } 458
459 -class ITextDocumentTextInfo(textInfos.TextInfo):
460
461 - def _get_pointAtStart(self):
462 p=textInfos.Point(0,0) 463 (p.x,p.y)=self._rangeObj.GetPoint(comInterfaces.tom.tomStart) 464 if p.x and p.y: 465 return p 466 else: 467 raise NotImplementedError
468
469 - def _getCharFormat(self,range):
470 oldSel=self.obj.ITextSelectionObject.duplicate 471 if not (oldSel.start==range.start and oldSel.end==range.end): 472 self.obj.ITextSelectionObject.start=range.start 473 self.obj.ITextSelectionObject.end=range.end 474 if self.obj.isWindowUnicode: 475 charFormatStruct=CharFormat2WStruct 476 else: 477 charFormatStruct=CharFormat2AStruct 478 charFormat=charFormatStruct() 479 charFormat.cbSize=ctypes.sizeof(charFormatStruct) 480 processHandle=self.obj.processHandle 481 internalCharFormat=winKernel.virtualAllocEx(processHandle,None,ctypes.sizeof(charFormat),winKernel.MEM_COMMIT,winKernel.PAGE_READWRITE) 482 try: 483 winKernel.writeProcessMemory(processHandle,internalCharFormat,ctypes.byref(charFormat),ctypes.sizeof(charFormat),None) 484 watchdog.cancellableSendMessage(self.obj.windowHandle,EM_GETCHARFORMAT,SCF_SELECTION, internalCharFormat) 485 winKernel.readProcessMemory(processHandle,internalCharFormat,ctypes.byref(charFormat),ctypes.sizeof(charFormat),None) 486 finally: 487 winKernel.virtualFreeEx(processHandle,internalCharFormat,0,winKernel.MEM_RELEASE) 488 if not (oldSel.start==range.start and oldSel.end==range.end): 489 self.obj.ITextSelectionObject.start=oldSel.start 490 self.obj.ITextSelectionObject.end=oldSel.end 491 return charFormat
492
493 - def _getFormatFieldAtRange(self,range,formatConfig):
494 formatField=textInfos.FormatField() 495 fontObj=None 496 paraFormatObj=None 497 charFormat=None 498 if formatConfig["reportAlignment"]: 499 if not paraFormatObj: paraFormatObj=range.para 500 alignment=paraFormatObj.alignment 501 if alignment==comInterfaces.tom.tomAlignLeft: 502 formatField["text-align"]="left" 503 elif alignment==comInterfaces.tom.tomAlignCenter: 504 formatField["text-align"]="center" 505 elif alignment==comInterfaces.tom.tomAlignRight: 506 formatField["text-align"]="right" 507 elif alignment==comInterfaces.tom.tomAlignJustify: 508 formatField["text-align"]="justify" 509 if formatConfig["reportLineNumber"]: 510 formatField["line-number"]=range.getIndex(comInterfaces.tom.tomLine) 511 if formatConfig["reportFontName"]: 512 if not fontObj: fontObj=range.font 513 formatField["font-name"]=fontObj.name 514 if formatConfig["reportFontSize"]: 515 if not fontObj: fontObj=range.font 516 formatField["font-size"]="%spt"%fontObj.size 517 if formatConfig["reportFontAttributes"]: 518 if not fontObj: fontObj=range.font 519 formatField["bold"]=bool(fontObj.bold) 520 formatField["italic"]=bool(fontObj.italic) 521 formatField["underline"]=bool(fontObj.underline) 522 formatField["strikethrough"]=bool(fontObj.StrikeThrough) 523 if fontObj.superscript: 524 formatField["text-position"]="super" 525 elif fontObj.subscript: 526 formatField["text-position"]="sub" 527 if formatConfig["reportLinks"]: 528 if charFormat is None: charFormat=self._getCharFormat(range) 529 formatField["link"]=bool(charFormat.dwEffects&CFM_LINK) 530 return formatField
531
532 - def _expandFormatRange(self,range,formatConfig):
533 startLimit=self._rangeObj.start 534 endLimit=self._rangeObj.end 535 chunkRange=range.duplicate 536 if formatConfig["reportLineNumber"]: 537 chunkRange.expand(comInterfaces.tom.tomLine) 538 else: 539 chunkRange.expand(comInterfaces.tom.tomParagraph) 540 chunkStart=chunkRange.start 541 chunkEnd=chunkRange.end 542 if startLimit<chunkStart: 543 startLimit=chunkStart 544 if endLimit>chunkEnd: 545 endLimit=chunkEnd 546 #range.moveEnd(comInterfaces.tom.tomCharFormat,1) 547 range.expand(comInterfaces.tom.tomCharFormat) 548 if range.end>endLimit: 549 range.end=endLimit 550 if range.start<startLimit: 551 range.start=startLimit
552
553 - def _getEmbeddedObjectLabel(self,embedRangeObj):
554 label=None 555 try: 556 o=embedRangeObj.GetEmbeddedObject() 557 except comtypes.COMError: 558 o=None 559 if not o: 560 return None 561 import oleacc 562 try: 563 label=o.QueryInterface(oleacc.IAccessible).accName(0); 564 except comtypes.COMError: 565 pass 566 if label: 567 return label 568 left,top=embedRangeObj.GetPoint(comInterfaces.tom.tomStart) 569 right,bottom=embedRangeObj.GetPoint(comInterfaces.tom.tomEnd) 570 import displayModel 571 label=displayModel.getWindowTextInRect(self.obj.appModule.helperLocalBindingHandle, self.obj.windowHandle, left, top, right, bottom+10,1,1)[0] 572 if label and not label.isspace(): 573 return label 574 try: 575 dataObj=o.QueryInterface(oleTypes.IDataObject) 576 except comtypes.COMError: 577 dataObj=None 578 if dataObj: 579 try: 580 dataObj=pythoncom._univgw.interface(hash(dataObj),pythoncom.IID_IDataObject) 581 format=(win32clipboard.CF_UNICODETEXT, None, pythoncom.DVASPECT_CONTENT, -1, pythoncom.TYMED_HGLOBAL) 582 medium=dataObj.GetData(format) 583 buf=ctypes.create_string_buffer(medium.data) 584 buf=ctypes.cast(buf,ctypes.c_wchar_p) 585 label=buf.value 586 except: 587 pass 588 if label: 589 return label 590 try: 591 oleObj=o.QueryInterface(oleTypes.IOleObject) 592 label=oleObj.GetUserType(1) 593 except comtypes.COMError: 594 pass 595 return label
596
597 - def _getTextAtRange(self,rangeObj):
598 embedRangeObj=None 599 bufText=rangeObj.text 600 if bufText is None: 601 bufText="" 602 newTextList=[] 603 start=rangeObj.start 604 for offset in range(len(bufText)): 605 if ord(bufText[offset])==0xfffc: 606 if embedRangeObj is None: embedRangeObj=rangeObj.duplicate 607 embedRangeObj.setRange(start+offset,start+offset+1) 608 label=self._getEmbeddedObjectLabel(embedRangeObj) 609 if label: 610 newTextList.append(label) 611 else: 612 newTextList.append(_("embedded object")) 613 else: 614 newTextList.append(bufText[offset]) 615 return "".join(newTextList)
616
617 - def __init__(self,obj,position,_rangeObj=None):
618 super(ITextDocumentTextInfo,self).__init__(obj,position) 619 if _rangeObj: 620 self._rangeObj=_rangeObj.Duplicate 621 return 622 if isinstance(position,textInfos.Point): 623 self._rangeObj=self.obj.ITextDocumentObject.rangeFromPoint(position.x,position.y) 624 elif position==textInfos.POSITION_ALL: 625 self._rangeObj=self.obj.ITextDocumentObject.range(0,0) 626 self._rangeObj.expand(comInterfaces.tom.tomStory) 627 elif position==textInfos.POSITION_SELECTION: 628 self._rangeObj=self.obj.ITextSelectionObject.duplicate 629 elif position==textInfos.POSITION_CARET: 630 self._rangeObj=self.obj.ITextSelectionObject.duplicate 631 self._rangeObj.Collapse(True) 632 elif position==textInfos.POSITION_FIRST: 633 self._rangeObj=self.obj.ITextDocumentObject.range(0,0) 634 elif position==textInfos.POSITION_LAST: 635 self._rangeObj=self.obj.ITextDocumentObject.range(0,0) 636 self._rangeObj.move(comInterfaces.tom.tomStory,1) 637 self._rangeObj.moveStart(comInterfaces.tom.tomCharacter,-1) 638 elif isinstance(position,textInfos.offsets.Offsets): 639 self._rangeObj=self.obj.ITextDocumentObject.range(position.startOffset,position.endOffset) 640 else: 641 raise NotImplementedError("position: %s"%position)
642
643 - def getTextWithFields(self,formatConfig=None):
644 if not formatConfig: 645 formatConfig=config.conf["documentFormatting"] 646 range=self._rangeObj.duplicate 647 range.collapse(True) 648 if not formatConfig["detectFormatAfterCursor"]: 649 range.expand(comInterfaces.tom.tomCharacter) 650 return [textInfos.FieldCommand("formatChange",self._getFormatFieldAtRange(range,formatConfig)), 651 self._getTextAtRange(self._rangeObj)] 652 commandList=[] 653 endLimit=self._rangeObj.end 654 while range.end<endLimit: 655 self._expandFormatRange(range,formatConfig) 656 commandList.append(textInfos.FieldCommand("formatChange",self._getFormatFieldAtRange(range,formatConfig))) 657 commandList.append(self._getTextAtRange(range)) 658 end=range.end 659 range.start=end 660 #Trying to set the start past the end of the document forces both start and end back to the previous offset, so catch this 661 if range.end<end: 662 break 663 return commandList
664
665 - def expand(self,unit):
666 if unit in NVDAUnitsToITextDocumentUnits: 667 self._rangeObj.Expand(NVDAUnitsToITextDocumentUnits[unit]) 668 else: 669 raise NotImplementedError("unit: %s"%unit)
670
671 - def compareEndPoints(self,other,which):
672 if which=="startToStart": 673 diff=self._rangeObj.Start-other._rangeObj.Start 674 elif which=="startToEnd": 675 diff=self._rangeObj.Start-other._rangeObj.End 676 elif which=="endToStart": 677 diff=self._rangeObj.End-other._rangeObj.Start 678 elif which=="endToEnd": 679 diff=self._rangeObj.End-other._rangeObj.End 680 else: 681 raise ValueError("bad argument - which: %s"%which) 682 if diff<0: 683 diff=-1 684 elif diff>0: 685 diff=1 686 return diff
687
688 - def setEndPoint(self,other,which):
689 if which=="startToStart": 690 self._rangeObj.Start=other._rangeObj.Start 691 elif which=="startToEnd": 692 self._rangeObj.Start=other._rangeObj.End 693 elif which=="endToStart": 694 self._rangeObj.End=other._rangeObj.Start 695 elif which=="endToEnd": 696 self._rangeObj.End=other._rangeObj.End 697 else: 698 raise ValueError("bad argument - which: %s"%which)
699
700 - def _get_isCollapsed(self):
701 if self._rangeObj.Start==self._rangeObj.End: 702 return True 703 else: 704 return False
705
706 - def collapse(self,end=False):
707 a=self._rangeObj.Start 708 b=self._rangeObj.end 709 startOffset=min(a,b) 710 endOffset=max(a,b) 711 if not end: 712 offset=startOffset 713 else: 714 offset=endOffset 715 self._rangeObj.SetRange(offset,offset)
716
717 - def copy(self):
718 return ITextDocumentTextInfo(self.obj,None,_rangeObj=self._rangeObj)
719
720 - def _get_text(self):
721 return self._getTextAtRange(self._rangeObj)
722
723 - def move(self,unit,direction,endPoint=None):
724 if unit in NVDAUnitsToITextDocumentUnits: 725 unit=NVDAUnitsToITextDocumentUnits[unit] 726 else: 727 raise NotImplementedError("unit: %s"%unit) 728 if endPoint=="start": 729 moveFunc=self._rangeObj.MoveStart 730 elif endPoint=="end": 731 moveFunc=self._rangeObj.MoveEnd 732 else: 733 moveFunc=self._rangeObj.Move 734 res=moveFunc(unit,direction) 735 return res
736
737 - def _get_bookmark(self):
738 return textInfos.offsets.Offsets(self._rangeObj.start,self._rangeObj.end)
739
740 - def updateCaret(self):
741 self.obj.ITextSelectionObject.start=self.obj.ITextSelectionObject.end=self._rangeObj.start
742
743 - def updateSelection(self):
744 self.obj.ITextSelectionObject.start=self._rangeObj.start 745 self.obj.ITextSelectionObject.end=self._rangeObj.end
746
747 -class Edit(EditableTextWithAutoSelectDetection, Window):
748 749 editAPIVersion=0 750 editAPIUnicode=True 751 useITextDocumentSupport=False 752 editValueUnit=textInfos.UNIT_LINE 753
754 - def _get_TextInfo(self):
755 if self.editAPIVersion>1 and (self.useITextDocumentSupport or self.windowClassName.endswith('PT')) and self.ITextDocumentObject: 756 return ITextDocumentTextInfo 757 else: 758 return EditTextInfo
759
760 - def _get_ITextDocumentObject(self):
761 if not hasattr(self,'_ITextDocumentObject'): 762 try: 763 ptr=ctypes.POINTER(comInterfaces.tom.ITextDocument)() 764 ctypes.windll.oleacc.AccessibleObjectFromWindow(self.windowHandle,-16,ctypes.byref(ptr._iid_),ctypes.byref(ptr)) 765 self._ITextDocumentObject=ptr 766 except: 767 log.error("Error getting ITextDocument",exc_info=True) 768 self._ITextDocumentObject=None 769 return self._ITextDocumentObject
770
772 if not hasattr(self,'_ITextSelectionObject'): 773 try: 774 self._ITextSelectionObject=self.ITextDocumentObject.selection 775 except: 776 self._ITextSelectionObject=None 777 return self._ITextSelectionObject
778
779 - def _get_value(self):
780 return None
781
782 - def _get_role(self):
784
785 - def event_caret(self):
786 global selOffsetsAtLastCaretEvent 787 #Fetching formatting and calculating word offsets needs to move the caret, so try to ignore these events 788 selOffsets=self.makeTextInfo(textInfos.POSITION_SELECTION).bookmark 789 if selOffsets==selOffsetsAtLastCaretEvent: 790 return 791 selOffsetsAtLastCaretEvent=selOffsets 792 #Make sure that this object *really* has the focus before bothering to speak any possible selection change 793 api.processPendingEvents() 794 if self is not api.getFocusObject() or eventHandler.isPendingEvents('gainFocus'): 795 return 796 if eventHandler.isPendingEvents('valueChange',self): 797 self.hasContentChangedSinceLastSelection=True 798 super(Edit,self).event_caret() 799 self.detectPossibleSelectionChange()
800
801 - def event_valueChange(self):
802 self.event_textChange()
803
804 - def _get_states(self):
805 states = super(Edit, self)._get_states() 806 if self.windowStyle & winUser.ES_MULTILINE: 807 states.add(controlTypes.STATE_MULTILINE) 808 return states
809
810 -class RichEdit(Edit):
811 editAPIVersion=1
812
813 -class RichEdit20(RichEdit):
814 editAPIVersion=2
815
816 -class RichEdit30(RichEdit):
817 editAPIVersion=3
818
819 -class RichEdit50(RichEdit):
820 editAPIVersion=5
821