10 Commits

Author SHA1 Message Date
d36b664319 Merge branch 'testing'
Plugins are in a much better state now, mostly working. The exception is, plugins that create a keyboard shortcut don't actually bind the shortcut. That one is turning out to be a lot harder to fix than I originally thought.
2025-04-05 16:32:17 -04:00
02be96aa69 Try to fix clipboard and simple plugins. 2025-04-04 18:04:58 -04:00
48575ab6cd Removed the old plugin manager. It didn't work anyway and needs to be rewritten. 2025-04-04 17:28:31 -04:00
2c28021ed4 Removed the old plugin manager. It didn't work anyway and needs to be rewritten. 2025-04-04 17:27:30 -04:00
3f7d60763d Merge branch 'testing'
Plugins are currently broken as Cthulhu moves over to pluggy. Libpeas and pygobject no longer play nicely together after latest updates. I really did not want to make a new release yet, because it is not ready, but a screen reader that at least reads instead of crashing at launch is better than nothing.
2025-04-03 20:17:14 -04:00
6bbf3d0e67 Merge branch 'testing' latest changes merged. 2024-12-22 19:04:57 -05:00
cbe3424e29 Fix the version.py file in preparation for merging. 2024-12-22 19:04:39 -05:00
327ad99e49 Preparing for stable tag release. 2024-12-18 19:49:25 -05:00
c46cf1c939 Merge branch 'testing' fixed preferences GUI. 2024-12-18 19:45:59 -05:00
a97bb30ed3 New version system merged. 2024-12-18 11:42:52 -05:00
12 changed files with 88 additions and 681 deletions

View File

@ -127,7 +127,6 @@ src/cthulhu/scripts/toolkits/gtk/Makefile
src/cthulhu/plugins/Makefile
src/cthulhu/plugins/ByeCthulhu/Makefile
src/cthulhu/plugins/HelloCthulhu/Makefile
src/cthulhu/plugins/PluginManager/Makefile
src/cthulhu/plugins/Clipboard/Makefile
src/cthulhu/plugins/DisplayVersion/Makefile
src/cthulhu/plugins/hello_world/Makefile

View File

@ -20,6 +20,8 @@
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
"""Clipboard plugin for Cthulhu."""
@ -62,6 +64,36 @@ class Clipboard(Plugin):
except Exception as e:
logger.error(f"Error activating Clipboard plugin: {e}")
@cthulhu_hookimpl
def deactivate(self, plugin=None):
"""Deactivate the plugin."""
# Skip if this deactivation call isn't for us
if plugin is not None and plugin is not self:
return
logger.info("Deactivating Clipboard plugin")
try:
# Unregister keyboard shortcut
if self.app:
api_helper = self.app.getAPIHelper()
if api_helper and hasattr(api_helper, 'unregisterShortcut'):
api_helper.unregisterShortcut('kb:cthulhu+shift+c')
logger.debug("Unregistered clipboard shortcut")
except Exception as e:
logger.error(f"Error deactivating Clipboard plugin: {e}")
"""Activate the plugin."""
# Skip if this activation call isn't for us
if plugin is not None and plugin is not self:
return
logger.info("Activating Clipboard plugin")
try:
# Register keyboard shortcut
self.registerGestureByString(self.speakClipboard, _('clipboard'), 'kb:cthulhu+shift+c')
logger.debug("Registered shortcut for clipboard")
except Exception as e:
logger.error(f"Error activating Clipboard plugin: {e}")
@cthulhu_hookimpl
def deactivate(self, plugin=None):
"""Deactivate the plugin."""

View File

@ -1,4 +1,4 @@
SUBDIRS = Clipboard DisplayVersion hello_world self_voice ByeCthulhu HelloCthulhu PluginManager SimplePluginSystem
SUBDIRS = Clipboard DisplayVersion hello_world self_voice ByeCthulhu HelloCthulhu SimplePluginSystem
cthulhu_pythondir=$(pkgpythondir)/plugins

View File

@ -1,8 +0,0 @@
cthulhu_python_PYTHON = \
__init__.py \
PluginManager.plugin \
PluginManager.py \
PluginManagerUi.py
cthulhu_pythondir=$(pkgpythondir)/plugins/PluginManager

View File

