*** empty log message ***
[m17n/m17n-im-config.git] / src / variable.c
index b8475da..313744c 100644 (file)
-#include <gtk/gtk.h>
-#include <m17n.h>
 #include <stdio.h>
 #include <string.h>
+#include <libintl.h>
+#include <gtk/gtk.h>
+#include <m17n.h>
+#include <config.h>
+
+#define _(String) dgettext (PACKAGE, String)
+
+#define CURRENT_VALUE          \
+  (mplist_next                 \
+   (mplist_next                        \
+    (mplist_next               \
+     (mplist_value             \
+      (minput_get_variable     \
+       (current_lang, current_name, current_variable))))))
+
+#define CURRENT_STATUS         \
+  (mplist_value                        \
+   (mplist_next                        \
+    (mplist_next               \
+     (mplist_value             \
+      (minput_get_variable     \
+       (current_lang, current_name, current_variable))))))
+
+#define CURRENT_DESCRIPTION                                    \
+  (mtext_data                                                  \
+   (mplist_value                                               \
+    (mplist_next                                               \
+     (mplist_value                                             \
+      (minput_get_variable                                     \
+       (current_lang, current_name, current_variable)))),      \
+    NULL,  NULL,  NULL,  NULL))
+
+#define CONFIG_VARIABLE(plist)                                         \
+  minput_config_variable (current_lang, current_name, current_variable,        \
+                         (plist))
+
+enum WidgetType
+  {
+    ENTRY_WIDGET,
+    COMBO_BOX_WIDGET,
+    SPIN_BUTTON_WIDGET
+  };
+
+struct ControllerInfo
+{
+  /* type of current variable: Minteger, Msymbol, or Mtext */
+  MSymbol vtype;
+
+  /* widget showing and controlling current variable */
+  GtkWidget *widget;
+
+  /* type of widget */
+  enum WidgetType wtype;
+
+  /* whether minput_config_variable () should be called on ok button */
+  gboolean need_config;
+
+  /* default button */
+  GtkWidget *default_;
+
+  /* revert button */
+  GtkWidget *revert;
+
+  /* status label */
+  GtkWidget *status;
+
+  /* dialog itself */
+  GtkWidget *dialog;
+};
+
+static MSymbol current_lang, current_name, current_variable;
 
 static void
-entry_cb (GtkWidget *entry, gpointer data)
+update_widget (struct ControllerInfo *ci)
 {
-  const gchar *text = gtk_entry_get_text (GTK_ENTRY (entry));
-  MSymbol lang, name, variable, key;
-  MPlist *plist = mplist ();
+  MPlist *plist;
+  MSymbol key, status;
+  void *value;
+
+  plist = CURRENT_VALUE;
+  /* plist == (value [valid-value ...]) */
+  key = mplist_key (plist);
+  value = mplist_value (plist);
+
+  if (ci->wtype == ENTRY_WIDGET)
+    {
+      if (key == Msymbol)
+       gtk_entry_set_text (GTK_ENTRY (ci->widget),
+                           msymbol_name ((MSymbol) value));
+      else if (key == Mtext)           
+       /* Fixme : Assuming the return value is in UTF-8 */
+       gtk_entry_set_text (GTK_ENTRY (ci->widget),
+                           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 (ci->widget), buf);
+       }
+    }
+  else if (ci->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 (ci->widget), i);
+    }
+  else                         /* ci->wtype == SPIN_BUTTON_WIDGET */
+    gtk_spin_button_set_value (GTK_SPIN_BUTTON (ci->widget),
+                              (gdouble) (int) value);
+}
+
+static void *
+default_cb (GtkButton *button, gpointer data)
+{
+  MPlist *empty = mplist ();
+  struct ControllerInfo *ci = data;
+
+  CONFIG_VARIABLE (empty);
+  m17n_object_unref (empty);
+  update_widget (ci);
+  gtk_widget_set_sensitive (ci->default_, FALSE);
+  if (CURRENT_STATUS == Mconfigured)
+    {
+      gtk_widget_set_sensitive (ci->revert, TRUE);
+      gtk_label_set_text (GTK_LABEL (ci->status), _("modified"));
+    }
+  else
+    {
+      gtk_widget_set_sensitive (ci->revert, FALSE);
+      gtk_label_set_text (GTK_LABEL (ci->status), _("default"));
+    }
+  ci->need_config = FALSE;
+}
+
+static void *
+revert_cb (GtkButton *button, gpointer data)
+{
+  struct ControllerInfo *ci = data;
+
+  CONFIG_VARIABLE (NULL);
+  update_widget (ci);
+  gtk_widget_set_sensitive (ci->revert, FALSE);
+  if (CURRENT_STATUS == Mnil)
+    {
+      gtk_widget_set_sensitive (ci->default_, FALSE);
+      gtk_label_set_text (GTK_LABEL (ci->status), _("default"));
+    }
+  else
+    {
+      gtk_widget_set_sensitive (ci->default_, TRUE);
+      gtk_label_set_text (GTK_LABEL (ci->status), _("customized"));
+    }
+  ci->need_config = FALSE;
+}
 
