X-Git-Url: http://git.chise.org/gitweb/?a=blobdiff_plain;f=src%2Fmim-config.c;h=d1729243c5c6945aeea520faf5a73ad8f7e3f9c7;hb=aec9bb02f0534e6666eadd54c8f07c892a57ee73;hp=ff2dc311bba9cc3173a7281df9314f6d1a854793;hpb=01986d7ad11cbafb20ea2d6882a8f7155a5b91a9;p=m17n%2Fm17n-im-config.git diff --git a/src/mim-config.c b/src/mim-config.c index ff2dc31..d172924 100644 --- a/src/mim-config.c +++ b/src/mim-config.c @@ -1,8 +1,35 @@ +/* mim-config.c -- M17N input method configuration + Copyright (C) 2007 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + + This file is part of the m17n-im-config package; a sub-part of the + m17n library. + + The m17n library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The m17n library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the m17n library; if not, write to the Free + Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + 02111-1307, USA. */ + #include #include #include +#include #include +#include #include +#include +#include #include #include "m17n-im-config.h" @@ -10,7 +37,16 @@ #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); + +typedef struct _MimConfigCallback +{ + GtkWidget *widget; + MimConfigCallbackFunc func; + gpointer data; +} MimConfigCallback; typedef struct _MimConfigStatus { @@ -45,12 +81,14 @@ get_mim_status (MSymbol lang, MSymbol name) MPlist *p = mplist_value (plist); MSymbol status_symbol; - status = MIM_STATUS_DEFAULT; p = mplist_next (mplist_next (p)); status_symbol = mplist_value (p); - if (status_symbol != Mnil && status_symbol != Minherited) - return (status_symbol == Mcustomized - ? MIM_STATUS_CUSTOMIZED : MIM_STATUS_MODIFIED); + if (status_symbol == Mconfigured) + return MIM_STATUS_MODIFIED; + if (status_symbol == Mcustomized) + status = MIM_STATUS_CUSTOMIZED; + else if (status == MIM_STATUS_NO) + status = MIM_STATUS_DEFAULT; } for (plist = minput_get_command (lang, name, Mnil); plist && mplist_key (plist) != Mnil; plist = mplist_next (plist)) @@ -58,12 +96,14 @@ get_mim_status (MSymbol lang, MSymbol name) MPlist *p = mplist_value (plist); MSymbol status_symbol; - status = MIM_STATUS_DEFAULT; p = mplist_next (mplist_next (p)); status_symbol = mplist_value (p); - if (status_symbol != Mnil && status_symbol != Minherited) - return (status_symbol == Mcustomized - ? MIM_STATUS_CUSTOMIZED : MIM_STATUS_MODIFIED); + if (status_symbol == Mconfigured) + return MIM_STATUS_MODIFIED; + if (status_symbol == Mcustomized) + status = MIM_STATUS_CUSTOMIZED; + else if (status == MIM_STATUS_NO) + status = MIM_STATUS_DEFAULT; } return status; } @@ -125,63 +165,7 @@ tree_expanded_cb (GtkTreeView *tree, GtkTreeIter *parent, } } -static void -edit_im (GtkTreeView *tree, MSymbol lang, MSymbol name) -{ - GtkWidget *dialog, *label; - gint response; - - dialog = (gtk_dialog_new_with_buttons - (_("Edit"), - GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (tree))), - GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR, - _("Default"), 0, - GTK_STOCK_REVERT_TO_SAVED, GTK_RESPONSE_NO, - GTK_STOCK_EDIT, GTK_RESPONSE_YES, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - NULL)); - label = gtk_label_new (msymbol_name (name)); - gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), label); - gtk_widget_show_all (dialog); - response = gtk_dialog_run (GTK_DIALOG (dialog)); - if (response != GTK_RESPONSE_CANCEL - && lang == Mt && (name == Mnil || name == msymbol ("unicode"))) - { - MSymbol variable = msymbol (name == Mnil ? "candidates-group-size" - : "prompt"); - - if (response == GTK_RESPONSE_NO) - { - minput_config_variable (lang, name, variable, NULL); - } - else if (response == 0) - { - MPlist *plist = mplist (); - - minput_config_variable (lang, name, variable, plist); - m17n_object_unref (plist); - } - else - { - MPlist *plist = mplist (); - - if (name == Mnil) - { - mplist_add (plist, Minteger, (void *) 3); - } - else - { - MText *mt = mtext_from_data (">>", 2, MTEXT_FORMAT_US_ASCII); - mplist_add (plist, Mtext, mt); - m17n_object_unref (mt); - } - minput_config_variable (lang, name, variable, plist); - m17n_object_unref (plist); - } - } - - gtk_widget_destroy (dialog); -} +static void config_im (GtkTreeView *tree, MSymbol lang, MSymbol name); static void update_child_row (GtkTreeModel *model, GtkTreeIter *iter, @@ -223,8 +207,7 @@ update_child_row (GtkTreeModel *model, GtkTreeIter *iter, callback = g_object_get_data (G_OBJECT (tree), CONFIG_CALLBACK_DATA); if (callback) - callback->func (config_status->num_modified == 0 ? FALSE : TRUE, - callback->arg); + callback->func (callback->widget, callback->data); } } @@ -247,7 +230,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); @@ -263,15 +246,10 @@ tree_activated_cb (GtkTreeView *tree, GtkTreePath *path, } } -static gboolean -config_deleted_cb (GtkWidget *widget, GdkEvent *event, gpointer data) -{ - M17N_FINI (); - return FALSE; -} - typedef struct _MimTable { + int lang_in_locale; + gchar *encoded_lang; gchar *lang; gchar *name; MSymbol symlang; @@ -283,7 +261,23 @@ sort_im (const void *p1, const void *p2) { const MimTable *t1 = p1; const MimTable *t2 = p2; - int result = strcmp (t1->lang, t2->lang); + int result; + + if (t1->symlang == t2->symlang) + result = 0; + else + { + if (t1->lang_in_locale != t2->lang_in_locale) + return (t1->lang_in_locale ? -1 : 1); + if ((! t1->encoded_lang) != (! t2->encoded_lang)) + return (t1->encoded_lang ? -1 : 1); + if (t1->encoded_lang) + result = strcoll (t1->encoded_lang, t2->encoded_lang); + else if ((! t1->lang) != (! t2->lang)) + return (t1->lang ? -1 : 1); + else + result = strcmp (t1->lang, t2->lang); + } return (result ? result : strcmp (t1->name, t2->name)); } @@ -295,11 +289,17 @@ make_store_for_input_methods () MPlist *imlist, *p; int i; MimTable *imtable; - char *lang; + char *lang, *other = _("Other"); GtkTreeIter iter1, iter2; enum MimStatus status; MimConfigStatus *config_status; + unsigned char conv_buf[256]; + MConverter *converter; + int locale_is_utf8 = 0; + static MSymbol Meng; + if (! Meng) + Meng = msymbol ("eng"); store = gtk_tree_store_new (NUM_COLS, G_TYPE_STRING, /* COL_TAG */ G_TYPE_STRING, /* COL_STATUS_STR */ @@ -322,73 +322,100 @@ make_store_for_input_methods () imlist = mdatabase_list (msymbol ("input-method"), Mnil, Mnil, Mnil); config_status->num_im = mplist_length (imlist); imtable = g_newa (MimTable, config_status->num_im); + + { + MLocale *locale = mlocale_set (LC_MESSAGES, NULL); + MSymbol coding = locale ? mlocale_get_prop (locale, Mcoding) : Mnil; + + if (coding == Mnil) + converter = NULL; + else if (coding == msymbol ("utf-8")) + { + converter = NULL; + locale_is_utf8 = 1; + } + else + { + converter = mconv_buffer_converter (coding, conv_buf, sizeof conv_buf); + if (converter) + converter->last_block = 1; + } + } + for (i = 0, p = imlist; mplist_key (p) != Mnil; p = mplist_next (p)) { MDatabase *mdb = (MDatabase *) mplist_value (p); MSymbol *tag = mdatabase_tag (mdb); + MPlist *pl; + + if (tag[1] == Mnil || tag[2] == Mnil) + continue; - if (tag[1] != Mnil && tag[2] != Mnil) + imtable[i].symlang = tag[1]; + imtable[i].symname = tag[2]; + imtable[i].name = msymbol_name (tag[2]); + imtable[i].lang = imtable[i].encoded_lang = NULL; + imtable[i].lang_in_locale = 0; + + if (tag[1] != Mt) { - MSymbol language = mlanguage_name (tag[1]); + pl = mlanguage_name_list (tag[1], Mnil, Mnil, Mnil); + if (pl) + imtable[i].lang_in_locale = 1; + else + pl = mlanguage_name_list (tag[1], Meng, Mnil, Mnil); + if (pl) + { + MText *mt = mplist_value (pl); + int nbytes; - if (language != Mnil) - imtable[i].lang = msymbol_name (language); + if (converter) + { + mconv_reset_converter (converter); + nbytes = mconv_encode (converter, mt); + if (converter->result == MCONVERSION_RESULT_SUCCESS) + { + imtable[i].encoded_lang = alloca (nbytes + 1); + if (imtable[i].encoded_lang) + { + memcpy (imtable[i].encoded_lang, conv_buf, nbytes); + imtable[i].encoded_lang[nbytes] = '\0'; + } + } + } + else if (locale_is_utf8) + imtable[i].encoded_lang + = mtext_data (mt, NULL, NULL, NULL, NULL); + imtable[i].lang = mtext_data (mt, NULL, NULL, NULL, NULL); + } else - /* `~' is for putting this element at the tail by sort. */ - imtable[i].lang = "~other"; - imtable[i].name = msymbol_name (tag[2]); - imtable[i].symlang = tag[1]; - imtable[i].symname = tag[2]; - i++; + imtable[i].lang = msymbol_name (tag[1]); } + i++; } + if (converter) + mconv_free_converter (converter); m17n_object_unref (imlist); config_status->num_im = i; qsort (imtable, config_status->num_im, sizeof (MimTable), sort_im); for (lang = NULL, i = 0; i < config_status->num_im; i++) { - if (lang != imtable[i].lang) - { - gchar *name; + gchar *langname = imtable[i].lang; + if (! langname) + langname = other; + if (lang != langname) + { gtk_tree_store_append (store, &iter1, NULL); - lang = imtable[i].lang; - if (lang[0] != '~') - { - MText *native_text; - gchar *native = NULL; - int nbytes; - - if (imtable[i].symlang != Mt - && (native_text = mlanguage_text (imtable[i].symlang))) - { - enum MTextFormat fmt; - - native = mtext_data (native_text, &fmt, &nbytes, - NULL, NULL); - if (fmt != MTEXT_FORMAT_US_ASCII - && fmt != MTEXT_FORMAT_UTF_8) - native = 0; - } - if (native) - { - name = alloca (strlen (lang) + nbytes + 4); - sprintf (name, "%s (%s)", lang, native); - } - else - name = lang; - } - else - name = lang + 1; - gtk_tree_store_set (store, &iter1, - COL_TAG, name, + COL_TAG, langname, COL_STATUS_STR, NULL, COL_STATUS, 0, COL_LANG, Mnil, COL_NAME, Mnil, -1); + lang = langname; } gtk_tree_store_append (store, &iter2, &iter1); gtk_tree_store_set (store, &iter2, @@ -444,18 +471,1160 @@ set_as_saved (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, return FALSE; } +static int initialized = 0; + +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. */ + gboolean (*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 MPlist *entry_keyseq; + +static void +update_status (struct ConfigControl *control) +{ + MSymbol status = CURRENT_STATUS; + + if (status == Mconfigured) + { + gtk_label_set_text (GTK_LABEL (control->status), + mim_status_str[MIM_STATUS_MODIFIED]); + gtk_widget_set_sensitive (control->default_, TRUE); + gtk_widget_set_sensitive (control->revert, TRUE); + } + else if (status == Mcustomized) + { + gtk_label_set_text (GTK_LABEL (control->status), + mim_status_str[MIM_STATUS_CUSTOMIZED]); + gtk_widget_set_sensitive (control->default_, TRUE); + gtk_widget_set_sensitive (control->revert, FALSE); + } + else + { + gtk_label_set_text (GTK_LABEL (control->status), + mim_status_str[MIM_STATUS_DEFAULT]); + gtk_widget_set_sensitive (control->default_, FALSE); + gtk_widget_set_sensitive (control->revert, FALSE); + } +} + +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_status (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_status (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; + } + if (control->config == minput_config_command) + m17n_object_unref (entry_keyseq); + 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_with_buttons + (msymbol_name (control->item), + GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (parent))), + GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR, + NULL)); + gtk_button_box_set_layout (GTK_BUTTON_BOX (GTK_DIALOG (dialog)->action_area), + GTK_BUTTONBOX_EDGE); + gtk_container_set_border_width (GTK_CONTAINER (dialog), 5); + + hbox = gtk_hbox_new (FALSE, 12); + gtk_container_set_border_width (GTK_CONTAINER (hbox), 5); + 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_status (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); +} + + +/* 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); + gtk_container_set_border_width (GTK_CONTAINER (dialog), 5); + + vbox = gtk_vbox_new (FALSE, 0); + gtk_container_set_border_width (GTK_CONTAINER(vbox), 5); + 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, *vbox; + + 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 (); + 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); + 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; + gtk_entry_set_text (GTK_ENTRY (control->data), msymbol_name (value)); + 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 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); + vbox = gtk_vbox_new (FALSE, 12); + gtk_container_set_border_width (GTK_CONTAINER (vbox), 5); + gtk_box_pack_start (GTK_BOX (vbox), control->data, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), + vbox, FALSE, FALSE, 0); + gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 6); +} + + +/* 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_status (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), 5); + + hbox = gtk_hbox_new (FALSE, 6); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + 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; + +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_status (control); + clear_cb (NULL, 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), 5); + + hbox = gtk_hbox_new (FALSE, 6); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + 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); + gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 6); +} + /* Public API */ GtkWidget * -mim_config_widget (MimConfigCallback *callback) +mim_config_new (GCallback func, gpointer data) { GtkWidget *tree, *config; GtkTreeStore *store; GtkCellRenderer *renderer; GtkTreeViewColumn *column; + if (initialized) + return NULL; M17N_INIT (); + if (merror_code < 0) + return NULL; + + initialized = 1; #if ENABLE_NLS bindtextdomain ("m17n-im-config", GETTEXTDIR); @@ -467,11 +1636,23 @@ mim_config_widget (MimConfigCallback *callback) 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)); - if (callback) - g_object_set_data (G_OBJECT (tree), CONFIG_CALLBACK_DATA, callback); renderer = gtk_cell_renderer_text_new (); column = (gtk_tree_view_column_new_with_attributes @@ -480,7 +1661,7 @@ mim_config_widget (MimConfigCallback *callback) renderer = gtk_cell_renderer_text_new (); column = (gtk_tree_view_column_new_with_attributes - (_("status"), renderer, "text", COL_STATUS_STR, NULL)); + (_("Status"), renderer, "text", COL_STATUS_STR, NULL)); gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column); g_signal_connect (G_OBJECT (tree), "row-expanded", @@ -493,9 +1674,20 @@ mim_config_widget (MimConfigCallback *callback) GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (config), tree); g_signal_connect (G_OBJECT (config), "destroy", - G_CALLBACK (config_deleted_cb), NULL); + G_CALLBACK (destroy_cb), NULL); g_object_set_data (G_OBJECT (config), CONFIG_TREE_VIEW, tree); + if (func) + { + MimConfigCallback *callback; + + callback = g_new (MimConfigCallback, 1); + callback->widget = config; + callback->func = (MimConfigCallbackFunc) func; + callback->data = data; + g_object_set_data_full (G_OBJECT (tree), CONFIG_CALLBACK_DATA, + callback, g_free); + } return config; } @@ -548,3 +1740,12 @@ mim_config_save (GtkWidget *config) gtk_tree_model_foreach (model, set_as_saved, config_status); return TRUE; } + +GtkTreeView * +mim_config_get_tree_view (GtkWidget *config) +{ + GtkTreeView *tree; + + tree = g_object_get_data (G_OBJECT (config), CONFIG_TREE_VIEW); + return tree; +}