@ -1,14 +0,0 @@
[Plugin]
Module=PluginManager
Loader=python3
Name=Plugin Manager
Description=Activate and Deactivate plugins
Authors=Chrys chrys@linux-a11y.org
Website=
Version=1.0
Copyright=
Builtin=true
Hidden=true
Depends=
Icon=
Help=

View File

@ -1,61 +0,0 @@
#!/usr/bin/env python3
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
#
# 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.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
from cthulhu import plugin
import gi
gi.require_version('Peas', '1.0')
from gi.repository import GObject
from gi.repository import Peas
import PluginManagerUi
class PluginManager(GObject.Object, Peas.Activatable, plugin.Plugin):
#__gtype_name__ = 'PluginManager'
object = GObject.Property(type=GObject.Object)
def __init__(self):
plugin.Plugin.__init__(self)
self.pluginManagerUi = None
def do_activate(self):
API = self.object
self.registerGestureByString(self.startPluginManagerUi, _('plugin manager'), 'kb:cthulhu+e')
def do_deactivate(self):
API = self.object
def startPluginManagerUi(self, script=None, inputEvent=None):
self.showUI()
return True
def showUI(self):
API = self.object
if self.pluginManagerUi == None:
self.pluginManagerUi = PluginManagerUi.PluginManagerUi(API.app)
self.pluginManagerUi.setTranslationContext(self.getTranslationContext())
self.pluginManagerUi.createUI()
self.pluginManagerUi.run()
self.pluginManagerUi = None
else:
self.pluginManagerUi.present()

View File

