156 lines
8.1 KiB
C
156 lines
8.1 KiB
C
// Copyright (c) 2026 Storm Dragon
|
|
// Authors: Storm Dragon, Angus Kola
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
#include "native_internal.h"
|
|
|
|
static GtkListStore *binding_store;
|
|
static void binding_selected(GtkTreeSelection *selection, gpointer data) {
|
|
(void)data;
|
|
GtkTreeModel *model;
|
|
GtkTreeIter iter;
|
|
gboolean selected = gtk_tree_selection_get_selected(selection, &model, &iter);
|
|
const char *controls[] = {"keyBindingEntry", "keyBindingSet", "keyBindingClear", "keyBindingReset"};
|
|
for (unsigned i = 0; i < G_N_ELEMENTS(controls); i++) gtk_widget_set_sensitive(prefs_widget(controls[i]), selected);
|
|
gchar *keys = NULL;
|
|
if (selected) gtk_tree_model_get(model, &iter, 2, &keys, -1);
|
|
gtk_entry_set_text(GTK_ENTRY(prefs_widget("keyBindingEntry")), keys && strcmp(keys, "Unbound") != 0 ? keys : "");
|
|
g_free(keys);
|
|
}
|
|
static void binding_clicked(GtkButton *button, gpointer data) { (void)button; prefs_set_action(GPOINTER_TO_INT(data)); }
|
|
void prefs_init_key_bindings(void) {
|
|
binding_store = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
|
|
GtkTreeView *tree = GTK_TREE_VIEW(prefs_widget("keyBindingsTreeview"));
|
|
gtk_tree_view_set_model(tree, GTK_TREE_MODEL(binding_store));
|
|
const char *titles[] = {"Command", "Scope", "Binding", "Default binding"};
|
|
for (int i = 0; i < 4; 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, 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(binding_selected), NULL);
|
|
const char *buttons[] = {"keyBindingSet", "keyBindingClear", "keyBindingReset", "keyBindingResetAll"};
|
|
for (int i = 0; i < 4; i++) g_signal_connect(prefs_widget(buttons[i]), "clicked", G_CALLBACK(binding_clicked), GINT_TO_POINTER(i + 4));
|
|
atk_object_set_name(gtk_widget_get_accessible(GTK_WIDGET(tree)), "Key bindings");
|
|
binding_selected(gtk_tree_view_get_selection(tree), NULL);
|
|
g_object_unref(binding_store); // The tree owns the model.
|
|
}
|
|
void prefs_bindings_clear(void) { gtk_list_store_clear(binding_store); }
|
|
void prefs_binding_append(const char *label, const char *scope, const char *keys, const char *defaults) {
|
|
GtkTreeIter iter;
|
|
gtk_list_store_append(binding_store, &iter);
|
|
gtk_list_store_set(binding_store, &iter, 0, label, 1, scope, 2, keys, 3, defaults, -1);
|
|
}
|
|
int prefs_binding_selected(void) {
|
|
GtkTreeModel *model;
|
|
GtkTreeIter iter;
|
|
if (!gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(prefs_widget("keyBindingsTreeview"))), &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_binding_select(int row) {
|
|
if (row < 0) return;
|
|
GtkTreePath *path = gtk_tree_path_new_from_indices(row, -1);
|
|
// Setting the cursor grabs focus and can switch the notebook to this page
|
|
// while loading preferences or changing the layout combo. Select the row
|
|
// without moving keyboard focus out of the current control.
|
|
GtkTreeView *tree = GTK_TREE_VIEW(prefs_widget("keyBindingsTreeview"));
|
|
gtk_tree_selection_select_path(gtk_tree_view_get_selection(tree), path);
|
|
gtk_tree_view_scroll_to_cell(tree, path, NULL, FALSE, 0, 0);
|
|
gtk_tree_path_free(path);
|
|
}
|
|
static GtkListStore *modifierStore;
|
|
static const struct { const char *id; const char *label; } modifierRows[] = {
|
|
{"Insert", "Insert"}, {"KP_Insert", "Numpad Insert"}, {"Caps_Lock", "Caps Lock"},
|
|
};
|
|
static void name_modifier_cells(void) {
|
|
AtkObject *accessible = gtk_widget_get_accessible(prefs_widget("cthulhuModifiersTreeview"));
|
|
if (!ATK_IS_TABLE(accessible)) return;
|
|
for (unsigned i = 0; i < G_N_ELEMENTS(modifierRows); i++) {
|
|
AtkObject *cell = atk_table_ref_at(ATK_TABLE(accessible), i, 0);
|
|
if (!cell) continue;
|
|
gboolean enabled = prefs_modifier_get(modifierRows[i].id);
|
|
const char *name = enabled ? "checked" : "unchecked";
|
|
if (g_strcmp0(atk_object_get_name(cell), name) != 0) atk_object_set_name(cell, name);
|
|
g_object_unref(cell);
|
|
}
|
|
}
|
|
void prefs_modifier_set(const char *id, int enabled) {
|
|
for (unsigned i = 0; i < G_N_ELEMENTS(modifierRows); i++) {
|
|
if (strcmp(id, modifierRows[i].id) != 0) continue;
|
|
if (prefs_modifier_get(id) == (enabled != 0)) return;
|
|
GtkTreeIter iter;
|
|
if (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(modifierStore), &iter, NULL, i))
|
|
gtk_list_store_set(modifierStore, &iter, 1, enabled != 0, -1);
|
|
name_modifier_cells();
|
|
return;
|
|
}
|
|
}
|
|
int prefs_modifier_get(const char *id) {
|
|
for (unsigned i = 0; i < G_N_ELEMENTS(modifierRows); i++) {
|
|
if (strcmp(id, modifierRows[i].id) != 0) continue;
|
|
GtkTreeIter iter;
|
|
gboolean enabled = FALSE;
|
|
if (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(modifierStore), &iter, NULL, i))
|
|
gtk_tree_model_get(GTK_TREE_MODEL(modifierStore), &iter, 1, &enabled, -1);
|
|
return enabled;
|
|
}
|
|
return FALSE;
|
|
}
|
|
static void toggle_modifier_path(const char *pathString) {
|
|
GtkTreeIter iter;
|
|
if (!gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(modifierStore), &iter, pathString)) return;
|
|
gboolean enabled;
|
|
gtk_tree_model_get(GTK_TREE_MODEL(modifierStore), &iter, 1, &enabled, -1);
|
|
gtk_list_store_set(modifierStore, &iter, 1, !enabled, -1);
|
|
name_modifier_cells();
|
|
}
|
|
static void modifier_toggled(GtkCellRendererToggle *renderer, gchar *pathString, gpointer data) {
|
|
(void)renderer; (void)data;
|
|
toggle_modifier_path(pathString);
|
|
}
|
|
static gboolean modifier_key_press(GtkWidget *view, GdkEventKey *event, gpointer data) {
|
|
(void)data;
|
|
if (event->keyval != GDK_KEY_space && event->keyval != GDK_KEY_KP_Space) return FALSE;
|
|
GtkTreePath *path = NULL;
|
|
gtk_tree_view_get_cursor(GTK_TREE_VIEW(view), &path, NULL);
|
|
if (!path) return FALSE;
|
|
gchar *pathString = gtk_tree_path_to_string(path);
|
|
toggle_modifier_path(pathString);
|
|
g_free(pathString);
|
|
gtk_tree_path_free(path);
|
|
return TRUE;
|
|
}
|
|
static void layout_modifiers(GtkToggleButton *button, gpointer data) {
|
|
if (!gtk_toggle_button_get_active(button)) return;
|
|
int laptop = GPOINTER_TO_INT(data);
|
|
prefs_modifier_set("Insert", FALSE);
|
|
prefs_modifier_set("KP_Insert", !laptop);
|
|
prefs_modifier_set("Caps_Lock", laptop);
|
|
}
|
|
void prefs_init_modifiers(void) {
|
|
modifierStore = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_BOOLEAN);
|
|
for (unsigned i = 0; i < G_N_ELEMENTS(modifierRows); i++) {
|
|
GtkTreeIter iter;
|
|
gtk_list_store_append(modifierStore, &iter);
|
|
gtk_list_store_set(modifierStore, &iter, 0, modifierRows[i].label, 1, FALSE, -1);
|
|
}
|
|
GtkTreeView *view = GTK_TREE_VIEW(prefs_widget("cthulhuModifiersTreeview"));
|
|
gtk_tree_view_set_model(view, GTK_TREE_MODEL(modifierStore));
|
|
GtkCellRenderer *textRenderer = gtk_cell_renderer_text_new();
|
|
GtkCellRenderer *toggleRenderer = gtk_cell_renderer_toggle_new();
|
|
g_signal_connect(toggleRenderer, "toggled", G_CALLBACK(modifier_toggled), NULL);
|
|
gtk_tree_view_append_column(view, gtk_tree_view_column_new_with_attributes("Enabled", toggleRenderer, "active", 1, NULL));
|
|
gtk_tree_view_append_column(view, gtk_tree_view_column_new_with_attributes("Cthulhu key", textRenderer, "text", 0, NULL));
|
|
g_signal_connect(view, "key-press-event", G_CALLBACK(modifier_key_press), NULL);
|
|
AtkObject *accessible = gtk_widget_get_accessible(GTK_WIDGET(view));
|
|
atk_object_set_name(accessible, "Cthulhu modifier keys");
|
|
atk_object_set_description(accessible, "Use arrow keys to select Insert, Numpad Insert, or Caps Lock. Space toggles each key independently. Tab leaves the table.");
|
|
g_signal_connect(prefs_widget("generalDesktopButton"), "toggled", G_CALLBACK(layout_modifiers), GINT_TO_POINTER(0));
|
|
g_signal_connect(prefs_widget("generalLaptopButton"), "toggled", G_CALLBACK(layout_modifiers), GINT_TO_POINTER(1));
|
|
name_modifier_cells();
|
|
g_object_unref(modifierStore); // The tree owns the model.
|
|
}
|
|
|