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

Source Code for Package NVDAObjects.window

  1  #NVDAObjects/window.py 
  2  #A part of NonVisual Desktop Access (NVDA) 
  3  #Copyright (C) 2006-2007 NVDA Contributors <http://www.nvda-project.org/> 
  4  #This file is covered by the GNU General Public License. 
  5  #See the file COPYING for more details. 
  6   
  7  import re 
  8  import ctypes 
  9  import ctypes.wintypes 
 10  import winKernel 
 11  import winUser 
 12  from logHandler import log 
 13  import controlTypes 
 14  import api 
 15  import displayModel 
 16  import eventHandler 
 17  from NVDAObjects import NVDAObject 
 18  from NVDAObjects.behaviors import EditableText, LiveText 
 19  import watchdog 
 20   
 21  re_WindowsForms=re.compile(r'^WindowsForms[0-9]*\.(.*)\.app\..*$') 
 22  re_ATL=re.compile(r'^ATL:(.*)$') 
 23   
 24  try: 
 25          GhostWindowFromHungWindow=ctypes.windll.user32.GhostWindowFromHungWindow 
 26  except AttributeError: 
 27          GhostWindowFromHungWindow=None 
28 29 -def isUsableWindow(windowHandle):
30 if not ctypes.windll.user32.IsWindowEnabled(windowHandle): 31 return False 32 if not ctypes.windll.user32.IsWindowVisible(windowHandle): 33 return False 34 if GhostWindowFromHungWindow and ctypes.windll.user32.GhostWindowFromHungWindow(windowHandle): 35 return False 36 return True
37
38 -class WindowProcessHandleContainer(object):
39 """ 40 Manages a Windows process handle. On instanciation it retreaves an open process handle from the process of the provided window, and closes the handle on deletion. 41 @ivar windowHandle: the handle of the window the whos process handle was requested 42 @type windowHandle: int 43 @ivar processHandle: The actual handle which can be used in any win32 calls that need it. 44 @type processHandle: int 45 """ 46
47 - def __init__(self,windowHandle):
48 """ 49 @param windowHandle: the handle of the window whos process handle should be retreaved. 50 @type windowHandle: int 51 """ 52 self.windowHandle=windowHandle 53 import oleacc 54 self.processHandle=oleacc.GetProcessHandleFromHwnd(self.windowHandle)
55
56 - def __del__(self):
57 winKernel.closeHandle(self.processHandle)
58
59 -class Window(NVDAObject):
60 """ 61 An NVDAObject for a window 62 @ivar windowHandle: The window's handle 63 @type windowHandle: int 64 @ivar windowClassName: the window's class 65 @type windowClassName: string 66 @ivar windowControlID: the window's control ID 67 @type windowControlID: int 68 @ivar windowText: The window's text (using winUser.WM_GETTEXT) 69 @type windowText: string 70 @ivar windowProcessID: The window's [processID,threadID] 71 @type windowProcessID: list of two ints 72 """ 73 74 @classmethod
75 - def getPossibleAPIClasses(cls,kwargs,relation=None):
76 windowHandle=kwargs['windowHandle'] 77 windowClassName=winUser.getClassName(windowHandle) 78 #The desktop window should stay as a window 79 if windowClassName=="#32769": 80 return 81 #If this window has a ghost window its too dangerous to try any higher APIs 82 if GhostWindowFromHungWindow and GhostWindowFromHungWindow(windowHandle): 83 return 84 if windowClassName=="EXCEL7" and (relation=='focus' or isinstance(relation,tuple)): 85 from . import excel 86 yield excel.ExcelCell 87 import JABHandler 88 if JABHandler.isJavaWindow(windowHandle): 89 import NVDAObjects.JAB 90 yield NVDAObjects.JAB.JAB 91 import UIAHandler 92 if UIAHandler.handler and UIAHandler.handler.isUIAWindow(windowHandle): 93 import NVDAObjects.UIA 94 yield NVDAObjects.UIA.UIA 95 import NVDAObjects.IAccessible 96 yield NVDAObjects.IAccessible.IAccessible
97
98 - def findOverlayClasses(self,clsList):
99 windowClassName=self.normalizeWindowClassName(self.windowClassName) 100 newCls=None 101 if windowClassName=="#32769": 102 newCls=Desktop 103 elif windowClassName=="Edit": 104 from .edit import Edit as newCls 105 elif windowClassName=="RichEdit": 106 from .edit import RichEdit as newCls 107 elif windowClassName=="RichEdit20": 108 from .edit import RichEdit20 as newCls 109 elif windowClassName=="RICHEDIT50W": 110 from .edit import RichEdit50 as newCls 111 elif windowClassName in ("Scintilla","TScintilla"): 112 from .scintilla import Scintilla as newCls 113 elif windowClassName in ("AkelEditW", "AkelEditA"): 114 from .akelEdit import AkelEdit as newCls 115 elif windowClassName=="ConsoleWindowClass": 116 from .winConsole import WinConsole as newCls 117 elif windowClassName=="_WwG": 118 from .winword import WordDocument as newCls 119 elif windowClassName=="EXCEL7": 120 from .excel import Excel7Window as newCls 121 if newCls: 122 clsList.append(newCls) 123 124 #If none of the chosen classes seem to support text editing 125 #But there is a caret currently in the window 126 #Then use the displayModelEditableText class to emulate text editing capabilities 127 if not any(issubclass(cls,EditableText) for cls in clsList): 128 gi=winUser.getGUIThreadInfo(self.windowThreadID) 129 if gi.hwndCaret==self.windowHandle and gi.flags&winUser.GUI_CARETBLINKING: 130 clsList.append(DisplayModelEditableText) 131 132 clsList.append(Window) 133 super(Window,self).findOverlayClasses(clsList)
134 135 @classmethod
136 - def kwargsFromSuper(cls,kwargs,relation=None):
137 windowHandle=None 138 if relation in ('focus','foreground'): 139 windowHandle=winUser.getForegroundWindow() 140 if not windowHandle: windowHandle=winUser.getDesktopWindow() 141 if windowHandle and relation=="focus": 142 threadID=winUser.getWindowThreadProcessID(windowHandle)[1] 143 threadInfo=winUser.getGUIThreadInfo(threadID) 144 if threadInfo.hwndFocus: windowHandle=threadInfo.hwndFocus 145 elif isinstance(relation,tuple): 146 windowHandle=ctypes.windll.user32.WindowFromPoint(ctypes.wintypes.POINT(relation[0],relation[1])) 147 if not windowHandle: 148 return False 149 kwargs['windowHandle']=windowHandle 150 return True
151
152 - def __init__(self,windowHandle=None):
153 if not windowHandle: 154 raise ValueError("invalid or not specified window handle") 155 self.windowHandle=windowHandle 156 super(Window,self).__init__()
157
158 - def _isEqual(self,other):
159 return super(Window,self)._isEqual(other) and other.windowHandle==self.windowHandle
160
161 - def _get_name(self):
162 return winUser.getWindowText(self.windowHandle)
163
164 - def _get_role(self):
166
167 - def _get_windowClassName(self):
168 if hasattr(self,"_windowClassName"): 169 return self._windowClassName 170 name=winUser.getClassName(self.windowHandle) 171 self._windowClassName=name 172 return name
173
174 - def _get_windowControlID(self):
175 if not hasattr(self,"_windowControlID"): 176 self._windowControlID=winUser.getControlID(self.windowHandle) 177 return self._windowControlID
178
179 - def _get_location(self):
180 r=ctypes.wintypes.RECT() 181 ctypes.windll.user32.GetWindowRect(self.windowHandle,ctypes.byref(r)) 182 return (r.left,r.top,r.right-r.left,r.bottom-r.top)
183
184 - def _get_displayText(self):
185 """The text at this object's location according to the display model for this object's window.""" 186 try: 187 left,top,width,height=self.location 188 except TypeError: 189 log.debugWarning("No location, returning no text") 190 return "" 191 import displayModel 192 text,rects=displayModel.getWindowTextInRect(self.appModule.helperLocalBindingHandle,self.windowHandle,left,top,left+width,top+height,8,32) 193 return text or ""
194
195 - def redraw(self):
196 """Redraw the display for this object. 197 """ 198 left, top, width, height = self.location 199 left, top = winUser.ScreenToClient(self.windowHandle, left, top) 200 winUser.RedrawWindow(self.windowHandle, 201 winUser.RECT(left, top, left + width, top + height), None, 202 winUser.RDW_INVALIDATE | winUser.RDW_UPDATENOW)
203
204 - def _get_windowText(self):
205 textLength=watchdog.cancellableSendMessage(self.windowHandle,winUser.WM_GETTEXTLENGTH,0,0) 206 textBuf=ctypes.create_unicode_buffer(textLength+2) 207 watchdog.cancellableSendMessage(self.windowHandle,winUser.WM_GETTEXT,textLength+1,textBuf) 208 return textBuf.value
209
210 - def _get_processID(self):
211 if hasattr(self,"_processIDThreadID"): 212 return self._processIDThreadID[0] 213 self._processIDThreadID=winUser.getWindowThreadProcessID(self.windowHandle) 214 return self._processIDThreadID[0]
215
216 - def _get_windowThreadID(self):
217 if hasattr(self,"_processIDThreadID"): 218 return self._processIDThreadID[1] 219 self._processIDThreadID=winUser.getWindowThreadProcessID(self.windowHandle) 220 return self._processIDThreadID[1]
221
222 - def _get_next(self):
223 nextWindow=winUser.getWindow(self.windowHandle,winUser.GW_HWNDNEXT) 224 while nextWindow and not isUsableWindow(nextWindow): 225 nextWindow=winUser.getWindow(nextWindow,winUser.GW_HWNDNEXT) 226 if nextWindow: 227 return Window(windowHandle=nextWindow)
228
229 - def _get_previous(self):
230 prevWindow=winUser.getWindow(self.windowHandle,winUser.GW_HWNDPREV) 231 while prevWindow and not isUsableWindow(prevWindow): 232 prevWindow=winUser.getWindow(prevWindow,winUser.GW_HWNDPREV) 233 if prevWindow: 234 return Window(windowHandle=prevWindow)
235
236 - def _get_firstChild(self):
237 childWindow=winUser.getTopWindow(self.windowHandle) 238 while childWindow and not isUsableWindow(childWindow): 239 childWindow=winUser.getWindow(childWindow,winUser.GW_HWNDNEXT) 240 if childWindow: 241 return Window(windowHandle=childWindow)
242
243 - def _get_lastChild(self):
244 childWindow=winUser.getTopWindow(self.windowHandle) 245 nextWindow=winUser.getWindow(childWindow,winUser.GW_HWNDNEXT) 246 while nextWindow: 247 childWindow=nextWindow 248 nextWindow=winUser.getWindow(childWindow,winUser.GW_HWNDNEXT) 249 while childWindow and not isUsableWindow(childWindow): 250 childWindow=winUser.getWindow(childWindow,winUser.GW_HWNDPREV) 251 if childWindow: 252 return Window(windowHandle=childWindow)
253
254 - def _get_parent(self):
255 parentHandle=winUser.getAncestor(self.windowHandle,winUser.GA_PARENT) 256 if parentHandle: 257 #Because we, we need to get the APIclass manually need to set the relation as parent 258 kwargs=dict(windowHandle=parentHandle) 259 APIClass=Window.findBestAPIClass(kwargs,relation="parent") 260 return APIClass(**kwargs) if APIClass else None
261
262 - def _get_isInForeground(self):
263 fg=winUser.getForegroundWindow() 264 return self.windowHandle==fg or winUser.isDescendantWindow(fg,self.windowHandle)
265
266 - def _get_states(self):
267 states=super(Window,self)._get_states() 268 style=self.windowStyle 269 if not style&winUser.WS_VISIBLE: 270 states.add(controlTypes.STATE_INVISIBLE) 271 if style&winUser.WS_DISABLED: 272 states.add(controlTypes.STATE_UNAVAILABLE) 273 return states
274
275 - def _get_windowStyle(self):
276 return winUser.getWindowStyle(self.windowHandle)
277
278 - def _get_isWindowUnicode(self):
279 if not hasattr(self,'_isWindowUnicode'): 280 self._isWindowUnicode=bool(ctypes.windll.user32.IsWindowUnicode(self.windowHandle)) 281 return self._isWindowUnicode
282
283 - def correctAPIForRelation(self,obj,relation=None):
284 if not obj: 285 return None 286 newWindowHandle=obj.windowHandle 287 oldWindowHandle=self.windowHandle 288 if newWindowHandle and oldWindowHandle and newWindowHandle!=oldWindowHandle: 289 kwargs=dict(windowHandle=newWindowHandle) 290 newAPIClass=Window.findBestAPIClass(kwargs,relation=relation) 291 oldAPIClass=self.APIClass 292 if newAPIClass and newAPIClass!=oldAPIClass: 293 return newAPIClass(chooseBestAPI=False,**kwargs) 294 return obj
295
296 - def _get_processHandle(self):
297 if not hasattr(self,'_processHandleContainer'): 298 self._processHandleContainer=WindowProcessHandleContainer(self.windowHandle) 299 return self._processHandleContainer.processHandle
300 301 @classmethod
302 - def normalizeWindowClassName(cls,name):
303 """ 304 Removes unneeded information from a window class name (e.g. ATL: and windows forms info), and or maps it to a much more well-known compatible class name. 305 Conversions are also cached for future normalizations. 306 @param name: the window class name to normalize 307 @type name: string 308 @returns: the normalized window class name 309 @rtype: string 310 """ 311 try: 312 return cls.normalizedWindowClassNameCache[name] 313 except KeyError: 314 pass 315 newName=windowClassMap.get(name,None) 316 if not newName: 317 for r in (re_WindowsForms,re_ATL): 318 m=re.match(r,name) 319 if m: 320 newName=m.group(1) 321 newName=windowClassMap.get(newName,newName) 322 break 323 if not newName: 324 newName=name 325 cls.normalizedWindowClassNameCache[name]=newName 326 return newName
327 328 normalizedWindowClassNameCache={} 329
330 - def _get_devInfo(self):
331 info = super(Window, self).devInfo 332 info.append("windowHandle: %r" % self.windowHandle) 333 try: 334 ret = repr(self.windowClassName) 335 except Exception as e: 336 ret = "exception: %s" % e 337 info.append("windowClassName: %s" % ret) 338 try: 339 ret = repr(self.windowControlID) 340 except Exception as e: 341 ret = "exception: %s" % e 342 info.append("windowControlID: %s" % ret) 343 try: 344 ret = repr(self.windowStyle) 345 except Exception as e: 346 ret = "exception: %s" % e 347 info.append("windowStyle: %s" % ret) 348 try: 349 ret = repr(self.windowThreadID) 350 except Exception as e: 351 ret = "exception: %s" % e 352 info.append("windowThreadID: %s" % ret) 353 try: 354 ret = self.windowText 355 if isinstance(ret, basestring) and len(ret) > 100: 356 ret = "%r (truncated)" % ret[:100] 357 else: 358 ret = repr(ret) 359 except Exception as e: 360 ret = "exception: %s" % e 361 info.append("windowText: %s" % ret) 362 return info
363
364 -class Desktop(Window):
365 366 isPresentableFocusAncestor = False 367
368 - def _get_name(self):
369 return _("Desktop")
370
371 -class DisplayModelEditableText(EditableText, Window):
372 373 role=controlTypes.ROLE_EDITABLETEXT 374 TextInfo = displayModel.EditableTextDisplayModelTextInfo 375
376 - def event_valueChange(self):
377 # Don't report value changes for editable text fields. 378 pass
379
380 -class DisplayModelLiveText(LiveText, Window):
381 TextInfo = displayModel.EditableTextDisplayModelTextInfo 382
383 - def startMonitoring(self):
384 # Force the window to be redrawn, as our display model might be out of date. 385 self.redraw() 386 displayModel.requestTextChangeNotifications(self, True) 387 super(DisplayModelLiveText, self).startMonitoring()
388
389 - def stopMonitoring(self):
392
393 - def _getTextLines(self):
394 return self.displayText.splitlines()
395 396 windowClassMap={ 397 "EDIT":"Edit", 398 "TTntEdit.UnicodeClass":"Edit", 399 "TMaskEdit":"Edit", 400 "TTntMemo.UnicodeClass":"Edit", 401 "TRichEdit":"Edit", 402 "TRichViewEdit":"Edit", 403 "TInEdit.UnicodeClass":"Edit", 404 "TInEdit":"Edit", 405 "TEdit":"Edit", 406 "TFilenameEdit":"Edit", 407 "TSpinEdit":"Edit", 408 "ThunderRT6TextBox":"Edit", 409 "TMemo":"Edit", 410 "RICHEDIT":"RichEdit", 411 "TPasswordEdit":"Edit", 412 "THppEdit.UnicodeClass":"Edit", 413 "TUnicodeTextEdit.UnicodeClass":"Edit", 414 "TTextEdit":"Edit", 415 "TPropInspEdit":"Edit", 416 "TFilterbarEdit.UnicodeClass":"Edit", 417 "EditControl":"Edit", 418 "TNavigableTntMemo.UnicodeClass":"Edit", 419 "TNavigableTntEdit.UnicodeClass":"Edit", 420 "TAltEdit.UnicodeClass":"Edit", 421 "TAltEdit":"Edit", 422 "TDefEdit":"Edit", 423 "TRichEditViewer":"RichEdit", 424 "WFMAINRE":"RichEdit20", 425 "RichEdit20A":"RichEdit20", 426 "RichEdit20W":"RichEdit20", 427 "TskRichEdit.UnicodeClass":"RichEdit20", 428 "RichEdit20WPT":"RichEdit20", 429 "RICHEDIT60W":"RICHEDIT50W", 430 "TChatRichEdit.UnicodeClass":"RichEdit20", 431 "TMyRichEdit":"RichEdit20", 432 "TExRichEdit":"RichEdit20", 433 "RichTextWndClass":"RichEdit20", 434 "TSRichEdit":"RichEdit20", 435 "ScintillaWindowImpl":"Scintilla", 436 "RICHEDIT60W_WLXPRIVATE":"RICHEDIT50W", 437 } 438