Files

164 lines
8.4 KiB
C

// 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;
}