7 #include "m17n-im-config.h"
9 #define _(String) dgettext (PACKAGE, String)
11 #define CONFIG_CALLBACK_DATA " config-callback-data"
12 #define CONFIG_STATUS_DATA " config-status-data"
13 #define CONFIG_TREE_VIEW " config-tree-view"
15 typedef struct _MimConfigStatus
17 /* Number of available input methods. */
19 /* Number of modified input methods. */
23 /* Status of variables and commands of an input method. */
28 MIM_STATUS_CUSTOMIZED,
34 static char *mim_status_str[MIM_STATUS_MAX];
37 get_mim_status (MSymbol lang, MSymbol name)
40 enum MimStatus status = MIM_STATUS_NO;
42 for (plist = minput_get_variable (lang, name, Mnil);
43 plist && mplist_key (plist) != Mnil; plist = mplist_next (plist))
45 MPlist *p = mplist_value (plist);
46 MSymbol status_symbol;
48 status = MIM_STATUS_DEFAULT;
49 p = mplist_next (mplist_next (p));
50 status_symbol = mplist_value (p);
51 if (status_symbol != Mnil && status_symbol != Minherited)
52 return (status_symbol == Mcustomized
53 ? MIM_STATUS_CUSTOMIZED : MIM_STATUS_MODIFIED);
55 for (plist = minput_get_command (lang, name, Mnil);
56 plist && mplist_key (plist) != Mnil; plist = mplist_next (plist))
58 MPlist *p = mplist_value (plist);
59 MSymbol status_symbol;
61 status = MIM_STATUS_DEFAULT;
62 p = mplist_next (mplist_next (p));
63 status_symbol = mplist_value (p);
64 if (status_symbol != Mnil && status_symbol != Minherited)
65 return (status_symbol == Mcustomized
66 ? MIM_STATUS_CUSTOMIZED : MIM_STATUS_MODIFIED);
71 /* Columns of each row. */
74 /* parent: language name
77 /* parent: NULL or "modified"
78 child: "default", "customized", or "modified" */
80 /* parent: num of modified children
81 child: enum MimStatus */
84 child: symbolic language name. */
87 child: symbolic IM name. */
89 /* number of columns */
93 /* Called when a row is expanded. We may have to initialize
96 tree_expanded_cb (GtkTreeView *tree, GtkTreeIter *parent,
97 GtkTreePath *path, gpointer data)
103 model = gtk_tree_view_get_model (tree);
104 if (gtk_tree_model_iter_children (model, &iter, parent))
108 gtk_tree_model_get (model, &iter, COL_STATUS_STR, &status_str, -1);
111 /* The first child is not yet initialized, and that means
112 the remaining children are not initialized either. */
113 gtk_tree_model_get (model, &iter, COL_LANG, &lang, -1);
115 enum MimStatus im_status;
117 gtk_tree_model_get (model, &iter, COL_NAME, &name, -1);
118 im_status = get_mim_status (lang, name);
119 gtk_tree_store_set (GTK_TREE_STORE (model), &iter,
120 COL_STATUS_STR, mim_status_str[im_status],
121 COL_STATUS, im_status,
123 } while (gtk_tree_model_iter_next (model, &iter));
129 edit_im (GtkTreeView *tree, MSymbol lang, MSymbol name)
131 GtkWidget *dialog, *label;
134 dialog = (gtk_dialog_new_with_buttons
136 GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (tree))),
137 GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR,
139 GTK_STOCK_REVERT_TO_SAVED, GTK_RESPONSE_NO,
140 GTK_STOCK_EDIT, GTK_RESPONSE_YES,
141 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
143 label = gtk_label_new (msymbol_name (name));
144 gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), label);
145 gtk_widget_show_all (dialog);
146 response = gtk_dialog_run (GTK_DIALOG (dialog));
147 if (response != GTK_RESPONSE_CANCEL
148 && lang == Mt && (name == Mnil || name == msymbol ("unicode")))
150 MSymbol command = msymbol (name == Mnil ? "commit" : "start");
152 if (response == GTK_RESPONSE_NO)
154 minput_config_command (lang, name, command, NULL);
156 else if (response == 0)
158 MPlist *plist = mplist ();
160 minput_config_command (lang, name, command, plist);
161 m17n_object_unref (plist);
167 cmd = minput_get_command (lang, name, command);
170 MPlist *plist, *key_seq_list, *key_seq;
172 plist = mplist_next (mplist_next (mplist_next (mplist_value (cmd))));
173 key_seq_list = mplist_copy (plist);
175 mplist_add (key_seq, Msymbol, msymbol ("C-x"));
176 mplist_add (key_seq, Msymbol, msymbol ("t"));
177 mplist_add (key_seq_list, Mplist, key_seq);
178 m17n_object_unref (key_seq);
179 minput_config_command (lang, name, command, key_seq_list);
180 m17n_object_unref (key_seq_list);
185 gtk_widget_destroy (dialog);
189 update_child_row (GtkTreeModel *model, GtkTreeIter *iter,
190 enum MimStatus status, MimConfigStatus *config_status,
196 inc_modified = (status == MIM_STATUS_MODIFIED ? 1 : -1);
198 gtk_tree_store_set (GTK_TREE_STORE (model), iter,
199 COL_STATUS_STR, mim_status_str[status],
200 COL_STATUS, status, -1);
201 if (gtk_tree_model_iter_parent (model, &parent, iter))
206 gtk_tree_model_get (model, &parent, COL_STATUS, &num_modified, -1);
207 num_modified += inc_modified;
208 gtk_tree_store_set (GTK_TREE_STORE (model), &parent,
209 COL_STATUS, num_modified, -1);
210 if (num_modified <= 1)
212 status_str = (status == MIM_STATUS_MODIFIED
213 ? mim_status_str[MIM_STATUS_MODIFIED] : NULL);
214 gtk_tree_store_set (GTK_TREE_STORE (model), &parent,
215 COL_STATUS_STR, status_str);
220 config_status = g_object_get_data (G_OBJECT (model), CONFIG_STATUS_DATA);
221 config_status->num_modified += inc_modified;
222 if (tree && config_status->num_modified <= 1)
224 MimConfigCallback *callback;
226 callback = g_object_get_data (G_OBJECT (tree), CONFIG_CALLBACK_DATA);
228 callback->func (config_status->num_modified == 0 ? FALSE : TRUE,
234 tree_activated_cb (GtkTreeView *tree, GtkTreePath *path,
235 GtkTreeViewColumn *column, gpointer data)
240 model = gtk_tree_view_get_model (tree);
241 if (gtk_tree_model_get_iter (model, &iter, path))
245 gtk_tree_model_get (model, &iter, COL_LANG, &lang, COL_NAME, &name, -1);
248 /* child row for an IM */
249 enum MimStatus old, new;
251 old = get_mim_status (lang, name);
252 edit_im (tree, lang, name);
253 new = get_mim_status (lang, name);
255 update_child_row (model, &iter, new, NULL, tree);
259 /* parent row for a language */
260 if (gtk_tree_view_row_expanded (tree, path))
261 gtk_tree_view_collapse_row (tree, path);
263 gtk_tree_view_expand_row (tree, path, TRUE);
269 config_deleted_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
275 typedef struct _MimTable
284 sort_im (const void *p1, const void *p2)
286 const MimTable *t1 = p1;
287 const MimTable *t2 = p2;
288 int result = strcmp (t1->lang, t2->lang);
290 return (result ? result : strcmp (t1->name, t2->name));
293 static GtkTreeStore *
294 make_store_for_input_methods ()
301 GtkTreeIter iter1, iter2;
302 enum MimStatus status;
303 MimConfigStatus *config_status;
305 store = gtk_tree_store_new (NUM_COLS,
306 G_TYPE_STRING, /* COL_TAG */
307 G_TYPE_STRING, /* COL_STATUS_STR */
308 G_TYPE_UINT, /* COL_STATUS */
309 G_TYPE_POINTER, /* COL_LANG */
310 G_TYPE_POINTER /* COL_NAME */
313 config_status = g_new0 (MimConfigStatus, 1);
314 gtk_tree_store_append (store, &iter1, NULL);
315 status = get_mim_status (Mt, Mnil);
316 gtk_tree_store_set (store, &iter1,
317 COL_TAG, _("global"),
318 COL_STATUS_STR, mim_status_str[status],
324 imlist = mdatabase_list (msymbol ("input-method"), Mnil, Mnil, Mnil);
325 config_status->num_im = mplist_length (imlist);
326 imtable = g_newa (MimTable, config_status->num_im);
327 for (i = 0, p = imlist; mplist_key (p) != Mnil; p = mplist_next (p))
329 MDatabase *mdb = (MDatabase *) mplist_value (p);
330 MSymbol *tag = mdatabase_tag (mdb);
332 if (tag[1] != Mnil && tag[2] != Mnil)
334 MSymbol language = mlanguage_name (tag[1]);
336 if (language != Mnil)
337 imtable[i].lang = msymbol_name (language);
339 /* `~' is for putting this element at the tail by sort. */
340 imtable[i].lang = "~other";
341 imtable[i].name = msymbol_name (tag[2]);
342 imtable[i].symlang = tag[1];
343 imtable[i].symname = tag[2];
347 m17n_object_unref (imlist);
348 config_status->num_im = i;
349 qsort (imtable, config_status->num_im, sizeof (MimTable), sort_im);
351 for (lang = NULL, i = 0; i < config_status->num_im; i++)
353 if (lang != imtable[i].lang)
357 gtk_tree_store_append (store, &iter1, NULL);
358 lang = imtable[i].lang;
362 gchar *native = NULL;
365 if (imtable[i].symlang != Mt
366 && (native_text = mlanguage_text (imtable[i].symlang)))
368 enum MTextFormat fmt;
370 native = mtext_data (native_text, &fmt, &nbytes,
372 if (fmt != MTEXT_FORMAT_US_ASCII
373 && fmt != MTEXT_FORMAT_UTF_8)
378 name = alloca (strlen (lang) + nbytes + 4);
379 sprintf (name, "%s (%s)", lang, native);
387 gtk_tree_store_set (store, &iter1,
389 COL_STATUS_STR, NULL,
395 gtk_tree_store_append (store, &iter2, &iter1);
396 gtk_tree_store_set (store, &iter2,
397 COL_TAG, imtable[i].name,
398 COL_STATUS_STR, NULL,
399 COL_LANG, imtable[i].symlang,
400 COL_NAME, imtable[i].symname,
403 config_status->num_modified = 0;
404 g_object_set_data_full (G_OBJECT (store), CONFIG_STATUS_DATA,
405 config_status, g_free);
410 revert_to_saved (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter,
413 enum MimStatus status;
415 MimConfigStatus *config_status = data;
417 gtk_tree_model_get (model, iter, COL_LANG, &lang, COL_NAME, &name, -1);
420 gtk_tree_model_get (model, iter, COL_STATUS, &status, -1);
421 if (status != MIM_STATUS_MODIFIED)
423 minput_config_variable (lang, name, Mnil, NULL);
424 minput_config_command (lang, name, Mnil, NULL);
425 status = get_mim_status (lang, name);
426 update_child_row (model, iter, status, config_status, NULL);
431 set_as_saved (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter,
434 enum MimStatus status;
436 MimConfigStatus *config_status = data;
438 gtk_tree_model_get (model, iter, COL_LANG, &lang, COL_NAME, &name, -1);
441 gtk_tree_model_get (model, iter, COL_STATUS, &status, -1);
442 if (status != MIM_STATUS_MODIFIED)
444 status = get_mim_status (lang, name);
445 update_child_row (model, iter, status, config_status, NULL);
453 mim_config_widget (MimConfigCallback *callback)
455 GtkWidget *tree, *config;
457 GtkCellRenderer *renderer;
458 GtkTreeViewColumn *column;
463 bindtextdomain ("m17n-im-config", GETTEXTDIR);
464 bind_textdomain_codeset ("m17n-im-config", "UTF-8");
467 mim_status_str[MIM_STATUS_DEFAULT] = _("default");
468 mim_status_str[MIM_STATUS_CUSTOMIZED] = _("customized");
469 mim_status_str[MIM_STATUS_MODIFIED] = _("modified");
470 mim_status_str[MIM_STATUS_NO] = _("uncustomizable");
472 store = make_store_for_input_methods ();
473 tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
474 g_object_unref (G_OBJECT (store));
476 g_object_set_data (G_OBJECT (tree), CONFIG_CALLBACK_DATA, callback);
478 renderer = gtk_cell_renderer_text_new ();
479 column = (gtk_tree_view_column_new_with_attributes
480 (_("Input Method"), renderer, "text", COL_TAG, NULL));
481 gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
483 renderer = gtk_cell_renderer_text_new ();
484 column = (gtk_tree_view_column_new_with_attributes
485 (_("status"), renderer, "text", COL_STATUS_STR, NULL));
486 gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
488 g_signal_connect (G_OBJECT (tree), "row-expanded",
489 G_CALLBACK (tree_expanded_cb), NULL);
490 g_signal_connect (G_OBJECT (tree), "row-activated",
491 G_CALLBACK (tree_activated_cb), NULL);
493 config =gtk_scrolled_window_new (NULL, NULL);
494 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (config),
495 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
496 gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (config), tree);
497 g_signal_connect (G_OBJECT (config), "delete_event",
498 G_CALLBACK (config_deleted_cb), NULL);
500 g_object_set_data (G_OBJECT (config), CONFIG_TREE_VIEW, tree);
506 mim_config_modified (GtkWidget *config)
510 MimConfigStatus *config_status;
512 tree = g_object_get_data (G_OBJECT (config), CONFIG_TREE_VIEW);
513 model = gtk_tree_view_get_model (tree);
514 config_status = g_object_get_data (G_OBJECT (model), CONFIG_STATUS_DATA);
516 return (config_status->num_modified > 0 ? TRUE : FALSE);
520 mim_config_revert (GtkWidget *config)
524 MimConfigStatus *config_status;
526 tree = g_object_get_data (G_OBJECT (config), CONFIG_TREE_VIEW);
527 model = gtk_tree_view_get_model (tree);
528 config_status = g_object_get_data (G_OBJECT (model), CONFIG_STATUS_DATA);
530 if (config_status->num_modified == 0)
532 gtk_tree_model_foreach (model, revert_to_saved, config_status);
537 mim_config_save (GtkWidget *config)
541 MimConfigStatus *config_status;
543 tree = g_object_get_data (G_OBJECT (config), CONFIG_TREE_VIEW);
544 model = gtk_tree_view_get_model (tree);
545 config_status = g_object_get_data (G_OBJECT (model), CONFIG_STATUS_DATA);
547 if (config_status->num_modified == 0)
549 minput_save_config ();
550 gtk_tree_model_foreach (model, set_as_saved, config_status);