diff --git a/src/cthulhu/action_presenter.py b/src/cthulhu/action_presenter.py index 345c8cb..c61b523 100644 --- a/src/cthulhu/action_presenter.py +++ b/src/cthulhu/action_presenter.py @@ -76,7 +76,8 @@ class ActionList(Gtk.Window): self.set_skip_taskbar_hint(True) self.set_skip_pager_hint(True) self.set_type_hint(Gdk.WindowTypeHint.DIALOG) - self.set_window_position(Gtk.WindowPosition.MOUSE) + # Note: set_window_position is deprecated, using move() instead + self.move(100, 100) # Position window at reasonable location self.set_default_size(400, 300) # Create scrolled window @@ -158,9 +159,9 @@ class ActionPresenter: self._obj = None self._window = None - # Initialize handlers and bindings + # Initialize handlers self._handlers = self.get_handlers(True) - self._bindings = keybindings.KeyBindings() + # _bindings will be initialized lazily in get_bindings() def get_handlers(self, refresh: bool = False) -> dict: """Returns a dictionary of input event handlers.""" @@ -207,7 +208,7 @@ class ActionPresenter: self._bindings = keybindings.KeyBindings() self._bindings.add(keybindings.KeyBinding( "a", - keybindings.DEFAULT_MODIFIER_MASK, + keybindings.defaultModifierMask, keybindings.CTHULHU_SHIFT_MODIFIER_MASK, self._handlers["show_actions_list"])) @@ -221,7 +222,7 @@ class ActionPresenter: debug.printTokens(debug.LEVEL_INFO, tokens, True) reason = "Action Presenter list is being destroyed" - app = AXUtilities.get_application(self._obj) + app = AXObject.get_application(self._obj) script = script_manager.getManager().getScript(app, self._obj) script_manager.getManager().setActiveScript(script, reason) @@ -247,48 +248,77 @@ class ActionPresenter: result = AXObject.do_named_action(self._obj, action) tokens = ["ACTION PRESENTER: Performing", action, "on", self._obj, "succeeded:", result] debug.printTokens(debug.LEVEL_INFO, tokens, True) - - if not result: - full_message = messages.ACTION_NOT_PERFORMED % action - cthulhu.presentMessage(full_message) - - self._gui.destroy() + + # Use idle_add for asynchronous destruction to allow action to complete + GLib.idle_add(self._gui.destroy) def present_with_time(self, obj, start_time: float) -> bool: """Presents accessible actions for the given object with timing.""" - if self._gui is not None: - msg = "ACTION PRESENTER: GUI already exists." - debug.printMessage(debug.LEVEL_INFO, msg, True) + try: + debug.printMessage(debug.LEVEL_INFO, f"ACTION PRESENTER: present_with_time called with obj: {obj}", True) + + if self._gui is not None: + msg = "ACTION PRESENTER: GUI already exists." + debug.printMessage(debug.LEVEL_INFO, msg, True) + return False + + if obj is None: + debug.printMessage(debug.LEVEL_INFO, "ACTION PRESENTER: obj is None, using locusOfFocus", True) + obj = cthulhu_state.locusOfFocus + + if obj is None: + debug.printMessage(debug.LEVEL_INFO, "ACTION PRESENTER: No object found, presenting NO_ACCESSIBLE_ACTIONS", True) + full_message = messages.NO_ACCESSIBLE_ACTIONS + cthulhu.presentMessage(full_message) + return False + + debug.printMessage(debug.LEVEL_INFO, f"ACTION PRESENTER: Getting actions for object: {obj}", True) + actions = AXObject.get_action_names(obj) + debug.printMessage(debug.LEVEL_INFO, f"ACTION PRESENTER: Found {len(actions) if actions else 0} actions: {actions}", True) + + if not actions: + debug.printMessage(debug.LEVEL_INFO, "ACTION PRESENTER: No actions found, presenting NO_ACCESSIBLE_ACTIONS", True) + full_message = messages.NO_ACCESSIBLE_ACTIONS + cthulhu.presentMessage(full_message) + return False + + debug.printMessage(debug.LEVEL_INFO, "ACTION PRESENTER: Creating GUI", True) + self._obj = obj + self._window = cthulhu_state.activeWindow + + self._gui = ActionList(self) + self._gui.populate_actions(actions) + + debug.printMessage(debug.LEVEL_INFO, "ACTION PRESENTER: GUI created successfully", True) + return True + except Exception as e: + debug.printMessage(debug.LEVEL_INFO, f"ACTION PRESENTER: ERROR in present_with_time: {e}", True) + import traceback + debug.printMessage(debug.LEVEL_INFO, f"ACTION PRESENTER: Traceback: {traceback.format_exc()}", True) return False - if obj is None: - obj = cthulhu_state.locusOfFocus - - if obj is None: - full_message = messages.NO_ACCESSIBLE_ACTIONS - cthulhu.presentMessage(full_message) - return False - - actions = AXObject.get_action_names(obj) - if not actions: - full_message = messages.NO_ACCESSIBLE_ACTIONS - cthulhu.presentMessage(full_message) - return False - - self._obj = obj - self._window = cthulhu_state.activeWindow - - self._gui = ActionList(self) - self._gui.populate_actions(actions) - - return True - def show_actions_list(self, script: script.Script = None, input_event: input_event.InputEvent = None) -> bool: """Shows the accessible actions list.""" - start_time = time.time() - return self.present_with_time(input_event.get_object(), start_time) + try: + msg = f"ACTION PRESENTER: show_actions_list called! input_event: {input_event}" + debug.printMessage(debug.LEVEL_INFO, msg, True) + + start_time = time.time() + + # Get object from input event if available, otherwise let present_with_time handle it + obj = input_event.getObject() if input_event else None + debug.printMessage(debug.LEVEL_INFO, f"ACTION PRESENTER: Object from input_event: {obj}", True) + + result = self.present_with_time(obj, start_time) + debug.printMessage(debug.LEVEL_INFO, f"ACTION PRESENTER: show_actions_list returning: {result}", True) + return result + except Exception as e: + debug.printMessage(debug.LEVEL_INFO, f"ACTION PRESENTER: ERROR in show_actions_list: {e}", True) + import traceback + debug.printMessage(debug.LEVEL_INFO, f"ACTION PRESENTER: Traceback: {traceback.format_exc()}", True) + return False _presenter = ActionPresenter() diff --git a/src/cthulhu/cthulhu.py b/src/cthulhu/cthulhu.py index 26b6420..a356f5d 100644 --- a/src/cthulhu/cthulhu.py +++ b/src/cthulhu/cthulhu.py @@ -142,14 +142,14 @@ class APIHelper: bindings.add(binding) # Register key grab at the system level - grab_ids = self.app.addKeyGrab(binding) + grab_ids = addKeyGrab(binding) logger.info(f"Key grab IDs: {grab_ids}") # For later removal if grab_ids: binding._grab_ids = grab_ids else: - logger.warning("No active script available - binding stored for later registration") + debug.printMessage(debug.LEVEL_INFO, "No active script available - binding stored for later registration", True) debug.printMessage(debug.LEVEL_INFO, f"Created binding: {binding.keysymstring} with modifiers {binding.modifiers}", True) logger.info("=== APIHelper.registerGestureByString completed ===") @@ -674,17 +674,19 @@ def loadUserSettings(script=None, inputEvent=None, skipReloadMessage=False): _storeXmodmap(_cthulhuModifiers) _createCthulhuXmodmap() - activePlugins = list(_settingsManager.getSetting('activePlugins')) - cthulhuApp.getPluginSystemManager().setActivePlugins(activePlugins) - + # Activate core systems FIRST before loading plugins _scriptManager.activate() _eventManager.activate() - - # Refresh keybindings to include plugin bindings (after script manager is active) - cthulhuApp.getPluginSystemManager().refresh_active_script_keybindings() cthulhuApp.getSignalManager().emitSignal('load-setting-begin') - # cthulhuApp.getPluginSystemManager().register_plugin_keybindings_with_active_script() + + # NOW load plugins after script system is ready + activePlugins = list(_settingsManager.getSetting('activePlugins')) + debug.printMessage(debug.LEVEL_INFO, f'CTHULHU: Loading active plugins: {activePlugins}', True) + cthulhuApp.getPluginSystemManager().setActivePlugins(activePlugins) + + # Refresh keybindings to include plugin bindings (after script manager is active) + cthulhuApp.getPluginSystemManager().refresh_active_script_keybindings() cthulhuApp.getSignalManager().emitSignal('load-setting-completed') diff --git a/src/cthulhu/plugin_system_manager.py b/src/cthulhu/plugin_system_manager.py index c868da5..e0feb65 100644 --- a/src/cthulhu/plugin_system_manager.py +++ b/src/cthulhu/plugin_system_manager.py @@ -135,7 +135,8 @@ class PluginSystemManager: active_script = cthulhu_state.activeScript for binding in bindings.keyBindings: active_script.getKeyBindings().add(binding) - grab_ids = self.app.addKeyGrab(binding) + from . import cthulhu + grab_ids = cthulhu.addKeyGrab(binding) if grab_ids: binding._grab_ids = grab_ids diff --git a/src/cthulhu/plugins/DisplayVersion/plugin.py b/src/cthulhu/plugins/DisplayVersion/plugin.py index e9aea2d..b3a80e5 100644 --- a/src/cthulhu/plugins/DisplayVersion/plugin.py +++ b/src/cthulhu/plugins/DisplayVersion/plugin.py @@ -12,6 +12,7 @@ import logging from cthulhu.plugin import Plugin, cthulhu_hookimpl from cthulhu import cthulhuVersion +from cthulhu import debug logger = logging.getLogger(__name__) @@ -21,8 +22,9 @@ class DisplayVersion(Plugin): def __init__(self, *args, **kwargs): """Initialize the plugin.""" super().__init__(*args, **kwargs) - logger.info("DisplayVersion plugin initialized") + debug.printMessage(debug.LEVEL_INFO, "DisplayVersion: Plugin initialized", True) self._kb_binding = None + self._activated = False @cthulhu_hookimpl def activate(self, plugin=None): @@ -31,32 +33,24 @@ class DisplayVersion(Plugin): if plugin is not None and plugin is not self: return - try: - logger.info("=== DisplayVersion plugin activation starting ===") - logger.info(f"Plugin name: {self.name}") - logger.info(f"App reference: {self.app}") + # Prevent duplicate activation + if self._activated: + debug.printMessage(debug.LEVEL_INFO, "DisplayVersion: Already activated, skipping", True) + return - # Also write to a debug file - with open('/tmp/displayversion_debug.log', 'a') as f: - f.write("=== DisplayVersion plugin activation starting ===\n") - f.write(f"Plugin name: {self.name}\n") - f.write(f"App reference: {self.app}\n") + try: + # Use debug.printMessage to ensure it goes to the main debug log + debug.printMessage(debug.LEVEL_INFO, "DisplayVersion: Plugin activation starting", True) + debug.printMessage(debug.LEVEL_INFO, f"DisplayVersion: Plugin name: {self.name}", True) # Check if we have access to the API helper if not self.app: - logger.error("DisplayVersion: No app reference available!") + debug.printMessage(debug.LEVEL_INFO, "DisplayVersion: ERROR - No app reference available!", True) return - api_helper = self.app.getAPIHelper() - if not api_helper: - logger.error("DisplayVersion: No API helper available!") - return - - logger.info(f"API helper: {api_helper}") - # Register keyboard shortcut gesture_string = 'kb:cthulhu+shift+v' - logger.info(f"DisplayVersion: Attempting to register gesture: {gesture_string}") + debug.printMessage(debug.LEVEL_INFO, f"DisplayVersion: Registering gesture: {gesture_string}", True) self._kb_binding = self.registerGestureByString( self.speakText, @@ -64,30 +58,17 @@ class DisplayVersion(Plugin): gesture_string ) - logger.info(f"DisplayVersion: Registered keybinding result: {self._kb_binding}") if self._kb_binding: - logger.info(f"Binding keysymstring: {self._kb_binding.keysymstring}") - logger.info(f"Binding modifiers: {self._kb_binding.modifiers}") - logger.info(f"Binding modifier_mask: {self._kb_binding.modifier_mask}") - logger.info(f"Binding handler: {self._kb_binding.handler}") - - with open('/tmp/displayversion_debug.log', 'a') as f: - f.write(f"SUCCESS: Keybinding created!\n") - f.write(f" keysymstring: {self._kb_binding.keysymstring}\n") - f.write(f" modifiers: {self._kb_binding.modifiers}\n") - f.write(f" modifier_mask: {self._kb_binding.modifier_mask}\n") + debug.printMessage(debug.LEVEL_INFO, f"DisplayVersion: Keybinding registered successfully", True) else: - logger.error("DisplayVersion: Failed to create keybinding!") - with open('/tmp/displayversion_debug.log', 'a') as f: - f.write("ERROR: Failed to create keybinding!\n") + debug.printMessage(debug.LEVEL_INFO, "DisplayVersion: ERROR - Failed to create keybinding!", True) - logger.info("=== DisplayVersion plugin activation completed ===") - with open('/tmp/displayversion_debug.log', 'a') as f: - f.write("=== DisplayVersion plugin activation completed ===\n\n") + self._activated = True + debug.printMessage(debug.LEVEL_INFO, "DisplayVersion: Plugin activation completed", True) except Exception as e: - logger.error(f"Error activating DisplayVersion plugin: {e}") + debug.printMessage(debug.LEVEL_INFO, f"DisplayVersion: ERROR activating plugin: {e}", True) import traceback - logger.error(traceback.format_exc()) + debug.printMessage(debug.LEVEL_INFO, f"DisplayVersion: {traceback.format_exc()}", True) @cthulhu_hookimpl def deactivate(self, plugin=None): @@ -96,12 +77,13 @@ class DisplayVersion(Plugin): if plugin is not None and plugin is not self: return - logger.info("Deactivating DisplayVersion plugin") + self._activated = False + debug.printMessage(debug.LEVEL_INFO, "DisplayVersion: Plugin deactivated", True) def speakText(self, script=None, inputEvent=None): """Speak the Cthulhu version when shortcut is pressed.""" try: - logger.info("DisplayVersion plugin: speakText called") + debug.printMessage(debug.LEVEL_INFO, "DisplayVersion: speakText called", True) if self.app: state = self.app.getDynamicApiManager().getAPI('CthulhuState') if state.activeScript: @@ -111,5 +93,5 @@ class DisplayVersion(Plugin): ) return True except Exception as e: - logger.error(f"Error in DisplayVersion speakText: {e}") + debug.printMessage(debug.LEVEL_INFO, f"DisplayVersion: ERROR in speakText: {e}", True) return False diff --git a/src/cthulhu/plugins/IndentationAudio/plugin.py b/src/cthulhu/plugins/IndentationAudio/plugin.py index 5e38a62..84318ad 100644 --- a/src/cthulhu/plugins/IndentationAudio/plugin.py +++ b/src/cthulhu/plugins/IndentationAudio/plugin.py @@ -44,7 +44,8 @@ class IndentationAudio(Plugin): self._tone_duration = 0.15 # Seconds self._max_frequency = 1200 # Cap frequency to avoid harsh sounds - logger.info("IndentationAudio plugin initialized") + self._activated = False + debug.printMessage(debug.LEVEL_INFO, "IndentationAudio: Plugin initialized", True) @cthulhu_hookimpl def activate(self, plugin=None): @@ -52,8 +53,13 @@ class IndentationAudio(Plugin): if plugin is not None and plugin is not self: return + # Prevent duplicate activation + if self._activated: + debug.printMessage(debug.LEVEL_INFO, "IndentationAudio: Already activated, skipping", True) + return + try: - logger.info("=== IndentationAudio plugin activation starting ===") + debug.printMessage(debug.LEVEL_INFO, "IndentationAudio: Plugin activation starting", True) # Initialize sound player if SOUND_AVAILABLE: @@ -69,11 +75,12 @@ class IndentationAudio(Plugin): # Connect to text caret movement events self._connect_to_events() - logger.info("IndentationAudio plugin activated successfully") + self._activated = True + debug.printMessage(debug.LEVEL_INFO, "IndentationAudio: Plugin activated successfully", True) return True except Exception as e: - logger.error(f"Failed to activate IndentationAudio plugin: {e}") + debug.printMessage(debug.LEVEL_INFO, f"IndentationAudio: ERROR activating plugin: {e}", True) return False @cthulhu_hookimpl @@ -83,7 +90,7 @@ class IndentationAudio(Plugin): return try: - logger.info("=== IndentationAudio plugin deactivation starting ===") + debug.printMessage(debug.LEVEL_INFO, "IndentationAudio: Plugin deactivation starting", True) # Disconnect from events self._disconnect_from_events() @@ -91,11 +98,12 @@ class IndentationAudio(Plugin): # Clear tracking data self._last_indentation_level.clear() - logger.info("IndentationAudio plugin deactivated successfully") + self._activated = False + debug.printMessage(debug.LEVEL_INFO, "IndentationAudio: Plugin deactivated successfully", True) return True except Exception as e: - logger.error(f"Failed to deactivate IndentationAudio plugin: {e}") + debug.printMessage(debug.LEVEL_INFO, f"IndentationAudio: ERROR deactivating plugin: {e}", True) return False def _register_keybinding(self): diff --git a/src/cthulhu/plugins/PluginManager/plugin.py b/src/cthulhu/plugins/PluginManager/plugin.py index a67a5d2..78e4b24 100644 --- a/src/cthulhu/plugins/PluginManager/plugin.py +++ b/src/cthulhu/plugins/PluginManager/plugin.py @@ -35,8 +35,9 @@ class PluginManager(Plugin): self._kb_binding = None self._dialog = None self._plugin_checkboxes = {} + self._activated = False - logger.info("PluginManager plugin initialized") + debug.printMessage(debug.LEVEL_INFO, "PluginManager: Plugin initialized", True) @cthulhu_hookimpl def activate(self, plugin=None): @@ -44,17 +45,23 @@ class PluginManager(Plugin): if plugin is not None and plugin is not self: return + # Prevent duplicate activation + if self._activated: + debug.printMessage(debug.LEVEL_INFO, "PluginManager: Already activated, skipping", True) + return + try: - logger.info("=== PluginManager plugin activation starting ===") + debug.printMessage(debug.LEVEL_INFO, "PluginManager: Plugin activation starting", True) # Register keybinding for opening plugin manager (Cthulhu+Shift+P) self._register_keybinding() - logger.info("PluginManager plugin activated successfully") + self._activated = True + debug.printMessage(debug.LEVEL_INFO, "PluginManager: Plugin activated successfully", True) return True except Exception as e: - logger.error(f"Failed to activate PluginManager plugin: {e}") + debug.printMessage(debug.LEVEL_INFO, f"PluginManager: ERROR activating plugin: {e}", True) return False @cthulhu_hookimpl @@ -64,25 +71,26 @@ class PluginManager(Plugin): return try: - logger.info("=== PluginManager plugin deactivation starting ===") + debug.printMessage(debug.LEVEL_INFO, "PluginManager: Plugin deactivation starting", True) # Close dialog if open if self._dialog: self._dialog.destroy() self._dialog = None - logger.info("PluginManager plugin deactivated successfully") + self._activated = False + debug.printMessage(debug.LEVEL_INFO, "PluginManager: Plugin deactivated successfully", True) return True except Exception as e: - logger.error(f"Failed to deactivate PluginManager plugin: {e}") + debug.printMessage(debug.LEVEL_INFO, f"PluginManager: ERROR deactivating plugin: {e}", True) return False def _register_keybinding(self): """Register the Cthulhu+Shift+P keybinding for opening plugin manager.""" try: if not self.app: - logger.error("PluginManager: No app reference available for keybinding") + debug.printMessage(debug.LEVEL_INFO, "PluginManager: ERROR - No app reference available for keybinding", True) return # Register Cthulhu+Shift+P keybinding @@ -96,13 +104,12 @@ class PluginManager(Plugin): ) if self._kb_binding: - logger.info(f"PluginManager: Registered keybinding {gesture_string}") debug.printMessage(debug.LEVEL_INFO, f"PluginManager: Registered keybinding {gesture_string}", True) else: - logger.error(f"PluginManager: Failed to register keybinding {gesture_string}") + debug.printMessage(debug.LEVEL_INFO, f"PluginManager: ERROR - Failed to register keybinding {gesture_string}", True) except Exception as e: - logger.error(f"PluginManager: Error registering keybinding: {e}") + debug.printMessage(debug.LEVEL_INFO, f"PluginManager: ERROR registering keybinding: {e}", True) def _open_plugin_manager(self, script, inputEvent=None): """Open the plugin manager dialog."""