@ -1,307 +0,0 @@
#!/usr/bin/env python3
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
#
# 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.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, Gdk
class PluginManagerUi(Gtk.ApplicationWindow):
def __init__(self, app, *args, **kwargs):
super().__init__(*args, **kwargs, title=_("Cthulhu Plugin Manager"))
self.app = app
self.translationContext = None
self.connect("destroy", self._onCancelButtonClicked)
self.connect('key-press-event', self._onKeyPressWindow)
def createUI(self):
self.set_default_size(650, 650)
self.set_position(Gtk.WindowPosition.CENTER_ALWAYS)
# pluginInfo (object) = 0
# name (str) = 1
# active (bool) = 2
# buildIn (bool) = 3
# dataDir (str) = 4
# moduleDir (str) = 5
# dependencies (object) = 6
# moduleName (str) = 7
# description (str) = 8
# authors (object) = 9
# website (str) = 10
# copyright (str) = 11
# version (str) = 12
# helpUri (str) = 13
# iconName (str) = 14
self.listStore = Gtk.ListStore(object,str, bool, bool, str, str,object,str,str,object,str,str,str,str,str)
self.treeView = Gtk.TreeView(model=self.listStore)
self.treeView.connect("row-activated", self._rowActivated)
self.treeView.connect('key-press-event', self._onKeyPressTreeView)
self.rendererText = Gtk.CellRendererText()
self.columnText = Gtk.TreeViewColumn(_("Name"), self.rendererText, text=1)
self.treeView.append_column(self.columnText)
self.rendererToggle = Gtk.CellRendererToggle()
self.rendererToggle.connect("toggled", self._onCellToggled)
self.columnToggle = Gtk.TreeViewColumn(_("Active"), self.rendererToggle, active=2)
self.treeView.append_column(self.columnToggle)
self.buttomBox = Gtk.Box(spacing=6)
self.mainVBox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
self.mainVBox.pack_start(self.treeView, True, True, 0)
self.mainVBox.pack_start(self.buttomBox, False, True, 0)
self.add(self.mainVBox)
self.oKButton = Gtk.Button.new_with_mnemonic(_("_Details"))
self.oKButton.connect("clicked", self._onDetailsButtonClicked)
self.buttomBox.pack_start(self.oKButton, True, True, 0)
self.oKButton = Gtk.Button.new_with_mnemonic(_("_OK"))
self.oKButton.connect("clicked", self._onOkButtonClicked)
self.buttomBox.pack_start(self.oKButton, True, True, 0)
self.applyButton = Gtk.Button.new_with_mnemonic(_("_Apply"))
self.applyButton.connect("clicked", self._onApplyButtonClicked)
self.buttomBox.pack_start(self.applyButton, True, True, 0)
self.applyButton = Gtk.Button.new_with_mnemonic(_("_Install"))
self.applyButton.connect("clicked", self._onInstallButtonClicked)
self.buttomBox.pack_start(self.applyButton, True, True, 0)
self.applyButton = Gtk.Button.new_with_mnemonic(_("_Uninstall"))
self.applyButton.connect("clicked", self._onUninstallButtonClicked)
self.buttomBox.pack_start(self.applyButton, True, True, 0)
self.cancelButton = Gtk.Button.new_with_mnemonic(_("_Cancel"))
self.cancelButton.connect("clicked", self._onCancelButtonClicked)
self.buttomBox.pack_start(self.cancelButton, True, True, 0)
def setTranslationContext(self, translationContext):
self.translationContext = translationContext
global _
_ = translationContext.gettext
def closeWindow(self):
Gtk.main_quit()
def uninstallPlugin(self):
selection = self.treeView.get_selection()
model, list_iter = selection.get_selected()
try:
if model.get_value(list_iter,0):
pluginInfo = model.get_value(list_iter,0)
pluginName = self.app.getPluginSystemManager().getPluginName(pluginInfo)
dialog = Gtk.MessageDialog(None,
Gtk.DialogFlags.MODAL,
type=Gtk.MessageType.INFO,
buttons=Gtk.ButtonsType.YES_NO)
dialog.set_markup("<b>%s</b>" % _('Remove Plugin {}?').format(pluginName))
dialog.format_secondary_markup(_('Do you really want to remove Plugin {}?').format(pluginName))
response = dialog.run()
dialog.destroy()
if response != Gtk.ResponseType.YES:
return
self.app.getPluginSystemManager().uninstallPlugin(model.get_value(list_iter,0))
self.refreshPluginList()
except:
pass
def installPlugin(self):
ok, filePath = self.chooseFile()
if not ok:
return
self.app.getPluginSystemManager().installPlugin(filePath)
self.refreshPluginList()
def _onKeyPressWindow(self, _, event):
_, key_val = event.get_keyval()
if key_val == Gdk.KEY_Escape:
self.closeWindow()
def _onKeyPressTreeView(self, _, event):
_, key_val = event.get_keyval()
if key_val == Gdk.KEY_Return:
self.applySettings()
self.closeWindow()
if key_val == Gdk.KEY_Escape:
self.closeWindow()
# CTRL + Q
#modifiers = event.get_state()
#if modifiers == Gdk.ModifierType.CONTROL_MASK | Gdk.ModifierType.MOD2_MASK:
# if key_val == Gdk.KEY_q:
# self._on_scan()
def applySettings(self):
for row in self.listStore:
pluginInfo = row[0]
isActive = row[2]
self.app.getPluginSystemManager().setPluginActive(pluginInfo, isActive)
gsettingsManager = self.app.getGsettingsManager()
gsettingsManager.set_settings_value_list('active-plugins', self.app.getPluginSystemManager().getActivePlugins())
self.app.getPluginSystemManager().syncAllPluginsActive()
self.refreshPluginList()
def _rowActivated(self, tree_view, path, column):
print('rowActivated')
def showDetails(self):
selection = self.treeView.get_selection()
model, list_iter = selection.get_selected()
try:
if model.get_value(list_iter,0):
pluginInfo = model.get_value(list_iter,0)
name = self.app.getPluginSystemManager().getPluginName(pluginInfo)
description = self.app.getPluginSystemManager().getPluginDescription(pluginInfo)
authors = self.app.getPluginSystemManager().getPluginAuthors(pluginInfo)
website =self.app.getPluginSystemManager().getPluginWebsite(pluginInfo)
copyright = self.app.getPluginSystemManager().getPluginCopyright(pluginInfo)
license = '' #self.app.getPluginSystemManager().getPluginName(pluginInfo)
version = self.app.getPluginSystemManager().getPluginVersion(pluginInfo)
dialog = Gtk.AboutDialog(self)
dialog.set_authors(authors)
dialog.set_website(website)
dialog.set_copyright(copyright)
dialog.set_license(license)
dialog.set_version(version)
dialog.set_program_name(name)
dialog.set_comments(description)
dialog.run()
dialog.destroy()
except:
pass
def _onDetailsButtonClicked(self, widget):
self.showDetails()
def _onOkButtonClicked(self, widget):
self.applySettings()
self.closeWindow()
def _onApplyButtonClicked(self, widget):
self.applySettings()
def _onInstallButtonClicked(self, widget):
self.installPlugin()
def _onUninstallButtonClicked(self, widget):
self.uninstallPlugin()
def _onCancelButtonClicked(self, widget):
self.closeWindow()
def refreshPluginList(self):
self.clearPluginList()
pluginList = self.app.getPluginSystemManager().plugins
for pluginInfo in pluginList:
self.addPlugin(pluginInfo)
def clearPluginList(self):
self.listStore.clear()
def addPlugin(self, pluginInfo):
ignoredPlugins = self.app.getPluginSystemManager().getIgnoredPlugins()
moduleDir = self.app.getPluginSystemManager().getPluginModuleDir(pluginInfo)
if moduleDir in ignoredPlugins:
return
hidden = self.app.getPluginSystemManager().isPluginHidden(pluginInfo)
if hidden:
return
moduleName = self.app.getPluginSystemManager().getPluginModuleName(pluginInfo)
name = self.app.getPluginSystemManager().getPluginName(pluginInfo)
version = self.app.getPluginSystemManager().getPluginVersion(pluginInfo)
website = self.app.getPluginSystemManager().getPluginWebsite(pluginInfo)
authors = self.app.getPluginSystemManager().getPluginAuthors(pluginInfo)
buildIn = self.app.getPluginSystemManager().isPluginBuildIn(pluginInfo)
description = self.app.getPluginSystemManager().getPluginDescription(pluginInfo)
iconName = self.app.getPluginSystemManager().getPluginIconName(pluginInfo)
copyright = self.app.getPluginSystemManager().getPluginCopyright(pluginInfo)
dependencies = self.app.getPluginSystemManager().getPluginDependencies(pluginInfo)
#settings = self.app.getPluginSystemManager().getPluginSettings(pluginInfo)
#hasDependencies = self.app.getPluginSystemManager().hasPluginDependency(pluginInfo)
loaded = self.app.getPluginSystemManager().isPluginLoaded(pluginInfo)
available = self.app.getPluginSystemManager().isPluginAvailable(pluginInfo)
active = self.app.getPluginSystemManager().isPluginActive(pluginInfo)
#externalData = self.app.getPluginSystemManager().getPluginExternalData(pluginInfo)
helpUri = self.app.getPluginSystemManager().getPlugingetHelpUri(pluginInfo)
dataDir = self.app.getPluginSystemManager().getPluginDataDir(pluginInfo)
# pluginInfo (object) = 0
# name (str) = 1
# active (bool) = 2
# buildIn (bool) = 3
# dataDir (str) = 4
# moduleDir (str) = 5
# dependencies (object) = 6
# moduleName (str) = 7
# description (str) = 8
# authors (object) = 9
# website (str) = 10
# copyright (str) = 11
# version (str) = 12
# helpUri (str) = 13
# iconName (str) = 14
self.listStore.append([pluginInfo, name, active, buildIn, dataDir, moduleDir, dependencies, moduleName, description, authors, website, copyright, version, helpUri, iconName])
def chooseFile(self):
dialog = Gtk.FileChooserDialog(
title=_("Please choose a file"), parent=self, action=Gtk.FileChooserAction.OPEN
)
dialog.add_buttons(
Gtk.STOCK_CANCEL,
Gtk.ResponseType.CANCEL,
Gtk.STOCK_OPEN,
Gtk.ResponseType.OK,
)
filter_plugin = Gtk.FileFilter()
filter_plugin.set_name(_("Plugin Archive"))
filter_plugin.add_mime_type("application/gzip")
dialog.add_filter(filter_plugin)
response = dialog.run()
filePath = ''
ok = False
if response == Gtk.ResponseType.OK:
ok = True
filePath = dialog.get_filename()
dialog.destroy()
return ok, filePath
def _onCellToggled(self, widget, path):
self.listStore[path][2] = not self.listStore[path][2]
def present(self):
cthulhu_state = self.app.getDynamicApiManager().getAPI('CthulhuState')
ts = 0
try:
ts = cthulhu_state.lastInputEvent.timestamp
except:
pass
if ts == 0:
ts = Gtk.get_current_event_time()
self.present_with_time(ts)
def run(self):
self.refreshPluginList()
self.present()
self.show_all()
Gtk.main()
self.destroy()

