313744c7ae4a3c4cb32dbdc85bd665d5dbd23472
[m17n/m17n-im-config.git] / src / variable.c
1 #include <stdio.h>
2 #include <string.h>
3 #include <libintl.h>
4 #include <gtk/gtk.h>
5 #include <m17n.h>
6 #include <config.h>
7
8 #define _(String) dgettext (PACKAGE, String)
9
10 #define CURRENT_VALUE           \
11   (mplist_next                  \
12    (mplist_next                 \
13     (mplist_next                \
14      (mplist_value              \
15       (minput_get_variable      \
16        (current_lang, current_name, current_variable))))))
17
18 #define CURRENT_STATUS          \
19   (mplist_value                 \
20    (mplist_next                 \
21     (mplist_next                \
22      (mplist_value              \
23       (minput_get_variable      \
24        (current_lang, current_name, current_variable))))))
25
26 #define CURRENT_DESCRIPTION                                     \
27   (mtext_data                                                   \
28    (mplist_value                                                \
29     (mplist_next                                                \
30      (mplist_value                                              \
31       (minput_get_variable                                      \
32        (current_lang, current_name, current_variable)))),       \
33     NULL,  NULL,  NULL,  NULL))
34
35 #define CONFIG_VARIABLE(plist)                                          \
36   minput_config_variable (current_lang, current_name, current_variable, \
37                           (plist))
38
39 enum WidgetType
40   {
41     ENTRY_WIDGET,
42     COMBO_BOX_WIDGET,
43     SPIN_BUTTON_WIDGET
44   };
45
46 struct ControllerInfo
47 {
48   /* type of current variable: Minteger, Msymbol, or Mtext */
49   MSymbol vtype;
50
51   /* widget showing and controlling current variable */
52   GtkWidget *widget;
53
54   /* type of widget */
55   enum WidgetType wtype;
56
57   /* whether minput_config_variable () should be called on ok button */
58   gboolean need_config;
59
60   /* default button */
61   GtkWidget *default_;
62
63   /* revert button */
64   GtkWidget *revert;
65
66   /* status label */
67   GtkWidget *status;
68
69   /* dialog itself */
70   GtkWidget *dialog;
71 };
72
73 static MSymbol current_lang, current_name, current_variable;
74
75 static void
76 update_widget (struct ControllerInfo *ci)
77 {
78   MPlist *plist;
79   MSymbol key, status;
80   void *value;
81
82   plist = CURRENT_VALUE;
83   /* plist == (value [valid-value ...]) */
84   key = mplist_key (plist);
85   value = mplist_value (plist);
86
87   if (ci->wtype == ENTRY_WIDGET)
88     {
89       if (key == Msymbol)
90         gtk_entry_set_text (GTK_ENTRY (ci->widget),
91                             msymbol_name ((MSymbol) value));
92       else if (key == Mtext)            
93         /* Fixme : Assuming the return value is in UTF-8 */
94         gtk_entry_set_text (GTK_ENTRY (ci->widget),
95                             mtext_data ((MText *) value,
96                                         NULL, NULL, NULL, NULL));
97       else                      /* key == Minteger */
98         {
99           gchar buf[32];
100           g_snprintf (buf, sizeof (buf), "%d", (gint) value);
101           gtk_entry_set_text (GTK_ENTRY (ci->widget), buf);
102         }
103     }
104   else if (ci->wtype == COMBO_BOX_WIDGET)
105     {
106       gint i;
107
108       for (i = 0, plist = mplist_next (plist);
109            plist && mplist_key (plist) == key;
110            i++, plist = mplist_next (plist))
111         if (mplist_value (plist) == value)
112           break;
113       gtk_combo_box_set_active (GTK_COMBO_BOX (ci->widget), i);
114     }
115   else                          /* ci->wtype == SPIN_BUTTON_WIDGET */
116     gtk_spin_button_set_value (GTK_SPIN_BUTTON (ci->widget),
117                                (gdouble) (int) value);
118 }
119
120 static void *
121 default_cb (GtkButton *button, gpointer data)
122 {
123   MPlist *empty = mplist ();
124   struct ControllerInfo *ci = data;
125
126   CONFIG_VARIABLE (empty);
127   m17n_object_unref (empty);
128   update_widget (ci);
129   gtk_widget_set_sensitive (ci->default_, FALSE);
130   if (CURRENT_STATUS == Mconfigured)
131     {
132       gtk_widget_set_sensitive (ci->revert, TRUE);
133       gtk_label_set_text (GTK_LABEL (ci->status), _("modified"));
134     }
135   else
136     {
137       gtk_widget_set_sensitive (ci->revert, FALSE);
138       gtk_label_set_text (GTK_LABEL (ci->status), _("default"));
139     }
140   ci->need_config = FALSE;
141 }
142
143 static void *
144 revert_cb (GtkButton *button, gpointer data)
145 {
146   struct ControllerInfo *ci = data;
147
148   CONFIG_VARIABLE (NULL);
149   update_widget (ci);
150   gtk_widget_set_sensitive (ci->revert, FALSE);
151   if (CURRENT_STATUS == Mnil)
152     {
153       gtk_widget_set_sensitive (ci->default_, FALSE);
154       gtk_label_set_text (GTK_LABEL (ci->status), _("default"));
155     }
156   else
157     {
158       gtk_widget_set_sensitive (ci->default_, TRUE);
159       gtk_label_set_text (GTK_LABEL (ci->status), _("customized"));
160     }
161   ci->need_config = FALSE;
162 }
163
164 static gboolean
165 config_with_entry (struct ControllerInfo *ci)
166 {
167   const gchar *text = gtk_entry_get_text (GTK_ENTRY (ci->widget));
168   MPlist *plist = mplist ();
169   gboolean ret = TRUE;
170
171   if (ci->vtype == Msymbol)
172     {
173       mplist_add (plist, Msymbol, msymbol (text));
174       CONFIG_VARIABLE (plist);
175     }
176   else if (ci->vtype == Mtext)
177     {
178       MText *mt;
179
180       mt = mconv_decode_buffer (Mcoding_utf_8, text, strlen (text));
181       mplist_add (plist, Mtext, mt);
182       CONFIG_VARIABLE (plist);
183       m17n_object_unref (mt);
184     }
185   else                          /* ci->vtype == Minteger */
186     {
187       int i;
188       gchar buf[32];
189
190       if (sscanf (text, "%d", &i) == 1)
191         {
192           mplist_add (plist, Minteger, (void *) i);
193           CONFIG_VARIABLE (plist);
194         }
195       else
196         {
197           GtkWidget *msg;
198
199           msg = gtk_message_dialog_new (GTK_WINDOW
200                                         (gtk_widget_get_toplevel (ci->dialog)),
201                                         GTK_DIALOG_DESTROY_WITH_PARENT,
202                                         GTK_MESSAGE_ERROR,
203                                         GTK_BUTTONS_CLOSE,
204                                         _("The value must be an integer."));
205           gtk_dialog_run (GTK_DIALOG (msg));
206           gtk_widget_destroy (msg);
207           ret = FALSE;
208         }
209     }
210
211   m17n_object_unref (plist);
212   return ret;
213 }
214
215 static gboolean
216 config_with_combo (struct ControllerInfo *ci)
217 {
218   gchar *text = gtk_combo_box_get_active_text (GTK_COMBO_BOX (ci->widget));
219   MPlist *plist = mplist ();
220
221   if (ci->vtype == Msymbol)
222     {
223       mplist_add (plist, Msymbol, msymbol (text));
224       CONFIG_VARIABLE (plist);
225     }
226   else if (ci->vtype == Mtext)
227     {
228       MText *mt;
229
230       mt = mconv_decode_buffer (Mcoding_utf_8, text, strlen (text));
231       mplist_add (plist, Mtext, mt);
232       CONFIG_VARIABLE (plist);
233       m17n_object_unref (mt);
234     }
235   else                          /* ci->vtype == Minteger */
236     {
237       int i;
238
239       sscanf (text, "%d", &i);
240       mplist_add (plist, Minteger, (void *) i);
241       CONFIG_VARIABLE (plist);
242     }
243   m17n_object_unref (plist);
244   return TRUE;
245 }
246
247 static gboolean
248 config_with_spin (struct ControllerInfo *ci)
249 {
250   gint i;
251
252   i = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (ci->widget));
253   MPlist *plist = mplist ();
254
255   mplist_add (plist, Minteger, (void *) i);
256   CONFIG_VARIABLE (plist);
257   m17n_object_unref (plist);
258   return TRUE;
259 }
260
261 static void *
262 ok_cb (GtkButton *button, gpointer data)
263 {
264   struct ControllerInfo *ci = data;
265
266   if (ci->need_config)
267     {
268       if (ci->wtype == ENTRY_WIDGET)
269         {
270           if (config_with_entry (ci))
271             gtk_dialog_response (GTK_DIALOG (ci->dialog), GTK_RESPONSE_OK);
272           else
273             revert_cb (NULL, ci);
274         }
275
276       else if (ci->wtype == COMBO_BOX_WIDGET)
277         {
278           config_with_combo (ci); /* always returns true */
279           gtk_dialog_response (GTK_DIALOG (ci->dialog), GTK_RESPONSE_OK);
280         }
281
282       else                              /* ci->wtype == SPIN_BUTTON */
283         {
284           config_with_spin (ci); /* always returns true */
285           gtk_dialog_response (GTK_DIALOG (ci->dialog), GTK_RESPONSE_OK);
286         }
287     }
288   else
289     gtk_dialog_response (GTK_DIALOG (ci->dialog), GTK_RESPONSE_OK);
290 }
291
292 static void
293 changed_cb (GtkEntry *entry, gpointer data)
294 {
295   struct ControllerInfo *ci = data;
296
297   gtk_widget_set_sensitive (ci->default_, TRUE);
298   gtk_widget_set_sensitive (ci->revert, TRUE);
299   gtk_label_set_text (GTK_LABEL (ci->status), _("modified"));
300   ci->need_config = TRUE;
301 }
302
303 enum
304   {
305     VCOL_VARIABLE,
306     VCOL_VALUE,
307     VCOL_STATUS,
308     NUM_VCOLS
309   };
310
311 static void
312 set_value_status (GtkListStore *store, GtkTreeIter *iter)
313 {
314   MPlist *plist;
315   MSymbol status;
316   gchar *value_str, *status_str, buf[32];
317
318   status = CURRENT_STATUS;
319   if (status == Mconfigured)
320     status_str = _("modified");
321   else if (status == Mcustomized)
322     status_str = _("customized");
323   else
324     status_str = _("default");
325
326   plist = CURRENT_VALUE;
327   /* plist == (value [valid-value ...]) */
328   if (mplist_key (plist) == Msymbol)
329     value_str = msymbol_name ((MSymbol) mplist_value (plist));
330   else if (mplist_key (plist) == Mtext)
331     /* Fixme : Assuming the return value is in UTF-8 */
332     value_str = mtext_data ((MText *) mplist_value (plist),
333                             NULL, NULL, NULL, NULL);
334   else
335     {
336       g_snprintf (buf, sizeof (buf), "%d", (gint) mplist_value (plist));
337       value_str = buf;
338     }
339
340   gtk_list_store_set (store, iter,
341                       VCOL_VALUE, value_str,
342                       VCOL_STATUS, status_str,
343                       -1);
344 }
345
346 static GtkWidget *
347 create_widget (struct ControllerInfo *ci)
348 {
349   MPlist *plist;
350   void *value;
351
352   plist = CURRENT_VALUE;
353   /* plist == (value [valid-value ...]) */
354   ci->vtype = mplist_key (plist);
355   value = mplist_value (plist);
356   plist = mplist_next (plist);
357
358   if (ci->vtype == Msymbol)
359     {
360       if (plist && mplist_key (plist) == Msymbol)
361         {
362           gint i, nth;
363
364           ci->widget = gtk_combo_box_new_text ();
365           g_signal_connect (GTK_OBJECT (ci->widget), "changed",
366                             G_CALLBACK (changed_cb), ci);
367           ci->wtype = COMBO_BOX_WIDGET;
368           for (i = 0; plist && mplist_key (plist) == Msymbol;
369                plist = mplist_next (plist), i++)
370             {
371               if (mplist_value (plist) == value)
372                 nth = i;
373               gtk_combo_box_append_text
374                 (GTK_COMBO_BOX (ci->widget),
375                  msymbol_name ((MSymbol) mplist_value (plist)));
376             }
377           gtk_combo_box_set_active (GTK_COMBO_BOX (ci->widget), nth);
378         }
379       else
380         {
381           ci->widget = gtk_entry_new ();
382           g_signal_connect (GTK_OBJECT (ci->widget), "changed",
383                             G_CALLBACK (changed_cb), ci);
384           g_signal_connect (GTK_OBJECT (ci->widget), "activate",
385                             G_CALLBACK (ok_cb), ci);
386           ci->wtype = ENTRY_WIDGET;
387           gtk_entry_set_text (GTK_ENTRY (ci->widget), msymbol_name (value));
388           gtk_editable_set_editable (GTK_EDITABLE (ci->widget), TRUE);
389         }
390     }
391   else if (ci->vtype == Mtext)
392     {
393       if (plist && mplist_key (plist) == Mtext)
394         {
395           gint i, nth;
396
397           ci->widget = gtk_combo_box_new_text ();
398           g_signal_connect (GTK_OBJECT (ci->widget), "changed",
399                             G_CALLBACK (changed_cb), ci);
400           ci->wtype = COMBO_BOX_WIDGET;
401           for (i = 0; plist && mplist_key (plist) == Mtext;
402                plist = mplist_next (plist), i++)
403             {
404               if (! mtext_cmp ((MText *) mplist_value (plist),
405                                (MText *) value))
406                 nth = i;
407               /* Fixme : Assuming the return value is in UTF-8 */
408               gtk_combo_box_append_text
409                 (GTK_COMBO_BOX (ci->widget),
410                  mtext_data ((MText *) mplist_value (plist),
411                              NULL, NULL, NULL, NULL));
412             }
413           gtk_combo_box_set_active (GTK_COMBO_BOX (ci->widget), nth);
414         }
415       else
416         {
417           ci->widget = gtk_entry_new ();
418           g_signal_connect (GTK_OBJECT (ci->widget), "changed",
419                             G_CALLBACK (changed_cb), ci);
420           g_signal_connect (GTK_OBJECT (ci->widget), "activate",
421                             G_CALLBACK (ok_cb), ci);
422           ci->wtype = ENTRY_WIDGET;
423           /* Fixme : Assuming the return value is in UTF-8 */
424           gtk_entry_set_text (GTK_ENTRY (ci->widget),
425                               mtext_data (value, NULL, NULL, NULL, NULL));
426           gtk_editable_set_editable (GTK_EDITABLE (ci->widget), TRUE);
427         }
428     }
429   else                          /* ci->vtype == Minteger */
430     {
431       if (plist && mplist_key (plist) == Minteger)
432         {
433           gint i, nth;
434
435           ci->widget = gtk_combo_box_new_text ();
436           g_signal_connect (GTK_OBJECT (ci->widget), "changed",
437                             G_CALLBACK (changed_cb), ci);
438           ci->wtype = COMBO_BOX_WIDGET;
439           for (i = 0; plist && mplist_key (plist) == Minteger;
440                plist = mplist_next (plist), i++)
441             {
442               gchar buf[32];
443
444               if (mplist_value (plist) == value)
445                 nth = i;
446               g_snprintf (buf, sizeof (buf), "%d",
447                           (gint) mplist_value (plist));
448               gtk_combo_box_append_text (GTK_COMBO_BOX (ci->widget), buf);
449             }
450           gtk_combo_box_set_active (GTK_COMBO_BOX (ci->widget), nth);
451         }
452       else if (plist && mplist_key (plist) == Mplist)
453         {
454           GtkObject *adj;
455           gdouble lower, upper;
456
457           plist = mplist_value (plist);
458           lower = (gdouble) (int) mplist_value (plist);
459           upper = (gdouble) (int) mplist_value (mplist_next (plist));
460           adj = gtk_adjustment_new ((gdouble) (int) value, lower, upper,
461                                     1.0, 10.0, 0);
462           ci->widget = gtk_spin_button_new (GTK_ADJUSTMENT (adj), 0, 0);
463           g_signal_connect (GTK_OBJECT (ci->widget), "changed",
464                             G_CALLBACK (changed_cb), ci);
465           ci->wtype = SPIN_BUTTON_WIDGET;
466           gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (ci->widget), TRUE);
467           gtk_spin_button_set_update_policy (GTK_SPIN_BUTTON (ci->widget),
468                                              GTK_UPDATE_ALWAYS);
469         }
470       else
471         {
472           gchar buf[32];
473
474           ci->widget = gtk_entry_new ();
475           g_signal_connect (GTK_OBJECT (ci->widget), "changed",
476                             G_CALLBACK (changed_cb), ci);
477           g_signal_connect (GTK_OBJECT (ci->widget), "activate",
478                             G_CALLBACK (ok_cb), ci);
479           ci->wtype = ENTRY_WIDGET;
480           g_snprintf (buf, sizeof (buf), "%d", (gint) value);
481           gtk_entry_set_text (GTK_ENTRY (ci->widget), buf);
482           gtk_editable_set_editable (GTK_EDITABLE (ci->widget), TRUE);
483         }
484     }
485
486   return ci->widget;
487 }
488
489 static void *
490 help_cb (GtkButton *button, gpointer data)
491 {
492   struct ControllerInfo *ci = data;
493   GtkWidget *msg;
494
495   msg = gtk_message_dialog_new (GTK_WINDOW
496                                 (gtk_widget_get_toplevel (ci->dialog)),
497                                 GTK_DIALOG_DESTROY_WITH_PARENT,
498                                 GTK_MESSAGE_INFO,
499                                 GTK_BUTTONS_CLOSE,
500                                 CURRENT_DESCRIPTION);
501   gtk_dialog_run (GTK_DIALOG (msg));
502   gtk_widget_destroy (msg);
503 }
504   
505 static void
506 row_activated_cb (GtkTreeView *parent, GtkTreePath *path,
507                   GtkTreeViewColumn *col, gpointer data)
508 {
509   GtkTreeModel *model;
510   GtkTreeIter iter;
511   GtkWidget *label, *ok, *help, *hbox, *vbox;
512   MSymbol status;
513   struct ControllerInfo ci;
514   gchar *variable;
515
516   model = gtk_tree_view_get_model (parent);
517   if (! gtk_tree_model_get_iter (model, &iter, path))
518     return;
519   gtk_tree_model_get (model, &iter, VCOL_VARIABLE, &variable, -1);
520   current_variable = msymbol (variable);
521
522   ci.dialog = (gtk_dialog_new_with_buttons
523                (variable,
524                 GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (parent))),
525                 GTK_DIALOG_DESTROY_WITH_PARENT,
526                 NULL));
527   
528   ci.default_ = gtk_button_new_from_stock (_("_Default"));
529   g_signal_connect (G_OBJECT (ci.default_), "clicked",
530                     G_CALLBACK (default_cb), &ci);
531
532   ci.revert = gtk_button_new_from_stock (GTK_STOCK_REVERT_TO_SAVED);
533   g_signal_connect (G_OBJECT (ci.revert), "clicked",
534                     G_CALLBACK (revert_cb), &ci);
535
536   label = gtk_label_new (_("Status : "));
537   ci.status = gtk_label_new (NULL);
538
539   ok = gtk_button_new_from_stock (GTK_STOCK_OK);
540   g_signal_connect (G_OBJECT (ok), "clicked",
541                     G_CALLBACK (ok_cb), &ci);
542
543   help = gtk_button_new_from_stock (GTK_STOCK_HELP);
544   g_signal_connect (G_OBJECT (help), "clicked",
545                     G_CALLBACK (help_cb), &ci);
546
547   vbox = gtk_vbox_new (FALSE, 12);
548   gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
549   gtk_container_add (GTK_CONTAINER (GTK_DIALOG (ci.dialog)->vbox), vbox);
550
551   gtk_container_add (GTK_CONTAINER (vbox), create_widget (&ci));
552
553   hbox = gtk_hbutton_box_new ();
554   gtk_box_set_spacing (GTK_BOX (hbox), 6);
555   gtk_container_add (GTK_CONTAINER (hbox), ci.default_);
556   gtk_container_add (GTK_CONTAINER (hbox), ci.revert);
557   gtk_container_add (GTK_CONTAINER (vbox), hbox);
558
559   hbox = gtk_hbox_new (FALSE, 6);
560   gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
561   gtk_box_pack_start (GTK_BOX (hbox), ci.status, FALSE, FALSE, 0);
562   gtk_container_add (GTK_CONTAINER (vbox), hbox);
563
564   gtk_button_box_set_layout
565     (GTK_BUTTON_BOX (GTK_DIALOG (ci.dialog)->action_area),
566      GTK_BUTTONBOX_EDGE);
567   gtk_container_add (GTK_CONTAINER (GTK_DIALOG (ci.dialog)->action_area),
568                      help);
569   gtk_container_add (GTK_CONTAINER (GTK_DIALOG (ci.dialog)->action_area),
570                      ok);
571
572   status = CURRENT_STATUS;
573   if (status == Mconfigured)
574     {
575       gtk_widget_set_sensitive (ci.default_, TRUE);
576       gtk_widget_set_sensitive (ci.revert, TRUE);
577       gtk_label_set_text (GTK_LABEL (ci.status), _("modified"));
578     }
579   else if (status == Mcustomized)
580     {
581       gtk_widget_set_sensitive (ci.default_, TRUE);
582       gtk_widget_set_sensitive (ci.revert, FALSE);
583       gtk_label_set_text (GTK_LABEL (ci.status), _("customized"));
584     }
585   else
586     {
587       gtk_widget_set_sensitive (ci.default_, FALSE);
588       gtk_widget_set_sensitive (ci.revert, FALSE);
589       gtk_label_set_text (GTK_LABEL (ci.status), _("default"));
590     }
591
592   ci.need_config = FALSE;
593
594   gtk_widget_show_all (ci.dialog);
595   gtk_dialog_run (GTK_DIALOG (ci.dialog));
596   gtk_tree_model_get_iter (model, &iter, path);
597   set_value_status (GTK_LIST_STORE (model), &iter);
598   gtk_widget_destroy (ci.dialog);
599 }
600
601 GtkWidget *
602 create_variable_list (MSymbol lang, MSymbol name)
603 {
604   GtkListStore *store;
605   GtkWidget *view;
606   GtkCellRenderer *renderer;
607   MPlist *plist;
608
609   current_lang = lang;
610   current_name = name;
611
612   plist = minput_get_variable (lang, name, Mnil);
613   if (! plist)
614     return gtk_label_new (_("No customizable variables."));
615
616   /*
617    * plist == ((variable description status value [valid-value ...])
618    *           (variable description status value [valid-value ...])
619    *           ...)
620    */
621
622   store = gtk_list_store_new (NUM_VCOLS,
623                               G_TYPE_STRING,
624                               G_TYPE_STRING,
625                               G_TYPE_STRING);
626   for (; plist && mplist_key (plist) == Mplist; plist = mplist_next (plist))
627     {
628       GtkTreeIter iter;
629       MPlist *pl;
630       MSymbol variable, value, status;
631       gchar *desc;
632       gchar *status_str;
633
634       pl = mplist_value (plist);
635       /* pl == (variable description status value [valid-value ...]) */
636       current_variable = mplist_value (pl);
637
638       pl = mplist_next (pl); 
639       /* pl == (description status value [valid-value ...]) */
640       if (mplist_key (pl) == Mtext)
641         /* Fixme : Assuming the return value is in UTF-8 */
642         desc = mtext_data (mplist_value (pl), NULL, NULL, NULL, NULL);
643       else
644         desc = NULL;
645
646       pl = mplist_next (pl);
647       /* pl == (status value [valid-value ...]) */
648       status = mplist_value (pl);
649       gtk_list_store_append (store, &iter);
650       gtk_list_store_set (store, &iter,
651                           VCOL_VARIABLE, msymbol_name (current_variable),
652                           -1);
653       set_value_status (store, &iter);
654     }
655   view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
656   g_object_unref (G_OBJECT (store));
657   renderer = gtk_cell_renderer_text_new ();
658   gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view),
659                                                -1,      
660                                                _("Name"),
661                                                renderer,
662                                                "text",
663                                                VCOL_VARIABLE,
664                                                NULL);
665   gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view),
666                                                -1,      
667                                                _("Value"),
668                                                renderer,
669                                                "text",
670                                                VCOL_VALUE,
671                                                NULL);
672   gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view),
673                                                -1,      
674                                                _("Status"),
675                                                renderer,
676                                                "text",
677                                                VCOL_STATUS,
678                                                NULL);
679   g_signal_connect (G_OBJECT (view), "row-activated",
680                     G_CALLBACK (row_activated_cb), NULL);
681   return view;
682 }