1 /* mim-config.c -- M17N input method configuration
3 National Institute of Advanced Industrial Science and Technology (AIST)
4 Registration Number H15PRO112
6 This file is part of the m17n-im-config package; a sub-part of the
9 The m17n library is free software; you can redistribute it and/or
10 modify it under the terms of the GNU Lesser General Public License
11 as published by the Free Software Foundation; either version 2.1 of
12 the License, or (at your option) any later version.
14 The m17n library is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
19 You should have received a copy of the GNU Lesser General Public
20 License along with the m17n library; if not, write to the Free
21 Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
28 #include <m17n-misc.h>
31 #include "m17n-im-config.h"
33 #define _(String) dgettext (PACKAGE, String)
35 #define CONFIG_CALLBACK_DATA " config-callback-data"
36 #define CONFIG_STATUS_DATA " config-status-data"
37 #define CONFIG_TREE_VIEW " config-tree-view"
39 typedef void (*MimConfigCallbackFunc) (GtkWidget *widget, gpointer data);
41 typedef struct _MimConfigCallback
44 MimConfigCallbackFunc func;
48 typedef struct _MimConfigStatus
50 /* Number of available input methods. */
52 /* Number of modified input methods. */
56 /* Status of variables and commands of an input method. */
61 MIM_STATUS_CUSTOMIZED,
67 static char *mim_status_str[MIM_STATUS_MAX];
70 get_mim_status (MSymbol lang, MSymbol name)
73 enum MimStatus status = MIM_STATUS_NO;
75 for (plist = minput_get_variable (lang, name, Mnil);
76 plist && mplist_key (plist) != Mnil; plist = mplist_next (plist))
78 MPlist *p = mplist_value (plist);
79 MSymbol status_symbol;
81 if (status == MIM_STATUS_NO)
82 status = MIM_STATUS_DEFAULT;
83 p = mplist_next (mplist_next (p));
84 status_symbol = mplist_value (p);
85 if (status_symbol == Mconfigured)
86 return MIM_STATUS_MODIFIED;
87 if (status_symbol == Mcustomized)
88 status = MIM_STATUS_CUSTOMIZED;
90 for (plist = minput_get_command (lang, name, Mnil);
91 plist && mplist_key (plist) != Mnil; plist = mplist_next (plist))
93 MPlist *p = mplist_value (plist);
94 MSymbol status_symbol;
96 if (status == MIM_STATUS_NO)
97 status = MIM_STATUS_DEFAULT;
98 p = mplist_next (mplist_next (p));
99 status_symbol = mplist_value (p);
100 if (status_symbol == Mconfigured)
101 return MIM_STATUS_MODIFIED;
102 if (status_symbol == Mcustomized)
103 status = MIM_STATUS_CUSTOMIZED;
108 /* Columns of each row. */
111 /* parent: language name
114 /* parent: NULL or "modified"
115 child: "default", "customized", or "modified" */
117 /* parent: num of modified children
118 child: enum MimStatus */
121 child: symbolic language name. */
124 child: symbolic IM name. */
126 /* number of columns */
130 /* Called when a row is expanded. We may have to initialize
133 tree_expanded_cb (GtkTreeView *tree, GtkTreeIter *parent,
134 GtkTreePath *path, gpointer data)
140 model = gtk_tree_view_get_model (tree);
141 if (gtk_tree_model_iter_children (model, &iter, parent))
145 gtk_tree_model_get (model, &iter, COL_STATUS_STR, &status_str, -1);
148 /* The first child is not yet initialized, and that means
149 the remaining children are not initialized either. */
150 gtk_tree_model_get (model, &iter, COL_LANG, &lang, -1);
152 enum MimStatus im_status;
154 gtk_tree_model_get (model, &iter, COL_NAME, &name, -1);
155 im_status = get_mim_status (lang, name);
156 gtk_tree_store_set (GTK_TREE_STORE (model), &iter,
157 COL_STATUS_STR, mim_status_str[im_status],
158 COL_STATUS, im_status,
160 } while (gtk_tree_model_iter_next (model, &iter));
165 extern GtkWidget *create_variable_entries (GtkTooltips *tip, MSymbol lang,
167 extern GtkWidget *create_command_entries (GtkTooltips *tip, MSymbol lang,
171 edit_im (GtkTreeView *tree, MSymbol lang, MSymbol name)
173 GtkWidget *dialog, *notebook, *scrolled, *vbox, *label;
174 GtkTooltips *tip = gtk_tooltips_new ();
178 dialog = (gtk_dialog_new_with_buttons
179 (name == Mnil ? "global" : msymbol_name (name),
180 GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (tree))),
181 GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR,
182 GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
184 gtk_widget_set_size_request (dialog, 500, 300);
186 notebook = gtk_notebook_new ();
187 gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), notebook);
189 /* Variables' page */
190 scrolled = gtk_scrolled_window_new (NULL, NULL);
191 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
192 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
193 label = gtk_label_new_with_mnemonic ("_Variables");
194 gtk_notebook_append_page (GTK_NOTEBOOK (notebook), scrolled, label);
195 vbox = gtk_vbox_new (FALSE, 0);
196 gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrolled),
198 gtk_box_pack_start (GTK_BOX (vbox),
199 create_variable_entries (tip, lang, name),
202 /* Commands' pages */
203 scrolled = gtk_scrolled_window_new (NULL, NULL);
204 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
205 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
206 label = gtk_label_new_with_mnemonic ("Co_mmands");
207 gtk_notebook_append_page (GTK_NOTEBOOK (notebook), scrolled, label);
208 vbox = gtk_vbox_new (FALSE, 0);
209 gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrolled),
211 gtk_box_pack_start (GTK_BOX (vbox),
212 create_command_entries (tip, lang, name),
215 gtk_widget_show_all (dialog);
216 gtk_dialog_run (dialog);
217 gtk_widget_destroy (dialog);
221 update_child_row (GtkTreeModel *model, GtkTreeIter *iter,
222 enum MimStatus status, MimConfigStatus *config_status,
228 inc_modified = (status == MIM_STATUS_MODIFIED ? 1 : -1);
230 gtk_tree_store_set (GTK_TREE_STORE (model), iter,
231 COL_STATUS_STR, mim_status_str[status],
232 COL_STATUS, status, -1);
233 if (gtk_tree_model_iter_parent (model, &parent, iter))
238 gtk_tree_model_get (model, &parent, COL_STATUS, &num_modified, -1);
239 num_modified += inc_modified;
240 gtk_tree_store_set (GTK_TREE_STORE (model), &parent,
241 COL_STATUS, num_modified, -1);
242 if (num_modified <= 1)
244 status_str = (status == MIM_STATUS_MODIFIED
245 ? mim_status_str[MIM_STATUS_MODIFIED] : NULL);
246 gtk_tree_store_set (GTK_TREE_STORE (model), &parent,
247 COL_STATUS_STR, status_str);
252 config_status = g_object_get_data (G_OBJECT (model), CONFIG_STATUS_DATA);
253 config_status->num_modified += inc_modified;
254 if (tree && config_status->num_modified <= 1)
256 MimConfigCallback *callback;
258 callback = g_object_get_data (G_OBJECT (tree), CONFIG_CALLBACK_DATA);
260 callback->func (callback->widget, callback->data);
265 tree_activated_cb (GtkTreeView *tree, GtkTreePath *path,
266 GtkTreeViewColumn *column, gpointer data)
271 model = gtk_tree_view_get_model (tree);
272 if (gtk_tree_model_get_iter (model, &iter, path))
276 gtk_tree_model_get (model, &iter, COL_LANG, &lang, COL_NAME, &name, -1);
279 /* child row for an IM */
280 enum MimStatus old, new;
282 old = get_mim_status (lang, name);
283 edit_im (tree, lang, name);
284 new = get_mim_status (lang, name);
286 update_child_row (model, &iter, new, NULL, tree);
290 /* parent row for a language */
291 if (gtk_tree_view_row_expanded (tree, path))
292 gtk_tree_view_collapse_row (tree, path);
294 gtk_tree_view_expand_row (tree, path, TRUE);
299 typedef struct _MimTable
308 sort_im (const void *p1, const void *p2)
310 const MimTable *t1 = p1;
311 const MimTable *t2 = p2;
312 int result = strcmp (t1->lang, t2->lang);
314 return (result ? result : strcmp (t1->name, t2->name));
317 static GtkTreeStore *
318 make_store_for_input_methods ()
325 GtkTreeIter iter1, iter2;
326 enum MimStatus status;
327 MimConfigStatus *config_status;
329 store = gtk_tree_store_new (NUM_COLS,
330 G_TYPE_STRING, /* COL_TAG */
331 G_TYPE_STRING, /* COL_STATUS_STR */
332 G_TYPE_UINT, /* COL_STATUS */
333 G_TYPE_POINTER, /* COL_LANG */
334 G_TYPE_POINTER /* COL_NAME */
337 config_status = g_new0 (MimConfigStatus, 1);
338 gtk_tree_store_append (store, &iter1, NULL);
339 status = get_mim_status (Mt, Mnil);
340 gtk_tree_store_set (store, &iter1,
341 COL_TAG, _("global"),
342 COL_STATUS_STR, mim_status_str[status],
348 imlist = mdatabase_list (msymbol ("input-method"), Mnil, Mnil, Mnil);
349 config_status->num_im = mplist_length (imlist);
350 imtable = g_newa (MimTable, config_status->num_im);
351 for (i = 0, p = imlist; mplist_key (p) != Mnil; p = mplist_next (p))
353 MDatabase *mdb = (MDatabase *) mplist_value (p);
354 MSymbol *tag = mdatabase_tag (mdb);
356 if (tag[1] != Mnil && tag[2] != Mnil)
358 MSymbol language = mlanguage_name (tag[1]);
360 if (language != Mnil)
361 imtable[i].lang = msymbol_name (language);
363 /* `~' is for putting this element at the tail by sort. */
364 imtable[i].lang = "~other";
365 imtable[i].name = msymbol_name (tag[2]);
366 imtable[i].symlang = tag[1];
367 imtable[i].symname = tag[2];
371 m17n_object_unref (imlist);
372 config_status->num_im = i;
373 qsort (imtable, config_status->num_im, sizeof (MimTable), sort_im);
375 for (lang = NULL, i = 0; i < config_status->num_im; i++)
377 if (lang != imtable[i].lang)
381 gtk_tree_store_append (store, &iter1, NULL);
382 lang = imtable[i].lang;
386 gchar *native = NULL;
389 if (imtable[i].symlang != Mt
390 && (native_text = mlanguage_text (imtable[i].symlang)))
392 enum MTextFormat fmt;
394 native = mtext_data (native_text, &fmt, &nbytes,
396 if (fmt != MTEXT_FORMAT_US_ASCII
397 && fmt != MTEXT_FORMAT_UTF_8)
402 name = alloca (strlen (lang) + nbytes + 4);
403 sprintf (name, "%s (%s)", lang, native);
411 gtk_tree_store_set (store, &iter1,
413 COL_STATUS_STR, NULL,
419 gtk_tree_store_append (store, &iter2, &iter1);
420 gtk_tree_store_set (store, &iter2,
421 COL_TAG, imtable[i].name,
422 COL_STATUS_STR, NULL,
423 COL_LANG, imtable[i].symlang,
424 COL_NAME, imtable[i].symname,
427 config_status->num_modified = 0;
428 g_object_set_data_full (G_OBJECT (store), CONFIG_STATUS_DATA,
429 config_status, g_free);
434 revert_to_saved (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter,
437 enum MimStatus status;
439 MimConfigStatus *config_status = data;
441 gtk_tree_model_get (model, iter, COL_LANG, &lang, COL_NAME, &name, -1);
444 gtk_tree_model_get (model, iter, COL_STATUS, &status, -1);
445 if (status != MIM_STATUS_MODIFIED)
447 minput_config_variable (lang, name, Mnil, NULL);
448 minput_config_command (lang, name, Mnil, NULL);
449 status = get_mim_status (lang, name);
450 update_child_row (model, iter, status, config_status, NULL);
455 set_as_saved (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter,
458 enum MimStatus status;
460 MimConfigStatus *config_status = data;
462 gtk_tree_model_get (model, iter, COL_LANG, &lang, COL_NAME, &name, -1);
465 gtk_tree_model_get (model, iter, COL_STATUS, &status, -1);
466 if (status != MIM_STATUS_MODIFIED)
468 status = get_mim_status (lang, name);
469 update_child_row (model, iter, status, config_status, NULL);
473 static int initialized = 0;
476 destroy_cb (GtkWidget *widget, gpointer data)
485 mim_config_new (GCallback func, gpointer data)
487 GtkWidget *tree, *config;
489 GtkCellRenderer *renderer;
490 GtkTreeViewColumn *column;
499 bindtextdomain ("m17n-im-config", GETTEXTDIR);
500 bind_textdomain_codeset ("m17n-im-config", "UTF-8");
503 mim_status_str[MIM_STATUS_DEFAULT] = _("default");
504 mim_status_str[MIM_STATUS_CUSTOMIZED] = _("customized");
505 mim_status_str[MIM_STATUS_MODIFIED] = _("modified");
506 mim_status_str[MIM_STATUS_NO] = _("uncustomizable");
508 store = make_store_for_input_methods ();
509 tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
510 g_object_unref (G_OBJECT (store));
512 renderer = gtk_cell_renderer_text_new ();
513 column = (gtk_tree_view_column_new_with_attributes
514 (_("Input Method"), renderer, "text", COL_TAG, NULL));
515 gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
517 renderer = gtk_cell_renderer_text_new ();
518 column = (gtk_tree_view_column_new_with_attributes
519 (_("Status"), renderer, "text", COL_STATUS_STR, NULL));
520 gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
522 g_signal_connect (G_OBJECT (tree), "row-expanded",
523 G_CALLBACK (tree_expanded_cb), NULL);
524 g_signal_connect (G_OBJECT (tree), "row-activated",
525 G_CALLBACK (tree_activated_cb), NULL);
527 config =gtk_scrolled_window_new (NULL, NULL);
528 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (config),
529 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
530 gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (config), tree);
531 g_signal_connect (G_OBJECT (config), "destroy",
532 G_CALLBACK (destroy_cb), NULL);
534 g_object_set_data (G_OBJECT (config), CONFIG_TREE_VIEW, tree);
537 MimConfigCallback *callback;
539 callback = g_new (MimConfigCallback, 1);
540 callback->widget = config;
541 callback->func = (MimConfigCallbackFunc) func;
542 callback->data = data;
543 g_object_set_data_full (G_OBJECT (tree), CONFIG_CALLBACK_DATA,
551 mim_config_modified (GtkWidget *config)
555 MimConfigStatus *config_status;
557 tree = g_object_get_data (G_OBJECT (config), CONFIG_TREE_VIEW);
558 model = gtk_tree_view_get_model (tree);
559 config_status = g_object_get_data (G_OBJECT (model), CONFIG_STATUS_DATA);
561 return (config_status->num_modified > 0 ? TRUE : FALSE);
565 mim_config_revert (GtkWidget *config)
569 MimConfigStatus *config_status;
571 tree = g_object_get_data (G_OBJECT (config), CONFIG_TREE_VIEW);
572 model = gtk_tree_view_get_model (tree);
573 config_status = g_object_get_data (G_OBJECT (model), CONFIG_STATUS_DATA);
575 if (config_status->num_modified == 0)
577 gtk_tree_model_foreach (model, revert_to_saved, config_status);
582 mim_config_save (GtkWidget *config)
586 MimConfigStatus *config_status;
588 tree = g_object_get_data (G_OBJECT (config), CONFIG_TREE_VIEW);
589 model = gtk_tree_view_get_model (tree);
590 config_status = g_object_get_data (G_OBJECT (model), CONFIG_STATUS_DATA);
592 if (config_status->num_modified == 0)
594 minput_save_config ();
595 gtk_tree_model_foreach (model, set_as_saved, config_status);
600 mim_config_get_tree_view (GtkWidget *config)
604 tree = g_object_get_data (G_OBJECT (config), CONFIG_TREE_VIEW);