View File

@ -1,107 +0,0 @@
#!/usr/bin/env python3
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
#
# 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.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
class ListBoxRowWithData(Gtk.ListBoxRow):
def __init__(self, data):
super(Gtk.ListBoxRow, self).__init__()
self.data = data
self.add(Gtk.Label(label=data))
class PluginManagerUi(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self)
self.pluginList = []
self.set_default_size(200, -1)
self.connect("destroy", Gtk.main_quit)
self.listBox = Gtk.ListBox()
self.buttomBox = Gtk.Box(spacing=6)
self.mainVBox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=20)
self.mainVBox.pack_start(self.listBox, True, True, 0)
self.mainVBox.pack_start(self.buttomBox, True, True, 0)
self.add(self.mainVBox)
self.oKButton = Gtk.Button(label="OK")
self.oKButton.connect("clicked", self.on_oKButton_clicked)
self.buttomBox.pack_start(self.oKButton, True, True, 0)
self.applyButton = Gtk.Button(label="Apply")
self.applyButton.connect("clicked", self.on_applyButton_clicked)
self.buttomBox.pack_start(self.applyButton, True, True, 0)
self.cancelButton = Gtk.Button(label="Cancel")
self.cancelButton.connect("clicked", self.on_cancelButton_clicked)
self.buttomBox.pack_start(self.cancelButton, True, True, 0)
self.listBox.connect("row-activated", self.on_row_activated)
def on_row_activated(self, listBox, listboxrow):
print("Row %i activated" % (listboxrow.get_index()))
def on_oKButton_clicked(self, widget):
print("OK")
def on_applyButton_clicked(self, widget):
print("Apply")
def on_cancelButton_clicked(self, widget):
print("Cancel")
def addPlugin(self, Name, Active, Description = ''):
self.pluginList.append([Name, Active, Description])
def run(self):
for plugin in self.pluginList:
print(plugin)
box = Gtk.Box(spacing=10)
pluginNameLabel = Gtk.Label(plugin[0])
#pluginActiveCheckButton = Gtk.CheckButton(label="_Active", use_underline=True)
#pluginActiveCheckButton.set_active(plugin[1])
pluginActiveSwitch = Gtk.Switch()
pluginActiveSwitch.set_active(plugin[1])
pluginDescriptionLabel = Gtk.Label(plugin[2])
box.pack_start(pluginNameLabel, True, True, 0)
box.pack_start(pluginActiveSwitch, True, True, 0)
box.pack_start(pluginDescriptionLabel, True, True, 0)
self.listBox.add(box)
self.show_all()
Gtk.main()
if __name__ == "__main__":
ui = PluginManagerUi()
ui.addPlugin('plugin1', True, 'bla')
ui.addPlugin('plugin2', True, 'bla')
ui.addPlugin('plugin3', True, 'bla')
ui.run()

