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 variable = msymbol (name == Mnil ? "candidates-group-size"
153 if (response == GTK_RESPONSE_NO)
155 minput_config_variable (lang, name, variable, NULL);
157 else if (response == 0)
159 MPlist *plist = mplist ();
161 minput_config_variable (lang, name, variable, plist);
162 m17n_object_unref (plist);
166 MPlist *plist = mplist ();
170 mplist_add (plist, Minteger, (void *) 3);
174 MText *mt = mtext_from_data (">>", 2, MTEXT_FORMAT_US_ASCII);
175 mplist_add (plist, Mtext, mt);
176 m17n_object_unref (mt);
178 minput_config_variable (lang, name, variable, plist);
179 m17n_object_unref (plist);
183 gtk_widget_destroy (dialog);
187 update_child_row (GtkTreeModel *model, GtkTreeIter *iter,
188 enum MimStatus status, MimConfigStatus *config_status,
194 inc_modified = (status == MIM_STATUS_MODIFIED ? 1 : -1);
196 gtk_tree_store_set (GTK_TREE_STORE (model), iter,
197 COL_STATUS_STR, mim_status_str[status],
198 COL_STATUS, status, -1);
199 if (gtk_tree_model_iter_parent (model, &parent, iter))
204 gtk_tree_model_get (model, &parent, COL_STATUS, &num_modified, -1);
205 num_modified += inc_modified;
206 gtk_tree_store_set (GTK_TREE_STORE (model), &parent,
207 COL_STATUS, num_modified, -1);
208 if (num_modified <= 1)
210 status_str = (status == MIM_STATUS_MODIFIED
211 ? mim_status_str[MIM_STATUS_MODIFIED] : NULL);
212 gtk_tree_store_set (GTK_TREE_STORE (model), &parent,
213 COL_STATUS_STR, status_str);
218 config_status = g_object_get_data (G_OBJECT (model), CONFIG_STATUS_DATA);
219 config_status->num_modified += inc_modified;
220 if (tree && config_status->num_modified <= 1)
222 MimConfigCallback *callback;
224 callback = g_object_get_data (G_OBJECT (tree), CONFIG_CALLBACK_DATA);
226 callback->func (config_status->num_modified == 0 ? FALSE : TRUE,
232 tree_activated_cb (GtkTreeView *tree, GtkTreePath *path,
233 GtkTreeViewColumn *column, gpointer data)
238 model = gtk_tree_view_get_model (tree);
239 if (gtk_tree_model_get_iter (model, &iter, path))
243 gtk_tree_model_get (model, &iter, COL_LANG, &lang, COL_NAME, &name, -1);
246 /* child row for an IM */
247 enum MimStatus old, new;
249 old = get_mim_status (lang, name);
250 edit_im (tree, lang, name);
251 new = get_mim_status (lang, name);
253 update_child_row (model, &iter, new, NULL, tree);
257 /* parent row for a language */
258 if (gtk_tree_view_row_expanded (tree, path))
259 gtk_tree_view_collapse_row (tree, path);
261 gtk_tree_view_expand_row (tree, path, TRUE);
267 config_deleted_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
273 typedef struct _MimTable
282 sort_im (const void *p1, const void *p2)
284 const MimTable *t1 = p1;
285 const MimTable *t2 = p2;
286 int result = strcmp (t1->lang, t2->lang);
288 return (result ? result : strcmp (t1->name, t2->name));
291 static GtkTreeStore *
292 make_store_for_input_methods ()
299 GtkTreeIter iter1, iter2;
300 enum MimStatus status;
301 MimConfigStatus *config_status;
303 store = gtk_tree_store_new (NUM_COLS,
304 G_TYPE_STRING, /* COL_TAG */
305 G_TYPE_STRING, /* COL_STATUS_STR */
306 G_TYPE_UINT, /* COL_STATUS */
307 G_TYPE_POINTER, /* COL_LANG */
308 G_TYPE_POINTER /* COL_NAME */
311 config_status = g_new0 (MimConfigStatus, 1);
312 gtk_tree_store_append (store, &iter1, NULL);
313 status = get_mim_status (Mt, Mnil);
314 gtk_tree_store_set (store, &iter1,
315 COL_TAG, _("global"),
316 COL_STATUS_STR, mim_status_str[status],
322 imlist = mdatabase_list (msymbol ("input-method"), Mnil, Mnil, Mnil);
323 config_status->num_im = mplist_length (imlist);
324 imtable = g_newa (MimTable, config_status->num_im);
325 for (i = 0, p = imlist; mplist_key (p) != Mnil; p = mplist_next (p))
327 MDatabase *mdb = (MDatabase *) mplist_value (p);
328 MSymbol *tag = mdatabase_tag (mdb);
330 if (tag[1] != Mnil && tag[2] != Mnil)
332 MSymbol language = mlanguage_name (tag[1]);
334 if (language != Mnil)
335 imtable[i].lang = msymbol_name (language);
337 /* `~' is for putting this element at the tail by sort. */
338 imtable[i].lang = "~other";
339 imtable[i].name = msymbol_name (tag[2]);
340 imtable[i].symlang = tag[1];
341 imtable[i].symname = tag[2];
345 m17n_object_unref (imlist);
346 config_status->num_im = i;
347 qsort (imtable, config_status->num_im, sizeof (MimTable), sort_im);
349 for (lang = NULL, i = 0; i < config_status->num_im; i++)
351 if (lang != imtable[i].lang)
355 gtk_tree_store_append (store, &iter1, NULL);
356 lang = imtable[i].lang;
360 gchar *native = NULL;
363 if (imtable[i].symlang != Mt
364 && (native_text = mlanguage_text (imtable[i].symlang)))
366 enum MTextFormat fmt;
368 native = mtext_data (native_text, &fmt, &nbytes,
370 if (fmt != MTEXT_FORMAT_US_ASCII
371 && fmt != MTEXT_FORMAT_UTF_8)
376 name = alloca (strlen (lang) + nbytes + 4);
377 sprintf (name, "%s (%s)", lang, native);
385 gtk_tree_store_set (store, &iter1,
387 COL_STATUS_STR, NULL,
393 gtk_tree_store_append (store, &iter2, &iter1);
394 gtk_tree_store_set (store, &iter2,
395 COL_TAG, imtable[i].name,
396 COL_STATUS_STR, NULL,
397 COL_LANG, imtable[i].symlang,
398 COL_NAME, imtable[i].symname,
401 config_status->num_modified = 0;
402 g_object_set_data_full (G_OBJECT (store), CONFIG_STATUS_DATA,
403 config_status, g_free);
408 revert_to_saved (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter,
411 enum MimStatus status;
413 MimConfigStatus *config_status = data;
415 gtk_tree_model_get (model, iter, COL_LANG, &lang, COL_NAME, &name, -1);
418 gtk_tree_model_get (model, iter, COL_STATUS, &status, -1);
419 if (status != MIM_STATUS_MODIFIED)
421 minput_config_variable (lang, name, Mnil, NULL);
422 minput_config_command (lang, name, Mnil, NULL);
423 status = get_mim_status (lang, name);
424 update_child_row (model, iter, status, config_status, NULL);
429 set_as_saved (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter,
432 enum MimStatus status;
434 MimConfigStatus *config_status = data;
436 gtk_tree_model_get (model, iter, COL_LANG, &lang, COL_NAME, &name, -1);
439 gtk_tree_model_get (model, iter, COL_STATUS, &status, -1);
440 if (status != MIM_STATUS_MODIFIED)
442 status = get_mim_status (lang, name);
443 update_child_row (model, iter, status, config_status, NULL);
451 mim_config_widget (MimConfigCallback *callback)
453 GtkWidget *tree, *config;
455 GtkCellRenderer *renderer;
456 GtkTreeViewColumn *column;
461 bindtextdomain ("m17n-im-config", GETTEXTDIR);
462 bind_textdomain_codeset ("m17n-im-config", "UTF-8");
465 mim_status_str[MIM_STATUS_DEFAULT] = _("default");
466 mim_status_str[MIM_STATUS_CUSTOMIZED] = _("customized");
467 mim_status_str[MIM_STATUS_MODIFIED] = _("modified");
468 mim_status_str[MIM_STATUS_NO] = _("uncustomizable");
470 store = make_store_for_input_methods ();
471 tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
472 g_object_unref (G_OBJECT (store));
474 g_object_set_data (G_OBJECT (tree), CONFIG_CALLBACK_DATA, callback);
476 renderer = gtk_cell_renderer_text_new ();
477 column = (gtk_tree_view_column_new_with_attributes
478 (_("Input Method"), renderer, "text", COL_TAG, NULL));
479 gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
481 renderer = gtk_cell_renderer_text_new ();
482 column = (gtk_tree_view_column_new_with_attributes
483 (_("status"), renderer, "text", COL_STATUS_STR, NULL));
484 gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
486 g_signal_connect (G_OBJECT (tree), "row-expanded",
487 G_CALLBACK (tree_expanded_cb), NULL);
488 g_signal_connect (G_OBJECT (tree), "row-activated",
489 G_CALLBACK (tree_activated_cb), NULL);
491 config =gtk_scrolled_window_new (NULL, NULL);
492 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (config),
493 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
494 gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (config), tree);
495 g_signal_connect (G_OBJECT (config), "destroy",
496 G_CALLBACK (config_deleted_cb), NULL);
498 g_object_set_data (G_OBJECT (config), CONFIG_TREE_VIEW, tree);
504 mim_config_modified (GtkWidget *config)
508 MimConfigStatus *config_status;
510 tree = g_object_get_data (G_OBJECT (config), CONFIG_TREE_VIEW);
511 model = gtk_tree_view_get_model (tree);
512 config_status = g_object_get_data (G_OBJECT (model), CONFIG_STATUS_DATA);
514 return (config_status->num_modified > 0 ? TRUE : FALSE);
518 mim_config_revert (GtkWidget *config)
522 MimConfigStatus *config_status;
524 tree = g_object_get_data (G_OBJECT (config), CONFIG_TREE_VIEW);
525 model = gtk_tree_view_get_model (tree);
526 config_status = g_object_get_data (G_OBJECT (model), CONFIG_STATUS_DATA);
528 if (config_status->num_modified == 0)
530 gtk_tree_model_foreach (model, revert_to_saved, config_status);
535 mim_config_save (GtkWidget *config)
539 MimConfigStatus *config_status;
541 tree = g_object_get_data (G_OBJECT (config), CONFIG_TREE_VIEW);
542 model = gtk_tree_view_get_model (tree);
543 config_status = g_object_get_data (G_OBJECT (model), CONFIG_STATUS_DATA);
545 if (config_status->num_modified == 0)
547 minput_save_config ();
548 gtk_tree_model_foreach (model, set_as_saved, config_status);