From: handa Date: Mon, 7 May 2007 04:45:54 +0000 (+0000) Subject: *** empty log message *** X-Git-Tag: REL-0-9-0~15 X-Git-Url: http://git.chise.org/gitweb/?p=m17n%2Fm17n-im-config.git;a=commitdiff_plain;h=5542797686d55e7c59d8e939ac6af69e19a8952b *** empty log message *** --- diff --git a/src/Makefile.am b/src/Makefile.am index 9fd504d..a31079a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -3,7 +3,7 @@ AM_CPPFLAGS = -DGETTEXTDIR=\"@GETTEXTDIR@\" lib_LTLIBRARIES = libm17n-im-config.la -libm17n_im_config_la_SOURCES = m17n-im-config.h mim-config.c variable.c command.c +libm17n_im_config_la_SOURCES = m17n-im-config.h mim-config.c libm17n_im_config_la_LIBADD = @GTK2_LIBS@ @M17NLIB_LIBS@ include_HEADERS=m17n-im-config.h diff --git a/src/mim-config.c b/src/mim-config.c index b8a7d20..84feb29 100644 --- a/src/mim-config.c +++ b/src/mim-config.c @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include #include "m17n-im-config.h" @@ -34,7 +36,7 @@ #define CONFIG_CALLBACK_DATA " config-callback-data" #define CONFIG_STATUS_DATA " config-status-data" -#define CONFIG_TREE_VIEW " config-tree-view" +#define CONFIG_TREE_VIEW " config-tree-view" typedef void (*MimConfigCallbackFunc) (GtkWidget *widget, gpointer data); @@ -162,61 +164,7 @@ tree_expanded_cb (GtkTreeView *tree, GtkTreeIter *parent, } } -extern GtkWidget *create_variable_list (MSymbol lang, MSymbol name); -extern GtkWidget *create_command_list (MSymbol lang, MSymbol name); - -static void -edit_im (GtkTreeView *tree, MSymbol lang, MSymbol name) -{ - GtkWidget *dialog, *notebook, *scrolled, *vbox, *label; - gint response; - MPlist *plist; - - dialog = (gtk_dialog_new_with_buttons - (name == Mnil ? "global" : msymbol_name (name), - GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (tree))), - GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR, - GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, - NULL)); - gtk_widget_set_size_request (dialog, 500, 300); - - vbox = gtk_vbox_new (FALSE, 0); - gtk_container_set_border_width (GTK_CONTAINER(vbox), 10); - gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), vbox); - - notebook = gtk_notebook_new (); - gtk_container_add (GTK_CONTAINER (vbox), notebook); - - /* Variables' page */ - scrolled = gtk_scrolled_window_new (NULL, NULL); - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled), - GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); - label = gtk_label_new_with_mnemonic (_("_Variables")); - gtk_notebook_append_page (GTK_NOTEBOOK (notebook), scrolled, label); - vbox = gtk_vbox_new (FALSE, 0); - gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrolled), - vbox); - gtk_box_pack_start (GTK_BOX (vbox), - create_variable_list (lang, name), - FALSE, FALSE, 0); - - /* Commands' pages */ - scrolled = gtk_scrolled_window_new (NULL, NULL); - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled), - GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); - label = gtk_label_new_with_mnemonic (_("Co_mmands")); - gtk_notebook_append_page (GTK_NOTEBOOK (notebook), scrolled, label); - vbox = gtk_vbox_new (FALSE, 0); - gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrolled), - vbox); - gtk_box_pack_start (GTK_BOX (vbox), - create_command_list (lang, name), - FALSE, FALSE, 0); - - gtk_widget_show_all (dialog); - gtk_dialog_run (GTK_DIALOG (dialog)); - gtk_widget_destroy (dialog); -} +static void config_im (GtkTreeView *tree, MSymbol lang, MSymbol name); static void update_child_row (GtkTreeModel *model, GtkTreeIter *iter, @@ -281,7 +229,7 @@ tree_activated_cb (GtkTreeView *tree, GtkTreePath *path, enum MimStatus old, new; old = get_mim_status (lang, name); - edit_im (tree, lang, name); + config_im (tree, lang, name); new = get_mim_status (lang, name); if (old != new) update_child_row (model, &iter, new, NULL, tree); @@ -362,7 +310,7 @@ make_store_for_input_methods () imtable[i].lang = msymbol_name (language); else /* `~' is for putting this element at the tail by sort. */ - imtable[i].lang = "~other"; + imtable[i].lang = _("~other"); imtable[i].name = msymbol_name (tag[2]); imtable[i].symlang = tag[1]; imtable[i].symname = tag[2]; @@ -398,7 +346,7 @@ make_store_for_input_methods () && fmt != MTEXT_FORMAT_UTF_8) native = 0; } - if (native) + if (0 && native) { name = alloca (strlen (lang) + nbytes + 4); sprintf (name, "%s (%s)", lang, native); @@ -477,6 +425,1126 @@ static void destroy_cb (GtkWidget *widget, gpointer data) { M17N_FINI (); + initialized = 0; +} + + +/****************************************************/ +/* Configuration of a specific variable or command. */ +/****************************************************/ + +/* Common staffs to variable and command */ + +struct ConfigControl +{ + /* Data type name ("Value" or "Key bindings"). */ + gchar *data_type_name; + MSymbol lang, name, item; + /* Fill in widgets in DIALOG for configuring a specific variable or + command. */ + void (*setup_dialog) (GtkWidget *dialog, struct ConfigControl *control); + /* Update the contents of DATA widget. */ + void (*update_data) (struct ConfigControl *control); + /* Convert PLIST to string. PLIST is a variable value or command + key sequeneses. */ + GString *(*data_string) (MPlist *plist); + /* minput_get_variable or minput_get_command. */ + MPlist *(*get) (MSymbol, MSymbol, MSymbol); + /* minput_config_variable or minput_config_command. */ + int (*config) (MSymbol, MSymbol, MSymbol, MPlist *); + /* If non-NULL, a function to call before finishing a dialog. */ + int (*config_on_ok) (struct ConfigControl *control); + + /* Widget showing the current data (value or key bindings) */ + GtkWidget *data; + + /* Button widget to configure the data to the default. */ + GtkWidget *default_; + + /* Button widget to cancel the configuration. */ + GtkWidget *revert; + + /* Label widget showing the current status. */ + GtkWidget *status; +}; + +struct CommandControl +{ + struct ConfigControl control; + GtkWidget *entry; + GtkWidget *clear; + GtkWidget *add; + GtkWidget *delete; +}; + +enum WidgetType + { + ENTRY_WIDGET, + COMBO_BOX_WIDGET, + SPIN_BUTTON_WIDGET + }; + +struct VariableControl +{ + struct ConfigControl control; + + /* type of current variable: Minteger, Msymbol, or Mtext */ + MSymbol vtype; + + /* type of widget */ + enum WidgetType wtype; +}; + +#define CONFIG_CONTROL(control) ((struct ConfigControl *) (control)) +#define COMMAND_CONTROL(control) ((struct CommandControl *) (control)) +#define VARIABLE_CONTROL(control) ((struct VariableControl *) (control)) + +#define CURRENT_DATA \ + (mplist_next \ + (mplist_next \ + (mplist_next \ + (mplist_value \ + (control->get (control->lang, control->name, control->item)))))) + +#define CURRENT_STATUS \ + (mplist_value \ + (mplist_next \ + (mplist_next \ + (mplist_value \ + (control->get (control->lang, control->name, control->item)))))) + +#define CURRENT_DESCRIPTION \ + (mtext_data \ + (mplist_value \ + (mplist_next \ + (mplist_value \ + (control->get (control->lang, control->name, control->item)))), \ + NULL, NULL, NULL, NULL)) + +#define CONFIG_DATA(plist) \ + control->config (control->lang, control->name, control->item, \ + (plist)) + +static void +update_widgets (struct ConfigControl *control) +{ + MSymbol status = CURRENT_STATUS; + + if (status == Mconfigured) + { + gtk_widget_set_sensitive (control->default_, TRUE); + gtk_widget_set_sensitive (control->revert, TRUE); + gtk_label_set_text (GTK_LABEL (control->status), + mim_status_str[MIM_STATUS_MODIFIED]); + } + else if (status == Mcustomized) + { + gtk_widget_set_sensitive (control->default_, TRUE); + gtk_widget_set_sensitive (control->revert, FALSE); + gtk_label_set_text (GTK_LABEL (control->status), + mim_status_str[MIM_STATUS_CUSTOMIZED]); + } + else + { + gtk_widget_set_sensitive (control->default_, FALSE); + gtk_widget_set_sensitive (control->revert, FALSE); + gtk_label_set_text (GTK_LABEL (control->status), + mim_status_str[MIM_STATUS_DEFAULT]); + } +} + +static void +help_cb (GtkButton *button, gpointer data) +{ + struct ConfigControl *control = data; + GtkWidget *msg; + + msg = gtk_message_dialog_new (GTK_WINDOW + (gtk_widget_get_toplevel (GTK_WIDGET (button))), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_INFO, + GTK_BUTTONS_CLOSE, + CURRENT_DESCRIPTION); + gtk_dialog_run (GTK_DIALOG (msg)); + gtk_widget_destroy (msg); +} + +static void +default_cb (GtkButton *button, gpointer data) +{ + MPlist *empty = mplist (); + struct ConfigControl *control = data; + + CONFIG_DATA (empty); + m17n_object_unref (empty); + control->update_data (control); + update_widgets (control); + control->config_on_ok = NULL; +} + +static void +revert_cb (GtkButton *button, gpointer data) +{ + struct ConfigControl *control = data; + + CONFIG_DATA (NULL); + control->update_data (control); + update_widgets (control); + control->config_on_ok = NULL; +} + +static void +ok_cb (GtkButton *button, gpointer data) +{ + struct ConfigControl *control = data; + + if (control->config_on_ok) + { + if (! control->config_on_ok (control)) + { + revert_cb (NULL, control); + return; + } + control->config_on_ok = NULL; + } + gtk_dialog_response (GTK_DIALOG + (gtk_widget_get_toplevel (GTK_WIDGET (button))), + GTK_RESPONSE_OK); +} + +enum + { + /* Variable or command name */ + CONFIG_COL_ITEM, + /* Status (default, modified, or customized). */ + CONFIG_COL_STATUS, + /* Variable value or command key bindings. */ + CONFIG_COL_DATA, + /* Number of columns of list store. */ + NUM_CONFIG_COLS + }; + + +static void +set_list_element (GtkListStore *store, GtkTreeIter *iter, + struct ConfigControl *control, MPlist *plist) +{ + MSymbol status; + gchar *status_str; + + if (! plist) + plist = mplist_value (control->get (control->lang, control->name, + control->item)); + plist = mplist_next (mplist_next (plist)); + + status = mplist_value (plist); + if (status == Mconfigured) + status_str = mim_status_str[MIM_STATUS_MODIFIED]; + else if (status == Mcustomized) + status_str = mim_status_str[MIM_STATUS_CUSTOMIZED]; + else + status_str = mim_status_str[MIM_STATUS_DEFAULT]; + plist = mplist_next (plist); + gtk_list_store_set (store, iter, + CONFIG_COL_ITEM, msymbol_name (control->item), + CONFIG_COL_STATUS, status_str, + CONFIG_COL_DATA, control->data_string (plist)->str, + -1); +} + +/* Called when an item (command or variable) name is activated. + Create a dialog widget to config that itme. */ + +static void +item_activated_cb (GtkTreeView *parent, GtkTreePath *path, + GtkTreeViewColumn *col, gpointer data) +{ + GtkTreeModel *model; + GtkTreeIter iter; + GtkWidget *dialog, *label, *help, *hbox, *ok; + gchar *item; + struct ConfigControl *control = CONFIG_CONTROL (data); + + model = gtk_tree_view_get_model (parent); + if (! gtk_tree_model_get_iter (model, &iter, path)) + return; + gtk_tree_model_get (model, &iter, CONFIG_COL_ITEM, &item, -1); + control->item = msymbol (item); + + dialog = gtk_dialog_new (); + gtk_window_set_title (GTK_WINDOW (dialog), msymbol_name (control->item)); + gtk_window_set_transient_for + (GTK_WINDOW (dialog), + GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (parent)))); + gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE); + gtk_button_box_set_layout (GTK_BUTTON_BOX (GTK_DIALOG (dialog)->action_area), + GTK_BUTTONBOX_EDGE); + + hbox = gtk_hbox_new (FALSE, 12); + gtk_container_set_border_width (GTK_CONTAINER (hbox), 12); + label = gtk_label_new (_("Status")); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + label = gtk_label_new (": "); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + control->status = gtk_label_new (NULL); + gtk_box_pack_start (GTK_BOX (hbox), control->status, FALSE, FALSE, 0); + gtk_box_pack_end (GTK_BOX (GTK_DIALOG (dialog)->vbox), + hbox, FALSE, FALSE, 0); + + help = gtk_button_new_from_stock (GTK_STOCK_HELP); + g_signal_connect (G_OBJECT (help), "clicked", + G_CALLBACK (help_cb), control); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area), + help, FALSE, FALSE, 0); + ok = gtk_button_new_from_stock (GTK_STOCK_OK); + g_signal_connect (G_OBJECT (ok), "clicked", + G_CALLBACK (ok_cb), control); + gtk_box_pack_end (GTK_BOX (GTK_DIALOG (dialog)->action_area), + ok, FALSE, FALSE, 0); + + control->setup_dialog (dialog, control); + + update_widgets (control); + gtk_widget_show_all (dialog); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_tree_model_get_iter (model, &iter, path); + set_list_element (GTK_LIST_STORE (model), &iter, control, NULL); + gtk_widget_destroy (dialog); + /*m17n_object_unref (entry_keyseq);*/ +} + + +/* Create a list view widget listing variable or command names with + their current status and data. */ + +GtkWidget * +create_item_list (MSymbol lang, MSymbol name, struct ConfigControl *control) +{ + GtkListStore *store; + GtkWidget *view; + MPlist *plist; + + plist = control->get (lang, name, Mnil); + /* plist == ((command/variable description status data ...) ...) */ + if (! plist) + return gtk_label_new (_("No customizable item.")); + store = gtk_list_store_new (NUM_CONFIG_COLS, + G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); + for (; plist && mplist_key (plist) == Mplist; plist = mplist_next (plist)) + { + GtkTreeIter iter; + MPlist *pl; + + pl = mplist_value (plist); + /* pl == (command/variable description status data ...) */ + control->item = mplist_value (pl); + gtk_list_store_append (store, &iter); + set_list_element (store, &iter, control, pl); + } + + view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store)); + g_object_unref (G_OBJECT (store)); + gtk_tree_view_insert_column_with_attributes + (GTK_TREE_VIEW (view), -1, _("Name"), gtk_cell_renderer_text_new (), + "text", CONFIG_COL_ITEM, NULL); + gtk_tree_view_insert_column_with_attributes + (GTK_TREE_VIEW (view), -1, _("Status"), gtk_cell_renderer_text_new (), + "text", CONFIG_COL_STATUS, NULL); + gtk_tree_view_insert_column_with_attributes + (GTK_TREE_VIEW (view), -1, control->data_type_name, + gtk_cell_renderer_text_new (), + "text", CONFIG_COL_DATA, NULL); + g_signal_connect (G_OBJECT (view), "row-activated", + G_CALLBACK (item_activated_cb), control); + + return view; +} + +static struct VariableControl var; +static struct CommandControl cmd; + +static void +config_im (GtkTreeView *tree, MSymbol lang, MSymbol name) +{ + GtkWidget *dialog, *notebook, *scrolled, *vbox, *label; + + var.control.lang = cmd.control.lang = lang; + var.control.name = cmd.control.name = name; + var.control.config_on_ok = cmd.control.config_on_ok = NULL; + + dialog = (gtk_dialog_new_with_buttons + (name == Mnil ? "global" : msymbol_name (name), + GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (tree))), + GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR, + GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, + NULL)); + gtk_widget_set_size_request (dialog, 500, 300); + + vbox = gtk_vbox_new (FALSE, 0); + gtk_container_set_border_width (GTK_CONTAINER(vbox), 10); + gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), vbox); + + notebook = gtk_notebook_new (); + gtk_container_add (GTK_CONTAINER (vbox), notebook); + + /* Variables' page */ + scrolled = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + label = gtk_label_new_with_mnemonic (_("_Variables")); + gtk_notebook_append_page (GTK_NOTEBOOK (notebook), scrolled, label); + vbox = gtk_vbox_new (FALSE, 0); + gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrolled), + vbox); + gtk_box_pack_start (GTK_BOX (vbox), + create_item_list (lang, name, CONFIG_CONTROL (&var)), + FALSE, FALSE, 0); + + /* Commands' pages */ + scrolled = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + label = gtk_label_new_with_mnemonic (_("Co_mmands")); + gtk_notebook_append_page (GTK_NOTEBOOK (notebook), scrolled, label); + vbox = gtk_vbox_new (FALSE, 0); + gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrolled), + vbox); + gtk_box_pack_start (GTK_BOX (vbox), + create_item_list (lang, name, CONFIG_CONTROL (&cmd)), + FALSE, FALSE, 0); + + gtk_widget_show_all (dialog); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); +} + + +/* Staffs for variable configuration. */ + +void +variable_update_data (struct ConfigControl *control) +{ + MPlist *plist; + MSymbol key; + void *value; + + plist = CURRENT_DATA; + /* plist == (value [valid-value ...]) */ + key = mplist_key (plist); + value = mplist_value (plist); + + if (VARIABLE_CONTROL (control)->wtype == ENTRY_WIDGET) + { + if (key == Msymbol) + gtk_entry_set_text (GTK_ENTRY (control->data), + msymbol_name ((MSymbol) value)); + else if (key == Mtext) + /* Fixme : Assuming the return value is in UTF-8 */ + gtk_entry_set_text (GTK_ENTRY (control->data), + mtext_data ((MText *) value, + NULL, NULL, NULL, NULL)); + else /* key == Minteger */ + { + gchar buf[32]; + g_snprintf (buf, sizeof (buf), "%d", (gint) value); + gtk_entry_set_text (GTK_ENTRY (control->data), buf); + } + } + else if (VARIABLE_CONTROL (control)->wtype == COMBO_BOX_WIDGET) + { + gint i; + + for (i = 0, plist = mplist_next (plist); + plist && mplist_key (plist) == key; + i++, plist = mplist_next (plist)) + if (mplist_value (plist) == value) + break; + gtk_combo_box_set_active (GTK_COMBO_BOX (control->data), i); + } + else /* ci->wtype == SPIN_BUTTON_WIDGET */ + gtk_spin_button_set_value (GTK_SPIN_BUTTON (control->data), + (gdouble) (int) value); +} + +static gboolean +config_with_entry (struct ConfigControl *control) +{ + const gchar *text = gtk_entry_get_text (GTK_ENTRY (control->data)); + MPlist *plist = mplist (); + gboolean ret = TRUE; + + if (VARIABLE_CONTROL (control)->vtype == Msymbol) + { + mplist_add (plist, Msymbol, msymbol (text)); + CONFIG_DATA (plist); + } + else if (VARIABLE_CONTROL (control)->vtype == Mtext) + { + MText *mt; + + mt = mconv_decode_buffer (Mcoding_utf_8, (guchar *) text, strlen (text)); + mplist_add (plist, Mtext, mt); + CONFIG_DATA (plist); + m17n_object_unref (mt); + } + else /* VARIABLE_CONTROL (control)->vtype == Minteger */ + { + int i; + + if (sscanf (text, "%d", &i) == 1) + { + mplist_add (plist, Minteger, (void *) i); + CONFIG_DATA (plist); + } + else + { + GtkWidget *msg; + + msg = gtk_message_dialog_new (GTK_WINDOW + (gtk_widget_get_toplevel (control->data)), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + _("The value must be an integer.")); + gtk_dialog_run (GTK_DIALOG (msg)); + gtk_widget_destroy (msg); + ret = FALSE; + } + } + + m17n_object_unref (plist); + return ret; +} + +static gboolean +config_with_combo (struct ConfigControl *control) +{ + gchar *text = gtk_combo_box_get_active_text (GTK_COMBO_BOX (control->data)); + MPlist *plist = mplist (); + + if (VARIABLE_CONTROL (control)->vtype == Msymbol) + { + mplist_add (plist, Msymbol, msymbol (text)); + CONFIG_DATA (plist); + } + else if (VARIABLE_CONTROL (control)->vtype == Mtext) + { + MText *mt; + + mt = mconv_decode_buffer (Mcoding_utf_8, (guchar *) text, strlen (text)); + mplist_add (plist, Mtext, mt); + CONFIG_DATA (plist); + m17n_object_unref (mt); + } + else /* VARIABLE_CONTROL (control)->vtype == Minteger */ + { + int i; + + sscanf (text, "%d", &i); + mplist_add (plist, Minteger, (void *) i); + CONFIG_DATA (plist); + } + m17n_object_unref (plist); + return TRUE; +} + +static gboolean +config_with_spin (struct ConfigControl *control) +{ + gint i = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (control->data)); + MPlist *plist = mplist (); + + mplist_add (plist, Minteger, (void *) i); + CONFIG_DATA (plist); + m17n_object_unref (plist); + return TRUE; +} + +static void +changed_cb (GtkEntry *entry, gpointer data) +{ + struct ConfigControl *control = data; + + gtk_widget_set_sensitive (control->default_, TRUE); + gtk_widget_set_sensitive (control->revert, TRUE); + gtk_label_set_text (GTK_LABEL (control->status), _("modified")); + if (VARIABLE_CONTROL (control)->wtype == ENTRY_WIDGET) + control->config_on_ok = config_with_entry; + else if (VARIABLE_CONTROL (control)->wtype == COMBO_BOX_WIDGET) + control->config_on_ok = config_with_combo; + else + control->config_on_ok = config_with_spin; +} + +static GString * +variable_data_string (MPlist *plist) +{ + static GString *str; + + if (! str) + str = g_string_sized_new (80); + else + g_string_truncate (str, 0); + + if (mplist_key (plist) == Msymbol) + g_string_append (str, msymbol_name ((MSymbol) mplist_value (plist))); + else if (mplist_key (plist) == Mtext) + /* Fixme : Assuming the return value is in UTF-8 */ + g_string_append (str, mtext_data ((MText *) mplist_value (plist), + NULL, NULL, NULL, NULL)); + else /* mplist_key (plist) == Minteger */ + g_string_append_printf (str, "%d", (gint) mplist_value (plist)); + return str; +} + +static void +variable_setup_dialog (GtkWidget *dialog, struct ConfigControl *control) +{ + MPlist *plist; + void *value; + GtkWidget *hbox; + + plist = CURRENT_DATA; + VARIABLE_CONTROL (control)->vtype = mplist_key (plist); + value = mplist_value (plist); + plist = mplist_next (plist); + + if (VARIABLE_CONTROL (control)->vtype == Msymbol) + { + if (mplist_key (plist) == Msymbol) + { + gint i, nth; + + control->data = gtk_combo_box_new_text (); + g_signal_connect (GTK_OBJECT (control->data), "changed", + G_CALLBACK (changed_cb), control); + VARIABLE_CONTROL (control)->wtype = COMBO_BOX_WIDGET; + for (i = 0; mplist_key (plist) == Msymbol; + plist = mplist_next (plist), i++) + { + if (mplist_value (plist) == value) + nth = i; + gtk_combo_box_append_text + (GTK_COMBO_BOX (control->data), + msymbol_name ((MSymbol) mplist_value (plist))); + } + gtk_combo_box_set_active (GTK_COMBO_BOX (control->data), nth); + } + else + { + control->data = gtk_entry_new (); + g_signal_connect (GTK_OBJECT (control->data), "changed", + G_CALLBACK (changed_cb), control); + g_signal_connect (GTK_OBJECT (control->data), "activate", + G_CALLBACK (ok_cb), control); + VARIABLE_CONTROL (control)->wtype = ENTRY_WIDGET; + gtk_entry_set_text (GTK_ENTRY (control->data), msymbol_name (value)); + gtk_editable_set_editable (GTK_EDITABLE (control->data), TRUE); + } + } + else if (VARIABLE_CONTROL (control)->vtype == Mtext) + { + if (plist && mplist_key (plist) == Mtext) + { + gint i, nth; + + control->data = gtk_combo_box_new_text (); + VARIABLE_CONTROL (control)->wtype = COMBO_BOX_WIDGET; + for (i = 0; plist && mplist_key (plist) == Mtext; + plist = mplist_next (plist), i++) + { + if (! mtext_cmp ((MText *) mplist_value (plist), + (MText *) value)) + nth = i; + /* Fixme : Assuming the return value is in UTF-8 */ + gtk_combo_box_append_text + (GTK_COMBO_BOX (control->data), + mtext_data ((MText *) mplist_value (plist), + NULL, NULL, NULL, NULL)); + } + gtk_combo_box_set_active (GTK_COMBO_BOX (control->data), nth); + g_signal_connect (GTK_OBJECT (control->data), "changed", + G_CALLBACK (changed_cb), control); + } + else + { + control->data = gtk_entry_new (); + VARIABLE_CONTROL (control)->wtype = ENTRY_WIDGET; + /* Fixme : Assuming the return value is in UTF-8 */ + gtk_entry_set_text (GTK_ENTRY (control->data), + mtext_data (value, NULL, NULL, NULL, NULL)); + gtk_editable_set_editable (GTK_EDITABLE (control->data), TRUE); + g_signal_connect (GTK_OBJECT (control->data), "changed", + G_CALLBACK (changed_cb), control); + g_signal_connect (GTK_OBJECT (control->data), "activate", + G_CALLBACK (ok_cb), control); + } + } + else /* control->vtype == Minteger */ + { + if (plist && mplist_key (plist) == Minteger) + { + gint i, nth; + + control->data = gtk_combo_box_new_text (); + VARIABLE_CONTROL (control)->wtype = COMBO_BOX_WIDGET; + for (i = 0; plist && mplist_key (plist) == Minteger; + plist = mplist_next (plist), i++) + { + gchar buf[32]; + + if (mplist_value (plist) == value) + nth = i; + g_snprintf (buf, sizeof (buf), "%d", + (gint) mplist_value (plist)); + gtk_combo_box_append_text (GTK_COMBO_BOX (control->data), buf); + } + gtk_combo_box_set_active (GTK_COMBO_BOX (control->data), nth); + g_signal_connect (GTK_OBJECT (control->data), "changed", + G_CALLBACK (changed_cb), control); + } + else if (plist && mplist_key (plist) == Mplist) + { + GtkObject *adj; + gdouble lower, upper; + + plist = mplist_value (plist); + lower = (gdouble) (int) mplist_value (plist); + upper = (gdouble) (int) mplist_value (mplist_next (plist)); + adj = gtk_adjustment_new ((gdouble) (int) value, lower, upper, + 1.0, 10.0, 0); + control->data = gtk_spin_button_new (GTK_ADJUSTMENT (adj), 0, 0); + VARIABLE_CONTROL (control)->wtype = SPIN_BUTTON_WIDGET; + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (control->data), TRUE); + gtk_spin_button_set_update_policy (GTK_SPIN_BUTTON (control->data), + GTK_UPDATE_ALWAYS); + g_signal_connect (GTK_OBJECT (control->data), "changed", + G_CALLBACK (changed_cb), control); + } + else + { + gchar buf[32]; + + control->data = gtk_entry_new (); + VARIABLE_CONTROL (control)->wtype = ENTRY_WIDGET; + g_snprintf (buf, sizeof (buf), "%d", (gint) value); + gtk_entry_set_text (GTK_ENTRY (control->data), buf); + gtk_editable_set_editable (GTK_EDITABLE (control->data), TRUE); + g_signal_connect (GTK_OBJECT (control->data), "changed", + G_CALLBACK (changed_cb), control); + g_signal_connect (GTK_OBJECT (control->data), "activate", + G_CALLBACK (ok_cb), control); + } + } + + control->default_ = gtk_button_new_from_stock (_("_Default")); + g_signal_connect (G_OBJECT (control->default_), "clicked", + G_CALLBACK (default_cb), control); + + control->revert = gtk_button_new_from_stock (GTK_STOCK_REVERT_TO_SAVED); + g_signal_connect (G_OBJECT (control->revert), "clicked", + G_CALLBACK (revert_cb), control); + + hbox = gtk_hbutton_box_new (); + gtk_box_set_spacing (GTK_BOX (hbox), 6); + gtk_container_add (GTK_CONTAINER (hbox), control->default_); + gtk_container_add (GTK_CONTAINER (hbox), control->revert); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), + control->data, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), + hbox, FALSE, FALSE, 0); +} + + +/* Staffs for command configuration. */ + +static void +selection_cb (GtkTreeSelection *selection, gpointer data) +{ + gtk_widget_set_sensitive + (COMMAND_CONTROL (data)->delete, + gtk_tree_selection_count_selected_rows (selection) ? TRUE : FALSE); +} + +static void +delete_cb (GtkButton *button, gpointer data) +{ + GtkTreeSelection *selection; + GtkTreeModel *model; + GtkTreeIter iter; + MPlist *pl, *new; + struct ConfigControl *control = data; + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (control->data)); + model = gtk_tree_view_get_model (GTK_TREE_VIEW (control->data)); + + if (! gtk_tree_model_get_iter_first (model, &iter)) + return; + + new = mplist (); + for (pl = CURRENT_DATA; mplist_key (pl) != Mnil; pl = mplist_next (pl)) + { + if (! gtk_tree_selection_iter_is_selected (selection, &iter)) + mplist_add (new, Mplist, mplist_value (pl)); + gtk_tree_model_iter_next (model, &iter); + } + CONFIG_DATA (new); + m17n_object_unref (new); + control->update_data (control); + update_widgets (control); +} + +static GtkWidget * +create_deleting_section (struct ConfigControl *control) +{ + struct CommandControl *cmd_control = COMMAND_CONTROL (control); + GtkListStore *store; + GtkWidget *label, *scrolled, *hbox, *vbox; + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + GtkTreeSelection *selection; + + label = gtk_label_new (_("Current key bindings:")); + + store = gtk_list_store_new (1, G_TYPE_STRING); + control->data = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store)); + g_object_unref (G_OBJECT (store)); + gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (control->data), FALSE); + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (control->data)); + gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE); + g_signal_connect (G_OBJECT (selection), "changed", + G_CALLBACK (selection_cb), control); + + scrolled = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrolled), + control->data); + + column = gtk_tree_view_column_new (); + gtk_tree_view_append_column (GTK_TREE_VIEW (control->data), column); + renderer = gtk_cell_renderer_text_new (); + gtk_tree_view_column_pack_start (column, renderer, TRUE); + gtk_tree_view_column_set_attributes (column, renderer, "text", 0, NULL); + + control->update_data (control); + + control->default_ = gtk_button_new_from_stock (_("_Default")); + g_signal_connect (G_OBJECT (control->default_), "clicked", + G_CALLBACK (default_cb), control); + + control->revert = gtk_button_new_from_stock (GTK_STOCK_REVERT_TO_SAVED); + g_signal_connect (G_OBJECT (control->revert), "clicked", + G_CALLBACK (revert_cb), control); + + cmd_control->delete = gtk_button_new_from_stock (GTK_STOCK_DELETE); + gtk_widget_set_sensitive (cmd_control->delete, FALSE); + g_signal_connect (G_OBJECT (cmd_control->delete), "clicked", + G_CALLBACK (delete_cb), control); + + vbox = gtk_vbox_new (FALSE, 12); + gtk_container_set_border_width (GTK_CONTAINER (vbox), 12); + + hbox = gtk_hbox_new (FALSE, 6); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 6); + gtk_container_add (GTK_CONTAINER (vbox), hbox); + + gtk_container_add (GTK_CONTAINER (vbox), scrolled); + + hbox = gtk_hbutton_box_new (); + gtk_button_box_set_layout (GTK_BUTTON_BOX (hbox), GTK_BUTTONBOX_END); + gtk_box_set_spacing (GTK_BOX (hbox), 6); + gtk_container_add (GTK_CONTAINER (hbox), control->default_); + gtk_container_add (GTK_CONTAINER (hbox), control->revert); + gtk_container_add (GTK_CONTAINER (hbox), cmd_control->delete); + gtk_container_add (GTK_CONTAINER (vbox), hbox); + + return vbox; +} + +static unsigned modifier_state = 0; +static MPlist *entry_keyseq; + +enum KeyMaskBit { + META_MASK_BIT = 1, + ALT_MASK_BIT = META_MASK_BIT << 1, + SUPER_MASK_BIT = ALT_MASK_BIT << 1, + HYPER_MASK_BIT = SUPER_MASK_BIT << 1 +}; + +static void +update_entry (GtkEntry *entry) +{ + if (mplist_key (entry_keyseq) == Mnil) + gtk_entry_set_text (entry, ""); + else + { + MPlist *p; + gchar *name; + + name = msymbol_name ((MSymbol) mplist_value (entry_keyseq)); + gtk_entry_set_text (entry, name); + for (p = mplist_next (entry_keyseq); mplist_key (p) != Mnil; + p = mplist_next (p)) + { + name = msymbol_name ((MSymbol) mplist_value (p)); + gtk_entry_append_text (entry, " "); + gtk_entry_append_text (entry, name); + } + gtk_editable_set_position (GTK_EDITABLE (entry), -1); + } +} + +static gboolean +key_pressed_cb (GtkEntry *entry, GdkEventKey *event, gpointer data) +{ + guint c; + MText *mt; + char buf[32]; + char *name; + int nbytes, i; + struct CommandControl *cmd_control = data; + + c = gdk_keyval_to_unicode (event->keyval); + if (c == 0) + { + switch (event->keyval) + { + case GDK_Meta_L: case GDK_Meta_R: + modifier_state |= META_MASK_BIT; return TRUE; + case GDK_Alt_L: case GDK_Alt_R: + modifier_state |= ALT_MASK_BIT; return TRUE; + case GDK_Super_L: case GDK_Super_R: + modifier_state |= SUPER_MASK_BIT; return TRUE; + case GDK_Hyper_L: case GDK_Hyper_R: + modifier_state |= HYPER_MASK_BIT; return TRUE; + default: + if (event->keyval >= GDK_Shift_L && event->keyval <= GDK_Shift_Lock) + return TRUE; + } + name = gdk_keyval_name (event->keyval); + if (! name) + return TRUE; + nbytes = strlen (name); + } + else + { + name = alloca (8); + mt = mtext (); + mtext_cat_char (mt, c); + nbytes = mconv_encode_buffer (msymbol ("utf-8"), mt, + (unsigned char *) name, 32); + m17n_object_unref (mt); + } + i = 0; + if (c == 0 && event->state & GDK_SHIFT_MASK) + buf[i++] = 'S', buf[i++] = '-'; + if (event->state & GDK_CONTROL_MASK) + buf[i++] = 'C', buf[i++] = '-'; + if (modifier_state & META_MASK_BIT) + buf[i++] = 'M', buf[i++] = '-'; + if (modifier_state & ALT_MASK_BIT) + buf[i++] = 'A', buf[i++] = '-'; + if (modifier_state & SUPER_MASK_BIT) + buf[i++] = 's', buf[i++] = '-'; + if (modifier_state & HYPER_MASK_BIT) + buf[i++] = 'H', buf[i++] = '-'; + strncpy (buf + i, name, nbytes); + buf[i + nbytes] = 0; + mplist_add (entry_keyseq, Msymbol, msymbol (buf)); + update_entry (entry); + gtk_widget_set_sensitive (cmd_control->clear, TRUE); + gtk_widget_set_sensitive (cmd_control->add, TRUE); + return TRUE; +} + +static gboolean +key_released_cb (GtkEntry *entry, GdkEventKey *event, gpointer data) +{ + guint c; + + c = gdk_keyval_to_unicode (event->keyval); + if (c == 0) + { + switch (event->keyval) + { + case GDK_Meta_L: case GDK_Meta_R: + modifier_state &= ~META_MASK_BIT; break; + case GDK_Alt_L: case GDK_Alt_R: + modifier_state &= ~ALT_MASK_BIT; break; + case GDK_Super_L: case GDK_Super_R: + modifier_state &= ~SUPER_MASK_BIT; break; + case GDK_Hyper_L: case GDK_Hyper_R: + modifier_state &= ~HYPER_MASK_BIT; break; + } + } + return FALSE; +} + +static void +clear_cb (GtkButton *button, gpointer data) +{ + struct CommandControl *cmd_control = data; + + mplist_set (entry_keyseq, Mnil, NULL); + gtk_widget_grab_focus (cmd_control->entry); + update_entry (GTK_ENTRY (cmd_control->entry)); + gtk_widget_set_sensitive (cmd_control->clear, FALSE); + gtk_widget_set_sensitive (cmd_control->add, FALSE); +} + +static void +add_cb (GtkButton *button, gpointer data) +{ + MPlist *new; + GtkTreeModel *model; + GtkTreeIter iter; + struct ConfigControl *control = data; + + if (mplist_length (entry_keyseq) == 0) + return; + model = gtk_tree_view_get_model (GTK_TREE_VIEW (control->data)); + if (gtk_tree_model_get_iter_first (model, &iter)) + { + gchar *keyseq = control->data_string (entry_keyseq)->str; + gchar *str; + + do { + gtk_tree_model_get (model, &iter, 0, &str, -1); + if (strcmp (keyseq, str) == 0) + /* entry_keyseq is already registered. */ + return; + } while (gtk_tree_model_iter_next (model, &iter)); + } + new = mplist_copy (CURRENT_DATA); + mplist_add (new, Mplist, entry_keyseq); + CONFIG_DATA (new); + m17n_object_unref (new); + control->update_data (control); + update_widgets (control); +} + +static GtkWidget * +create_adding_section (struct ConfigControl *control) +{ + struct CommandControl *cmd_control = COMMAND_CONTROL (control); + GtkWidget *label, *hbox, *vbox; + + label = gtk_label_new (_("New key binding:")); + + entry_keyseq = mplist (); + cmd_control->entry = gtk_entry_new (); + g_signal_connect (G_OBJECT (cmd_control->entry), "key-press-event", + G_CALLBACK (key_pressed_cb), cmd_control); + g_signal_connect (G_OBJECT (cmd_control->entry), "key-release-event", + G_CALLBACK (key_released_cb), cmd_control); + + cmd_control->clear = gtk_button_new_from_stock (GTK_STOCK_CLEAR); + gtk_widget_set_sensitive (cmd_control->clear, FALSE); + g_signal_connect (G_OBJECT (cmd_control->clear), "clicked", + G_CALLBACK (clear_cb), cmd_control); + + cmd_control->add = gtk_button_new_from_stock (GTK_STOCK_ADD); + gtk_widget_set_sensitive (cmd_control->add, FALSE); + g_signal_connect (G_OBJECT (cmd_control->add), "clicked", + G_CALLBACK (add_cb), cmd_control); + + vbox = gtk_vbox_new (FALSE, 12); + gtk_container_set_border_width (GTK_CONTAINER (vbox), 12); + + hbox = gtk_hbox_new (FALSE, 6); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 6); + gtk_container_add (GTK_CONTAINER (vbox), hbox); + + gtk_container_add (GTK_CONTAINER (vbox), cmd_control->entry); + + hbox = gtk_hbutton_box_new (); + gtk_button_box_set_layout (GTK_BUTTON_BOX (hbox), GTK_BUTTONBOX_END); + gtk_box_set_spacing (GTK_BOX (hbox), 6); + gtk_container_add (GTK_CONTAINER (hbox), cmd_control->clear); + gtk_container_add (GTK_CONTAINER (hbox), cmd_control->add); + gtk_container_add (GTK_CONTAINER (vbox), hbox); + + return vbox; +} + +static void +append_key_sequence (GString *str, MPlist *keyseq) +{ + static MSymbol space_symbol; + MPlist *p; + + if (! space_symbol) + space_symbol = msymbol (" "); + + for (p = keyseq ; mplist_key (p) != Mnil; p = mplist_next (p)) + { + MSymbol key = (MSymbol) mplist_value (p); + + if (p != keyseq) + g_string_append_c (str, ' '); + if (key == space_symbol) + g_string_append (str, "Space"); + else + g_string_append (str, msymbol_name (key)); + } +} + +static GString * +command_data_string (MPlist *plist) +{ + static GString *str; + + if (! str) + str = g_string_sized_new (80); + else + g_string_truncate (str, 0); + + if (mplist_key (plist) == Mplist) + { + MPlist *pl; + + /* PLIST == ((KEY KEY ...) ...) */ + for (pl = plist; mplist_key (pl) != Mnil; pl = mplist_next (pl)) + { + if (pl != plist) + g_string_append (str, ", "); + append_key_sequence (str, mplist_value (pl)); + } + } + else + { + /* PLIST == (KEY KEY ...) */ + append_key_sequence (str, plist); + } + return str; +} + +static void +command_update_data (struct ConfigControl *control) +{ + GtkTreeView *tree = GTK_TREE_VIEW (control->data); + GtkListStore *store = GTK_LIST_STORE (gtk_tree_view_get_model (tree)); + GtkTreeIter iter; + MPlist *pl; + + gtk_list_store_clear (store); + for (pl = CURRENT_DATA; mplist_key (pl) != Mnil; pl = mplist_next (pl)) + { + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, + 0, control->data_string (mplist_value (pl))->str, + -1); + } +} + +static void +command_setup_dialog (GtkWidget *dialog, struct ConfigControl *control) +{ + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), + create_deleting_section (control), FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), + create_adding_section (control), FALSE, FALSE, 0); } @@ -496,6 +1564,8 @@ mim_config_new (GCallback func, gpointer data) if (merror_code < 0) return NULL; + initialized = 1; + #if ENABLE_NLS bindtextdomain ("m17n-im-config", GETTEXTDIR); bind_textdomain_codeset ("m17n-im-config", "UTF-8"); @@ -506,6 +1576,20 @@ mim_config_new (GCallback func, gpointer data) mim_status_str[MIM_STATUS_MODIFIED] = _("modified"); mim_status_str[MIM_STATUS_NO] = _("uncustomizable"); + var.control.data_type_name = _("Value"); + var.control.setup_dialog = variable_setup_dialog; + var.control.update_data = variable_update_data; + var.control.data_string = variable_data_string; + var.control.get = minput_get_variable; + var.control.config = minput_config_variable; + + cmd.control.data_type_name = _("Key Bindings"); + cmd.control.setup_dialog = command_setup_dialog; + cmd.control.update_data = command_update_data; + cmd.control.data_string = command_data_string; + cmd.control.get = minput_get_command; + cmd.control.config = minput_config_command; + store = make_store_for_input_methods (); tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store)); g_object_unref (G_OBJECT (store));