def makeSettings(self, settingsSizer): sHelper = gui.guiHelper.BoxSizerHelper(self, sizer=settingsSizer) self.autoUpdateCheckBox = sHelper.addItem( # Translators: This is the label for a checkbox in the # Add-on Updater settings panel. wx.CheckBox(self, label=_("Automatically check for add-on &updates"))) self.autoUpdateCheckBox.SetValue(addonUtils.updateState["autoUpdate"]) if (winVersion.getWinVer() >= winVersion.WIN10 and winVersion.getWinVer().productType == "workstation"): updateNotificationChoices = [ # Translators: one of the add-on update notification choices. ("toast", _("toast")), # Translators: one of the add-on update notification choices. ("dialog", _("dialog")), ] self.updateNotification = sHelper.addLabeledControl( # Translators: This is the label for a combo box in the # Add-on Updater settings panel. _("&Add-on update notification:"), wx.Choice, choices=[x[1] for x in updateNotificationChoices]) self.updateNotification.SetSelection( next( (x for x, y in enumerate(updateNotificationChoices) if y[0] == addonUtils.updateState["updateNotification"]))) # Checkable list comes from NVDA Core issue 7491 (credit: Derek Riemer and Babbage B.V.). # Some add-ons come with pretty badly formatted summary text, # so try catching them and exclude them from this list. # Also, Vocalizer add-on family should be excluded from this list (requested by add-on author). self.noAddonUpdates = sHelper.addLabeledControl( _("Do ¬ update add-ons:"), CustomCheckListBox, choices=[ addon.manifest["summary"] for addon in addonHandler.getAvailableAddons() if isinstance(addon.manifest['summary'], str) and "vocalizer" not in addon.name ]) self.noAddonUpdates.SetCheckedStrings(addonHandlerEx.shouldNotUpdate()) self.noAddonUpdates.SetSelection(0) self.devAddonUpdates = sHelper.addLabeledControl( _("Prefer &development releases:"), CustomCheckListBox, choices=[ addon.manifest["summary"] for addon in addonHandler.getAvailableAddons() if isinstance(addon.manifest['summary'], str) and "vocalizer" not in addon.name ]) self.devAddonUpdates.SetCheckedStrings( addonHandlerEx.preferDevUpdates()) self.devAddonUpdates.SetSelection(0)
def fixCOMRegistrations(): """Registers most common COM proxies, in case they have accidentally been unregistered or overwritten by 3rd party software installs or uninstalls. """ is64bit = os.environ.get("PROCESSOR_ARCHITEW6432", "").endswith("64") winVer = winVersion.getWinVer() OSMajorMinor = (winVer.major, winVer.minor) log.debug( f"Fixing COM registrations for Windows {OSMajorMinor[0]}.{OSMajorMinor[1]}, " "{} bit.".format("64" if is64bit else "32")) # OLEACC (MSAA) proxies apply32bitRegistryPatch(OLEACC_REG_FILE_PATH) if is64bit: apply64bitRegistryPatch(OLEACC_REG_FILE_PATH) # IDispatch and other common OLE interfaces register32bitServer(os.path.join(SYSTEM32, "oleaut32.dll")) register32bitServer(os.path.join(SYSTEM32, "actxprxy.dll")) if is64bit: register64bitServer(os.path.join(SYSTEM32, "oleaut32.dll")) register64bitServer(os.path.join(SYSTEM32, "actxprxy.dll")) # IServiceProvider on windows 7 can become unregistered if OSMajorMinor == (6, 1): # Windows 7 # There was no "Program Files (x86)" in Windows 7 32-bit, so we cover both cases register32bitServer( os.path.join(PROGRAM_FILES_X86 if is64bit else PROGRAM_FILES, "Internet Explorer", "ieproxy.dll")) if is64bit: register64bitServer( os.path.join(PROGRAM_FILES, "Internet Explorer", "ieproxy.dll"))
def script_reportComment(self, gesture): if winVersion.getWinVer() >= winVersion.WIN11: noteElement = self.UIAAnnotationObjects.get(self._UIAExcelCustomAnnotationTypes.note.id) if noteElement: name = noteElement.CurrentName desc = noteElement.GetCurrentPropertyValue(UIAHandler.UIA_FullDescriptionPropertyId) # Translators: a note on a cell in Microsoft excel. text = _("{name}: {desc}").format(name=name, desc=desc) ui.message(text) else: # Translators: message when a cell in Excel contains no note ui.message(_("No note on this cell")) commentsElement = self.UIAAnnotationObjects.get(UIAHandler.AnnotationType_Comment) if commentsElement: comment = commentsElement.GetCurrentPropertyValue(UIAHandler.UIA_FullDescriptionPropertyId) author = commentsElement.GetCurrentPropertyValue(UIAHandler.UIA_AnnotationAuthorPropertyId) numReplies = commentsElement.GetCurrentPropertyValue(self._UIAExcelCustomProps.commentReplyCount.id) if numReplies == 0: # Translators: a comment on a cell in Microsoft excel. text = _("Comment thread: {comment} by {author}").format( comment=comment, author=author ) else: # Translators: a comment on a cell in Microsoft excel. text = _("Comment thread: {comment} by {author} with {numReplies} replies").format( comment=comment, author=author, numReplies=numReplies ) ui.message(text) else: # Translators: A message in Excel when there is no comment thread ui.message(_("No comment thread on this cell"))
def fixCOMRegistrations(): """ Registers most common COM proxies, in case they had accidentally been unregistered or overwritten by 3rd party software installs/uninstalls. """ is64bit = os.environ.get("PROCESSOR_ARCHITEW6432", "").endswith('64') winVer = winVersion.getWinVer() OSMajorMinor = (winVer.major, winVer.minor) log.debug( "Fixing COM registration for Windows %s.%s, %s" % (OSMajorMinor[0], OSMajorMinor[1], "64 bit" if is64bit else "32 bit")) # Commands taken from NVDA issue #2807 comment https://github.com/nvaccess/nvda/issues/2807#issuecomment-320149243 # OLEACC (MSAA) proxies applyRegistryPatch(OLEACC_REG_FILE_PATH) if is64bit: applyRegistryPatch(OLEACC_REG_FILE_PATH, wow64=True) # IDispatch and other common OLE interfaces registerServer(os.path.join(system32, 'oleaut32.dll')) registerServer(os.path.join(system32, 'actxprxy.dll')) if is64bit: registerServer(os.path.join(sysWow64, 'oleaut32.dll'), wow64=True) registerServer(os.path.join(sysWow64, 'actxprxy.dll'), wow64=True) # IServiceProvider on windows 7 can become unregistered if OSMajorMinor == (6, 1): # Windows 7 registerServer( os.path.join(programFiles, 'Internet Explorer', 'ieproxy.dll')) if is64bit: registerServer(os.path.join(programFilesX86, 'Internet Explorer', 'ieproxy.dll'), wow64=True)
def _getFormatFieldAtRange(self, textRange, formatConfig, ignoreMixedValues=False): formatField = super()._getFormatFieldAtRange( textRange, formatConfig, ignoreMixedValues=ignoreMixedValues) if not formatField: return formatField if winVersion.getWinVer() >= winVersion.WIN11: docElement = self.obj.UIAElement if formatConfig['reportLineNumber']: lineNumber = UIARemote.msWord_getCustomAttributeValue( docElement, textRange, UIACustomAttributeID.LINE_NUMBER) if isinstance(lineNumber, int): formatField.field['line-number'] = lineNumber if formatConfig['reportPage']: sectionNumber = UIARemote.msWord_getCustomAttributeValue( docElement, textRange, UIACustomAttributeID.SECTION_NUMBER) if isinstance(sectionNumber, int): formatField.field['section-number'] = sectionNumber if False: # #13511: Fetching of text-column-number is disabled # as it causes Microsoft Word 16.0.1493 and newer to crash!! # This should only be reenabled for versions identified not to crash. textColumnNumber = UIARemote.msWord_getCustomAttributeValue( docElement, textRange, UIACustomAttributeID.COLUMN_NUMBER) if isinstance(textColumnNumber, int): formatField.field[ 'text-column-number'] = textColumnNumber return formatField
def isGoodUIAWindow(self, hwnd): currentWinVer = winVersion.getWinVer() # #9204: shell raises window open event for emoji panel in build 18305 and later. if (currentWinVer >= winVersion.WIN10_1903 and winUser.getClassName(hwnd) == "ApplicationFrameWindow"): return True # #13506: Windows 11 UI elements such as Taskbar should be reclassified as UIA windows, # letting NVDA announce shell elements when navigating with mouse and/or touch, # notably when interacting with windows labeled "DesktopWindowXamlSource". # WORKAROUND UNTIL A PERMANENT FIX IS FOUND ACROSS APPS if (currentWinVer >= winVersion.WIN11 # Traverse parents until arriving at the top-level window with the below class names. # This is more so for the shell root (first class name), and for others, class name check would work # since they are top-level windows for windows shown on screen such as Task View. # However, look for the ancestor for consistency. and winUser.getClassName( winUser.getAncestor(hwnd, winUser.GA_ROOT)) in ( # Windows 11 shell UI root, housing various shell elements shown on screen if enabled. "Shell_TrayWnd", # Start, Search, Widgets, other shell elements # Top-level window class names from Windows 11 shell features "Shell_InputSwitchTopLevelWindow", # Language switcher "XamlExplorerHostIslandWindow", # Task View and Snap Layouts ) # #13717: on some systems, Windows 11 shell elements are reported as IAccessible, # notably Start button, causing IAccessible handler to report attribute error when handling events. and winUser.getClassName(hwnd) != "Start"): return True return False
def _setProductInfo(self): """Set productName and productVersion attributes. There are at least two ways of obtaining product info for an app: * Package info for hosted apps * File version info for other apps and for some hosted apps """ # Sometimes (I.E. when NVDA starts) handle is 0, so stop if it is the case if not self.processHandle: raise RuntimeError("processHandle is 0") # No need to worry about immersive (hosted) apps and friends until Windows 8. if winVersion.getWinVer() >= winVersion.WIN8: # Some apps such as File Explorer says it is an immersive process but error 15700 is shown. # Therefore resort to file version info behavior because it is not a hosted app. # Others such as Store version of Office are not truly hosted apps, # yet returns an internal version anyway because they are converted desktop apps. # For immersive apps, default implementation is generic - returns Windows version information. # Thus probe package full name and parse the serialized representation of package info structure. packageInfo = self._getImmersivePackageInfo() if packageInfo is not None: # Product name is of the form publisher.name for a hosted app. productInfo = packageInfo.split("_") else: # File Explorer and friends which are really native aps. productInfo = self._getExecutableFileInfo() else: # Not only native apps, but also some converted desktop aps such as Office. productInfo = self._getExecutableFileInfo() self.productName = productInfo[0] self.productVersion = productInfo[1]
def _get_states(self): states = super().states if controlTypes.State.FOCUSED in states and self.selectionContainer.getSelectedItemsCount() == 0: # #12530: In some versions of Excel, the selection pattern reports 0 selected items, # even though the focused UIA element reports as selected. # NVDA only silences the positive SELECTED state when one item is selected. # Therefore, by discarding both the SELECTED and SELECTABLE states, # we eliminate the redundant selection announcement. states.discard(controlTypes.State.SELECTED) states.discard(controlTypes.State.SELECTABLE) if self._isContentTooLargeForCell: if not self._nextCellHasContent: states.add(controlTypes.State.OVERFLOWING) else: states.add(controlTypes.State.CROPPED) if self._getUIACacheablePropertyValue(self._UIAExcelCustomProps.cellFormula.id): states.add(controlTypes.State.HASFORMULA) if self._getUIACacheablePropertyValue(self._UIAExcelCustomProps.hasDataValidationDropdown.id): states.add(controlTypes.State.HASPOPUP) if winVersion.getWinVer() >= winVersion.WIN11: try: annotationTypes = self._getUIACacheablePropertyValue(UIAHandler.UIA_AnnotationTypesPropertyId) except COMError: # annotationTypes cannot be fetched on older Operating Systems such as Windows 7. annotationTypes = None if annotationTypes: if self._UIAExcelCustomAnnotationTypes.note.id in annotationTypes: states.add(controlTypes.State.HASNOTE) return states
def event_nameChange(self, obj, nextHandler): # Logic for IME candidate items is handled all within its own object # Therefore pass these events straight on. if isinstance(obj, ImeCandidateItem): return nextHandler() elif isinstance(obj, ImeCandidateUI): return nextHandler() # On some systems, touch keyboard keys keeps firing name change event. # In build 17704, whenever skin tones are selected, name change is fired by emoji entries (GridViewItem). if ((obj.UIAElement.cachedClassName in ("CRootKey", "GridViewItem")) # Just ignore useless clipboard status. # Also top emoji search result must be announced for better user experience. or (obj.UIAElement.cachedAutomationID in ("TEMPLATE_PART_ClipboardItemsList", "TEMPLATE_PART_Search_TextBlock")) # And no, emoji entries should not be announced here. or (self._recentlySelected is not None and self._recentlySelected in obj.name)): return # The word "blank" is kept announced, so suppress this on build 17666 and later. if winVersion.getWinVer() > winVersion.WIN10_1803: # In build 17672 and later, # return immediately when element selected event on clipboard item was fired just prior to this. # In some cases, parent will be None, as seen when emoji panel is closed in build 18267. try: if obj.UIAElement.cachedAutomationID == "TEMPLATE_PART_ClipboardItemIndex" or obj.parent.UIAElement.cachedAutomationID == "TEMPLATE_PART_ClipboardItemsList": return except AttributeError: return if not self._emojiPanelJustOpened or obj.UIAElement.cachedAutomationID != "TEMPLATE_PART_ExpressionGroupedFullView": speech.cancelSpeech() self._emojiPanelJustOpened = False # Don't forget to add "Microsoft Candidate UI" as something that should be suppressed. if obj.UIAElement.cachedAutomationID not in ("TEMPLATE_PART_ExpressionFullViewItemsGrid", "TEMPLATE_PART_ClipboardItemIndex", "CandidateWindowControl"): ui.message(obj.name) nextHandler()
def test_getWinVer(self): # Test a 3-tuple consisting of version major, minor, build. # sys.getwindowsversion() internally returns a named tuple, so comparing tuples is possible. currentWinVer = winVersion.getWinVer() winVerPython = sys.getwindowsversion() self.assertTupleEqual( (currentWinVer.major, currentWinVer.minor, currentWinVer.build), winVerPython[:3])
def event_NVDAObject_init(self, obj): if isinstance(obj, UIA): # #10341: Build 18363 introduces modern search experience in File Explorer. # As part of this, suggestion count is part of a live region. # Although it is geared for Narrator, it is applicable to other screen readers as well. # The live region itself is a child of the one shown here. if (winVersion.getWinVer() >= winVersion.WIN10_1909 and obj.UIAAutomationId == "suggestionCountForNarrator" and obj.firstChild is not None): obj.name = obj.firstChild.name
def script_win10Dictation(self, gesture): # Press Windows+H on Windows 10 Version 1709 (Fall Creators Update) and later. import winVersion import keyboardHandler if winVersion.getWinVer() < winVersion.WIN10_1709: # Translators: message shown when dictation command is unavailable. ui.message( _("Dictation is supported on Windows 10 Version 1709 or later") ) else: keyboardHandler.KeyboardInputGesture.fromName("windows+h").send()
def event_UIA_window_windowOpen(self, obj, nextHandler): firstChild = obj.firstChild # Handle Ime Candidate UI being shown if isinstance(firstChild, ImeCandidateUI): eventHandler.queueEvent("show", firstChild) return # Make sure to announce most recently used emoji first in post-1709 builds. # Fake the announcement by locating 'most recently used" category and calling selected event on this. # However, in build 17666 and later, child count is the same for both emoji panel and hardware keyboard candidates list. # Thankfully first child automation ID's are different for each modern input technology. # However this event is raised when the input panel closes. if obj.firstChild is None: return # #9104: different aspects of modern input panel are represented by automation iD's. childAutomationID = obj.firstChild.UIAElement.cachedAutomationID # Emoji panel for 1709 (build 16299) and 1803 (17134). emojiPanelInitial = winVersion.WIN10_1709 # This event is properly raised in build 17134. emojiPanelWindowOpenEvent = winVersion.WIN10_1803 if (emojiPanelInitial <= winVersion.getWinVer() <= emojiPanelWindowOpenEvent and childAutomationID in ("TEMPLATE_PART_ExpressiveInputFullViewFuntionBarItemControl", "TEMPLATE_PART_ExpressiveInputFullViewFuntionBarCloseButton")): self.event_UIA_elementSelected(obj.lastChild.firstChild, nextHandler) # Handle hardware keyboard suggestions. # Treat it the same as CJK composition list - don't announce this if candidate announcement setting is off. elif childAutomationID == "CandidateWindowControl" and config.conf[ "inputComposition"]["autoReportAllCandidates"]: try: self.event_UIA_elementSelected( obj.firstChild.firstChild.firstChild, nextHandler) except AttributeError: # Because this is dictation window. pass # Emoji panel in build 17666 and later (unless this changes). elif childAutomationID == "TEMPLATE_PART_ExpressionGroupedFullView": self._emojiPanelJustOpened = True try: self.event_UIA_elementSelected( obj.firstChild.children[-2].firstChild.firstChild, nextHandler) except AttributeError: # In build 18272's emoji panel, emoji list becomes empty in some situations. pass # Clipboard history. # Move to clipboard list so element selected event can pick it up. # #9103: if clipboard is empty, a status message is displayed instead, and luckily it is located where clipboard data items can be found. elif childAutomationID == "TEMPLATE_PART_ClipboardTitleBar": self.event_UIA_elementSelected(obj.children[-2], nextHandler) nextHandler()
def chooseNVDAObjectOverlayClasses(self, obj, clsList): if obj.windowClassName == self.TERMINAL_WINDOW_CLASS and isinstance( obj, IAccessible ) and obj.IAccessibleRole == oleacc.ROLE_SYSTEM_CLIENT: try: clsList.remove(DisplayModelEditableText) except ValueError: pass if getWinVer() >= WIN10_1607: clsList[0:0] = (KeyboardHandlerBasedTypedCharSupport, DisplayModelLiveText) else: clsList[0:0] = (Terminal, DisplayModelLiveText)
def onInstall(): import winVersion import gui import wx if winVersion.getWinVer() < winVersion.WIN10: gui.messageBox( _( # Translators: Dialog text shown when trying to install the add-on on an older version of Windows. "You are using an older version of Windows. This add-on requires Windows 10 or later." ), # Translators: title of the dialog shown when trying to install the add-on on an old version of Windows. _("Old Windows version"), wx.OK | wx.ICON_WARNING) raise RuntimeError( "Attempting to install Enhanced Touch Gestures on Windows releases earlier than 10" )
def shouldUseToUnicodeEx(focus=None): "Returns whether to use ToUnicodeEx to determine typed characters." if not focus: focus = api.getFocusObject() from NVDAObjects.behaviors import KeyboardHandlerBasedTypedCharSupport return ( # This is only possible in Windows 10 1607 and above winVersion.getWinVer() >= winVersion.WIN10_1607 and ( # Either of # We couldn't inject in-process, and its not a legacy console window without keyboard support. # console windows have their own specific typed character support. (not focus.appModule.helperLocalBindingHandle and focus.windowClassName != 'ConsoleWindowClass') # or the focus is within a UWP app, where WM_CHAR never gets sent or focus.windowClassName.startswith('Windows.UI.Core') # Or this is a console with keyboard support, where WM_CHAR messages are doubled or isinstance(focus, KeyboardHandlerBasedTypedCharSupport)))
def __post_init__(self) -> None: """ The id field must be initialised at runtime. A GUID uniquely identifies a custom annotation, but the UIA system relies on integer IDs. Any application (clients or providers) can register a custom annotation type, subsequent applications will get the same id for a given GUID. Registering custom annotations is only supported on Windows 11 and above. For any lesser version, id will be 0. """ if winVersion.getWinVer() >= winVersion.WIN11: import NVDAHelper self.id = NVDAHelper.localLib.registerUIAAnnotationType( byref(self.guid), ) else: self.id = 0
class EdgeNode(web.UIAWeb): _edgeIsPreGapRemoval = winVersion.getWinVer().build < 15048 _TextInfo = EdgeTextInfo_preGapRemoval if _edgeIsPreGapRemoval else EdgeTextInfo def getNormalizedUIATextRangeFromElement(self, UIAElement): textRange = super().getNormalizedUIATextRangeFromElement(UIAElement) if not textRange or not self._edgeIsPreGapRemoval: return textRange # Move the start of a UIA text range past any element start character stops lastCharInfo = EdgeTextInfo_preGapRemoval(obj=self, position=None, _rangeObj=textRange) lastCharInfo._rangeObj = textRange charInfo = lastCharInfo.copy() charInfo.collapse() # charInfo is a EdgeTextInfo_preGapRemoval, so why is # EdgeTextInfo.move used instead of # EdgeTextInfo_preGapRemoval.move? while 0 != super(EdgeTextInfo, charInfo).move(textInfos.UNIT_CHARACTER, 1): charInfo.setEndPoint(lastCharInfo, "startToStart") if charInfo.text or charInfo._hasEmbedded(): break lastCharInfo.setEndPoint(charInfo, "startToEnd") charInfo.collapse(True) return textRange def _get__isTextEmpty(self): # NOTE: we can not check the result of the EdgeTextInfo move implementation to determine if we added # any characters to the range, since it seems to return 1 even when the text property has not changed. # Also we can not move (repeatedly by one character) since this can overrun the end of the field in edge. # So instead, we use self to make a text info (which should have the right range) and then use the UIA # specific _rangeObj.getText function to get a subset of the full range of characters. ti = self.makeTextInfo(self) if ti.isCollapsed: # it is collapsed therefore it is empty. # exit early so we do not have to do not have to fetch `ti.text` which # is potentially costly to performance. return True numberOfCharacters = 2 text = ti._rangeObj.getText(numberOfCharacters) # Edge can report newline for empty fields: if text == "\n": return True return False
def event_gainFocus(self): # #6671: Normally we do not allow WorkerW thread to send gain focus event, # as it causes 'pane" to be announced when minimizing windows or moving to desktop. # However when closing Windows 7 Start Menu in some cases # focus lands on it instead of the focused desktop item. # Simply ignore the event if running on anything other than Win 7. if winVersion.getWinVer() > winVersion.WIN7_SP1: return if eventHandler.isPendingEvents("gainFocus"): return if self.simpleFirstChild: # If focus is not going to be moved autotically # we need to forcefully move it to the focused desktop item. # As we are interested in the first focusable object below the pane use simpleFirstChild. self.simpleFirstChild.setFocus() return super().event_gainFocus()
def touchSupported(debugLog: bool = False): """Returns if the system and current NVDA session supports touchscreen interaction. @param debugLog: Whether to log additional details about touch support to the NVDA log. """ if not config.isInstalledCopy() and not config.isAppX: if debugLog: log.debugWarning("Touch only supported on installed copies") return False if winVersion.getWinVer() < winVersion.WIN8: if debugLog: log.debugWarning("Touch only supported on Windows 8 and higher") return False maxTouches = windll.user32.GetSystemMetrics(SM_MAXIMUMTOUCHES) if maxTouches <= 0: if debugLog: log.debugWarning("No touch devices found") return False return True
def _get_isWindowsStoreApp(self): """Whether this process is a Windows Store (immersive) process. An immersive process is a Windows app that runs inside a Windows Runtime (WinRT) container. These include Windows store apps on Windows 8 and 8.1, and Universal Windows Platform (UWP) apps on Windows 10. A special case is a converted desktop app distributed on Microsoft Store. Not all immersive apps are packaged as a true Store app with a package info e.g. File Explorer reports itself as immersive when it is not. @rtype: bool """ if winVersion.getWinVer() < winVersion.WIN8: # Windows Store/UWP apps were introduced in Windows 8. self.isWindowsStoreApp = False return False # Package info is much more accurate than IsImmersiveProcess # because IsImmersive Process returns nonzero for File Explorer # and zero for Store version of Office. if self._getImmersivePackageInfo() is not None: self.isWindowsStoreApp = True return True self.isWindowsStoreApp = False return self.isWindowsStoreApp
def shouldUseUIAInMSWord(appModule: appModuleHandler.AppModule) -> bool: allow = AllowUiaInMSWord.getConfig() if allow == AllowUiaInMSWord.ALWAYS: log.debug("User has requested UIA in MS Word always") return True canUseOlderInProcessApproach = bool(appModule.helperLocalBindingHandle) if not canUseOlderInProcessApproach: log.debug( "Using UIA in MS Word as no alternative object model available") return True if winVersion.getWinVer() < winVersion.WIN11: log.debug( "Not using UIA in MS Word on pre Windows 11 OS due to missing custom extensions" ) return False if allow != AllowUiaInMSWord.WHERE_SUITABLE: log.debug("User does not want UIA in MS Word unless necessary") return False isOfficeApp = appModule.productName.startswith( ("Microsoft Office", "Microsoft Outlook")) if not isOfficeApp: log.debug(f"Unknown Office app: {appModule.productName}") return False try: officeVersion = tuple( int(x) for x in appModule.productVersion.split('.')[:3]) except Exception: log.debugWarning( f"Unable to parse office version: {appModule.productVersion}", exc_info=True) return False if officeVersion < (16, 0, 15000): log.debug( f"MS word too old for suitable UIA, Office version: {officeVersion}" ) return False log.debug(f"Using UIA due to suitable Office version: {officeVersion}") return True
def _showAddonUpdateUI(): def _showAddonUpdateUICallback(info): import gui from .addonGuiEx import AddonUpdatesDialog gui.mainFrame.prePopup() AddonUpdatesDialog(gui.mainFrame, info).Show() gui.mainFrame.postPopup() try: info = checkForAddonUpdates() except: info = None raise if info is not None: # Show either the update notification toast (Windows 10 and later) # or the results dialog (other Windows releases and server systems). # On Windows 10 and later (client versions), this behavior is configurable. # If toast is shown, checking for add-on updates from tools menu will merely show the results dialog. # wxPython 4.1.0 (and consequently, wxWidges 3.1.0) simplifies this by # allowing action handlers to be defined for toasts, which will then show the results dialog on the spot. # However it doesn't work for desktop apps such as NVDA. import winVersion winVer = winVersion.getWinVer() if (winVer >= winVersion.WIN10 and winVer.productType == "workstation" and addonUtils.updateState["updateNotification"] == "toast"): global _updateInfo updateMessage = _( # Translators: presented as part of add-on update notification message. "One or more add-on updates are available. " "Go to NVDA menu, Tools, Check for add-on updates to review them." ) # Translators: title of the add-on update notification message. wx.adv.NotificationMessage(_("NVDA add-on updates"), updateMessage).Show(timeout=30) _updateInfo = info else: wx.CallAfter(_showAddonUpdateUICallback, info)
def check(cls): # Only present this as an available synth if this is Windows 10. return winVersion.getWinVer() >= winVersion.WIN10
def isGoodUIAWindow(self, hwnd): # #9204: shell raises window open event for emoji panel in build 18305 and later. if (winVersion.getWinVer() >= winVersion.WIN10_1903 and winUser.getClassName(hwnd) == "ApplicationFrameWindow"): return True return False
def _get_TextInfo(cls): if winVersion.getWinVer() <= winVersion.WIN7_SP1: cls.TextInfo = EditTextInfo else: cls.TextInfo = super().TextInfo return cls.TextInfo
# This file is covered by the GNU General Public License. # See the file COPYING for more details. """Utilities for working with the Windows Ease of Access Center. """ from enum import Enum, IntEnum from typing import Any, List import globalVars from logHandler import log import winreg import winUser import winVersion # Windows >= 8 canConfigTerminateOnDesktopSwitch: bool = winVersion.getWinVer( ) >= winVersion.WIN8 _APP_KEY_NAME = "nvda_nvda_v1" def __getattr__(attrName: str) -> Any: """Module level `__getattr__` used to preserve backward compatibility.""" if attrName == "ROOT_KEY" and globalVars._allowDeprecatedAPI: log.warning("ROOT_KEY is deprecated, use RegistryKey.ROOT instead.") return RegistryKey.ROOT.value if attrName == "APP_KEY_PATH" and globalVars._allowDeprecatedAPI: log.warning("APP_KEY_PATH is deprecated, use RegistryKey.APP instead.") return RegistryKey.APP.value if attrName == "APP_KEY_NAME" and globalVars._allowDeprecatedAPI: log.warning("APP_KEY_NAME is deprecated.") return _APP_KEY_NAME raise AttributeError(
def findExtraOverlayClasses(obj, clsList): if getWinVer( ) >= WIN10_1607 and config.conf['terminals']['keyboardSupportInLegacy']: clsList.append(EnhancedLegacyWinConsole) else: clsList.append(LegacyWinConsole)
#easeOfAccess.py #A part of NonVisual Desktop Access (NVDA) #Copyright (C) 2014 NV Access Limited #This file is covered by the GNU General Public License. #See the file COPYING for more details. """Utilities for working with the Windows Ease of Access Center. """ import winreg import ctypes import winUser import winVersion # Windows >= Vista isSupported = winVersion.getWinVer().major >= 6 # Windows >= 8 canConfigTerminateOnDesktopSwitch = isSupported and winVersion.getWinVer( ) >= winVersion.WIN8 ROOT_KEY = r"Software\Microsoft\Windows NT\CurrentVersion\Accessibility" APP_KEY_NAME = "nvda_nvda_v1" APP_KEY_PATH = r"%s\ATs\%s" % (ROOT_KEY, APP_KEY_NAME) def isRegistered(): try: winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, APP_KEY_PATH, 0, winreg.KEY_READ | winreg.KEY_WOW64_64KEY) return True except WindowsError: return False
return _curSynth def getSynthInstance(name, asDefault=False): newSynth: SynthDriver = _getSynthDriver(name)() if asDefault and newSynth.name == 'oneCore': # Will raise an exception if oneCore does not support the system language newSynth._getDefaultVoice(pickAny=False) newSynth.initSettings() return newSynth # The synthDrivers that should be used by default. # The first that successfully initializes will be used when config is set to auto (I.e. new installs of NVDA). defaultSynthPriorityList = ['espeak', 'silence'] if winVersion.getWinVer() >= winVersion.WIN10: # Default to OneCore on Windows 10 and above defaultSynthPriorityList.insert(0, 'oneCore') def setSynth(name: Optional[str], isFallback: bool = False): asDefault = False global _curSynth, _audioOutputDevice if name is None: _curSynth.terminate() _curSynth = None return True if name == 'auto': asDefault = True name = defaultSynthPriorityList[0] if _curSynth: