diff --git a/meson.build b/meson.build index ac9ee02..ed57dbd 100644 --- a/meson.build +++ b/meson.build @@ -28,6 +28,11 @@ if not json_result.found() error('json module is required') endif +pluggy_result = python.find_installation('python3', modules:['pluggy'], required: false) +if not pluggy_result.found() + error('pluggy module is required') +endif + # End users might not have the Gtk development libraries installed, making pkg-config fail. # Therefore, check this dependency via python. gtk_major_version = '3' @@ -50,7 +55,6 @@ optional_modules = { 'brlapi': 'braille output', 'louis': 'contracted braille', 'speechd': 'speech output', - 'pluggy': 'plugin system', 'dasbus': 'D-Bus remote controller', 'psutil': 'system information commands', 'gi.repository.Wnck': 'mouse review', diff --git a/pyproject.toml b/pyproject.toml index a0166cd..ffac654 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,6 +11,7 @@ requires-python = ">=3.9" license = { text = "LGPL-2.1-or-later" } dependencies = [ "pygobject>=3.18", + "pluggy", "brlapi; extra == 'braille'", "python-speechd; extra == 'speech'", "piper-tts; extra == 'piper'", diff --git a/src/cthulhu.py b/src/cthulhu.py index 2843c66..aecf2d9 100644 --- a/src/cthulhu.py +++ b/src/cthulhu.py @@ -368,7 +368,7 @@ def main(): debug.printMessage(debug.LEVEL_INFO, "INFO: Preparing to launch.", True) from cthulhu import cthulhu - manager = cthulhu.getSettingsManager() + manager = cthulhu.cthulhuApp.settingsManager if not manager: print(messages.CLI_SETTINGS_MANAGER_ERROR) diff --git a/src/cthulhu/ax_utilities.py b/src/cthulhu/ax_utilities.py index 4de6e53..9d53eb7 100644 --- a/src/cthulhu/ax_utilities.py +++ b/src/cthulhu/ax_utilities.py @@ -129,6 +129,11 @@ class AXUtilities: debug.print_tokens(debug.LEVEL_INFO, tokens, True) return False + if not AXUtilitiesState.is_visible(window): + tokens.append("lacks visible state") + debug.print_tokens(debug.LEVEL_INFO, tokens, True) + return False + if AXUtilitiesState.is_iconified(window): tokens.append("is iconified") debug.print_tokens(debug.LEVEL_INFO, tokens, True) @@ -174,35 +179,27 @@ class AXUtilities: tokens = ["AXUtilities: These windows all claim to be active:", candidates] debug.print_tokens(debug.LEVEL_INFO, tokens, True) - # Some electron apps running in the background claim to be active even when they - # are not. These are the ones we know about. We can add others as we go. - suspect_apps = ["slack", - "discord", - "outline-client", - "whatsapp-desktop-linux"] - filtered = [] + focused_candidates = [] for frame in candidates: - if AXObject.get_name(AXUtilitiesApplication.get_application(frame)) in suspect_apps: - tokens = ["AXUtilities: Suspecting", frame, "is a non-active Electron app"] - debug.print_tokens(debug.LEVEL_INFO, tokens, True) - else: - filtered.append(frame) + if AXUtilitiesState.is_focused(frame): + focused_candidates.append(frame) + continue + focused = AXUtilities.get_focused_object(frame) + if focused is not None: + focused_candidates.append(frame) - if len(filtered) == 1: - tokens = ["AXUtilities: Active window is believed to be", filtered[0]] + if len(focused_candidates) == 1: + tokens = ["AXUtilities: Active window has focus:", focused_candidates[0]] debug.print_tokens(debug.LEVEL_INFO, tokens, True) - return filtered[0] + return focused_candidates[0] - guess: Optional[Atspi.Accessible] = None - if filtered: - tokens = ["AXUtilities: Still have multiple active windows:", filtered] + if not focused_candidates and len(candidates) > 1: + tokens = ["AXUtilities: No focused active window found"] debug.print_tokens(debug.LEVEL_INFO, tokens, True) - guess = filtered[0] + return None - if guess is not None: - tokens = ["AXUtilities: Returning", guess, "as active window"] - else: - tokens = ["AXUtilities: No active window found"] + guess = focused_candidates[0] if focused_candidates else candidates[0] + tokens = ["AXUtilities: Returning", guess, "as active window"] debug.print_tokens(debug.LEVEL_INFO, tokens, True) return guess diff --git a/src/cthulhu/bookmarks.py b/src/cthulhu/bookmarks.py index 68481d8..6d474f6 100644 --- a/src/cthulhu/bookmarks.py +++ b/src/cthulhu/bookmarks.py @@ -29,6 +29,8 @@ import pickle import os import urllib.parse +from . import cthulhu # Need access to cthulhuApp + from . import cmdnames from . import keybindings from . import input_event @@ -36,7 +38,7 @@ from . import messages from . import settings_manager from .ax_object import AXObject -_settingsManager = settings_manager.getManager() +_settingsManager = None # Removed - use cthulhu.cthulhuApp.settingsManager class Bookmarks: """Represents a default bookmark handler.""" @@ -236,7 +238,7 @@ class Bookmarks: """ Read saved bookmarks from disk. Currently an unpickled object that represents a bookmark """ filename = filename or self._script.name.split(' ')[0] - cthulhuDir = _settingsManager.getPrefsDir() + cthulhuDir = cthulhu.cthulhuApp.settingsManager.getPrefsDir() if not cthulhuDir: return @@ -254,7 +256,7 @@ class Bookmarks: """ Write bookmarks to disk. bookmarksObj must be a pickleable object. """ filename = filename or self._script.name.split(' ')[0] - cthulhuDir = _settingsManager.getPrefsDir() + cthulhuDir = cthulhu.cthulhuApp.settingsManager.getPrefsDir() cthulhuBookmarksDir = os.path.join(cthulhuDir, "bookmarks") # create directory if it does not exist. correct place?? try: diff --git a/src/cthulhu/braille.py b/src/cthulhu/braille.py index 139d0e6..71f1b4c 100644 --- a/src/cthulhu/braille.py +++ b/src/cthulhu/braille.py @@ -60,10 +60,26 @@ from .ax_object import AXObject from .ax_hypertext import AXHypertext from .cthulhu_platform import tablesdir -_logger = logger.getLogger() -log = _logger.newLog("braille") +# Lazy initialization to avoid circular imports +_logger = None +log = None _monitor = None -_settingsManager = settings_manager.getManager() +_settingsManager = None + +def _ensureLogger(): + """Ensure logger is initialized.""" + global _logger, log + if _logger is None: + from . import cthulhu + _logger = cthulhu.cthulhuApp.logger + log = _logger.newLog("braille") + +def _ensureSettingsManager(): + """Ensure settings manager is initialized.""" + global _settingsManager + if _settingsManager is None: + from . import cthulhu + _settingsManager = cthulhu.cthulhuApp.settingsManager try: msg = "BRAILLE: About to import brlapi." @@ -1223,6 +1239,8 @@ def disableBraille(): """Hand off control to other screen readers, shutting down the BrlAPI connection if needed""" + _ensureSettingsManager() + global idle tokens = ["BRAILLE: Disabling braille. BrlAPI running:", _brlAPIRunning] @@ -1232,6 +1250,7 @@ def disableBraille(): msg = "BRAILLE: BrlApi running and not idle." debug.printMessage(debug.LEVEL_INFO, msg, True) + _ensureSettingsManager() if not _idleBraille() and not _settingsManager.getSetting('enableBraille'): # BrlAPI before 0.8 and we really want to shut down msg = "BRAILLE: could not go idle, completely shut down" @@ -1241,6 +1260,8 @@ def disableBraille(): def checkBrailleSetting(): """Disable Braille if it got disabled in the preferences""" + _ensureSettingsManager() + msg = "BRAILLE: Checking braille setting." debug.printMessage(debug.LEVEL_INFO, msg, True) @@ -1271,6 +1292,9 @@ def refresh(panToCursor=True, targetCursorCell=0, getLinkMask=True, stopFlash=Tr # TODO - JD: Split this work out into smaller methods. + _ensureLogger() + _ensureSettingsManager() + global endIsShowing global beginningIsShowing global cursorCell @@ -1284,6 +1308,7 @@ def refresh(panToCursor=True, targetCursorCell=0, getLinkMask=True, stopFlash=Tr killFlash(restoreSaved=False) # TODO - JD: This should be taken care of in cthulhu.py. + _ensureSettingsManager() if not _settingsManager.getSetting('enableBraille') \ and not _settingsManager.getSetting('enableBrailleMonitor'): if _brlAPIRunning: @@ -1433,6 +1458,7 @@ def refresh(panToCursor=True, targetCursorCell=0, getLinkMask=True, stopFlash=Tr submask += '\x00' * (len(substring) - len(submask)) + _ensureSettingsManager() if _settingsManager.getSetting('enableBraille'): _enableBraille() diff --git a/src/cthulhu/braille_generator.py b/src/cthulhu/braille_generator.py index 47d4177..d8021d8 100644 --- a/src/cthulhu/braille_generator.py +++ b/src/cthulhu/braille_generator.py @@ -49,7 +49,7 @@ from .ax_utilities import AXUtilities from .ax_utilities_relation import AXUtilitiesRelation from .braille_rolenames import shortRoleNames -_settingsManager = settings_manager.getManager() +_settingsManager = None # Removed - use cthulhu.cthulhuApp.settingsManager class Space: """A dummy class to indicate we want to insert a space into an @@ -96,8 +96,8 @@ class BrailleGenerator(generator.Generator): return AXObject.get_name(obj) == AXObject.get_name(region.accessible) def generateBraille(self, obj, **args): - if not _settingsManager.getSetting('enableBraille') \ - and not _settingsManager.getSetting('enableBrailleMonitor'): + if not cthulhu.cthulhuApp.settingsManager.getSetting('enableBraille') \ + and not cthulhu.cthulhuApp.settingsManager.getSetting('enableBrailleMonitor'): debug.printMessage(debug.LEVEL_INFO, "BRAILLE GENERATOR: generation disabled", True) return [[], None] @@ -163,12 +163,12 @@ class BrailleGenerator(generator.Generator): """ if args.get('isProgressBarUpdate') \ - and not _settingsManager.getSetting('brailleProgressBarUpdates'): + and not cthulhu.cthulhuApp.settingsManager.getSetting('brailleProgressBarUpdates'): return [] result = [] role = args.get('role', AXObject.get_role(obj)) - verbosityLevel = _settingsManager.getSetting('brailleVerbosityLevel') + verbosityLevel = cthulhu.cthulhuApp.settingsManager.getSetting('brailleVerbosityLevel') doNotPresent = [Atspi.Role.UNKNOWN, Atspi.Role.REDUNDANT_OBJECT, @@ -200,7 +200,7 @@ class BrailleGenerator(generator.Generator): - obj: an Accessible object """ - if _settingsManager.getSetting('brailleRolenameStyle') \ + if cthulhu.cthulhuApp.settingsManager.getSetting('brailleRolenameStyle') \ == settings.BRAILLE_ROLENAME_STYLE_SHORT: role = args.get('role', AXObject.get_role(obj)) rv = shortRoleNames.get(role) @@ -230,7 +230,7 @@ class BrailleGenerator(generator.Generator): or an empty array if no accelerator can be found. """ - verbosityLevel = _settingsManager.getSetting('brailleVerbosityLevel') + verbosityLevel = cthulhu.cthulhuApp.settingsManager.getSetting('brailleVerbosityLevel') if verbosityLevel == settings.VERBOSITY_LEVEL_BRIEF: return [] @@ -275,7 +275,7 @@ class BrailleGenerator(generator.Generator): previous object with focus. """ result = [] - if not _settingsManager.getSetting('enableBrailleContext'): + if not cthulhu.cthulhuApp.settingsManager.getSetting('enableBrailleContext'): return result args['includeContext'] = False @@ -417,14 +417,14 @@ class BrailleGenerator(generator.Generator): return [] def _getProgressBarUpdateInterval(self): - interval = _settingsManager.getSetting('progressBarBrailleInterval') + interval = cthulhu.cthulhuApp.settingsManager.getSetting('progressBarBrailleInterval') if interval is None: return super()._getProgressBarUpdateInterval() return int(interval) def _shouldPresentProgressBarUpdate(self, obj, **args): - if not _settingsManager.getSetting('brailleProgressBarUpdates'): + if not cthulhu.cthulhuApp.settingsManager.getSetting('brailleProgressBarUpdates'): return False return super()._shouldPresentProgressBarUpdate(obj, **args) @@ -470,7 +470,7 @@ class BrailleGenerator(generator.Generator): # are on the very first line. Otherwise, we show only the # line. # - include = _settingsManager.getSetting('enableBrailleContext') + include = cthulhu.cthulhuApp.settingsManager.getSetting('enableBrailleContext') if not include: return include if AXObject.supports_text(obj) \ @@ -493,7 +493,7 @@ class BrailleGenerator(generator.Generator): def _generateEol(self, obj, **args): result = [] - if not _settingsManager.getSetting('disableBrailleEOL'): + if not cthulhu.cthulhuApp.settingsManager.getSetting('disableBrailleEOL'): if not args.get('mode', None): args['mode'] = self._mode args['stringType'] = 'eol' diff --git a/src/cthulhu/chat.py b/src/cthulhu/chat.py index ae10ed6..cc587f8 100644 --- a/src/cthulhu/chat.py +++ b/src/cthulhu/chat.py @@ -31,6 +31,7 @@ __date__ = "$Date$" __copyright__ = "Copyright (c) 2010-2011 The Cthulhu Team" __license__ = "LGPL" +from . import cthulhu # Need access to cthulhuApp from . import cmdnames from . import debug from . import guilabels @@ -43,7 +44,7 @@ from . import settings_manager from .ax_object import AXObject from .ax_utilities import AXUtilities -_settingsManager = settings_manager.getManager() +_settingsManager = None # Removed - use cthulhu.cthulhuApp.settingsManager ############################################################################# # # @@ -422,19 +423,19 @@ class Chat: grid.set_border_width(12) label = guilabels.CHAT_SPEAK_ROOM_NAME - value = _settingsManager.getSetting('chatSpeakRoomName') + value = cthulhu.cthulhuApp.settingsManager.getSetting('chatSpeakRoomName') self.speakNameCheckButton = Gtk.CheckButton.new_with_mnemonic(label) self.speakNameCheckButton.set_active(value) grid.attach(self.speakNameCheckButton, 0, 0, 1, 1) label = guilabels.CHAT_ANNOUNCE_BUDDY_TYPING - value = _settingsManager.getSetting('chatAnnounceBuddyTyping') + value = cthulhu.cthulhuApp.settingsManager.getSetting('chatAnnounceBuddyTyping') self.buddyTypingCheckButton = Gtk.CheckButton.new_with_mnemonic(label) self.buddyTypingCheckButton.set_active(value) grid.attach(self.buddyTypingCheckButton, 0, 1, 1, 1) label = guilabels.CHAT_SEPARATE_MESSAGE_HISTORIES - value = _settingsManager.getSetting('chatRoomHistories') + value = cthulhu.cthulhuApp.settingsManager.getSetting('chatRoomHistories') self.chatRoomHistoriesCheckButton = \ Gtk.CheckButton.new_with_mnemonic(label) self.chatRoomHistoriesCheckButton.set_active(value) @@ -452,7 +453,7 @@ class Chat: messagesGrid = Gtk.Grid() messagesAlignment.add(messagesGrid) - value = _settingsManager.getSetting('chatMessageVerbosity') + value = cthulhu.cthulhuApp.settingsManager.getSetting('chatMessageVerbosity') label = guilabels.CHAT_SPEAK_MESSAGES_ALL rb1 = Gtk.RadioButton.new_with_mnemonic(None, label) @@ -512,8 +513,8 @@ class Chat: """ line = messages.CHAT_ROOM_NAME_PREFIX_ON - speakRoomName = _settingsManager.getSetting('chatSpeakRoomName') - _settingsManager.setSetting('chatSpeakRoomName', not speakRoomName) + speakRoomName = cthulhu.cthulhuApp.settingsManager.getSetting('chatSpeakRoomName') + cthulhu.cthulhuApp.settingsManager.setSetting('chatSpeakRoomName', not speakRoomName) if speakRoomName: line = messages.CHAT_ROOM_NAME_PREFIX_OFF self._script.presentMessage(line) @@ -529,8 +530,8 @@ class Chat: """ line = messages.CHAT_BUDDY_TYPING_ON - announceTyping = _settingsManager.getSetting('chatAnnounceBuddyTyping') - _settingsManager.setSetting( + announceTyping = cthulhu.cthulhuApp.settingsManager.getSetting('chatAnnounceBuddyTyping') + cthulhu.cthulhuApp.settingsManager.setSetting( 'chatAnnounceBuddyTyping', not announceTyping) if announceTyping: line = messages.CHAT_BUDDY_TYPING_OFF @@ -547,8 +548,8 @@ class Chat: """ line = messages.CHAT_SEPARATE_HISTORIES_ON - roomHistories = _settingsManager.getSetting('chatRoomHistories') - _settingsManager.setSetting('chatRoomHistories', not roomHistories) + roomHistories = cthulhu.cthulhuApp.settingsManager.getSetting('chatRoomHistories') + cthulhu.cthulhuApp.settingsManager.setSetting('chatRoomHistories', not roomHistories) if roomHistories: line = messages.CHAT_SEPARATE_HISTORIES_OFF self._script.presentMessage(line) @@ -596,7 +597,7 @@ class Chat: messageNumber = self.messageListLength - (index + 1) message, chatRoomName = None, None - if _settingsManager.getSetting('chatRoomHistories'): + if cthulhu.cthulhuApp.settingsManager.getSetting('chatRoomHistories'): conversation = self.getConversation(cthulhu_state.locusOfFocus) if conversation: message = conversation.getNthMessage(messageNumber) @@ -621,7 +622,7 @@ class Chat: # Only speak/braille the new message if it matches how the user # wants chat messages spoken. # - verbosity = _settingsManager.getAppSetting(self._script.app, 'chatMessageVerbosity') + verbosity = cthulhu.cthulhuApp.settingsManager.getAppSetting(self._script.app, 'chatMessageVerbosity') if cthulhu_state.activeScript.name != self._script.name \ and verbosity == settings.CHAT_SPEAK_ALL_IF_FOCUSED: return @@ -630,7 +631,7 @@ class Chat: text = "" if chatRoomName and \ - _settingsManager.getAppSetting(self._script.app, 'chatSpeakRoomName'): + cthulhu.cthulhuApp.settingsManager.getAppSetting(self._script.app, 'chatSpeakRoomName'): text = messages.CHAT_MESSAGE_FROM_ROOM % chatRoomName if not settings.presentChatRoomLast: @@ -738,7 +739,7 @@ class Chat: Returns True if we spoke the change; False otherwise """ - if _settingsManager.getSetting('chatAnnounceBuddyTyping'): + if cthulhu.cthulhuApp.settingsManager.getSetting('chatAnnounceBuddyTyping'): conversation = self.getConversation(event.source) if conversation and (status != conversation.getTypingStatus()): voice = self._script.speechGenerator.voice(string=status) diff --git a/src/cthulhu/cthulhu.py b/src/cthulhu/cthulhu.py index a95fadf..d8f979e 100644 --- a/src/cthulhu/cthulhu.py +++ b/src/cthulhu/cthulhu.py @@ -49,7 +49,7 @@ class APIHelper: self.app = app self._gestureBindings = {} - def registerGestureByString(self, function, name, gestureString, inputEventType='default', normalizer='cthulhu', learnModeEnabled=True, contextName=None): + def registerGestureByString(self, function, name, gestureString, inputEventType='default', normalizer='cthulhu', learnModeEnabled=True, contextName=None, globalBinding=False): """Register a gesture by string.""" import logging logger = logging.getLogger(__name__) @@ -134,9 +134,17 @@ class APIHelper: self._gestureBindings[contextName] = [] self._gestureBindings[contextName].append(binding) logger.info(f"Stored binding in context '{contextName}'") - + + if contextName and self.app: + try: + plugin_manager = self.app.getPluginSystemManager() + except Exception: + plugin_manager = None + if plugin_manager: + plugin_manager.add_keybinding(contextName, binding, global_binding=globalBinding) + # Only add to active script if one exists - if cthulhu_state.activeScript: + if cthulhu_state.activeScript and not globalBinding: logger.info(f"Adding binding to active script: {cthulhu_state.activeScript}") bindings = cthulhu_state.activeScript.getKeyBindings() bindings.add(binding) @@ -231,6 +239,7 @@ from . import acss from . import text_attribute_names from . import speechserver from . import input_event +from . import input_event_manager from . import pronunciation_dict from . import cthulhu_gtkbuilder from . import signal_manager @@ -239,23 +248,7 @@ from . import translation_manager from . import resource_manager -# Lazy initialization to avoid circular imports -_eventManager = None -_scriptManager = None -_settingsManager = None -_logger = None - -def _ensureManagers(): - """Ensure managers are initialized (lazy initialization).""" - global _eventManager, _scriptManager, _settingsManager, _logger - if _eventManager is None: - _eventManager = event_manager.getManager() - if _scriptManager is None: - _scriptManager = script_manager.get_manager() - if _settingsManager is None: - _settingsManager = settings_manager.getManager() - if _logger is None: - _logger = logger.getLogger() +# Old global variables removed - now using cthulhuApp.* instead def onEnabledChanged(gsetting, key): try: @@ -266,13 +259,6 @@ def onEnabledChanged(gsetting, key): if key == 'screen-reader-enabled' and not enabled: shutdown() -def getSettingsManager(): - _ensureManagers() - return _settingsManager - -def getLogger(): - return _logger - EXIT_CODE_HANG = 50 # The user-settings module (see loadUserSettings). @@ -350,7 +336,7 @@ def _processBrailleEvent(event): cthulhu_state.lastInputEvent = event try: - consumed = _eventManager.processBrailleEvent(event) + consumed = cthulhuApp.eventManager.processBrailleEvent(event) except Exception: debug.printException(debug.LEVEL_SEVERE) @@ -375,11 +361,6 @@ def deviceChangeHandler(deviceManager, device): debug.printMessage(debug.LEVEL_INFO, msg, True) cthulhu_modifier_manager.getManager().refreshCthulhuModifiers("Keyboard change detected.") -def setKeyHandling(new): - """Toggle use of the new vs. legacy key handling mode. - """ - _eventManager.setKeyHandling(new) - def loadUserSettings(script=None, inputEvent=None, skipReloadMessage=False): """Loads (and reloads) the user settings module, reinitializing things such as speech if necessary. @@ -398,35 +379,35 @@ def loadUserSettings(script=None, inputEvent=None, skipReloadMessage=False): speech.shutdown() braille.shutdown() - _scriptManager.deactivate() + cthulhuApp.scriptManager.deactivate() cthulhuApp.getSignalManager().emitSignal('load-setting-begin') reloaded = False if _userSettings: - _profile = _settingsManager.getSetting('activeProfile')[1] + _profile = cthulhuApp.settingsManager.getSetting('activeProfile')[1] try: - _userSettings = _settingsManager.getGeneralSettings(_profile) - _settingsManager.setProfile(_profile) + _userSettings = cthulhuApp.settingsManager.getGeneralSettings(_profile) + cthulhuApp.settingsManager.setProfile(_profile) reloaded = True except ImportError: debug.printException(debug.LEVEL_INFO) except Exception: debug.printException(debug.LEVEL_SEVERE) else: - _profile = _settingsManager.profile + _profile = cthulhuApp.settingsManager.profile try: - _userSettings = _settingsManager.getGeneralSettings(_profile) + _userSettings = cthulhuApp.settingsManager.getGeneralSettings(_profile) except ImportError: debug.printException(debug.LEVEL_INFO) except Exception: debug.printException(debug.LEVEL_SEVERE) if not script: - script = _scriptManager.get_default_script() + script = cthulhuApp.scriptManager.get_default_script() - _settingsManager.loadAppSettings(script) + cthulhuApp.settingsManager.loadAppSettings(script) - if _settingsManager.getSetting('enableSpeech'): + if cthulhuApp.settingsManager.getSetting('enableSpeech'): msg = 'CTHULHU: About to enable speech' debug.printMessage(debug.LEVEL_INFO, msg, True) try: @@ -439,7 +420,7 @@ def loadUserSettings(script=None, inputEvent=None, skipReloadMessage=False): msg = 'CTHULHU: Speech is not enabled in settings' debug.printMessage(debug.LEVEL_INFO, msg, True) - if _settingsManager.getSetting('enableBraille'): + if cthulhuApp.settingsManager.getSetting('enableBraille'): msg = 'CTHULHU: About to enable braille' debug.printMessage(debug.LEVEL_INFO, msg, True) try: @@ -453,24 +434,24 @@ def loadUserSettings(script=None, inputEvent=None, skipReloadMessage=False): debug.printMessage(debug.LEVEL_INFO, msg, True) - if _settingsManager.getSetting('enableMouseReview'): + if cthulhuApp.settingsManager.getSetting('enableMouseReview'): mouse_review.getReviewer().activate() else: mouse_review.getReviewer().deactivate() - if _settingsManager.getSetting('enableSound'): + if cthulhuApp.settingsManager.getSetting('enableSound'): player.init() cthulhu_modifier_manager.getManager().refreshCthulhuModifiers("Loading user settings.") # Activate core systems FIRST before loading plugins - _scriptManager.activate() - _eventManager.activate() + cthulhuApp.scriptManager.activate() + cthulhuApp.eventManager.activate() cthulhuApp.getSignalManager().emitSignal('load-setting-begin') # NOW load plugins after script system is ready - activePluginsSetting = _settingsManager.getSetting('activePlugins') or [] + activePluginsSetting = cthulhuApp.settingsManager.getSetting('activePlugins') or [] debug.printMessage( debug.LEVEL_INFO, f"CTHULHU: Active plugins defaults: {settings.activePlugins}", @@ -524,7 +505,7 @@ def showAppPreferencesGUI(script=None, inputEvent=None): prefs = {} for key in settings.userCustomizableSettings: - prefs[key] = _settingsManager.getSetting(key) + prefs[key] = cthulhuApp.settingsManager.getSetting(key) script = script or cthulhu_state.activeScript _showPreferencesUI(script, prefs) @@ -538,9 +519,8 @@ def showPreferencesGUI(script=None, inputEvent=None): Returns True to indicate the input event has been consumed. """ - _ensureManagers() # Initialize managers if not already done - prefs = _settingsManager.getGeneralSettings(_settingsManager.profile) - script = _scriptManager.get_default_script() + prefs = cthulhuApp.settingsManager.getGeneralSettings(cthulhuApp.settingsManager.profile) + script = cthulhuApp.scriptManager.get_default_script() _showPreferencesUI(script, prefs) return True @@ -548,27 +528,18 @@ def showPreferencesGUI(script=None, inputEvent=None): def addKeyGrab(binding): """ Add a key grab for the given key binding.""" - if cthulhu_state.device is None: - return [] - - ret = [] - for kd in binding.keyDefs(): - ret.append(cthulhu_state.device.add_key_grab(kd, None)) - return ret + manager = input_event_manager.get_manager() + return manager.add_grabs_for_keybinding(binding) def removeKeyGrab(id): """ Remove the key grab for the given key binding.""" - if cthulhu_state.device is None: - return - - cthulhu_state.device.remove_key_grab(id) + manager = input_event_manager.get_manager() + manager.remove_grab_by_id(id) def mapModifier(keycode): - if cthulhu_state.device is None: - return - - return cthulhu_state.device.map_modifier(keycode) + manager = input_event_manager.get_manager() + return manager.map_keycode_to_modifier(keycode) def quitCthulhu(script=None, inputEvent=None): """Quit Cthulhu. Check if the user wants to confirm this action. @@ -610,7 +581,7 @@ def init(): global _initialized - if _initialized and _settingsManager.isScreenReaderServiceEnabled(): + if _initialized and cthulhuApp.settingsManager.isScreenReaderServiceEnabled(): debug.printMessage(debug.LEVEL_INFO, 'CTHULHU: Already initialized', True) return False @@ -622,7 +593,7 @@ def init(): # Activate settings manager before loading user settings debug.printMessage(debug.LEVEL_INFO, 'CTHULHU: Activating settings manager', True) - _settingsManager.activate() + cthulhuApp.settingsManager.activate() loadUserSettings() @@ -757,8 +728,8 @@ def shutdown(script=None, inputEvent=None): # Deactivate the event manager first so that it clears its queue and will not # accept new events. Then let the script manager unregister script event listeners. - _eventManager.deactivate() - _scriptManager.deactivate() + cthulhuApp.eventManager.deactivate() + cthulhuApp.scriptManager.deactivate() # Shutdown all the other support. # @@ -867,8 +838,8 @@ def main(): signal.signal(signal.SIGSEGV, crashOnSignal) debug.printMessage(debug.LEVEL_INFO, "CTHULHU: Enabling accessibility (if needed).", True) - if not _settingsManager.isAccessibilityEnabled(): - _settingsManager.setAccessibility(True) + if not cthulhuApp.settingsManager.isAccessibilityEnabled(): + cthulhuApp.settingsManager.setAccessibility(True) debug.printMessage(debug.LEVEL_INFO, "CTHULHU: Initializing.", True) init() @@ -887,17 +858,17 @@ def main(): # setActiveWindow does some corrective work needed thanks to # mutter-x11-frames. So retrieve the window just in case. window = cthulhu_state.activeWindow - script = _scriptManager.get_script(app, window) - _scriptManager.set_active_script(script, "Launching.") + script = cthulhuApp.scriptManager.get_script(app, window) + cthulhuApp.scriptManager.set_active_script(script, "Launching.") focusedObject = AXUtilities.get_focused_object(window) tokens = ["CTHULHU: Focused object is:", focusedObject] debug.printTokens(debug.LEVEL_INFO, tokens, True) if focusedObject: setLocusOfFocus(None, focusedObject) - script = _scriptManager.get_script( + script = cthulhuApp.scriptManager.get_script( AXObject.get_application(focusedObject), focusedObject) - _scriptManager.set_active_script(script, "Found focused object.") + cthulhuApp.scriptManager.set_active_script(script, "Found focused object.") try: msg = "CTHULHU: Starting ATSPI registry." @@ -919,14 +890,16 @@ class Cthulhu(GObject.Object): "setup-inputeventhandlers-completed": (GObject.SignalFlags.RUN_LAST, None, ()), # compat signal for register input event handlers "request-cthulhu-preferences": (GObject.SignalFlags.RUN_LAST, None, ()), "request-application-preferences": (GObject.SignalFlags.RUN_LAST, None, ()), + "active-script-changed": (GObject.SignalFlags.RUN_LAST, None, (GObject.TYPE_PYOBJECT,)), # New signal to indicate active script change } def __init__(self): GObject.Object.__init__(self) # add members self.resourceManager = resource_manager.ResourceManager(self) - self.eventManager = _eventManager - self.settingsManager = _settingsManager - self.scriptManager = _scriptManager + self.settingsManager = settings_manager.SettingsManager(self) # Directly instantiate + self.eventManager = event_manager.EventManager(self) # Directly instantiate + self.scriptManager = script_manager.ScriptManager(self) # Directly instantiate + self.logger = logger.Logger() # Directly instantiate self.signalManager = signal_manager.SignalManager(self) self.dynamicApiManager = dynamic_api_manager.DynamicApiManager(self) self.translationManager = translation_manager.TranslationManager(self) @@ -948,6 +921,8 @@ class Cthulhu(GObject.Object): return self.eventManager def getSettingsManager(self): return self.settingsManager + def getScriptManager(self): + return self.scriptManager def get_scriptManager(self): return self.scriptManager def getDebugManager(self): @@ -956,6 +931,12 @@ class Cthulhu(GObject.Object): return self.translationManager def getResourceManager(self): return self.resourceManager + def getLogger(self): # New getter for the logger + return self.logger + def addKeyGrab(self, binding): + return addKeyGrab(binding) + def removeKeyGrab(self, grab_id): + return removeKeyGrab(grab_id) def run(self, cacheValues=True): return main(cacheValues) def stop(self): @@ -965,10 +946,10 @@ class Cthulhu(GObject.Object): # should be removed step by step # use clean objects, getters and setters instead - self.getDynamicApiManager().registerAPI('Logger', _logger) - self.getDynamicApiManager().registerAPI('SettingsManager', settings_manager) - self.getDynamicApiManager().registerAPI('ScriptManager', script_manager) - self.getDynamicApiManager().registerAPI('EventManager', event_manager) + self.getDynamicApiManager().registerAPI('Logger', self.logger) # Use instance + self.getDynamicApiManager().registerAPI('SettingsManager', self.settingsManager) # Use instance + self.getDynamicApiManager().registerAPI('ScriptManager', self.scriptManager) # Use instance + self.getDynamicApiManager().registerAPI('EventManager', self.eventManager) # Use instance self.getDynamicApiManager().registerAPI('Speech', speech) self.getDynamicApiManager().registerAPI('Sound', sound) self.getDynamicApiManager().registerAPI('Braille', braille) diff --git a/src/cthulhu/cthulhu_bin.py.in b/src/cthulhu/cthulhu_bin.py.in index 98e01ad..0ffc6f7 100644 --- a/src/cthulhu/cthulhu_bin.py.in +++ b/src/cthulhu/cthulhu_bin.py.in @@ -326,7 +326,7 @@ def main(): debug.printMessage(debug.LEVEL_INFO, "INFO: Preparing to launch.", True) from cthulhu import cthulhu - manager = cthulhu.getSettingsManager() + manager = cthulhu.cthulhuApp.settingsManager if not manager: print(messages.CLI_SETTINGS_MANAGER_ERROR) diff --git a/src/cthulhu/cthulhu_gui_prefs.py b/src/cthulhu/cthulhu_gui_prefs.py index 4e2714f..1273a49 100644 --- a/src/cthulhu/cthulhu_gui_prefs.py +++ b/src/cthulhu/cthulhu_gui_prefs.py @@ -69,7 +69,7 @@ from . import sound_theme_manager from . import script_manager from .ax_object import AXObject -_settingsManager = settings_manager.getManager() +_settingsManager = None # Removed - use cthulhu.cthulhuApp.settingsManager try: import louis @@ -202,7 +202,7 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper): # in case the user played with the sliders. # try: - voices = _settingsManager.getSetting('voices') + voices = cthulhu.cthulhuApp.settingsManager.getSetting('voices') defaultVoice = voices[settings.DEFAULT_VOICE] except KeyError: defaultVoice = {} @@ -387,7 +387,7 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper): # TODO - JD: Will this ever be the case?? self._isInitialSetup = \ - not os.path.exists(_settingsManager.getPrefsDir()) + not os.path.exists(cthulhu.cthulhuApp.settingsManager.getPrefsDir()) try: self._initPluginsPage() @@ -1013,7 +1013,7 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper): keyBindingsDict = self.getKeyBindingsModelDict(self.keyBindingsModel) self.prefsDict.update(self.script.getPreferencesFromGUI()) self.prefsDict.update(self._get_plugin_preferences_from_gui()) - _settingsManager.saveSettings(self.script, + cthulhu.cthulhuApp.settingsManager.saveSettings(self.script, self.prefsDict, pronunciationDict, keyBindingsDict) @@ -1628,7 +1628,7 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper): # # Where * = speechSystems, speechServers, speechLanguages, speechFamilies # - factories = _settingsManager.getSpeechServerFactories() + factories = cthulhu.cthulhuApp.settingsManager.getSpeechServerFactories() if len(factories) == 0 or not self.prefsDict.get('enableSpeech', True): self.workingFactories = [] self.speechSystemsChoice = None @@ -1677,7 +1677,7 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper): [attrList, attrDict] = \ self.script.utilities.stringToKeysAndDict(setAttributes) [allAttrList, allAttrDict] = self.script.utilities.stringToKeysAndDict( - _settingsManager.getSetting('allTextAttributes')) + cthulhu.cthulhuApp.settingsManager.getSetting('allTextAttributes')) for i in range(0, len(attrList)): for path in range(0, len(allAttrList)): @@ -1714,7 +1714,7 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper): [attrList, attrDict] = \ self.script.utilities.stringToKeysAndDict(setAttributes) [allAttrList, allAttrDict] = self.script.utilities.stringToKeysAndDict( - _settingsManager.getSetting('allTextAttributes')) + cthulhu.cthulhuApp.settingsManager.getSetting('allTextAttributes')) for i in range(0, len(attrList)): for path in range(0, len(allAttrList)): @@ -1876,7 +1876,7 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper): # the known text attributes. # [allAttrList, allAttrDict] = self.script.utilities.stringToKeysAndDict( - _settingsManager.getSetting('allTextAttributes')) + cthulhu.cthulhuApp.settingsManager.getSetting('allTextAttributes')) for i in range(0, len(allAttrList)): thisIter = model.append() localizedKey = text_attribute_names.getTextAttributeName( @@ -1937,14 +1937,14 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper): # self._setSpokenTextAttributes( self.getTextAttributesView, - _settingsManager.getSetting('enabledSpokenTextAttributes'), + cthulhu.cthulhuApp.settingsManager.getSetting('enabledSpokenTextAttributes'), True, True) # Check all the enabled (brailled) text attributes. # self._setBrailledTextAttributes( self.getTextAttributesView, - _settingsManager.getSetting('enabledBrailledTextAttributes'), + cthulhu.cthulhuApp.settingsManager.getSetting('enabledBrailledTextAttributes'), True) # Connect a handler for when the user changes columns within the @@ -1984,7 +1984,7 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper): def pronunciationFocusChange(self, widget, event, isFocused): """Callback for the pronunciation tree's focus-{in,out}-event signal.""" - _settingsManager.setSetting('usePronunciationDictionary', not isFocused) + cthulhu.cthulhuApp.settingsManager.setSetting('usePronunciationDictionary', not isFocused) def pronunciationCursorChanged(self, widget): """Set the search column in the pronunciation dictionary tree view @@ -2023,7 +2023,7 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper): # if not self.script.app: _profile = self.prefsDict.get('activeProfile')[1] - pronDict = _settingsManager.getPronunciations(_profile) + pronDict = cthulhu.cthulhuApp.settingsManager.getPronunciations(_profile) else: pronDict = pronunciation_dict.pronunciation_dict for pronKey in sorted(pronDict.keys()): @@ -2492,7 +2492,7 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper): def __getAvailableProfiles(self): """Get available user profiles.""" - return _settingsManager.availableProfiles() + return cthulhu.cthulhuApp.settingsManager.availableProfiles() def _initAIState(self): """Initialize AI Assistant tab widgets with current settings.""" @@ -2841,7 +2841,7 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper): # self._setSpokenTextAttributes( self.getTextAttributesView, - _settingsManager.getSetting('enabledSpokenTextAttributes'), + cthulhu.cthulhuApp.settingsManager.getSetting('enabledSpokenTextAttributes'), True, True) if self.script.app: @@ -3041,7 +3041,7 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper): try: self.script.setupInputEventHandlers() keyBinds = keybindings.KeyBindings() - keyBinds = _settingsManager.overrideKeyBindings(self.script, keyBinds) + keyBinds = cthulhu.cthulhuApp.settingsManager.overrideKeyBindings(self.script, keyBinds) keyBind = keybindings.KeyBinding(None, None, None, None) treeModel = self.keyBindingsModel @@ -3103,7 +3103,7 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper): mrKeyBindings = self.script.getMouseReviewer().get_bindings() acKeyBindings = self.script.getActionPresenter().get_bindings() - layout = _settingsManager.getSetting('keyboardLayout') + layout = cthulhu.cthulhuApp.settingsManager.getSetting('keyboardLayout') isDesktop = layout == settings.GENERAL_KEYBOARD_LAYOUT_DESKTOP frKeyBindings = self.script.getFlatReviewPresenter().get_bindings(isDesktop) waiKeyBindings = self.script.getWhereAmIPresenter().get_bindings(isDesktop) @@ -3350,9 +3350,9 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper): rate = widget.get_value() voiceType = self.get_widget("voiceTypesCombo").get_active() self._setRateForVoiceType(voiceType, rate) - voices = _settingsManager.getSetting('voices') + voices = cthulhu.cthulhuApp.settingsManager.getSetting('voices') voices.get(settings.DEFAULT_VOICE, {})[acss.ACSS.RATE] = rate - _settingsManager.setSetting('voices', voices) + cthulhu.cthulhuApp.settingsManager.setSetting('voices', voices) def pitchValueChanged(self, widget): """Signal handler for the "value_changed" signal for the pitchScale @@ -3367,9 +3367,9 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper): pitch = widget.get_value() voiceType = self.get_widget("voiceTypesCombo").get_active() self._setPitchForVoiceType(voiceType, pitch) - voices = _settingsManager.getSetting('voices') + voices = cthulhu.cthulhuApp.settingsManager.getSetting('voices') voices.get(settings.DEFAULT_VOICE, {})[acss.ACSS.AVERAGE_PITCH] = pitch - _settingsManager.setSetting('voices', voices) + cthulhu.cthulhuApp.settingsManager.setSetting('voices', voices) def volumeValueChanged(self, widget): """Signal handler for the "value_changed" signal for the voiceScale @@ -3384,9 +3384,9 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper): volume = widget.get_value() voiceType = self.get_widget("voiceTypesCombo").get_active() self._setVolumeForVoiceType(voiceType, volume) - voices = _settingsManager.getSetting('voices') + voices = cthulhu.cthulhuApp.settingsManager.getSetting('voices') voices.get(settings.DEFAULT_VOICE, {})[acss.ACSS.GAIN] = volume - _settingsManager.setSetting('voices', voices) + cthulhu.cthulhuApp.settingsManager.setSetting('voices', voices) def checkButtonToggled(self, widget): """Signal handler for "toggled" signal for basic GtkCheckButton @@ -4025,7 +4025,7 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper): - widget: the component that generated the signal. """ - attributes = _settingsManager.getSetting('allTextAttributes') + attributes = cthulhu.cthulhuApp.settingsManager.getSetting('allTextAttributes') self._setSpokenTextAttributes( self.getTextAttributesView, attributes, True) self._setBrailledTextAttributes( @@ -4043,7 +4043,7 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper): - widget: the component that generated the signal. """ - attributes = _settingsManager.getSetting('allTextAttributes') + attributes = cthulhu.cthulhuApp.settingsManager.getSetting('allTextAttributes') self._setSpokenTextAttributes( self.getTextAttributesView, attributes, False) self._setBrailledTextAttributes( @@ -4061,18 +4061,18 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper): - widget: the component that generated the signal. """ - attributes = _settingsManager.getSetting('allTextAttributes') + attributes = cthulhu.cthulhuApp.settingsManager.getSetting('allTextAttributes') self._setSpokenTextAttributes( self.getTextAttributesView, attributes, False) self._setBrailledTextAttributes( self.getTextAttributesView, attributes, False) - attributes = _settingsManager.getSetting('enabledSpokenTextAttributes') + attributes = cthulhu.cthulhuApp.settingsManager.getSetting('enabledSpokenTextAttributes') self._setSpokenTextAttributes( self.getTextAttributesView, attributes, True) attributes = \ - _settingsManager.getSetting('enabledBrailledTextAttributes') + cthulhu.cthulhuApp.settingsManager.getSetting('enabledBrailledTextAttributes') self._setBrailledTextAttributes( self.getTextAttributesView, attributes, True) @@ -4177,7 +4177,7 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper): # Restore the default rate/pitch/gain, # in case the user played with the sliders. # - voices = _settingsManager.getSetting('voices') + voices = cthulhu.cthulhuApp.settingsManager.getSetting('voices') defaultVoice = voices.get(settings.DEFAULT_VOICE) if defaultVoice is not None: defaultVoice[acss.ACSS.GAIN] = self.savedGain @@ -4231,7 +4231,7 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper): self.prefsDict['profile'] = activeProfile self.prefsDict['activeProfile'] = activeProfile self.prefsDict['startingProfile'] = startingProfile - _settingsManager.setStartingProfile(startingProfile) + cthulhu.cthulhuApp.settingsManager.setStartingProfile(startingProfile) self._apply_plugin_changes() self.writeUserPreferences() @@ -4300,11 +4300,11 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper): self.suspendEvents() - factory = _settingsManager.getSetting('speechServerFactory') + factory = cthulhu.cthulhuApp.settingsManager.getSetting('speechServerFactory') if factory: self._setSpeechSystemsChoice(factory) - server = _settingsManager.getSetting('speechServerInfo') + server = cthulhu.cthulhuApp.settingsManager.getSetting('speechServerInfo') if server: self._setSpeechServersChoice(server) @@ -4447,14 +4447,14 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper): if not newProfile or newProfile == oldProfile: newProfile = newStartingProfile - _settingsManager.removeProfile(oldProfile[1]) + cthulhu.cthulhuApp.settingsManager.removeProfile(oldProfile[1]) self.loadProfile(newProfile) # Make sure nothing is referencing the removed profile anymore startingProfile = self.prefsDict.get('startingProfile') if not startingProfile or startingProfile == oldProfile: self.prefsDict['startingProfile'] = newStartingProfile - _settingsManager.setStartingProfile(newStartingProfile) + cthulhu.cthulhuApp.settingsManager.setStartingProfile(newStartingProfile) self.writeUserPreferences() dialog.destroy() @@ -4491,8 +4491,8 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper): self.saveBasicSettings() self.prefsDict['activeProfile'] = profile - _settingsManager.setProfile(profile[1]) - self.prefsDict = _settingsManager.getGeneralSettings(profile[1]) + cthulhu.cthulhuApp.settingsManager.setProfile(profile[1]) + self.prefsDict = cthulhu.cthulhuApp.settingsManager.getGeneralSettings(profile[1]) cthulhu.loadUserSettings(skipReloadMessage=True) diff --git a/src/cthulhu/cthulhu_modifier_manager.py b/src/cthulhu/cthulhu_modifier_manager.py index 6ef08f2..86ea50f 100644 --- a/src/cthulhu/cthulhu_modifier_manager.py +++ b/src/cthulhu/cthulhu_modifier_manager.py @@ -218,8 +218,12 @@ class CthulhuModifierManager: debug.printMessage(debug.LEVEL_INFO, msg, True) GLib.timeout_add(1, toggle, keyboardEvent.modifiers, modifier) -_manager = CthulhuModifierManager() - +_manager = None def getManager(): - """Returns the CthulhuModifierManager singleton.""" + """Returns the Cthulhu Modifier Manager""" + + global _manager + if _manager is None: + _manager = CthulhuModifierManager() return _manager + diff --git a/src/cthulhu/date_and_time_presenter.py b/src/cthulhu/date_and_time_presenter.py index a4a2bf9..fd19c91 100644 --- a/src/cthulhu/date_and_time_presenter.py +++ b/src/cthulhu/date_and_time_presenter.py @@ -34,12 +34,13 @@ __license__ = "LGPL" import time +from . import cthulhu # Need access to cthulhuApp from . import cmdnames from . import input_event from . import keybindings from . import settings_manager -_settingsManager = settings_manager.getManager() +_settingsManager = None # Removed - use cthulhu.cthulhuApp.settingsManager class DateAndTimePresenter: """Provides commands to present the date and time.""" @@ -101,14 +102,14 @@ class DateAndTimePresenter: def present_time(self, script, event=None): """Presents the current time.""" - format = _settingsManager.getSetting('presentTimeFormat') + format = cthulhu.cthulhuApp.settingsManager.getSetting('presentTimeFormat') script.presentMessage(time.strftime(format, time.localtime())) return True def present_date(self, script, event=None): """Presents the current date.""" - format = _settingsManager.getSetting('presentDateFormat') + format = cthulhu.cthulhuApp.settingsManager.getSetting('presentDateFormat') script.presentMessage(time.strftime(format, time.localtime())) return True diff --git a/src/cthulhu/debug.py b/src/cthulhu/debug.py index e58d789..48f241e 100644 --- a/src/cthulhu/debug.py +++ b/src/cthulhu/debug.py @@ -318,21 +318,27 @@ def println(level, text="", timestamp=False, stack=False): if debugFile: try: debugFile.writelines([text, "\n"]) + debugFile.flush() except TypeError: text = "TypeError when trying to write text" debugFile.writelines([text, "\n"]) + debugFile.flush() except Exception: text = "Exception when trying to write text" debugFile.writelines([text, "\n"]) + debugFile.flush() else: try: sys.stderr.writelines([text, "\n"]) + sys.stderr.flush() except TypeError: text = "TypeError when trying to write text" sys.stderr.writelines([text, "\n"]) + sys.stderr.flush() except Exception: text = "Exception when trying to write text" sys.stderr.writelines([text, "\n"]) + sys.stderr.flush() def printResult(level, result=None): """Prints the return result, along with information about the diff --git a/src/cthulhu/event_manager.py b/src/cthulhu/event_manager.py index f9a6624..7258c0c 100644 --- a/src/cthulhu/event_manager.py +++ b/src/cthulhu/event_manager.py @@ -37,6 +37,7 @@ import queue import threading import time +from . import cthulhu from . import debug from . import input_event from . import input_event_manager @@ -46,15 +47,14 @@ from . import settings from .ax_object import AXObject from .ax_utilities import AXUtilities -_scriptManager = script_manager.get_manager() - class EventManager: EMBEDDED_OBJECT_CHARACTER = '\ufffc' - def __init__(self, asyncMode=True): + def __init__(self, app, asyncMode=True): debug.printMessage(debug.LEVEL_INFO, 'EVENT MANAGER: Initializing', True) debug.printMessage(debug.LEVEL_INFO, f'EVENT MANAGER: Async Mode is {asyncMode}', True) + self.app = app self._asyncMode = asyncMode self._scriptListenerCounts = {} self._active = False @@ -98,8 +98,35 @@ class EventManager: debug.printMessage(debug.LEVEL_INFO, 'EVENT MANAGER: Activating', True) self._activateKeyHandling() self._active = True + GLib.idle_add(self._sync_focus_on_startup) debug.printMessage(debug.LEVEL_INFO, 'EVENT MANAGER: Activated', True) + def _sync_focus_on_startup(self): + """Initialize active window and focus when startup missed focus events.""" + + focus = cthulhu_state.locusOfFocus + if focus and not AXObject.is_dead(focus): + return False + + window = cthulhu_state.activeWindow + if not AXUtilities.can_be_active_window(window): + window = AXUtilities.find_active_window() + if window is not None: + tokens = ["EVENT MANAGER: Setting initial active window to", window] + debug.printTokens(debug.LEVEL_INFO, tokens, True) + cthulhu.setActiveWindow(window, alsoSetLocusOfFocus=True, notifyScript=False) + + if window is None: + return False + + focused = AXUtilities.get_focused_object(window) + if focused is not None and focused != cthulhu_state.locusOfFocus: + cthulhu.setLocusOfFocus(None, focused, notifyScript=True, force=True) + elif cthulhu_state.locusOfFocus is None: + cthulhu.setLocusOfFocus(None, window, notifyScript=True, force=True) + + return False + def _activateKeyHandling(self): """Activates keyboard handling using InputEventManager with Atspi.Device.""" @@ -126,17 +153,6 @@ class EventManager: self._keyHandlingActive = False debug.printMessage(debug.LEVEL_INFO, 'EVENT MANAGER: Keyboard handling deactivated', True) - def setKeyHandling(self, enable): - """Enables or disables keyboard handling. - - Arguments: - - enable: if True, activate keyboard handling; if False, deactivate. - """ - if enable: - self._activateKeyHandling() - else: - self._deactivateKeyHandling() - def deactivate(self): """Called when this event manager is deactivated.""" @@ -636,7 +652,7 @@ class EventManager: # To decrease the likelihood that the popup will be destroyed before we # have its contents. asyncMode = False - script = _scriptManager.get_script(AXObject.get_application(e.source), e.source) + script = cthulhu.cthulhuApp.scriptManager.get_script(AXObject.get_application(e.source), e.source) script.eventCache[e.type] = (e, time.time()) self._addToQueue(e, asyncMode) @@ -658,8 +674,8 @@ class EventManager: if not self._isNoFocus(): return False - defaultScript = _scriptManager.get_default_script() - _scriptManager.set_active_script(defaultScript, 'No focus') + defaultScript = cthulhu.cthulhuApp.scriptManager.get_default_script() + cthulhu.cthulhuApp.scriptManager.set_active_script(defaultScript, 'No focus') defaultScript.idleMessage() return False @@ -827,7 +843,7 @@ class EventManager: """Returns the script associated with event.""" if event.type.startswith("mouse:"): - return _scriptManager.get_script_for_mouse_button_event(event) + return cthulhu.cthulhuApp.scriptManager.get_script_for_mouse_button_event(event) script = None app = AXObject.get_application(event.source) @@ -856,7 +872,7 @@ class EventManager: tokens = ["EVENT MANAGER: Getting script for", app, "check:", check] debug.printTokens(debug.LEVEL_INFO, tokens, True) - script = _scriptManager.get_script(app, event.source, sanity_check=check) + script = cthulhu.cthulhuApp.scriptManager.get_script(app, event.source, sanity_check=check) tokens = ["EVENT MANAGER: Script is ", script] debug.printTokens(debug.LEVEL_INFO, tokens, True) return script @@ -1062,14 +1078,14 @@ class EventManager: if eType.startswith("object:children-changed:remove") \ and event.source == AXUtilities.get_desktop(): - _scriptManager.reclaim_scripts() + cthulhu.cthulhuApp.scriptManager.reclaim_scripts() return if eType.startswith("window:") and not eType.endswith("create"): - _scriptManager.reclaim_scripts() + cthulhu.cthulhuApp.scriptManager.reclaim_scripts() elif eType.startswith("object:state-changed:active") \ and AXUtilities.is_frame(event.source): - _scriptManager.reclaim_scripts() + cthulhu.cthulhuApp.scriptManager.reclaim_scripts() if AXObject.is_dead(event.source) or AXUtilities.is_defunct(event.source): tokens = ["EVENT MANAGER: Ignoring defunct object:", event.source] @@ -1081,7 +1097,7 @@ class EventManager: debug.printMessage(debug.LEVEL_INFO, msg, True) cthulhu_state.locusOfFocus = None cthulhu_state.activeWindow = None - _scriptManager.set_active_script(None, "Active window is dead or defunct") + cthulhu.cthulhuApp.scriptManager.set_active_script(None, "Active window is dead or defunct") return if AXUtilities.is_iconified(event.source): @@ -1125,7 +1141,7 @@ class EventManager: if setNewActiveScript: try: - _scriptManager.set_active_script(script, reason) + cthulhu.cthulhuApp.scriptManager.set_active_script(script, reason) except Exception as error: tokens = ["EVENT MANAGER: Exception setting active script for", event.source, ":", error] @@ -1165,7 +1181,10 @@ class EventManager: else: return False -_manager = EventManager() +_manager = None def getManager(): + global _manager + if _manager is None: + _manager = cthulhu.cthulhuApp.eventManager return _manager diff --git a/src/cthulhu/flat_review_presenter.py b/src/cthulhu/flat_review_presenter.py index e1b3338..ebcd238 100644 --- a/src/cthulhu/flat_review_presenter.py +++ b/src/cthulhu/flat_review_presenter.py @@ -50,7 +50,7 @@ from . import cthulhu_state from . import settings_manager from . import settings -_settingsManager = settings_manager.getManager() +_settingsManager = None # Removed - use cthulhu.cthulhuApp.settingsManager class FlatReviewPresenter: """Provides access to on-screen objects via flat-review.""" @@ -58,7 +58,7 @@ class FlatReviewPresenter: def __init__(self): self._context = None self._current_contents = "" - self._restrict = _settingsManager.getSetting("flatReviewIsRestricted") + self._restrict = cthulhu.cthulhuApp.settingsManager.getSetting("flatReviewIsRestricted") self._handlers = self._setup_handlers() self._desktop_bindings = self._setup_desktop_bindings() self._laptop_bindings = self._setup_laptop_bindings() @@ -731,7 +731,7 @@ class FlatReviewPresenter: if event is None: return - if _settingsManager.getSetting('speechVerbosityLevel') != settings.VERBOSITY_LEVEL_BRIEF: + if cthulhu.cthulhuApp.settingsManager.getSetting('speechVerbosityLevel') != settings.VERBOSITY_LEVEL_BRIEF: script.presentMessage(messages.FLAT_REVIEW_START) self._item_presentation(script, event, script.targetCursorCell) @@ -752,7 +752,7 @@ class FlatReviewPresenter: if event is None or script is None: return - if _settingsManager.getSetting('speechVerbosityLevel') != settings.VERBOSITY_LEVEL_BRIEF: + if cthulhu.cthulhuApp.settingsManager.getSetting('speechVerbosityLevel') != settings.VERBOSITY_LEVEL_BRIEF: script.presentMessage(messages.FLAT_REVIEW_STOP) script.updateBraille(cthulhu_state.locusOfFocus) @@ -1039,7 +1039,7 @@ class FlatReviewPresenter: """ Toggles the restricting of flat review to the current object. """ self._restrict = not self._restrict - _settingsManager.setSetting("flatReviewIsRestricted", self._restrict) + cthulhu.cthulhuApp.settingsManager.setSetting("flatReviewIsRestricted", self._restrict) if self._restrict: script.presentMessage(messages.FLAT_REVIEW_RESTRICTED) diff --git a/src/cthulhu/focus_manager.py b/src/cthulhu/focus_manager.py index 35a0d60..02aed9a 100644 --- a/src/cthulhu/focus_manager.py +++ b/src/cthulhu/focus_manager.py @@ -74,7 +74,8 @@ SAY_ALL = "say-all" class FocusManager: """Manages the focused object, window, etc.""" - def __init__(self) -> None: + def __init__(self, app) -> None: # Added app argument + self.app = app # Store app instance self._window: Optional[Atspi.Accessible] = cthulhu_state.activeWindow self._focus: Optional[Atspi.Accessible] = cthulhu_state.locusOfFocus self._object_of_interest: Optional[Atspi.Accessible] = cthulhu_state.objOfInterest @@ -286,11 +287,11 @@ class FocusManager: _get_ax_utilities().save_object_info_for_events(obj) # TODO - JD: Consider always updating the active script here. - script = script_manager.get_manager().get_active_script() + script = self.app.scriptManager.get_active_script() if event and (script and not script.app): app = _get_ax_utilities().get_application(event.source) - script = script_manager.get_manager().get_script(app, event.source) - script_manager.get_manager().set_active_script(script, "Setting locus of focus") + script = self.app.scriptManager.get_script(app, event.source) + self.app.scriptManager.set_active_script(script, "Setting locus of focus") old_focus = self._focus if AXObject.is_dead(old_focus): @@ -383,8 +384,8 @@ class FocusManager: self.set_locus_of_focus(None, self._window, notify_script=True) app = _get_ax_utilities().get_application(self._focus) - script = script_manager.get_manager().get_script(app, self._focus) - script_manager.get_manager().set_active_script(script, "Setting active window") + script = self.app.scriptManager.get_script(app, self._focus) + self.app.scriptManager.set_active_script(script, "Setting active window") @dbus_service.command def toggle_presentation_mode( @@ -434,7 +435,7 @@ class FocusManager: def get_in_layout_mode(self) -> bool: """Returns True if layout mode (as opposed to object mode) is active (web content only).""" - if script := script_manager.get_manager().get_active_script(): + if script := self.app.scriptManager.get_active_script(): return script.in_layout_mode() return False @@ -442,7 +443,7 @@ class FocusManager: def get_in_focus_mode(self) -> bool: """Returns True if focus mode is active (web content only).""" - if script := script_manager.get_manager().get_active_script(): + if script := self.app.scriptManager.get_active_script(): return script.in_focus_mode() return False @@ -450,7 +451,7 @@ class FocusManager: def get_focus_mode_is_sticky(self) -> bool: """Returns True if focus mode is active and 'sticky' (web content only).""" - if script := script_manager.get_manager().get_active_script(): + if script := self.app.scriptManager.get_active_script(): return script.focus_mode_is_sticky() return False @@ -458,12 +459,17 @@ class FocusManager: def get_browse_mode_is_sticky(self) -> bool: """Returns True if browse mode is active and 'sticky' (web content only).""" - if script := script_manager.get_manager().get_active_script(): + if script := self.app.scriptManager.get_active_script(): return script.browse_mode_is_sticky() return False -_manager: FocusManager = FocusManager() +_manager = None +def get_manager(): + """Returns the Focus Manager""" -def get_manager() -> FocusManager: - """Returns the focus manager singleton.""" + global _manager + if _manager is None: + from . import cthulhu + _manager = FocusManager(cthulhu.cthulhuApp) return _manager + diff --git a/src/cthulhu/generator.py b/src/cthulhu/generator.py index 9c2a2f5..b6b4f13 100644 --- a/src/cthulhu/generator.py +++ b/src/cthulhu/generator.py @@ -80,7 +80,15 @@ def _formatExceptionInfo(maxTBlevel=5): # METHOD_PREFIX = "_generate" -_settingsManager = settings_manager.getManager() +# Lazy initialization to avoid circular imports +_settingsManager = None + +def _ensureSettingsManager(): + """Ensure settings manager is initialized.""" + global _settingsManager + if _settingsManager is None: + from . import cthulhu + _settingsManager = cthulhu.cthulhu.cthulhuApp.settingsManager class Generator: """Takes accessible objects and generates a presentation for those @@ -1237,7 +1245,8 @@ class Generator: return [] def _getProgressBarUpdateInterval(self): - return int(_settingsManager.getSetting('progressBarUpdateInterval')) + _ensureSettingsManager() + return int(cthulhu.cthulhuApp.settingsManager.getSetting('progressBarUpdateInterval')) def _shouldPresentProgressBarUpdate(self, obj, **args): percent = self._script.utilities.getValueAsPercent(obj) diff --git a/src/cthulhu/input_event.py b/src/cthulhu/input_event.py index ece3cdf..f4a0c13 100644 --- a/src/cthulhu/input_event.py +++ b/src/cthulhu/input_event.py @@ -81,21 +81,8 @@ class InputEvent: self._clickCount = count -def _getXkbStickyKeysState(): - from subprocess import check_output - - try: - output = check_output(['xkbset', 'q']) - for line in output.decode('ASCII', errors='ignore').split('\n'): - if line.startswith('Sticky-Keys = '): - return line.endswith('On') - except Exception: - pass - return False - class KeyboardEvent(InputEvent): - - stickyKeys = _getXkbStickyKeysState() + stickyKeys = False duplicateCount = 0 cthulhuModifierPressed = False @@ -791,25 +778,6 @@ class KeyboardEvent(InputEvent): return self._handler - def _getUserHandler(self): - # TODO - JD: This should go away once plugin support is in place. - try: - bindings = settings.keyBindingsMap.get(self._script.__module__) - except Exception: - bindings = None - if not bindings: - try: - bindings = settings.keyBindingsMap.get("default") - except Exception: - bindings = None - - try: - handler = bindings.getInputHandler(self) - except Exception: - handler = None - - return handler - def shouldConsume(self): """Returns True if this event should be consumed.""" @@ -820,9 +788,16 @@ class KeyboardEvent(InputEvent): if not self.timestamp: return False, 'No timestamp' + globalHandlerUsed = False if not self._script: - debug.printMessage(debug.LEVEL_INFO, "shouldConsume: No active script", True) - return False, 'No active script when received' + globalHandler = self._getGlobalHandler() + if globalHandler: + self._handler = globalHandler + self._script = script_manager.get_manager().get_default_script() + globalHandlerUsed = True + else: + debug.printMessage(debug.LEVEL_INFO, "shouldConsume: No active script", True) + return False, 'No active script when received' debug.printMessage(debug.LEVEL_INFO, f"shouldConsume: Active script={self._script.__class__.__name__}", True) @@ -835,8 +810,13 @@ class KeyboardEvent(InputEvent): if cthulhu_state.bypassNextCommand: return False, 'Bypass next command' - self._handler = self._getUserHandler() \ - or self._script.keyBindings.getInputHandler(self) + if not self._handler: + self._handler = self._script.keyBindings.getInputHandler(self) + if not self._handler: + globalHandler = self._getGlobalHandler() + if globalHandler: + self._handler = globalHandler + globalHandlerUsed = True if self._handler: debug.printMessage(debug.LEVEL_INFO, f"shouldConsume: Handler found: {self._handler.description}", True) @@ -847,6 +827,8 @@ class KeyboardEvent(InputEvent): # because that method is updating state, even in instances where there # is no handler. scriptConsumes = self._script.consumesKeyboardEvent(self) + if globalHandlerUsed: + scriptConsumes = True debug.printMessage(debug.LEVEL_INFO, f"shouldConsume: scriptConsumes={scriptConsumes}", True) if self._isReleaseForLastNonModifierKeyEvent(): @@ -866,6 +848,18 @@ class KeyboardEvent(InputEvent): return scriptConsumes, 'Script indication' + def _getGlobalHandler(self): + try: + plugin_manager = cthulhu.cthulhuApp.getPluginSystemManager() + except Exception: + return None + if not plugin_manager: + return None + global_bindings = plugin_manager.get_global_keybindings() + if not global_bindings: + return None + return global_bindings.getInputHandler(self) + def didConsume(self): """Returns True if this event was consumed.""" diff --git a/src/cthulhu/input_event_manager.py b/src/cthulhu/input_event_manager.py index 5d4d9fe..3ce833f 100644 --- a/src/cthulhu/input_event_manager.py +++ b/src/cthulhu/input_event_manager.py @@ -44,12 +44,6 @@ from typing import TYPE_CHECKING, Optional import gi gi.require_version("Atspi", "2.0") from gi.repository import Atspi -try: - gi.require_version("Wnck", "3.0") - from gi.repository import Wnck - _wnck_available = True -except Exception: - _wnck_available = False from . import debug from . import focus_manager @@ -59,7 +53,6 @@ from . import settings from . import cthulhu_state from .ax_object import AXObject from .ax_utilities import AXUtilities -from .ax_utilities_application import AXUtilitiesApplication if TYPE_CHECKING: from . import keybindings @@ -76,35 +69,6 @@ class InputEventManager: self._grabbed_bindings: dict[int, keybindings.KeyBinding] = {} self._paused: bool = False - def _active_window_has_accessible_app(self) -> Optional[bool]: - """Returns True if the WM active window maps to an AT-SPI app.""" - - if not _wnck_available: - return None - - screen = Wnck.Screen.get_default() - if not screen: - return None - - try: - screen.force_update() - active_window = screen.get_active_window() - except Exception: - return None - - if not active_window: - return None - - try: - pid = active_window.get_pid() - except Exception: - return None - - if pid <= 0: - return None - - return AXUtilitiesApplication.get_application_with_pid(pid) is not None - def start_key_watcher(self) -> None: """Starts the watcher for keyboard input events.""" @@ -136,24 +100,29 @@ class InputEventManager: msg = f"INPUT EVENT MANAGER: {grab_id} for: {binding}" debug.print_message(debug.LEVEL_INFO, msg, True) + def _get_key_definitions(self, binding: keybindings.KeyBinding) -> list[Atspi.KeyDefinition]: + if hasattr(binding, "key_definitions"): + return list(binding.key_definitions()) + if hasattr(binding, "keyDefs"): + return list(binding.keyDefs()) + return [] + def add_grabs_for_keybinding(self, binding: keybindings.KeyBinding) -> list[int]: """Adds grabs for binding if it is enabled, returns grab IDs.""" - if not (binding.is_enabled() and binding.is_bound()): - return [] - - if binding.has_grabs(): - tokens = ["INPUT EVENT MANAGER:", binding, "already has grabs."] - debug.print_tokens(debug.LEVEL_INFO, tokens, True) - return [] - if self._device is None: tokens = ["INPUT EVENT MANAGER: No device to add grab for", binding] debug.print_tokens(debug.LEVEL_INFO, tokens, True) return [] grab_ids = [] - for kd in binding.key_definitions(): + key_definitions = self._get_key_definitions(binding) + if not key_definitions: + tokens = ["INPUT EVENT MANAGER: No key definitions for", binding] + debug.print_tokens(debug.LEVEL_INFO, tokens, True) + return [] + + for kd in key_definitions: grab_id = self._device.add_key_grab(kd, None) # When we have double/triple-click bindings, the single-click binding will be # registered first, and subsequent attempts to register what is externally the @@ -174,7 +143,12 @@ class InputEventManager: debug.print_tokens(debug.LEVEL_INFO, tokens, True) return - grab_ids = binding.get_grab_ids() + grab_ids = None + if hasattr(binding, "get_grab_ids"): + grab_ids = binding.get_grab_ids() + elif hasattr(binding, "_grab_ids"): + grab_ids = list(binding._grab_ids) + if not grab_ids: tokens = ["INPUT EVENT MANAGER:", binding, "doesn't have grabs to remove."] debug.print_tokens(debug.LEVEL_INFO, tokens, True) @@ -186,6 +160,25 @@ class InputEventManager: if removed is None: msg = f"INPUT EVENT MANAGER: No key binding for grab id {grab_id}" debug.print_message(debug.LEVEL_INFO, msg, True) + if hasattr(binding, "_grab_ids") and grab_id in binding._grab_ids: + binding._grab_ids.remove(grab_id) + if hasattr(binding, "_grab_ids") and not binding._grab_ids: + delattr(binding, "_grab_ids") + + def remove_grab_by_id(self, grab_id: int) -> None: + """Removes a grab by id.""" + + if self._device is None: + msg = f"INPUT EVENT MANAGER: No device to remove grab id {grab_id}" + debug.print_message(debug.LEVEL_INFO, msg, True) + return + + self._device.remove_key_grab(grab_id) + binding = self._grabbed_bindings.pop(grab_id, None) + if binding and hasattr(binding, "_grab_ids") and grab_id in binding._grab_ids: + binding._grab_ids.remove(grab_id) + if not binding._grab_ids: + delattr(binding, "_grab_ids") def map_keysym_to_modifier(self, keysym: int) -> int: """Maps keysym as a modifier, returns the newly-mapped modifier.""" @@ -198,6 +191,17 @@ class InputEventManager: self._mapped_keysyms.append(keysym) return self._device.map_keysym_modifier(keysym) + def map_keycode_to_modifier(self, keycode: int) -> int: + """Maps keycode as a modifier, returns the newly-mapped modifier.""" + + if self._device is None: + msg = f"INPUT EVENT MANAGER: No device to map keycode {keycode} to modifier" + debug.print_message(debug.LEVEL_INFO, msg, True) + return 0 + + self._mapped_keycodes.append(keycode) + return self._device.map_modifier(keycode) + def unmap_all_modifiers(self) -> None: """Unmaps all previously mapped modifiers.""" @@ -317,12 +321,6 @@ class InputEventManager: manager = focus_manager.get_manager() if pressed: - has_accessible_app = self._active_window_has_accessible_app() - if has_accessible_app is False and manager.get_active_window() is not None: - msg = "INPUT EVENT MANAGER: Active window has no AT-SPI app. Clearing active window." - debug.print_message(debug.LEVEL_INFO, msg, True) - manager.set_active_window(None, notify_script=True) - window = manager.get_active_window() if not AXUtilities.can_be_active_window(window): new_window = AXUtilities.find_active_window() @@ -335,6 +333,8 @@ class InputEventManager: # One example: Brave's popup menus live in frames which lack the active state. tokens = ["WARNING:", window, "cannot be active window. No alternative found."] debug.print_tokens(debug.LEVEL_WARNING, tokens, True) + window = None + manager.set_active_window(None, notify_script=True) event.set_window(window) event.set_object(manager.get_locus_of_focus()) event.set_script(script_manager.get_manager().get_active_script()) diff --git a/src/cthulhu/learn_mode_presenter.py b/src/cthulhu/learn_mode_presenter.py index 4e28902..be72b1e 100644 --- a/src/cthulhu/learn_mode_presenter.py +++ b/src/cthulhu/learn_mode_presenter.py @@ -60,7 +60,8 @@ from .ax_object import AXObject class LearnModePresenter: """Provides implementation of learn mode""" - def __init__(self): + def __init__(self, app): + self.app = app self._handlers = self._setup_handlers() self._bindings = self._setup_bindings() self._is_active = False @@ -215,7 +216,7 @@ class LearnModePresenter: if event is None: event = cthulhu_state.lastNonModifierKeyEvent - layout = settings_manager.getManager().getSetting("keyboardLayout") + layout = self.app.getSettingsManager().getSetting("keyboardLayout") is_desktop = layout == settings.GENERAL_KEYBOARD_LAYOUT_DESKTOP items = 0 @@ -393,12 +394,14 @@ class CommandListGUI: time_stamp = Gtk.get_current_event_time() self._gui.present_with_time(time_stamp) - _presenter = None def getPresenter(): """Returns the Learn Mode Presenter""" - + global _presenter if _presenter is None: - _presenter = LearnModePresenter() + from . import cthulhu + _presenter = LearnModePresenter(cthulhu.cthulhuApp) return _presenter + + diff --git a/src/cthulhu/liveregions.py b/src/cthulhu/liveregions.py index 6caa161..00cbba1 100644 --- a/src/cthulhu/liveregions.py +++ b/src/cthulhu/liveregions.py @@ -32,6 +32,7 @@ import copy import time from gi.repository import GLib +from . import cthulhu # Need access to cthulhuApp from . import cmdnames from . import chnames from . import debug @@ -45,7 +46,7 @@ from .ax_object import AXObject from .ax_text import AXText from .ax_utilities_relation import AXUtilitiesRelation -_settingsManager = settings_manager.getManager() +_settingsManager = None # Removed - use cthulhu.cthulhuApp.settingsManager # define 'live' property types LIVE_OFF = -1 @@ -310,7 +311,7 @@ class LiveRegionManager: def advancePoliteness(self, script, inputEvent): """Advance the politeness level of the given object""" - if not _settingsManager.getSetting('inferLiveRegions'): + if not cthulhu.cthulhuApp.settingsManager.getSetting('inferLiveRegions'): self._script.presentMessage(messages.LIVE_REGIONS_OFF) return @@ -353,7 +354,7 @@ class LiveRegionManager: """Speak the given number cached message""" msgnum = int(inputEvent.event_string[1:]) - if not _settingsManager.getSetting('inferLiveRegions'): + if not cthulhu.cthulhuApp.settingsManager.getSetting('inferLiveRegions'): self._script.presentMessage(messages.LIVE_REGIONS_OFF) return @@ -366,7 +367,7 @@ class LiveRegionManager: """User toggle to set all live regions to LIVE_OFF or back to their original politeness.""" - if not _settingsManager.getSetting('inferLiveRegions'): + if not cthulhu.cthulhuApp.settingsManager.getSetting('inferLiveRegions'): self._script.presentMessage(messages.LIVE_REGIONS_OFF) return @@ -599,10 +600,10 @@ class LiveRegionManager: obj = AXObject.get_parent(obj) def toggleMonitoring(self, script, inputEvent): - if not _settingsManager.getSetting('inferLiveRegions'): - _settingsManager.setSetting('inferLiveRegions', True) + if not cthulhu.cthulhuApp.settingsManager.getSetting('inferLiveRegions'): + cthulhu.cthulhuApp.settingsManager.setSetting('inferLiveRegions', True) self._script.presentMessage(messages.LIVE_REGIONS_MONITORING_ON) else: - _settingsManager.setSetting('inferLiveRegions', False) + cthulhu.cthulhuApp.settingsManager.setSetting('inferLiveRegions', False) self.flushMessages() self._script.presentMessage(messages.LIVE_REGIONS_MONITORING_OFF) diff --git a/src/cthulhu/logger.py b/src/cthulhu/logger.py index 8de4614..2b85eff 100644 --- a/src/cthulhu/logger.py +++ b/src/cthulhu/logger.py @@ -71,7 +71,3 @@ class Logger: stream = self._logs.get(name) stream.close() -_logger = Logger() - -def getLogger(): - return _logger diff --git a/src/cthulhu/mouse_review.py b/src/cthulhu/mouse_review.py index b2e2031..a6ed955 100644 --- a/src/cthulhu/mouse_review.py +++ b/src/cthulhu/mouse_review.py @@ -60,8 +60,7 @@ from .ax_object import AXObject from .ax_text import AXText from .ax_utilities import AXUtilities -_scriptManager = script_manager.get_manager() -_settingsManager = settings_manager.getManager() +# Removed global cthulhu.cthulhuApp.scriptManager and cthulhu.cthulhuApp.settingsManager class _StringContext: """The textual information associated with an _ItemContext.""" @@ -341,8 +340,9 @@ class _ItemContext: class MouseReviewer: """Main class for the mouse-review feature.""" - def __init__(self): - self._active = _settingsManager.getSetting("enableMouseReview") + def __init__(self, app): + self.app = app + self._active = self.app.getSettingsManager().getSetting("enableMouseReview") self._currentMouseOver = _ItemContext() self._pointer = None self._workspace = None @@ -432,7 +432,7 @@ class MouseReviewer: script = None frame = None if obj: - script = _scriptManager.get_script(AXObject.get_application(obj), obj) + script = self.app.getScriptManager().get_script(AXObject.get_application(obj), obj) if script: frame = script.utilities.topLevelObject(obj) self._currentMouseOver = _ItemContext(obj=obj, frame=frame, script=script) @@ -497,7 +497,7 @@ class MouseReviewer: return self._active = not self._active - _settingsManager.setSetting("enableMouseReview", self._active) + self.app.getSettingsManager().setSetting("enableMouseReview", self._active) if not self._active: self.deactivate() @@ -621,7 +621,7 @@ class MouseReviewer: if not window: return - script = _scriptManager.get_script(AXObject.get_application(window)) + script = self.app.getScriptManager().get_script(AXObject.get_application(window)) if not script: return @@ -643,7 +643,7 @@ class MouseReviewer: tokens = [f"MOUSE REVIEW: Object at ({pX}, {pY}) is", obj] debug.printTokens(debug.LEVEL_INFO, tokens, True) - script = _scriptManager.get_script(AXObject.get_application(window), obj) + script = self.app.getScriptManager().get_script(AXObject.get_application(window), obj) if menu and obj and not AXObject.find_ancestor(obj, AXUtilities.is_menu): if script.utilities.intersectingRegion(obj, menu) != (0, 0, 0, 0): tokens = ["MOUSE REVIEW:", obj, "believed to be under", menu] @@ -695,10 +695,14 @@ class MouseReviewer: msg += f"^^^^^ PROCESS OBJECT EVENT {event.type} ^^^^^\n" debug.printMessage(debug.LEVEL_INFO, msg, False) - _reviewer = None def getReviewer(): + """Returns the Mouse Reviewer""" + global _reviewer if _reviewer is None: - _reviewer = MouseReviewer() + from . import cthulhu + _reviewer = MouseReviewer(cthulhu.cthulhuApp) return _reviewer + + diff --git a/src/cthulhu/plugin.py b/src/cthulhu/plugin.py index 8063233..a9e0eca 100644 --- a/src/cthulhu/plugin.py +++ b/src/cthulhu/plugin.py @@ -11,28 +11,11 @@ import os import logging -# Import pluggy for hook specifications -try: - import pluggy - cthulhu_hookimpl = pluggy.HookimplMarker("cthulhu") - PLUGGY_AVAILABLE = True - logging.getLogger(__name__).info("Successfully imported pluggy") -except ImportError: - # Fallback if pluggy is not available - def cthulhu_hookimpl(func=None, **kwargs): - """Fallback decorator when pluggy is not available. +import pluggy + +# Import pluggy for hook specifications +cthulhu_hookimpl = pluggy.HookimplMarker("cthulhu") - This is a no-op decorator that returns the original function. - It allows the code to continue working without pluggy, though - plugins will be disabled. - """ - if func is None: - return lambda f: f - return func - PLUGGY_AVAILABLE = False - logging.getLogger(__name__).warning("Pluggy not available, plugins will be disabled") - import traceback - logging.getLogger(__name__).debug(traceback.format_exc()) logger = logging.getLogger(__name__) @@ -95,7 +78,7 @@ class Plugin: """Return a dict of plugin preferences from the GUI.""" return {} - def registerGestureByString(self, function, name, gestureString, learnModeEnabled=True): + def registerGestureByString(self, function, name, gestureString, learnModeEnabled=True, globalBinding=True): """Register a gesture by string.""" if self.app: api_helper = self.app.getAPIHelper() @@ -107,6 +90,7 @@ class Plugin: 'default', 'cthulhu', learnModeEnabled, + globalBinding=globalBinding, contextName=self.module_name ) diff --git a/src/cthulhu/plugin_system_manager.py b/src/cthulhu/plugin_system_manager.py index affa873..f9f0054 100644 --- a/src/cthulhu/plugin_system_manager.py +++ b/src/cthulhu/plugin_system_manager.py @@ -19,15 +19,9 @@ import shutil import subprocess from enum import IntEnum +import pluggy from . import dbus_service - -# Import pluggy if available -try: - import pluggy - PLUGGY_AVAILABLE = True -except ImportError: - PLUGGY_AVAILABLE = False - logging.getLogger(__name__).info("Pluggy not available, plugins will be disabled") +from . import keybindings # Added import # Set to True for more detailed plugin loading debug info PLUGIN_DEBUG = True @@ -123,34 +117,33 @@ class PluginSystemManager: _manager = self # Initialize plugin manager - if PLUGGY_AVAILABLE: - logger.info("Pluggy is available, setting up plugin manager") - self.plugin_manager = pluggy.PluginManager("cthulhu") + logger.info("Setting up plugin manager") + self.plugin_manager = pluggy.PluginManager("cthulhu") - # Define hook specifications - hook_spec = pluggy.HookspecMarker("cthulhu") + # Define hook specifications + hook_spec = pluggy.HookspecMarker("cthulhu") - class CthulhuHookSpecs: - @hook_spec - def activate(self, plugin=None): - """Called when the plugin is activated.""" - pass + class CthulhuHookSpecs: + @hook_spec + def activate(self, plugin=None): + """Called when the plugin is activated.""" + pass - @hook_spec - def deactivate(self, plugin=None): - """Called when the plugin is deactivated.""" - pass + @hook_spec + def deactivate(self, plugin=None): + """Called when the plugin is deactivated.""" + pass - logger.info("Adding hook specifications to plugin manager") - self.plugin_manager.add_hookspecs(CthulhuHookSpecs) - else: - logger.warning("Pluggy is not available, plugins will be disabled") - self.plugin_manager = None + logger.info("Adding hook specifications to plugin manager") + self.plugin_manager.add_hookspecs(CthulhuHookSpecs) # Plugin storage self._plugins = {} # module_name -> PluginInfo self._plugin_name_index = {} # canonical_name -> [module_name] self._active_plugins = [] + self._plugin_keybindings = {} # plugin_name -> [KeyBinding] + self._global_keybindings = keybindings.KeyBindings() + self._global_bindings = [] # Create plugin directories self._setup_plugin_dirs() @@ -158,186 +151,113 @@ class PluginSystemManager: # Log available plugins directory paths logger.info(f"System plugins directory: {PluginType.SYSTEM.get_root_dir()}") logger.info(f"User plugins directory: {PluginType.USER.get_root_dir()}") + + # Connect to active-script-changed signal + self.app.getSignalManager().connectSignal( + 'active-script-changed', self._on_active_script_changed, 'default') - def register_plugin_global_keybindings(self, plugin): - """Register a plugin's keybindings with all scripts.""" - if not hasattr(plugin, 'get_bindings'): - return - - try: - bindings = plugin.get_bindings() - if not bindings or not bindings.keyBindings: - return - - logger.info(f"Registering global keybindings for plugin: {plugin.name}") - - # First register with the active script - from . import cthulhu_state - if cthulhu_state.activeScript: - active_script = cthulhu_state.activeScript - for binding in bindings.keyBindings: - active_script.getKeyBindings().add(binding) - from . import cthulhu - grab_ids = cthulhu.addKeyGrab(binding) - if grab_ids: - binding._grab_ids = grab_ids - - # Store these bindings for future script changes - plugin_name = plugin.name or plugin.module_name - if not hasattr(self, '_plugin_global_bindings'): - self._plugin_global_bindings = {} - self._plugin_global_bindings[plugin_name] = bindings - - # Connect to script changes to ensure bindings work with all scripts - if not hasattr(self, '_connected_to_script_changes'): - signal_manager = self.app.getSignalManager() - if signal_manager: - signal_manager.connectSignal('load-setting-completed', self._on_settings_changed, None) - self._connected_to_script_changes = True - except Exception as e: - logger.error(f"Error registering global keybindings for plugin {plugin.name}: {e}") - import traceback - logger.error(traceback.format_exc()) - - def refresh_active_script_keybindings(self): - """Force active script to refresh its keybindings to include plugin bindings.""" - from . import cthulhu_state - if cthulhu_state.activeScript: - active_script = cthulhu_state.activeScript - with open('/tmp/plugin_registration.log', 'a') as f: - f.write(f"=== refresh_active_script_keybindings() CALLED ===\n") - f.write(f"Active script: {active_script.name}\n") - - # Force the script to recreate its keybindings to include plugin bindings - old_keybindings = active_script.keyBindings - active_script.keyBindings = active_script.getKeyBindings() - - with open('/tmp/plugin_registration.log', 'a') as f: - f.write(f"Keybindings refreshed: old={len(old_keybindings.keyBindings) if old_keybindings else 0}, new={len(active_script.keyBindings.keyBindings)}\n") - - def register_plugin_keybindings_with_active_script(self): - """Register all plugin keybindings with the active script.""" - - logger.info("=== register_plugin_keybindings_with_active_script() CALLED ===") - with open('/tmp/plugin_registration.log', 'a') as f: - f.write("=== register_plugin_keybindings_with_active_script() CALLED ===\n") - - if not PLUGGY_AVAILABLE: - logger.warning("PLUGGY not available") - with open('/tmp/plugin_registration.log', 'a') as f: - f.write("ERROR: PLUGGY not available\n") - return - - from . import cthulhu_state - if not cthulhu_state.activeScript: - logger.warning("No active script available to register plugin keybindings") - with open('/tmp/plugin_registration.log', 'a') as f: - f.write("ERROR: No active script available\n") - return - - active_script = cthulhu_state.activeScript - logger.info(f"Registering plugin keybindings with active script: {active_script}") - with open('/tmp/plugin_registration.log', 'a') as f: - f.write(f"Active script: {active_script}\n") - - # First, register keybindings from APIHelper's stored bindings - # This is where plugin keybindings actually get stored - from . import cthulhu - api_helper = cthulhu.cthulhuApp.getAPIHelper() - if api_helper and hasattr(api_helper, '_gestureBindings'): - logger.info("=== FOUND APIHelper with _gestureBindings ===") - with open('/tmp/plugin_registration.log', 'a') as f: - f.write("=== Registering stored gesture bindings from APIHelper ===\n") - f.write(f"Total contexts: {len(api_helper._gestureBindings)}\n") - - for context_name, bindings_list in api_helper._gestureBindings.items(): - logger.info(f"Processing context '{context_name}' with {len(bindings_list)} bindings") - with open('/tmp/plugin_registration.log', 'a') as f: - f.write(f"Context '{context_name}': {len(bindings_list)} bindings\n") - - for binding in bindings_list: - logger.info(f"Adding stored binding: {binding.keysymstring} with modifiers {binding.modifiers}") - with open('/tmp/plugin_registration.log', 'a') as f: - f.write(f" Binding: {binding.keysymstring} modifiers={binding.modifiers} desc={binding.handler.description}\n") - - # Check if binding already exists to avoid duplicates - if not active_script.getKeyBindings().hasKeyBinding(binding, "keysNoMask"): - active_script.getKeyBindings().add(binding) - with open('/tmp/plugin_registration.log', 'a') as f: - f.write(f" ADDED to active script!\n") - - # Force recalculation of keycode if it wasn't set when device was None - if not binding.keycode and binding.keysymstring: - from . import keybindings - binding.keycode = keybindings.getKeycode(binding.keysymstring) - # Register key grab at system level - this was missing! - grab_ids = cthulhu.addKeyGrab(binding) - if grab_ids: - binding._grab_ids = grab_ids - else: - logger.warning(f"Failed to create key grab for {binding.keysymstring} - device may not be available") - else: - logger.info(f"Binding already exists: {binding.keysymstring}") - with open('/tmp/plugin_registration.log', 'a') as f: - f.write(f" Already exists, skipped\n") - else: - logger.warning("=== NO APIHelper or no _gestureBindings found ===") - with open('/tmp/plugin_registration.log', 'a') as f: - f.write("ERROR: No APIHelper or no _gestureBindings found!\n") - if api_helper: - f.write(f"APIHelper exists but _gestureBindings: {hasattr(api_helper, '_gestureBindings')}\n") + def add_keybinding(self, plugin_name, binding, global_binding=False): + """Add a keybinding associated with a specific plugin.""" + if plugin_name not in self._plugin_keybindings: + self._plugin_keybindings[plugin_name] = [] + self._plugin_keybindings[plugin_name].append(binding) + logger.debug(f"Added keybinding '{binding.asString()}' for plugin '{plugin_name}'") + if global_binding: + if binding not in self._global_bindings: + self._global_bindings.append(binding) + self._global_keybindings.add(binding) + grab_ids = self.app.addKeyGrab(binding) + if grab_ids: + binding._global_grab_ids = grab_ids else: - f.write("No APIHelper found!\n") + logger.warning(f"Failed to create global key grab for {binding.keysymstring}") - # Also check the old method for any plugins that use get_bindings() - for pluginInfo in self._plugins.values(): - if not pluginInfo.loaded or not pluginInfo.instance: - continue + def _activate_plugin_keybindings(self, plugin_info): + """Activates all keybindings for a given plugin with the active script.""" + from . import cthulhu_state # Import here to avoid circular dependency + if not cthulhu_state.activeScript: + logger.warning(f"No active script to activate keybindings for plugin: {plugin_info.get_module_name()}") + return - plugin = pluginInfo.instance - if not hasattr(plugin, 'get_bindings') or not plugin.get_bindings(): - continue - - logger.info(f"Registering keybindings for plugin: {plugin.name}") - bindings = plugin.get_bindings() - for binding in bindings.keyBindings: - logger.info(f"Adding binding: {binding.keysymstring} with modifiers {binding.modifiers}") - # Check if binding already exists to avoid duplicates + plugin_name = plugin_info.get_module_name() + if plugin_name in self._plugin_keybindings: + active_script = cthulhu_state.activeScript + for binding in self._plugin_keybindings[plugin_name]: + if binding in self._global_bindings: + continue if not active_script.getKeyBindings().hasKeyBinding(binding, "keysNoMask"): active_script.getKeyBindings().add(binding) - # Force recalculation of keycode if it wasn't set when device was None - if not binding.keycode and binding.keysymstring: - from . import keybindings - binding.keycode = keybindings.getKeycode(binding.keysymstring) - # Register key grab at system level - this was missing! - grab_ids = cthulhu.addKeyGrab(binding) + grab_ids = self.app.addKeyGrab(binding) if grab_ids: binding._grab_ids = grab_ids else: - logger.warning(f"Failed to create key grab for {binding.keysymstring} - device may not be available") + logger.warning(f"Failed to create key grab for {binding.keysymstring} for plugin {plugin_name}") + logger.debug(f"Activated keybinding '{binding.asString()}' for plugin '{plugin_name}'") - def _on_settings_changed(self, app=None): - """Re-register all plugin keybindings when settings change.""" - if not hasattr(self, '_plugin_global_bindings'): - return - - from . import cthulhu_state + def _deactivate_plugin_keybindings(self, plugin_info): + """Deactivates all keybindings for a given plugin from the active script.""" + from . import cthulhu_state # Import here to avoid circular dependency if not cthulhu_state.activeScript: + logger.warning(f"No active script to deactivate keybindings for plugin: {plugin_info.get_module_name()}") return - active_script = cthulhu_state.activeScript - for plugin_name, bindings in self._plugin_global_bindings.items(): - logger.info(f"Re-registering keybindings for plugin: {plugin_name}") - for binding in bindings.keyBindings: - # Check if binding already exists - if active_script.getKeyBindings().hasKeyBinding(binding, "keysNoMask"): + plugin_name = plugin_info.get_module_name() + if plugin_name in self._plugin_keybindings: + active_script = cthulhu_state.activeScript + for binding in self._plugin_keybindings[plugin_name]: + if binding in self._global_bindings: + if hasattr(binding, '_global_grab_ids'): + for grab_id in binding._global_grab_ids: + self.app.removeKeyGrab(grab_id) + del binding._global_grab_ids + if binding in self._global_bindings: + self._global_bindings.remove(binding) + self._global_keybindings.remove(binding) continue + if active_script.getKeyBindings().hasKeyBinding(binding, "keysNoMask"): + active_script.getKeyBindings().remove(binding) + if hasattr(binding, '_grab_ids'): + for grab_id in binding._grab_ids: + self.app.removeKeyGrab(grab_id) + del binding._grab_ids + logger.debug(f"Deactivated keybinding '{binding.asString()}' for plugin '{plugin_name}'") - active_script.getKeyBindings().add(binding) - from . import cthulhu - grab_ids = cthulhu.addKeyGrab(binding) - if grab_ids: - binding._grab_ids = grab_ids + def refresh_active_script_keybindings(self): + """Public method to refresh keybindings for the currently active script.""" + from . import cthulhu_state + if cthulhu_state.activeScript: + self._on_active_script_changed(self.app, cthulhu_state.activeScript) + + def get_global_keybindings(self): + return self._global_keybindings + + def _on_active_script_changed(self, app, new_script): + """Called when the active script changes. Re-applies keybindings for all active plugins.""" + logger.info(f"Active script changed to {new_script.name if new_script else 'None'}. Re-applying plugin keybindings.") + + # First, remove all existing plugin keybindings from the old script + # This requires iterating through all plugins, not just active ones, to ensure cleanup + from . import cthulhu_state + if cthulhu_state.activeScript: # If there was an old active script + old_script = cthulhu_state.activeScript + for plugin_name, bindings in self._plugin_keybindings.items(): + for binding in bindings: + if binding in self._global_bindings: + continue + if old_script.getKeyBindings().hasKeyBinding(binding, "keysNoMask"): + old_script.getKeyBindings().remove(binding) + if hasattr(binding, '_grab_ids'): + for grab_id in binding._grab_ids: + self.app.removeKeyGrab(grab_id) + del binding._grab_ids + logger.debug(f"Removed keybinding '{binding.asString()}' from old script for plugin '{plugin_name}'") + + # Now, if there's a new active script, apply keybindings for currently active plugins + if new_script: + for plugin_name in self._active_plugins: + plugin_info = self._plugins.get(plugin_name) + if plugin_info and plugin_info.loaded: + self._activate_plugin_keybindings(plugin_info) def _setup_plugin_dirs(self): """Ensure plugin directories exist.""" @@ -958,11 +878,6 @@ class PluginSystemManager: def loadPlugin(self, pluginInfo): """Load a plugin.""" - # Skip if pluggy is not available - if not PLUGGY_AVAILABLE: - logger.info(f"Skipping plugin {pluginInfo.get_name()}: pluggy not available") - return False - module_name = pluginInfo.get_module_name() logger.info(f"=== PluginSystemManager.loadPlugin starting for: {module_name} ===") @@ -1055,8 +970,8 @@ class PluginSystemManager: self._register_plugin_dbus_module(pluginInfo) - # Register any global keybindings from the plugin - self.register_plugin_global_keybindings(pluginInfo.instance) + # Activate plugin-specific keybindings + self._activate_plugin_keybindings(pluginInfo) return True @@ -1068,10 +983,6 @@ class PluginSystemManager: def unloadPlugin(self, pluginInfo): """Unload a plugin.""" - # Skip if pluggy is not available - if not PLUGGY_AVAILABLE: - return False - if pluginInfo.builtin: return False @@ -1108,9 +1019,6 @@ class PluginSystemManager: def unloadAllPlugins(self, ForceAllPlugins=False): """Unload all plugins.""" - if not PLUGGY_AVAILABLE: - return - for pluginInfo in self.plugins: if ForceAllPlugins or pluginInfo.loaded: self.unloadPlugin(pluginInfo) diff --git a/src/cthulhu/plugins/GameMode/plugin.py b/src/cthulhu/plugins/GameMode/plugin.py index d89a43e..428856a 100644 --- a/src/cthulhu/plugins/GameMode/plugin.py +++ b/src/cthulhu/plugins/GameMode/plugin.py @@ -76,7 +76,8 @@ class GameMode(Plugin): self._kbBinding = self.registerGestureByString( self._toggle_game_mode, description, - gestureString + gestureString, + globalBinding=True ) if self._kbBinding: debug.printMessage(debug.LEVEL_INFO, f"GameMode: Registered keybinding {gestureString}", True) diff --git a/src/cthulhu/plugins/PluginManager/plugin.py b/src/cthulhu/plugins/PluginManager/plugin.py index 3433bfc..0693f2d 100644 --- a/src/cthulhu/plugins/PluginManager/plugin.py +++ b/src/cthulhu/plugins/PluginManager/plugin.py @@ -19,11 +19,12 @@ gi.require_version('Gtk', '3.0') from gi.repository import Gtk, Gdk, Pango from cthulhu.plugin import Plugin, cthulhu_hookimpl +from cthulhu import cthulhu from cthulhu import debug from cthulhu import settings_manager logger = logging.getLogger(__name__) -_settingsManager = settings_manager.getManager() +_settingsManager = None # Removed - use cthulhu.cthulhuApp.settingsManager class PluginManager(Plugin): @@ -225,7 +226,7 @@ class PluginManager(Plugin): available_plugins = self._discover_plugins() # Get currently active plugins - active_plugins = _settingsManager.getSetting('activePlugins') or [] + active_plugins = cthulhu.cthulhuApp.settingsManager.getSetting('activePlugins') or [] debug.printMessage(debug.LEVEL_INFO, f"PluginManager: Found {len(available_plugins)} plugins", True) @@ -449,7 +450,7 @@ class PluginManager(Plugin): debug.printMessage(debug.LEVEL_INFO, f"PluginManager: Plugin {plugin_name} toggled to {'active' if is_active else 'inactive'}", True) # Get current active plugins - active_plugins = _settingsManager.getSetting('activePlugins') or [] + active_plugins = cthulhu.cthulhuApp.settingsManager.getSetting('activePlugins') or [] active_plugins = list(active_plugins) if is_active and plugin_name not in active_plugins: @@ -457,31 +458,31 @@ class PluginManager(Plugin): elif not is_active and plugin_name in active_plugins: active_plugins.remove(plugin_name) - _settingsManager.setSetting('activePlugins', active_plugins) - if hasattr(_settingsManager, "general") and isinstance(_settingsManager.general, dict): - _settingsManager.general['activePlugins'] = active_plugins + cthulhu.cthulhuApp.settingsManager.setSetting('activePlugins', active_plugins) + if hasattr(cthulhu.cthulhuApp.settingsManager, "general") and isinstance(cthulhu.cthulhuApp.settingsManager.general, dict): + cthulhu.cthulhuApp.settingsManager.general['activePlugins'] = active_plugins try: - active_profile = _settingsManager.getSetting('activeProfile') + active_profile = cthulhu.cthulhuApp.settingsManager.getSetting('activeProfile') if isinstance(active_profile, (list, tuple)) and len(active_profile) > 1: profile_name = active_profile[1] else: - profile_name = _settingsManager.profile or 'default' + profile_name = cthulhu.cthulhuApp.settingsManager.profile or 'default' - current_general = _settingsManager.getGeneralSettings(profile_name) or {} + current_general = cthulhu.cthulhuApp.settingsManager.getGeneralSettings(profile_name) or {} current_general['activePlugins'] = active_plugins - _settingsManager.profile = profile_name - _settingsManager._setProfileGeneral(current_general) + cthulhu.cthulhuApp.settingsManager.profile = profile_name + cthulhu.cthulhuApp.settingsManager._setProfileGeneral(current_general) - pronunciations = _settingsManager.getPronunciations(profile_name) or {} - keybindings = _settingsManager.getKeybindings(profile_name) or {} + pronunciations = cthulhu.cthulhuApp.settingsManager.getPronunciations(profile_name) or {} + keybindings = cthulhu.cthulhuApp.settingsManager.getKeybindings(profile_name) or {} - backend = _settingsManager._backend + backend = cthulhu.cthulhuApp.settingsManager._backend if backend: backend.saveProfileSettings( profile_name, - _settingsManager.profileGeneral, + cthulhu.cthulhuApp.settingsManager.profileGeneral, pronunciations, keybindings ) diff --git a/src/cthulhu/plugins/SimplePluginSystem/plugin.py b/src/cthulhu/plugins/SimplePluginSystem/plugin.py index 95948ea..cc7d5a5 100644 --- a/src/cthulhu/plugins/SimplePluginSystem/plugin.py +++ b/src/cthulhu/plugins/SimplePluginSystem/plugin.py @@ -310,7 +310,7 @@ class SimplePluginSystem(Plugin): currPluginSetting['functionname'] = self.id_generator() return currPluginSetting - def registerGestureByString(self, function, description, shortcut): + def registerGestureByString(self, function, description, shortcut, globalBinding=True): """Register a keyboard shortcut for a function. This is a compatibility wrapper for the new plugin system. @@ -326,6 +326,7 @@ class SimplePluginSystem(Plugin): 'default', 'cthulhu', True, + globalBinding=globalBinding, contextName=self.module_name ) logger.debug(f"Registered shortcut {shortcut} for {description}") diff --git a/src/cthulhu/plugins/nvda2cthulhu/plugin.py b/src/cthulhu/plugins/nvda2cthulhu/plugin.py index 7fc34c5..f551ff6 100644 --- a/src/cthulhu/plugins/nvda2cthulhu/plugin.py +++ b/src/cthulhu/plugins/nvda2cthulhu/plugin.py @@ -128,7 +128,8 @@ class Nvda2Cthulhu(Plugin): self.toggle_translation, "NVDA to Cthulhu translation", "kb:cthulhu+control+shift+t", - learnModeEnabled=True + learnModeEnabled=True, + globalBinding=True ) if not self._dependencies_available(): self._present_message("NVDA to Cthulhu missing dependencies: python-msgpack and python-tornado") @@ -186,6 +187,8 @@ class Nvda2Cthulhu(Plugin): return True def toggle_translation(self, script=None, inputEvent=None): + if not self.settingsManager and self.app: + self.settingsManager = self.app.getSettingsManager() if not self.settingsManager: return False if not self._translation_command_available(): @@ -342,8 +345,7 @@ class Nvda2Cthulhu(Plugin): def _present_message(self, message): try: - scriptManagerApi = self.app.getDynamicApiManager().getAPI('ScriptManager') - scriptManager = scriptManagerApi.get_manager() + scriptManager = self.app.getScriptManager() scriptManager.get_default_script().presentMessage(message, resetStyles=False) except Exception: logger.info(message) diff --git a/src/cthulhu/resource_manager.py b/src/cthulhu/resource_manager.py index c768e10..2c461df 100644 --- a/src/cthulhu/resource_manager.py +++ b/src/cthulhu/resource_manager.py @@ -28,9 +28,9 @@ import traceback class TryFunction(): def __init__(self, function): self.function = function - def runSignal(self, app): + def runSignal(self, app, *args): try: - return self.function(app) + return self.function(app, *args) except Exception as e: print('try signal',e , traceback.print_exc()) def runInputEvent(self, script=None, inputEvent=None): diff --git a/src/cthulhu/script.py b/src/cthulhu/script.py index 29812ed..f4deba4 100644 --- a/src/cthulhu/script.py +++ b/src/cthulhu/script.py @@ -49,6 +49,7 @@ import gi gi.require_version("Atspi", "2.0") from gi.repository import Atspi +from . import cthulhu # Need access to cthulhuApp from . import ax_event_synthesizer from . import action_presenter from . import braille_generator @@ -78,9 +79,7 @@ from . import tutorialgenerator from . import where_am_i_presenter from .ax_object import AXObject -_eventManager = event_manager.getManager() -_scriptManager = script_manager.get_manager() -_settingsManager = settings_manager.getManager() +# Old global variables removed - scripts now access via cthulhuApp or self._app class Script: """The specific focus tracking scripts for applications. @@ -276,7 +275,7 @@ class Script: def getStructuralNavigation(self): """Returns the 'structural navigation' class for this script.""" types = self.getEnabledStructuralNavigationTypes() - enable = _settingsManager.getSetting('structuralNavigationEnabled') + enable = cthulhu.cthulhuApp.settingsManager.getSetting('structuralNavigationEnabled') return structural_navigation.StructuralNavigation(self, types, enable) def getLiveRegionManager(self): @@ -350,7 +349,7 @@ class Script: - script: the script. """ - _eventManager.registerScriptListeners(self) + cthulhu.cthulhuApp.eventManager.registerScriptListeners(self) def deregisterEventListeners(self): """Tells the event manager to stop listening for all the event types @@ -360,7 +359,7 @@ class Script: - script: the script. """ - _eventManager.deregisterScriptListeners(self) + cthulhu.cthulhuApp.eventManager.deregisterScriptListeners(self) def processObjectEvent(self, event): """Processes all AT-SPI object events of interest to this @@ -497,33 +496,16 @@ class Script: Returns True if the event is of interest. """ - user_bindings = None - user_bindings_map = settings.keyBindingsMap - if self.__module__ in user_bindings_map: - user_bindings = user_bindings_map[self.__module__] - elif "default" in user_bindings_map: - user_bindings = user_bindings_map["default"] - consumes = False self._lastCommandWasStructNav = False - if user_bindings: - handler = user_bindings.getInputHandler(keyboardEvent) - if handler \ - and handler.function in self.structuralNavigation.functions: - consumes = self.useStructuralNavigationModel() - if consumes: - self._lastCommandWasStructNav = True - else: - consumes = handler is not None - if not consumes: - handler = self.keyBindings.getInputHandler(keyboardEvent) - if handler \ - and handler.function in self.structuralNavigation.functions: - consumes = self.useStructuralNavigationModel() - if consumes: - self._lastCommandWasStructNav = True - else: - consumes = handler is not None + handler = self.keyBindings.getInputHandler(keyboardEvent) + if handler \ + and handler.function in self.structuralNavigation.functions: + consumes = self.useStructuralNavigationModel() + if consumes: + self._lastCommandWasStructNav = True + else: + consumes = handler is not None return consumes def consumesBrailleEvent(self, brailleEvent): diff --git a/src/cthulhu/script_manager.py b/src/cthulhu/script_manager.py index 47e9602..dda84b2 100644 --- a/src/cthulhu/script_manager.py +++ b/src/cthulhu/script_manager.py @@ -43,8 +43,9 @@ def _get_ax_utilities(): class ScriptManager: - def __init__(self): + def __init__(self, app): # Added app argument debug.printMessage(debug.LEVEL_INFO, "SCRIPT MANAGER: Initializing", True) + self.app = app # Store app instance self.appScripts = {} self.toolkitScripts = {} self.customScripts = {} @@ -345,11 +346,9 @@ class ScriptManager: newScript.activate() - # Register plugin keybindings with the new active script + # Emit signal that active script has changed, so PluginSystemManager can update keybindings from . import cthulhu - plugin_manager = cthulhu.cthulhuApp.getPluginSystemManager() - if plugin_manager: - pass # plugin_manager.register_plugin_keybindings_with_active_script() + cthulhu.cthulhuApp.getSignalManager().emitSignal('active-script-changed', newScript) tokens = ["SCRIPT MANAGER: Setting active script to", newScript, "reason:", reason] debug.printTokens(debug.LEVEL_INFO, tokens, True) @@ -428,7 +427,13 @@ class ScriptManager: del app -_manager = ScriptManager() - +_manager = None def get_manager(): + """Returns the Script Manager""" + + global _manager + if _manager is None: + from . import cthulhu + _manager = ScriptManager(cthulhu.cthulhuApp) return _manager + diff --git a/src/cthulhu/script_utilities.py b/src/cthulhu/script_utilities.py index 7e95521..62f8e91 100644 --- a/src/cthulhu/script_utilities.py +++ b/src/cthulhu/script_utilities.py @@ -73,7 +73,7 @@ from .ax_value import AXValue from .ax_utilities import AXUtilities from .ax_utilities_relation import AXUtilitiesRelation -_settingsManager = settings_manager.getManager() +_settingsManager = None # Removed - use cthulhu.cthulhuApp.settingsManager # Try to import sound system for indentation beeps try: @@ -1017,9 +1017,9 @@ class Utilities: return True def isProgressBarUpdate(self, obj): - if not _settingsManager.getSetting('speakProgressBarUpdates') \ - and not _settingsManager.getSetting('brailleProgressBarUpdates') \ - and not _settingsManager.getSetting('beepProgressBarUpdates'): + if not cthulhu.cthulhuApp.settingsManager.getSetting('speakProgressBarUpdates') \ + and not cthulhu.cthulhuApp.settingsManager.getSetting('brailleProgressBarUpdates') \ + and not cthulhu.cthulhuApp.settingsManager.getSetting('beepProgressBarUpdates'): return False, "Updates not enabled" if not self.isProgressBar(obj): @@ -1028,11 +1028,11 @@ class Utilities: if self.hasNoSize(obj): return False, "Has no size" - if _settingsManager.getSetting('ignoreStatusBarProgressBars'): + if cthulhu.cthulhuApp.settingsManager.getSetting('ignoreStatusBarProgressBars'): if AXObject.find_ancestor(obj, AXUtilities.is_status_bar): return False, "Is status bar descendant" - verbosity = _settingsManager.getSetting('progressBarVerbosity') + verbosity = cthulhu.cthulhuApp.settingsManager.getSetting('progressBarVerbosity') if verbosity == settings.PROGRESS_BAR_ALL: return True, "Verbosity is all" @@ -1220,12 +1220,12 @@ class Utilities: return False if not self.getDocumentForObject(table): - return _settingsManager.getSetting('readFullRowInGUITable') + return cthulhu.cthulhuApp.settingsManager.getSetting('readFullRowInGUITable') if self.isSpreadSheetTable(table): - return _settingsManager.getSetting('readFullRowInSpreadSheet') + return cthulhu.cthulhuApp.settingsManager.getSetting('readFullRowInSpreadSheet') - return _settingsManager.getSetting('readFullRowInDocumentTable') + return cthulhu.cthulhuApp.settingsManager.getSetting('readFullRowInDocumentTable') def isSorted(self, obj): return False @@ -2958,7 +2958,7 @@ class Utilities: # If the user has set their punctuation level to All, then the synthesizer will # do the work for us. If the user has set their punctuation level to None, then # they really don't want punctuation and we mustn't override that. - style = _settingsManager.getSetting("verbalizePunctuationStyle") + style = cthulhu.cthulhuApp.settingsManager.getSetting("verbalizePunctuationStyle") if style in [settings.PUNCTUATION_STYLE_ALL, settings.PUNCTUATION_STYLE_NONE]: return False @@ -3202,15 +3202,15 @@ class Utilities: return changed, previousColumns def _indentation_enabled(self): - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return False - return _settingsManager.getSetting('enableIndentation') + return cthulhu.cthulhuApp.settingsManager.getSetting('enableIndentation') def _indentation_speech_enabled(self): if not self._indentation_enabled(): return False - presentationMode = _settingsManager.getSetting('indentationPresentationMode') \ + presentationMode = cthulhu.cthulhuApp.settingsManager.getSetting('indentationPresentationMode') \ or settings.indentationPresentationMode return presentationMode in ( settings.INDENTATION_PRESENTATION_SPEECH, @@ -3221,7 +3221,7 @@ class Utilities: if not self._indentation_enabled(): return False - presentationMode = _settingsManager.getSetting('indentationPresentationMode') \ + presentationMode = cthulhu.cthulhuApp.settingsManager.getSetting('indentationPresentationMode') \ or settings.indentationPresentationMode return presentationMode in ( settings.INDENTATION_PRESENTATION_BEEPS, @@ -3265,7 +3265,7 @@ class Utilities: def get_indentation_presentation(self, line, obj=None): data = self._get_indentation_data(line) hasIndentation = bool(data["indentation"]) - presentationMode = _settingsManager.getSetting('indentationPresentationMode') \ + presentationMode = cthulhu.cthulhuApp.settingsManager.getSetting('indentationPresentationMode') \ or settings.indentationPresentationMode indentDebug = data["indentation"].replace("\t", "\\t").replace(" ", ".") @@ -5075,10 +5075,10 @@ class Utilities: return False if AXUtilities.is_password_text(event.source): - return _settingsManager.getSetting("enableKeyEcho") + return cthulhu.cthulhuApp.settingsManager.getSetting("enableKeyEcho") if len(event.any_data.strip()) == 1: - return _settingsManager.getSetting("enableEchoByCharacter") + return cthulhu.cthulhuApp.settingsManager.getSetting("enableEchoByCharacter") return False @@ -5333,7 +5333,7 @@ class Utilities: child = self.findChildAtOffset(obj, newEnd - 1) self.handleTextSelectionChange(child, False) - speakMessage = speakMessage and not _settingsManager.getSetting('onlySpeakDisplayedText') + speakMessage = speakMessage and not cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText') for start, end, message in changes: string = AXText.get_substring(obj, start, end) endsWithChild = string.endswith(self.EMBEDDED_OBJECT_CHARACTER) @@ -5360,7 +5360,7 @@ class Utilities: def _speakTextSelectionState(self, nSelections): """Hacky and to-be-obsoleted method.""" - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return False eventStr, mods = self.lastKeyAndModifiers() diff --git a/src/cthulhu/scripts/apps/notify-osd/script.py b/src/cthulhu/scripts/apps/notify-osd/script.py index 767a3fa..6c48018 100644 --- a/src/cthulhu/scripts/apps/notify-osd/script.py +++ b/src/cthulhu/scripts/apps/notify-osd/script.py @@ -38,7 +38,7 @@ import cthulhu.settings_manager as settings_manager from cthulhu.ax_object import AXObject from cthulhu.ax_value import AXValue -_settingsManager = settings_manager.getManager() +_settingsManager = None # Removed - use cthulhuApp.settingsManager ######################################################################## # # diff --git a/src/cthulhu/scripts/apps/soffice/braille_generator.py b/src/cthulhu/scripts/apps/soffice/braille_generator.py index dc12fac..c205d2b 100644 --- a/src/cthulhu/scripts/apps/soffice/braille_generator.py +++ b/src/cthulhu/scripts/apps/soffice/braille_generator.py @@ -42,7 +42,7 @@ import cthulhu.settings_manager as settings_manager from cthulhu.ax_object import AXObject from cthulhu.ax_utilities import AXUtilities -_settingsManager = settings_manager.getManager() +_settingsManager = None # Removed - use cthulhuApp.settingsManager class BrailleGenerator(braille_generator.BrailleGenerator): diff --git a/src/cthulhu/scripts/default.py b/src/cthulhu/scripts/default.py index 03e7c17..3bdee8a 100644 --- a/src/cthulhu/scripts/default.py +++ b/src/cthulhu/scripts/default.py @@ -54,7 +54,7 @@ import cthulhu.input_event as input_event import cthulhu.input_event_manager as input_event_manager import cthulhu.keybindings as keybindings import cthulhu.messages as messages -import cthulhu.cthulhu as cthulhu +from cthulhu import cthulhu import cthulhu.cthulhu_modifier_manager as cthulhu_modifier_manager import cthulhu.cthulhu_state as cthulhu_state import cthulhu.phonnames as phonnames @@ -71,8 +71,8 @@ from cthulhu.ax_text import AXText from cthulhu.ax_utilities import AXUtilities from cthulhu.ax_utilities_relation import AXUtilitiesRelation -_scriptManager = script_manager.get_manager() -_settingsManager = settings_manager.getManager() +_scriptManager = None # Removed - use cthulhu.cthulhuApp.scriptManager +_settingsManager = None # Removed - use cthulhu.cthulhuApp.settingsManager ######################################################################## # # @@ -391,7 +391,7 @@ class Script(script.Script): for keyBinding in bindings.keyBindings: keyBindings.add(keyBinding) - layout = _settingsManager.getSetting('keyboardLayout') + layout = cthulhu.cthulhuApp.settingsManager.getSetting('keyboardLayout') isDesktop = layout == settings.GENERAL_KEYBOARD_LAYOUT_DESKTOP bindings = self.flatReviewPresenter.get_bindings(isDesktop) for keyBinding in bindings.keyBindings: @@ -431,7 +431,6 @@ class Script(script.Script): # Add plugin keybindings from APIHelper storage try: - import cthulhu.cthulhu as cthulhu if hasattr(cthulhu, 'cthulhuApp') and cthulhu.cthulhuApp: api_helper = cthulhu.cthulhuApp.getAPIHelper() with open('/tmp/extension_bindings_debug.log', 'a') as f: @@ -491,7 +490,7 @@ class Script(script.Script): keyBindings.add(keyBinding) try: - keyBindings = _settingsManager.overrideKeyBindings(self, keyBindings) + keyBindings = cthulhu.cthulhuApp.settingsManager.overrideKeyBindings(self, keyBindings) except Exception as error: tokens = ["DEFAULT: Exception when overriding keybindings in", self, ":", error] debug.printTokens(debug.LEVEL_WARNING, tokens, True) @@ -504,7 +503,7 @@ class Script(script.Script): keyBindings = keybindings.KeyBindings() - layout = _settingsManager.getSetting('keyboardLayout') + layout = cthulhu.cthulhuApp.settingsManager.getSetting('keyboardLayout') if layout == settings.GENERAL_KEYBOARD_LAYOUT_DESKTOP: for keyBinding in self.__getDesktopBindings().keyBindings: keyBindings.add(keyBinding) @@ -763,17 +762,13 @@ class Script(script.Script): tokens = ["DEFAULT: Activating script for", self.app] debug.printTokens(debug.LEVEL_INFO, tokens, True) - _settingsManager.loadAppSettings(self) + cthulhu.cthulhuApp.settingsManager.loadAppSettings(self) braille.checkBrailleSetting() braille.setupKeyRanges(self.brailleBindings.keys()) speech.checkSpeechSetting() self.speechAndVerbosityManager.update_punctuation_level() self.speechAndVerbosityManager.update_capitalization_style() - # Use new InputEventManager for global keyboard capture by default - # Only fall back to legacy handling for problematic applications - cthulhu.setKeyHandling(True) - self.addKeyGrabs() tokens = ["DEFAULT: Script for", self.app, "activated"] @@ -786,8 +781,8 @@ class Script(script.Script): - obj: the Accessible """ - if not _settingsManager.getSetting('enableBraille') \ - and not _settingsManager.getSetting('enableBrailleMonitor'): + if not cthulhu.cthulhuApp.settingsManager.getSetting('enableBraille') \ + and not cthulhu.cthulhuApp.settingsManager.getSetting('enableBrailleMonitor'): debug.printMessage(debug.LEVEL_INFO, "BRAILLE: update disabled", True) return @@ -882,8 +877,8 @@ class Script(script.Script): associated with cell 0.""" if isinstance(inputEvent, input_event.KeyboardEvent) \ - and not _settingsManager.getSetting('enableBraille') \ - and not _settingsManager.getSetting('enableBrailleMonitor'): + and not cthulhu.cthulhuApp.settingsManager.getSetting('enableBraille') \ + and not cthulhu.cthulhuApp.settingsManager.getSetting('enableBrailleMonitor'): msg = "DEFAULT: panBrailleLeft command requires braille or braille monitor" debug.printMessage(debug.LEVEL_INFO, msg, True) return @@ -955,8 +950,8 @@ class Script(script.Script): associated with cell 0.""" if isinstance(inputEvent, input_event.KeyboardEvent) \ - and not _settingsManager.getSetting('enableBraille') \ - and not _settingsManager.getSetting('enableBrailleMonitor'): + and not cthulhu.cthulhuApp.settingsManager.getSetting('enableBraille') \ + and not cthulhu.cthulhuApp.settingsManager.getSetting('enableBrailleMonitor'): msg = "DEFAULT: panBrailleRight command requires braille or braille monitor" debug.printMessage(debug.LEVEL_INFO, msg, True) return @@ -1152,13 +1147,13 @@ class Script(script.Script): def cycleSettingsProfile(self, inputEvent=None): """Cycle through the user's existing settings profiles.""" - profiles = _settingsManager.availableProfiles() + profiles = cthulhu.cthulhuApp.settingsManager.availableProfiles() if not (profiles and profiles[0]): self.presentMessage(messages.PROFILE_NOT_FOUND) return True def isMatch(x): - return x is not None and x[1] == _settingsManager.getProfile() + return x is not None and x[1] == cthulhu.cthulhuApp.settingsManager.getProfile() current = list(filter(isMatch, profiles))[0] try: @@ -1166,7 +1161,7 @@ class Script(script.Script): except IndexError: name, profileID = profiles[0] - _settingsManager.setProfile(profileID, updateLocale=True) + cthulhu.cthulhuApp.settingsManager.setProfile(profileID, updateLocale=True) braille.checkBrailleSetting() @@ -1538,7 +1533,7 @@ class Script(script.Script): debug.printTokens(debug.LEVEL_INFO, tokens, True) return - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return isSelected = AXUtilities.is_selected(event.source) @@ -1676,7 +1671,7 @@ class Script(script.Script): if role == Atspi.Role.TOOL_TIP: keyString, mods = self.utilities.lastKeyAndModifiers() if keyString != "F1" \ - and not _settingsManager.getSetting('presentToolTips'): + and not cthulhu.cthulhuApp.settingsManager.getSetting('presentToolTips'): return if event.detail1: self.presentObject(obj, interrupt=True) @@ -1699,7 +1694,7 @@ class Script(script.Script): debug.printMessage(debug.LEVEL_INFO, msg, True) return - if _settingsManager.getSetting('speakMisspelledIndicator'): + if cthulhu.cthulhuApp.settingsManager.getSetting('speakMisspelledIndicator'): offset = AXText.get_caret_offset(event.source) if not AXText.get_substring(event.source, offset, offset + 1).isalnum(): offset -= 1 @@ -1821,11 +1816,11 @@ class Script(script.Script): if len(string) != 1: return - if _settingsManager.getSetting('enableEchoBySentence') \ + if cthulhu.cthulhuApp.settingsManager.getSetting('enableEchoBySentence') \ and self.echoPreviousSentence(event.source): return - if _settingsManager.getSetting('enableEchoByWord'): + if cthulhu.cthulhuApp.settingsManager.getSetting('enableEchoByWord'): self.echoPreviousWord(event.source) def onTextSelectionChanged(self, event): @@ -1988,7 +1983,7 @@ class Script(script.Script): cthulhu.setLocusOfFocus(event, None) cthulhu.setActiveWindow(None) - _scriptManager.set_active_script(None, "Window deactivated") + cthulhu.cthulhuApp.scriptManager.set_active_script(None, "Window deactivated") def onClipboardContentsChanged(self, *args): if self.flatReviewPresenter.is_active(): @@ -2072,7 +2067,7 @@ class Script(script.Script): return def _rewindSayAll(self, context, minCharCount=10): - if not _settingsManager.getSetting('rewindAndFastForwardInSayAll'): + if not cthulhu.cthulhuApp.settingsManager.getSetting('rewindAndFastForwardInSayAll'): return False index = self._sayAllContexts.index(context) @@ -2090,7 +2085,7 @@ class Script(script.Script): return True def _fastForwardSayAll(self, context): - if not _settingsManager.getSetting('rewindAndFastForwardInSayAll'): + if not cthulhu.cthulhuApp.settingsManager.getSetting('rewindAndFastForwardInSayAll'): return False if AXObject.supports_text(context.obj): @@ -2324,7 +2319,7 @@ class Script(script.Script): if not character or character == '\r': character = "\n" - speakBlankLines = _settingsManager.getSetting('speakBlankLines') + speakBlankLines = cthulhu.cthulhuApp.settingsManager.getSetting('speakBlankLines') if character == "\n": line, _, _ = AXText.get_line_at_offset(obj, max(0, offset)) if not line or line == "\n": @@ -2447,7 +2442,7 @@ class Script(script.Script): # Announce when we cross a hard line boundary. if "\n" in word: - if _settingsManager.getSetting('enableSpeechIndentation'): + if cthulhu.cthulhuApp.settingsManager.getSetting('enableSpeechIndentation'): self.speakCharacter("\n") if word.startswith("\n"): startOffset += 1 @@ -2528,8 +2523,8 @@ class Script(script.Script): at that cell. Otherwise, we will pan in display-sized increments to show the review cursor.""" - if not _settingsManager.getSetting('enableBraille') \ - and not _settingsManager.getSetting('enableBrailleMonitor'): + if not cthulhu.cthulhuApp.settingsManager.getSetting('enableBraille') \ + and not cthulhu.cthulhuApp.settingsManager.getSetting('enableBrailleMonitor'): debug.printMessage(debug.LEVEL_INFO, "BRAILLE: update review disabled", True) return @@ -2665,7 +2660,7 @@ class Script(script.Script): # Determine the correct "say all by" mode to use. # - sayAllStyle = _settingsManager.getSetting('sayAllStyle') + sayAllStyle = cthulhu.cthulhuApp.settingsManager.getSetting('sayAllStyle') if sayAllStyle == settings.SAYALL_STYLE_SENTENCE: mode = "sentence" elif sayAllStyle == settings.SAYALL_STYLE_LINE: @@ -2848,7 +2843,7 @@ class Script(script.Script): attributes. """ - if _settingsManager.getSetting('speakMisspelledIndicator'): + if cthulhu.cthulhuApp.settingsManager.getSetting('speakMisspelledIndicator'): if not AXObject.supports_text(obj): return # If we're on whitespace, we cannot be on a misspelled word. @@ -2941,18 +2936,18 @@ class Script(script.Script): if briefMessage is None: briefMessage = fullMessage - if _settingsManager.getSetting('enableSpeech'): - if not _settingsManager.getSetting('messagesAreDetailed'): + if cthulhu.cthulhuApp.settingsManager.getSetting('enableSpeech'): + if not cthulhu.cthulhuApp.settingsManager.getSetting('messagesAreDetailed'): message = briefMessage else: message = fullMessage if message: self.speakMessage(message, voice=voice, resetStyles=resetStyles, force=force) - if (_settingsManager.getSetting('enableBraille') \ - or _settingsManager.getSetting('enableBrailleMonitor')) \ - and _settingsManager.getSetting('enableFlashMessages'): - if not _settingsManager.getSetting('flashIsDetailed'): + if (cthulhu.cthulhuApp.settingsManager.getSetting('enableBraille') \ + or cthulhu.cthulhuApp.settingsManager.getSetting('enableBrailleMonitor')) \ + and cthulhu.cthulhuApp.settingsManager.getSetting('enableFlashMessages'): + if not cthulhu.cthulhuApp.settingsManager.getSetting('flashIsDetailed'): message = briefMessage else: message = fullMessage @@ -2965,10 +2960,10 @@ class Script(script.Script): message = [i for i in message if isinstance(i, str)] message = " ".join(message) - if _settingsManager.getSetting('flashIsPersistent'): + if cthulhu.cthulhuApp.settingsManager.getSetting('flashIsPersistent'): duration = -1 else: - duration = _settingsManager.getSetting('brailleFlashTime') + duration = cthulhu.cthulhuApp.settingsManager.getSetting('brailleFlashTime') braille.displayMessage(message, flashTime=duration) @@ -3067,8 +3062,8 @@ class Script(script.Script): a cursor routing key. """ - if not _settingsManager.getSetting('enableBraille') \ - and not _settingsManager.getSetting('enableBrailleMonitor'): + if not cthulhu.cthulhuApp.settingsManager.getSetting('enableBraille') \ + and not cthulhu.cthulhuApp.settingsManager.getSetting('enableBrailleMonitor'): debug.printMessage(debug.LEVEL_INFO, "BRAILLE: display message disabled", True) return @@ -3091,8 +3086,8 @@ class Script(script.Script): a cursor routing key. """ - if not _settingsManager.getSetting('enableBraille') \ - and not _settingsManager.getSetting('enableBrailleMonitor'): + if not cthulhu.cthulhuApp.settingsManager.getSetting('enableBraille') \ + and not cthulhu.cthulhuApp.settingsManager.getSetting('enableBrailleMonitor'): debug.printMessage(debug.LEVEL_INFO, "BRAILLE: display regions disabled", True) return @@ -3254,8 +3249,8 @@ class Script(script.Script): def updateBrailleForNewCaretPosition(self, obj): """Try to reposition the cursor without having to do a full update.""" - if not _settingsManager.getSetting('enableBraille') \ - and not _settingsManager.getSetting('enableBrailleMonitor'): + if not cthulhu.cthulhuApp.settingsManager.getSetting('enableBraille') \ + and not cthulhu.cthulhuApp.settingsManager.getSetting('enableBrailleMonitor'): debug.printMessage(debug.LEVEL_INFO, "BRAILLE: update caret disabled", True) return @@ -3364,31 +3359,31 @@ class Script(script.Script): prior to speaking the new text. """ - if not _settingsManager.getSetting('enableSpeech') \ - or (_settingsManager.getSetting('onlySpeakDisplayedText') and not force): + if not cthulhu.cthulhuApp.settingsManager.getSetting('enableSpeech') \ + or (cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText') and not force): return - voices = _settingsManager.getSetting('voices') + voices = cthulhu.cthulhuApp.settingsManager.getSetting('voices') systemVoice = voices.get(settings.SYSTEM_VOICE) voice = voice or systemVoice if voice == systemVoice and resetStyles: - capStyle = _settingsManager.getSetting('capitalizationStyle') - _settingsManager.setSetting('capitalizationStyle', settings.CAPITALIZATION_STYLE_NONE) + capStyle = cthulhu.cthulhuApp.settingsManager.getSetting('capitalizationStyle') + cthulhu.cthulhuApp.settingsManager.setSetting('capitalizationStyle', settings.CAPITALIZATION_STYLE_NONE) self.speechAndVerbosityManager.update_capitalization_style() - punctStyle = _settingsManager.getSetting('verbalizePunctuationStyle') - _settingsManager.setSetting('verbalizePunctuationStyle', + punctStyle = cthulhu.cthulhuApp.settingsManager.getSetting('verbalizePunctuationStyle') + cthulhu.cthulhuApp.settingsManager.setSetting('verbalizePunctuationStyle', settings.PUNCTUATION_STYLE_NONE) self.speechAndVerbosityManager.update_punctuation_level() speech.speak(string, voice, interrupt) if voice == systemVoice and resetStyles: - _settingsManager.setSetting('capitalizationStyle', capStyle) + cthulhu.cthulhuApp.settingsManager.setSetting('capitalizationStyle', capStyle) self.speechAndVerbosityManager.update_capitalization_style() - _settingsManager.setSetting('verbalizePunctuationStyle', punctStyle) + cthulhu.cthulhuApp.settingsManager.setSetting('verbalizePunctuationStyle', punctStyle) self.speechAndVerbosityManager.update_punctuation_level() @staticmethod diff --git a/src/cthulhu/scripts/terminal/script_utilities.py b/src/cthulhu/scripts/terminal/script_utilities.py index 1596c83..f59ef3c 100644 --- a/src/cthulhu/scripts/terminal/script_utilities.py +++ b/src/cthulhu/scripts/terminal/script_utilities.py @@ -44,7 +44,7 @@ from cthulhu.ax_object import AXObject from cthulhu.ax_text import AXText from cthulhu.ax_utilities import AXUtilities -_settingsManager = settings_manager.getManager() +_settingsManager = None # Removed - use cthulhu.cthulhuApp.settingsManager class Utilities(script_utilities.Utilities): @@ -216,7 +216,7 @@ class Utilities(script_utilities.Utilities): return False def willEchoCharacter(self, event): - if not _settingsManager.getSetting("enableEchoByCharacter"): + if not cthulhu.cthulhuApp.settingsManager.getSetting("enableEchoByCharacter"): return False if len(event.event_string) != 1 \ diff --git a/src/cthulhu/scripts/web/script.py b/src/cthulhu/scripts/web/script.py index e482f2b..0954f63 100644 --- a/src/cthulhu/scripts/web/script.py +++ b/src/cthulhu/scripts/web/script.py @@ -68,7 +68,7 @@ from .speech_generator import SpeechGenerator from .tutorial_generator import TutorialGenerator from .script_utilities import Utilities -_settingsManager = settings_manager.getManager() +_settingsManager = None # Removed - use cthulhu.cthulhuApp.settingsManager class Script(default.Script): @@ -94,12 +94,12 @@ class Script(default.Script): self._navSuspended = False self._structNavWasEnabled = None - if _settingsManager.getSetting('caretNavigationEnabled') is None: - _settingsManager.setSetting('caretNavigationEnabled', True) - if _settingsManager.getSetting('sayAllOnLoad') is None: - _settingsManager.setSetting('sayAllOnLoad', True) - if _settingsManager.getSetting('pageSummaryOnLoad') is None: - _settingsManager.setSetting('pageSummaryOnLoad', True) + if cthulhu.cthulhuApp.settingsManager.getSetting('caretNavigationEnabled') is None: + cthulhu.cthulhuApp.settingsManager.setSetting('caretNavigationEnabled', True) + if cthulhu.cthulhuApp.settingsManager.getSetting('sayAllOnLoad') is None: + cthulhu.cthulhuApp.settingsManager.setSetting('sayAllOnLoad', True) + if cthulhu.cthulhuApp.settingsManager.getSetting('pageSummaryOnLoad') is None: + cthulhu.cthulhuApp.settingsManager.setSetting('pageSummaryOnLoad', True) self._changedLinesOnlyCheckButton = None self._controlCaretNavigationCheckButton = None @@ -234,7 +234,7 @@ class Script(default.Script): self.inputEventHandlers.get("toggleLayoutModeHandler"))) - layout = _settingsManager.getSetting('keyboardLayout') + layout = cthulhu.cthulhuApp.settingsManager.getSetting('keyboardLayout') if layout == settings.GENERAL_KEYBOARD_LAYOUT_DESKTOP: key = "KP_Multiply" else: @@ -402,14 +402,14 @@ class Script(default.Script): generalAlignment.add(generalGrid) label = guilabels.USE_CARET_NAVIGATION - value = _settingsManager.getSetting('caretNavigationEnabled') + value = cthulhu.cthulhuApp.settingsManager.getSetting('caretNavigationEnabled') self._controlCaretNavigationCheckButton = \ Gtk.CheckButton.new_with_mnemonic(label) self._controlCaretNavigationCheckButton.set_active(value) generalGrid.attach(self._controlCaretNavigationCheckButton, 0, 0, 1, 1) label = guilabels.AUTO_FOCUS_MODE_CARET_NAV - value = _settingsManager.getSetting('caretNavTriggersFocusMode') + value = cthulhu.cthulhuApp.settingsManager.getSetting('caretNavTriggersFocusMode') self._autoFocusModeCaretNavCheckButton = Gtk.CheckButton.new_with_mnemonic(label) self._autoFocusModeCaretNavCheckButton.set_active(value) generalGrid.attach(self._autoFocusModeCaretNavCheckButton, 0, 1, 1, 1) @@ -422,31 +422,31 @@ class Script(default.Script): generalGrid.attach(self._structuralNavigationCheckButton, 0, 2, 1, 1) label = guilabels.AUTO_FOCUS_MODE_STRUCT_NAV - value = _settingsManager.getSetting('structNavTriggersFocusMode') + value = cthulhu.cthulhuApp.settingsManager.getSetting('structNavTriggersFocusMode') self._autoFocusModeStructNavCheckButton = Gtk.CheckButton.new_with_mnemonic(label) self._autoFocusModeStructNavCheckButton.set_active(value) generalGrid.attach(self._autoFocusModeStructNavCheckButton, 0, 3, 1, 1) label = guilabels.AUTO_FOCUS_MODE_NATIVE_NAV - value = _settingsManager.getSetting('nativeNavTriggersFocusMode') + value = cthulhu.cthulhuApp.settingsManager.getSetting('nativeNavTriggersFocusMode') self._autoFocusModeNativeNavCheckButton = Gtk.CheckButton.new_with_mnemonic(label) self._autoFocusModeNativeNavCheckButton.set_active(value) generalGrid.attach(self._autoFocusModeNativeNavCheckButton, 0, 4, 1, 1) label = guilabels.READ_PAGE_UPON_LOAD - value = _settingsManager.getSetting('sayAllOnLoad') + value = cthulhu.cthulhuApp.settingsManager.getSetting('sayAllOnLoad') self._sayAllOnLoadCheckButton = Gtk.CheckButton.new_with_mnemonic(label) self._sayAllOnLoadCheckButton.set_active(value) generalGrid.attach(self._sayAllOnLoadCheckButton, 0, 5, 1, 1) label = guilabels.PAGE_SUMMARY_UPON_LOAD - value = _settingsManager.getSetting('pageSummaryOnLoad') + value = cthulhu.cthulhuApp.settingsManager.getSetting('pageSummaryOnLoad') self._pageSummaryOnLoadCheckButton = Gtk.CheckButton.new_with_mnemonic(label) self._pageSummaryOnLoadCheckButton.set_active(value) generalGrid.attach(self._pageSummaryOnLoadCheckButton, 0, 6, 1, 1) label = guilabels.CONTENT_LAYOUT_MODE - value = _settingsManager.getSetting('layoutMode') + value = cthulhu.cthulhuApp.settingsManager.getSetting('layoutMode') self._layoutModeCheckButton = Gtk.CheckButton.new_with_mnemonic(label) self._layoutModeCheckButton.set_active(value) generalGrid.attach(self._layoutModeCheckButton, 0, 7, 1, 1) @@ -465,28 +465,28 @@ class Script(default.Script): tableAlignment.add(tableGrid) label = guilabels.TABLE_SPEAK_CELL_COORDINATES - value = _settingsManager.getSetting('speakCellCoordinates') + value = cthulhu.cthulhuApp.settingsManager.getSetting('speakCellCoordinates') self._speakCellCoordinatesCheckButton = \ Gtk.CheckButton.new_with_mnemonic(label) self._speakCellCoordinatesCheckButton.set_active(value) tableGrid.attach(self._speakCellCoordinatesCheckButton, 0, 0, 1, 1) label = guilabels.TABLE_SPEAK_CELL_SPANS - value = _settingsManager.getSetting('speakCellSpan') + value = cthulhu.cthulhuApp.settingsManager.getSetting('speakCellSpan') self._speakCellSpanCheckButton = \ Gtk.CheckButton.new_with_mnemonic(label) self._speakCellSpanCheckButton.set_active(value) tableGrid.attach(self._speakCellSpanCheckButton, 0, 1, 1, 1) label = guilabels.TABLE_ANNOUNCE_CELL_HEADER - value = _settingsManager.getSetting('speakCellHeaders') + value = cthulhu.cthulhuApp.settingsManager.getSetting('speakCellHeaders') self._speakCellHeadersCheckButton = \ Gtk.CheckButton.new_with_mnemonic(label) self._speakCellHeadersCheckButton.set_active(value) tableGrid.attach(self._speakCellHeadersCheckButton, 0, 2, 1, 1) label = guilabels.TABLE_SKIP_BLANK_CELLS - value = _settingsManager.getSetting('skipBlankCells') + value = cthulhu.cthulhuApp.settingsManager.getSetting('skipBlankCells') self._skipBlankCellsCheckButton = \ Gtk.CheckButton.new_with_mnemonic(label) self._skipBlankCellsCheckButton.set_active(value) @@ -505,7 +505,7 @@ class Script(default.Script): findGrid = Gtk.Grid() findAlignment.add(findGrid) - verbosity = _settingsManager.getSetting('findResultsVerbosity') + verbosity = cthulhu.cthulhuApp.settingsManager.getSetting('findResultsVerbosity') label = guilabels.FIND_SPEAK_RESULTS value = verbosity != settings.FIND_SPEAK_NONE @@ -530,7 +530,7 @@ class Script(default.Script): hgrid.attach(self._minimumFindLengthLabel, 0, 0, 1, 1) self._minimumFindLengthAdjustment = \ - Gtk.Adjustment(_settingsManager.getSetting( + Gtk.Adjustment(cthulhu.cthulhuApp.settingsManager.getSetting( 'findResultsMinimumLength'), 0, 20, 1) self._minimumFindLengthSpinButton = Gtk.SpinButton() self._minimumFindLengthSpinButton.set_adjustment( @@ -673,7 +673,7 @@ class Script(default.Script): self._sayAllIsInterrupted = False - sayAllStyle = _settingsManager.getSetting('sayAllStyle') + sayAllStyle = cthulhu.cthulhuApp.settingsManager.getSetting('sayAllStyle') sayAllBySentence = sayAllStyle == settings.SAYALL_STYLE_SENTENCE if offset is None: obj, characterOffset = self.utilities.getCaretContext() @@ -777,10 +777,10 @@ class Script(default.Script): start, end = selections[0] offset = max(offset, start) self.utilities.setCaretContext(obj, offset, documentFrame=document) - if end - start < _settingsManager.getSetting('findResultsMinimumLength'): + if end - start < cthulhu.cthulhuApp.settingsManager.getSetting('findResultsMinimumLength'): return - verbosity = _settingsManager.getSetting('findResultsVerbosity') + verbosity = cthulhu.cthulhuApp.settingsManager.getSetting('findResultsVerbosity') if verbosity == settings.FIND_SPEAK_NONE: return @@ -820,7 +820,7 @@ class Script(default.Script): if not self.utilities.inDocumentContent(): return super()._rewindSayAll(context, minCharCount) - if not _settingsManager.getSetting('rewindAndFastForwardInSayAll'): + if not cthulhu.cthulhuApp.settingsManager.getSetting('rewindAndFastForwardInSayAll'): return False try: @@ -839,7 +839,7 @@ class Script(default.Script): if not self.utilities.inDocumentContent(): return super()._fastForwardSayAll(context) - if not _settingsManager.getSetting('rewindAndFastForwardInSayAll'): + if not cthulhu.cthulhuApp.settingsManager.getSetting('rewindAndFastForwardInSayAll'): return False try: @@ -915,7 +915,7 @@ class Script(default.Script): debug.printMessage(debug.LEVEL_INFO, msg, True) return False - if not _settingsManager.getSetting('structNavTriggersFocusMode') \ + if not cthulhu.cthulhuApp.settingsManager.getSetting('structNavTriggersFocusMode') \ and self._lastCommandWasStructNav: msg = "WEB: Not using focus mode due to struct nav settings" debug.printMessage(debug.LEVEL_INFO, msg, True) @@ -924,14 +924,14 @@ class Script(default.Script): if prevObj and AXObject.is_dead(prevObj): prevObj = None - if not _settingsManager.getSetting('caretNavTriggersFocusMode') \ + if not cthulhu.cthulhuApp.settingsManager.getSetting('caretNavTriggersFocusMode') \ and self._lastCommandWasCaretNav \ and not self.utilities.isNavigableToolTipDescendant(prevObj): msg = "WEB: Not using focus mode due to caret nav settings" debug.printMessage(debug.LEVEL_INFO, msg, True) return False - if not _settingsManager.getSetting('nativeNavTriggersFocusMode') \ + if not cthulhu.cthulhuApp.settingsManager.getSetting('nativeNavTriggersFocusMode') \ and not (self._lastCommandWasStructNav or self._lastCommandWasCaretNav): msg = "WEB: Not changing focus/browse mode due to native nav settings" debug.printMessage(debug.LEVEL_INFO, msg, True) @@ -1087,8 +1087,8 @@ class Script(default.Script): def updateBraille(self, obj, **args): """Updates the braille display to show the given object.""" - if not _settingsManager.getSetting('enableBraille') \ - and not _settingsManager.getSetting('enableBrailleMonitor'): + if not cthulhu.cthulhuApp.settingsManager.getSetting('enableBraille') \ + and not cthulhu.cthulhuApp.settingsManager.getSetting('enableBrailleMonitor'): debug.printMessage(debug.LEVEL_INFO, "BRAILLE: disabled", True) return @@ -1130,8 +1130,8 @@ class Script(default.Script): def displayContents(self, contents, **args): """Displays contents in braille.""" - if not _settingsManager.getSetting('enableBraille') \ - and not _settingsManager.getSetting('enableBrailleMonitor'): + if not cthulhu.cthulhuApp.settingsManager.getSetting('enableBraille') \ + and not cthulhu.cthulhuApp.settingsManager.getSetting('enableBrailleMonitor'): debug.printMessage(debug.LEVEL_INFO, "BRAILLE: disabled", True) return @@ -1206,7 +1206,7 @@ class Script(default.Script): def useCaretNavigationModel(self, keyboardEvent, debugOutput=True): """Returns True if caret navigation should be used.""" - if not _settingsManager.getSetting('caretNavigationEnabled'): + if not cthulhu.cthulhuApp.settingsManager.getSetting('caretNavigationEnabled'): if debugOutput: msg = "WEB: Not using caret navigation: it's not enabled." debug.printMessage(debug.LEVEL_INFO, msg, True) @@ -1377,12 +1377,12 @@ class Script(default.Script): self.refreshKeyGrabs() def toggleLayoutMode(self, inputEvent): - layoutMode = not _settingsManager.getSetting('layoutMode') + layoutMode = not cthulhu.cthulhuApp.settingsManager.getSetting('layoutMode') if layoutMode: self.presentMessage(messages.MODE_LAYOUT) else: self.presentMessage(messages.MODE_OBJECT) - _settingsManager.setSetting('layoutMode', layoutMode) + cthulhu.cthulhuApp.settingsManager.setSetting('layoutMode', layoutMode) def togglePresentationMode(self, inputEvent, documentFrame=None): [obj, characterOffset] = self.utilities.getCaretContext(documentFrame) @@ -1414,11 +1414,11 @@ class Script(default.Script): if inputEvent is not None: return False - if _settingsManager.getSetting('roleSoundPresentation') \ + if cthulhu.cthulhuApp.settingsManager.getSetting('roleSoundPresentation') \ == settings.ROLE_SOUND_PRESENTATION_SPEECH_ONLY: return False - if not _settingsManager.getSetting('enableSound'): + if not cthulhu.cthulhuApp.settingsManager.getSetting('enableSound'): return False icon = self._getControlSoundIcon(obj) @@ -1798,7 +1798,7 @@ class Script(default.Script): tokens = ["WEB: Not presenting due to focus mode for", obj] debug.printTokens(debug.LEVEL_INFO, tokens, True) - if not _settingsManager.getSetting('onlySpeakDisplayedText') and shouldPresent: + if not cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText') and shouldPresent: if event.detail1: self.presentMessage(messages.PAGE_LOADING_START) elif AXObject.get_name(event.source): @@ -1828,7 +1828,7 @@ class Script(default.Script): debug.printMessage(debug.LEVEL_INFO, msg, True) return True - if _settingsManager.getSetting('pageSummaryOnLoad') and shouldPresent: + if cthulhu.cthulhuApp.settingsManager.getSetting('pageSummaryOnLoad') and shouldPresent: obj = obj or event.source tokens = ["WEB: Getting page summary for obj", obj] debug.printTokens(debug.LEVEL_INFO, tokens, True) @@ -1872,11 +1872,11 @@ class Script(default.Script): if self.utilities.documentFragment(event.source): msg = "WEB: Not doing SayAll due to page fragment" debug.printMessage(debug.LEVEL_INFO, msg, True) - elif not _settingsManager.getSetting('sayAllOnLoad'): + elif not cthulhu.cthulhuApp.settingsManager.getSetting('sayAllOnLoad'): msg = "WEB: Not doing SayAll due to sayAllOnLoad being False" debug.printMessage(debug.LEVEL_INFO, msg, True) self.speakContents(self.utilities.getLineContentsAtOffset(obj, offset)) - elif _settingsManager.getSetting('enableSpeech'): + elif cthulhu.cthulhuApp.settingsManager.getSetting('enableSpeech'): msg = "WEB: Doing SayAll" debug.printMessage(debug.LEVEL_INFO, msg, True) self.sayAll(None) diff --git a/src/cthulhu/scripts/web/script_utilities.py b/src/cthulhu/scripts/web/script_utilities.py index 9ebd461..efd647f 100644 --- a/src/cthulhu/scripts/web/script_utilities.py +++ b/src/cthulhu/scripts/web/script_utilities.py @@ -58,8 +58,8 @@ from cthulhu.ax_utilities import AXUtilities from cthulhu.ax_utilities_relation import AXUtilitiesRelation from cthulhu import speech_and_verbosity_manager -_scriptManager = script_manager.get_manager() -_settingsManager = settings_manager.getManager() +_scriptManager = None # Removed - use cthulhu.cthulhuApp.scriptManager +_settingsManager = None # Removed - use cthulhu.cthulhuApp.settingsManager class Utilities(script_utilities.Utilities): @@ -284,7 +284,7 @@ class Utilities(script_utilities.Utilities): # TODO - JD: Is this exception handling still needed? try: - script = _scriptManager.get_script(app, cthulhu_state.activeWindow) + script = cthulhu.cthulhuApp.scriptManager.get_script(app, cthulhu_state.activeWindow) tokens = ["WEB: Script for active Window is", script] debug.printTokens(debug.LEVEL_INFO, tokens, True) except Exception: @@ -1759,7 +1759,7 @@ class Utilities(script_utilities.Utilities): return self._currentLineContents if layoutMode is None: - layoutMode = _settingsManager.getSetting('layoutMode') or self._script.inFocusMode() + layoutMode = cthulhu.cthulhuApp.settingsManager.getSetting('layoutMode') or self._script.inFocusMode() objects = [] if offset > 0 and self.treatAsEndOfLine(obj, offset): @@ -5427,13 +5427,13 @@ class Utilities(script_utilities.Utilities): return None def handleAsLiveRegion(self, event): - if not _settingsManager.getSetting('inferLiveRegions'): + if not cthulhu.cthulhuApp.settingsManager.getSetting('inferLiveRegions'): return False if not self.isLiveRegion(event.source): return False - if not _settingsManager.getSetting('presentLiveRegionFromInactiveTab') \ + if not cthulhu.cthulhuApp.settingsManager.getSetting('presentLiveRegionFromInactiveTab') \ and self.getTopLevelDocumentForObject(event.source) != self.activeDocument(): msg = "WEB: Live region source is not in active tab." debug.printMessage(debug.LEVEL_INFO, msg, True) diff --git a/src/cthulhu/scripts/web/sound_generator.py b/src/cthulhu/scripts/web/sound_generator.py index 8c34de3..ed2213e 100644 --- a/src/cthulhu/scripts/web/sound_generator.py +++ b/src/cthulhu/scripts/web/sound_generator.py @@ -35,10 +35,11 @@ import gi gi.require_version("Atspi", "2.0") from gi.repository import Atspi +from cthulhu import cthulhu from cthulhu import settings_manager from cthulhu import sound_generator -_settingsManager = settings_manager.getManager() +_settingsManager = None # Removed - use cthulhu.cthulhuApp.settingsManager class SoundGenerator(sound_generator.SoundGenerator): @@ -49,7 +50,7 @@ class SoundGenerator(sound_generator.SoundGenerator): def _generateClickable(self, obj, **args): """Returns an array of sounds indicating obj is clickable.""" - if not _settingsManager.getSetting('playSoundForState'): + if not cthulhu.cthulhuApp.settingsManager.getSetting('playSoundForState'): return [] if not self._script.utilities.inDocumentContent(obj): @@ -70,7 +71,7 @@ class SoundGenerator(sound_generator.SoundGenerator): def _generateHasLongDesc(self, obj, **args): """Returns an array of sounds indicating obj has a longdesc.""" - if not _settingsManager.getSetting('playSoundForState'): + if not cthulhu.cthulhuApp.settingsManager.getSetting('playSoundForState'): return [] if not self._script.utilities.inDocumentContent(obj): diff --git a/src/cthulhu/scripts/web/speech_generator.py b/src/cthulhu/scripts/web/speech_generator.py index 3f0c39f..6147ddf 100644 --- a/src/cthulhu/scripts/web/speech_generator.py +++ b/src/cthulhu/scripts/web/speech_generator.py @@ -36,6 +36,7 @@ gi.require_version("Atspi", "2.0") from gi.repository import Atspi import urllib +from cthulhu import cthulhu from cthulhu import debug from cthulhu import input_event_manager from cthulhu import messages @@ -49,7 +50,7 @@ from cthulhu.ax_object import AXObject from cthulhu.ax_text import AXText from cthulhu.ax_utilities import AXUtilities -_settingsManager = settings_manager.getManager() +_settingsManager = None # Removed - use cthulhu.cthulhuApp.settingsManager class SpeechGenerator(speech_generator.SpeechGenerator): @@ -139,7 +140,7 @@ class SpeechGenerator(speech_generator.SpeechGenerator): return super()._generateAnyTextSelection(obj, **args) def _generateHasPopup(self, obj, **args): - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] if not self._script.utilities.inDocumentContent(obj): @@ -164,7 +165,7 @@ class SpeechGenerator(speech_generator.SpeechGenerator): return result def _generateClickable(self, obj, **args): - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] if not self._script.utilities.inDocumentContent(obj): @@ -185,7 +186,7 @@ class SpeechGenerator(speech_generator.SpeechGenerator): return [] def _generateDescription(self, obj, **args): - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] if not self._script.utilities.inDocumentContent(obj): @@ -218,7 +219,7 @@ class SpeechGenerator(speech_generator.SpeechGenerator): return super()._generateDescription(obj, **args) def _generateHasLongDesc(self, obj, **args): - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] if not self._script.utilities.inDocumentContent(obj): @@ -236,7 +237,7 @@ class SpeechGenerator(speech_generator.SpeechGenerator): return [] def _generateHasDetails(self, obj, **args): - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] if not self._script.utilities.inDocumentContent(obj): @@ -257,7 +258,7 @@ class SpeechGenerator(speech_generator.SpeechGenerator): return result def _generateAllDetails(self, obj, **args): - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] objs = self._script.utilities.detailsIn(obj) @@ -288,7 +289,7 @@ class SpeechGenerator(speech_generator.SpeechGenerator): return result def _generateDetailsFor(self, obj, **args): - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] if not self._script.utilities.inDocumentContent(obj): @@ -447,7 +448,7 @@ class SpeechGenerator(speech_generator.SpeechGenerator): return super()._generateLabel(obj, **args) def _generateNewNodeLevel(self, obj, **args): - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] if self._script.utilities.isTextBlockElement(obj) \ @@ -457,7 +458,7 @@ class SpeechGenerator(speech_generator.SpeechGenerator): return super()._generateNewNodeLevel(obj, **args) def _generateLeaving(self, obj, **args): - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] if not args.get('leaving'): @@ -480,8 +481,8 @@ class SpeechGenerator(speech_generator.SpeechGenerator): return [] def _generateNumberOfChildren(self, obj, **args): - if _settingsManager.getSetting('onlySpeakDisplayedText') \ - or _settingsManager.getSetting('speechVerbosityLevel') == settings.VERBOSITY_LEVEL_BRIEF: + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText') \ + or cthulhu.cthulhuApp.settingsManager.getSetting('speechVerbosityLevel') == settings.VERBOSITY_LEVEL_BRIEF: return [] # We handle things even for non-document content due to issues in @@ -538,7 +539,7 @@ class SpeechGenerator(speech_generator.SpeechGenerator): return self._generateDisplayedText(rad, **args) def _generateRoleName(self, obj, **args): - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] if not self._script.utilities.inDocumentContent(obj): @@ -554,8 +555,8 @@ class SpeechGenerator(speech_generator.SpeechGenerator): result.extend(self.voice(speech_generator.SYSTEM, obj=obj, **args)) role = args.get('role', AXObject.get_role(obj)) - roleSoundPresentation = _settingsManager.getSetting('roleSoundPresentation') - soundEnabled = _settingsManager.getSetting('enableSound') + roleSoundPresentation = cthulhu.cthulhuApp.settingsManager.getSetting('roleSoundPresentation') + soundEnabled = cthulhu.cthulhuApp.settingsManager.getSetting('enableSound') roleSoundIcon = None if roleSoundPresentation != settings.ROLE_SOUND_PRESENTATION_SPEECH_ONLY \ and soundEnabled: @@ -677,7 +678,7 @@ class SpeechGenerator(speech_generator.SpeechGenerator): if roleSoundPresentation == settings.ROLE_SOUND_PRESENTATION_SPEECH_ONLY: return result - if not _settingsManager.getSetting('enableSound'): + if not cthulhu.cthulhuApp.settingsManager.getSetting('enableSound'): return result if roleSoundPresentation == settings.ROLE_SOUND_PRESENTATION_SOUND_ONLY: @@ -775,7 +776,7 @@ class SpeechGenerator(speech_generator.SpeechGenerator): if not self._script.inFocusMode(): return result - if _settingsManager.getSetting('speakCellCoordinates'): + if cthulhu.cthulhuApp.settingsManager.getSetting('speakCellCoordinates'): label = self._script.utilities.labelForCellCoordinates(obj) if label: result.append(label) @@ -875,7 +876,7 @@ class SpeechGenerator(speech_generator.SpeechGenerator): if not result: if self._script.inSayAll(treatInterruptedAsIn=False) \ - or not _settingsManager.getSetting('speakBlankLines') \ + or not cthulhu.cthulhuApp.settingsManager.getSetting('speakBlankLines') \ or args.get('formatType') == 'ancestor': string = "" else: diff --git a/src/cthulhu/settings.py b/src/cthulhu/settings.py index d36bed5..26b9147 100644 --- a/src/cthulhu/settings.py +++ b/src/cthulhu/settings.py @@ -440,7 +440,6 @@ enabledBrailledTextAttributes = \ # and braille bindings. Unsupported and undocumented for now. # Use at your own risk. # -keyBindingsMap = {} brailleBindingsMap = {} # TODO - JD: Is this still needed now that AT-SPI has its own timeout? diff --git a/src/cthulhu/settings_manager.py b/src/cthulhu/settings_manager.py index 7c368ab..40f9b88 100644 --- a/src/cthulhu/settings_manager.py +++ b/src/cthulhu/settings_manager.py @@ -34,51 +34,42 @@ __license__ = "LGPL" import copy import importlib +import json import os from gi.repository import Gio, GLib from . import debug from . import cthulhu_i18n -from . import script_manager from . import settings from . import pronunciation_dict from .acss import ACSS from .ax_object import AXObject from .keybindings import KeyBinding -try: - _proxy = Gio.DBusProxy.new_for_bus_sync( - Gio.BusType.SESSION, - Gio.DBusProxyFlags.NONE, - None, - 'org.a11y.Bus', - '/org/a11y/bus', - 'org.freedesktop.DBus.Properties', - None) -except Exception: - _proxy = None - -_scriptManager = script_manager.get_manager() +# Removed global cthulhuApp.scriptManager declaration. +# Note: Do not import cthulhu module here to avoid circular import class SettingsManager(object): """Settings backend manager. This class manages cthulhu user's settings using different backends""" - _instance = None - def __new__(cls, *args, **kwargs): - if '__instance' not in vars(cls): - cls.__instance = object.__new__(cls, *args, **kwargs) - return cls.__instance - - def __init__(self, backend='json'): - """Initialize a SettingsManager Object. - If backend isn't defined then uses default backend, in this - case json-backend. - backend parameter can use the follow values: - backend='json' - """ + def __init__(self, app, backend='json'): # Modified signature debug.printMessage(debug.LEVEL_INFO, 'SETTINGS MANAGER: Initializing', True) + self.app = app # Store app instance + + # Move _proxy initialization here + try: + self._proxy = Gio.DBusProxy.new_for_bus_sync( + Gio.BusType.SESSION, + Gio.DBusProxyFlags.NONE, + None, + 'org.a11y.Bus', + '/org/a11y/bus', + 'org.freedesktop.DBus.Properties', + None) + except Exception: + self._proxy = None self.backendModule = None self._backend = None @@ -238,6 +229,64 @@ class SettingsManager(object): except Exception: pass self.defaultGeneral[key] = value + default_active_plugins = copy.deepcopy(self.defaultGeneral.get("activePlugins")) + overrides = self._load_default_general_overrides() + if overrides: + for key, value in overrides.items(): + if key not in self.defaultGeneral: + continue + if key == "activePlugins": + continue + self.defaultGeneral[key] = value + if default_active_plugins is not None: + self.defaultGeneral["activePlugins"] = default_active_plugins + + def _load_default_general_overrides(self): + if not self._prefsDir: + return {} + + settings_path = os.path.join(self._prefsDir, "user-settings.conf") + if not os.path.exists(settings_path): + return {} + + try: + with open(settings_path, "r", encoding="utf-8") as settings_file: + prefs = json.load(settings_file) + except Exception as error: + msg = f"SETTINGS MANAGER: Unable to read default settings from {settings_path}: {error}" + debug.printMessage(debug.LEVEL_WARNING, msg, True) + return {} + + general = prefs.get("general") + if not isinstance(general, dict): + return {} + + if hasattr(self._backend, "_migrateSettings"): + try: + general = self._backend._migrateSettings(dict(general)) + except Exception as error: + msg = f"SETTINGS MANAGER: Unable to migrate default settings: {error}" + debug.printMessage(debug.LEVEL_WARNING, msg, True) + general = dict(general) + else: + general = dict(general) + + voices = general.get("voices") + if isinstance(voices, dict): + converted_voices = {} + for voice_type, voice_def in voices.items(): + try: + converted_voices[voice_type] = ACSS(voice_def) + except Exception: + converted_voices[voice_type] = voice_def + general["voices"] = converted_voices + + return general + + def getDefaultSetting(self, settingName): + if settingName in self.defaultGeneral: + return self.defaultGeneral.get(settingName) + return getattr(settings, settingName, None) def _getCustomizedSettings(self): if self._customizationCompleted: @@ -372,11 +421,11 @@ class SettingsManager(object): debug.printMessage(debug.LEVEL_INFO, msg, True) msg = 'SETTINGS MANAGER: Accessibility enabled: ' - if not _proxy: + if not self._proxy: rv = False msg += 'Error (no proxy)' else: - rv = _proxy.Get('(ss)', 'org.a11y.Status', 'IsEnabled') + rv = self._proxy.Get('(ss)', 'org.a11y.Status', 'IsEnabled') msg += str(rv) debug.printMessage(debug.LEVEL_INFO, msg, True) @@ -386,13 +435,13 @@ class SettingsManager(object): msg = f'SETTINGS MANAGER: Attempting to set accessibility to {enable}.' debug.printMessage(debug.LEVEL_INFO, msg, True) - if not _proxy: + if not self._proxy: msg = 'SETTINGS MANAGER: Error (no proxy)' debug.printMessage(debug.LEVEL_INFO, msg, True) return False vEnable = GLib.Variant('b', enable) - _proxy.Set('(ssv)', 'org.a11y.Status', 'IsEnabled', vEnable) + self._proxy.Set('(ssv)', 'org.a11y.Status', 'IsEnabled', vEnable) msg = f'SETTINGS MANAGER: Finished setting accessibility to {enable}.' debug.printMessage(debug.LEVEL_INFO, msg, True) @@ -404,11 +453,11 @@ class SettingsManager(object): msg = 'SETTINGS MANAGER: Is screen reader service enabled? ' - if not _proxy: + if not self._proxy: rv = False msg += 'Error (no proxy)' else: - rv = _proxy.Get('(ss)', 'org.a11y.Status', 'ScreenReaderEnabled') + rv = self._proxy.Get('(ss)', 'org.a11y.Status', 'ScreenReaderEnabled') msg += str(rv) debug.printMessage(debug.LEVEL_INFO, msg, True) @@ -695,7 +744,24 @@ class SettingsManager(object): self._setPronunciationsRuntime(self.pronunciations) script.keyBindings = self.overrideKeyBindings(script, script.getKeyBindings()) -_manager = SettingsManager() +_managerInstance = None def getManager(): - return _manager + """Get the settings manager instance. Compatibility function. + + This function provides backward compatibility for existing code that uses + settings_manager.getManager(). Returns None during initial import phase, + and the actual instance once cthulhuApp is initialized. + + Code should check for None or call this lazily when actually needed. + """ + global _managerInstance + if _managerInstance is None: + try: + from . import cthulhu + _managerInstance = cthulhu.cthulhuApp.settingsManager + except (ImportError, AttributeError): + # During import phase, cthulhuApp may not exist yet + pass + return _managerInstance + diff --git a/src/cthulhu/signal_manager.py b/src/cthulhu/signal_manager.py index 22ad931..51e61c2 100644 --- a/src/cthulhu/signal_manager.py +++ b/src/cthulhu/signal_manager.py @@ -74,10 +74,10 @@ class SignalManager(): if resourceContext: resourceContext.removeSubscriptionByFunction(function) return ok - def emitSignal(self, signalName): - # emit an signal + def emitSignal(self, signalName, *args): + # emit a signal with optional arguments try: - self.app.emit(signalName) + self.app.emit(signalName, *args) print('after Emit Signal: {}'.format(signalName)) except: print('Signal "{}" does not exist.'.format(signalName)) diff --git a/src/cthulhu/sound_generator.py b/src/cthulhu/sound_generator.py index cba7b22..9a3727f 100644 --- a/src/cthulhu/sound_generator.py +++ b/src/cthulhu/sound_generator.py @@ -37,12 +37,13 @@ from gi.repository import Atspi import os +from . import cthulhu # Need access to cthulhuApp from . import generator from . import settings_manager from .ax_object import AXObject from .ax_utilities import AXUtilities -_settingsManager = settings_manager.getManager() +_settingsManager = None # Removed - use cthulhu.cthulhuApp.settingsManager METHOD_PREFIX = "_generate" @@ -79,7 +80,7 @@ class Tone: def __init__(self, duration, frequency, volumeMultiplier=1, wave=SINE_WAVE): self.duration = duration self.frequency = min(max(0, frequency), 20000) - self.volume = _settingsManager.getSetting('soundVolume') * volumeMultiplier + self.volume = cthulhu.cthulhuApp.settingsManager.getSetting('soundVolume') * volumeMultiplier self.wave = wave def __str__(self): @@ -91,7 +92,7 @@ class SoundGenerator(generator.Generator): def __init__(self, script): super().__init__(script, 'sound') - self._sounds = os.path.join(_settingsManager.getPrefsDir(), 'sounds') + self._sounds = os.path.join(cthulhu.cthulhuApp.settingsManager.getPrefsDir(), 'sounds') def _convertFilenameToIcon(self, filename): icon = Icon(self._sounds, filename) @@ -114,7 +115,7 @@ class SoundGenerator(generator.Generator): def _generateAvailability(self, obj, **args): """Returns an array of sounds indicating obj is grayed out.""" - if not _settingsManager.getSetting('playSoundForState'): + if not cthulhu.cthulhuApp.settingsManager.getSetting('playSoundForState'): return [] filenames = super()._generateAvailability(obj, **args) @@ -127,7 +128,7 @@ class SoundGenerator(generator.Generator): def _generateCheckedState(self, obj, **args): """Returns an array of sounds indicating the checked state of obj.""" - if not _settingsManager.getSetting('playSoundForState'): + if not cthulhu.cthulhuApp.settingsManager.getSetting('playSoundForState'): return [] filenames = super()._generateCheckedState(obj, **args) @@ -140,7 +141,7 @@ class SoundGenerator(generator.Generator): def _generateClickable(self, obj, **args): """Returns an array of sounds indicating obj is clickable.""" - if not _settingsManager.getSetting('playSoundForState'): + if not cthulhu.cthulhuApp.settingsManager.getSetting('playSoundForState'): return [] filenames = super()._generateClickable(obj, **args) @@ -153,7 +154,7 @@ class SoundGenerator(generator.Generator): def _generateExpandableState(self, obj, **args): """Returns an array of sounds indicating the expanded state of obj.""" - if not _settingsManager.getSetting('playSoundForState'): + if not cthulhu.cthulhuApp.settingsManager.getSetting('playSoundForState'): return [] filenames = super()._generateExpandableState(obj, **args) @@ -166,7 +167,7 @@ class SoundGenerator(generator.Generator): def _generateHasLongDesc(self, obj, **args): """Returns an array of sounds indicating obj has a longdesc.""" - if not _settingsManager.getSetting('playSoundForState'): + if not cthulhu.cthulhuApp.settingsManager.getSetting('playSoundForState'): return [] filenames = super()._generateHasLongDesc(obj, **args) @@ -179,7 +180,7 @@ class SoundGenerator(generator.Generator): def _generateMenuItemCheckedState(self, obj, **args): """Returns an array of sounds indicating the checked state of obj.""" - if not _settingsManager.getSetting('playSoundForState'): + if not cthulhu.cthulhuApp.settingsManager.getSetting('playSoundForState'): return [] filenames = super()._generateMenuItemCheckedState(obj, **args) @@ -192,7 +193,7 @@ class SoundGenerator(generator.Generator): def _generateMultiselectableState(self, obj, **args): """Returns an array of sounds indicating obj is multiselectable.""" - if not _settingsManager.getSetting('playSoundForState'): + if not cthulhu.cthulhuApp.settingsManager.getSetting('playSoundForState'): return [] filenames = super()._generateMultiselectableState(obj, **args) @@ -205,7 +206,7 @@ class SoundGenerator(generator.Generator): def _generateRadioState(self, obj, **args): """Returns an array of sounds indicating the selected state of obj.""" - if not _settingsManager.getSetting('playSoundForState'): + if not cthulhu.cthulhuApp.settingsManager.getSetting('playSoundForState'): return [] filenames = super()._generateRadioState(obj, **args) @@ -218,7 +219,7 @@ class SoundGenerator(generator.Generator): def _generateReadOnly(self, obj, **args): """Returns an array of sounds indicating obj is read only.""" - if not _settingsManager.getSetting('playSoundForState'): + if not cthulhu.cthulhuApp.settingsManager.getSetting('playSoundForState'): return [] filenames = super()._generateReadOnly(obj, **args) @@ -231,7 +232,7 @@ class SoundGenerator(generator.Generator): def _generateRequired(self, obj, **args): """Returns an array of sounds indicating obj is required.""" - if not _settingsManager.getSetting('playSoundForState'): + if not cthulhu.cthulhuApp.settingsManager.getSetting('playSoundForState'): return [] filenames = super()._generateRequired(obj, **args) @@ -244,7 +245,7 @@ class SoundGenerator(generator.Generator): def _generateSwitchState(self, obj, **args): """Returns an array of sounds indicating the on/off state of obj.""" - if not _settingsManager.getSetting('playSoundForState'): + if not cthulhu.cthulhuApp.settingsManager.getSetting('playSoundForState'): return [] filenames = super()._generateSwitchState(obj, **args) @@ -257,7 +258,7 @@ class SoundGenerator(generator.Generator): def _generateToggleState(self, obj, **args): """Returns an array of sounds indicating the toggled state of obj.""" - if not _settingsManager.getSetting('playSoundForState'): + if not cthulhu.cthulhuApp.settingsManager.getSetting('playSoundForState'): return [] filenames = super()._generateToggleState(obj, **args) @@ -270,7 +271,7 @@ class SoundGenerator(generator.Generator): def _generateVisitedState(self, obj, **args): """Returns an array of sounds indicating the visited state of obj.""" - if not _settingsManager.getSetting('playSoundForState'): + if not cthulhu.cthulhuApp.settingsManager.getSetting('playSoundForState'): return [] if not args.get('mode', None): @@ -294,7 +295,7 @@ class SoundGenerator(generator.Generator): def _generatePercentage(self, obj, **args): """Returns an array of sounds reflecting the percentage of obj.""" - if not _settingsManager.getSetting('playSoundForValue'): + if not cthulhu.cthulhuApp.settingsManager.getSetting('playSoundForValue'): return [] percent = self._script.utilities.getValueAsPercent(obj) @@ -309,7 +310,7 @@ class SoundGenerator(generator.Generator): if args.get('isProgressBarUpdate'): if not self._shouldPresentProgressBarUpdate(obj, **args): return [] - elif not _settingsManager.getSetting('playSoundForValue'): + elif not cthulhu.cthulhuApp.settingsManager.getSetting('playSoundForValue'): return [] percent = self._script.utilities.getValueAsPercent(obj) @@ -334,14 +335,14 @@ class SoundGenerator(generator.Generator): return [Tone(duration, frequency, volumeMultiplier, Tone.SINE_WAVE)] def _getProgressBarUpdateInterval(self): - interval = _settingsManager.getSetting('progressBarBeepInterval') + interval = cthulhu.cthulhuApp.settingsManager.getSetting('progressBarBeepInterval') if interval is None: return super()._getProgressBarUpdateInterval() return int(interval) def _shouldPresentProgressBarUpdate(self, obj, **args): - if not _settingsManager.getSetting('beepProgressBarUpdates'): + if not cthulhu.cthulhuApp.settingsManager.getSetting('beepProgressBarUpdates'): return False return super()._shouldPresentProgressBarUpdate(obj, **args) @@ -355,7 +356,7 @@ class SoundGenerator(generator.Generator): def _generatePositionInSet(self, obj, **args): """Returns an array of sounds reflecting the set position of obj.""" - if not _settingsManager.getSetting('playSoundForPositionInSet'): + if not cthulhu.cthulhuApp.settingsManager.getSetting('playSoundForPositionInSet'): return [] # TODO: Implement the result. @@ -367,7 +368,7 @@ class SoundGenerator(generator.Generator): def _generateRoleName(self, obj, **args): """Returns an array of sounds indicating the role of obj.""" - if not _settingsManager.getSetting('playSoundForRole'): + if not cthulhu.cthulhuApp.settingsManager.getSetting('playSoundForRole'): return [] role = args.get('role', AXObject.get_role(obj)) diff --git a/src/cthulhu/sound_theme_manager.py b/src/cthulhu/sound_theme_manager.py index 912f0f5..9c9680c 100644 --- a/src/cthulhu/sound_theme_manager.py +++ b/src/cthulhu/sound_theme_manager.py @@ -41,12 +41,9 @@ from gi.repository import GLib from gi.repository import Atspi from . import debug -from . import settings_manager from . import sound from .sound_generator import Icon -_settingsManager = settings_manager.getManager() - # Sound event constants - add new events here for easy extensibility SOUND_FOCUS_MODE = "focus_mode" SOUND_BROWSE_MODE = "browse_mode" @@ -84,18 +81,8 @@ THEME_NONE = "none" class SoundThemeManager: """Manages sound themes for Cthulhu.""" - _instance = None - - def __new__(cls): - if cls._instance is None: - cls._instance = super().__new__(cls) - cls._instance._initialized = False - return cls._instance - - def __init__(self): - if self._initialized: - return - self._initialized = True + def __init__(self, app): + self.app = app self._systemSoundsDir = None self._userSoundsDir = None @@ -283,7 +270,7 @@ class SoundThemeManager: def getRoleSoundIcon(self, role, themeName=None): """Return an Icon for the role sound from the current theme, if any.""" - themeName = themeName or _settingsManager.getSetting('soundTheme') or 'default' + themeName = themeName or self.app.getSettingsManager().getSetting('soundTheme') or 'default' if themeName == THEME_NONE: return None @@ -296,7 +283,7 @@ class SoundThemeManager: def getRoleStateSoundIcon(self, role, stateKey, themeName=None): """Return an Icon for the role/state sound from the current theme, if any.""" - themeName = themeName or _settingsManager.getSetting('soundTheme') or 'default' + themeName = themeName or self.app.getSettingsManager().getSetting('soundTheme') or 'default' if themeName == THEME_NONE: return None @@ -320,10 +307,10 @@ class SoundThemeManager: Returns: True if sound was played, False otherwise """ - if requireSoundSetting and not _settingsManager.getSetting('enableSound'): + if requireSoundSetting and not self.app.getSettingsManager().getSetting('enableSound'): return False - themeName = _settingsManager.getSetting('soundTheme') + themeName = self.app.getSettingsManager().getSetting('soundTheme') if not themeName: themeName = 'default' @@ -398,13 +385,14 @@ class SoundThemeManager: requireSoundSetting=True ) - _manager = None - - def getManager(): - """Get the singleton SoundThemeManager instance.""" + """Returns the Sound Theme Manager""" + global _manager if _manager is None: - _manager = SoundThemeManager() + from . import cthulhu + _manager = SoundThemeManager(cthulhu.cthulhuApp) return _manager + + diff --git a/src/cthulhu/speech.py b/src/cthulhu/speech.py index 44aa89b..b4d3248 100644 --- a/src/cthulhu/speech.py +++ b/src/cthulhu/speech.py @@ -46,8 +46,17 @@ from .speechserver import VoiceFamily from .acss import ACSS from . import speech_history -_logger = logger.getLogger() -log = _logger.newLog("speech") +# Lazy initialization to avoid circular imports +_logger = None +log = None + +def _ensureLogger(): + """Ensure logger is initialized.""" + global _logger, log + if _logger is None: + from . import cthulhu + _logger = cthulhu.cthulhuApp.logger + log = _logger.newLog("speech") # The speech server to use for all speech operations. # @@ -152,6 +161,7 @@ def __resolveACSS(acss=None): return ACSS(voices[settings.DEFAULT_VOICE]) def sayAll(utteranceIterator, progressCallback): + _ensureLogger() if settings.silenceSpeech: return if _speechserver: @@ -181,6 +191,8 @@ def sayAll(utteranceIterator, progressCallback): def _speak(text, acss, interrupt): """Speaks the individual string using the given ACSS.""" + _ensureLogger() + # Block speech for applications in sleep mode, except sleep mode status messages from . import cthulhu_state from . import sleep_mode_manager @@ -333,6 +345,8 @@ def speakKeyEvent(event, acss=None): - event: input_event.KeyboardEvent to speak. """ + _ensureLogger() + if settings.silenceSpeech: return @@ -364,6 +378,9 @@ def speakCharacter(character, acss=None): used to augment/override the default voice settings. """ + + _ensureLogger() + if settings.silenceSpeech: return diff --git a/src/cthulhu/speech_and_verbosity_manager.py b/src/cthulhu/speech_and_verbosity_manager.py index 2a54002..3feb57a 100644 --- a/src/cthulhu/speech_and_verbosity_manager.py +++ b/src/cthulhu/speech_and_verbosity_manager.py @@ -33,6 +33,7 @@ __copyright__ = "Copyright (c) 2005-2008 Sun Microsystems Inc." \ __license__ = "LGPL" from . import cmdnames +from . import cthulhu from . import dbus_service from . import debug from . import input_event @@ -45,12 +46,13 @@ from . import settings from . import settings_manager from . import speech -_settings_manager = settings_manager.getManager() +# Removed global _settings_manager class SpeechAndVerbosityManager: """Configures speech and verbosity settings.""" - def __init__(self): + def __init__(self, app): + self.app = app self._speech_settings_order = [ "rate", "pitch", @@ -740,12 +742,12 @@ class SpeechAndVerbosityManager: for key in settings.userCustomizableSettings: general[key] = getattr(settings, key) - current_profile = _settings_manager.profile - pronunciations = _settings_manager.getPronunciations(current_profile) - keybindings = _settings_manager.getKeybindings(current_profile) + current_profile = self.app.getSettingsManager().profile + pronunciations = self.app.getSettingsManager().getPronunciations(current_profile) + keybindings = self.app.getSettingsManager().getKeybindings(current_profile) - default_script = script_manager.get_manager().get_default_script() - _settings_manager.saveSettings(default_script, + default_script = self.app.getScriptManager().get_default_script() + self.app.getSettingsManager().saveSettings(default_script, general, pronunciations, keybindings) @@ -938,13 +940,13 @@ class SpeechAndVerbosityManager: @dbus_service.getter def get_speech_is_muted(self) -> bool: """Returns whether speech output is temporarily muted.""" - return _settings_manager.getSetting('silenceSpeech') + return self.app.getSettingsManager().getSetting('silenceSpeech') @dbus_service.setter def set_speech_is_muted(self, value: bool) -> bool: """Sets whether speech output is temporarily muted.""" try: - _settings_manager.setSetting('silenceSpeech', value) + cthulhu.cthulhuApp.settingsManager.setSetting('silenceSpeech', value) return True except Exception as e: debug.printMessage(debug.LEVEL_WARNING, f"Error setting speech mute: {e}", True) @@ -953,7 +955,7 @@ class SpeechAndVerbosityManager: @dbus_service.getter def get_verbosity_level(self) -> str: """Returns the current speech verbosity level.""" - level = _settings_manager.getSetting('speechVerbosityLevel') + level = cthulhu.cthulhuApp.settingsManager.getSetting('speechVerbosityLevel') if level == settings.VERBOSITY_LEVEL_BRIEF: return "brief" else: @@ -964,9 +966,9 @@ class SpeechAndVerbosityManager: """Sets the speech verbosity level.""" try: if value.lower() == "brief": - _settings_manager.setSetting('speechVerbosityLevel', settings.VERBOSITY_LEVEL_BRIEF) + cthulhu.cthulhuApp.settingsManager.setSetting('speechVerbosityLevel', settings.VERBOSITY_LEVEL_BRIEF) elif value.lower() == "verbose": - _settings_manager.setSetting('speechVerbosityLevel', settings.VERBOSITY_LEVEL_VERBOSE) + cthulhu.cthulhuApp.settingsManager.setSetting('speechVerbosityLevel', settings.VERBOSITY_LEVEL_VERBOSE) else: return False return True @@ -977,13 +979,13 @@ class SpeechAndVerbosityManager: @dbus_service.getter def get_speak_numbers_as_digits(self) -> bool: """Returns whether numbers are spoken as digits.""" - return _settings_manager.getSetting('speakNumbersAsDigits') + return cthulhu.cthulhuApp.settingsManager.getSetting('speakNumbersAsDigits') @dbus_service.setter def set_speak_numbers_as_digits(self, value: bool) -> bool: """Sets whether numbers are spoken as digits.""" try: - _settings_manager.setSetting('speakNumbersAsDigits', value) + cthulhu.cthulhuApp.settingsManager.setSetting('speakNumbersAsDigits', value) return True except Exception as e: debug.printMessage(debug.LEVEL_WARNING, f"Error setting speak numbers as digits: {e}", True) @@ -992,13 +994,13 @@ class SpeechAndVerbosityManager: @dbus_service.getter def get_only_speak_displayed_text(self) -> bool: """Returns whether only displayed text should be spoken.""" - return _settings_manager.getSetting('onlySpeakDisplayedText') + return cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText') @dbus_service.setter def set_only_speak_displayed_text(self, value: bool) -> bool: """Sets whether only displayed text should be spoken.""" try: - _settings_manager.setSetting('onlySpeakDisplayedText', value) + cthulhu.cthulhuApp.settingsManager.setSetting('onlySpeakDisplayedText', value) return True except Exception as e: debug.printMessage(debug.LEVEL_WARNING, f"Error setting only speak displayed text: {e}", True) @@ -1007,11 +1009,10 @@ class SpeechAndVerbosityManager: @dbus_service.getter def get_speak_indentation_and_justification(self) -> bool: """Returns whether speaking of indentation and justification is enabled.""" - return _settings_manager.getSetting('enableSpeechIndentation') + return cthulhu.cthulhuApp.settingsManager.getSetting('enableSpeechIndentation') - @staticmethod - def _sync_indentation_presentation_mode(enable_speech): - mode = _settings_manager.getSetting('indentationPresentationMode') \ + def _sync_indentation_presentation_mode(self, enable_speech): + mode = self.app.getSettingsManager().getSetting('indentationPresentationMode') \ or settings.indentationPresentationMode if enable_speech: if mode == settings.INDENTATION_PRESENTATION_OFF: @@ -1024,13 +1025,13 @@ class SpeechAndVerbosityManager: elif mode == settings.INDENTATION_PRESENTATION_SPEECH_AND_BEEPS: mode = settings.INDENTATION_PRESENTATION_BEEPS - _settings_manager.setSetting('indentationPresentationMode', mode) + self.app.getSettingsManager().setSetting('indentationPresentationMode', mode) @dbus_service.setter def set_speak_indentation_and_justification(self, value: bool) -> bool: """Sets whether speaking of indentation and justification is enabled.""" try: - _settings_manager.setSetting('enableSpeechIndentation', value) + cthulhu.cthulhuApp.settingsManager.setSetting('enableSpeechIndentation', value) self._sync_indentation_presentation_mode(value) return True except Exception as e: @@ -1040,7 +1041,7 @@ class SpeechAndVerbosityManager: @dbus_service.getter def get_punctuation_level(self) -> str: """Returns the current punctuation level.""" - level = _settings_manager.getSetting('verbalizePunctuationStyle') + level = cthulhu.cthulhuApp.settingsManager.getSetting('verbalizePunctuationStyle') if level == settings.PUNCTUATION_STYLE_NONE: return "none" elif level == settings.PUNCTUATION_STYLE_SOME: @@ -1058,13 +1059,13 @@ class SpeechAndVerbosityManager: try: value_lower = value.lower() if value_lower == "none": - _settings_manager.setSetting('verbalizePunctuationStyle', settings.PUNCTUATION_STYLE_NONE) + cthulhu.cthulhuApp.settingsManager.setSetting('verbalizePunctuationStyle', settings.PUNCTUATION_STYLE_NONE) elif value_lower == "some": - _settings_manager.setSetting('verbalizePunctuationStyle', settings.PUNCTUATION_STYLE_SOME) + cthulhu.cthulhuApp.settingsManager.setSetting('verbalizePunctuationStyle', settings.PUNCTUATION_STYLE_SOME) elif value_lower == "most": - _settings_manager.setSetting('verbalizePunctuationStyle', settings.PUNCTUATION_STYLE_MOST) + cthulhu.cthulhuApp.settingsManager.setSetting('verbalizePunctuationStyle', settings.PUNCTUATION_STYLE_MOST) elif value_lower == "all": - _settings_manager.setSetting('verbalizePunctuationStyle', settings.PUNCTUATION_STYLE_ALL) + cthulhu.cthulhuApp.settingsManager.setSetting('verbalizePunctuationStyle', settings.PUNCTUATION_STYLE_ALL) else: return False return True @@ -1075,7 +1076,7 @@ class SpeechAndVerbosityManager: @dbus_service.getter def get_capitalization_style(self) -> str: """Returns the current capitalization style.""" - style = _settings_manager.getSetting('capitalizationStyle') + style = cthulhu.cthulhuApp.settingsManager.getSetting('capitalizationStyle') if style == settings.CAPITALIZATION_STYLE_NONE: return "none" elif style == settings.CAPITALIZATION_STYLE_ICON: @@ -1091,11 +1092,11 @@ class SpeechAndVerbosityManager: try: value_lower = value.lower() if value_lower == "none": - _settings_manager.setSetting('capitalizationStyle', settings.CAPITALIZATION_STYLE_NONE) + cthulhu.cthulhuApp.settingsManager.setSetting('capitalizationStyle', settings.CAPITALIZATION_STYLE_NONE) elif value_lower == "icon": - _settings_manager.setSetting('capitalizationStyle', settings.CAPITALIZATION_STYLE_ICON) + cthulhu.cthulhuApp.settingsManager.setSetting('capitalizationStyle', settings.CAPITALIZATION_STYLE_ICON) elif value_lower == "spell": - _settings_manager.setSetting('capitalizationStyle', settings.CAPITALIZATION_STYLE_SPELL) + cthulhu.cthulhuApp.settingsManager.setSetting('capitalizationStyle', settings.CAPITALIZATION_STYLE_SPELL) else: return False self.update_capitalization_style() @@ -1111,13 +1112,13 @@ class SpeechAndVerbosityManager: @dbus_service.getter def get_speak_misspelled_indicator(self) -> bool: """Returns whether the misspelled indicator is spoken.""" - return _settings_manager.getSetting('speakMisspelledIndicator') + return cthulhu.cthulhuApp.settingsManager.getSetting('speakMisspelledIndicator') @dbus_service.setter def set_speak_misspelled_indicator(self, value: bool) -> bool: """Sets whether the misspelled indicator is spoken.""" try: - _settings_manager.setSetting('speakMisspelledIndicator', value) + cthulhu.cthulhuApp.settingsManager.setSetting('speakMisspelledIndicator', value) return True except Exception as e: debug.printMessage(debug.LEVEL_WARNING, f"Error setting speak misspelled indicator: {e}", True) @@ -1126,13 +1127,13 @@ class SpeechAndVerbosityManager: @dbus_service.getter def get_speak_description(self) -> bool: """Returns whether object descriptions are spoken.""" - return _settings_manager.getSetting('speakDescription') + return cthulhu.cthulhuApp.settingsManager.getSetting('speakDescription') @dbus_service.setter def set_speak_description(self, value: bool) -> bool: """Sets whether object descriptions are spoken.""" try: - _settings_manager.setSetting('speakDescription', value) + cthulhu.cthulhuApp.settingsManager.setSetting('speakDescription', value) return True except Exception as e: debug.printMessage(debug.LEVEL_WARNING, f"Error setting speak description: {e}", True) @@ -1141,13 +1142,13 @@ class SpeechAndVerbosityManager: @dbus_service.getter def get_speak_position_in_set(self) -> bool: """Returns whether the position and set size of objects are spoken.""" - return _settings_manager.getSetting('enablePositionSpeaking') + return cthulhu.cthulhuApp.settingsManager.getSetting('enablePositionSpeaking') @dbus_service.setter def set_speak_position_in_set(self, value: bool) -> bool: """Sets whether the position and set size of objects are spoken.""" try: - _settings_manager.setSetting('enablePositionSpeaking', value) + cthulhu.cthulhuApp.settingsManager.setSetting('enablePositionSpeaking', value) return True except Exception as e: debug.printMessage(debug.LEVEL_WARNING, f"Error setting speak position in set: {e}", True) @@ -1156,13 +1157,13 @@ class SpeechAndVerbosityManager: @dbus_service.getter def get_speak_widget_mnemonic(self) -> bool: """Returns whether widget mnemonics are spoken.""" - return _settings_manager.getSetting('enableMnemonicSpeaking') + return cthulhu.cthulhuApp.settingsManager.getSetting('enableMnemonicSpeaking') @dbus_service.setter def set_speak_widget_mnemonic(self, value: bool) -> bool: """Sets whether widget mnemonics are spoken.""" try: - _settings_manager.setSetting('enableMnemonicSpeaking', value) + cthulhu.cthulhuApp.settingsManager.setSetting('enableMnemonicSpeaking', value) return True except Exception as e: debug.printMessage(debug.LEVEL_WARNING, f"Error setting speak widget mnemonic: {e}", True) @@ -1171,13 +1172,13 @@ class SpeechAndVerbosityManager: @dbus_service.getter def get_speak_tutorial_messages(self) -> bool: """Returns whether tutorial messages are spoken.""" - return _settings_manager.getSetting('enableTutorialMessages') + return cthulhu.cthulhuApp.settingsManager.getSetting('enableTutorialMessages') @dbus_service.setter def set_speak_tutorial_messages(self, value: bool) -> bool: """Sets whether tutorial messages are spoken.""" try: - _settings_manager.setSetting('enableTutorialMessages', value) + cthulhu.cthulhuApp.settingsManager.setSetting('enableTutorialMessages', value) return True except Exception as e: debug.printMessage(debug.LEVEL_WARNING, f"Error setting speak tutorial messages: {e}", True) @@ -1186,13 +1187,13 @@ class SpeechAndVerbosityManager: @dbus_service.getter def get_insert_pauses_between_utterances(self) -> bool: """Returns whether pauses are inserted between utterances, e.g. between name and role.""" - return _settings_manager.getSetting('enablePauseBreaks') + return cthulhu.cthulhuApp.settingsManager.getSetting('enablePauseBreaks') @dbus_service.setter def set_insert_pauses_between_utterances(self, value: bool) -> bool: """Sets whether pauses are inserted between utterances, e.g. between name and role.""" try: - _settings_manager.setSetting('enablePauseBreaks', value) + cthulhu.cthulhuApp.settingsManager.setSetting('enablePauseBreaks', value) return True except Exception as e: debug.printMessage(debug.LEVEL_WARNING, f"Error setting insert pauses: {e}", True) @@ -1201,13 +1202,13 @@ class SpeechAndVerbosityManager: @dbus_service.getter def get_repeated_character_limit(self) -> int: """Returns the count at which repeated, non-alphanumeric symbols will be described.""" - return _settings_manager.getSetting('repeatCharacterLimit') + return cthulhu.cthulhuApp.settingsManager.getSetting('repeatCharacterLimit') @dbus_service.setter def set_repeated_character_limit(self, value: int) -> bool: """Sets the count at which repeated, non-alphanumeric symbols will be described.""" try: - _settings_manager.setSetting('repeatCharacterLimit', value) + cthulhu.cthulhuApp.settingsManager.setSetting('repeatCharacterLimit', value) return True except Exception as e: debug.printMessage(debug.LEVEL_WARNING, f"Error setting repeated character limit: {e}", True) @@ -1216,13 +1217,13 @@ class SpeechAndVerbosityManager: @dbus_service.getter def get_use_pronunciation_dictionary(self) -> bool: """Returns whether the user's pronunciation dictionary should be applied.""" - return _settings_manager.getSetting('usePronunciationDictionary') + return cthulhu.cthulhuApp.settingsManager.getSetting('usePronunciationDictionary') @dbus_service.setter def set_use_pronunciation_dictionary(self, value: bool) -> bool: """Sets whether the user's pronunciation dictionary should be applied.""" try: - _settings_manager.setSetting('usePronunciationDictionary', value) + cthulhu.cthulhuApp.settingsManager.setSetting('usePronunciationDictionary', value) return True except Exception as e: debug.printMessage(debug.LEVEL_WARNING, f"Error setting use pronunciation dictionary: {e}", True) @@ -1231,13 +1232,13 @@ class SpeechAndVerbosityManager: @dbus_service.getter def get_messages_are_detailed(self) -> bool: """Returns whether informative messages will be detailed or brief.""" - return _settings_manager.getSetting('messagesAreDetailed') + return cthulhu.cthulhuApp.settingsManager.getSetting('messagesAreDetailed') @dbus_service.setter def set_messages_are_detailed(self, value: bool) -> bool: """Sets whether informative messages will be detailed or brief.""" try: - _settings_manager.setSetting('messagesAreDetailed', value) + cthulhu.cthulhuApp.settingsManager.setSetting('messagesAreDetailed', value) return True except Exception as e: debug.printMessage(debug.LEVEL_WARNING, f"Error setting messages are detailed: {e}", True) @@ -1246,13 +1247,13 @@ class SpeechAndVerbosityManager: @dbus_service.getter def get_speak_indentation_only_if_changed(self) -> bool: """Returns whether indentation will be announced only if it has changed.""" - return _settings_manager.getSetting('speakIndentationOnlyIfChanged') + return cthulhu.cthulhuApp.settingsManager.getSetting('speakIndentationOnlyIfChanged') @dbus_service.setter def set_speak_indentation_only_if_changed(self, value: bool) -> bool: """Sets whether indentation will be announced only if it has changed.""" try: - _settings_manager.setSetting('speakIndentationOnlyIfChanged', value) + cthulhu.cthulhuApp.settingsManager.setSetting('speakIndentationOnlyIfChanged', value) return True except Exception as e: debug.printMessage(debug.LEVEL_WARNING, f"Error setting speak indentation only if changed: {e}", True) @@ -1265,13 +1266,13 @@ class SpeechAndVerbosityManager: @dbus_service.getter def get_speak_blank_lines(self) -> bool: """Returns whether blank lines will be spoken.""" - return _settings_manager.getSetting('speakBlankLines') + return cthulhu.cthulhuApp.settingsManager.getSetting('speakBlankLines') @dbus_service.setter def set_speak_blank_lines(self, value: bool) -> bool: """Sets whether blank lines will be spoken.""" try: - _settings_manager.setSetting('speakBlankLines', value) + cthulhu.cthulhuApp.settingsManager.setSetting('speakBlankLines', value) return True except Exception as e: debug.printMessage(debug.LEVEL_WARNING, f"Error setting speak blank lines: {e}", True) @@ -1280,13 +1281,13 @@ class SpeechAndVerbosityManager: @dbus_service.getter def get_speak_row_in_gui_table(self) -> bool: """Returns whether Up/Down in GUI tables speaks the row or just the cell.""" - return _settings_manager.getSetting('readFullRowInGUITable') + return cthulhu.cthulhuApp.settingsManager.getSetting('readFullRowInGUITable') @dbus_service.setter def set_speak_row_in_gui_table(self, value: bool) -> bool: """Sets whether Up/Down in GUI tables speaks the row or just the cell.""" try: - _settings_manager.setSetting('readFullRowInGUITable', value) + cthulhu.cthulhuApp.settingsManager.setSetting('readFullRowInGUITable', value) return True except Exception as e: debug.printMessage(debug.LEVEL_WARNING, f"Error setting speak row in GUI table: {e}", True) @@ -1295,13 +1296,13 @@ class SpeechAndVerbosityManager: @dbus_service.getter def get_speak_row_in_document_table(self) -> bool: """Returns whether Up/Down in text-document tables speaks the row or just the cell.""" - return _settings_manager.getSetting('readFullRowInDocumentTable') + return cthulhu.cthulhuApp.settingsManager.getSetting('readFullRowInDocumentTable') @dbus_service.setter def set_speak_row_in_document_table(self, value: bool) -> bool: """Sets whether Up/Down in text-document tables speaks the row or just the cell.""" try: - _settings_manager.setSetting('readFullRowInDocumentTable', value) + cthulhu.cthulhuApp.settingsManager.setSetting('readFullRowInDocumentTable', value) return True except Exception as e: debug.printMessage(debug.LEVEL_WARNING, f"Error setting speak row in document table: {e}", True) @@ -1310,13 +1311,13 @@ class SpeechAndVerbosityManager: @dbus_service.getter def get_speak_row_in_spreadsheet(self) -> bool: """Returns whether Up/Down in spreadsheets speaks the row or just the cell.""" - return _settings_manager.getSetting('readFullRowInSpreadSheet') + return cthulhu.cthulhuApp.settingsManager.getSetting('readFullRowInSpreadSheet') @dbus_service.setter def set_speak_row_in_spreadsheet(self, value: bool) -> bool: """Sets whether Up/Down in spreadsheets speaks the row or just the cell.""" try: - _settings_manager.setSetting('readFullRowInSpreadSheet', value) + cthulhu.cthulhuApp.settingsManager.setSetting('readFullRowInSpreadSheet', value) return True except Exception as e: debug.printMessage(debug.LEVEL_WARNING, f"Error setting speak row in spreadsheet: {e}", True) @@ -1325,13 +1326,13 @@ class SpeechAndVerbosityManager: @dbus_service.getter def get_announce_cell_span(self) -> bool: """Returns whether cell spans are announced when greater than 1.""" - return _settings_manager.getSetting('speakCellSpan') + return cthulhu.cthulhuApp.settingsManager.getSetting('speakCellSpan') @dbus_service.setter def set_announce_cell_span(self, value: bool) -> bool: """Sets whether cell spans are announced when greater than 1.""" try: - _settings_manager.setSetting('speakCellSpan', value) + cthulhu.cthulhuApp.settingsManager.setSetting('speakCellSpan', value) return True except Exception as e: debug.printMessage(debug.LEVEL_WARNING, f"Error setting announce cell span: {e}", True) @@ -1340,13 +1341,13 @@ class SpeechAndVerbosityManager: @dbus_service.getter def get_announce_cell_coordinates(self) -> bool: """Returns whether (non-spreadsheet) cell coordinates are announced.""" - return _settings_manager.getSetting('speakCellCoordinates') + return cthulhu.cthulhuApp.settingsManager.getSetting('speakCellCoordinates') @dbus_service.setter def set_announce_cell_coordinates(self, value: bool) -> bool: """Sets whether (non-spreadsheet) cell coordinates are announced.""" try: - _settings_manager.setSetting('speakCellCoordinates', value) + cthulhu.cthulhuApp.settingsManager.setSetting('speakCellCoordinates', value) return True except Exception as e: debug.printMessage(debug.LEVEL_WARNING, f"Error setting announce cell coordinates: {e}", True) @@ -1355,13 +1356,13 @@ class SpeechAndVerbosityManager: @dbus_service.getter def get_announce_spreadsheet_cell_coordinates(self) -> bool: """Returns whether spreadsheet cell coordinates are announced.""" - return _settings_manager.getSetting('speakSpreadsheetCoordinates') + return cthulhu.cthulhuApp.settingsManager.getSetting('speakSpreadsheetCoordinates') @dbus_service.setter def set_announce_spreadsheet_cell_coordinates(self, value: bool) -> bool: """Sets whether spreadsheet cell coordinates are announced.""" try: - _settings_manager.setSetting('speakSpreadsheetCoordinates', value) + cthulhu.cthulhuApp.settingsManager.setSetting('speakSpreadsheetCoordinates', value) return True except Exception as e: debug.printMessage(debug.LEVEL_WARNING, f"Error setting announce spreadsheet cell coordinates: {e}", True) @@ -1370,13 +1371,13 @@ class SpeechAndVerbosityManager: @dbus_service.getter def get_always_announce_selected_range_in_spreadsheet(self) -> bool: """Returns whether the selected range in spreadsheets is always announced.""" - return _settings_manager.getSetting('alwaysSpeakSelectedSpreadsheetRange') + return cthulhu.cthulhuApp.settingsManager.getSetting('alwaysSpeakSelectedSpreadsheetRange') @dbus_service.setter def set_always_announce_selected_range_in_spreadsheet(self, value: bool) -> bool: """Sets whether the selected range in spreadsheets is always announced.""" try: - _settings_manager.setSetting('alwaysSpeakSelectedSpreadsheetRange', value) + cthulhu.cthulhuApp.settingsManager.setSetting('alwaysSpeakSelectedSpreadsheetRange', value) return True except Exception as e: debug.printMessage(debug.LEVEL_WARNING, f"Error setting always announce selected range: {e}", True) @@ -1385,13 +1386,13 @@ class SpeechAndVerbosityManager: @dbus_service.getter def get_announce_cell_headers(self) -> bool: """Returns whether cell headers are announced.""" - return _settings_manager.getSetting('speakCellHeaders') + return cthulhu.cthulhuApp.settingsManager.getSetting('speakCellHeaders') @dbus_service.setter def set_announce_cell_headers(self, value: bool) -> bool: """Sets whether cell headers are announced.""" try: - _settings_manager.setSetting('speakCellHeaders', value) + cthulhu.cthulhuApp.settingsManager.setSetting('speakCellHeaders', value) return True except Exception as e: debug.printMessage(debug.LEVEL_WARNING, f"Error setting announce cell headers: {e}", True) @@ -1404,13 +1405,13 @@ class SpeechAndVerbosityManager: @dbus_service.getter def get_announce_blockquote(self) -> bool: """Returns whether blockquotes are announced when entered.""" - return _settings_manager.getSetting('speakContextBlockquote') + return cthulhu.cthulhuApp.settingsManager.getSetting('speakContextBlockquote') @dbus_service.setter def set_announce_blockquote(self, value: bool) -> bool: """Sets whether blockquotes are announced when entered.""" try: - _settings_manager.setSetting('speakContextBlockquote', value) + cthulhu.cthulhuApp.settingsManager.setSetting('speakContextBlockquote', value) return True except Exception as e: debug.printMessage(debug.LEVEL_WARNING, f"Error setting announce blockquote: {e}", True) @@ -1419,13 +1420,13 @@ class SpeechAndVerbosityManager: @dbus_service.getter def get_announce_form(self) -> bool: """Returns whether non-landmark forms are announced when entered.""" - return _settings_manager.getSetting('speakContextNonLandmarkForm') + return cthulhu.cthulhuApp.settingsManager.getSetting('speakContextNonLandmarkForm') @dbus_service.setter def set_announce_form(self, value: bool) -> bool: """Sets whether non-landmark forms are announced when entered.""" try: - _settings_manager.setSetting('speakContextNonLandmarkForm', value) + cthulhu.cthulhuApp.settingsManager.setSetting('speakContextNonLandmarkForm', value) return True except Exception as e: debug.printMessage(debug.LEVEL_WARNING, f"Error setting announce form: {e}", True) @@ -1434,13 +1435,13 @@ class SpeechAndVerbosityManager: @dbus_service.getter def get_announce_grouping(self) -> bool: """Returns whether groupings are announced when entered.""" - return _settings_manager.getSetting('speakContextPanel') + return cthulhu.cthulhuApp.settingsManager.getSetting('speakContextPanel') @dbus_service.setter def set_announce_grouping(self, value: bool) -> bool: """Sets whether groupings are announced when entered.""" try: - _settings_manager.setSetting('speakContextPanel', value) + cthulhu.cthulhuApp.settingsManager.setSetting('speakContextPanel', value) return True except Exception as e: debug.printMessage(debug.LEVEL_WARNING, f"Error setting announce grouping: {e}", True) @@ -1449,13 +1450,13 @@ class SpeechAndVerbosityManager: @dbus_service.getter def get_announce_landmark(self) -> bool: """Returns whether landmarks are announced when entered.""" - return _settings_manager.getSetting('speakContextLandmark') + return cthulhu.cthulhuApp.settingsManager.getSetting('speakContextLandmark') @dbus_service.setter def set_announce_landmark(self, value: bool) -> bool: """Sets whether landmarks are announced when entered.""" try: - _settings_manager.setSetting('speakContextLandmark', value) + cthulhu.cthulhuApp.settingsManager.setSetting('speakContextLandmark', value) return True except Exception as e: debug.printMessage(debug.LEVEL_WARNING, f"Error setting announce landmark: {e}", True) @@ -1464,13 +1465,13 @@ class SpeechAndVerbosityManager: @dbus_service.getter def get_announce_list(self) -> bool: """Returns whether lists are announced when entered.""" - return _settings_manager.getSetting('speakContextList') + return cthulhu.cthulhuApp.settingsManager.getSetting('speakContextList') @dbus_service.setter def set_announce_list(self, value: bool) -> bool: """Sets whether lists are announced when entered.""" try: - _settings_manager.setSetting('speakContextList', value) + cthulhu.cthulhuApp.settingsManager.setSetting('speakContextList', value) return True except Exception as e: debug.printMessage(debug.LEVEL_WARNING, f"Error setting announce list: {e}", True) @@ -1479,13 +1480,13 @@ class SpeechAndVerbosityManager: @dbus_service.getter def get_announce_table(self) -> bool: """Returns whether tables are announced when entered.""" - return _settings_manager.getSetting('speakContextTable') + return cthulhu.cthulhuApp.settingsManager.getSetting('speakContextTable') @dbus_service.setter def set_announce_table(self, value: bool) -> bool: """Sets whether tables are announced when entered.""" try: - _settings_manager.setSetting('speakContextTable', value) + cthulhu.cthulhuApp.settingsManager.setSetting('speakContextTable', value) return True except Exception as e: debug.printMessage(debug.LEVEL_WARNING, f"Error setting announce table: {e}", True) @@ -1494,13 +1495,13 @@ class SpeechAndVerbosityManager: @dbus_service.getter def get_use_color_names(self) -> bool: """Returns whether colors are announced by name or as RGB values.""" - return _settings_manager.getSetting('useColorNames') + return cthulhu.cthulhuApp.settingsManager.getSetting('useColorNames') @dbus_service.setter def set_use_color_names(self, value: bool) -> bool: """Sets whether colors are announced by name or as RGB values.""" try: - _settings_manager.setSetting('useColorNames', value) + cthulhu.cthulhuApp.settingsManager.setSetting('useColorNames', value) return True except Exception as e: debug.printMessage(debug.LEVEL_WARNING, f"Error setting use color names: {e}", True) @@ -1559,7 +1560,7 @@ class SpeechAndVerbosityManager: def set_speech_is_enabled(self, value: bool) -> bool: """Sets whether the speech server is enabled. See also is-muted.""" try: - _settings_manager.setSetting('enableSpeech', value) + cthulhu.cthulhuApp.settingsManager.setSetting('enableSpeech', value) if value: self.start_speech() else: @@ -1572,7 +1573,7 @@ class SpeechAndVerbosityManager: @dbus_service.getter def get_speech_is_enabled(self) -> bool: """Returns whether the speech server is enabled. See also is-muted.""" - return _settings_manager.getSetting('enableSpeech') + return cthulhu.cthulhuApp.settingsManager.getSetting('enableSpeech') @dbus_service.command def decrease_rate(self, script=None, event=None): @@ -1723,7 +1724,7 @@ class SpeechAndVerbosityManager: def cycle_capitalization_style(self, script=None, event=None): """Cycle through the speech-dispatcher capitalization styles.""" - current_style = _settings_manager.getSetting('capitalizationStyle') + current_style = cthulhu.cthulhuApp.settingsManager.getSetting('capitalizationStyle') if current_style == settings.CAPITALIZATION_STYLE_NONE: new_style = settings.CAPITALIZATION_STYLE_SPELL full = messages.CAPITALIZATION_SPELL_FULL @@ -1737,7 +1738,7 @@ class SpeechAndVerbosityManager: full = messages.CAPITALIZATION_NONE_FULL brief = messages.CAPITALIZATION_NONE_BRIEF - _settings_manager.setSetting('capitalizationStyle', new_style) + cthulhu.cthulhuApp.settingsManager.setSetting('capitalizationStyle', new_style) if script: script.presentMessage(full, brief) self.update_capitalization_style() @@ -1747,7 +1748,7 @@ class SpeechAndVerbosityManager: def cycle_punctuation_level(self, script=None, event=None): """Cycle through the punctuation levels for speech.""" - current_level = _settings_manager.getSetting('verbalizePunctuationStyle') + current_level = cthulhu.cthulhuApp.settingsManager.getSetting('verbalizePunctuationStyle') if current_level == settings.PUNCTUATION_STYLE_NONE: new_level = settings.PUNCTUATION_STYLE_SOME full = messages.PUNCTUATION_SOME_FULL @@ -1765,7 +1766,7 @@ class SpeechAndVerbosityManager: full = messages.PUNCTUATION_NONE_FULL brief = messages.PUNCTUATION_NONE_BRIEF - _settings_manager.setSetting('verbalizePunctuationStyle', new_level) + cthulhu.cthulhuApp.settingsManager.setSetting('verbalizePunctuationStyle', new_level) if script: script.presentMessage(full, brief) self.update_punctuation_level() @@ -1775,9 +1776,9 @@ class SpeechAndVerbosityManager: def cycle_key_echo(self, script=None, event=None): """Cycle through the key echo levels.""" (new_key, new_word, new_sentence) = (False, False, False) - key = _settings_manager.getSetting('enableKeyEcho') - word = _settings_manager.getSetting('enableEchoByWord') - sentence = _settings_manager.getSetting('enableEchoBySentence') + key = cthulhu.cthulhuApp.settingsManager.getSetting('enableKeyEcho') + word = cthulhu.cthulhuApp.settingsManager.getSetting('enableEchoByWord') + sentence = cthulhu.cthulhuApp.settingsManager.getSetting('enableEchoBySentence') if (key, word, sentence) == (False, False, False): (new_key, new_word, new_sentence) = (True, False, False) @@ -1804,9 +1805,9 @@ class SpeechAndVerbosityManager: full = messages.KEY_ECHO_NONE_FULL brief = messages.KEY_ECHO_NONE_BRIEF - _settings_manager.setSetting('enableKeyEcho', new_key) - _settings_manager.setSetting('enableEchoByWord', new_word) - _settings_manager.setSetting('enableEchoBySentence', new_sentence) + cthulhu.cthulhuApp.settingsManager.setSetting('enableKeyEcho', new_key) + cthulhu.cthulhuApp.settingsManager.setSetting('enableEchoByWord', new_word) + cthulhu.cthulhuApp.settingsManager.setSetting('enableEchoBySentence', new_sentence) if script: script.presentMessage(full, brief) return True @@ -1815,7 +1816,7 @@ class SpeechAndVerbosityManager: def change_number_style(self, script=None, event=None): """Changes spoken number style between digits and words.""" - speak_digits = _settings_manager.getSetting('speakNumbersAsDigits') + speak_digits = cthulhu.cthulhuApp.settingsManager.getSetting('speakNumbersAsDigits') if speak_digits: brief = messages.NUMBER_STYLE_WORDS_BRIEF full = messages.NUMBER_STYLE_WORDS_FULL @@ -1823,7 +1824,7 @@ class SpeechAndVerbosityManager: brief = messages.NUMBER_STYLE_DIGITS_BRIEF full = messages.NUMBER_STYLE_DIGITS_FULL - _settings_manager.setSetting('speakNumbersAsDigits', not speak_digits) + cthulhu.cthulhuApp.settingsManager.setSetting('speakNumbersAsDigits', not speak_digits) if script: script.presentMessage(full, brief) return True @@ -1833,39 +1834,39 @@ class SpeechAndVerbosityManager: """Toggles speech.""" script.presentationInterrupt() - if _settings_manager.getSetting('silenceSpeech'): - _settings_manager.setSetting('silenceSpeech', False) + if self.app.getSettingsManager().getSetting('silenceSpeech'): + self.app.getSettingsManager().setSetting('silenceSpeech', False) script.presentMessage(messages.SPEECH_ENABLED) - elif not _settings_manager.getSetting('enableSpeech'): - _settings_manager.setSetting('enableSpeech', True) + elif not self.app.getSettingsManager().getSetting('enableSpeech'): + self.app.getSettingsManager().setSetting('enableSpeech', True) speech.init() script.presentMessage(messages.SPEECH_ENABLED) else: script.presentMessage(messages.SPEECH_DISABLED) - _settings_manager.setSetting('silenceSpeech', True) + self.app.getSettingsManager().setSetting('silenceSpeech', True) return True @dbus_service.command def toggle_verbosity(self, script, event=None): """Toggles speech verbosity level between verbose and brief.""" - value = _settings_manager.getSetting('speechVerbosityLevel') + value = self.app.getSettingsManager().getSetting('speechVerbosityLevel') if value == settings.VERBOSITY_LEVEL_BRIEF: script.presentMessage(messages.SPEECH_VERBOSITY_VERBOSE) - _settings_manager.setSetting('speechVerbosityLevel', settings.VERBOSITY_LEVEL_VERBOSE) + self.app.getSettingsManager().setSetting('speechVerbosityLevel', settings.VERBOSITY_LEVEL_VERBOSE) else: script.presentMessage(messages.SPEECH_VERBOSITY_BRIEF) - _settings_manager.setSetting('speechVerbosityLevel', settings.VERBOSITY_LEVEL_BRIEF) + self.app.getSettingsManager().setSetting('speechVerbosityLevel', settings.VERBOSITY_LEVEL_BRIEF) return True @dbus_service.command def toggle_indentation_and_justification(self, script=None, event=None): """Toggles the speaking of indentation and justification.""" - value = _settings_manager.getSetting('enableSpeechIndentation') - _settings_manager.setSetting('enableSpeechIndentation', not value) + value = self.app.getSettingsManager().getSetting('enableSpeechIndentation') + self.app.getSettingsManager().setSetting('enableSpeechIndentation', not value) self._sync_indentation_presentation_mode(not value) - if _settings_manager.getSetting('enableSpeechIndentation'): + if self.app.getSettingsManager().getSetting('enableSpeechIndentation'): full = messages.INDENTATION_JUSTIFICATION_ON_FULL brief = messages.INDENTATION_JUSTIFICATION_ON_BRIEF else: @@ -1894,8 +1895,8 @@ class SpeechAndVerbosityManager: else: setting_name = 'readFullRowInDocumentTable' - speak_row = _settings_manager.getSetting(setting_name) - _settings_manager.setSetting(setting_name, not speak_row) + speak_row = cthulhu.cthulhuApp.settingsManager.getSetting(setting_name) + cthulhu.cthulhuApp.settingsManager.setSetting(setting_name, not speak_row) if not speak_row: msg = messages.TABLE_MODE_ROW @@ -1908,8 +1909,9 @@ class SpeechAndVerbosityManager: _manager = None def getManager(): """Returns the Speech and Verbosity Manager""" - + global _manager if _manager is None: - _manager = SpeechAndVerbosityManager() + from . import cthulhu + _manager = SpeechAndVerbosityManager(cthulhu.cthulhuApp) return _manager diff --git a/src/cthulhu/speech_generator.py b/src/cthulhu/speech_generator.py index a651600..2f39be6 100644 --- a/src/cthulhu/speech_generator.py +++ b/src/cthulhu/speech_generator.py @@ -43,6 +43,7 @@ import urllib.parse from . import acss from . import chnames +from . import cthulhu from . import debug from . import generator from . import messages @@ -105,7 +106,7 @@ voiceType = { VALUE: settings.SYSTEM_VOICE, # Users may prefer DEFAULT_VOICE here } -_settingsManager = settings_manager.getManager() +_settingsManager = None # Removed - use cthulhu.cthulhuApp.settingsManager class SpeechGenerator(generator.Generator): """Takes accessible objects and produces a string to speak for @@ -164,7 +165,7 @@ class SpeechGenerator(generator.Generator): role = args.get('role', AXObject.get_role(obj)) if role == Atspi.Role.LAYERED_PANE \ - and _settingsManager.getSetting('onlySpeakDisplayedText'): + and cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] result = generator.Generator._generateName(self, obj, **args) @@ -226,11 +227,11 @@ class SpeechGenerator(generator.Generator): if not name: return name - roleSoundPresentation = _settingsManager.getSetting('roleSoundPresentation') + roleSoundPresentation = cthulhu.cthulhuApp.settingsManager.getSetting('roleSoundPresentation') if roleSoundPresentation != settings.ROLE_SOUND_PRESENTATION_SOUND_ONLY: return name - if not _settingsManager.getSetting('enableSound'): + if not cthulhu.cthulhuApp.settingsManager.getSetting('enableSound'): return name if not self._shouldStripRoleFromName(obj): @@ -351,13 +352,13 @@ class SpeechGenerator(generator.Generator): if alreadyUsed: return [] - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] - if not _settingsManager.getSetting('speakDescription') and not args.get('alerttext'): + if not cthulhu.cthulhuApp.settingsManager.getSetting('speakDescription') and not args.get('alerttext'): return [] - if args.get('inMouseReview') and not _settingsManager.getSetting('presentToolTips'): + if args.get('inMouseReview') and not cthulhu.cthulhuApp.settingsManager.getSetting('presentToolTips'): return [] priorObj = args.get('priorObj') @@ -376,10 +377,10 @@ class SpeechGenerator(generator.Generator): """Returns an array of strings for use by speech and braille that represent the description of the image on the object.""" - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] - if not _settingsManager.getSetting('speakDescription'): + if not cthulhu.cthulhuApp.settingsManager.getSetting('speakDescription'): return [] result = generator.Generator._generateImageDescription(self, obj, **args) @@ -399,8 +400,8 @@ class SpeechGenerator(generator.Generator): return result def _generateHasPopup(self, obj, **args): - if _settingsManager.getSetting('onlySpeakDisplayedText') \ - or _settingsManager.getSetting('speechVerbosityLevel') \ + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText') \ + or cthulhu.cthulhuApp.settingsManager.getSetting('speechVerbosityLevel') \ == settings.VERBOSITY_LEVEL_BRIEF: return [] @@ -410,8 +411,8 @@ class SpeechGenerator(generator.Generator): return result def _generateClickable(self, obj, **args): - if _settingsManager.getSetting('onlySpeakDisplayedText') \ - or _settingsManager.getSetting('speechVerbosityLevel') \ + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText') \ + or cthulhu.cthulhuApp.settingsManager.getSetting('speechVerbosityLevel') \ == settings.VERBOSITY_LEVEL_BRIEF: return [] @@ -421,7 +422,7 @@ class SpeechGenerator(generator.Generator): return result def _generateHasLongDesc(self, obj, **args): - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] result = generator.Generator._generateHasLongDesc(self, obj, **args) @@ -430,7 +431,7 @@ class SpeechGenerator(generator.Generator): return result def _generateHasDetails(self, obj, **args): - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] result = generator.Generator._generateHasDetails(self, obj, **args) @@ -439,7 +440,7 @@ class SpeechGenerator(generator.Generator): return result def _generateDetailsFor(self, obj, **args): - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] result = generator.Generator._generateDetailsFor(self, obj, **args) @@ -448,7 +449,7 @@ class SpeechGenerator(generator.Generator): return result def _generateAllDetails(self, obj, **args): - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] result = generator.Generator._generateAllDetails(self, obj, **args) @@ -457,7 +458,7 @@ class SpeechGenerator(generator.Generator): return result def _generateDeletionStart(self, obj, **args): - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] startOffset = args.get('startOffset', 0) @@ -475,7 +476,7 @@ class SpeechGenerator(generator.Generator): return result def _generateDeletionEnd(self, obj, **args): - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] endOffset = args.get('endOffset') @@ -500,7 +501,7 @@ class SpeechGenerator(generator.Generator): return result def _generateInsertionStart(self, obj, **args): - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] startOffset = args.get('startOffset', 0) @@ -518,7 +519,7 @@ class SpeechGenerator(generator.Generator): return result def _generateInsertionEnd(self, obj, **args): - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] endOffset = args.get('endOffset') @@ -543,7 +544,7 @@ class SpeechGenerator(generator.Generator): return result def _generateMarkStart(self, obj, **args): - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] startOffset = args.get('startOffset', 0) @@ -562,7 +563,7 @@ class SpeechGenerator(generator.Generator): return result def _generateMarkEnd(self, obj, **args): - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] endOffset = args.get('endOffset') @@ -576,7 +577,7 @@ class SpeechGenerator(generator.Generator): return result def _generateAvailability(self, obj, **args): - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] result = generator.Generator._generateAvailability(self, obj, **args) @@ -585,7 +586,7 @@ class SpeechGenerator(generator.Generator): return result def _generateInvalid(self, obj, **args): - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] result = generator.Generator._generateInvalid(self, obj, **args) @@ -594,7 +595,7 @@ class SpeechGenerator(generator.Generator): return result def _generateRequired(self, obj, **args): - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] result = generator.Generator._generateRequired(self, obj, **args) @@ -603,7 +604,7 @@ class SpeechGenerator(generator.Generator): return result def _generateTable(self, obj, **args): - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] if args.get("leaving"): @@ -615,7 +616,7 @@ class SpeechGenerator(generator.Generator): if role in disabled: return [] - if _settingsManager.getSetting('speechVerbosityLevel') == settings.VERBOSITY_LEVEL_BRIEF: + if cthulhu.cthulhuApp.settingsManager.getSetting('speechVerbosityLevel') == settings.VERBOSITY_LEVEL_BRIEF: return self._generateRoleName(obj, **args) result = generator.Generator._generateTable(self, obj, **args) @@ -635,7 +636,7 @@ class SpeechGenerator(generator.Generator): of a speech generator that we can update and the user can override.]]] """ - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] result = [] @@ -655,7 +656,7 @@ class SpeechGenerator(generator.Generator): if obj == args.get("priorObj"): return [] - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] if self._script.utilities.isStatusBarNotification(obj): @@ -669,8 +670,8 @@ class SpeechGenerator(generator.Generator): result = [] role = args.get('role', AXObject.get_role(obj)) - roleSoundPresentation = _settingsManager.getSetting('roleSoundPresentation') - soundEnabled = _settingsManager.getSetting('enableSound') + roleSoundPresentation = cthulhu.cthulhuApp.settingsManager.getSetting('roleSoundPresentation') + soundEnabled = cthulhu.cthulhuApp.settingsManager.getSetting('enableSound') roleSoundIcon = None if roleSoundPresentation != settings.ROLE_SOUND_PRESENTATION_SPEECH_ONLY \ and soundEnabled: @@ -710,7 +711,7 @@ class SpeechGenerator(generator.Generator): if self._script.utilities.isStatusBarDescendant(obj): doNotPresent.append(Atspi.Role.LABEL) - if _settingsManager.getSetting('speechVerbosityLevel') \ + if cthulhu.cthulhuApp.settingsManager.getSetting('speechVerbosityLevel') \ == settings.VERBOSITY_LEVEL_BRIEF: doNotPresent.extend([Atspi.Role.ICON, Atspi.Role.CANVAS]) @@ -812,11 +813,11 @@ class SpeechGenerator(generator.Generator): if not result: return result - roleSoundPresentation = _settingsManager.getSetting('roleSoundPresentation') + roleSoundPresentation = cthulhu.cthulhuApp.settingsManager.getSetting('roleSoundPresentation') if roleSoundPresentation == settings.ROLE_SOUND_PRESENTATION_SPEECH_ONLY: return result - if not _settingsManager.getSetting('enableSound'): + if not cthulhu.cthulhuApp.settingsManager.getSetting('enableSound'): return result icon = sound_theme_manager.getManager().getRoleStateSoundIcon(role, stateKey) @@ -834,7 +835,7 @@ class SpeechGenerator(generator.Generator): for check boxes. [[[WDW - should we return an empty array if we can guarantee we know this thing is not checkable?]]] """ - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] result = generator.Generator._generateCheckedState(self, obj, **args) @@ -858,7 +859,7 @@ class SpeechGenerator(generator.Generator): tree node. If the object is not expandable, an empty array will be returned. """ - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] result = generator.Generator._generateExpandableState(self, obj, **args) @@ -867,7 +868,7 @@ class SpeechGenerator(generator.Generator): return result def _generateCheckedStateIfCheckable(self, obj, **args): - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] result = super()._generateCheckedStateIfCheckable(obj, **args) @@ -880,7 +881,7 @@ class SpeechGenerator(generator.Generator): represent the checked state of the menu item, only if it is checked. Otherwise, and empty array will be returned. """ - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] result = generator.Generator.\ @@ -900,7 +901,7 @@ class SpeechGenerator(generator.Generator): the object. This is typically for list boxes. If the object is not multiselectable, an empty array will be returned. """ - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] result = super()._generateMultiselectableState(obj, **args) @@ -914,7 +915,7 @@ class SpeechGenerator(generator.Generator): for check boxes. [[[WDW - should we return an empty array if we can guarantee we know this thing is not checkable?]]] """ - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] result = generator.Generator._generateRadioState(self, obj, **args) @@ -927,7 +928,7 @@ class SpeechGenerator(generator.Generator): def _generateSwitchState(self, obj, **args): """Returns an array of strings indicating the on/off state of obj.""" - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] result = generator.Generator._generateSwitchState(self, obj, **args) @@ -945,7 +946,7 @@ class SpeechGenerator(generator.Generator): for check boxes. [[[WDW - should we return an empty array if we can guarantee we know this thing is not checkable?]]] """ - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] result = generator.Generator._generateToggleState(self, obj, **args) @@ -1149,7 +1150,7 @@ class SpeechGenerator(generator.Generator): if args.get('readingRow'): return [] - if not _settingsManager.getSetting('speakCellHeaders'): + if not cthulhu.cthulhuApp.settingsManager.getSetting('speakCellHeaders'): return [] if args.get('inMouseReview') and args.get('priorObj'): @@ -1178,7 +1179,7 @@ class SpeechGenerator(generator.Generator): if args.get('readingRow'): return [] - if not _settingsManager.getSetting('speakCellHeaders'): + if not cthulhu.cthulhuApp.settingsManager.getSetting('speakCellHeaders'): return [] if args.get('inMouseReview') and args.get('priorObj'): @@ -1202,7 +1203,7 @@ class SpeechGenerator(generator.Generator): result.extend(self.generate(obj, **args)) self._restoreRole(oldRole, args) if not (result and result[0]) \ - and _settingsManager.getSetting('speakBlankLines') \ + and cthulhu.cthulhuApp.settingsManager.getSetting('speakBlankLines') \ and not args.get('readingRow', False) \ and args.get('formatType') != 'ancestor': result.append(messages.BLANK) @@ -1212,7 +1213,7 @@ class SpeechGenerator(generator.Generator): return result def _generateUnselectedStateIfSelectable(self, obj, **args): - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] if args.get('inMouseReview'): @@ -1240,7 +1241,7 @@ class SpeechGenerator(generator.Generator): returned. [[[WDW - I wonder if this string should be moved to settings.py.]]] """ - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] if args.get('inMouseReview'): @@ -1284,7 +1285,7 @@ class SpeechGenerator(generator.Generator): if args.get('readingRow'): return [] - if not _settingsManager.getSetting('speakCellCoordinates'): + if not cthulhu.cthulhuApp.settingsManager.getSetting('speakCellCoordinates'): return [] return self._generateColumn(obj, **args) @@ -1293,7 +1294,7 @@ class SpeechGenerator(generator.Generator): """Returns an array of strings (and possibly voice and audio specifications) reflecting the column number of a cell. """ - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] result = [] @@ -1317,7 +1318,7 @@ class SpeechGenerator(generator.Generator): if args.get('readingRow'): return [] - if not _settingsManager.getSetting('speakCellCoordinates'): + if not cthulhu.cthulhuApp.settingsManager.getSetting('speakCellCoordinates'): return [] return self._generateRow(obj, **args) @@ -1326,7 +1327,7 @@ class SpeechGenerator(generator.Generator): """Returns an array of strings (and possibly voice and audio specifications) reflecting the row number of a cell. """ - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] result = [] @@ -1349,7 +1350,7 @@ class SpeechGenerator(generator.Generator): of its column number, the total number of columns, its row, and the total number of rows. """ - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] result = [] @@ -1376,10 +1377,10 @@ class SpeechGenerator(generator.Generator): specifications) indicating that this cell is the last cell in the table. """ - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] - if _settingsManager.getSetting('speechVerbosityLevel') \ + if cthulhu.cthulhuApp.settingsManager.getSetting('speechVerbosityLevel') \ != settings.VERBOSITY_LEVEL_VERBOSE: return [] @@ -1412,7 +1413,7 @@ class SpeechGenerator(generator.Generator): return result [text, caretOffset, startOffset] = self._script.getTextLineAtCaret(obj) - if text == '\n' and _settingsManager.getSetting('speakBlankLines') \ + if text == '\n' and cthulhu.cthulhuApp.settingsManager.getSetting('speakBlankLines') \ and not self._script.inSayAll() and args.get('total', 1) == 1 \ and args.get('formatType') != 'ancestor': result = [messages.BLANK] @@ -1628,7 +1629,7 @@ class SpeechGenerator(generator.Generator): object is selected. [[[WDW - I wonder if this string should be moved to settings.py.]]] """ - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] if not AXObject.supports_text(obj): @@ -1648,7 +1649,7 @@ class SpeechGenerator(generator.Generator): object is selected. [[[WDW - I wonder if this string should be moved to settings.py.]]] """ - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] result = [] @@ -1667,7 +1668,7 @@ class SpeechGenerator(generator.Generator): return [] result.extend(self.voice(DEFAULT, obj=obj, **args)) - if result[0] in ['\n', ''] and _settingsManager.getSetting('speakBlankLines') \ + if result[0] in ['\n', ''] and cthulhu.cthulhuApp.settingsManager.getSetting('speakBlankLines') \ and not self._script.inSayAll() and args.get('total', 1) == 1 \ and args.get('formatType') != 'ancestor': result[0] = messages.BLANK @@ -1685,7 +1686,7 @@ class SpeechGenerator(generator.Generator): - obj: the text object. """ - if not _settingsManager.getSetting('enableSpeechIndentation'): + if not cthulhu.cthulhuApp.settingsManager.getSetting('enableSpeechIndentation'): return [] line, caretOffset, startOffset = self._script.getTextLineAtCaret(obj) @@ -1720,7 +1721,7 @@ class SpeechGenerator(generator.Generator): is typically set by Cthulhu to be the previous object with focus. """ - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] result = [] @@ -1750,7 +1751,7 @@ class SpeechGenerator(generator.Generator): object. This is typically for progress bars. [[[WDW - we should consider returning an empty array if there is no value. """ - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] percentValue = self._script.utilities.getValueAsPercent(obj) @@ -1815,8 +1816,8 @@ class SpeechGenerator(generator.Generator): be moved to settings.py.]]] """ - if _settingsManager.getSetting('onlySpeakDisplayedText') \ - or _settingsManager.getSetting('speechVerbosityLevel') == settings.VERBOSITY_LEVEL_BRIEF: + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText') \ + or cthulhu.cthulhuApp.settingsManager.getSetting('speechVerbosityLevel') == settings.VERBOSITY_LEVEL_BRIEF: return [] result = [] @@ -1847,7 +1848,7 @@ class SpeechGenerator(generator.Generator): apply?]]] [[[WDW - I wonder if this string should be moved to settings.py.]]] """ - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] result = [] @@ -1865,7 +1866,7 @@ class SpeechGenerator(generator.Generator): apply?]]] [[[WDW - I wonder if this string should be moved to settings.py.]]] """ - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] result = [] @@ -1899,7 +1900,7 @@ class SpeechGenerator(generator.Generator): panel or a layered pane. """ - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] container = obj @@ -1927,7 +1928,7 @@ class SpeechGenerator(generator.Generator): This object will be an icon panel or a layered pane. """ - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] container = obj @@ -1949,7 +1950,7 @@ class SpeechGenerator(generator.Generator): [[[WDW - I wonder if this string should be moved to settings.py.]]] """ - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] result = [] @@ -1987,15 +1988,15 @@ class SpeechGenerator(generator.Generator): enabled, disabled = [], [] if self._script.inSayAll(): - if _settingsManager.getSetting('sayAllContextBlockquote'): + if cthulhu.cthulhuApp.settingsManager.getSetting('sayAllContextBlockquote'): enabled.append(Atspi.Role.BLOCK_QUOTE) - if _settingsManager.getSetting('sayAllContextLandmark'): + if cthulhu.cthulhuApp.settingsManager.getSetting('sayAllContextLandmark'): enabled.extend([Atspi.Role.LANDMARK, 'ROLE_DPUB_LANDMARK']) - if _settingsManager.getSetting('sayAllContextList'): + if cthulhu.cthulhuApp.settingsManager.getSetting('sayAllContextList'): enabled.append(Atspi.Role.LIST) enabled.append(Atspi.Role.DESCRIPTION_LIST) enabled.append('ROLE_FEED') - if _settingsManager.getSetting('sayAllContextPanel'): + if cthulhu.cthulhuApp.settingsManager.getSetting('sayAllContextPanel'): enabled.extend([Atspi.Role.PANEL, Atspi.Role.TOOL_TIP, 'ROLE_CONTENT_DELETION', @@ -2003,20 +2004,20 @@ class SpeechGenerator(generator.Generator): 'ROLE_CONTENT_MARK', 'ROLE_CONTENT_SUGGESTION', 'ROLE_DPUB_SECTION']) - if _settingsManager.getSetting('sayAllContextNonLandmarkForm'): + if cthulhu.cthulhuApp.settingsManager.getSetting('sayAllContextNonLandmarkForm'): enabled.append(Atspi.Role.FORM) - if _settingsManager.getSetting('sayAllContextTable'): + if cthulhu.cthulhuApp.settingsManager.getSetting('sayAllContextTable'): enabled.append(Atspi.Role.TABLE) else: - if _settingsManager.getSetting('speakContextBlockquote'): + if cthulhu.cthulhuApp.settingsManager.getSetting('speakContextBlockquote'): enabled.append(Atspi.Role.BLOCK_QUOTE) - if _settingsManager.getSetting('speakContextLandmark'): + if cthulhu.cthulhuApp.settingsManager.getSetting('speakContextLandmark'): enabled.extend([Atspi.Role.LANDMARK, 'ROLE_DPUB_LANDMARK', 'ROLE_REGION']) - if _settingsManager.getSetting('speakContextList'): + if cthulhu.cthulhuApp.settingsManager.getSetting('speakContextList'): enabled.append(Atspi.Role.LIST) enabled.append(Atspi.Role.DESCRIPTION_LIST) enabled.append('ROLE_FEED') - if _settingsManager.getSetting('speakContextPanel'): + if cthulhu.cthulhuApp.settingsManager.getSetting('speakContextPanel'): enabled.extend([Atspi.Role.PANEL, Atspi.Role.TOOL_TIP, 'ROLE_CONTENT_DELETION', @@ -2024,9 +2025,9 @@ class SpeechGenerator(generator.Generator): 'ROLE_CONTENT_MARK', 'ROLE_CONTENT_SUGGESTION', 'ROLE_DPUB_SECTION']) - if _settingsManager.getSetting('speakContextNonLandmarkForm'): + if cthulhu.cthulhuApp.settingsManager.getSetting('speakContextNonLandmarkForm'): enabled.append(Atspi.Role.FORM) - if _settingsManager.getSetting('speakContextTable'): + if cthulhu.cthulhuApp.settingsManager.getSetting('speakContextTable'): enabled.append(Atspi.Role.TABLE) disabled = list(set(allRoles).symmetric_difference(enabled)) @@ -2278,7 +2279,7 @@ class SpeechGenerator(generator.Generator): specifications) that represent the text of the ancestors for the object being left.""" - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] if self._script.utilities.inFindContainer(): @@ -2344,7 +2345,7 @@ class SpeechGenerator(generator.Generator): with focus. """ - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] if self._script.utilities.inFindContainer(): @@ -2410,7 +2411,7 @@ class SpeechGenerator(generator.Generator): specifications) that represent the relative position of an object in a group. """ - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] # TODO - JD: We need other ways to determine group membership. Not all @@ -2444,8 +2445,8 @@ class SpeechGenerator(generator.Generator): object in a list. """ - if _settingsManager.getSetting('onlySpeakDisplayedText') \ - or not (_settingsManager.getSetting('enablePositionSpeaking') \ + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText') \ + or not (cthulhu.cthulhuApp.settingsManager.getSetting('enablePositionSpeaking') \ or args.get('forceList', False)): return [] @@ -2506,14 +2507,14 @@ class SpeechGenerator(generator.Generator): return result def _getProgressBarUpdateInterval(self): - interval = _settingsManager.getSetting('progressBarSpeechInterval') + interval = cthulhu.cthulhuApp.settingsManager.getSetting('progressBarSpeechInterval') if interval is None: interval = super()._getProgressBarUpdateInterval() return int(interval) def _shouldPresentProgressBarUpdate(self, obj, **args): - if not _settingsManager.getSetting('speakProgressBarUpdates'): + if not cthulhu.cthulhuApp.settingsManager.getSetting('speakProgressBarUpdates'): return False return super()._shouldPresentProgressBarUpdate(obj, **args) @@ -2588,7 +2589,7 @@ class SpeechGenerator(generator.Generator): specifications) that represent the accelerator for the object, or an empty array if no accelerator can be found. """ - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] result = [] @@ -2605,11 +2606,11 @@ class SpeechGenerator(generator.Generator): specifications) that represent the mnemonic for the object, or an empty array if no mnemonic can be found. """ - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] result = [] - if _settingsManager.getSetting('enableMnemonicSpeaking') \ + if cthulhu.cthulhuApp.settingsManager.getSetting('enableMnemonicSpeaking') \ or args.get('forceMnemonic', False): [mnemonic, shortcut, accelerator] = \ self._script.utilities.mnemonicShortcutAccelerator(obj) @@ -2637,7 +2638,7 @@ class SpeechGenerator(generator.Generator): tutorial generator. A tutorial can be forced by setting the 'forceTutorial' attribute of the args dictionary to True. """ - if _settingsManager.getSetting('onlySpeakDisplayedText'): + if cthulhu.cthulhuApp.settingsManager.getSetting('onlySpeakDisplayedText'): return [] result = [] @@ -3011,11 +3012,11 @@ class SpeechGenerator(generator.Generator): ##################################################################### def _generatePause(self, obj, **args): - if not _settingsManager.getSetting('enablePauseBreaks') \ + if not cthulhu.cthulhuApp.settingsManager.getSetting('enablePauseBreaks') \ or args.get('eliminatePauses', False): return [] - if _settingsManager.getSetting('verbalizePunctuationStyle') == \ + if cthulhu.cthulhuApp.settingsManager.getSetting('verbalizePunctuationStyle') == \ settings.PUNCTUATION_STYLE_ALL: return [] @@ -3032,7 +3033,7 @@ class SpeechGenerator(generator.Generator): """ voicename = voiceType.get(key) or voiceType.get(DEFAULT) - voices = _settingsManager.getSetting('voices') + voices = cthulhu.cthulhuApp.settingsManager.getSetting('voices') voice = acss.ACSS(voices.get(voiceType.get(DEFAULT), {})) language = args.get('language') diff --git a/src/cthulhu/speechdispatcherfactory.py b/src/cthulhu/speechdispatcherfactory.py index 6317f48..4b233e6 100644 --- a/src/cthulhu/speechdispatcherfactory.py +++ b/src/cthulhu/speechdispatcherfactory.py @@ -37,6 +37,7 @@ import re import time from . import chnames +from . import cthulhu from . import debug from . import guilabels from . import messages @@ -47,7 +48,7 @@ from . import punctuation_settings from . import settings_manager from .acss import ACSS -_settingsManager = settings_manager.getManager() +_settingsManager = None # Removed - use cthulhu.cthulhuApp.settingsManager try: import speechd @@ -287,7 +288,7 @@ class SpeechServer(speechserver.SpeechServer): f"volume {self._current_voice_properties.get(ACSS.GAIN)}, " f"language {self._get_language_and_dialect(family)[0]}, " f"punctuation: " - f"{styles.get(_settingsManager.getSetting('verbalizePunctuationStyle'))}\n" + f"{styles.get(cthulhu.cthulhuApp.settingsManager.getSetting('verbalizePunctuationStyle'))}\n" f"SD rate {sd_rate}, pitch {sd_pitch}, volume {sd_volume}, language {sd_language}" ) debug.printMessage(debug.LEVEL_INFO, msg, True) @@ -328,7 +329,7 @@ class SpeechServer(speechserver.SpeechServer): Returns a text string with the punctuation symbols adjusted accordingly. """ - style = _settingsManager.getSetting("verbalizePunctuationStyle") + style = cthulhu.cthulhuApp.settingsManager.getSetting("verbalizePunctuationStyle") if style == settings.PUNCTUATION_STYLE_NONE: return oldText diff --git a/src/cthulhu/spellcheck.py b/src/cthulhu/spellcheck.py index 22bccc5..4e91219 100644 --- a/src/cthulhu/spellcheck.py +++ b/src/cthulhu/spellcheck.py @@ -47,7 +47,7 @@ from cthulhu.ax_object import AXObject from cthulhu.ax_text import AXText from cthulhu.ax_utilities import AXUtilities -_settingsManager = settings_manager.getManager() +_settingsManager = None # Removed - use cthulhu.cthulhuApp.settingsManager class SpellCheck: @@ -195,7 +195,7 @@ class SpellCheck: if self.presentMistake(detailed): self.presentSuggestion(detailed) - if detailed or _settingsManager.getSetting('spellcheckPresentContext'): + if detailed or cthulhu.cthulhuApp.settingsManager.getSetting('spellcheckPresentContext'): self.presentContext() return True @@ -212,7 +212,7 @@ class SpellCheck: msg = messages.MISSPELLED_WORD % word voice = self._script.speechGenerator.voice(string=msg) self._script.speakMessage(msg, voice=voice) - if detailed or _settingsManager.getSetting('spellcheckSpellError'): + if detailed or cthulhu.cthulhuApp.settingsManager.getSetting('spellcheckSpellError'): self._script.spellCurrentItem(word) return True @@ -233,7 +233,7 @@ class SpellCheck: msg = f"{label} {string}" voice = self._script.speechGenerator.voice(string=msg) self._script.speakMessage(msg, voice=voice) - if detailed or _settingsManager.getSetting('spellcheckSpellSuggestion'): + if detailed or cthulhu.cthulhuApp.settingsManager.getSetting('spellcheckSpellSuggestion'): self._script.spellCurrentItem(string) return True @@ -260,10 +260,10 @@ class SpellCheck: msg = f"{label} {string}" voice = self._script.speechGenerator.voice(string=msg) self._script.speakMessage(msg.strip(), voice=voice) - if detailed or _settingsManager.getSetting('spellcheckSpellSuggestion'): + if detailed or cthulhu.cthulhuApp.settingsManager.getSetting('spellcheckSpellSuggestion'): self._script.spellCurrentItem(string) - if _settingsManager.getSetting('enablePositionSpeaking') \ + if cthulhu.cthulhuApp.settingsManager.getSetting('enablePositionSpeaking') \ and items[0] == cthulhu_state.locusOfFocus: index, total = self._getSuggestionIndexAndPosition(items[0]) msg = object_properties.GROUP_INDEX_SPEECH % {"index": index, "total": total} @@ -310,19 +310,19 @@ class SpellCheck: alignment.add(grid) label = guilabels.SPELL_CHECK_SPELL_ERROR - value = _settingsManager.getSetting('spellcheckSpellError') + value = cthulhu.cthulhuApp.settingsManager.getSetting('spellcheckSpellError') self.spellErrorCheckButton = Gtk.CheckButton.new_with_mnemonic(label) self.spellErrorCheckButton.set_active(value) grid.attach(self.spellErrorCheckButton, 0, 0, 1, 1) label = guilabels.SPELL_CHECK_SPELL_SUGGESTION - value = _settingsManager.getSetting('spellcheckSpellSuggestion') + value = cthulhu.cthulhuApp.settingsManager.getSetting('spellcheckSpellSuggestion') self.spellSuggestionCheckButton = Gtk.CheckButton.new_with_mnemonic(label) self.spellSuggestionCheckButton.set_active(value) grid.attach(self.spellSuggestionCheckButton, 0, 1, 1, 1) label = guilabels.SPELL_CHECK_PRESENT_CONTEXT - value = _settingsManager.getSetting('spellcheckPresentContext') + value = cthulhu.cthulhuApp.settingsManager.getSetting('spellcheckPresentContext') self.presentContextCheckButton = Gtk.CheckButton.new_with_mnemonic(label) self.presentContextCheckButton.set_active(value) grid.attach(self.presentContextCheckButton, 0, 2, 1, 1) diff --git a/src/cthulhu/structural_navigation.py b/src/cthulhu/structural_navigation.py index 55e414d..49998fc 100644 --- a/src/cthulhu/structural_navigation.py +++ b/src/cthulhu/structural_navigation.py @@ -57,7 +57,7 @@ from .ax_text import AXText from .ax_selection import AXSelection from .ax_utilities import AXUtilities -_settingsManager = settings_manager.getManager() +_settingsManager = None # Removed - use cthulhu.cthulhuApp.settingsManager ########################################################################### # # @@ -1224,7 +1224,7 @@ class StructuralNavigation: def _presentWithSayAll(self, obj, offset): if self._script.inSayAll() \ - and _settingsManager.getSetting('structNavInSayAll'): + and cthulhu.cthulhuApp.settingsManager.getSetting('structNavInSayAll'): self._script.sayAll(obj, offset) return True