-  lang = g_object_get_data (G_OBJECT (entry), "mim_lang");
-  name = g_object_get_data (G_OBJECT (entry), "mim_name");
-  variable = g_object_get_data (G_OBJECT (entry), "mim_variable");
-  key = g_object_get_data (G_OBJECT (entry), "mim_key");
+static gboolean
+config_with_entry (struct ControllerInfo *ci)
+{
+  const gchar *text = gtk_entry_get_text (GTK_ENTRY (ci->widget));
+  MPlist *plist = mplist ();
+  gboolean ret = TRUE;
 
-  if (key == Msymbol)
+  if (ci->vtype == Msymbol)
     {
       mplist_add (plist, Msymbol, msymbol (text));
-      minput_config_variable (lang, name, variable, plist);
+      CONFIG_VARIABLE (plist);
     }
-  else if (key == Mtext)
+  else if (ci->vtype == Mtext)
     {
       MText *mt;
 
-      mt = mtext_from_data (text, strlen (text), MTEXT_FORMAT_UTF_8);
+      mt = mconv_decode_buffer (Mcoding_utf_8, text, strlen (text));
       mplist_add (plist, Mtext, mt);
-      minput_config_variable (lang, name, variable, plist);
+      CONFIG_VARIABLE (plist);
       m17n_object_unref (mt);
     }
-  else if (key == Minteger)
+  else                         /* ci->vtype == Minteger */
     {
       int i;
       gchar buf[32];
@@ -37,265 +190,493 @@ entry_cb (GtkWidget *entry, gpointer data)
       if (sscanf (text, "%d", &i) == 1)
        {
          mplist_add (plist, Minteger, (void *) i);
-           minput_config_variable (lang, name, variable, plist);
+         CONFIG_VARIABLE (plist);
        }
       else
        {
-         printf ("Invalid value\n");
+         GtkWidget *msg;
+
+         msg = gtk_message_dialog_new (GTK_WINDOW
+                                       (gtk_widget_get_toplevel (ci->dialog)),
+                                       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;
        }
-      sprintf (buf, "%d", (int) mplist_value (plist));
-      gtk_entry_set_text (GTK_ENTRY (entry), buf);
-      gtk_editable_set_position (GTK_EDITABLE (entry), -1);
     }
+
   m17n_object_unref (plist);
+  return ret;
 }
 
-static void
-combo_cb (GtkWidget *combo, gpointer data)
+static gboolean
+config_with_combo (struct ControllerInfo *ci)
 {
-  gchar *text = gtk_combo_box_get_active_text (GTK_COMBO_BOX (combo));
-  MSymbol lang, name, variable, key;
+  gchar *text = gtk_combo_box_get_active_text (GTK_COMBO_BOX (ci->widget));
   MPlist *plist = mplist ();
 
-  lang = g_object_get_data (G_OBJECT (combo), "mim_lang");
-  name = g_object_get_data (G_OBJECT (combo), "mim_name");
-  variable = g_object_get_data (G_OBJECT (combo), "mim_variable");
-  key = g_object_get_data (G_OBJECT (combo), "mim_key");
-
-  if (key == Msymbol)
+  if (ci->vtype == Msymbol)
     {
       mplist_add (plist, Msymbol, msymbol (text));
-      minput_config_variable (lang, name, variable, plist);
+      CONFIG_VARIABLE (plist);
     }
-  else if (key == Mtext)
+  else if (ci->vtype == Mtext)
     {
       MText *mt;
 
-      mt = mtext_from_data (text, strlen (text), MTEXT_FORMAT_UTF_8);
+      mt = mconv_decode_buffer (Mcoding_utf_8, text, strlen (text));
       mplist_add (plist, Mtext, mt);
-      minput_config_variable (lang, name, variable, plist);
+      CONFIG_VARIABLE (plist);
       m17n_object_unref (mt);
     }
-  else if (key == Minteger)
+  else                         /* ci->vtype == Minteger */
     {
       int i;
-      gchar buf[32];
 
-      if (sscanf (text, "%d", &i) == 1)
-       {
-         mplist_add (plist, Minteger, (void *) i);
-         minput_config_variable (lang, name, variable, plist);
-       }
-      else
-       {
-         printf ("Invalid value\n");
-       }
+      sscanf (text, "%d", &i);
+      mplist_add (plist, Minteger, (void *) i);
+      CONFIG_VARIABLE (plist);
     }
   m17n_object_unref (plist);
+  return TRUE;
 }
 
-static void
-spin_cb (GtkWidget *spin, gpointer data)
+static gboolean
+config_with_spin (struct ControllerInfo *ci)
 {
-  MSymbol lang, name, variable;
-  MPlist *plist = mplist ();
+  gint i;
 
-  lang = g_object_get_data (G_OBJECT (spin), "mim_lang");
-  name = g_object_get_data (G_OBJECT (spin), "mim_name");
-  variable = g_object_get_data (G_OBJECT (spin), "mim_variable");
+  i = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (ci->widget));
+  MPlist *plist = mplist ();
 
-  mplist_add
-    (plist, Minteger,
-     (void *) gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (spin)));
-  minput_config_variable (lang, name, variable, plist);
+  mplist_add (plist, Minteger, (void *) i);
+  CONFIG_VARIABLE (plist);
   m17n_object_unref (plist);
