Cthulhu is no longer a fork. Now it is a screen reader written in Golang.
This commit is contained in:
1 parent
a998fe67de
commit
069fc674dc
708 files changed
+112482
-532
No files matched your search
@@ -0,0 +1,163 @@
|
||||
// Copyright (c) 2026 Storm Dragon
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#include "native_internal.h"
|
||||
|
||||
static GtkListStore *tentacleStore;
|
||||
static gboolean updating;
|
||||
|
||||
static void name_toggle_cells(void) {
|
||||
AtkObject *accessible = gtk_widget_get_accessible(prefs_widget("tentaclesTree"));
|
||||
if (!ATK_IS_TABLE(accessible)) return;
|
||||
int rows = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(tentacleStore), NULL);
|
||||
for (int row = 0; row < rows; row++) {
|
||||
AtkObject *cell = atk_table_ref_at(ATK_TABLE(accessible), row, 0);
|
||||
if (!cell) continue;
|
||||
GtkTreeIter iter;
|
||||
gboolean enabled = FALSE;
|
||||
gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(tentacleStore), &iter, NULL, row);
|
||||
gtk_tree_model_get(GTK_TREE_MODEL(tentacleStore), &iter, 0, &enabled, -1);
|
||||
atk_object_set_name(cell, enabled ? "checked" : "unchecked");
|
||||
g_object_unref(cell);
|
||||
}
|
||||
}
|
||||
static void tentacle_selected(GtkTreeSelection *selection, gpointer data) {
|
||||
(void)selection; (void)data;
|
||||
if (!updating) prefs_set_action(16);
|
||||
}
|
||||
static void tentacle_clicked(GtkButton *button, gpointer data) {
|
||||
(void)button;
|
||||
prefs_set_action(GPOINTER_TO_INT(data));
|
||||
}
|
||||
static void tentacle_toggled(GtkCellRendererToggle *renderer, gchar *pathString, gpointer data) {
|
||||
(void)renderer; (void)data;
|
||||
if (updating) return;
|
||||
GtkTreePath *path = gtk_tree_path_new_from_string(pathString);
|
||||
gtk_tree_view_set_cursor(GTK_TREE_VIEW(prefs_widget("tentaclesTree")), path, NULL, FALSE);
|
||||
gtk_tree_path_free(path);
|
||||
// The accepted backend snapshot owns the check state; never check
|
||||
// optimistically before persistence or after an operation has failed.
|
||||
prefs_set_action(11);
|
||||
}
|
||||
static gboolean tentacle_key(GtkWidget *widget, GdkEventKey *event, gpointer data) {
|
||||
(void)widget; (void)data;
|
||||
if (event->keyval != GDK_KEY_space && event->keyval != GDK_KEY_KP_Space) return FALSE;
|
||||
prefs_set_action(11);
|
||||
return TRUE;
|
||||
}
|
||||
static void repository_switched(GtkNotebook *notebook, GtkWidget *page, guint index, gpointer data) {
|
||||
(void)notebook; (void)index; (void)data;
|
||||
GtkWidget *scroll = prefs_widget("tentacleTableScroll");
|
||||
GtkWidget *parent = gtk_widget_get_parent(scroll);
|
||||
if (parent != page) {
|
||||
g_object_ref(scroll);
|
||||
gtk_container_remove(GTK_CONTAINER(parent), scroll);
|
||||
gtk_container_add(GTK_CONTAINER(page), scroll);
|
||||
g_object_unref(scroll);
|
||||
}
|
||||
if (!updating) prefs_set_action(17);
|
||||
}
|
||||
void prefs_init_tentacles(void) {
|
||||
updating = FALSE;
|
||||
tentacleStore = gtk_list_store_new(7, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
|
||||
GtkTreeView *tree = GTK_TREE_VIEW(prefs_widget("tentaclesTree"));
|
||||
gtk_tree_view_set_model(tree, GTK_TREE_MODEL(tentacleStore));
|
||||
GtkCellRenderer *toggle = gtk_cell_renderer_toggle_new();
|
||||
g_signal_connect(toggle, "toggled", G_CALLBACK(tentacle_toggled), NULL);
|
||||
gtk_tree_view_append_column(tree, gtk_tree_view_column_new_with_attributes("Enabled", toggle, "active", 0, NULL));
|
||||
const char *titles[] = {"Name", "Status", "Version", "Repository", "Details", "Shortcuts"};
|
||||
for (unsigned i = 0; i < G_N_ELEMENTS(titles); i++) {
|
||||
GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
|
||||
gtk_tree_view_append_column(tree, gtk_tree_view_column_new_with_attributes(titles[i], renderer, "text", i + 1, NULL));
|
||||
}
|
||||
gtk_tree_selection_set_mode(gtk_tree_view_get_selection(tree), GTK_SELECTION_SINGLE);
|
||||
g_signal_connect(gtk_tree_view_get_selection(tree), "changed", G_CALLBACK(tentacle_selected), NULL);
|
||||
g_signal_connect(tree, "key-press-event", G_CALLBACK(tentacle_key), NULL);
|
||||
g_signal_connect(prefs_widget("tentacleRepositories"), "switch-page", G_CALLBACK(repository_switched), NULL);
|
||||
const struct { const char *id; int action; } buttons[] = {
|
||||
{"tentacleNew", 9}, {"tentacleRetry", 13}, {"tentacleRemove", 14},
|
||||
{"tentacleRefresh", 15}, {"tentacleRepositoryRemove", 12}, {"tentacleRepositoryRefresh", 18},
|
||||
{"tentacleInstall", 19}, {"tentacleSettings", 20},
|
||||
};
|
||||
for (unsigned i = 0; i < G_N_ELEMENTS(buttons); i++)
|
||||
g_signal_connect(prefs_widget(buttons[i].id), "clicked", G_CALLBACK(tentacle_clicked), GINT_TO_POINTER(buttons[i].action));
|
||||
atk_object_set_name(gtk_widget_get_accessible(GTK_WIDGET(tree)), "Tentacles");
|
||||
atk_object_set_name(gtk_widget_get_accessible(prefs_widget("tentacleRepositoryURL")), "Repository URL");
|
||||
atk_object_set_name(gtk_widget_get_accessible(prefs_widget("tentacleRepositories")), "Tentacle repositories");
|
||||
g_object_unref(tentacleStore); // Owned by the tree.
|
||||
}
|
||||
void prefs_tentacles_clear(void) {
|
||||
updating = TRUE;
|
||||
gtk_list_store_clear(tentacleStore);
|
||||
}
|
||||
void prefs_tentacle_append(int enabled, const char *name, const char *state, const char *version, const char *repository, const char *message, const char *shortcuts) {
|
||||
GtkTreeIter iter;
|
||||
gtk_list_store_append(tentacleStore, &iter);
|
||||
gtk_list_store_set(tentacleStore, &iter, 0, enabled != 0, 1, name, 2, state, 3, version, 4, repository, 5, message, 6, shortcuts, -1);
|
||||
}
|
||||
int prefs_tentacle_selected(void) {
|
||||
GtkTreeModel *model;
|
||||
GtkTreeIter iter;
|
||||
GtkTreeView *tree = GTK_TREE_VIEW(prefs_widget("tentaclesTree"));
|
||||
if (!gtk_tree_selection_get_selected(gtk_tree_view_get_selection(tree), &model, &iter)) return -1;
|
||||
GtkTreePath *path = gtk_tree_model_get_path(model, &iter);
|
||||
int row = gtk_tree_path_get_indices(path)[0];
|
||||
gtk_tree_path_free(path);
|
||||
return row;
|
||||
}
|
||||
void prefs_tentacle_select(int row) {
|
||||
updating = TRUE;
|
||||
GtkTreeView *tree = GTK_TREE_VIEW(prefs_widget("tentaclesTree"));
|
||||
gtk_tree_selection_unselect_all(gtk_tree_view_get_selection(tree));
|
||||
if (row >= 0) {
|
||||
GtkTreePath *path = gtk_tree_path_new_from_indices(row, -1);
|
||||
if (gtk_widget_has_focus(GTK_WIDGET(tree))) gtk_tree_view_set_cursor(tree, path, NULL, FALSE);
|
||||
else gtk_tree_selection_select_path(gtk_tree_view_get_selection(tree), path);
|
||||
gtk_tree_path_free(path);
|
||||
}
|
||||
name_toggle_cells();
|
||||
updating = FALSE;
|
||||
}
|
||||
void prefs_tentacle_controls(int available, int installed, int enabled, int repository) {
|
||||
gtk_widget_set_sensitive(prefs_widget("tentacleNew"), available);
|
||||
gtk_widget_set_sensitive(prefs_widget("tentacleRepositoryURL"), available);
|
||||
gtk_widget_set_sensitive(prefs_widget("tentacleRetry"), available && installed && enabled);
|
||||
gtk_widget_set_sensitive(prefs_widget("tentacleSettings"), available && installed);
|
||||
gtk_widget_set_sensitive(prefs_widget("tentacleRemove"), available && installed);
|
||||
gtk_widget_set_sensitive(prefs_widget("tentacleInstall"), available && !installed && prefs_tentacle_selected() >= 0);
|
||||
gtk_widget_set_sensitive(prefs_widget("tentacleRepositoryRefresh"), available && repository);
|
||||
gtk_widget_set_sensitive(prefs_widget("tentacleRepositoryRemove"), available && repository);
|
||||
}
|
||||
int prefs_tentacle_confirm(const char *message, const char *label) {
|
||||
GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(prefs_widget("window")),
|
||||
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION,
|
||||
GTK_BUTTONS_NONE, "%s", message);
|
||||
gtk_window_set_title(GTK_WINDOW(dialog), "Install tentacle");
|
||||
gtk_dialog_add_button(GTK_DIALOG(dialog), "Cancel", GTK_RESPONSE_CANCEL);
|
||||
gtk_dialog_add_button(GTK_DIALOG(dialog), label, GTK_RESPONSE_ACCEPT);
|
||||
gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_CANCEL);
|
||||
int response = gtk_dialog_run(GTK_DIALOG(dialog));
|
||||
gtk_widget_destroy(dialog);
|
||||
return response == GTK_RESPONSE_ACCEPT;
|
||||
}
|
||||
void prefs_tentacle_repositories_clear(void) {
|
||||
updating = TRUE;
|
||||
GtkNotebook *notebook = GTK_NOTEBOOK(prefs_widget("tentacleRepositories"));
|
||||
gtk_notebook_set_current_page(notebook, 0);
|
||||
while (gtk_notebook_get_n_pages(notebook) > 1) gtk_notebook_remove_page(notebook, -1);
|
||||
updating = FALSE;
|
||||
}
|
||||
void prefs_tentacle_repository_append(const char *label) {
|
||||
updating = TRUE;
|
||||
GtkWidget *page = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
|
||||
gtk_notebook_append_page(GTK_NOTEBOOK(prefs_widget("tentacleRepositories")), page, gtk_label_new(label));
|
||||
gtk_widget_show_all(page);
|
||||
updating = FALSE;
|
||||
}
|
||||
int prefs_tentacle_repository_selected(void) {
|
||||
return gtk_notebook_get_current_page(GTK_NOTEBOOK(prefs_widget("tentacleRepositories")));
|
||||
}
|
||||
void prefs_tentacle_repository_select(int page) {
|
||||
updating = TRUE;
|
||||
gtk_notebook_set_current_page(GTK_NOTEBOOK(prefs_widget("tentacleRepositories")), page);
|
||||
updating = FALSE;
|
||||
}
|
||||
Reference in new issue
Block a user