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