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,
30 #include "m17n-im-config.h"
32 #define _(String) dgettext (PACKAGE, String)
34 #define CONFIG_CALLBACK_DATA " config-callback-data"
35 #define CONFIG_STATUS_DATA " config-status-data"
36 #define CONFIG_TREE_VIEW " config-tree-view"
38 typedef struct _MimConfigStatus
40 /* Number of available input methods. */
42 /* Number of modified input methods. */
46 /* Status of variables and commands of an input method. */
51 MIM_STATUS_CUSTOMIZED,
57 static char *mim_status_str[MIM_STATUS_MAX];
60 get_mim_status (MSymbol lang, MSymbol name)
63 enum MimStatus status = MIM_STATUS_NO;
65 for (plist = minput_get_variable (lang, name, Mnil);
66 plist && mplist_key (plist) != Mnil; plist = mplist_next (plist))
68 MPlist *p = mplist_value (plist);
69 MSymbol status_symbol;
71 status = MIM_STATUS_DEFAULT;
72 p = mplist_next (mplist_next (p));
73 status_symbol = mplist_value (p);
74 if (status_symbol != Mnil && status_symbol != Minherited)
75 return (status_symbol == Mcustomized
76 ? MIM_STATUS_CUSTOMIZED : MIM_STATUS_MODIFIED);
78 for (plist = minput_get_command (lang, name, Mnil);
79 plist && mplist_key (plist) != Mnil; plist = mplist_next (plist))
81 MPlist *p = mplist_value (plist);
82 MSymbol status_symbol;
84 status = MIM_STATUS_DEFAULT;
85 p = mplist_next (mplist_next (p));
86 status_symbol = mplist_value (p);
87 if (status_symbol != Mnil && status_symbol != Minherited)
88 return (status_symbol == Mcustomized
89 ? MIM_STATUS_CUSTOMIZED : MIM_STATUS_MODIFIED);
94 /* Columns of each row. */
97 /* parent: language name
100 /* parent: NULL or "modified"
101 child: "default", "customized", or "modified" */
103 /* parent: num of modified children
104 child: enum MimStatus */
107 child: symbolic language name. */
110 child: symbolic IM name. */
112 /* number of columns */
116 /* Called when a row is expanded. We may have to initialize
119 tree_expanded_cb (GtkTreeView *tree, GtkTreeIter *parent,
120 GtkTreePath *path, gpointer data)
126 model = gtk_tree_view_get_model (tree);
127 if (gtk_tree_model_iter_children (model, &iter, parent))
131 gtk_tree_model_get (model, &iter, COL_STATUS_STR, &status_str, -1);
134 /* The first child is not yet initialized, and that means
135 the remaining children are not initialized either. */
136 gtk_tree_model_get (model, &iter, COL_LANG, &lang, -1);
138 enum MimStatus im_status;
140 gtk_tree_model_get (model, &iter, COL_NAME, &name, -1);
141 im_status = get_mim_status (lang, name);
142 gtk_tree_store_set (GTK_TREE_STORE (model), &iter,
143 COL_STATUS_STR, mim_status_str[im_status],
144 COL_STATUS, im_status,
146 } while (gtk_tree_model_iter_next (model, &iter));
152 edit_im (GtkTreeView *tree, MSymbol lang, MSymbol name)
154 GtkWidget *dialog, *label;
157 dialog = (gtk_dialog_new_with_buttons
159 GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (tree))),
160 GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR,
162 GTK_STOCK_REVERT_TO_SAVED, GTK_RESPONSE_NO,
163 GTK_STOCK_EDIT, GTK_RESPONSE_YES,
164 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
166 label = gtk_label_new (msymbol_name (name));
167 gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), label);
168 gtk_widget_show_all (dialog);
169 response = gtk_dialog_run (GTK_DIALOG (dialog));
170 if (response != GTK_RESPONSE_CANCEL
171 && lang == Mt && (name == Mnil || name == msymbol ("unicode")))
173 MSymbol variable = msymbol (name == Mnil ? "candidates-group-size"
176 if (response == GTK_RESPONSE_NO)
178 minput_config_variable (lang, name, variable, NULL);
180 else if (response == 0)
182 MPlist *plist = mplist ();
184 minput_config_variable (lang, name, variable, plist);
185 m17n_object_unref (plist);
189 MPlist *plist = mplist ();
193 mplist_add (plist, Minteger, (void *) 3);
197 MText *mt = mtext_from_data (">>", 2, MTEXT_FORMAT_US_ASCII);
198 mplist_add (plist, Mtext, mt);
199 m17n_object_unref (mt);
201 minput_config_variable (lang, name, variable, plist);
202 m17n_object_unref (plist);
206 gtk_widget_destroy (dialog);
210 update_child_row (GtkTreeModel *model, GtkTreeIter *iter,
211 enum MimStatus status, MimConfigStatus *config_status,
217 inc_modified = (status == MIM_STATUS_MODIFIED ? 1 : -1);
219 gtk_tree_store_set (GTK_TREE_STORE (model), iter,
220 COL_STATUS_STR, mim_status_str[status],
221 COL_STATUS, status, -1);
222 if (gtk_tree_model_iter_parent (model, &parent, iter))
227 gtk_tree_model_get (model, &parent, COL_STATUS, &num_modified, -1);
228 num_modified += inc_modified;
229 gtk_tree_store_set (GTK_TREE_STORE (model), &parent,
230 COL_STATUS, num_modified, -1);
231 if (num_modified <= 1)
233 status_str = (status == MIM_STATUS_MODIFIED
234 ? mim_status_str[MIM_STATUS_MODIFIED] : NULL);
235 gtk_tree_store_set (GTK_TREE_STORE (model), &parent,
236 COL_STATUS_STR, status_str);
241 config_status = g_object_get_data (G_OBJECT (model), CONFIG_STATUS_DATA);
242 config_status->num_modified += inc_modified;
243 if (tree && config_status->num_modified <= 1)
245 MimConfigCallback *callback;
247 callback = g_object_get_data (G_OBJECT (tree), CONFIG_CALLBACK_DATA);
249 callback->func (config_status->num_modified == 0 ? FALSE : TRUE,
255 tree_activated_cb (GtkTreeView *tree, GtkTreePath *path,
256 GtkTreeViewColumn *column, gpointer data)
261 model = gtk_tree_view_get_model (tree);
262 if (gtk_tree_model_get_iter (model, &iter, path))
266 gtk_tree_model_get (model, &iter, COL_LANG, &lang, COL_NAME, &name, -1);
269 /* child row for an IM */
270 enum MimStatus old, new;
272 old = get_mim_status (lang, name);
273 edit_im (tree, lang, name);
274 new = get_mim_status (lang, name);
276 update_child_row (model, &iter, new, NULL, tree);
280 /* parent row for a language */
281 if (gtk_tree_view_row_expanded (tree, path))
282 gtk_tree_view_collapse_row (tree, path);
284 gtk_tree_view_expand_row (tree, path, TRUE);
289 typedef struct _MimTable
298 sort_im (const void *p1, const void *p2)
300 const MimTable *t1 = p1;
301 const MimTable *t2 = p2;
302 int result = strcmp (t1->lang, t2->lang);
304 return (result ? result : strcmp (t1->name, t2->name));
307 static GtkTreeStore *
308 make_store_for_input_methods ()
315 GtkTreeIter iter1, iter2;
316 enum MimStatus status;
317 MimConfigStatus *config_status;
319 store = gtk_tree_store_new (NUM_COLS,
320 G_TYPE_STRING, /* COL_TAG */
321 G_TYPE_STRING, /* COL_STATUS_STR */
322 G_TYPE_UINT, /* COL_STATUS */
323 G_TYPE_POINTER, /* COL_LANG */
324 G_TYPE_POINTER /* COL_NAME */
327 config_status = g_new0 (MimConfigStatus, 1);
328 gtk_tree_store_append (store, &iter1, NULL);
329 status = get_mim_status (Mt, Mnil);
330 gtk_tree_store_set (store, &iter1,
331 COL_TAG, _("global"),
332 COL_STATUS_STR, mim_status_str[status],
338 imlist = mdatabase_list (msymbol ("input-method"), Mnil, Mnil, Mnil);
339 config_status->num_im = mplist_length (imlist);
340 imtable = g_newa (MimTable, config_status->num_im);
341 for (i = 0, p = imlist; mplist_key (p) != Mnil; p = mplist_next (p))
343 MDatabase *mdb = (MDatabase *) mplist_value (p);
344 MSymbol *tag = mdatabase_tag (mdb);
346 if (tag[1] != Mnil && tag[2] != Mnil)
348 MSymbol language = mlanguage_name (tag[1]);
350 if (language != Mnil)
351 imtable[i].lang = msymbol_name (language);
353 /* `~' is for putting this element at the tail by sort. */
354 imtable[i].lang = "~other";
355 imtable[i].name = msymbol_name (tag[2]);
356 imtable[i].symlang = tag[1];
357 imtable[i].symname = tag[2];
361 m17n_object_unref (imlist);
362 config_status->num_im = i;
363 qsort (imtable, config_status->num_im, sizeof (MimTable), sort_im);
365 for (lang = NULL, i = 0; i < config_status->num_im; i++)
367 if (lang != imtable[i].lang)
371 gtk_tree_store_append (store, &iter1, NULL);
372 lang = imtable[i].lang;
376 gchar *native = NULL;
379 if (imtable[i].symlang != Mt
380 && (native_text = mlanguage_text (imtable[i].symlang)))
382 enum MTextFormat fmt;
384 native = mtext_data (native_text, &fmt, &nbytes,
386 if (fmt != MTEXT_FORMAT_US_ASCII
387 && fmt != MTEXT_FORMAT_UTF_8)
392 name = alloca (strlen (lang) + nbytes + 4);
393 sprintf (name, "%s (%s)", lang, native);
401 gtk_tree_store_set (store, &iter1,
403 COL_STATUS_STR, NULL,
409 gtk_tree_store_append (store, &iter2, &iter1);
410 gtk_tree_store_set (store, &iter2,
411 COL_TAG, imtable[i].name,
412 COL_STATUS_STR, NULL,
413 COL_LANG, imtable[i].symlang,
414 COL_NAME, imtable[i].symname,
417 config_status->num_modified = 0;
418 g_object_set_data_full (G_OBJECT (store), CONFIG_STATUS_DATA,
419 config_status, g_free);
424 revert_to_saved (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter,
427 enum MimStatus status;
429 MimConfigStatus *config_status = data;
431 gtk_tree_model_get (model, iter, COL_LANG, &lang, COL_NAME, &name, -1);
434 gtk_tree_model_get (model, iter, COL_STATUS, &status, -1);
435 if (status != MIM_STATUS_MODIFIED)
437 minput_config_variable (lang, name, Mnil, NULL);
438 minput_config_command (lang, name, Mnil, NULL);
439 status = get_mim_status (lang, name);
440 update_child_row (model, iter, status, config_status, NULL);
445 set_as_saved (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter,
448 enum MimStatus status;
450 MimConfigStatus *config_status = data;
452 gtk_tree_model_get (model, iter, COL_LANG, &lang, COL_NAME, &name, -1);
455 gtk_tree_model_get (model, iter, COL_STATUS, &status, -1);
456 if (status != MIM_STATUS_MODIFIED)
458 status = get_mim_status (lang, name);
459 update_child_row (model, iter, status, config_status, NULL);
472 bindtextdomain ("m17n-im-config", GETTEXTDIR);
473 bind_textdomain_codeset ("m17n-im-config", "UTF-8");
476 mim_status_str[MIM_STATUS_DEFAULT] = _("default");
477 mim_status_str[MIM_STATUS_CUSTOMIZED] = _("customized");
478 mim_status_str[MIM_STATUS_MODIFIED] = _("modified");
479 mim_status_str[MIM_STATUS_NO] = _("uncustomizable");
493 mim_config_widget (MimConfigCallback *callback)
495 GtkWidget *tree, *config;
497 GtkCellRenderer *renderer;
498 GtkTreeViewColumn *column;
500 store = make_store_for_input_methods ();
501 tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
502 g_object_unref (G_OBJECT (store));
504 g_object_set_data (G_OBJECT (tree), CONFIG_CALLBACK_DATA, callback);
506 renderer = gtk_cell_renderer_text_new ();
507 column = (gtk_tree_view_column_new_with_attributes
508 (_("Input Method"), renderer, "text", COL_TAG, NULL));
509 gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
511 renderer = gtk_cell_renderer_text_new ();
512 column = (gtk_tree_view_column_new_with_attributes
513 (_("status"), renderer, "text", COL_STATUS_STR, NULL));
514 gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
516 g_signal_connect (G_OBJECT (tree), "row-expanded",
517 G_CALLBACK (tree_expanded_cb), NULL);
518 g_signal_connect (G_OBJECT (tree), "row-activated",
519 G_CALLBACK (tree_activated_cb), NULL);
521 config =gtk_scrolled_window_new (NULL, NULL);
522 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (config),
523 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
524 gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (config), tree);
526 g_object_set_data (G_OBJECT (config), CONFIG_TREE_VIEW, tree);
532 mim_config_modified (GtkWidget *config)
536 MimConfigStatus *config_status;
538 tree = g_object_get_data (G_OBJECT (config), CONFIG_TREE_VIEW);
539 model = gtk_tree_view_get_model (tree);
540 config_status = g_object_get_data (G_OBJECT (model), CONFIG_STATUS_DATA);
542 return (config_status->num_modified > 0 ? TRUE : FALSE);
546 mim_config_revert (GtkWidget *config)
550 MimConfigStatus *config_status;
552 tree = g_object_get_data (G_OBJECT (config), CONFIG_TREE_VIEW);
553 model = gtk_tree_view_get_model (tree);
554 config_status = g_object_get_data (G_OBJECT (model), CONFIG_STATUS_DATA);
556 if (config_status->num_modified == 0)
558 gtk_tree_model_foreach (model, revert_to_saved, config_status);
563 mim_config_save (GtkWidget *config)
567 MimConfigStatus *config_status;
569 tree = g_object_get_data (G_OBJECT (config), CONFIG_TREE_VIEW);
570 model = gtk_tree_view_get_model (tree);
571 config_status = g_object_get_data (G_OBJECT (model), CONFIG_STATUS_DATA);
573 if (config_status->num_modified == 0)
575 minput_save_config ();
576 gtk_tree_model_foreach (model, set_as_saved, config_status);