+  return TRUE;
 }
 
-void
-create_variable_entries (GtkWidget *frame, GtkTooltips *tip,
-                        MSymbol lang, MSymbol name)
+static void *
+ok_cb (GtkButton *button, gpointer data)
 {
-  GtkWidget *table;
-  MPlist *plist;
-  gint row;
+  struct ControllerInfo *ci = data;
 
-  plist = minput_get_variable (lang, name, Mnil);
-  if (! plist)
+  if (ci->need_config)
     {
-      GtkWidget *message;
+      if (ci->wtype == ENTRY_WIDGET)
+       {
+         if (config_with_entry (ci))
+           gtk_dialog_response (GTK_DIALOG (ci->dialog), GTK_RESPONSE_OK);
+         else
+           revert_cb (NULL, ci);
+       }
+
+      else if (ci->wtype == COMBO_BOX_WIDGET)
+       {
+         config_with_combo (ci); /* always returns true */
+         gtk_dialog_response (GTK_DIALOG (ci->dialog), GTK_RESPONSE_OK);
+       }
 
-      message = gtk_label_new ("No variables for this method");
-      gtk_container_add (GTK_CONTAINER (frame), message);
-      return;
+      else                             /* ci->wtype == SPIN_BUTTON */
+       {
+         config_with_spin (ci); /* always returns true */
+         gtk_dialog_response (GTK_DIALOG (ci->dialog), GTK_RESPONSE_OK);
+       }
     }
+  else
+    gtk_dialog_response (GTK_DIALOG (ci->dialog), GTK_RESPONSE_OK);
+}
 
