/* Copyright (c) 2005-2009 uim Project http://code.google.com/p/uim/ All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of authors nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include "uim/uim.h" #include "uim/uim-custom.h" #include "uim/gettext.h" #include "../gtk/key-util-gtk.h" #include "pref-gtk-keytab.h" #define DEFAULT_OLIST_WINDOW_WIDTH 480 #define DEFAULT_OLIST_WINDOW_HEIGHT 350 #define DEFAULT_KEYCONF_WINDOW_WIDTH 280 #define DEFAULT_KEYCONF_WINDOW_HEIGHT 220 #define OBJECT_DATA_UIM_CUSTOM_SYM "uim-pref-gtk::uim-custom-sym" extern gboolean uim_pref_gtk_value_changed; extern void uim_pref_gtk_mark_value_changed(void); static GtkSizeGroup *spin_button_sgroup = NULL; static struct OListPrefWin { GtkWidget *window; GtkWidget *tree_view[2]; GtkWidget *up_button; GtkWidget *down_button; GtkWidget *left_button; GtkWidget *right_button; } olist_pref_win = { NULL, {NULL, NULL}, }; static struct KeyPrefWin { GtkWidget *window; GtkWidget *tree_view; GtkWidget *add_button; GtkWidget *remove_button; GtkWidget *keycode_entry; gint grabbed_key_val; gint grabbed_key_state; } key_pref_win = { NULL, NULL, NULL, NULL, NULL, 0, 0, }; static void uimpref_file_entry_class_init(UimPrefFileEntryClass *klass); static void uimpref_file_entry_init(UimPrefFileEntry *entry); GType uimpref_file_entry_get_type(void) { static GType uimpref_file_entry_type = 0; if (!uimpref_file_entry_type) { static const GTypeInfo uimpref_file_entry_info = { sizeof(UimPrefFileEntryClass), NULL, NULL, (GClassInitFunc)uimpref_file_entry_class_init, NULL, NULL, sizeof(UimPrefFileEntry), 0, (GInstanceInitFunc)uimpref_file_entry_init, }; uimpref_file_entry_type = g_type_register_static(GTK_TYPE_ENTRY, "UimPrefFileEntry", &uimpref_file_entry_info, 0); } return uimpref_file_entry_type; } static void uimpref_file_entry_class_init(UimPrefFileEntryClass *klass) { } static void uimpref_file_entry_init(UimPrefFileEntry *entry) { entry->type = UCustomPathnameType_RegularFile; } GtkWidget * uimpref_file_entry_new() { return GTK_WIDGET(g_object_new(uimpref_file_entry_get_type(), NULL)); } static void custom_check_button_toggled_cb(GtkToggleButton *button, gpointer user_data) { const char *custom_sym; struct uim_custom *custom; uim_bool rv; gboolean active; custom_sym = g_object_get_data(G_OBJECT(button), OBJECT_DATA_UIM_CUSTOM_SYM); g_return_if_fail(custom_sym); custom = uim_custom_get(custom_sym); g_return_if_fail(custom && custom->type == UCustom_Bool); active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)); custom->value->as_bool = active; rv = uim_custom_set(custom); if (rv) { uim_pref_gtk_mark_value_changed(); } else { g_printerr("Failed to set bool value for \"%s\".\n", custom->symbol); /* FIXME! reset the widget */ } uim_custom_free(custom); } static void sync_value_bool(GtkCheckButton *button) { const char *custom_sym; struct uim_custom *custom; g_signal_handlers_block_by_func(G_OBJECT(button), (gpointer)(uintptr_t) custom_check_button_toggled_cb, NULL); custom_sym = g_object_get_data(G_OBJECT(button), OBJECT_DATA_UIM_CUSTOM_SYM); g_return_if_fail(custom_sym); custom = uim_custom_get(custom_sym); g_return_if_fail(custom && custom->type == UCustom_Bool); gtk_widget_set_sensitive(GTK_WIDGET(button), custom->is_active); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), custom->value->as_bool); uim_custom_free(custom); g_signal_handlers_unblock_by_func(G_OBJECT(button), (gpointer)(uintptr_t) custom_check_button_toggled_cb, NULL); } static void update_custom_type_bool_cb(void *ptr, const char *custom_sym) { sync_value_bool(GTK_CHECK_BUTTON(ptr)); } static void add_custom_type_bool(GtkWidget *vbox, struct uim_custom *custom) { GtkWidget *hbox; GtkWidget *check_button; hbox = gtk_hbox_new(FALSE, 8); check_button = gtk_check_button_new_with_label(custom->label); g_object_set_data_full(G_OBJECT(check_button), OBJECT_DATA_UIM_CUSTOM_SYM, g_strdup(custom->symbol), (GDestroyNotify) g_free); sync_value_bool(GTK_CHECK_BUTTON(check_button)); g_signal_connect(G_OBJECT(check_button), "toggled", G_CALLBACK(custom_check_button_toggled_cb), NULL); uim_custom_cb_add(custom->symbol, check_button, update_custom_type_bool_cb); gtk_box_pack_start (GTK_BOX (hbox), check_button, FALSE, TRUE, 0); gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 0); } static void custom_spin_button_value_changed(GtkSpinButton *spin, gpointer user_data) { const char *custom_sym; struct uim_custom *custom; uim_bool rv; custom_sym = g_object_get_data(G_OBJECT(spin), OBJECT_DATA_UIM_CUSTOM_SYM); g_return_if_fail(custom_sym); custom = uim_custom_get(custom_sym); g_return_if_fail(custom && custom->type == UCustom_Int); custom->value->as_int = gtk_spin_button_get_value(spin); rv = uim_custom_set(custom); if (rv) { uim_pref_gtk_mark_value_changed(); } else { g_printerr("Failed to set int value for \"%s\".\n", custom->symbol); /* FIXME! reset the widget */ } uim_custom_free(custom); } static void sync_value_int(GtkSpinButton *spin) { const char *custom_sym; struct uim_custom *custom; GtkAdjustment *adj; g_signal_handlers_block_by_func(G_OBJECT(spin), (gpointer)(uintptr_t) custom_spin_button_value_changed, NULL); adj = gtk_spin_button_get_adjustment(spin); custom_sym = g_object_get_data(G_OBJECT(spin), OBJECT_DATA_UIM_CUSTOM_SYM); g_return_if_fail(custom_sym); custom = uim_custom_get(custom_sym); g_return_if_fail(custom && custom->type == UCustom_Int); gtk_widget_set_sensitive(GTK_WIDGET(spin)->parent, custom->is_active); if (custom->range->as_int.min != (int) adj->lower || custom->range->as_int.max != (int) adj->upper) gtk_spin_button_set_range(spin, custom->range->as_int.min, custom->range->as_int.max); if (custom->value->as_int != (int) gtk_spin_button_get_value(spin)) { gtk_spin_button_set_value(spin, custom->value->as_int); } uim_custom_free(custom); g_signal_handlers_unblock_by_func(G_OBJECT(spin), (gpointer)(uintptr_t) custom_spin_button_value_changed, NULL); } static void update_custom_type_int_cb(void *ptr, const char *custom_sym) { sync_value_int(GTK_SPIN_BUTTON(ptr)); } static void add_custom_type_integer(GtkWidget *vbox, struct uim_custom *custom) { GtkWidget *hbox; GtkWidget *label; GtkAdjustment *adjustment; GtkWidget *spin; if (!spin_button_sgroup) spin_button_sgroup = gtk_size_group_new(GTK_SIZE_GROUP_VERTICAL); hbox = gtk_hbox_new(FALSE, 8); label = gtk_label_new(custom->label); gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0); adjustment = (GtkAdjustment*)gtk_adjustment_new(custom->value->as_int, /* initial size */ custom->range->as_int.min, /* minimum */ custom->range->as_int.max, /* maximum */ 1.0, 10.0, 0); spin = gtk_spin_button_new(adjustment, 1.0, 0); gtk_size_group_add_widget(spin_button_sgroup, spin); gtk_box_pack_end (GTK_BOX (hbox), spin, FALSE, TRUE, 0); gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 0); g_object_set_data_full(G_OBJECT(adjustment), OBJECT_DATA_UIM_CUSTOM_SYM, g_strdup(custom->symbol), (GDestroyNotify) g_free); g_object_set_data_full(G_OBJECT(spin), OBJECT_DATA_UIM_CUSTOM_SYM, g_strdup(custom->symbol), (GDestroyNotify) g_free); sync_value_int(GTK_SPIN_BUTTON(spin)); g_signal_connect(G_OBJECT(spin), "value-changed", G_CALLBACK(custom_spin_button_value_changed), NULL); uim_custom_cb_add(custom->symbol, spin, update_custom_type_int_cb); } static void custom_entry_changed_cb(GtkEntry *entry, gpointer user_data) { const char *custom_sym; struct uim_custom *custom; uim_bool rv; const char *str; custom_sym = g_object_get_data(G_OBJECT(entry), OBJECT_DATA_UIM_CUSTOM_SYM); g_return_if_fail(custom_sym); custom = uim_custom_get(custom_sym); g_return_if_fail(custom && (custom->type == UCustom_Str || custom->type == UCustom_Pathname)); str = gtk_entry_get_text(GTK_ENTRY(entry)); if (custom->type == UCustom_Str) { free(custom->value->as_str); custom->value->as_str = strdup(str); rv = uim_custom_set(custom); } else if (custom->type == UCustom_Pathname) { free(custom->value->as_pathname->str); custom->value->as_pathname->str = strdup(str); rv = uim_custom_set(custom); } else { rv = UIM_FALSE; } if (rv) { uim_pref_gtk_mark_value_changed(); } else { g_printerr("Failed to set str value for \"%s\".\n", custom->symbol); /* FIXME! reset the widget */ } uim_custom_free(custom); } static void sync_value_string(GtkEntry *entry) { const char *custom_sym; struct uim_custom *custom; g_signal_handlers_block_by_func(G_OBJECT(entry), (gpointer)(uintptr_t) custom_entry_changed_cb, NULL); custom_sym = g_object_get_data(G_OBJECT(entry), OBJECT_DATA_UIM_CUSTOM_SYM); g_return_if_fail(custom_sym); custom = uim_custom_get(custom_sym); g_return_if_fail(custom && (custom->type == UCustom_Str || custom->type == UCustom_Pathname)); gtk_widget_set_sensitive(GTK_WIDGET(entry)->parent, custom->is_active); if (custom->type == UCustom_Str) { gtk_entry_set_text(GTK_ENTRY(entry), custom->value->as_str); } else if (custom->type == UCustom_Pathname) { gtk_entry_set_text(GTK_ENTRY(entry), custom->value->as_pathname->str); } uim_custom_free(custom); g_signal_handlers_unblock_by_func(G_OBJECT(entry), (gpointer)(uintptr_t) custom_entry_changed_cb, NULL); } static void update_custom_type_string_cb(void *ptr, const char *custom_sym) { sync_value_string(GTK_ENTRY(ptr)); } static void add_custom_type_string(GtkWidget *vbox, struct uim_custom *custom) { GtkWidget *hbox; GtkWidget *label; GtkWidget *entry; hbox = gtk_hbox_new(FALSE, 8); label = gtk_label_new(custom->label); gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0); entry = gtk_entry_new(); gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0); gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 0); g_object_set_data_full(G_OBJECT(entry), OBJECT_DATA_UIM_CUSTOM_SYM, g_strdup(custom->symbol), (GDestroyNotify) g_free); sync_value_string(GTK_ENTRY(entry)); g_signal_connect(G_OBJECT(entry), "changed", G_CALLBACK(custom_entry_changed_cb), NULL); uim_custom_cb_add(custom->symbol, entry, update_custom_type_string_cb); } static void custom_pathname_button_clicked_cb(GtkWidget *button, GtkWidget *entry) { GtkWidget *dialog; GtkFileChooserAction action; switch (UIMPREF_FILE_ENTRY(entry)->type) { case UCustomPathnameType_Directory: action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER; break; case UCustomPathnameType_RegularFile: default: action = GTK_FILE_CHOOSER_ACTION_OPEN; break; } dialog = gtk_file_chooser_dialog_new (_("Specify file"), NULL, action, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL); if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) { char *filename; filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); if (filename) { gtk_entry_set_text(GTK_ENTRY(entry), filename); g_free (filename); } } gtk_widget_destroy (dialog); } static void add_custom_type_pathname(GtkWidget *vbox, struct uim_custom *custom) { GtkWidget *hbox; GtkWidget *label; GtkWidget *entry; GtkWidget *button; const char *button_label; hbox = gtk_hbox_new(FALSE, 8); label = gtk_label_new(custom->label); gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT); gtk_misc_set_alignment(GTK_MISC(label), 0, 0); gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0); entry = uimpref_file_entry_new(); UIMPREF_FILE_ENTRY(entry)->type = custom->value->as_pathname->type; gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0); /* Since both pathname type opens the file dialog to select an item * rather than open it, the label should always be "Select..." here. * The type is obvious for uses even if the button label does not * indicate it. Information about the action the button causes is * more important. Even if a better label has been found, it should * not contain the term 'directory' since GNOME uses 'folder' for * it. -- YamaKen 2006-01-21 */ switch (custom->value->as_pathname->type) { case UCustomPathnameType_Directory: button_label = N_("Select..."); break; case UCustomPathnameType_RegularFile: default: button_label = N_("Select..."); break; } button = gtk_button_new_with_label(dgettext(GETTEXT_PACKAGE, button_label)); gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, TRUE, 0); gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 0); g_object_set_data_full(G_OBJECT(entry), OBJECT_DATA_UIM_CUSTOM_SYM, g_strdup(custom->symbol), (GDestroyNotify) g_free); sync_value_string(GTK_ENTRY(entry)); g_signal_connect(G_OBJECT(entry), "changed", G_CALLBACK(custom_entry_changed_cb), NULL); g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(custom_pathname_button_clicked_cb), entry); uim_custom_cb_add(custom->symbol, entry, update_custom_type_string_cb); } static void custom_combo_box_changed(GtkComboBox *combo_box, gpointer user_data) { const char *custom_sym; struct uim_custom *custom; struct uim_custom_choice *choice = NULL; gint i, num; uim_bool rv; custom_sym = g_object_get_data(G_OBJECT(combo_box), OBJECT_DATA_UIM_CUSTOM_SYM); g_return_if_fail(custom_sym); custom = uim_custom_get(custom_sym); g_return_if_fail(custom_sym && custom->type == UCustom_Choice); num = gtk_combo_box_get_active(combo_box); /* check length of custom->range->as_choice.valid_items */ for (i = 0; custom->range->as_choice.valid_items && custom->range->as_choice.valid_items[i]; i++) { if (i == num) { choice = custom->range->as_choice.valid_items[i]; } } g_return_if_fail(choice); free(custom->value->as_choice->symbol); free(custom->value->as_choice->label); free(custom->value->as_choice->desc); custom->value->as_choice->symbol = choice->symbol ? strdup(choice->symbol): NULL; custom->value->as_choice->label = choice->label ? strdup(choice->label) : NULL; custom->value->as_choice->desc = choice->desc ? strdup(choice->desc) : NULL; rv = uim_custom_set(custom); if (rv) { uim_pref_gtk_mark_value_changed(); } else { g_printerr("Failed to set str value for \"%s\".\n", custom->symbol); /* FIXME! reset the widget */ } uim_custom_free(custom); } static void sync_value_choice(GtkComboBox *combobox) { const char *custom_sym; struct uim_custom *custom; struct uim_custom_choice **item; gint i = 0, default_index = 0; gchar *default_symbol; custom_sym = g_object_get_data(G_OBJECT(combobox), OBJECT_DATA_UIM_CUSTOM_SYM); g_return_if_fail(custom_sym); custom = uim_custom_get(custom_sym); g_return_if_fail (custom && custom->type == UCustom_Choice); item = custom->range->as_choice.valid_items; if (item == NULL || *item == NULL) { uim_custom_free(custom); return; } g_signal_handlers_block_by_func(G_OBJECT(combobox), (gpointer)(uintptr_t) custom_combo_box_changed, NULL); gtk_list_store_clear(GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(combobox)))); default_symbol = custom->value->as_choice->symbol; while (*item) { gtk_combo_box_append_text(GTK_COMBO_BOX(combobox), (*item)->label); if (!strcmp(default_symbol, (*item)->symbol)) default_index = i; i++; item++; } gtk_combo_box_set_active(GTK_COMBO_BOX(combobox), default_index); gtk_widget_set_sensitive(GTK_WIDGET(combobox)->parent, custom->is_active); g_signal_handlers_unblock_by_func(G_OBJECT(combobox), (gpointer)(uintptr_t) custom_combo_box_changed, NULL); uim_custom_free(custom); } static void update_custom_type_choice_cb(void *ptr, const char *custom_sym) { sync_value_choice(GTK_COMBO_BOX(ptr)); } static void add_custom_type_choice(GtkWidget *vbox, struct uim_custom *custom) { GtkWidget *hbox; GtkWidget *label; GtkWidget *combobox; hbox = gtk_hbox_new(FALSE, 8); gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 0); label = gtk_label_new(custom->label); gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0); combobox = gtk_combo_box_new_text(); gtk_box_pack_start (GTK_BOX (hbox), combobox, FALSE, TRUE, 0); g_object_set_data_full(G_OBJECT(combobox), OBJECT_DATA_UIM_CUSTOM_SYM, g_strdup(custom->symbol), (GDestroyNotify) g_free); sync_value_choice(GTK_COMBO_BOX(combobox)); g_signal_connect(G_OBJECT(combobox), "changed", G_CALLBACK(custom_combo_box_changed), NULL); uim_custom_cb_add(custom->symbol, combobox, update_custom_type_choice_cb); } static void olist_pref_dialog_response_cb(GtkDialog *dialog, gint action, GtkEntry *key_entry) { gtk_widget_destroy(GTK_WIDGET(dialog)); } static void sync_value_olist(GtkEntry *entry) { const char *custom_sym; struct uim_custom *custom; struct uim_custom_choice *item; GString *str; gint i; custom_sym = g_object_get_data(G_OBJECT(entry), OBJECT_DATA_UIM_CUSTOM_SYM); g_return_if_fail(custom_sym); custom = uim_custom_get(custom_sym); g_return_if_fail(custom && custom->type == UCustom_OrderedList); str = g_string_new(""); if (custom->value->as_olist) { for (item = custom->value->as_olist[0], i = 0; item; item = custom->value->as_olist[++i]) { if (i != 0) g_string_append(str, ", "); g_string_append_printf(str, "\"%s\"", item->label); } } else { /* error message */ } gtk_entry_set_text(GTK_ENTRY(entry), str->str); gtk_widget_set_sensitive(GTK_WIDGET(entry)->parent, custom->is_active); g_string_free(str, TRUE); uim_custom_free(custom); } static void olist_pref_tree_view_set_value(GtkEntry *olist_entry, gboolean left_view, gboolean right_view) { const char *custom_sym; struct uim_custom *custom; struct uim_custom_choice *item; GtkTreeView *view; GtkListStore *store; GtkTreeIter iter; gint i; custom_sym = g_object_get_data(G_OBJECT(olist_entry), OBJECT_DATA_UIM_CUSTOM_SYM); g_return_if_fail(custom_sym); custom = uim_custom_get(custom_sym); g_return_if_fail(custom && custom->type == UCustom_OrderedList); /* left tree view */ if (left_view) { view = GTK_TREE_VIEW(olist_pref_win.tree_view[0]); store = GTK_LIST_STORE(gtk_tree_view_get_model(view)); gtk_list_store_clear(store); for (item = custom->value->as_olist[0], i = 0; item; item = custom->value->as_olist[++i]) { gtk_list_store_append(store, &iter); gtk_list_store_set(store, &iter, 0, item->label, 1, item->symbol, -1); } } /* right tree view */ if (right_view) { view = GTK_TREE_VIEW(olist_pref_win.tree_view[1]); store = GTK_LIST_STORE(gtk_tree_view_get_model(view)); gtk_list_store_clear(store); for (item = custom->range->as_olist.valid_items[0], i = 0; item; item = custom->range->as_olist.valid_items[++i]) { struct uim_custom_choice *item2; gint j; gboolean enabled = FALSE; for (item2 = custom->value->as_olist[0], j = 0; item2; item2 = custom->value->as_olist[++j]) { if (!strcmp(item->symbol, item2->symbol)) { enabled = TRUE; break; } } if (!enabled) { gtk_list_store_append(store, &iter); gtk_list_store_set(store, &iter, 0, item->label, 1, item->symbol, -1); } } } uim_custom_free(custom); } static void set_olist_buttons_sensitive(GtkEntry *olist_entry) { const char *custom_sym; struct uim_custom *custom; GtkWidget *view; GtkTreeSelection *selection; gint num; gboolean is_selected1 = FALSE, is_selected2 = FALSE; gboolean is_multiple = FALSE, is_first = FALSE, is_end = FALSE; custom_sym = g_object_get_data(G_OBJECT(olist_entry), OBJECT_DATA_UIM_CUSTOM_SYM); g_return_if_fail(custom_sym); custom = uim_custom_get(custom_sym); g_return_if_fail(custom && custom->type == UCustom_OrderedList); view = olist_pref_win.tree_view[0]; selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view)); num = gtk_tree_selection_count_selected_rows(selection); if (num > 0) { is_selected1 = TRUE; if (num > 1) { is_multiple = TRUE; } else { GtkTreeModel *model; GList *rows = gtk_tree_selection_get_selected_rows(selection, &model); GtkTreePath *path = rows->data; GtkTreeIter iter; if (path && !gtk_tree_path_prev(path)) is_first = TRUE; if (gtk_tree_model_get_iter(model, &iter, path)) { if (!gtk_tree_model_iter_next(model, &iter)) { is_end = TRUE; } } g_list_foreach (rows, (GFunc)gtk_tree_path_free, NULL); g_list_free (rows); } } view = olist_pref_win.tree_view[1]; selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view)); num = gtk_tree_selection_count_selected_rows(selection); if (num > 0) is_selected2 = TRUE; gtk_widget_set_sensitive(olist_pref_win.up_button, is_selected1 && !is_first && !is_multiple); gtk_widget_set_sensitive(olist_pref_win.down_button, is_selected1 && !is_end && !is_multiple); gtk_widget_set_sensitive(olist_pref_win.right_button, is_selected1); gtk_widget_set_sensitive(olist_pref_win.left_button, is_selected2); uim_custom_free(custom); } static void olist_pref_up_button_clicked_cb(GtkWidget *widget, GtkEntry *olist_entry) { const char *custom_sym; struct uim_custom *custom; struct uim_custom_choice *choice; GtkWidget *view; GtkTreeSelection *selection; GtkTreeModel *model; GtkTreeIter iter1, iter2; GtkTreePath *path; GList *rows = NULL; gint num; gboolean rv; uim_bool urv; gint *indices, idx; custom_sym = g_object_get_data(G_OBJECT(olist_entry), OBJECT_DATA_UIM_CUSTOM_SYM); g_return_if_fail(custom_sym); custom = uim_custom_get(custom_sym);; g_return_if_fail(custom && custom->type == UCustom_OrderedList); view = olist_pref_win.tree_view[0]; selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view)); num = gtk_tree_selection_count_selected_rows(selection); if (num < 1) { uim_custom_free(custom); return; } if (num > 1) { uim_custom_free(custom); return; } rows = gtk_tree_selection_get_selected_rows(selection, &model); path = rows->data; rv = gtk_tree_model_get_iter(model, &iter1, path); if (!rv) goto ERROR; indices = gtk_tree_path_get_indices(path); if (!indices) goto ERROR; idx = *indices; rv = gtk_tree_path_prev(path); if (!rv) goto ERROR; /* real move */ choice = custom->value->as_olist[idx]; custom->value->as_olist[idx] = custom->value->as_olist[idx - 1]; custom->value->as_olist[idx - 1] = choice; urv = uim_custom_set(custom); if (urv == UIM_FALSE) return; uim_pref_gtk_mark_value_changed(); /* sync the view */ rv = gtk_tree_model_get_iter(model, &iter2, path); if (!rv) goto ERROR; gtk_list_store_swap(GTK_LIST_STORE(model), &iter1, &iter2); path = gtk_tree_model_get_path(model, &iter1); gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(view), path, NULL, FALSE, 0.0, 0.0); gtk_tree_path_free(path); set_olist_buttons_sensitive(olist_entry); sync_value_olist(GTK_ENTRY(olist_entry)); ERROR: uim_custom_free(custom); g_list_foreach(rows, (GFunc)gtk_tree_path_free, NULL); g_list_free(rows); } static void olist_pref_down_button_clicked_cb(GtkWidget *widget, GtkEntry *olist_entry) { const char *custom_sym; struct uim_custom *custom; struct uim_custom_choice *choice; GtkWidget *view; GtkTreeSelection *selection; GtkTreeModel *model; GtkTreeIter iter1, iter2; GtkTreePath *path; GList *rows = NULL; gint *indices, idx, num; gboolean rv; uim_bool urv; custom_sym = g_object_get_data(G_OBJECT(olist_entry), OBJECT_DATA_UIM_CUSTOM_SYM); g_return_if_fail(custom_sym); custom = uim_custom_get(custom_sym); g_return_if_fail(custom && custom->type == UCustom_OrderedList); view = olist_pref_win.tree_view[0]; selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view)); num = gtk_tree_selection_count_selected_rows(selection); if (num < 1) { uim_custom_free(custom); return; } if (num > 1) { uim_custom_free(custom); return; } rows = gtk_tree_selection_get_selected_rows(selection, &model); path = rows->data; if (!path) goto ERROR; indices = gtk_tree_path_get_indices(path); if (!indices) goto ERROR; idx = *indices; rv = gtk_tree_model_get_iter(model, &iter1, path); if (!rv) goto ERROR; iter2 = iter1; rv = gtk_tree_model_iter_next(model, &iter2); if (!rv) goto ERROR; /* real move */ choice = custom->value->as_olist[idx]; custom->value->as_olist[idx] = custom->value->as_olist[idx + 1]; custom->value->as_olist[idx + 1] = choice; urv = uim_custom_set(custom); if (urv == UIM_FALSE) goto ERROR; uim_pref_gtk_mark_value_changed(); /* sync the view */ gtk_list_store_swap(GTK_LIST_STORE(model), &iter1, &iter2); path = gtk_tree_model_get_path(model, &iter1); gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(view), path, NULL, FALSE, 0.0, 0.0); gtk_tree_path_free(path); set_olist_buttons_sensitive(olist_entry); sync_value_olist(GTK_ENTRY(olist_entry)); ERROR: uim_custom_free(custom); g_list_foreach(rows, (GFunc)gtk_tree_path_free, NULL); g_list_free(rows); } static void olist_pref_left_button_clicked_cb(GtkWidget *widget, GtkEntry *olist_entry) { const char *custom_sym; struct uim_custom *custom; GtkWidget *view; GtkTreeSelection *selection; GtkTreeModel *model; GtkTreeIter iter; GtkTreePath *path; gint num; GList *rows = NULL, *node; uim_bool urv; custom_sym = g_object_get_data(G_OBJECT(olist_entry), OBJECT_DATA_UIM_CUSTOM_SYM); g_return_if_fail(custom_sym); custom = uim_custom_get(custom_sym); g_return_if_fail(custom && custom->type == UCustom_OrderedList); view = olist_pref_win.tree_view[1]; selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view)); rows = gtk_tree_selection_get_selected_rows(selection, &model); if (!rows) { uim_custom_free(custom); return; } for (num = 0; custom->value->as_olist[num]; num++); for (node = rows; node; node = g_list_next(node)) { char *symbol = NULL; struct uim_custom_choice *choice = NULL; gint i; path = node->data; gtk_tree_model_get_iter(model, &iter, path); gtk_tree_model_get(model, &iter, 1, &symbol, -1); if (!symbol) continue; for (i = 0; custom->range->as_olist.valid_items[i]->symbol; i++) { if (!strcmp(custom->range->as_olist.valid_items[i]->symbol, symbol)) { choice = custom->range->as_olist.valid_items[i]; break; } } g_free(symbol); if (!choice) continue; num++; custom->value->as_olist = realloc(custom->value->as_olist, sizeof(struct uim_custom_choice *) * (num + 1)); custom->value->as_olist[num - 1] = malloc(sizeof(struct uim_custom_choice)); custom->value->as_olist[num - 1]->symbol = choice->symbol ? strdup(choice->symbol) : NULL; custom->value->as_olist[num - 1]->label = choice->label ? strdup(choice->label) : NULL; custom->value->as_olist[num - 1]->desc = choice->desc ? strdup(choice->desc) : NULL; custom->value->as_olist[num] = NULL; } urv = uim_custom_set(custom); if (urv != UIM_FALSE) { sync_value_olist(GTK_ENTRY(olist_entry)); olist_pref_tree_view_set_value(GTK_ENTRY(olist_entry), TRUE, TRUE); uim_pref_gtk_mark_value_changed(); } else { /* error message */ } uim_custom_free(custom); g_list_foreach(rows, (GFunc)gtk_tree_path_free, NULL); g_list_free(rows); } static void olist_pref_right_button_clicked_cb(GtkWidget *widget, GtkEntry *olist_entry) { const char *custom_sym; struct uim_custom *custom; struct uim_custom_choice *choice; GtkWidget *view; GtkTreeSelection *selection; GtkTreeModel *model; GtkTreePath *path; gint *indices, idx, num; GList *rows = NULL, *node; uim_bool urv; custom_sym = g_object_get_data(G_OBJECT(olist_entry), OBJECT_DATA_UIM_CUSTOM_SYM); g_return_if_fail(custom_sym); custom = uim_custom_get(custom_sym); g_return_if_fail(custom && custom->type == UCustom_OrderedList); view = olist_pref_win.tree_view[0]; selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view)); rows = gtk_tree_selection_get_selected_rows(selection, &model); if (!rows) { uim_custom_free(custom); return; } for (num = 0; custom->value->as_olist[num]; num++); for (node = g_list_last(rows); node; node = g_list_previous(node)) { path = node->data; indices = gtk_tree_path_get_indices(path); if (!indices) continue; idx = *indices; choice = custom->value->as_olist[idx]; free(choice->symbol); free(choice->label); free(choice->desc); free(choice); memmove(custom->value->as_olist + idx, custom->value->as_olist + idx + 1, sizeof(struct uim_custom_choice *) * (num - idx)); custom->value->as_olist = realloc(custom->value->as_olist, sizeof(struct uim_custom_choice *) * num); num--; } urv = uim_custom_set(custom); if (urv != UIM_FALSE) { sync_value_olist(GTK_ENTRY(olist_entry)); olist_pref_tree_view_set_value(GTK_ENTRY(olist_entry), TRUE, TRUE); uim_pref_gtk_mark_value_changed(); /* FIXME! reset the selection */ } else { /* error message */ } uim_custom_free(custom); g_list_foreach(rows, (GFunc)gtk_tree_path_free, NULL); g_list_free(rows); } static gboolean olist_pref_selection_changed(GtkTreeSelection *selection, GtkEntry *olist_entry) { set_olist_buttons_sensitive(olist_entry); return TRUE; } static void choose_olist_clicked_cb(GtkWidget *widget, GtkEntry *olist_entry) { const char *custom_sym; struct uim_custom *custom; GtkWidget *dialog, *hbox, *vbox, *scrwin, *table; GtkWidget *tree_view, *button, *arrow, *label; GtkListStore *store; GtkCellRenderer *renderer; GtkTreeViewColumn *column; GtkTreeSelection *selection; custom_sym = g_object_get_data(G_OBJECT(olist_entry), OBJECT_DATA_UIM_CUSTOM_SYM);; g_return_if_fail(custom_sym); custom = uim_custom_get(custom_sym); g_return_if_fail(custom); dialog = gtk_dialog_new_with_buttons( custom->label, GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(olist_entry))), GTK_DIALOG_MODAL, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL); uim_custom_free(custom); olist_pref_win.window = dialog; gtk_window_set_default_size(GTK_WINDOW(dialog), DEFAULT_OLIST_WINDOW_WIDTH, DEFAULT_OLIST_WINDOW_HEIGHT); g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(olist_pref_dialog_response_cb), olist_entry); hbox = gtk_hbox_new(FALSE, 0); gtk_container_set_border_width(GTK_CONTAINER(hbox), 4); gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox, TRUE, TRUE, 0); gtk_widget_show(hbox); /* left tree view */ vbox = gtk_vbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 0); gtk_widget_show(vbox); label = gtk_label_new(_("Enabled")); gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 4); gtk_widget_show(label); scrwin = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrwin), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrwin), GTK_SHADOW_IN); gtk_box_pack_start(GTK_BOX(vbox), scrwin, TRUE, TRUE, 0); gtk_widget_show(scrwin); store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING); tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); olist_pref_win.tree_view[0] = tree_view; gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree_view), FALSE); selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view)); gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE); g_signal_connect (G_OBJECT(selection), "changed", G_CALLBACK(olist_pref_selection_changed), olist_entry); gtk_container_add(GTK_CONTAINER(scrwin), tree_view); gtk_widget_show(tree_view); renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes(_("Enabled items"), renderer, "text", 0, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column); g_object_unref(store); /* button area */ vbox = gtk_vbox_new(TRUE, 0); gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 4); gtk_widget_show(vbox); table = gtk_table_new(3, 6, FALSE); gtk_table_set_row_spacings(GTK_TABLE(table), 3); gtk_table_set_col_spacings(GTK_TABLE(table), 3); gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0); gtk_widget_show(table); /* up button */ button = gtk_button_new(); olist_pref_win.up_button = button; gtk_table_attach_defaults(GTK_TABLE(table), button, 1, 2, 0, 1); gtk_widget_show(button); arrow = gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_NONE); gtk_container_add(GTK_CONTAINER(button), arrow); gtk_widget_show(arrow); g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(olist_pref_up_button_clicked_cb), olist_entry); /* down button */ button = gtk_button_new(); olist_pref_win.down_button = button; gtk_table_attach_defaults(GTK_TABLE(table), button, 1, 2, 2, 3); gtk_widget_show(button); arrow = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_NONE); gtk_container_add(GTK_CONTAINER(button), arrow); gtk_widget_show(arrow); g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(olist_pref_down_button_clicked_cb), olist_entry); /* left button */ button = gtk_button_new(); olist_pref_win.left_button = button; gtk_table_attach_defaults(GTK_TABLE(table), button, 0, 1, 1, 2); gtk_widget_show(button); arrow = gtk_arrow_new(GTK_ARROW_LEFT, GTK_SHADOW_NONE); gtk_container_add(GTK_CONTAINER(button), arrow); gtk_widget_show(arrow); g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(olist_pref_left_button_clicked_cb), olist_entry); /* right button */ button = gtk_button_new(); olist_pref_win.right_button = button; gtk_table_attach_defaults(GTK_TABLE(table), button, 2, 3, 1, 2); gtk_widget_show(button); arrow = gtk_arrow_new(GTK_ARROW_RIGHT, GTK_SHADOW_NONE); gtk_container_add(GTK_CONTAINER(button), arrow); gtk_widget_show(arrow); g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(olist_pref_right_button_clicked_cb), olist_entry); /* right tree view */ vbox = gtk_vbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 0); gtk_widget_show(vbox); label = gtk_label_new(_("Disabled")); gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 4); gtk_widget_show(label); scrwin = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrwin), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrwin), GTK_SHADOW_IN); gtk_box_pack_start(GTK_BOX(vbox), scrwin, TRUE, TRUE, 0); gtk_widget_show(scrwin); store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING); tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); olist_pref_win.tree_view[1] = tree_view; gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree_view), FALSE); selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view)); gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE); g_signal_connect (G_OBJECT(selection), "changed", G_CALLBACK(olist_pref_selection_changed), olist_entry); gtk_container_add(GTK_CONTAINER(scrwin), tree_view); gtk_widget_show(tree_view); renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes(_("Disabled items"), renderer, "text", 0, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column); g_object_unref(store); /* set value */ olist_pref_tree_view_set_value(olist_entry, TRUE, TRUE); set_olist_buttons_sensitive(olist_entry); /* show dialog */ gtk_widget_show(dialog); } static void update_custom_type_olist_cb(void *ptr, const char *custom_sym) { sync_value_olist(GTK_ENTRY(ptr)); } static void add_custom_type_orderedlist(GtkWidget *vbox, struct uim_custom *custom) { GtkWidget *hbox; GtkWidget *label, *entry, *button; hbox = gtk_hbox_new(FALSE, 8); gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); label = gtk_label_new(custom->label); gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0); entry = gtk_entry_new(); gtk_entry_set_editable(GTK_ENTRY(entry), FALSE); gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0); button = gtk_button_new_with_label(_("Edit...")); gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0); g_object_set_data_full(G_OBJECT(entry), OBJECT_DATA_UIM_CUSTOM_SYM, g_strdup(custom->symbol), (GDestroyNotify) g_free); sync_value_olist(GTK_ENTRY(entry)); g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(choose_olist_clicked_cb), entry); uim_custom_cb_add(custom->symbol, entry, update_custom_type_olist_cb); } static gboolean key_pref_selection_changed(GtkTreeSelection *selection, gpointer data) { GtkTreeModel *model; GtkTreeIter iter; gboolean selected; selected = gtk_tree_selection_get_selected(selection, &model, &iter); gtk_widget_set_sensitive(key_pref_win.remove_button, selected); return TRUE; } static void key_pref_set_value(gint ukey, gint umod) { GString *keystr; const char *sym; keystr = g_string_new(""); /* * Ignore Shift modifier for printable char keys for * easy-to-recognize key configuration. uim-custom performs * implicit shift key encoding/decoding appropriately. */ if (((ukey >= 256) || !g_ascii_isgraph(ukey)) && (umod & UMod_Shift)) g_string_append(keystr, ""); if (umod & UMod_Control) g_string_append(keystr, ""); if (umod & UMod_Alt) g_string_append(keystr, ""); if (umod & UMod_Meta) g_string_append(keystr, ""); if (umod & UMod_Super) g_string_append(keystr, ""); if (umod & UMod_Hyper) g_string_append(keystr, ""); switch (ukey) { case 0x20: /* * "space" is not proper uim keysym and only exists for user * convenience. It is converted to " " by uim-custom */ g_string_append(keystr, "space"); break; default: #if 0 if (keyval >= GDK_KP_0 && keyval <= GDK_KP_9) { g_string_append_printf(keystr, "%d", keyval - GDK_KP_0 + UKey_0); #endif if (ukey < 128) { g_string_append_printf(keystr, "%c", ukey); } else if ((sym = uim_pref_get_keysym(ukey))) { g_string_append(keystr, sym); } else { /* UKey_Other */ } break; } gtk_entry_set_text(GTK_ENTRY(key_pref_win.keycode_entry), keystr->str); key_pref_win.grabbed_key_val = 0; key_pref_win.grabbed_key_state = 0; g_string_free(keystr, TRUE); } static gboolean grab_win_key_press_cb (GtkWidget *widget, GdkEventKey *event, GtkEntry *key_entry) { im_uim_convert_keyevent(event, &key_pref_win.grabbed_key_val, &key_pref_win.grabbed_key_state); return TRUE; } static gboolean grab_win_key_release_cb (GtkWidget *widget, GdkEventKey *event, GtkEntry *key_entry) { key_pref_set_value(key_pref_win.grabbed_key_val, key_pref_win.grabbed_key_state); im_uim_convert_keyevent(event, &key_pref_win.grabbed_key_val, &key_pref_win.grabbed_key_state); g_signal_handlers_disconnect_by_func(G_OBJECT(widget), (gpointer)(uintptr_t) grab_win_key_press_cb, key_entry); g_signal_handlers_disconnect_by_func(G_OBJECT(widget), (gpointer)(uintptr_t) grab_win_key_release_cb, key_entry); gtk_dialog_response(GTK_DIALOG(widget), 0); return TRUE; } static gboolean key_choose_entry_key_press_cb (GtkWidget *widget, GdkEventKey *event, GtkEntry *key_entry) { int ukey, umod; im_uim_convert_keyevent(event, &ukey, &umod); key_pref_set_value(ukey, umod); return TRUE; } static gboolean key_choose_entry_key_release_cb (GtkWidget *widget, GdkEventKey *event, GtkEntry *key_entry) { int ukey, umod; im_uim_convert_keyevent(event, &ukey, &umod); return TRUE; } static void choose_key_button_clicked_cb(GtkWidget *widget, GtkEntry *key_entry) { GtkWidget *dialog; gint rv; dialog = gtk_message_dialog_new(GTK_WINDOW(gtk_widget_get_toplevel(widget)), GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_CANCEL, _("Press keys to grab (e.g. a)")); gtk_window_set_title(GTK_WINDOW(dialog), _("Grabbing keys")); g_signal_connect(G_OBJECT(dialog), "key-press-event", G_CALLBACK(grab_win_key_press_cb), key_entry); g_signal_connect(G_OBJECT(dialog), "key-release-event", G_CALLBACK(grab_win_key_release_cb), key_entry); gtk_widget_realize(dialog); gdk_keyboard_grab(GTK_WIDGET(dialog)->window, TRUE, GDK_CURRENT_TIME); rv = gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); } static void sync_value_key(GtkEntry *entry) { GString *str; const char *custom_sym; struct uim_custom *custom; struct uim_custom_key *key; gint i; custom_sym = g_object_get_data(G_OBJECT(entry), OBJECT_DATA_UIM_CUSTOM_SYM); g_return_if_fail(custom_sym); custom = uim_custom_get(custom_sym); g_return_if_fail(custom && custom->type == UCustom_Key); str = g_string_new(""); if (custom->value->as_key) { for (key = custom->value->as_key[0], i = 0; key; key = custom->value->as_key[++i]) { if (i != 0) g_string_append(str, ", "); g_string_append_printf(str, "\"%s\"", key->literal); } } else { /* error message */ } gtk_entry_set_text(GTK_ENTRY(entry), str->str); gtk_widget_set_sensitive(GTK_WIDGET(entry)->parent, custom->is_active); g_string_free(str, TRUE); uim_custom_free(custom); } static void key_pref_add_button_clicked_cb(GtkWidget *widget, GtkEntry *key_entry) { const char *custom_sym; struct uim_custom *custom; const char *key_code; GString *str; GtkTreeModel *model; GtkTreeIter iter; gint num; uim_bool rv; g_return_if_fail(GTK_IS_ENTRY(key_entry)); custom_sym = g_object_get_data(G_OBJECT(key_entry), OBJECT_DATA_UIM_CUSTOM_SYM); g_return_if_fail(custom_sym); custom = uim_custom_get(custom_sym); g_return_if_fail(custom && custom->type == UCustom_Key); key_code = gtk_entry_get_text(GTK_ENTRY(key_pref_win.keycode_entry)); if (!key_code || !*key_code) { uim_custom_free(custom); return; } str = g_string_new(""); g_string_append(str, key_code); for (num = 0; custom->value->as_key[num]; num++); num++; custom->value->as_key = realloc(custom->value->as_key, sizeof(struct uim_custom_key *) * (num + 1)); custom->value->as_key[num - 1] = malloc(sizeof(struct uim_custom_key)); custom->value->as_key[num - 1]->type = UCustomKey_Regular; custom->value->as_key[num - 1]->editor_type = UCustomKeyEditor_Basic; custom->value->as_key[num - 1]->literal = strdup(str->str); custom->value->as_key[num - 1]->label = strdup(""); custom->value->as_key[num - 1]->desc = strdup(""); custom->value->as_key[num] = NULL; rv = uim_custom_set(custom); if (rv != UIM_FALSE) { uim_pref_gtk_mark_value_changed(); model = gtk_tree_view_get_model(GTK_TREE_VIEW(key_pref_win.tree_view)); gtk_list_store_append(GTK_LIST_STORE(model), &iter); gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, str->str, -1); sync_value_key(key_entry); } else { g_printerr("Failed to set key value for \"%s\".\n", custom->symbol); } g_string_free(str, TRUE); uim_custom_free(custom); } static void key_pref_remove_button_clicked_cb(GtkWidget *widget, GtkEntry *key_entry) { const char *custom_sym; struct uim_custom *custom; GtkTreeSelection *selection; GtkTreeModel *model; GtkTreeIter iter; GtkTreePath *path; gboolean selected; gint num, *indices; uim_bool rv; g_return_if_fail(GTK_IS_ENTRY(key_entry)); custom_sym = g_object_get_data(G_OBJECT(key_entry), OBJECT_DATA_UIM_CUSTOM_SYM); g_return_if_fail(custom_sym); custom = uim_custom_get(custom_sym); g_return_if_fail(custom && custom->type == UCustom_Key); selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(key_pref_win.tree_view)); selected = gtk_tree_selection_get_selected(selection, &model, &iter); if (!selected) { uim_custom_free(custom); return; } for (num = 0; custom->value->as_key[num]; num++); path = gtk_tree_model_get_path(model, &iter); indices = gtk_tree_path_get_indices(path); if (num > 0 && *indices < num) { free(custom->value->as_key[*indices]->literal); free(custom->value->as_key[*indices]->label); free(custom->value->as_key[*indices]->desc); free(custom->value->as_key[*indices]); memmove(custom->value->as_key + *indices, custom->value->as_key + *indices + 1, sizeof(struct uim_custom_key *) * (num - *indices)); custom->value->as_key = realloc(custom->value->as_key, sizeof(struct uim_custom_key *) * num); rv = uim_custom_set(custom); if (rv != UIM_FALSE) { uim_pref_gtk_mark_value_changed(); gtk_list_store_remove(GTK_LIST_STORE(model), &iter); sync_value_key(key_entry); } else { g_printerr("Failed to set key value for \"%s\".\n", custom->symbol); } } else { /* error */ } gtk_tree_path_free(path); selected = gtk_tree_selection_get_selected(selection, &model, &iter); gtk_widget_set_sensitive(key_pref_win.remove_button, selected); uim_custom_free(custom); } static void key_val_entry_changed_cb(GtkEntry *entry, GtkEntry *key_entry) { const char *str = gtk_entry_get_text(entry); /* FIXME! validate key value */ gtk_widget_set_sensitive(key_pref_win.add_button, str && *str); } static void key_pref_dialog_response_cb(GtkDialog *dialog, gint action, GtkEntry *key_entry) { gtk_widget_destroy(GTK_WIDGET(dialog)); } static void choose_key_clicked_cb(GtkWidget *widget, GtkEntry *key_entry) { GtkWidget *dialog, *scrwin, *view, *hbox, *vbox, *label, *button, *entry; GtkTreeSelection *selection; GtkListStore *store; GtkCellRenderer *renderer; GtkTreeViewColumn *column; const char *custom_sym; struct uim_custom *custom; struct uim_custom_key *key; gint i; GtkIMContext *im_context; gchar title[256]; g_return_if_fail(GTK_IS_ENTRY(key_entry)); custom_sym = g_object_get_data(G_OBJECT(key_entry), OBJECT_DATA_UIM_CUSTOM_SYM); g_return_if_fail(custom_sym); custom = uim_custom_get(custom_sym); g_return_if_fail(custom && custom->type == UCustom_Key); g_snprintf(title, sizeof(title), _("%s - key configuration"), custom->label); /* setup key pref dialog */ dialog = gtk_dialog_new_with_buttons( title, GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(key_entry))), GTK_DIALOG_MODAL, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL); key_pref_win.window = dialog; gtk_window_set_default_size(GTK_WINDOW(dialog), DEFAULT_KEYCONF_WINDOW_WIDTH, DEFAULT_KEYCONF_WINDOW_HEIGHT); g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(key_pref_dialog_response_cb), key_entry); hbox = gtk_hbox_new(FALSE, 0); gtk_container_set_border_width(GTK_CONTAINER(hbox), 4); gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox, TRUE, TRUE, 0); gtk_widget_show(hbox); scrwin = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrwin), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrwin), GTK_SHADOW_IN); gtk_box_pack_start(GTK_BOX(hbox), scrwin, TRUE, TRUE, 0); gtk_widget_show(scrwin); store = gtk_list_store_new(1, G_TYPE_STRING); view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); key_pref_win.tree_view = view; gtk_container_add(GTK_CONTAINER(scrwin), view); gtk_widget_show(view); gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE); renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes(_("Key preference"), renderer, "text", 0, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(view), column); selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view)); gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE); g_signal_connect (G_OBJECT(selection), "changed", G_CALLBACK(key_pref_selection_changed), key_entry); vbox = gtk_vbox_new(TRUE, 0); gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 4); gtk_widget_show(vbox); #if 0 button = gtk_button_new_from_stock(GTK_STOCK_GO_UP); gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 2); gtk_widget_show(button); button = gtk_button_new_from_stock(GTK_STOCK_GO_DOWN); gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 2); gtk_widget_show(button); #endif button = gtk_button_new_from_stock(GTK_STOCK_ADD); key_pref_win.add_button = button; gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 2); g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(key_pref_add_button_clicked_cb), key_entry); gtk_widget_set_sensitive(button, FALSE); gtk_widget_show(button); button = gtk_button_new_from_stock(GTK_STOCK_REMOVE); key_pref_win.remove_button = button; gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 2); g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(key_pref_remove_button_clicked_cb), key_entry); gtk_widget_set_sensitive(button, FALSE); gtk_widget_show(button); hbox = gtk_hbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox, FALSE, FALSE, 0); gtk_container_set_border_width(GTK_CONTAINER(hbox), 4); gtk_widget_show(hbox); label = gtk_label_new(_("Key:")); gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 4); gtk_widget_show(label); entry = gtk_entry_new(); /* XXX hack alert! This modifies private part of gtk_entry */ im_context = gtk_im_context_simple_new(); g_object_unref(GTK_ENTRY(entry)->im_context); GTK_ENTRY(entry)->im_context = im_context; gtk_entry_set_editable(GTK_ENTRY(entry), FALSE); key_pref_win.keycode_entry = entry; gtk_widget_set_size_request(GTK_WIDGET(entry), 100, -1); gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 4); g_signal_connect(G_OBJECT(entry), "changed", G_CALLBACK(key_val_entry_changed_cb), key_entry); g_signal_connect(G_OBJECT(entry), "key-press-event", G_CALLBACK(key_choose_entry_key_press_cb), key_entry); g_signal_connect(G_OBJECT(entry), "key-release-event", G_CALLBACK(key_choose_entry_key_release_cb), key_entry); gtk_widget_show(entry); button = gtk_button_new_with_label(_("Grab...")); gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 4); g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(choose_key_button_clicked_cb), key_entry); gtk_widget_show(button); /* set value */ if (custom->value->as_key) { for (key = custom->value->as_key[0], i = 0; key; key = custom->value->as_key[++i]) { GtkTreeIter iter; gtk_list_store_append(store, &iter); gtk_list_store_set(store, &iter, 0, key->literal, -1); } } else { /* error message */ } /* show dialog */ gtk_widget_show(dialog); /* clean */ g_object_unref(G_OBJECT(store)); uim_custom_free(custom); } static void update_custom_type_key_cb(void *ptr, const char *custom_sym) { sync_value_key(GTK_ENTRY(ptr)); } static void add_custom_type_key(GtkWidget *vbox, struct uim_custom *custom) { GtkWidget *hbox; GtkWidget *label, *entry, *button; hbox = gtk_hbox_new(FALSE, 8); label = gtk_label_new(custom->label); gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0); entry = gtk_entry_new(); gtk_entry_set_editable(GTK_ENTRY(entry), FALSE); g_object_set_data_full(G_OBJECT(entry), OBJECT_DATA_UIM_CUSTOM_SYM, g_strdup(custom->symbol), (GDestroyNotify) g_free); gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0); sync_value_key(GTK_ENTRY(entry)); button = gtk_button_new_with_label(_("Edit...")); gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0); g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(choose_key_clicked_cb), entry); uim_custom_cb_add(custom->symbol, entry, update_custom_type_key_cb); gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); } void uim_pref_gtk_add_custom(GtkWidget *vbox, const char *custom_sym) { struct uim_custom *custom; custom = uim_custom_get(custom_sym); if (custom) { /* Should be rewritten with table. */ switch (custom->type) { case UCustom_Bool: add_custom_type_bool(vbox, custom); break; case UCustom_Int: add_custom_type_integer(vbox, custom); break; case UCustom_Str: add_custom_type_string(vbox, custom); break; case UCustom_Pathname: add_custom_type_pathname(vbox, custom); break; case UCustom_Choice: add_custom_type_choice(vbox, custom); break; case UCustom_OrderedList: add_custom_type_orderedlist(vbox, custom); break; case UCustom_Key: add_custom_type_key(vbox, custom); break; default: g_printerr("Invalid custom type: %d\n", custom->type); break; } } else { g_printerr("Failed to get uim_custom object for %s.\n", custom_sym); } uim_custom_free(custom); } void uim_pref_gtk_set_default_value(GtkWidget *widget) { const char *custom_sym; struct uim_custom *custom; union uim_custom_value *value, *defval; uim_bool rv; gint i, num; int type; custom_sym = g_object_get_data(G_OBJECT(widget), OBJECT_DATA_UIM_CUSTOM_SYM); if (!custom_sym) return; custom = uim_custom_get(custom_sym); g_return_if_fail(custom); value = custom->value; defval = custom->default_value; type = custom->type; switch (type) { case UCustom_Bool: value->as_bool = defval->as_bool; break; case UCustom_Int: value->as_int = defval->as_int; break; case UCustom_Str: free(value->as_str); value->as_str = strdup(defval->as_str); break; case UCustom_Pathname: free(value->as_pathname->str); value->as_pathname->str = strdup(defval->as_pathname->str); value->as_pathname->type = defval->as_pathname->type; break; case UCustom_Choice: free(value->as_choice->symbol); free(value->as_choice->label); free(value->as_choice->desc); value->as_choice->symbol = strdup(defval->as_choice->symbol); value->as_choice->label = strdup(defval->as_choice->label); value->as_choice->desc = strdup(defval->as_choice->desc); break; case UCustom_OrderedList: for (num = 0; defval->as_olist[num]; num++); for (i = 0; value->as_olist[i]; i++) { free(value->as_olist[i]->symbol); free(value->as_olist[i]->label); free(value->as_olist[i]->desc); free(value->as_olist[i]); } value->as_olist = realloc(value->as_olist, sizeof(struct uim_custom_choice *) * (num + 1)); for (i = 0; i < num; i++) { value->as_olist[i] = malloc(sizeof(struct uim_custom_choice)); value->as_olist[i]->symbol = strdup(defval->as_olist[i]->symbol); value->as_olist[i]->label = strdup(defval->as_olist[i]->label); value->as_olist[i]->desc = strdup(defval->as_olist[i]->desc); } value->as_olist[num] = NULL; break; case UCustom_Key: for (num = 0; defval->as_key[num]; num++); for (i = 0; value->as_key[i]; i++) { free(value->as_key[i]->literal); free(value->as_key[i]->label); free(value->as_key[i]->desc); free(value->as_key[i]); } value->as_key = realloc(value->as_key, sizeof(struct uim_custom_key *) * (num + 1)); for (i = 0; i < num; i++) { value->as_key[i] = malloc(sizeof(struct uim_custom_key)); *value->as_key[i] = *defval->as_key[i]; value->as_key[i]->literal = strdup(defval->as_key[i]->literal); value->as_key[i]->label = strdup(defval->as_key[i]->label); value->as_key[i]->desc = strdup(defval->as_key[i]->desc); } value->as_key[num] = NULL; break; default: uim_custom_free(custom); return; } rv = uim_custom_set(custom); if (rv == UIM_FALSE) { g_printerr("Failed to set value for \"%s\".\n", custom->symbol); uim_custom_free(custom); return; } uim_custom_free(custom); switch (type) { case UCustom_Bool: sync_value_bool(GTK_CHECK_BUTTON(widget)); break; case UCustom_Int: sync_value_int(GTK_SPIN_BUTTON(widget)); break; case UCustom_Str: sync_value_string(GTK_ENTRY(widget)); break; case UCustom_Pathname: sync_value_string(GTK_ENTRY(widget)); break; case UCustom_Choice: sync_value_choice(GTK_COMBO_BOX(widget)); break; case UCustom_OrderedList: sync_value_olist(GTK_ENTRY(widget)); break; case UCustom_Key: sync_value_key(GTK_ENTRY(widget)); break; default: break; } return; }