*** empty log message ***
authorhanda <handa>
Mon, 7 May 2007 04:45:54 +0000 (04:45 +0000)
committerhanda <handa>
Mon, 7 May 2007 04:45:54 +0000 (04:45 +0000)
src/Makefile.am
src/mim-config.c

index 9fd504d..a31079a 100644 (file)
@@ -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
index b8a7d20..84feb29 100644 (file)
@@ -27,6 +27,8 @@
 #include <m17n.h>
 #include <m17n-misc.h>
 #include <gtk/gtk.h>
+#include <gdk/gdk.h>
+#include <gdk/gdkkeysyms.h>
 #include <config.h>
 #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;
+}
+
+\f
+/****************************************************/
+/* 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);
+}
+
+\f
+/* 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);
+}
+
+\f
+/* 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);
 }
 
 \f
@@ -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));