-  /*
-   * plist == ((variable description status value [valid-value ...])
-   *           (variable description status value [valid-value ...])
-   *           ...)
-   */
-  table = gtk_table_new (2, mplist_length (plist), FALSE);
-  gtk_container_add (GTK_CONTAINER (frame), table);
+static void
+changed_cb (GtkEntry *entry, gpointer data)
+{
+  struct ControllerInfo *ci = data;
+
+  gtk_widget_set_sensitive (ci->default_, TRUE);
+  gtk_widget_set_sensitive (ci->revert, TRUE);
+  gtk_label_set_text (GTK_LABEL (ci->status), _("modified"));
+  ci->need_config = TRUE;
+}
+
+enum
+  {
+    VCOL_VARIABLE,
+    VCOL_VALUE,
+    VCOL_STATUS,
+    NUM_VCOLS
+  };
+
+static void
+set_value_status (GtkListStore *store, GtkTreeIter *iter)
+{
+  MPlist *plist;
+  MSymbol status;
+  gchar *value_str, *status_str, buf[32];
 
-  for (row = 0; plist && mplist_key (plist) == Mplist;
-       row++, plist = mplist_next (plist))
+  status = CURRENT_STATUS;
+  if (status == Mconfigured)
+    status_str = _("modified");
+  else if (status == Mcustomized)
+    status_str = _("customized");
+  else
+    status_str = _("default");
+
+  plist = CURRENT_VALUE;
+  /* plist == (value [valid-value ...]) */
+  if (mplist_key (plist) == Msymbol)
+    value_str = msymbol_name ((MSymbol) mplist_value (plist));
+  else if (mplist_key (plist) == Mtext)
+    /* Fixme : Assuming the return value is in UTF-8 */
+    value_str = mtext_data ((MText *) mplist_value (plist),
+                           NULL, NULL, NULL, NULL);
+  else
     {
-      GtkWidget *label, *widget;
-      MPlist *pl, *data;
-      MSymbol variable, key;
-      void *value;
-      gchar *desc;
+      g_snprintf (buf, sizeof (buf), "%d", (gint) mplist_value (plist));
+      value_str = buf;
+    }
 
-      pl = mplist_value (plist);
-      variable = mplist_value (pl);
-      label = gtk_label_new (msymbol_name (variable));
-      gtk_table_attach_defaults (GTK_TABLE (table), label, 0, 1, row, row + 1);
+  gtk_list_store_set (store, iter,
+                     VCOL_VALUE, value_str,
+                     VCOL_STATUS, status_str,
+                     -1);
+}
 
