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 status = MIM_STATUS_DEFAULT;
82 p = mplist_next (mplist_next (p));
83 status_symbol = mplist_value (p);
84 if (status_symbol != Mnil && status_symbol != Minherited)
85 return (status_symbol == Mcustomized
86 ? MIM_STATUS_CUSTOMIZED : MIM_STATUS_MODIFIED);
88 for (plist = minput_get_command (lang, name, Mnil);
89 plist && mplist_key (plist) != Mnil; plist = mplist_next (plist))
91 MPlist *p = mplist_value (plist);
92 MSymbol status_symbol;
94 status = MIM_STATUS_DEFAULT;
95 p = mplist_next (mplist_next (p));
96 status_symbol = mplist_value (p);
97 if (status_symbol != Mnil && status_symbol != Minherited)
98 return (status_symbol == Mcustomized
99 ? MIM_STATUS_CUSTOMIZED : MIM_STATUS_MODIFIED);
104 /* Columns of each row. */
107 /* parent: language name
110 /* parent: NULL or "modified"
111 child: "default", "customized", or "modified" */
113 /* parent: num of modified children
114 child: enum MimStatus */
117 child: symbolic language name. */
120 child: symbolic IM name. */
122 /* number of columns */
126 /* Called when a row is expanded. We may have to initialize
129 tree_expanded_cb (GtkTreeView *tree, GtkTreeIter *parent,
130 GtkTreePath *path, gpointer data)
136 model = gtk_tree_view_get_model (tree);
137 if (gtk_tree_model_iter_children (model, &iter, parent))
141 gtk_tree_model_get (model, &iter, COL_STATUS_STR, &status_str, -1);
144 /* The first child is not yet initialized, and that means
145 the remaining children are not initialized either. */
146 gtk_tree_model_get (model, &iter, COL_LANG, &lang, -1);
148 enum MimStatus im_status;
150 gtk_tree_model_get (model, &iter, COL_NAME, &name, -1);
151 im_status = get_mim_status (lang, name);
152 gtk_tree_store_set (GTK_TREE_STORE (model), &iter,
153 COL_STATUS_STR, mim_status_str[im_status],
154 COL_STATUS, im_status,
156 } while (gtk_tree_model_iter_next (model, &iter));
162 edit_im (GtkTreeView *tree, MSymbol lang, MSymbol name)
164 GtkWidget *dialog, *label;
167 dialog = (gtk_dialog_new_with_buttons
169 GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (tree))),
170 GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR,
172 GTK_STOCK_REVERT_TO_SAVED, GTK_RESPONSE_NO,
173 GTK_STOCK_EDIT, GTK_RESPONSE_YES,
174 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
176 label = gtk_label_new (msymbol_name (name));
177 gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), label);
178 gtk_widget_show_all (dialog);
179 response = gtk_dialog_run (GTK_DIALOG (dialog));
180 if (response != GTK_RESPONSE_CANCEL
181 && lang == Mt && (name == Mnil || name == msymbol ("unicode")))
183 MSymbol variable = msymbol (name == Mnil ? "candidates-group-size"
186 if (response == GTK_RESPONSE_NO)
188 minput_config_variable (lang, name, variable, NULL);
190 else if (response == 0)
192 MPlist *plist = mplist ();
194 minput_config_variable (lang, name, variable, plist);
195 m17n_object_unref (plist);
199 MPlist *plist = mplist ();
203 mplist_add (plist, Minteger, (void *) 3);
207 MText *mt = mtext_from_data (">>", 2, MTEXT_FORMAT_US_ASCII);
208 mplist_add (plist, Mtext, mt);
209 m17n_object_unref (mt);
211 minput_config_variable (lang, name, variable, plist);
212 m17n_object_unref (plist);
216 gtk_widget_destroy (dialog);
220 update_child_row (GtkTreeModel *model, GtkTreeIter *iter,
221 enum MimStatus status, MimConfigStatus *config_status,
227 inc_modified = (status == MIM_STATUS_MODIFIED ? 1 : -1);
229 gtk_tree_store_set (GTK_TREE_STORE (model), iter,
230 COL_STATUS_STR, mim_status_str[status],
231 COL_STATUS, status, -1);
232 if (gtk_tree_model_iter_parent (model, &parent, iter))
237 gtk_tree_model_get (model, &parent, COL_STATUS, &num_modified, -1);
238 num_modified += inc_modified;
239 gtk_tree_store_set (GTK_TREE_STORE (model), &parent,
240 COL_STATUS, num_modified, -1);
241 if (num_modified <= 1)
243 status_str = (status == MIM_STATUS_MODIFIED
244 ? mim_status_str[MIM_STATUS_MODIFIED] : NULL);
245 gtk_tree_store_set (GTK_TREE_STORE (model), &parent,
246 COL_STATUS_STR, status_str);
251 config_status = g_object_get_data (G_OBJECT (model), CONFIG_STATUS_DATA);
252 config_status->num_modified += inc_modified;
253 if (tree && config_status->num_modified <= 1)
255 MimConfigCallback *callback;
257 callback = g_object_get_data (G_OBJECT (tree), CONFIG_CALLBACK_DATA);
259 callback->func (callback->widget, callback->data);
264 tree_activated_cb (GtkTreeView *tree, GtkTreePath *path,
265 GtkTreeViewColumn *column, gpointer data)
270 model = gtk_tree_view_get_model (tree);
271 if (gtk_tree_model_get_iter (model, &iter, path))
275 gtk_tree_model_get (model, &iter, COL_LANG, &lang, COL_NAME, &name, -1);
278 /* child row for an IM */
279 enum MimStatus old, new;
281 old = get_mim_status (lang, name);
282 edit_im (tree, lang, name);
283 new = get_mim_status (lang, name);
285 update_child_row (model, &iter, new, NULL, tree);
289 /* parent row for a language */
290 if (gtk_tree_view_row_expanded (tree, path))
291 gtk_tree_view_collapse_row (tree, path);
293 gtk_tree_view_expand_row (tree, path, TRUE);
298 typedef struct _MimTable
307 sort_im (const void *p1, const void *p2)
309 const MimTable *t1 = p1;
310 const MimTable *t2 = p2;
311 int result = strcmp (t1->lang, t2->lang);
313 return (result ? result : strcmp (t1->name, t2->name));
316 static GtkTreeStore *
317 make_store_for_input_methods ()
324 GtkTreeIter iter1, iter2;
325 enum MimStatus status;
326 MimConfigStatus *config_status;
328 store = gtk_tree_store_new (NUM_COLS,
329 G_TYPE_STRING, /* COL_TAG */
330 G_TYPE_STRING, /* COL_STATUS_STR */
331 G_TYPE_UINT, /* COL_STATUS */
332 G_TYPE_POINTER, /* COL_LANG */
333 G_TYPE_POINTER /* COL_NAME */
336 config_status = g_new0 (MimConfigStatus, 1);
337 gtk_tree_store_append (store, &iter1, NULL);
338 status = get_mim_status (Mt, Mnil);
339 gtk_tree_store_set (store, &iter1,
340 COL_TAG, _("global"),
341 COL_STATUS_STR, mim_status_str[status],
347 imlist = mdatabase_list (msymbol ("input-method"), Mnil, Mnil, Mnil);
348 config_status->num_im = mplist_length (imlist);
349 imtable = g_newa (MimTable, config_status->num_im);
350 for (i = 0, p = imlist; mplist_key (p) != Mnil; p = mplist_next (p))
352 MDatabase *mdb = (MDatabase *) mplist_value (p);
353 MSymbol *tag = mdatabase_tag (mdb);
355 if (tag[1] != Mnil && tag[2] != Mnil)
357 MSymbol language = mlanguage_name (tag[1]);
359 if (language != Mnil)
360 imtable[i].lang = msymbol_name (language);
362 /* `~' is for putting this element at the tail by sort. */
363 imtable[i].lang = "~other";
364 imtable[i].name = msymbol_name (tag[2]);
365 imtable[i].symlang = tag[1];
366 imtable[i].symname = tag[2];
370 m17n_object_unref (imlist);
371 config_status->num_im = i;
372 qsort (imtable, config_status->num_im, sizeof (MimTable), sort_im);
374 for (lang = NULL, i = 0; i < config_status->num_im; i++)
376 if (lang != imtable[i].lang)
380 gtk_tree_store_append (store, &iter1, NULL);
381 lang = imtable[i].lang;
385 gchar *native = NULL;
388 if (imtable[i].symlang != Mt
389 && (native_text = mlanguage_text (imtable[i].symlang)))
391 enum MTextFormat fmt;
393 native = mtext_data (native_text, &fmt, &nbytes,
395 if (fmt != MTEXT_FORMAT_US_ASCII
396 && fmt != MTEXT_FORMAT_UTF_8)
401 name = alloca (strlen (lang) + nbytes + 4);
402 sprintf (name, "%s (%s)", lang, native);
410 gtk_tree_store_set (store, &iter1,
412 COL_STATUS_STR, NULL,
418 gtk_tree_store_append (store, &iter2, &iter1);
419 gtk_tree_store_set (store, &iter2,
420 COL_TAG, imtable[i].name,
421 COL_STATUS_STR, NULL,
422 COL_LANG, imtable[i].symlang,
423 COL_NAME, imtable[i].symname,
426 config_status->num_modified = 0;
427 g_object_set_data_full (G_OBJECT (store), CONFIG_STATUS_DATA,
428 config_status, g_free);
433 revert_to_saved (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter,
436 enum MimStatus status;
438 MimConfigStatus *config_status = data;
440 gtk_tree_model_get (model, iter, COL_LANG, &lang, COL_NAME, &name, -1);
443 gtk_tree_model_get (model, iter, COL_STATUS, &status, -1);
444 if (status != MIM_STATUS_MODIFIED)
446 minput_config_variable (lang, name, Mnil, NULL);
447 minput_config_command (lang, name, Mnil, NULL);
448 status = get_mim_status (lang, name);
449 update_child_row (model, iter, status, config_status, NULL);
454 set_as_saved (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter,
457 enum MimStatus status;
459 MimConfigStatus *config_status = data;
461 gtk_tree_model_get (model, iter, COL_LANG, &lang, COL_NAME, &name, -1);
464 gtk_tree_model_get (model, iter, COL_STATUS, &status, -1);
465 if (status != MIM_STATUS_MODIFIED)
467 status = get_mim_status (lang, name);
468 update_child_row (model, iter, status, config_status, NULL);
472 static int initialized = 0;
475 destroy_cb (GtkWidget *widget, gpointer data)
484 mim_config_new (GCallback func, gpointer data)
486 GtkWidget *tree, *config;
488 GtkCellRenderer *renderer;
489 GtkTreeViewColumn *column;
498 bindtextdomain ("m17n-im-config", GETTEXTDIR);
499 bind_textdomain_codeset ("m17n-im-config", "UTF-8");
502 mim_status_str[MIM_STATUS_DEFAULT] = _("default");
503 mim_status_str[MIM_STATUS_CUSTOMIZED] = _("customized");
504 mim_status_str[MIM_STATUS_MODIFIED] = _("modified");
505 mim_status_str[MIM_STATUS_NO] = _("uncustomizable");
507 store = make_store_for_input_methods ();
508 tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
509 g_object_unref (G_OBJECT (store));
511 renderer = gtk_cell_renderer_text_new ();
512 column = (gtk_tree_view_column_new_with_attributes
513 (_("Input Method"), renderer, "text", COL_TAG, NULL));
514 gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
516 renderer = gtk_cell_renderer_text_new ();
517 column = (gtk_tree_view_column_new_with_attributes
518 (_("status"), renderer, "text", COL_STATUS_STR, NULL));
519 gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
521 g_signal_connect (G_OBJECT (tree), "row-expanded",
522 G_CALLBACK (tree_expanded_cb), NULL);
523 g_signal_connect (G_OBJECT (tree), "row-activated",
524 G_CALLBACK (tree_activated_cb), NULL);
526 config =gtk_scrolled_window_new (NULL, NULL);
527 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (config),
528 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
529 gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (config), tree);
530 g_signal_connect (G_OBJECT (config), "destroy",
531 G_CALLBACK (destroy_cb), NULL);
533 g_object_set_data (G_OBJECT (config), CONFIG_TREE_VIEW, tree);
536 MimConfigCallback *callback;
538 callback = g_new (MimConfigCallback, 1);
539 callback->widget = config;
540 callback->func = (MimConfigCallbackFunc) func;
541 callback->data = data;
542 g_object_set_data_full (G_OBJECT (tree), CONFIG_CALLBACK_DATA,
550 mim_config_modified (GtkWidget *config)
554 MimConfigStatus *config_status;
556 tree = g_object_get_data (G_OBJECT (config), CONFIG_TREE_VIEW);
557 model = gtk_tree_view_get_model (tree);
558 config_status = g_object_get_data (G_OBJECT (model), CONFIG_STATUS_DATA);
560 return (config_status->num_modified > 0 ? TRUE : FALSE);
564 mim_config_revert (GtkWidget *config)
568 MimConfigStatus *config_status;
570 tree = g_object_get_data (G_OBJECT (config), CONFIG_TREE_VIEW);
571 model = gtk_tree_view_get_model (tree);
572 config_status = g_object_get_data (G_OBJECT (model), CONFIG_STATUS_DATA);
574 if (config_status->num_modified == 0)
576 gtk_tree_model_foreach (model, revert_to_saved, config_status);
581 mim_config_save (GtkWidget *config)
585 MimConfigStatus *config_status;
587 tree = g_object_get_data (G_OBJECT (config), CONFIG_TREE_VIEW);
588 model = gtk_tree_view_get_model (tree);
589 config_status = g_object_get_data (G_OBJECT (model), CONFIG_STATUS_DATA);
591 if (config_status->num_modified == 0)
593 minput_save_config ();
594 gtk_tree_model_foreach (model, set_as_saved, config_status);