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 p = mplist_next (mplist_next (p));
82 status_symbol = mplist_value (p);
83 if (status_symbol == Mconfigured)
84 return MIM_STATUS_MODIFIED;
85 if (status_symbol == Mcustomized)
86 status = MIM_STATUS_CUSTOMIZED;
87 else if (status == MIM_STATUS_NO)
88 status = MIM_STATUS_DEFAULT;
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 p = mplist_next (mplist_next (p));
97 status_symbol = mplist_value (p);
98 if (status_symbol == Mconfigured)
99 return MIM_STATUS_MODIFIED;
100 if (status_symbol == Mcustomized)
101 status = MIM_STATUS_CUSTOMIZED;
102 else if (status == MIM_STATUS_NO)
103 status = MIM_STATUS_DEFAULT;
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_list (MSymbol lang, MSymbol name);
166 extern GtkWidget *create_command_list (MSymbol lang, MSymbol name);
169 edit_im (GtkTreeView *tree, MSymbol lang, MSymbol name)
171 GtkWidget *dialog, *notebook, *scrolled, *vbox, *label;
175 dialog = (gtk_dialog_new_with_buttons
176 (name == Mnil ? "global" : msymbol_name (name),
177 GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (tree))),
178 GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR,
179 GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
181 gtk_widget_set_size_request (dialog, 500, 300);
183 vbox = gtk_vbox_new (FALSE, 0);
184 gtk_container_set_border_width (GTK_CONTAINER(vbox), 10);
185 gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), vbox);
187 notebook = gtk_notebook_new ();
188 gtk_container_add (GTK_CONTAINER (vbox), notebook);
190 /* Variables' page */
191 scrolled = gtk_scrolled_window_new (NULL, NULL);
192 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
193 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
194 label = gtk_label_new_with_mnemonic (_("_Variables"));
195 gtk_notebook_append_page (GTK_NOTEBOOK (notebook), scrolled, label);
196 vbox = gtk_vbox_new (FALSE, 0);
197 gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrolled),
199 gtk_box_pack_start (GTK_BOX (vbox),
200 create_variable_list (lang, name),
203 /* Commands' pages */
204 scrolled = gtk_scrolled_window_new (NULL, NULL);
205 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
206 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
207 label = gtk_label_new_with_mnemonic (_("Co_mmands"));
208 gtk_notebook_append_page (GTK_NOTEBOOK (notebook), scrolled, label);
209 vbox = gtk_vbox_new (FALSE, 0);
210 gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrolled),
212 gtk_box_pack_start (GTK_BOX (vbox),
213 create_command_list (lang, name),
216 gtk_widget_show_all (dialog);
217 gtk_dialog_run (GTK_DIALOG (dialog));
218 gtk_widget_destroy (dialog);
222 update_child_row (GtkTreeModel *model, GtkTreeIter *iter,
223 enum MimStatus status, MimConfigStatus *config_status,
229 inc_modified = (status == MIM_STATUS_MODIFIED ? 1 : -1);
231 gtk_tree_store_set (GTK_TREE_STORE (model), iter,
232 COL_STATUS_STR, mim_status_str[status],
233 COL_STATUS, status, -1);
234 if (gtk_tree_model_iter_parent (model, &parent, iter))
239 gtk_tree_model_get (model, &parent, COL_STATUS, &num_modified, -1);
240 num_modified += inc_modified;
241 gtk_tree_store_set (GTK_TREE_STORE (model), &parent,
242 COL_STATUS, num_modified, -1);
243 if (num_modified <= 1)
245 status_str = (status == MIM_STATUS_MODIFIED
246 ? mim_status_str[MIM_STATUS_MODIFIED] : NULL);
247 gtk_tree_store_set (GTK_TREE_STORE (model), &parent,
248 COL_STATUS_STR, status_str);
253 config_status = g_object_get_data (G_OBJECT (model), CONFIG_STATUS_DATA);
254 config_status->num_modified += inc_modified;
255 if (tree && config_status->num_modified <= 1)
257 MimConfigCallback *callback;
259 callback = g_object_get_data (G_OBJECT (tree), CONFIG_CALLBACK_DATA);
261 callback->func (callback->widget, callback->data);
266 tree_activated_cb (GtkTreeView *tree, GtkTreePath *path,
267 GtkTreeViewColumn *column, gpointer data)
272 model = gtk_tree_view_get_model (tree);
273 if (gtk_tree_model_get_iter (model, &iter, path))
277 gtk_tree_model_get (model, &iter, COL_LANG, &lang, COL_NAME, &name, -1);
280 /* child row for an IM */
281 enum MimStatus old, new;
283 old = get_mim_status (lang, name);
284 edit_im (tree, lang, name);
285 new = get_mim_status (lang, name);
287 update_child_row (model, &iter, new, NULL, tree);
291 /* parent row for a language */
292 if (gtk_tree_view_row_expanded (tree, path))
293 gtk_tree_view_collapse_row (tree, path);
295 gtk_tree_view_expand_row (tree, path, TRUE);
300 typedef struct _MimTable
309 sort_im (const void *p1, const void *p2)
311 const MimTable *t1 = p1;
312 const MimTable *t2 = p2;
313 int result = strcmp (t1->lang, t2->lang);
315 return (result ? result : strcmp (t1->name, t2->name));
318 static GtkTreeStore *
319 make_store_for_input_methods ()
326 GtkTreeIter iter1, iter2;
327 enum MimStatus status;
328 MimConfigStatus *config_status;
330 store = gtk_tree_store_new (NUM_COLS,
331 G_TYPE_STRING, /* COL_TAG */
332 G_TYPE_STRING, /* COL_STATUS_STR */
333 G_TYPE_UINT, /* COL_STATUS */
334 G_TYPE_POINTER, /* COL_LANG */
335 G_TYPE_POINTER /* COL_NAME */
338 config_status = g_new0 (MimConfigStatus, 1);
339 gtk_tree_store_append (store, &iter1, NULL);
340 status = get_mim_status (Mt, Mnil);
341 gtk_tree_store_set (store, &iter1,
342 COL_TAG, _("global"),
343 COL_STATUS_STR, mim_status_str[status],
349 imlist = mdatabase_list (msymbol ("input-method"), Mnil, Mnil, Mnil);
350 config_status->num_im = mplist_length (imlist);
351 imtable = g_newa (MimTable, config_status->num_im);
352 for (i = 0, p = imlist; mplist_key (p) != Mnil; p = mplist_next (p))
354 MDatabase *mdb = (MDatabase *) mplist_value (p);
355 MSymbol *tag = mdatabase_tag (mdb);
357 if (tag[1] != Mnil && tag[2] != Mnil)
359 MSymbol language = mlanguage_name (tag[1]);
361 if (language != Mnil)
362 imtable[i].lang = msymbol_name (language);
364 /* `~' is for putting this element at the tail by sort. */
365 imtable[i].lang = "~other";
366 imtable[i].name = msymbol_name (tag[2]);
367 imtable[i].symlang = tag[1];
368 imtable[i].symname = tag[2];
372 m17n_object_unref (imlist);
373 config_status->num_im = i;
374 qsort (imtable, config_status->num_im, sizeof (MimTable), sort_im);
376 for (lang = NULL, i = 0; i < config_status->num_im; i++)
378 if (lang != imtable[i].lang)
382 gtk_tree_store_append (store, &iter1, NULL);
383 lang = imtable[i].lang;
387 gchar *native = NULL;
390 if (imtable[i].symlang != Mt
391 && (native_text = mlanguage_text (imtable[i].symlang)))
393 enum MTextFormat fmt;
395 native = mtext_data (native_text, &fmt, &nbytes,
397 if (fmt != MTEXT_FORMAT_US_ASCII
398 && fmt != MTEXT_FORMAT_UTF_8)
403 name = alloca (strlen (lang) + nbytes + 4);
404 sprintf (name, "%s (%s)", lang, native);
412 gtk_tree_store_set (store, &iter1,
414 COL_STATUS_STR, NULL,
420 gtk_tree_store_append (store, &iter2, &iter1);
421 gtk_tree_store_set (store, &iter2,
422 COL_TAG, imtable[i].name,
423 COL_STATUS_STR, NULL,
424 COL_LANG, imtable[i].symlang,
425 COL_NAME, imtable[i].symname,
428 config_status->num_modified = 0;
429 g_object_set_data_full (G_OBJECT (store), CONFIG_STATUS_DATA,
430 config_status, g_free);
435 revert_to_saved (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter,
438 enum MimStatus status;
440 MimConfigStatus *config_status = data;
442 gtk_tree_model_get (model, iter, COL_LANG, &lang, COL_NAME, &name, -1);
445 gtk_tree_model_get (model, iter, COL_STATUS, &status, -1);
446 if (status != MIM_STATUS_MODIFIED)
448 minput_config_variable (lang, name, Mnil, NULL);
449 minput_config_command (lang, name, Mnil, NULL);
450 status = get_mim_status (lang, name);
451 update_child_row (model, iter, status, config_status, NULL);
456 set_as_saved (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter,
459 enum MimStatus status;
461 MimConfigStatus *config_status = data;
463 gtk_tree_model_get (model, iter, COL_LANG, &lang, COL_NAME, &name, -1);
466 gtk_tree_model_get (model, iter, COL_STATUS, &status, -1);
467 if (status != MIM_STATUS_MODIFIED)
469 status = get_mim_status (lang, name);
470 update_child_row (model, iter, status, config_status, NULL);
474 static int initialized = 0;
477 destroy_cb (GtkWidget *widget, gpointer data)
486 mim_config_new (GCallback func, gpointer data)
488 GtkWidget *tree, *config;
490 GtkCellRenderer *renderer;
491 GtkTreeViewColumn *column;
500 bindtextdomain ("m17n-im-config", GETTEXTDIR);
501 bind_textdomain_codeset ("m17n-im-config", "UTF-8");
504 mim_status_str[MIM_STATUS_DEFAULT] = _("default");
505 mim_status_str[MIM_STATUS_CUSTOMIZED] = _("customized");
506 mim_status_str[MIM_STATUS_MODIFIED] = _("modified");
507 mim_status_str[MIM_STATUS_NO] = _("uncustomizable");
509 store = make_store_for_input_methods ();
510 tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
511 g_object_unref (G_OBJECT (store));
513 renderer = gtk_cell_renderer_text_new ();
514 column = (gtk_tree_view_column_new_with_attributes
515 (_("Input Method"), renderer, "text", COL_TAG, NULL));
516 gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
518 renderer = gtk_cell_renderer_text_new ();
519 column = (gtk_tree_view_column_new_with_attributes
520 (_("Status"), renderer, "text", COL_STATUS_STR, NULL));
521 gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
523 g_signal_connect (G_OBJECT (tree), "row-expanded",
524 G_CALLBACK (tree_expanded_cb), NULL);
525 g_signal_connect (G_OBJECT (tree), "row-activated",
526 G_CALLBACK (tree_activated_cb), NULL);
528 config =gtk_scrolled_window_new (NULL, NULL);
529 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (config),
530 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
531 gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (config), tree);
532 g_signal_connect (G_OBJECT (config), "destroy",
533 G_CALLBACK (destroy_cb), NULL);
535 g_object_set_data (G_OBJECT (config), CONFIG_TREE_VIEW, tree);
538 MimConfigCallback *callback;
540 callback = g_new (MimConfigCallback, 1);
541 callback->widget = config;
542 callback->func = (MimConfigCallbackFunc) func;
543 callback->data = data;
544 g_object_set_data_full (G_OBJECT (tree), CONFIG_CALLBACK_DATA,
552 mim_config_modified (GtkWidget *config)
556 MimConfigStatus *config_status;
558 tree = g_object_get_data (G_OBJECT (config), CONFIG_TREE_VIEW);
559 model = gtk_tree_view_get_model (tree);
560 config_status = g_object_get_data (G_OBJECT (model), CONFIG_STATUS_DATA);
562 return (config_status->num_modified > 0 ? TRUE : FALSE);
566 mim_config_revert (GtkWidget *config)
570 MimConfigStatus *config_status;
572 tree = g_object_get_data (G_OBJECT (config), CONFIG_TREE_VIEW);
573 model = gtk_tree_view_get_model (tree);
574 config_status = g_object_get_data (G_OBJECT (model), CONFIG_STATUS_DATA);
576 if (config_status->num_modified == 0)
578 gtk_tree_model_foreach (model, revert_to_saved, config_status);
583 mim_config_save (GtkWidget *config)
587 MimConfigStatus *config_status;
589 tree = g_object_get_data (G_OBJECT (config), CONFIG_TREE_VIEW);
590 model = gtk_tree_view_get_model (tree);
591 config_status = g_object_get_data (G_OBJECT (model), CONFIG_STATUS_DATA);
593 if (config_status->num_modified == 0)
595 minput_save_config ();
596 gtk_tree_model_foreach (model, set_as_saved, config_status);
601 mim_config_get_tree_view (GtkWidget *config)
605 tree = g_object_get_data (G_OBJECT (config), CONFIG_TREE_VIEW);