-      pl = mplist_next (pl);
-      if (mplist_key (pl) == Mtext)
-       /* Fixme : Assuming the return value is in UTF-8 */
-       desc = mtext_data (mplist_value (pl), NULL, NULL, NULL, NULL);
-      else
-       desc = NULL;
+static GtkWidget *
+create_widget (struct ControllerInfo *ci)
+{
+  MPlist *plist;
+  void *value;
 
-      pl = mplist_next (mplist_next (pl)); /* ignore status */
-      key = mplist_key (pl);
-      value = mplist_value (pl);
-      pl = mplist_next (pl);
+  plist = CURRENT_VALUE;
+  /* plist == (value [valid-value ...]) */
+  ci->vtype = mplist_key (plist);
+  value = mplist_value (plist);
+  plist = mplist_next (plist);
 
-      if (key == Msymbol)
+  if (ci->vtype == Msymbol)
+    {
+      if (plist && mplist_key (plist) == Msymbol)
        {
-         if (mplist_key (pl) == Msymbol)
-           {
-             int i, nth = -1;
-
-             widget = gtk_combo_box_new_text ();
-             for (i = 0; pl && mplist_key (pl) == Msymbol;
-                  pl = mplist_next (pl), i++)
-               {
-                 if (mplist_value (pl) == value)
-                   nth = i;
-                 gtk_combo_box_append_text (GTK_COMBO_BOX (widget),
-                                            msymbol_name (mplist_value (pl)));
-               }
-             if (nth != -1)
-               gtk_combo_box_set_active (GTK_COMBO_BOX (widget), nth);
-             g_signal_connect (G_OBJECT (widget), "changed",
-                               G_CALLBACK (combo_cb), NULL);
-           }
-         else
+         gint i, nth;
+
+         ci->widget = gtk_combo_box_new_text ();
+         g_signal_connect (GTK_OBJECT (ci->widget), "changed",
+                           G_CALLBACK (changed_cb), ci);
+         ci->wtype = COMBO_BOX_WIDGET;
+         for (i = 0; plist && mplist_key (plist) == Msymbol;
+              plist = mplist_next (plist), i++)
            {
-             widget = gtk_entry_new ();
-             gtk_entry_set_text (GTK_ENTRY (widget), msymbol_name (value));
-             gtk_editable_set_editable (GTK_EDITABLE (widget), TRUE);
-             g_signal_connect (G_OBJECT (widget), "activate",
-                               G_CALLBACK (entry_cb), NULL);
+             if (mplist_value (plist) == value)
+               nth = i;
+             gtk_combo_box_append_text
+               (GTK_COMBO_BOX (ci->widget),
+                msymbol_name ((MSymbol) mplist_value (plist)));
            }
+         gtk_combo_box_set_active (GTK_COMBO_BOX (ci->widget), nth);
        }
-      
-      else if (key == Mtext)
+      else
        {
-         if (mplist_key (pl) == Mtext)
-           {
-             int i, nth = -1;
-
-             widget = gtk_combo_box_new_text ();
-             for (i = 0; pl && mplist_key (pl) == Mtext;
-                  pl = mplist_next (pl), i++)
-               {
-                 if (! mtext_cmp (mplist_value (pl), value))
-                   nth = i;
-                 /* Fixme : Assuming the return value is in UTF-8 */
-                 gtk_combo_box_append_text
-                   (GTK_COMBO_BOX (widget),
-                    mtext_data (mplist_value (pl), NULL, NULL, NULL, NULL));
-               }
-             if (nth != -1)
-               gtk_combo_box_set_active (GTK_COMBO_BOX (widget), nth);
-             g_signal_connect (G_OBJECT (widget), "changed",
-                               G_CALLBACK (combo_cb), NULL);
-           }
-         else
+         ci->widget = gtk_entry_new ();
+         g_signal_connect (GTK_OBJECT (ci->widget), "changed",
+                           G_CALLBACK (changed_cb), ci);
+         g_signal_connect (GTK_OBJECT (ci->widget), "activate",
+                           G_CALLBACK (ok_cb), ci);
+         ci->wtype = ENTRY_WIDGET;
+         gtk_entry_set_text (GTK_ENTRY (ci->widget), msymbol_name (value));
+         gtk_editable_set_editable (GTK_EDITABLE (ci->widget), TRUE);
+       }
+    }
+  else if (ci->vtype == Mtext)
+    {
+      if (plist && mplist_key (plist) == Mtext)
+       {
+         gint i, nth;
+
+         ci->widget = gtk_combo_box_new_text ();
+         g_signal_connect (GTK_OBJECT (ci->widget), "changed",
+                           G_CALLBACK (changed_cb), ci);
+         ci->wtype = COMBO_BOX_WIDGET;
+         for (i = 0; plist && mplist_key (plist) == Mtext;
+              plist = mplist_next (plist), i++)
            {
-             widget = gtk_entry_new ();
+             if (! mtext_cmp ((MText *) mplist_value (plist),
+                              (MText *) value))
+               nth = i;
              /* Fixme : Assuming the return value is in UTF-8 */
-             gtk_entry_set_text (GTK_ENTRY (widget),
-                                 mtext_data (value, NULL, NULL, NULL, NULL));
-             gtk_editable_set_editable (GTK_EDITABLE (widget), TRUE);
-             g_signal_connect (G_OBJECT (widget), "activate",
-                               G_CALLBACK (entry_cb), NULL);
+             gtk_combo_box_append_text
+               (GTK_COMBO_BOX (ci->widget),
+                mtext_data ((MText *) mplist_value (plist),
+                            NULL, NULL, NULL, NULL));
            }
+         gtk_combo_box_set_active (GTK_COMBO_BOX (ci->widget), nth);
        }
-
-      else if (key == Minteger)
+      else
        {
-         if (mplist_key (pl) == Minteger)
-           {
-             int i, nth = -1;
-
-             widget = gtk_combo_box_new_text ();
-             for (i = 0; pl && mplist_key (pl) == Minteger;
-                  pl = mplist_next (pl), i++)
-               {
-                 gchar buf[32];
-
-                 if (mplist_value (pl) == value)
-                   nth = i;
-                 snprintf (buf, 31, "%d", (int) mplist_value (pl));
-                 gtk_combo_box_append_text (GTK_COMBO_BOX (widget), buf);
-               }
-             if (nth != -1)
-               gtk_combo_box_set_active (GTK_COMBO_BOX (widget), nth);
-             g_signal_connect (G_OBJECT (widget), "changed",
-                               G_CALLBACK (combo_cb), NULL);
-           }
-         else if (mplist_key (pl) == Mplist)
-           {
-             GtkAdjustment *adj;
-             gdouble lower, upper;
-
-             pl = mplist_value (pl);
-             lower = (gdouble) (int) mplist_value (pl);
-             pl = mplist_next (pl);
-             upper = (gdouble) (int) mplist_value (pl);
-             adj = (GtkAdjustment *)
-               gtk_adjustment_new ((gdouble) (int) value, (gdouble) lower,
-                                   (gdouble) upper, 1.0, 10.0, 0);
-             widget = gtk_spin_button_new (adj, 0, 0);
-             gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (widget), TRUE);
-             gtk_spin_button_set_update_policy (GTK_SPIN_BUTTON (widget),
-                                                GTK_UPDATE_IF_VALID);
-             g_signal_connect (G_OBJECT (widget), "value_changed",
-                               G_CALLBACK (spin_cb), NULL);
-           }
-         else
+         ci->widget = gtk_entry_new ();
+         g_signal_connect (GTK_OBJECT (ci->widget), "changed",
+                           G_CALLBACK (changed_cb), ci);
+         g_signal_connect (GTK_OBJECT (ci->widget), "activate",
+                           G_CALLBACK (ok_cb), ci);
+         ci->wtype = ENTRY_WIDGET;
+         /* Fixme : Assuming the return value is in UTF-8 */
+         gtk_entry_set_text (GTK_ENTRY (ci->widget),
+                             mtext_data (value, NULL, NULL, NULL, NULL));
+         gtk_editable_set_editable (GTK_EDITABLE (ci->widget), TRUE);
+       }
+    }
+  else                         /* ci->vtype == Minteger */
+    {
+      if (plist && mplist_key (plist) == Minteger)
+       {
+         gint i, nth;
+
+         ci->widget = gtk_combo_box_new_text ();
+         g_signal_connect (GTK_OBJECT (ci->widget), "changed",
+                           G_CALLBACK (changed_cb), ci);
+         ci->wtype = COMBO_BOX_WIDGET;
+         for (i = 0; plist && mplist_key (plist) == Minteger;
+              plist = mplist_next (plist), i++)
            {
              gchar buf[32];
 
-             widget = gtk_entry_new ();
-             snprintf (buf, 31, "%d", (int) value);
-             gtk_entry_set_text (GTK_ENTRY (widget), buf);
-             gtk_editable_set_editable (GTK_EDITABLE (widget), TRUE);
-             g_signal_connect (G_OBJECT (widget), "activate",
-                               G_CALLBACK (entry_cb), NULL);
+             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 (ci->widget), buf);
            }
+         gtk_combo_box_set_active (GTK_COMBO_BOX (ci->widget), nth);
        }
