1 from ctypes.wintypes import POINT
2 from comtypes import COMError
3 import weakref
4 import UIAHandler
5 import globalVars
6 import eventHandler
7 import controlTypes
8 import config
9 import speech
10 import api
11 import textInfos
12 from logHandler import log
13 from NVDAObjects.window import Window
14 from NVDAObjects import NVDAObjectTextInfo, InvalidNVDAObject
15 from NVDAObjects.behaviors import ProgressBar, EditableTextWithoutAutoSelectDetection
16
17 -class UIATextInfo(textInfos.TextInfo):
18
19 NVDAUnitsToUIAUnits={
20 "character":UIAHandler.TextUnit_Character,
21 "word":UIAHandler.TextUnit_Word,
22 "line":UIAHandler.TextUnit_Line,
23 "paragraph":UIAHandler.TextUnit_Paragraph,
24 "readingChunk":UIAHandler.TextUnit_Line,
25 }
26
28 formatField=textInfos.FormatField()
29 if formatConfig["reportFontName"]:
30 try:
31 fontNameValue=range.GetAttributeValue(UIAHandler.UIA_FontNameAttributeId)
32 except COMError:
33 fontNameValue=UIAHandler.handler.reservedNotSupportedValue
34 if fontNameValue!=UIAHandler.handler.reservedNotSupportedValue:
35 formatField["font-name"]=fontNameValue
36 if formatConfig["reportFontSize"]:
37 try:
38 fontSizeValue=range.GetAttributeValue(UIAHandler.UIA_FontSizeAttributeId)
39 except COMError:
40 fontSizeValue=UIAHandler.handler.reservedNotSupportedValue
41 if fontSizeValue!=UIAHandler.handler.reservedNotSupportedValue:
42 formatField['font-size']="%g pt"%float(fontSizeValue)
43 return formatField
44
45 - def __init__(self,obj,position):
46 super(UIATextInfo,self).__init__(obj,position)
47 if isinstance(position,UIAHandler.IUIAutomationTextRange):
48 self._rangeObj=position
49 elif position in (textInfos.POSITION_CARET,textInfos.POSITION_SELECTION):
50 sel=self.obj.UIATextPattern.GetSelection()
51 if sel.length>0:
52 self._rangeObj=sel.getElement(0).clone()
53 else:
54 raise NotImplementedError("UIAutomationTextRangeArray is empty")
55 if position==textInfos.POSITION_CARET:
56 self.collapse()
57 elif isinstance(position,UIATextInfo):
58 self._rangeObj=position._rangeObj
59 elif position==textInfos.POSITION_FIRST:
60 self._rangeObj=self.obj.UIATextPattern.documentRange
61 self.collapse()
62 elif position==textInfos.POSITION_LAST:
63 self._rangeObj=self.obj.UIATextPattern.documentRange
64 self.collapse(True)
65 elif position==textInfos.POSITION_ALL:
66 self._rangeObj=self.obj.UIATextPattern.documentRange
67 else:
68 raise ValueError("Unknown position %s"%position)
69
70 - def __eq__(self,other):
71 if self is other: return True
72 if self.__class__ is not other.__class__: return False
73 return bool(self._rangeObj.compare(other._rangeObj))
74
75 - def _get_bookmark(self):
77
78 - def getTextWithFields(self,formatConfig=None):
79 if not formatConfig:
80 formatConfig=config.conf["documentFormatting"]
81 rangeObj=self._rangeObj.Clone()
82 rangeObj.MoveEndpointByRange(UIAHandler.TextPatternRangeEndpoint_End,rangeObj,UIAHandler.TextPatternRangeEndpoint_Start)
83 rangeObj.ExpandToEnclosingUnit(UIAHandler.TextUnit_Character)
84 formatField=self._getFormatFieldAtRange(rangeObj,formatConfig)
85 field=textInfos.FieldCommand("formatChange",formatField)
86 return [field,self.text]
87
88 - def _get_text(self):
89 return self._rangeObj.GetText(-1)
90
91 - def expand(self,unit):
92 UIAUnit=self.NVDAUnitsToUIAUnits[unit]
93 self._rangeObj.ExpandToEnclosingUnit(UIAUnit)
94
95 - def move(self,unit,direction,endPoint=None):
96 UIAUnit=self.NVDAUnitsToUIAUnits[unit]
97 if endPoint=="start":
98 res=self._rangeObj.MoveEndpointByUnit(UIAHandler.TextPatternRangeEndpoint_Start,UIAUnit,direction)
99 elif endPoint=="end":
100 res=self._rangeObj.MoveEndpointByUnit(UIAHandler.TextPatternRangeEndpoint_End,UIAUnit,direction)
101 else:
102 res=self._rangeObj.Move(UIAUnit,direction)
103
104 if direction<0 and res>0:
105 res=0-res
106 return res
107
109 return self.__class__(self.obj,self._rangeObj.clone())
110
111 - def collapse(self,end=False):
112 if end:
113 self._rangeObj.MoveEndpointByRange(UIAHandler.TextPatternRangeEndpoint_Start,self._rangeObj,UIAHandler.TextPatternRangeEndpoint_End)
114 else:
115 self._rangeObj.MoveEndpointByRange(UIAHandler.TextPatternRangeEndpoint_End,self._rangeObj,UIAHandler.TextPatternRangeEndpoint_Start)
116
117 - def compareEndPoints(self,other,which):
118 if which.startswith('start'):
119 src=UIAHandler.TextPatternRangeEndpoint_Start
120 else:
121 src=UIAHandler.TextPatternRangeEndpoint_End
122 if which.endswith('Start'):
123 target=UIAHandler.TextPatternRangeEndpoint_Start
124 else:
125 target=UIAHandler.TextPatternRangeEndpoint_End
126 return self._rangeObj.CompareEndpoints(src,other._rangeObj,target)
127
128 - def setEndPoint(self,other,which):
129 if which.startswith('start'):
130 src=UIAHandler.TextPatternRangeEndpoint_Start
131 else:
132 src=UIAHandler.TextPatternRangeEndpoint_End
133 if which.endswith('Start'):
134 target=UIAHandler.TextPatternRangeEndpoint_Start
135 else:
136 target=UIAHandler.TextPatternRangeEndpoint_End
137 self._rangeObj.MoveEndpointByRange(src,other._rangeObj,target)
138
139 - def updateSelection(self):
140 self._rangeObj.Select()
141
142 updateCaret = updateSelection
143
145
146 liveNVDAObjectTable=weakref.WeakValueDictionary()
147
178
179 @classmethod
181 UIAElement=None
182 windowHandle=kwargs.get('windowHandle')
183 if isinstance(relation,tuple):
184 UIAElement=UIAHandler.handler.clientObject.ElementFromPointBuildCache(POINT(relation[0],relation[1]),UIAHandler.handler.baseCacheRequest)
185 elif relation=="focus":
186 try:
187 UIAElement=UIAHandler.handler.clientObject.getFocusedElementBuildCache(UIAHandler.handler.baseCacheRequest)
188
189 kwargs['windowHandle']=None
190 except COMError:
191 log.debugWarning("getFocusedElement failed", exc_info=True)
192 else:
193 UIAElement=UIAHandler.handler.clientObject.ElementFromHandleBuildCache(windowHandle,UIAHandler.handler.baseCacheRequest)
194 if not UIAElement:
195 return False
196 kwargs['UIAElement']=UIAElement
197 return True
198
199 - def __new__(cls,relation=None,windowHandle=None,UIAElement=None):
200 try:
201 runtimeId=UIAElement.getRuntimeId()
202 except COMError:
203 log.debugWarning("Could not get UIA element runtime Id",exc_info=True)
204 runtimeId=None
205
206 obj=cls.liveNVDAObjectTable.get(runtimeId,None) if runtimeId else None
207 if not obj:
208 obj=super(UIA,cls).__new__(cls)
209 if not obj:
210 return None
211 obj.UIARuntimeId=runtimeId
212 obj.UIAElement=UIAElement
213 return obj
214
215 - def __init__(self,windowHandle=None,UIAElement=None):
216 if getattr(self,"_doneInit",False):
217
218 return
219 self._doneInit=True
220
221 if not UIAElement:
222 raise ValueError("needs a UIA element")
223
224 UIACachedWindowHandle=UIAElement.cachedNativeWindowHandle
225 self.UIAIsWindowElement=bool(UIACachedWindowHandle)
226 if UIACachedWindowHandle:
227 windowHandle=UIACachedWindowHandle
228 if not windowHandle:
229 windowHandle=UIAHandler.handler.getNearestWindowHandle(UIAElement)
230 if not windowHandle:
231 raise InvalidNVDAObject("no windowHandle")
232 super(UIA,self).__init__(windowHandle=windowHandle)
233
234 if UIAElement.getCachedPropertyValue(UIAHandler.UIA_IsTextPatternAvailablePropertyId):
235 self.TextInfo=UIATextInfo
236 self.value=""
237
238
239 if self.UIARuntimeId:
240
241
242 self.liveNVDAObjectTable[self.UIARuntimeId]=self
243
245 if not isinstance(other,UIA):
246 return False
247 try:
248 return UIAHandler.handler.clientObject.CompareElements(self.UIAElement,other.UIAElement)
249 except:
250 return False
251
253 if not hasattr(self,'_UIAInvokePattern'):
254 punk=self.UIAElement.GetCurrentPattern(UIAHandler.UIA_InvokePatternId)
255 if punk:
256 self._UIAInvokePattern=punk.QueryInterface(UIAHandler.IUIAutomationInvokePattern)
257 else:
258 self._UIAInvokePattern=None
259 return self._UIAInvokePattern
260
262 if not hasattr(self,'_UIATextPattern'):
263 punk=self.UIAElement.GetCurrentPattern(UIAHandler.UIA_TextPatternId)
264 if punk:
265 self._UIATextPattern=punk.QueryInterface(UIAHandler.IUIAutomationTextPattern)
266 else:
267 self._UIATextPattern=None
268 return self._UIATextPattern
269
272
274 info=super(UIA,self).devInfo
275 info.append("UIAElement: %r"%self.UIAElement)
276 try:
277 ret=self.UIAElement.currentAutomationID
278 except Exception as e:
279 ret="Exception: %s"%e
280 info.append("UIA automationID: %s"%ret)
281 try:
282 ret=self.UIAElement.currentFrameworkID
283 except Exception as e:
284 ret="Exception: %s"%e
285 info.append("UIA frameworkID: %s"%ret)
286 try:
287 ret=str(self.UIAElement.getRuntimeID())
288 except Exception as e:
289 ret="Exception: %s"%e
290 info.append("UIA runtimeID: %s"%ret)
291 try:
292 ret=self.UIAElement.cachedProviderDescription
293 except Exception as e:
294 ret="Exception: %s"%e
295 info.append("UIA providerDescription: %s"%ret)
296 try:
297 ret=self.UIAElement.currentClassName
298 except Exception as e:
299 ret="Exception: %s"%e
300 info.append("UIA className: %s"%ret)
301 return info
302
304 try:
305 return self.UIAElement.currentName
306 except COMError:
307 return ""
308
316
318 try:
319 return self.UIAElement.currentHelpText
320 except COMError:
321 return ""
322
324 try:
325 return self.UIAElement.currentAccessKey
326 except COMError:
327 return None
328
372
374 if obj and self.windowHandle != obj.windowHandle and not obj.UIAElement.cachedNativeWindowHandle:
375
376 return obj
377 return super(UIA, self).correctAPIForRelation(obj, relation)
378
380 try:
381 parentElement=UIAHandler.handler.baseTreeWalker.GetParentElementBuildCache(self.UIAElement,UIAHandler.handler.baseCacheRequest)
382 except COMError:
383 parentElement=None
384 if not parentElement:
385 return super(UIA,self).parent
386 return self.correctAPIForRelation(UIA(UIAElement=parentElement),relation="parent")
387
397
407
417
427
429 val=self.UIAElement.getCurrentPropertyValueEx(UIAHandler.UIA_GridItemRowPropertyId,True)
430 if val!=UIAHandler.handler.reservedNotSupportedValue:
431 return val+1
432 raise NotImplementedError
433
435 val=self.UIAElement.getCurrentPropertyValueEx(UIAHandler.UIA_GridItemColumnPropertyId,True)
436 if val!=UIAHandler.handler.reservedNotSupportedValue:
437 return val+1
438 raise NotImplementedError
439
441 val=self.UIAElement.getCurrentPropertyValueEx(UIAHandler.UIA_GridRowCountPropertyId,True)
442 if val!=UIAHandler.handler.reservedNotSupportedValue:
443 return val
444 raise NotImplementedError
445
447 val=self.UIAElement.getCurrentPropertyValueEx(UIAHandler.UIA_GridColumnCountPropertyId,True)
448 if val!=UIAHandler.handler.reservedNotSupportedValue:
449 return val
450 raise NotImplementedError
451
453 return self.UIAElement.cachedProcessId
454
456 try:
457 r=self.UIAElement.currentBoundingRectangle
458 except COMError:
459 return None
460 left=r.left
461 top=r.top
462 width=r.right-left
463 height=r.bottom-top
464 return left,top,width,height
465
467 val=self.UIAElement.getCurrentPropertyValueEx(UIAHandler.UIA_RangeValueValuePropertyId,True)
468 if val!=UIAHandler.handler.reservedNotSupportedValue:
469 minVal=self.UIAElement.getCurrentPropertyValueEx(UIAHandler.UIA_RangeValueMinimumPropertyId,False)
470 maxVal=self.UIAElement.getCurrentPropertyValueEx(UIAHandler.UIA_RangeValueMaximumPropertyId,False)
471 val=((val-minVal)/maxVal)*100.0
472 return "%g"%val
473 val=self.UIAElement.getCurrentPropertyValueEx(UIAHandler.UIA_ValueValuePropertyId,True)
474 if val!=UIAHandler.handler.reservedNotSupportedValue:
475 return val
476
478 if self.UIAInvokePattern:
479 return 1
480 return 0
481
483 if not index:
484 index=self.defaultActionIndex
485 if index==0 and self.UIAInvokePattern:
486 return _("invoke")
487 raise NotImplementedError
488
490 if not index:
491 index=self.defaultActionIndex
492 if index==0 and self.UIAInvokePattern:
493 self.UIAInvokePattern.Invoke()
494 return
495 raise NotImplementedError
496
498 try:
499 return self.UIAElement.currentHasKeyboardFocus
500 except COMError:
501 return False
502
504
507
509 level=0
510 obj=self
511 while obj:
512 level+=1
513 parent=obj.parent=obj.parent
514 if not parent or parent==obj or parent.role!=controlTypes.ROLE_TREEVIEWITEM:
515 return level
516 obj=parent
517 return level
518
520 return {'level':self._level}
521
531
533 """UIA list items in an Items View repeate the name as the value"""
534
537
539 """A slider that tends to give focus to its thumb control"""
540
543
550
562
564 """A combo box without the Value pattern.
565 UIA combo boxes don't necessarily support the Value pattern unless they take arbitrary text values.
566 However, NVDA expects combo boxes to have a value and to fire valueChange events.
567 The value is obtained by retrieving the selected item's name.
568 The valueChange event is fired on this object by L{ListItem.event_stateChange}.
569 """
570
572 punk = self.UIAElement.GetCurrentPattern(UIAHandler.UIA_SelectionPatternId)
573 if punk:
574 self.UIASelectionPattern = punk.QueryInterface(UIAHandler.IUIAutomationSelectionPattern)
575 else:
576 self.UIASelectionPattern = None
577 return self.UIASelectionPattern
578
580 try:
581 return self.UIASelectionPattern.GetCurrentSelection().GetElement(0).CurrentName
582 except COMError:
583 return None
584
595