19
20
import controlTypes
20
21
import appModuleHandler
21
import virtualBufferHandler
22
import treeInterceptorHandler
24
import globalPluginHandler
24
26
class NVDAObjectTextInfo(textInfos.offsets.OffsetsTextInfo):
27
"""A default TextInfo which is used to enable text review of information about widgets that don't support text content.
28
The L{NVDAObject.basicText} attribute is used as the text to expose.
31
def _get_unit_mouseChunk(self):
32
return textInfos.UNIT_STORY
26
34
def _getStoryText(self):
27
35
return self.obj.basicText
63
72
obj.findOverlayClasses(clsList)
65
74
clsList.append(APIClass)
66
# Allow app modules to add overlay classes.
75
# Allow app modules to choose overlay classes.
67
76
appModule=obj.appModule
68
77
if appModule and "chooseNVDAObjectOverlayClasses" in appModule.__class__.__dict__:
69
78
appModule.chooseNVDAObjectOverlayClasses(obj, clsList)
79
# Allow global plugins to choose overlay classes.
80
for plugin in globalPluginHandler.runningPlugins:
81
if "chooseNVDAObjectOverlayClasses" in plugin.__class__.__dict__:
82
plugin.chooseNVDAObjectOverlayClasses(obj, clsList)
71
84
# Determine the bases for the new class.
73
86
for index in xrange(len(clsList)):
74
# A class doesn't need to be a base if it is already a subclass of a previous base.
87
# A class doesn't need to be a base if it is already implicitly included by being a superclass of a previous base.
75
88
if index==0 or not issubclass(clsList[index-1],clsList[index]):
76
89
bases.append(clsList[index])
128
def clearDynamicClassCache(cls):
129
"""Clear the dynamic class cache.
130
This should be called when a plugin is unloaded so that any used overlay classes in the unloaded plugin can be garbage collected.
132
cls._dynamicClassCache.clear()
109
134
class NVDAObject(baseObject.ScriptableObject):
111
NVDA's representation of a control or widget in the Operating System. Provides information such as a name, role, value, description etc.
135
"""NVDA's representation of a single control/widget.
136
Every widget, regardless of how it is exposed by an application or the operating system, is represented by a single NVDAObject instance.
137
This allows NVDA to work with all widgets in a uniform way.
138
An NVDAObject provides information about the widget (e.g. its name, role and value),
139
as well as functionality to manipulate it (e.g. perform an action or set focus).
140
Events for the widget are handled by special event methods on the object.
141
Commands triggered by input from the user can also be handled by special methods called scripts.
142
See L{ScriptableObject} for more details.
144
The only attribute that absolutely must be provided is L{processID}.
145
However, subclasses should provide at least the L{name} and L{role} attributes in order for the object to be meaningful to the user.
146
Attributes such as L{parent}, L{firstChild}, L{next} and L{previous} link an instance to other NVDAObjects in the hierarchy.
147
In order to facilitate access to text exposed by a widget which supports text content (e.g. an editable text control),
148
a L{textInfos.TextInfo} should be implemented and the L{TextInfo} attribute should specify this class.
150
There are two main types of NVDAObject classes:
151
* API classes, which provide the core functionality to work with objects exposed using a particular API (e.g. MSAA/IAccessible).
152
* Overlay classes, which supplement the core functionality provided by an API class to handle a specific widget or type of widget.
153
Most developers need only be concerned with overlay classes.
154
The overlay classes to be used for an instance are determined using the L{findOverlayClasses} method on the API class.
155
An L{AppModule} can also choose overlay classes for an instance using the L{AppModule.chooseNVDAObjectOverlayClasses} method.
114
158
__metaclass__=DynamicNVDAObjectType
115
159
cachePropertiesByDefault = True
117
TextInfo=NVDAObjectTextInfo #:The TextInfo class this object should use
161
#: The TextInfo class this object should use to provide access to text.
162
#: @type: type; L{textInfos.TextInfo}
163
TextInfo=NVDAObjectTextInfo
120
166
def findBestAPIClass(cls,kwargs,relation=None):
245
293
return not self.__eq__(other)
247
def _get_virtualBufferClass(self):
295
def _get_treeInterceptorClass(self):
249
If this NVDAObject should use a virtualBuffer, then this property provides the L{virtualBuffers.VirtualBuffer} class it should use.
297
If this NVDAObject should use a treeInterceptor, then this property provides the L{treeInterceptorHandler.TreeInterceptor} class it should use.
250
298
If not then it should be not implemented.
252
300
raise NotImplementedError
254
def _get_virtualBuffer(self):
255
"""Retreaves the virtualBuffer associated with this object.
256
If a virtualBuffer has not been specifically set, the L{virtualBufferHandler} is asked if it can find a virtualBuffer containing this object.
257
@return: the virtualBuffer
258
@rtype: L{virtualBuffers.VirtualBuffer}
302
def _get_treeInterceptor(self):
303
"""Retreaves the treeInterceptor associated with this object.
304
If a treeInterceptor has not been specifically set, the L{treeInterceptorHandler} is asked if it can find a treeInterceptor containing this object.
305
@return: the treeInterceptor
306
@rtype: L{treeInterceptorHandler.TreeInterceptor}
260
if hasattr(self,'_virtualBuffer'):
261
v=self._virtualBuffer
262
if isinstance(v,weakref.ref):
264
if v and v in virtualBufferHandler.runningTable:
308
if hasattr(self,'_treeInterceptor'):
309
ti=self._treeInterceptor
310
if isinstance(ti,weakref.ref):
312
if ti and ti in treeInterceptorHandler.runningTable:
267
self._virtualBuffer=None
315
self._treeInterceptor=None
270
v=virtualBufferHandler.getVirtualBuffer(self)
272
self._virtualBuffer=weakref.ref(v)
318
ti=treeInterceptorHandler.getTreeInterceptor(self)
320
self._treeInterceptor=weakref.ref(ti)
275
def _set_virtualBuffer(self,obj):
276
"""Specifically sets a virtualBuffer to be associated with this object.
323
def _set_treeInterceptor(self,obj):
324
"""Specifically sets a treeInterceptor to be associated with this object.
279
self._virtualBuffer=weakref.ref(obj)
327
self._treeInterceptor=weakref.ref(obj)
280
328
else: #We can't point a weakref to None, so just set the private variable to None, it can handle that
281
self._virtualBuffer=None
329
self._treeInterceptor=None
283
331
def _get_appModule(self):
284
332
"""Retreaves the appModule representing the application this object is a part of by asking L{appModuleHandler}.
464
520
if controlTypes.STATE_INVISIBLE in states or controlTypes.STATE_UNAVAILABLE in states:
465
521
return self.presType_unavailable
467
if controlTypes.STATE_FOCUSED in states:
468
return self.presType_content
470
524
#Static text should be content only if it really use usable text
471
525
if role==controlTypes.ROLE_STATICTEXT:
472
526
text=self.makeTextInfo(textInfos.POSITION_ALL).text
473
527
return self.presType_content if text and not text.isspace() else self.presType_layout
475
if role in (controlTypes.ROLE_UNKNOWN, controlTypes.ROLE_PANE, controlTypes.ROLE_ROOTPANE, controlTypes.ROLE_LAYEREDPANE, controlTypes.ROLE_SCROLLPANE, controlTypes.ROLE_SECTION,controlTypes.ROLE_PARAGRAPH,controlTypes.ROLE_TITLEBAR):
529
if role in (controlTypes.ROLE_UNKNOWN, controlTypes.ROLE_PANE, controlTypes.ROLE_TEXTFRAME, controlTypes.ROLE_ROOTPANE, controlTypes.ROLE_LAYEREDPANE, controlTypes.ROLE_SCROLLPANE, controlTypes.ROLE_SECTION,controlTypes.ROLE_PARAGRAPH,controlTypes.ROLE_TITLEBAR,controlTypes.ROLE_LABEL):
476
530
return self.presType_layout
478
532
description = self.description
479
if not name and not description and role in (controlTypes.ROLE_WINDOW,controlTypes.ROLE_LABEL,controlTypes.ROLE_PANEL, controlTypes.ROLE_PROPERTYPAGE, controlTypes.ROLE_TEXTFRAME, controlTypes.ROLE_GROUPING,controlTypes.ROLE_OPTIONPANE,controlTypes.ROLE_INTERNALFRAME,controlTypes.ROLE_FORM,controlTypes.ROLE_TABLEBODY):
480
return self.presType_layout
481
if not name and not description and role in (controlTypes.ROLE_TABLE,controlTypes.ROLE_TABLEROW,controlTypes.ROLE_TABLECOLUMN,controlTypes.ROLE_TABLECELL) and not config.conf["documentFormatting"]["reportTables"]:
482
return self.presType_layout
533
if not name and not description:
534
if role in (controlTypes.ROLE_WINDOW,controlTypes.ROLE_PANEL, controlTypes.ROLE_PROPERTYPAGE, controlTypes.ROLE_TEXTFRAME, controlTypes.ROLE_GROUPING,controlTypes.ROLE_OPTIONPANE,controlTypes.ROLE_INTERNALFRAME,controlTypes.ROLE_FORM,controlTypes.ROLE_TABLEBODY):
535
return self.presType_layout
536
if role == controlTypes.ROLE_TABLE and not config.conf["documentFormatting"]["reportTables"]:
537
return self.presType_layout
538
if role in (controlTypes.ROLE_TABLEROW,controlTypes.ROLE_TABLECOLUMN,controlTypes.ROLE_TABLECELL) and (not config.conf["documentFormatting"]["reportTables"] or not config.conf["documentFormatting"]["reportTableCellCoords"]):
539
return self.presType_layout
483
540
if role in (controlTypes.ROLE_TABLEROW,controlTypes.ROLE_TABLECOLUMN):
662
def _get_indexInParent(self):
663
"""The index of this object in its parent object.
664
@return: The 0 based index, C{None} if there is no parent.
666
@raise NotImplementedError: If not supported by the underlying object.
668
raise NotImplementedError
670
def _get_flowsTo(self):
671
"""The object to which content flows from this object.
672
@return: The object to which this object flows, C{None} if none.
673
@rtype: L{NVDAObject}
674
@raise NotImplementedError: If not supported by the underlying object.
676
raise NotImplementedError
678
def _get_flowsFrom(self):
679
"""The object from which content flows to this object.
680
@return: The object from which this object flows, C{None} if none.
681
@rtype: L{NVDAObject}
682
@raise NotImplementedError: If not supported by the underlying object.
684
raise NotImplementedError
686
def _get_embeddingTextInfo(self):
687
"""Retrieve the parent text range which embeds this object.
688
The returned text range will have its start positioned on the embedded object character associated with this object.
689
That is, calling L{textInfos.TextInfo.getEmbeddedObject}() on the returned text range will return this object.
690
@return: The text range for the embedded object character associated with this object or C{None} if this is not an embedded object.
691
@rtype: L{textInfos.TextInfo}
692
@raise NotImplementedError: If not supported.
694
raise NotImplementedError
605
696
def _get_isPresentableFocusAncestor(self):
606
697
"""Determine if this object should be presented to the user in the focus ancestry.
607
698
@return: C{True} if it should be presented in the focus ancestry, C{False} if not.
651
733
self._mouseEntered=True
653
735
info=self.makeTextInfo(textInfos.Point(x,y))
654
info.expand(info.unit_mouseChunk)
656
info=NVDAObjectTextInfo(self,textInfos.POSITION_ALL)
736
except NotImplementedError:
737
info=NVDAObjectTextInfo(self,textInfos.POSITION_FIRST)
657
740
if config.conf["reviewCursor"]["followMouse"]:
658
741
api.setReviewPosition(info)
659
if not config.conf["mouse"]["reportTextUnderMouse"]:
742
info.expand(info.unit_mouseChunk)
661
743
oldInfo=getattr(self,'_lastMouseTextInfoObject',None)
662
744
self._lastMouseTextInfoObject=info
663
745
if not oldInfo or info.__class__!=oldInfo.__class__ or info.compareEndPoints(oldInfo,"startToStart")!=0 or info.compareEndPoints(oldInfo,"endToEnd")!=0:
721
803
def event_caret(self):
722
804
if self is api.getFocusObject() and not eventHandler.isPendingEvents("gainFocus"):
723
805
braille.handler.handleCaretMove(self)
724
if config.conf["reviewCursor"]["followCaret"]:
806
if config.conf["reviewCursor"]["followCaret"] and api.getNavigatorObject() is self:
726
808
api.setReviewPosition(self.makeTextInfo(textInfos.POSITION_CARET))
727
809
except (NotImplementedError, RuntimeError):
812
def _get_flatReviewPosition(self):
813
"""Locates a TextInfo positioned at this object, in the closest flat review."""
814
parent=self.simpleParent
816
ti=parent.treeInterceptor
817
if ti and self in ti and ti.rootNVDAObject==parent:
818
return ti.makeTextInfo(self)
819
if issubclass(parent.TextInfo,DisplayModelTextInfo):
821
return parent.makeTextInfo(api.getReviewPosition().pointAtStart)
822
except (NotImplementedError,LookupError):
825
return parent.makeTextInfo(self)
826
except (NotImplementedError,RuntimeError):
828
return parent.makeTextInfo(textInfos.POSITION_FIRST)
829
parent=parent.simpleParent
730
831
def _get_basicText(self):
731
832
newTime=time.time()
732
833
oldTime=getattr(self,'_basicTextTime',0)
733
834
if newTime-oldTime>0.5:
734
self._basicText=" ".join([x for x in self.name, self.value, self.description if isinstance(x, basestring) and len(x) > 0 and not x.isspace()])
835
self._basicText=u" ".join([x for x in self.name, self.value, self.description if isinstance(x, basestring) and len(x) > 0 and not x.isspace()])
735
836
if len(self._basicText)==0:
738
839
self._basicTextTime=newTime
739
840
return self._basicText
741
842
def makeTextInfo(self,position):
742
843
return self.TextInfo(self,position)
744
class AutoSelectDetectionNVDAObject(NVDAObject):
746
"""Provides an NVDAObject with the means to detect if the text selection has changed, and if so to announce the change
747
@ivar hasContentChangedSinceLastSelection: if True then the content has changed.
748
@ivar hasContentChangedSinceLastSelection: boolean
751
def initAutoSelectDetection(self):
752
"""Initializes the autoSelect detection code so that it knows about what is currently selected."""
754
self._lastSelectionPos=self.makeTextInfo(textInfos.POSITION_SELECTION)
756
self._lastSelectionPos=None
757
self.hasContentChangedSinceLastSelection=False
759
def detectPossibleSelectionChange(self):
760
"""Detects if the selection has been changed, and if so it speaks the change."""
761
oldInfo=getattr(self,'_lastSelectionPos',None)
765
newInfo=self.makeTextInfo(textInfos.POSITION_SELECTION)
767
self._lastSelectionPos=None
769
self._lastSelectionPos=newInfo.copy()
770
hasContentChanged=self.hasContentChangedSinceLastSelection
771
self.hasContentChangedSinceLastSelection=False
772
if hasContentChanged:
776
speech.speakSelectionChange(oldInfo,newInfo,generalize=generalize)
845
def _get_devInfo(self):
846
"""Information about this object useful to developers.
847
Subclasses may extend this, calling the superclass property first.
848
@return: A list of text strings providing information about this object useful to developers.
853
ret = repr(self.name)
854
except Exception as e:
855
ret = "exception: %s" % e
856
info.append("name: %s" % ret)
859
for name, const in controlTypes.__dict__.iteritems():
860
if name.startswith("ROLE_") and ret == const:
863
except Exception as e:
864
ret = "exception: %s" % e
865
info.append("role: %s" % ret)
867
stateConsts = dict((const, name) for name, const in controlTypes.__dict__.iteritems() if name.startswith("STATE_"))
869
stateConsts.get(state) or str(state)
870
for state in self.states)
871
except Exception as e:
872
ret = "exception: %s" % e
873
info.append("states: %s" % ret)
876
except Exception as e:
877
ret = "exception: %s" % e
878
info.append("Python object: %s" % ret)
880
ret = repr(self.__class__.__mro__)
881
except Exception as e:
882
ret = "exception: %s" % e
883
info.append("Python class mro: %s" % ret)
885
ret = repr(self.description)
886
except Exception as e:
887
ret = "exception: %s" % e
888
info.append("description: %s" % ret)
890
ret = repr(self.location)
891
except Exception as e:
892
ret = "exception: %s" % e
893
info.append("location: %s" % ret)
896
if isinstance(ret, basestring) and len(ret) > 100:
897
ret = "%r (truncated)" % ret[:100]
900
except Exception as e:
901
ret = "exception: %s" % e
902
info.append("value: %s" % ret)
904
ret = repr(self.appModule)
905
except Exception as e:
906
ret = "exception: %s" % e
907
info.append("appModule: %s" % ret)
909
ret = repr(self.TextInfo)
910
except Exception as e:
911
ret = "exception: %s" % e
912
info.append("TextInfo: %s" % ret)