201 lines
10 KiB
C
201 lines
10 KiB
C
// Copyright (c) 2026 Storm Dragon
|
|
// Authors: Storm Dragon, Angus Kola
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
#include "native_internal.h"
|
|
|
|
enum { WORD, REPLACEMENT };
|
|
static GtkListStore *pronunciations;
|
|
|
|
static void pronunciation_edited(GtkCellRendererText *renderer, gchar *path, gchar *text, gpointer data) {
|
|
(void)renderer;
|
|
GtkTreeIter iter;
|
|
if (gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(pronunciations), &iter, path))
|
|
gtk_list_store_set(pronunciations, &iter, GPOINTER_TO_INT(data), text, -1);
|
|
}
|
|
|
|
static void pronunciation_add(GtkButton *button, gpointer data) {
|
|
(void)button; (void)data;
|
|
GtkTreeIter iter;
|
|
gtk_list_store_append(pronunciations, &iter);
|
|
gtk_list_store_set(pronunciations, &iter, WORD, "", REPLACEMENT, "", -1);
|
|
GtkTreePath *path = gtk_tree_model_get_path(GTK_TREE_MODEL(pronunciations), &iter);
|
|
GtkTreeView *view = GTK_TREE_VIEW(prefs_widget("pronunciationTreeView"));
|
|
gtk_widget_grab_focus(GTK_WIDGET(view));
|
|
gtk_tree_view_set_cursor(view, path, gtk_tree_view_get_column(view, WORD), TRUE);
|
|
gtk_tree_path_free(path);
|
|
}
|
|
|
|
static void pronunciation_delete(GtkButton *button, gpointer data) {
|
|
(void)button; (void)data;
|
|
GtkTreeIter iter;
|
|
GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(prefs_widget("pronunciationTreeView")));
|
|
if (gtk_tree_selection_get_selected(selection, NULL, &iter)) gtk_list_store_remove(pronunciations, &iter);
|
|
gtk_widget_grab_focus(prefs_widget("pronunciationTreeView"));
|
|
}
|
|
|
|
static gboolean pronunciation_key(GtkWidget *widget, GdkEventKey *event, gpointer data) {
|
|
(void)data;
|
|
if (event->keyval != GDK_KEY_Return && event->keyval != GDK_KEY_KP_Enter && event->keyval != GDK_KEY_F2) return FALSE;
|
|
GtkTreePath *path = NULL;
|
|
GtkTreeViewColumn *column = NULL;
|
|
gtk_tree_view_get_cursor(GTK_TREE_VIEW(widget), &path, &column);
|
|
if (!path) return FALSE;
|
|
gtk_tree_view_set_cursor(GTK_TREE_VIEW(widget), path, column ? column : gtk_tree_view_get_column(GTK_TREE_VIEW(widget), 0), TRUE);
|
|
gtk_tree_path_free(path);
|
|
return TRUE;
|
|
}
|
|
|
|
void prefs_init_pronunciation(void) {
|
|
GtkTreeView *view = GTK_TREE_VIEW(prefs_widget("pronunciationTreeView"));
|
|
pronunciations = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
|
|
gtk_tree_view_set_model(view, GTK_TREE_MODEL(pronunciations));
|
|
const char *labels[] = {"Word", "Pronunciation"};
|
|
for (int column = 0; column < 2; column++) {
|
|
GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
|
|
g_object_set(renderer, "editable", TRUE, NULL);
|
|
g_signal_connect(renderer, "edited", G_CALLBACK(pronunciation_edited), GINT_TO_POINTER(column));
|
|
gtk_tree_view_append_column(view, gtk_tree_view_column_new_with_attributes(labels[column], renderer, "text", column, NULL));
|
|
}
|
|
atk_object_set_name(gtk_widget_get_accessible(GTK_WIDGET(view)), "Pronunciation dictionary");
|
|
atk_object_set_description(gtk_widget_get_accessible(GTK_WIDGET(view)), "Select a cell and press Enter to edit it. Tab moves to the next control.");
|
|
g_signal_connect(view, "key-press-event", G_CALLBACK(pronunciation_key), NULL);
|
|
g_signal_connect(prefs_widget("pronunciationAddButton"), "clicked", G_CALLBACK(pronunciation_add), NULL);
|
|
g_signal_connect(prefs_widget("pronunciationDeleteButton"), "clicked", G_CALLBACK(pronunciation_delete), NULL);
|
|
g_object_unref(pronunciations);
|
|
}
|
|
|
|
void prefs_pronunciation_clear(void) { gtk_list_store_clear(pronunciations); }
|
|
void prefs_pronunciation_append(const char *word, const char *replacement) {
|
|
GtkTreeIter iter;
|
|
gtk_list_store_append(pronunciations, &iter);
|
|
gtk_list_store_set(pronunciations, &iter, WORD, word, REPLACEMENT, replacement, -1);
|
|
}
|
|
int prefs_pronunciation_count(void) { return gtk_tree_model_iter_n_children(GTK_TREE_MODEL(pronunciations), NULL); }
|
|
char *prefs_pronunciation_value(int row, int column) {
|
|
GtkTreeIter iter;
|
|
if (column < 0 || column > 1 || !gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(pronunciations), &iter, NULL, row)) return NULL;
|
|
char *value = NULL;
|
|
gtk_tree_model_get(GTK_TREE_MODEL(pronunciations), &iter, column, &value, -1);
|
|
return value;
|
|
}
|
|
|
|
enum { ATTRIBUTE_KEY, ATTRIBUTE_LABEL, ATTRIBUTE_SPEAK, ATTRIBUTE_BRAILLE };
|
|
static GtkListStore *attributes;
|
|
|
|
static void attribute_toggle(GtkCellRendererToggle *renderer, gchar *path, gpointer data) {
|
|
(void)renderer;
|
|
GtkTreeIter iter;
|
|
int column = GPOINTER_TO_INT(data);
|
|
if (!gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(attributes), &iter, path)) return;
|
|
gboolean active = FALSE;
|
|
gtk_tree_model_get(GTK_TREE_MODEL(attributes), &iter, column, &active, -1);
|
|
gtk_list_store_set(attributes, &iter, column, !active, -1);
|
|
}
|
|
|
|
static gboolean attribute_key(GtkWidget *widget, GdkEventKey *event, gpointer data) {
|
|
(void)data;
|
|
if (event->keyval != GDK_KEY_space && event->keyval != GDK_KEY_KP_Space) return FALSE;
|
|
GtkTreePath *path = NULL;
|
|
GtkTreeViewColumn *column = NULL;
|
|
gtk_tree_view_get_cursor(GTK_TREE_VIEW(widget), &path, &column);
|
|
if (!path) return FALSE;
|
|
GtkTreeIter iter;
|
|
gboolean found = gtk_tree_model_get_iter(GTK_TREE_MODEL(attributes), &iter, path);
|
|
gtk_tree_path_free(path);
|
|
if (!found) return FALSE;
|
|
int selected = column == gtk_tree_view_get_column(GTK_TREE_VIEW(widget), 2) ? ATTRIBUTE_BRAILLE : ATTRIBUTE_SPEAK;
|
|
gboolean active = FALSE;
|
|
gtk_tree_model_get(GTK_TREE_MODEL(attributes), &iter, selected, &active, -1);
|
|
gtk_list_store_set(attributes, &iter, selected, !active, -1);
|
|
return TRUE;
|
|
}
|
|
|
|
static void attribute_move(GtkButton *button, gpointer data) {
|
|
(void)button;
|
|
GtkTreeView *view = GTK_TREE_VIEW(prefs_widget("textAttributesTreeView"));
|
|
GtkTreeIter iter, other;
|
|
if (!gtk_tree_selection_get_selected(gtk_tree_view_get_selection(view), NULL, &iter)) return;
|
|
GtkTreePath *path = gtk_tree_model_get_path(GTK_TREE_MODEL(attributes), &iter);
|
|
int row = gtk_tree_path_get_indices(path)[0];
|
|
int last = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(attributes), NULL) - 1;
|
|
int target = row;
|
|
switch (GPOINTER_TO_INT(data)) {
|
|
case 0: target = 0; break;
|
|
case 1: target = row - 1; break;
|
|
case 2: target = row + 1; break;
|
|
case 3: target = last; break;
|
|
}
|
|
if (target >= 0 && target <= last && target != row &&
|
|
gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(attributes), &other, NULL, target)) {
|
|
if (target < row) gtk_list_store_move_before(attributes, &iter, &other);
|
|
else gtk_list_store_move_after(attributes, &iter, &other);
|
|
GtkTreePath *new_path = gtk_tree_path_new_from_indices(target, -1);
|
|
gtk_tree_view_set_cursor(view, new_path, NULL, FALSE);
|
|
gtk_tree_path_free(new_path);
|
|
}
|
|
gtk_tree_path_free(path);
|
|
gtk_widget_grab_focus(GTK_WIDGET(view));
|
|
}
|
|
|
|
static void attribute_select_all(GtkButton *button, gpointer data) {
|
|
(void)button;
|
|
gboolean active = GPOINTER_TO_INT(data) != 0;
|
|
GtkTreeIter iter;
|
|
gboolean valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(attributes), &iter);
|
|
while (valid) {
|
|
gtk_list_store_set(attributes, &iter, ATTRIBUTE_SPEAK, active, ATTRIBUTE_BRAILLE, active, -1);
|
|
valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(attributes), &iter);
|
|
}
|
|
}
|
|
|
|
static void attribute_reset(GtkButton *button, gpointer data) { (void)button; (void)data; prefs_set_action(8); }
|
|
|
|
void prefs_init_text_attributes(void) {
|
|
GtkTreeView *view = GTK_TREE_VIEW(prefs_widget("textAttributesTreeView"));
|
|
attributes = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN);
|
|
gtk_tree_view_set_model(view, GTK_TREE_MODEL(attributes));
|
|
GtkCellRenderer *text = gtk_cell_renderer_text_new();
|
|
gtk_tree_view_append_column(view, gtk_tree_view_column_new_with_attributes("Attribute", text, "text", ATTRIBUTE_LABEL, NULL));
|
|
const char *labels[] = {"Speak", "Braille"};
|
|
for (int i = 0; i < 2; i++) {
|
|
GtkCellRenderer *renderer = gtk_cell_renderer_toggle_new();
|
|
g_object_set(renderer, "activatable", TRUE, NULL);
|
|
g_signal_connect(renderer, "toggled", G_CALLBACK(attribute_toggle), GINT_TO_POINTER(i+ATTRIBUTE_SPEAK));
|
|
gtk_tree_view_append_column(view, gtk_tree_view_column_new_with_attributes(labels[i], renderer, "active", i+ATTRIBUTE_SPEAK, NULL));
|
|
}
|
|
g_signal_connect(view, "key-press-event", G_CALLBACK(attribute_key), NULL);
|
|
AtkObject *accessible = gtk_widget_get_accessible(GTK_WIDGET(view));
|
|
atk_object_set_name(accessible, "Text attributes");
|
|
atk_object_set_description(accessible, "Use arrows to select an attribute. Space toggles the selected Speak or Braille column. Use the Move buttons to change order.");
|
|
const char *moves[] = {"textMoveToTopButton", "textMoveUpOneButton", "textMoveDownOneButton", "textMoveToBottomButton"};
|
|
for (int i = 0; i < 4; i++) g_signal_connect(prefs_widget(moves[i]), "clicked", G_CALLBACK(attribute_move), GINT_TO_POINTER(i));
|
|
g_signal_connect(prefs_widget("textSelectAllButton"), "clicked", G_CALLBACK(attribute_select_all), GINT_TO_POINTER(1));
|
|
g_signal_connect(prefs_widget("textUnselectAllButton"), "clicked", G_CALLBACK(attribute_select_all), GINT_TO_POINTER(0));
|
|
g_signal_connect(prefs_widget("textResetButton"), "clicked", G_CALLBACK(attribute_reset), NULL);
|
|
g_object_unref(attributes);
|
|
}
|
|
|
|
void prefs_text_attributes_clear(void) { gtk_list_store_clear(attributes); }
|
|
void prefs_text_attribute_append(const char *key, const char *label, int speak, int braille) {
|
|
GtkTreeIter iter;
|
|
gtk_list_store_append(attributes, &iter);
|
|
gtk_list_store_set(attributes, &iter, ATTRIBUTE_KEY, key, ATTRIBUTE_LABEL, label,
|
|
ATTRIBUTE_SPEAK, speak != 0, ATTRIBUTE_BRAILLE, braille != 0, -1);
|
|
}
|
|
int prefs_text_attribute_count(void) { return gtk_tree_model_iter_n_children(GTK_TREE_MODEL(attributes), NULL); }
|
|
char *prefs_text_attribute_key(int row) {
|
|
GtkTreeIter iter;
|
|
if (!gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(attributes), &iter, NULL, row)) return NULL;
|
|
char *key = NULL;
|
|
gtk_tree_model_get(GTK_TREE_MODEL(attributes), &iter, ATTRIBUTE_KEY, &key, -1);
|
|
return key;
|
|
}
|
|
int prefs_text_attribute_enabled(int row, int column) {
|
|
GtkTreeIter iter;
|
|
if (column < ATTRIBUTE_SPEAK || column > ATTRIBUTE_BRAILLE ||
|
|
!gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(attributes), &iter, NULL, row)) return 0;
|
|
gboolean active = FALSE;
|
|
gtk_tree_model_get(GTK_TREE_MODEL(attributes), &iter, column, &active, -1);
|
|
return active;
|
|
}
|