+      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);
+         ci->widget = gtk_spin_button_new (GTK_ADJUSTMENT (adj), 0, 0);
+         g_signal_connect (GTK_OBJECT (ci->widget), "changed",
+                           G_CALLBACK (changed_cb), ci);
+         ci->wtype = SPIN_BUTTON_WIDGET;
+         gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (ci->widget), TRUE);
+         gtk_spin_button_set_update_policy (GTK_SPIN_BUTTON (ci->widget),
+                                            GTK_UPDATE_ALWAYS);
+       }
       else
        {
-         widget = gtk_entry_new ();
-         gtk_entry_set_text (GTK_ENTRY (widget), "???");
-         gtk_editable_set_editable (GTK_EDITABLE (widget), TRUE);
-         g_signal_connect (G_OBJECT (widget), "activate",
-                           G_CALLBACK (entry_cb), NULL);
+         gchar buf[32];
+
+         ci->widget = gtk_entry_new ();
+         g_signal_connect (GTK_OBJECT (ci->widget), "changed",
+                           G_CALLBACK (changed_cb), ci);
+         g_signal_connect (GTK_OBJECT (ci->widget), "activate",
+                           G_CALLBACK (ok_cb), ci);
+         ci->wtype = ENTRY_WIDGET;
+         g_snprintf (buf, sizeof (buf), "%d", (gint) value);
+         gtk_entry_set_text (GTK_ENTRY (ci->widget), buf);
+         gtk_editable_set_editable (GTK_EDITABLE (ci->widget), TRUE);
        }