View File

@ -1,117 +0,0 @@
#!/usr/bin/env python3
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
#
# 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.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
class ListBoxRowWithData(Gtk.ListBoxRow):
def __init__(self, data):
super(Gtk.ListBoxRow, self).__init__()
self.data = data
self.add(Gtk.Label(label=data))
class ListBoxWindow(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title="ListBox Demo")
self.set_border_width(10)
box_outer = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
self.add(box_outer)
listbox = Gtk.ListBox()
listbox.set_selection_mode(Gtk.SelectionMode.NONE)
box_outer.pack_start(listbox, True, True, 0)
row = Gtk.ListBoxRow()
hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=50)
row.add(hbox)
vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
hbox.pack_start(vbox, True, True, 0)
label1 = Gtk.Label(label="Automatic Date & Time", xalign=0)
label2 = Gtk.Label(label="Requires internet access", xalign=0)
vbox.pack_start(label1, True, True, 0)
vbox.pack_start(label2, True, True, 0)
switch = Gtk.Switch()
switch.props.valign = Gtk.Align.CENTER
hbox.pack_start(switch, False, True, 0)
listbox.add(row)
row = Gtk.ListBoxRow()
hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=50)
row.add(hbox)
label = Gtk.Label(label="Enable Automatic Update", xalign=0)
check = Gtk.CheckButton()
hbox.pack_start(label, True, True, 0)
hbox.pack_start(check, False, True, 0)
listbox.add(row)
row = Gtk.ListBoxRow()
hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=50)
row.add(hbox)
label = Gtk.Label(label="Date Format", xalign=0)
combo = Gtk.ComboBoxText()
combo.insert(0, "0", "24-hour")
combo.insert(1, "1", "AM/PM")
hbox.pack_start(label, True, True, 0)
hbox.pack_start(combo, False, True, 0)
listbox.add(row)
listbox_2 = Gtk.ListBox()
items = "This is a sorted ListBox Fail".split()
for item in items:
listbox_2.add(ListBoxRowWithData(item))
def sort_func(row_1, row_2, data, notify_destroy):
return row_1.data.lower() > row_2.data.lower()
def filter_func(row, data, notify_destroy):
return False if row.data == "Fail" else True
listbox_2.set_sort_func(sort_func, None, False)
listbox_2.set_filter_func(filter_func, None, False)
def on_row_activated(listbox_widget, row):
print(row.data)
listbox_2.connect("row-activated", on_row_activated)
box_outer.pack_start(listbox_2, True, True, 0)
listbox_2.show_all()
win = ListBoxWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()

