Improvements to the plugins tab.
This commit is contained in:
@@ -106,6 +106,10 @@ if louis and not tablesdir:
|
|||||||
DATE_FORMAT_ABBREVIATED_MDY, DATE_FORMAT_ABBREVIATED_YMD) = list(range(16))
|
DATE_FORMAT_ABBREVIATED_MDY, DATE_FORMAT_ABBREVIATED_YMD) = list(range(16))
|
||||||
|
|
||||||
class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
|
class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
|
||||||
|
PLUGIN_COL_ENABLED = 0
|
||||||
|
PLUGIN_COL_DISPLAY = 1
|
||||||
|
PLUGIN_COL_CAN_TOGGLE = 2
|
||||||
|
PLUGIN_COL_NAME = 3
|
||||||
|
|
||||||
def __init__(self, fileName, windowName, prefsDict):
|
def __init__(self, fileName, windowName, prefsDict):
|
||||||
"""Initialize the Cthulhu configuration GUI.
|
"""Initialize the Cthulhu configuration GUI.
|
||||||
@@ -135,8 +139,11 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
|
|||||||
self.planeCellRendererText = None
|
self.planeCellRendererText = None
|
||||||
self.pronunciationModel = None
|
self.pronunciationModel = None
|
||||||
self.pronunciationView = None
|
self.pronunciationView = None
|
||||||
self._plugin_checkboxes = {}
|
self._plugin_treeview = None
|
||||||
self._plugin_listbox = None
|
self._plugin_model = None
|
||||||
|
self._plugin_iters = {}
|
||||||
|
self._plugin_enabled_iter = None
|
||||||
|
self._plugin_disabled_iter = None
|
||||||
self._plugin_sources = []
|
self._plugin_sources = []
|
||||||
self._plugin_sources_entry = None
|
self._plugin_sources_entry = None
|
||||||
self._plugin_sources_listbox = None
|
self._plugin_sources_listbox = None
|
||||||
@@ -420,9 +427,41 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
|
|||||||
pluginsScrolled = Gtk.ScrolledWindow()
|
pluginsScrolled = Gtk.ScrolledWindow()
|
||||||
pluginsScrolled.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
|
pluginsScrolled.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
|
||||||
pluginsScrolled.set_size_request(-1, 200)
|
pluginsScrolled.set_size_request(-1, 200)
|
||||||
self._plugin_listbox = Gtk.ListBox()
|
self._plugin_model = Gtk.TreeStore(
|
||||||
self._plugin_listbox.set_selection_mode(Gtk.SelectionMode.NONE)
|
GObject.TYPE_BOOLEAN, # enabled
|
||||||
pluginsScrolled.add(self._plugin_listbox)
|
GObject.TYPE_STRING, # display text
|
||||||
|
GObject.TYPE_BOOLEAN, # can toggle
|
||||||
|
GObject.TYPE_STRING, # plugin name
|
||||||
|
)
|
||||||
|
self._plugin_treeview = Gtk.TreeView(model=self._plugin_model)
|
||||||
|
self._plugin_treeview.set_headers_visible(True)
|
||||||
|
self._plugin_treeview.set_enable_search(False)
|
||||||
|
self._plugin_treeview.connect("key-press-event", self._on_plugin_tree_key_press)
|
||||||
|
self._plugin_treeview.connect("row-activated", self._on_plugin_tree_row_activated)
|
||||||
|
|
||||||
|
toggle_renderer = Gtk.CellRendererToggle()
|
||||||
|
toggle_renderer.set_activatable(True)
|
||||||
|
toggle_renderer.connect("toggled", self._on_plugin_tree_toggled)
|
||||||
|
toggle_column = Gtk.TreeViewColumn(
|
||||||
|
"Enabled",
|
||||||
|
toggle_renderer,
|
||||||
|
active=self.PLUGIN_COL_ENABLED,
|
||||||
|
activatable=self.PLUGIN_COL_CAN_TOGGLE
|
||||||
|
)
|
||||||
|
toggle_column.add_attribute(toggle_renderer, "visible", self.PLUGIN_COL_CAN_TOGGLE)
|
||||||
|
self._plugin_treeview.append_column(toggle_column)
|
||||||
|
|
||||||
|
text_renderer = Gtk.CellRendererText()
|
||||||
|
text_renderer.set_property("ellipsize", Pango.EllipsizeMode.END)
|
||||||
|
text_column = Gtk.TreeViewColumn(
|
||||||
|
"Plugin",
|
||||||
|
text_renderer,
|
||||||
|
text=self.PLUGIN_COL_DISPLAY
|
||||||
|
)
|
||||||
|
text_column.set_expand(True)
|
||||||
|
self._plugin_treeview.append_column(text_column)
|
||||||
|
|
||||||
|
pluginsScrolled.add(self._plugin_treeview)
|
||||||
pluginsFrameBox.pack_start(pluginsScrolled, True, True, 0)
|
pluginsFrameBox.pack_start(pluginsScrolled, True, True, 0)
|
||||||
|
|
||||||
pluginsPage.pack_start(pluginsFrame, True, True, 0)
|
pluginsPage.pack_start(pluginsFrame, True, True, 0)
|
||||||
@@ -496,12 +535,12 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
|
|||||||
listbox.remove(child)
|
listbox.remove(child)
|
||||||
|
|
||||||
def _populate_plugin_list(self):
|
def _populate_plugin_list(self):
|
||||||
if not self._plugin_listbox:
|
if not self._plugin_treeview:
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self._clear_listbox(self._plugin_listbox)
|
self._plugin_model.clear()
|
||||||
self._plugin_checkboxes.clear()
|
self._plugin_iters = {}
|
||||||
self._available_plugins = set()
|
self._available_plugins = set()
|
||||||
self._plugin_canonical_map = {}
|
self._plugin_canonical_map = {}
|
||||||
self._plugin_group_map = {}
|
self._plugin_group_map = {}
|
||||||
@@ -526,60 +565,62 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
|
|||||||
canonical_counts[canonical] = canonical_counts.get(canonical, 0) + 1
|
canonical_counts[canonical] = canonical_counts.get(canonical, 0) + 1
|
||||||
if info.builtin:
|
if info.builtin:
|
||||||
canonical_builtins[canonical] = True
|
canonical_builtins[canonical] = True
|
||||||
|
|
||||||
|
self._plugin_enabled_iter = self._plugin_model.append(
|
||||||
|
None,
|
||||||
|
[False, "Enabled plugins", False, ""]
|
||||||
|
)
|
||||||
|
self._plugin_disabled_iter = self._plugin_model.append(
|
||||||
|
None,
|
||||||
|
[False, "Disabled plugins", False, ""]
|
||||||
|
)
|
||||||
|
|
||||||
for plugin_info in sorted(plugin_infos, key=lambda item: (item.get_name() or item.get_module_name()).lower()):
|
for plugin_info in sorted(plugin_infos, key=lambda item: (item.get_name() or item.get_module_name()).lower()):
|
||||||
plugin_name = plugin_info.get_module_name()
|
plugin_name = plugin_info.get_module_name()
|
||||||
canonical_name = plugin_info.get_canonical_name()
|
canonical_name = plugin_info.get_canonical_name()
|
||||||
if plugin_info.hidden or canonical_name == "PluginManager":
|
if plugin_info.hidden or canonical_name == "PluginManager":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
row = Gtk.ListBoxRow()
|
self._plugin_canonical_map[plugin_name] = canonical_name
|
||||||
row.set_activatable(False)
|
|
||||||
hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
|
|
||||||
hbox.set_border_width(5)
|
|
||||||
|
|
||||||
checkbox = Gtk.CheckButton()
|
|
||||||
is_active = (
|
is_active = (
|
||||||
plugin_name in active_plugins
|
plugin_name in active_plugins
|
||||||
or plugin_name.lower() in active_plugins_lower
|
or plugin_name.lower() in active_plugins_lower
|
||||||
or (plugin_info.preferred_alias and (canonical_name in active_plugins or canonical_name.lower() in active_plugins_lower))
|
or (plugin_info.preferred_alias and (canonical_name in active_plugins or canonical_name.lower() in active_plugins_lower))
|
||||||
or plugin_info.builtin
|
or plugin_info.builtin
|
||||||
)
|
)
|
||||||
|
|
||||||
|
can_toggle = True
|
||||||
if canonical_builtins.get(canonical_name) and not plugin_info.builtin:
|
if canonical_builtins.get(canonical_name) and not plugin_info.builtin:
|
||||||
is_active = False
|
is_active = False
|
||||||
checkbox.set_active(is_active)
|
can_toggle = False
|
||||||
if plugin_info.builtin:
|
|
||||||
checkbox.set_sensitive(False)
|
|
||||||
elif canonical_builtins.get(canonical_name):
|
|
||||||
checkbox.set_sensitive(False)
|
|
||||||
checkbox.connect("toggled", self._on_plugin_checkbox_toggled, plugin_name)
|
|
||||||
|
|
||||||
display_name = GLib.markup_escape_text(plugin_info.get_name() or plugin_name)
|
if plugin_info.builtin:
|
||||||
info_text = f"<b>{display_name}</b>"
|
can_toggle = False
|
||||||
|
|
||||||
|
display_name = plugin_info.get_name() or plugin_name
|
||||||
|
info_text = display_name
|
||||||
description = plugin_info.get_description()
|
description = plugin_info.get_description()
|
||||||
if description:
|
if description:
|
||||||
info_text += f"\n{GLib.markup_escape_text(description)}"
|
info_text += f" - {description}"
|
||||||
version = plugin_info.get_version()
|
version = plugin_info.get_version()
|
||||||
if version:
|
if version:
|
||||||
info_text += f" (v{GLib.markup_escape_text(version)})"
|
info_text += f" (v{version})"
|
||||||
if canonical_counts.get(canonical_name, 0) > 1:
|
if canonical_counts.get(canonical_name, 0) > 1:
|
||||||
info_text += f"\nSource: {GLib.markup_escape_text(plugin_info.get_source_label())}"
|
info_text += f" - Source: {plugin_info.get_source_label()}"
|
||||||
if canonical_builtins.get(canonical_name) and not plugin_info.builtin:
|
if canonical_builtins.get(canonical_name) and not plugin_info.builtin:
|
||||||
info_text += "\nDisabled because a builtin plugin uses this name."
|
info_text += " - Disabled because a builtin plugin uses this name."
|
||||||
|
|
||||||
label = Gtk.Label()
|
parent_iter = self._plugin_enabled_iter if is_active else self._plugin_disabled_iter
|
||||||
label.set_markup(info_text)
|
tree_iter = self._plugin_model.append(
|
||||||
label.set_halign(Gtk.Align.START)
|
parent_iter,
|
||||||
label.set_line_wrap(True)
|
[is_active, info_text, can_toggle, plugin_name]
|
||||||
|
)
|
||||||
hbox.pack_start(checkbox, False, False, 0)
|
self._plugin_iters[plugin_name] = tree_iter
|
||||||
hbox.pack_start(label, True, True, 0)
|
|
||||||
row.add(hbox)
|
|
||||||
self._plugin_listbox.add(row)
|
|
||||||
self._plugin_checkboxes[plugin_name] = checkbox
|
|
||||||
self._plugin_canonical_map[plugin_name] = canonical_name
|
|
||||||
self._plugin_group_map.setdefault(canonical_name, []).append(plugin_name)
|
self._plugin_group_map.setdefault(canonical_name, []).append(plugin_name)
|
||||||
|
|
||||||
self._plugin_listbox.show_all()
|
self._plugin_treeview.collapse_all()
|
||||||
|
self._plugin_treeview.show_all()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
debug.printMessage(debug.LEVEL_WARNING, f"PREFERENCES DIALOG: Plugin list build failed: {e}", True)
|
debug.printMessage(debug.LEVEL_WARNING, f"PREFERENCES DIALOG: Plugin list build failed: {e}", True)
|
||||||
|
|
||||||
@@ -685,30 +726,92 @@ class CthulhuSetupGUI(cthulhu_gtkbuilder.GtkBuilderWrapper):
|
|||||||
self._set_plugin_update_status(message)
|
self._set_plugin_update_status(message)
|
||||||
self._populate_plugin_list()
|
self._populate_plugin_list()
|
||||||
|
|
||||||
def _on_plugin_checkbox_toggled(self, checkbox, plugin_name):
|
def _on_plugin_tree_toggled(self, renderer, path):
|
||||||
if not checkbox.get_active():
|
if not self._plugin_model:
|
||||||
return
|
return
|
||||||
canonical_name = self._plugin_canonical_map.get(plugin_name)
|
|
||||||
if not canonical_name:
|
tree_iter = self._plugin_model.get_iter(path)
|
||||||
|
if not tree_iter:
|
||||||
return
|
return
|
||||||
for other_name in self._plugin_group_map.get(canonical_name, []):
|
|
||||||
if other_name == plugin_name:
|
can_toggle = self._plugin_model.get_value(tree_iter, self.PLUGIN_COL_CAN_TOGGLE)
|
||||||
continue
|
if not can_toggle:
|
||||||
other_checkbox = self._plugin_checkboxes.get(other_name)
|
return
|
||||||
if other_checkbox and other_checkbox.get_active():
|
|
||||||
other_checkbox.set_active(False)
|
plugin_name = self._plugin_model.get_value(tree_iter, self.PLUGIN_COL_NAME)
|
||||||
|
if not plugin_name:
|
||||||
|
return
|
||||||
|
|
||||||
|
current_active = self._plugin_model.get_value(tree_iter, self.PLUGIN_COL_ENABLED)
|
||||||
|
new_active = not current_active
|
||||||
|
|
||||||
|
if new_active:
|
||||||
|
canonical_name = self._plugin_canonical_map.get(plugin_name)
|
||||||
|
for other_name in self._plugin_group_map.get(canonical_name, []):
|
||||||
|
if other_name == plugin_name:
|
||||||
|
continue
|
||||||
|
self._set_plugin_row_active(other_name, False)
|
||||||
|
|
||||||
|
self._set_plugin_row_active(plugin_name, new_active)
|
||||||
|
|
||||||
|
def _on_plugin_tree_key_press(self, widget, event):
|
||||||
|
if event.keyval != Gdk.KEY_space:
|
||||||
|
return False
|
||||||
|
|
||||||
|
selection = self._plugin_treeview.get_selection()
|
||||||
|
model, tree_iter = selection.get_selected()
|
||||||
|
if not tree_iter:
|
||||||
|
return False
|
||||||
|
|
||||||
|
path = model.get_path(tree_iter)
|
||||||
|
self._on_plugin_tree_toggled(None, path)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _on_plugin_tree_row_activated(self, treeview, path, column):
|
||||||
|
self._on_plugin_tree_toggled(None, path)
|
||||||
|
|
||||||
|
def _set_plugin_row_active(self, plugin_name, is_active):
|
||||||
|
tree_iter = self._plugin_iters.get(plugin_name)
|
||||||
|
if not tree_iter:
|
||||||
|
return
|
||||||
|
|
||||||
|
current_active = self._plugin_model.get_value(tree_iter, self.PLUGIN_COL_ENABLED)
|
||||||
|
if current_active == is_active:
|
||||||
|
return
|
||||||
|
|
||||||
|
can_toggle = self._plugin_model.get_value(tree_iter, self.PLUGIN_COL_CAN_TOGGLE)
|
||||||
|
display_text = self._plugin_model.get_value(tree_iter, self.PLUGIN_COL_DISPLAY)
|
||||||
|
|
||||||
|
selection = self._plugin_treeview.get_selection()
|
||||||
|
model, selected_iter = selection.get_selected()
|
||||||
|
was_selected = selected_iter == tree_iter
|
||||||
|
|
||||||
|
self._plugin_model.remove(tree_iter)
|
||||||
|
|
||||||
|
parent_iter = self._plugin_enabled_iter if is_active else self._plugin_disabled_iter
|
||||||
|
new_iter = self._plugin_model.append(
|
||||||
|
parent_iter,
|
||||||
|
[is_active, display_text, can_toggle, plugin_name]
|
||||||
|
)
|
||||||
|
self._plugin_iters[plugin_name] = new_iter
|
||||||
|
|
||||||
|
if was_selected:
|
||||||
|
path = self._plugin_model.get_path(new_iter)
|
||||||
|
self._plugin_treeview.expand_to_path(path)
|
||||||
|
selection.select_path(path)
|
||||||
|
|
||||||
def _get_active_plugins_from_ui(self):
|
def _get_active_plugins_from_ui(self):
|
||||||
existing_plugins = list(self.prefsDict.get("activePlugins", settings.activePlugins) or [])
|
existing_plugins = list(self.prefsDict.get("activePlugins", settings.activePlugins) or [])
|
||||||
preserved_plugins = [
|
preserved_plugins = [
|
||||||
name for name in existing_plugins
|
name for name in existing_plugins
|
||||||
if name not in self._plugin_checkboxes and name in self._available_plugins
|
if name not in self._plugin_iters and name in self._available_plugins
|
||||||
]
|
]
|
||||||
selected_plugins = []
|
selected_plugins = []
|
||||||
for canonical_name, plugin_names in self._plugin_group_map.items():
|
for canonical_name, plugin_names in self._plugin_group_map.items():
|
||||||
active_in_group = [
|
active_in_group = [
|
||||||
name for name in plugin_names
|
name for name in plugin_names
|
||||||
if self._plugin_checkboxes.get(name) and self._plugin_checkboxes[name].get_active()
|
if self._plugin_iters.get(name)
|
||||||
|
and self._plugin_model.get_value(self._plugin_iters[name], self.PLUGIN_COL_ENABLED)
|
||||||
]
|
]
|
||||||
if active_in_group:
|
if active_in_group:
|
||||||
selected_plugins.append(active_in_group[-1])
|
selected_plugins.append(active_in_group[-1])
|
||||||
|
|||||||
@@ -876,9 +876,25 @@ class KeyboardEvent(InputEvent):
|
|||||||
|
|
||||||
return method.__func__ == self._handler.function
|
return method.__func__ == self._handler.function
|
||||||
|
|
||||||
|
def _should_interrupt_presentation_on_press(self):
|
||||||
|
if not settings.gameMode:
|
||||||
|
return True
|
||||||
|
|
||||||
|
if cthulhu_state.bypassNextCommand and not self.is_modifier_key():
|
||||||
|
return False
|
||||||
|
|
||||||
|
if self._handler or self._consumer:
|
||||||
|
return True
|
||||||
|
|
||||||
|
if self.isCthulhuModifier():
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
def _present(self, inputEvent=None):
|
def _present(self, inputEvent=None):
|
||||||
if self.is_pressed_key():
|
if self.is_pressed_key():
|
||||||
self._script.presentationInterrupt()
|
if self._should_interrupt_presentation_on_press():
|
||||||
|
self._script.presentationInterrupt()
|
||||||
|
|
||||||
if self._script.learnModePresenter.is_active():
|
if self._script.learnModePresenter.is_active():
|
||||||
return False
|
return False
|
||||||
|
|||||||
@@ -2333,6 +2333,12 @@ STOP_CTHULHU = _("Cthulhu lurks beneath the waves.")
|
|||||||
SLEEP_MODE_ENABLED_FOR = _("Sleep mode enabled for %s")
|
SLEEP_MODE_ENABLED_FOR = _("Sleep mode enabled for %s")
|
||||||
SLEEP_MODE_DISABLED_FOR = _("Sleep mode disabled for %s")
|
SLEEP_MODE_DISABLED_FOR = _("Sleep mode disabled for %s")
|
||||||
|
|
||||||
|
# Translators: This message is presented to the user when game mode is enabled.
|
||||||
|
GAME_MODE_ENABLED = _("Game mode enabled")
|
||||||
|
|
||||||
|
# Translators: This message is presented to the user when game mode is disabled.
|
||||||
|
GAME_MODE_DISABLED = _("Game mode disabled")
|
||||||
|
|
||||||
# Translators: This message means speech synthesis is not installed or working.
|
# Translators: This message means speech synthesis is not installed or working.
|
||||||
SPEECH_UNAVAILABLE = _("Speech is unavailable.")
|
SPEECH_UNAVAILABLE = _("Speech is unavailable.")
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,14 @@ logger = logging.getLogger(__name__)
|
|||||||
if PLUGIN_DEBUG:
|
if PLUGIN_DEBUG:
|
||||||
logger.setLevel(logging.DEBUG)
|
logger.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
|
LEGACY_PLUGIN_NAME_ALIASES = {
|
||||||
|
"ocrdesktop": "OCR",
|
||||||
|
}
|
||||||
|
|
||||||
|
LEGACY_PLUGIN_DIR_ALIASES = {
|
||||||
|
"OCRDesktop": "OCR",
|
||||||
|
}
|
||||||
|
|
||||||
_manager = None
|
_manager = None
|
||||||
|
|
||||||
def getManager():
|
def getManager():
|
||||||
@@ -450,6 +458,18 @@ class PluginSystemManager:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
canonical_name = os.path.basename(plugin_dir)
|
canonical_name = os.path.basename(plugin_dir)
|
||||||
|
canonical_override = LEGACY_PLUGIN_DIR_ALIASES.get(canonical_name)
|
||||||
|
if canonical_override:
|
||||||
|
if canonical_override in self._plugin_name_index:
|
||||||
|
logger.info(
|
||||||
|
f"Skipping deprecated plugin directory {canonical_name} because "
|
||||||
|
f"{canonical_override} is already registered."
|
||||||
|
)
|
||||||
|
return True
|
||||||
|
logger.info(
|
||||||
|
f"Registering deprecated plugin directory {canonical_name} as {canonical_override}."
|
||||||
|
)
|
||||||
|
canonical_name = canonical_override
|
||||||
origin, source_id = self._get_origin_info(plugin_dir)
|
origin, source_id = self._get_origin_info(plugin_dir)
|
||||||
module_name = self._make_unique_plugin_id(canonical_name, source_id)
|
module_name = self._make_unique_plugin_id(canonical_name, source_id)
|
||||||
if canonical_name != module_name:
|
if canonical_name != module_name:
|
||||||
@@ -623,6 +643,25 @@ class PluginSystemManager:
|
|||||||
logger.info(f"PLUGIN SYSTEM: setActivePlugins called with: {activePlugins}")
|
logger.info(f"PLUGIN SYSTEM: setActivePlugins called with: {activePlugins}")
|
||||||
logger.info(f"Setting active plugins: {activePlugins}")
|
logger.info(f"Setting active plugins: {activePlugins}")
|
||||||
|
|
||||||
|
original_active = list(activePlugins or [])
|
||||||
|
normalized_requested = []
|
||||||
|
for name in original_active:
|
||||||
|
if not name:
|
||||||
|
continue
|
||||||
|
name_lower = name.lower()
|
||||||
|
alias = LEGACY_PLUGIN_NAME_ALIASES.get(name_lower)
|
||||||
|
if alias:
|
||||||
|
logger.info(f"Mapping legacy plugin name {name} to {alias}")
|
||||||
|
name = alias
|
||||||
|
normalized_requested.append(name)
|
||||||
|
activePlugins = normalized_requested
|
||||||
|
if activePlugins != original_active:
|
||||||
|
try:
|
||||||
|
from . import settings as settings_module
|
||||||
|
settings_module.activePlugins = list(activePlugins)
|
||||||
|
except Exception:
|
||||||
|
logger.debug("Unable to normalize settings.activePlugins for legacy plugin aliases.")
|
||||||
|
|
||||||
# Make sure we have scanned for plugins first
|
# Make sure we have scanned for plugins first
|
||||||
if not self._plugins:
|
if not self._plugins:
|
||||||
logger.info("No plugins found, rescanning...")
|
logger.info("No plugins found, rescanning...")
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
|
# Copyright (c) 2025 Stormux
|
||||||
|
#
|
||||||
|
# This library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
"""GameMode plugin package."""
|
||||||
|
|
||||||
|
from .plugin import GameMode
|
||||||
|
|
||||||
|
__all__ = ['GameMode']
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
gamemode_python_sources = files([
|
||||||
|
'__init__.py',
|
||||||
|
'plugin.py'
|
||||||
|
])
|
||||||
|
|
||||||
|
python3.install_sources(
|
||||||
|
gamemode_python_sources,
|
||||||
|
subdir: 'cthulhu/plugins/GameMode'
|
||||||
|
)
|
||||||
|
|
||||||
|
install_data(
|
||||||
|
'plugin.info',
|
||||||
|
install_dir: python3.get_install_dir() / 'cthulhu' / 'plugins' / 'GameMode'
|
||||||
|
)
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
name = GameMode
|
||||||
|
version = 1.0.0
|
||||||
|
description = Reduces speech interruptions while holding keys
|
||||||
|
authors = Stormux <storm_dragon@stormux.org>
|
||||||
|
website = https://git.stormux.org/storm/cthulhu
|
||||||
|
copyright = Copyright 2025
|
||||||
|
builtin = false
|
||||||
|
hidden = false
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
|
# Copyright (c) 2025 Stormux
|
||||||
|
#
|
||||||
|
# This library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
"""GameMode plugin for Cthulhu - reduce speech interruption while gaming."""
|
||||||
|
|
||||||
|
from cthulhu.plugin import Plugin, cthulhu_hookimpl
|
||||||
|
from cthulhu import debug
|
||||||
|
from cthulhu import messages
|
||||||
|
from cthulhu import settings_manager
|
||||||
|
|
||||||
|
settingsManager = settings_manager.getManager()
|
||||||
|
|
||||||
|
|
||||||
|
class GameMode(Plugin):
|
||||||
|
"""Plugin that reduces speech interruption while holding keys."""
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
"""Initialize the GameMode plugin."""
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self._activated = False
|
||||||
|
self._kbBinding = None
|
||||||
|
|
||||||
|
debug.printMessage(debug.LEVEL_INFO, "GameMode: Plugin initialized", True)
|
||||||
|
|
||||||
|
@cthulhu_hookimpl
|
||||||
|
def activate(self, plugin=None):
|
||||||
|
"""Activate the GameMode plugin."""
|
||||||
|
if plugin is not None and plugin is not self:
|
||||||
|
return
|
||||||
|
|
||||||
|
if self._activated:
|
||||||
|
debug.printMessage(debug.LEVEL_INFO, "GameMode: Already activated, skipping", True)
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
debug.printMessage(debug.LEVEL_INFO, "GameMode: Plugin activation starting", True)
|
||||||
|
self._register_keybinding()
|
||||||
|
self._activated = True
|
||||||
|
debug.printMessage(debug.LEVEL_INFO, "GameMode: Plugin activated successfully", True)
|
||||||
|
return True
|
||||||
|
except Exception as error:
|
||||||
|
debug.printMessage(debug.LEVEL_INFO, f"GameMode: ERROR activating plugin: {error}", True)
|
||||||
|
return False
|
||||||
|
|
||||||
|
@cthulhu_hookimpl
|
||||||
|
def deactivate(self, plugin=None):
|
||||||
|
"""Deactivate the GameMode plugin."""
|
||||||
|
if plugin is not None and plugin is not self:
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
debug.printMessage(debug.LEVEL_INFO, "GameMode: Plugin deactivation starting", True)
|
||||||
|
settingsManager.setSetting('gameMode', False)
|
||||||
|
self._activated = False
|
||||||
|
debug.printMessage(debug.LEVEL_INFO, "GameMode: Plugin deactivated successfully", True)
|
||||||
|
return True
|
||||||
|
except Exception as error:
|
||||||
|
debug.printMessage(debug.LEVEL_INFO, f"GameMode: ERROR deactivating plugin: {error}", True)
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _register_keybinding(self):
|
||||||
|
"""Register the Cthulhu+Control+G keybinding for toggling game mode."""
|
||||||
|
try:
|
||||||
|
if not self.app:
|
||||||
|
debug.printMessage(debug.LEVEL_INFO, "GameMode: No app reference for keybinding", True)
|
||||||
|
return
|
||||||
|
|
||||||
|
gestureString = "kb:cthulhu+control+g"
|
||||||
|
description = "Toggle game mode"
|
||||||
|
self._kbBinding = self.registerGestureByString(
|
||||||
|
self._toggle_game_mode,
|
||||||
|
description,
|
||||||
|
gestureString
|
||||||
|
)
|
||||||
|
if self._kbBinding:
|
||||||
|
debug.printMessage(debug.LEVEL_INFO, f"GameMode: Registered keybinding {gestureString}", True)
|
||||||
|
else:
|
||||||
|
debug.printMessage(debug.LEVEL_INFO, f"GameMode: Failed to register keybinding {gestureString}", True)
|
||||||
|
except Exception as error:
|
||||||
|
debug.printMessage(debug.LEVEL_INFO, f"GameMode: Error registering keybinding: {error}", True)
|
||||||
|
|
||||||
|
def _toggle_game_mode(self, script, inputEvent=None):
|
||||||
|
"""Toggle game mode on or off."""
|
||||||
|
currentValue = bool(settingsManager.getSetting('gameMode'))
|
||||||
|
newValue = not currentValue
|
||||||
|
settingsManager.setSetting('gameMode', newValue)
|
||||||
|
|
||||||
|
message = messages.GAME_MODE_ENABLED if newValue else messages.GAME_MODE_DISABLED
|
||||||
|
if script:
|
||||||
|
script.presentMessage(message)
|
||||||
|
return True
|
||||||
@@ -16,7 +16,7 @@ from pathlib import Path
|
|||||||
|
|
||||||
import gi
|
import gi
|
||||||
gi.require_version('Gtk', '3.0')
|
gi.require_version('Gtk', '3.0')
|
||||||
from gi.repository import Gtk, GLib
|
from gi.repository import Gtk, Gdk, Pango
|
||||||
|
|
||||||
from cthulhu.plugin import Plugin, cthulhu_hookimpl
|
from cthulhu.plugin import Plugin, cthulhu_hookimpl
|
||||||
from cthulhu import debug
|
from cthulhu import debug
|
||||||
@@ -29,12 +29,18 @@ _settingsManager = settings_manager.getManager()
|
|||||||
class PluginManager(Plugin):
|
class PluginManager(Plugin):
|
||||||
"""Plugin that provides a GUI interface for managing other plugins."""
|
"""Plugin that provides a GUI interface for managing other plugins."""
|
||||||
|
|
||||||
|
PLUGIN_COL_ENABLED = 0
|
||||||
|
PLUGIN_COL_DISPLAY = 1
|
||||||
|
PLUGIN_COL_CAN_TOGGLE = 2
|
||||||
|
PLUGIN_COL_NAME = 3
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
"""Initialize the PluginManager plugin."""
|
"""Initialize the PluginManager plugin."""
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self._kb_binding = None
|
self._kb_binding = None
|
||||||
self._dialog = None
|
self._dialog = None
|
||||||
self._plugin_checkboxes = {}
|
self._plugin_treeview = None
|
||||||
|
self._plugin_model = None
|
||||||
self._activated = False
|
self._activated = False
|
||||||
|
|
||||||
debug.printMessage(debug.LEVEL_INFO, "PluginManager: Plugin initialized", True)
|
debug.printMessage(debug.LEVEL_INFO, "PluginManager: Plugin initialized", True)
|
||||||
@@ -164,10 +170,36 @@ class PluginManager(Plugin):
|
|||||||
scrolled.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
|
scrolled.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
|
||||||
scrolled.set_size_request(-1, 200)
|
scrolled.set_size_request(-1, 200)
|
||||||
|
|
||||||
# Create list box for plugins
|
self._plugin_model = Gtk.TreeStore(bool, str, bool, str)
|
||||||
self._plugin_listbox = Gtk.ListBox()
|
self._plugin_treeview = Gtk.TreeView(model=self._plugin_model)
|
||||||
self._plugin_listbox.set_selection_mode(Gtk.SelectionMode.NONE)
|
self._plugin_treeview.set_headers_visible(True)
|
||||||
scrolled.add(self._plugin_listbox)
|
self._plugin_treeview.set_enable_search(False)
|
||||||
|
self._plugin_treeview.connect("key-press-event", self._on_plugin_list_key_press)
|
||||||
|
self._plugin_treeview.connect("row-activated", self._on_plugin_row_activated)
|
||||||
|
|
||||||
|
toggle_renderer = Gtk.CellRendererToggle()
|
||||||
|
toggle_renderer.set_activatable(True)
|
||||||
|
toggle_renderer.connect("toggled", self._on_plugin_toggled)
|
||||||
|
toggle_column = Gtk.TreeViewColumn(
|
||||||
|
"Enabled",
|
||||||
|
toggle_renderer,
|
||||||
|
active=self.PLUGIN_COL_ENABLED,
|
||||||
|
activatable=self.PLUGIN_COL_CAN_TOGGLE
|
||||||
|
)
|
||||||
|
toggle_column.add_attribute(toggle_renderer, "visible", self.PLUGIN_COL_CAN_TOGGLE)
|
||||||
|
self._plugin_treeview.append_column(toggle_column)
|
||||||
|
|
||||||
|
text_renderer = Gtk.CellRendererText()
|
||||||
|
text_renderer.set_property("ellipsize", Pango.EllipsizeMode.END)
|
||||||
|
text_column = Gtk.TreeViewColumn(
|
||||||
|
"Plugin",
|
||||||
|
text_renderer,
|
||||||
|
text=self.PLUGIN_COL_DISPLAY
|
||||||
|
)
|
||||||
|
text_column.set_expand(True)
|
||||||
|
self._plugin_treeview.append_column(text_column)
|
||||||
|
|
||||||
|
scrolled.add(self._plugin_treeview)
|
||||||
|
|
||||||
content_area.pack_start(scrolled, True, True, 0)
|
content_area.pack_start(scrolled, True, True, 0)
|
||||||
|
|
||||||
@@ -197,49 +229,41 @@ class PluginManager(Plugin):
|
|||||||
|
|
||||||
debug.printMessage(debug.LEVEL_INFO, f"PluginManager: Found {len(available_plugins)} plugins", True)
|
debug.printMessage(debug.LEVEL_INFO, f"PluginManager: Found {len(available_plugins)} plugins", True)
|
||||||
|
|
||||||
# Clear existing checkboxes
|
# Clear existing model rows
|
||||||
self._plugin_checkboxes.clear()
|
self._plugin_model.clear()
|
||||||
|
|
||||||
# Add each plugin as a checkbox (except PluginManager itself)
|
# Add each plugin as a checkbox (except PluginManager itself)
|
||||||
|
enabled_iter = self._plugin_model.append(
|
||||||
|
None,
|
||||||
|
[False, "Enabled plugins", False, ""]
|
||||||
|
)
|
||||||
|
disabled_iter = self._plugin_model.append(
|
||||||
|
None,
|
||||||
|
[False, "Disabled plugins", False, ""]
|
||||||
|
)
|
||||||
|
|
||||||
for plugin_name, plugin_info in sorted(available_plugins.items()):
|
for plugin_name, plugin_info in sorted(available_plugins.items()):
|
||||||
# Skip PluginManager to prevent users from disabling plugin management
|
# Skip PluginManager to prevent users from disabling plugin management
|
||||||
if plugin_name == "PluginManager":
|
if plugin_name == "PluginManager":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Create row container
|
display_name = plugin_info.get('name', plugin_name)
|
||||||
row = Gtk.ListBoxRow()
|
display_text = display_name
|
||||||
row.set_activatable(False)
|
description = plugin_info.get('description')
|
||||||
|
if description:
|
||||||
|
display_text += f" - {description}"
|
||||||
|
version = plugin_info.get('version')
|
||||||
|
if version:
|
||||||
|
display_text += f" (v{version})"
|
||||||
|
|
||||||
# Create horizontal box for checkbox and info
|
is_active = plugin_name in active_plugins
|
||||||
hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
|
parent_iter = enabled_iter if is_active else disabled_iter
|
||||||
hbox.set_border_width(5)
|
self._plugin_model.append(
|
||||||
|
parent_iter,
|
||||||
|
[is_active, display_text, True, plugin_name]
|
||||||
|
)
|
||||||
|
|
||||||
# Create checkbox
|
self._plugin_treeview.collapse_all()
|
||||||
checkbox = Gtk.CheckButton()
|
|
||||||
checkbox.set_active(plugin_name in active_plugins)
|
|
||||||
checkbox.connect("toggled", self._on_plugin_toggled, plugin_name)
|
|
||||||
|
|
||||||
# Create plugin info label
|
|
||||||
info_text = f"<b>{plugin_info.get('name', plugin_name)}</b>"
|
|
||||||
if plugin_info.get('description'):
|
|
||||||
info_text += f"\n{plugin_info['description']}"
|
|
||||||
if plugin_info.get('version'):
|
|
||||||
info_text += f" (v{plugin_info['version']})"
|
|
||||||
|
|
||||||
label = Gtk.Label()
|
|
||||||
label.set_markup(info_text)
|
|
||||||
label.set_halign(Gtk.Align.START)
|
|
||||||
label.set_line_wrap(True)
|
|
||||||
|
|
||||||
# Pack widgets
|
|
||||||
hbox.pack_start(checkbox, False, False, 0)
|
|
||||||
hbox.pack_start(label, True, True, 0)
|
|
||||||
|
|
||||||
row.add(hbox)
|
|
||||||
self._plugin_listbox.add(row)
|
|
||||||
|
|
||||||
# Store checkbox reference
|
|
||||||
self._plugin_checkboxes[plugin_name] = checkbox
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"PluginManager: Error populating plugin list: {e}")
|
logger.error(f"PluginManager: Error populating plugin list: {e}")
|
||||||
@@ -344,32 +368,101 @@ class PluginManager(Plugin):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"PluginManager: Error scanning directory {directory}: {e}")
|
logger.error(f"PluginManager: Error scanning directory {directory}: {e}")
|
||||||
|
|
||||||
def _on_plugin_toggled(self, checkbox, plugin_name):
|
def _on_plugin_toggled(self, renderer, path):
|
||||||
"""Handle plugin checkbox toggle."""
|
"""Handle plugin toggle via TreeView."""
|
||||||
|
try:
|
||||||
|
tree_iter = self._plugin_model.get_iter(path)
|
||||||
|
if not tree_iter:
|
||||||
|
return
|
||||||
|
|
||||||
|
can_toggle = self._plugin_model.get_value(tree_iter, self.PLUGIN_COL_CAN_TOGGLE)
|
||||||
|
if not can_toggle:
|
||||||
|
return
|
||||||
|
|
||||||
|
is_active = self._plugin_model.get_value(tree_iter, self.PLUGIN_COL_ENABLED)
|
||||||
|
plugin_name = self._plugin_model.get_value(tree_iter, self.PLUGIN_COL_NAME)
|
||||||
|
new_active = not is_active
|
||||||
|
self._plugin_model.set_value(tree_iter, self.PLUGIN_COL_ENABLED, new_active)
|
||||||
|
self._set_plugin_active(plugin_name, new_active)
|
||||||
|
self._rebuild_plugin_groups()
|
||||||
|
except Exception as error:
|
||||||
|
logger.error(f"PluginManager: Error toggling plugin at path {path}: {error}")
|
||||||
|
debug.printMessage(debug.LEVEL_INFO, f"PluginManager: Error toggling plugin at path {path}: {error}", True)
|
||||||
|
|
||||||
|
def _on_plugin_list_key_press(self, widget, event):
|
||||||
|
"""Toggle plugin on Space while keeping Tab navigation outside the list."""
|
||||||
|
if event.keyval != Gdk.KEY_space:
|
||||||
|
return False
|
||||||
|
|
||||||
|
selection = self._plugin_treeview.get_selection()
|
||||||
|
model, tree_iter = selection.get_selected()
|
||||||
|
if not tree_iter:
|
||||||
|
return False
|
||||||
|
|
||||||
|
path = model.get_path(tree_iter)
|
||||||
|
self._on_plugin_toggled(None, path)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _on_plugin_row_activated(self, treeview, path, column):
|
||||||
|
"""Toggle plugin when activating a row."""
|
||||||
|
self._on_plugin_toggled(None, path)
|
||||||
|
|
||||||
|
def _rebuild_plugin_groups(self):
|
||||||
|
"""Rebuild the tree so enabled/disabled groups stay accurate."""
|
||||||
|
if not self._plugin_model:
|
||||||
|
return
|
||||||
|
|
||||||
|
selection = self._plugin_treeview.get_selection()
|
||||||
|
model, tree_iter = selection.get_selected()
|
||||||
|
selected_name = None
|
||||||
|
if tree_iter:
|
||||||
|
selected_name = model.get_value(tree_iter, self.PLUGIN_COL_NAME)
|
||||||
|
|
||||||
|
self._populate_plugin_list()
|
||||||
|
|
||||||
|
if selected_name:
|
||||||
|
self._select_plugin_row(selected_name)
|
||||||
|
|
||||||
|
def _select_plugin_row(self, plugin_name):
|
||||||
|
"""Select the row for the given plugin name."""
|
||||||
|
def _walk(model, tree_iter):
|
||||||
|
while tree_iter:
|
||||||
|
name = model.get_value(tree_iter, self.PLUGIN_COL_NAME)
|
||||||
|
if name == plugin_name:
|
||||||
|
path = model.get_path(tree_iter)
|
||||||
|
self._plugin_treeview.expand_to_path(path)
|
||||||
|
self._plugin_treeview.get_selection().select_path(path)
|
||||||
|
return True
|
||||||
|
if model.iter_has_child(tree_iter):
|
||||||
|
child = model.iter_children(tree_iter)
|
||||||
|
if _walk(model, child):
|
||||||
|
return True
|
||||||
|
tree_iter = model.iter_next(tree_iter)
|
||||||
|
return False
|
||||||
|
|
||||||
|
root = self._plugin_model.get_iter_first()
|
||||||
|
_walk(self._plugin_model, root)
|
||||||
|
|
||||||
|
def _set_plugin_active(self, plugin_name, is_active):
|
||||||
|
"""Update settings to enable or disable a plugin."""
|
||||||
try:
|
try:
|
||||||
is_active = checkbox.get_active()
|
|
||||||
debug.printMessage(debug.LEVEL_INFO, f"PluginManager: Plugin {plugin_name} toggled to {'active' if is_active else 'inactive'}", True)
|
debug.printMessage(debug.LEVEL_INFO, f"PluginManager: Plugin {plugin_name} toggled to {'active' if is_active else 'inactive'}", True)
|
||||||
|
|
||||||
# Get current active plugins
|
# Get current active plugins
|
||||||
active_plugins = _settingsManager.getSetting('activePlugins') or []
|
active_plugins = _settingsManager.getSetting('activePlugins') or []
|
||||||
active_plugins = list(active_plugins) # Make a copy
|
active_plugins = list(active_plugins)
|
||||||
|
|
||||||
# Update the list
|
|
||||||
if is_active and plugin_name not in active_plugins:
|
if is_active and plugin_name not in active_plugins:
|
||||||
active_plugins.append(plugin_name)
|
active_plugins.append(plugin_name)
|
||||||
elif not is_active and plugin_name in active_plugins:
|
elif not is_active and plugin_name in active_plugins:
|
||||||
active_plugins.remove(plugin_name)
|
active_plugins.remove(plugin_name)
|
||||||
|
|
||||||
# Save updated settings
|
|
||||||
_settingsManager.setSetting('activePlugins', active_plugins)
|
_settingsManager.setSetting('activePlugins', active_plugins)
|
||||||
|
|
||||||
# Save to disk using the backend directly
|
|
||||||
try:
|
try:
|
||||||
# Get current general settings
|
|
||||||
current_general = _settingsManager.getGeneralSettings()
|
current_general = _settingsManager.getGeneralSettings()
|
||||||
current_general['activePlugins'] = active_plugins
|
current_general['activePlugins'] = active_plugins
|
||||||
|
|
||||||
# Save using the backend
|
|
||||||
backend = _settingsManager._backend
|
backend = _settingsManager._backend
|
||||||
if backend:
|
if backend:
|
||||||
backend.saveDefaultSettings(
|
backend.saveDefaultSettings(
|
||||||
@@ -381,14 +474,14 @@ class PluginManager(Plugin):
|
|||||||
else:
|
else:
|
||||||
debug.printMessage(debug.LEVEL_INFO, "PluginManager: No backend available for saving", True)
|
debug.printMessage(debug.LEVEL_INFO, "PluginManager: No backend available for saving", True)
|
||||||
|
|
||||||
except Exception as save_e:
|
except Exception as save_error:
|
||||||
debug.printMessage(debug.LEVEL_INFO, f"PluginManager: Error saving via backend: {save_e}", True)
|
debug.printMessage(debug.LEVEL_INFO, f"PluginManager: Error saving via backend: {save_error}", True)
|
||||||
|
|
||||||
debug.printMessage(debug.LEVEL_INFO, f"PluginManager: Updated active plugins: {active_plugins}", True)
|
debug.printMessage(debug.LEVEL_INFO, f"PluginManager: Updated active plugins: {active_plugins}", True)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as error:
|
||||||
logger.error(f"PluginManager: Error toggling plugin {plugin_name}: {e}")
|
logger.error(f"PluginManager: Error toggling plugin {plugin_name}: {error}")
|
||||||
debug.printMessage(debug.LEVEL_INFO, f"PluginManager: Error toggling {plugin_name}: {e}", True)
|
debug.printMessage(debug.LEVEL_INFO, f"PluginManager: Error toggling {plugin_name}: {error}", True)
|
||||||
|
|
||||||
|
|
||||||
def _on_dialog_response(self, dialog, response_id):
|
def _on_dialog_response(self, dialog, response_id):
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ subdir('ByeCthulhu')
|
|||||||
subdir('Clipboard')
|
subdir('Clipboard')
|
||||||
subdir('DisplayVersion')
|
subdir('DisplayVersion')
|
||||||
subdir('HelloCthulhu')
|
subdir('HelloCthulhu')
|
||||||
|
subdir('GameMode')
|
||||||
subdir('IndentationAudio')
|
subdir('IndentationAudio')
|
||||||
subdir('OCR')
|
subdir('OCR')
|
||||||
subdir('PluginManager')
|
subdir('PluginManager')
|
||||||
|
|||||||
@@ -824,6 +824,7 @@ class Script(script.Script):
|
|||||||
sleepModeManager.toggleSleepMode(self)
|
sleepModeManager.toggleSleepMode(self)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def bypassNextCommand(self, inputEvent=None):
|
def bypassNextCommand(self, inputEvent=None):
|
||||||
"""Causes the next keyboard command to be ignored by Cthulhu
|
"""Causes the next keyboard command to be ignored by Cthulhu
|
||||||
and passed along to the current application.
|
and passed along to the current application.
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ userCustomizableSettings = [
|
|||||||
"enableEchoByWord",
|
"enableEchoByWord",
|
||||||
"enableEchoBySentence",
|
"enableEchoBySentence",
|
||||||
"enableKeyEcho",
|
"enableKeyEcho",
|
||||||
|
"gameMode",
|
||||||
"enableAlphabeticKeys",
|
"enableAlphabeticKeys",
|
||||||
"enableNumericKeys",
|
"enableNumericKeys",
|
||||||
"enablePunctuationKeys",
|
"enablePunctuationKeys",
|
||||||
@@ -317,6 +318,7 @@ verbalizePunctuationStyle = PUNCTUATION_STYLE_MOST
|
|||||||
speechVerbosityLevel = VERBOSITY_LEVEL_VERBOSE
|
speechVerbosityLevel = VERBOSITY_LEVEL_VERBOSE
|
||||||
messagesAreDetailed = True
|
messagesAreDetailed = True
|
||||||
enablePauseBreaks = True
|
enablePauseBreaks = True
|
||||||
|
gameMode = False
|
||||||
speakDescription = True
|
speakDescription = True
|
||||||
speakContextBlockquote = True
|
speakContextBlockquote = True
|
||||||
speakContextPanel = True
|
speakContextPanel = True
|
||||||
|
|||||||
Reference in New Issue
Block a user