+    }
+
+  return ci->widget;
+}
+
+static void *
+help_cb (GtkButton *button, gpointer data)
+{
+  struct ControllerInfo *ci = data;
+  GtkWidget *msg;
+
+  msg = gtk_message_dialog_new (GTK_WINDOW
+                               (gtk_widget_get_toplevel (ci->dialog)),
+                               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
+row_activated_cb (GtkTreeView *parent, GtkTreePath *path,
+                 GtkTreeViewColumn *col, gpointer data)
+{
+  GtkTreeModel *model;
+  GtkTreeIter iter;
+  GtkWidget *label, *ok, *help, *hbox, *vbox;
+  MSymbol status;
+  struct ControllerInfo ci;
+  gchar *variable;
+
+  model = gtk_tree_view_get_model (parent);
+  if (! gtk_tree_model_get_iter (model, &iter, path))
+    return;
+  gtk_tree_model_get (model, &iter, VCOL_VARIABLE, &variable, -1);
+  current_variable = msymbol (variable);
+
+  ci.dialog = (gtk_dialog_new_with_buttons
+              (variable,
+               GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (parent))),
+               GTK_DIALOG_DESTROY_WITH_PARENT,
+               NULL));
+  
+  ci.default_ = gtk_button_new_from_stock (_("_Default"));
+  g_signal_connect (G_OBJECT (ci.default_), "clicked",
+                   G_CALLBACK (default_cb), &ci);
+
+  ci.revert = gtk_button_new_from_stock (GTK_STOCK_REVERT_TO_SAVED);
+  g_signal_connect (G_OBJECT (ci.revert), "clicked",
+                   G_CALLBACK (revert_cb), &ci);
+
+  label = gtk_label_new (_("Status : "));
+  ci.status = gtk_label_new (NULL);
+
+  ok = gtk_button_new_from_stock (GTK_STOCK_OK);
+  g_signal_connect (G_OBJECT (ok), "clicked",
+                   G_CALLBACK (ok_cb), &ci);
+
+  help = gtk_button_new_from_stock (GTK_STOCK_HELP);
+  g_signal_connect (G_OBJECT (help), "clicked",
+                   G_CALLBACK (help_cb), &ci);
+
+  vbox = gtk_vbox_new (FALSE, 12);
+  gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
+  gtk_container_add (GTK_CONTAINER (GTK_DIALOG (ci.dialog)->vbox), vbox);
+
+  gtk_container_add (GTK_CONTAINER (vbox), create_widget (&ci));
+
+  hbox = gtk_hbutton_box_new ();
+  gtk_box_set_spacing (GTK_BOX (hbox), 6);
+  gtk_container_add (GTK_CONTAINER (hbox), ci.default_);
+  gtk_container_add (GTK_CONTAINER (hbox), ci.revert);
+  gtk_container_add (GTK_CONTAINER (vbox), hbox);
+
+  hbox = gtk_hbox_new (FALSE, 6);
+  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+  gtk_box_pack_start (GTK_BOX (hbox), ci.status, FALSE, FALSE, 0);
+  gtk_container_add (GTK_CONTAINER (vbox), hbox);
+
+  gtk_button_box_set_layout
+    (GTK_BUTTON_BOX (GTK_DIALOG (ci.dialog)->action_area),
+     GTK_BUTTONBOX_EDGE);
+  gtk_container_add (GTK_CONTAINER (GTK_DIALOG (ci.dialog)->action_area),
+                    help);
+  gtk_container_add (GTK_CONTAINER (GTK_DIALOG (ci.dialog)->action_area),
+                    ok);
+
+  status = CURRENT_STATUS;
+  if (status == Mconfigured)
+    {
+      gtk_widget_set_sensitive (ci.default_, TRUE);
+      gtk_widget_set_sensitive (ci.revert, TRUE);
+      gtk_label_set_text (GTK_LABEL (ci.status), _("modified"));
+    }
+  else if (status == Mcustomized)
+    {
+      gtk_widget_set_sensitive (ci.default_, TRUE);
+      gtk_widget_set_sensitive (ci.revert, FALSE);
+      gtk_label_set_text (GTK_LABEL (ci.status), _("customized"));
+    }
+  else
+    {
+      gtk_widget_set_sensitive (ci.default_, FALSE);
+      gtk_widget_set_sensitive (ci.revert, FALSE);
+      gtk_label_set_text (GTK_LABEL (ci.status), _("default"));
+    }
+
+  ci.need_config = FALSE;
+
+  gtk_widget_show_all (ci.dialog);
+  gtk_dialog_run (GTK_DIALOG (ci.dialog));
+  gtk_tree_model_get_iter (model, &iter, path);
+  set_value_status (GTK_LIST_STORE (model), &iter);
+  gtk_widget_destroy (ci.dialog);
+}
+
+GtkWidget *
+create_variable_list (MSymbol lang, MSymbol name)
+{
+  GtkListStore *store;
+  GtkWidget *view;
+  GtkCellRenderer *renderer;
+  MPlist *plist;
+
+  current_lang = lang;
+  current_name = name;
+
+  plist = minput_get_variable (lang, name, Mnil);
+  if (! plist)
+    return gtk_label_new (_("No customizable variables."));
+
+  /*
+   * plist == ((variable description status value [valid-value ...])
+   *           (variable description status value [valid-value ...])
+   *           ...)
+   */
 
-      gtk_tooltips_set_tip (tip, widget, desc, NULL);
-      g_object_set_data (G_OBJECT (widget), "mim_lang", lang);
-      g_object_set_data (G_OBJECT (widget), "mim_name", name);
-      g_object_set_data (G_OBJECT (widget), "mim_variable", variable);
-      g_object_set_data (G_OBJECT (widget), "mim_key", key);
-      gtk_table_attach_defaults        (GTK_TABLE (table), widget,
-                                1, 2, row, row + 1);
+  store = gtk_list_store_new (NUM_VCOLS,
+                             G_TYPE_STRING,
+                             G_TYPE_STRING,
+                             G_TYPE_STRING);
+  for (; plist && mplist_key (plist) == Mplist; plist = mplist_next (plist))
+    {
+      GtkTreeIter iter;
+      MPlist *pl;
+      MSymbol variable, value, status;
+      gchar *desc;
+      gchar *status_str;
+
+      pl = mplist_value (plist);
+      /* pl == (variable description status value [valid-value ...]) */
+      current_variable = mplist_value (pl);
+
+      pl = mplist_next (pl); 
+      /* pl == (description status value [valid-value ...]) */
+      if (mplist_key (pl) == Mtext)
+       /* Fixme : Assuming the return value is in UTF-8 */
+       desc = mtext_data (mplist_value (pl), NULL, NULL, NULL, NULL);
+      else
+       desc = NULL;
+
+      pl = mplist_next (pl);
+      /* pl == (status value [valid-value ...]) */
+      status = mplist_value (pl);
+      gtk_list_store_append (store, &iter);
+      gtk_list_store_set (store, &iter,
+                         VCOL_VARIABLE, msymbol_name (current_variable),
+                         -1);
+      set_value_status (store, &iter);
     }
+  view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
+  g_object_unref (G_OBJECT (store));
+  renderer = gtk_cell_renderer_text_new ();
+  gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view),
+                                              -1,      
+                                              _("Name"),
+                                              renderer,
+                                              "text",
+                                              VCOL_VARIABLE,
+                                              NULL);
+  gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view),
+                                              -1,      
+                                              _("Value"),
+                                              renderer,
+                                              "text",
+                                              VCOL_VALUE,
+                                              NULL);
+  gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view),
+                                              -1,      
+                                              _("Status"),
+                                              renderer,
+                                              "text",
+                                              VCOL_STATUS,
+                                              NULL);
+  g_signal_connect (G_OBJECT (view), "row-activated",
+                   G_CALLBACK (row_activated_cb), NULL);
+  return view;
 }