View File

@ -1,25 +0,0 @@
#!/usr/bin/env python3
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
#
# 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.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca

View File

@ -1,9 +1,6 @@
#!/usr/bin/env python3
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -20,8 +17,6 @@
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA 02110-1301 USA.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca
"""Simple Plugin System for Cthulhu."""
@ -321,13 +316,24 @@ class SimplePluginSystem(Plugin):
This is a compatibility wrapper for the new plugin system.
"""
try:
# Try to get the InputEventManager and register the shortcut
input_manager = self.app.getDynamicApiManager().getAPI('InputEventManager')
if input_manager:
input_manager.registerGestureByString(function, description, shortcut)
logger.debug(f"Registered shortcut {shortcut} for {description}")
if self.app:
api_helper = self.app.getAPIHelper()
if api_helper:
api_helper.registerGestureByString(
function,
description,
shortcut,
'default',
'cthulhu',
True,
contextName=self.module_name
)
logger.debug(f"Registered shortcut {shortcut} for {description}")
return True
else:
logger.error("Could not get APIHelper")
else:
logger.error("Could not get InputEventManager API")
logger.error("No app reference available")
except Exception as e:
logger.error(f"Error registering shortcut {shortcut}: {e}")
@ -337,13 +343,16 @@ class SimplePluginSystem(Plugin):
This is a compatibility wrapper for the new plugin system.
"""
try:
# Try to get the InputEventManager and unregister the shortcut
input_manager = self.app.getDynamicApiManager().getAPI('InputEventManager')
if input_manager:
input_manager.unregisterGestureByString(shortcut)
logger.debug(f"Unregistered shortcut {shortcut}")
if self.app:
api_helper = self.app.getAPIHelper()
if api_helper and hasattr(api_helper, 'unregisterShortcut'):
api_helper.unregisterShortcut(shortcut)
logger.debug(f"Unregistered shortcut {shortcut}")
return True
else:
logger.error("Could not get APIHelper or unregisterShortcut method")
else:
logger.error("Could not get InputEventManager API")
logger.error("No app reference available")
except Exception as e:
logger.error(f"Error unregistering shortcut {shortcut}: {e}")
@ -352,10 +361,16 @@ class SimplePluginSystem(Plugin):
if not self.loaded:
try:
logger.info(f"Loading plugins from {self.plugin_repo}")
self.plugin_list = glob.glob(self.plugin_repo + '*')
plugin_files = glob.glob(self.plugin_repo + '*')
self.plugin_list = [] # Reset the plugin list to avoid confusion
for currplugin in self.plugin_list:
for currplugin in plugin_files:
try:
# Ensure currplugin is a valid path string
if not isinstance(currplugin, (str, bytes, os.PathLike)):
logger.error(f"Invalid plugin path: {type(currplugin)}")
continue
currPluginSetting = self.initSettings()
currPluginSetting = self.getPluginSettings(currplugin, currPluginSetting)

View File

@ -413,4 +413,4 @@ presentChatRoomLast = False
presentLiveRegionFromInactiveTab = False
# Plugins
activePlugins = ['Clipboard', 'DisplayVersion', 'ByeCthulhu', 'Time', 'HelloCthulhu', 'hello_world', 'self_voice', 'PluginManager', 'SimplePluginSystem']
activePlugins = ['Clipboard', 'DisplayVersion', 'ByeCthulhu', 'HelloCthulhu', 'hello_world', 'self_voice', 'SimplePluginSystem']