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