Imported Upstream version 0.4.1
Aron Xu
12 years ago
6 | 6 | |
7 | 7 | find_package(PkgConfig REQUIRED) |
8 | 8 | |
9 | if (ENABLE_GTK3) | |
10 | set(GLIB_REQUIRED_VERSION "2.28") | |
11 | else (ENABLE_GTK3) | |
12 | set(GLIB_REQUIRED_VERSION "2.26") | |
13 | endif (ENABLE_GTK3) | |
14 | ||
15 | PKG_CHECK_MODULES (GLIB2 "glib-2.0 >= ${GLIB_REQUIRED_VERSION}" REQUIRED) | |
9 | 16 | PKG_CHECK_MODULES (GIO2 "gio-2.0 >= 2.26" REQUIRED) |
10 | 17 | PKG_CHECK_MODULES(ISO_CODES "iso-codes" REQUIRED) |
11 | 18 | _pkgconfig_invoke("iso-codes" ISO_CODES PREFIX "" "--variable=prefix") |
31 | 38 | set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--no-undefined,--as-needed ${CMAKE_SHARED_LINKER_FLAGS}") |
32 | 39 | set(CMAKE_MODULE_LINKER_FLAGS "-Wl,--no-undefined,--as-needed ${CMAKE_MODULE_LINKER_FLAGS}") |
33 | 40 | |
41 | string(REGEX REPLACE "^([0-9]+)\\.[0-9]+\\.[0-9]+.*" "\\1" FCITX4_MAJOR_VERSION "${FCITX4_VERSION}") | |
42 | string(REGEX REPLACE "^[0-9]+\\.([0-9]+)\\.[0-9]+.*" "\\1" FCITX4_MINOR_VERSION "${FCITX4_VERSION}") | |
43 | string(REGEX REPLACE "^[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" FCITX4_PATCH_VERSION "${FCITX4_VERSION}") | |
44 | ||
34 | 45 | set(datadir ${CMAKE_INSTALL_PREFIX}/share) |
35 | 46 | set(localedir ${CMAKE_INSTALL_PREFIX}/share/locale) |
36 | 47 | set(exec_prefix "${CMAKE_INSTALL_PREFIX}") |
3 | 3 | #define EXEC_PREFIX "@exec_prefix@" |
4 | 4 | #define PACKAGE "@FCITX4_PACKAGE_NAME@" |
5 | 5 | #define LIBLOCALEDIR "@liblocaledir@" |
6 | #define ISO_CODES_PREFIX "@ISO_CODES_PREFIX@"⏎ | |
6 | #define ISO_CODES_PREFIX "@ISO_CODES_PREFIX@" | |
7 | #define FCITX4_MAJOR_VERSION @FCITX4_MAJOR_VERSION@ | |
8 | #define FCITX4_MINOR_VERSION @FCITX4_MINOR_VERSION@ | |
9 | #define FCITX4_PATCH_VERSION @FCITX4_PATCH_VERSION@ | |
10 | ||
11 | #define FCITX_CHECK_VERSION(major,minor,micro) \ | |
12 | (FCITX4_MAJOR_VERSION > (major) || \ | |
13 | (FCITX4_MAJOR_VERSION == (major) && FCITX4_MINOR_VERSION > (minor)) || \ | |
14 | (FCITX4_MAJOR_VERSION == (major) && FCITX4_MINOR_VERSION == (minor) && \ | |
15 | FCITX4_PATCH_VERSION >= (micro)))⏎ |
2 | 2 | |
3 | 3 | include_directories ( |
4 | 4 | ${GTK2_INCLUDE_DIRS} |
5 | ${GLIB2_INCLUDE_DIRS} | |
5 | 6 | ${GIO2_INCLUDE_DIRS} |
6 | 7 | ${FCITX4_FCITX_INCLUDE_DIRS} |
7 | 8 | ${FCITX4_FCITX_UTILS_INCLUDE_DIRS} |
10 | 11 | |
11 | 12 | link_directories ( |
12 | 13 | ${GTK2_LIBRARY_DIRS} |
14 | ${GLIB2_LIBRARY_DIRS} | |
13 | 15 | ${GIO2_LIBRARY_DIRS} |
14 | 16 | ${FCITX4_FCITX_LIBRARY_DIRS} |
15 | 17 | ${FCITX4_FCITX_UTILS_LIBRARY_DIRS} |
42 | 44 | ${FCITX4_FCITX_UTILS_LIBRARIES} |
43 | 45 | ${FCITX4_FCITX_CONFIG_LIBRARIES} |
44 | 46 | ${FCITX4_FCITX_LIBRARIES} |
47 | ${GLIB2_LIBRARIES} | |
45 | 48 | ${GIO2_LIBRARIES} |
46 | 49 | ) |
47 | 50 |
0 | 0 | /*************************************************************************** |
1 | * Copyright (C) 2010~2011 by CSSlayer * | |
1 | * Copyright (C) 2010~2012 by CSSlayer * | |
2 | 2 | * * |
3 | 3 | * This program is free software; you can redistribute it and/or modify * |
4 | 4 | * it under the terms of the GNU General Public License as published by * |
0 | 0 | /*************************************************************************** |
1 | * Copyright (C) 2010~2011 by CSSlayer * | |
1 | * Copyright (C) 2010~2012 by CSSlayer * | |
2 | 2 | * * |
3 | 3 | * This program is free software; you can redistribute it and/or modify * |
4 | 4 | * it under the terms of the GNU General Public License as published by * |
131 | 131 | bind_textdomain_codeset(cfdesc->domain, "UTF-8"); |
132 | 132 | |
133 | 133 | FILE *fp; |
134 | fp = FcitxXDGGetFileWithPrefix(self->prefix, self->name, "rt", NULL); | |
134 | fp = FcitxXDGGetFileWithPrefix(self->prefix, self->name, "r", NULL); | |
135 | 135 | self->gconfig.configFile = FcitxConfigParseConfigFileFp(fp, cfdesc); |
136 | 136 | |
137 | 137 | FcitxConfigGroupDesc *cgdesc = NULL; |
143 | 143 | if (codesc == NULL) |
144 | 144 | continue; |
145 | 145 | |
146 | GtkWidget* hbox = gtk_hbox_new(FALSE, 0); | |
146 | 147 | GtkWidget *table = gtk_table_new(2, HASH_COUNT(codesc), FALSE); |
147 | 148 | GtkWidget *plabel = gtk_label_new(D_(cfdesc->domain, cgdesc->groupName)); |
148 | 149 | GtkWidget *scrollwnd = gtk_scrolled_window_new(NULL, NULL); |
149 | GtkWidget *viewport = gtk_viewport_new(NULL, NULL); | |
150 | ||
151 | gtk_container_set_border_width(GTK_CONTAINER(table), 4); | |
152 | gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollwnd), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); | |
153 | gtk_container_add(GTK_CONTAINER(scrollwnd), viewport); | |
154 | gtk_container_add(GTK_CONTAINER(viewport), table); | |
150 | ||
151 | gtk_container_set_border_width(GTK_CONTAINER(table), 0); | |
152 | gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollwnd), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); | |
153 | gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrollwnd), table); | |
154 | gtk_box_pack_start(GTK_BOX(hbox), scrollwnd, TRUE, TRUE, 0); | |
155 | 155 | gtk_notebook_append_page(GTK_NOTEBOOK(configNotebook), |
156 | scrollwnd, | |
156 | hbox, | |
157 | 157 | plabel); |
158 | 158 | |
159 | 159 | int i = 0; |
504 | 504 | FcitxConfigResetConfigToDefaultValue(&config_widget->gconfig); |
505 | 505 | FcitxConfigBindSync(&config_widget->gconfig); |
506 | 506 | } else if (action == CONFIG_WIDGET_SAVE) { |
507 | FILE* fp = FcitxXDGGetFileUserWithPrefix(config_widget->prefix, config_widget->name, "wt", NULL); | |
507 | FILE* fp = FcitxXDGGetFileUserWithPrefix(config_widget->prefix, config_widget->name, "w", NULL); | |
508 | 508 | |
509 | 509 | if (fp) { |
510 | 510 | FcitxConfigSaveConfigFileFp(fp, &config_widget->gconfig, config_widget->cfdesc); |
0 | 0 | /*************************************************************************** |
1 | * Copyright (C) 2010~2011 by CSSlayer * | |
1 | * Copyright (C) 2010~2012 by CSSlayer * | |
2 | 2 | * * |
3 | 3 | * This program is free software; you can redistribute it and/or modify * |
4 | 4 | * it under the terms of the GNU General Public License as published by * |
0 | 0 | /*************************************************************************** |
1 | * Copyright (C) 2010~2011 by CSSlayer * | |
1 | * Copyright (C) 2010~2012 by CSSlayer * | |
2 | 2 | * * |
3 | 3 | * This program is free software; you can redistribute it and/or modify * |
4 | 4 | * it under the terms of the GNU General Public License as published by * |
0 | 0 | /*************************************************************************** |
1 | * Copyright (C) 2010~2011 by CSSlayer * | |
1 | * Copyright (C) 2010~2012 by CSSlayer * | |
2 | 2 | * * |
3 | 3 | * This program is free software; you can redistribute it and/or modify * |
4 | 4 | * it under the terms of the GNU General Public License as published by * |
0 | 0 | /*************************************************************************** |
1 | * Copyright (C) 2010~2011 by CSSlayer * | |
1 | * Copyright (C) 2010~2012 by CSSlayer * | |
2 | 2 | * * |
3 | 3 | * This program is free software; you can redistribute it and/or modify * |
4 | 4 | * it under the terms of the GNU General Public License as published by * |
83 | 83 | GPtrArray *array = NULL; |
84 | 84 | GVariant* value; |
85 | 85 | GVariantIter *iter; |
86 | const gchar *name, *unique_name, *langcode; | |
86 | gchar *name, *unique_name, *langcode; | |
87 | 87 | gboolean enable; |
88 | 88 | value = g_dbus_proxy_get_cached_property(G_DBUS_PROXY(im), "IMList"); |
89 | 89 | |
119 | 119 | item->unique_name = strdup(unique_name); |
120 | 120 | item->langcode = strdup(langcode); |
121 | 121 | g_ptr_array_add(array, item); |
122 | g_free(name); | |
123 | g_free(unique_name); | |
124 | g_free(langcode); | |
122 | 125 | } |
123 | 126 | g_variant_iter_free(iter); |
124 | 127 |
0 | 0 | /*************************************************************************** |
1 | * Copyright (C) 2010~2011 by CSSlayer * | |
1 | * Copyright (C) 2010~2012 by CSSlayer * | |
2 | 2 | * * |
3 | 3 | * This program is free software; you can redistribute it and/or modify * |
4 | 4 | * it under the terms of the GNU General Public License as published by * |
0 | 0 | /*************************************************************************** |
1 | * Copyright (C) 2010~2011 by CSSlayer * | |
1 | * Copyright (C) 2010~2012 by CSSlayer * | |
2 | 2 | * * |
3 | 3 | * This program is free software; you can redistribute it and/or modify * |
4 | 4 | * it under the terms of the GNU General Public License as published by * |
28 | 28 | G_DEFINE_TYPE(FcitxImWidget, fcitx_im_widget, GTK_TYPE_HBOX) |
29 | 29 | |
30 | 30 | enum { |
31 | LIST_IM_STRING, | |
32 | LIST_IM, | |
33 | N_COLUMNS | |
31 | AVAIL_TREE_IM_STRING, | |
32 | AVAIL_TREE_IM, | |
33 | AVAIL_TREE_LANG, | |
34 | AVAIL_N_COLUMNS | |
34 | 35 | }; |
36 | ||
37 | enum { | |
38 | IM_LIST_IM_STRING, | |
39 | IM_LIST_IM, | |
40 | IM_N_COLUMNS | |
41 | }; | |
42 | ||
43 | typedef struct { | |
44 | FcitxImWidget* widget; | |
45 | GHashTable* langTable; | |
46 | } foreach_ct; | |
35 | 47 | |
36 | 48 | static void fcitx_im_widget_finalize(GObject* object); |
37 | 49 | static void _fcitx_im_widget_connect(FcitxImWidget* self); |
49 | 61 | GtkTreeIter *iter, |
50 | 62 | gpointer data); |
51 | 63 | static const gchar* _get_current_lang(); |
64 | static void icon_press_cb (GtkEntry *entry, | |
65 | gint position, | |
66 | GdkEventButton *event, | |
67 | gpointer data); | |
52 | 68 | |
53 | 69 | static void |
54 | 70 | fcitx_im_widget_class_init(FcitxImWidgetClass *klass) |
60 | 76 | static void |
61 | 77 | fcitx_im_widget_init(FcitxImWidget* self) |
62 | 78 | { |
63 | self->availimstore = gtk_list_store_new(N_COLUMNS, G_TYPE_STRING, G_TYPE_POINTER); | |
79 | self->availimstore = gtk_tree_store_new(AVAIL_N_COLUMNS, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_STRING); | |
64 | 80 | self->filtermodel = gtk_tree_model_filter_new(GTK_TREE_MODEL(self->availimstore), NULL); |
65 | 81 | |
66 | 82 | gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(self->filtermodel), |
68 | 84 | self , |
69 | 85 | NULL); |
70 | 86 | self->sortmodel = gtk_tree_model_sort_new_with_model(self->filtermodel); |
71 | gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(self->sortmodel), LIST_IM_STRING, GTK_SORT_ASCENDING); | |
87 | gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(self->sortmodel), AVAIL_TREE_IM_STRING, GTK_SORT_ASCENDING); | |
72 | 88 | self->availimview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(self->sortmodel)); |
73 | 89 | |
74 | 90 | GtkWidget* label = gtk_label_new(_("Available Input Method")); |
75 | 91 | self->filterentry = gtk_entry_new(); |
92 | gtk_entry_set_icon_from_stock (GTK_ENTRY (self->filterentry), | |
93 | GTK_ENTRY_ICON_SECONDARY, | |
94 | GTK_STOCK_CLEAR); | |
76 | 95 | |
77 | 96 | GtkCellRenderer* renderer; |
78 | 97 | GtkTreeViewColumn* column; |
79 | 98 | renderer = gtk_cell_renderer_text_new(); |
80 | 99 | column = gtk_tree_view_column_new_with_attributes( |
81 | 100 | _("Input Method"), renderer, |
82 | "text", LIST_IM_STRING, | |
101 | "text", AVAIL_TREE_IM_STRING, | |
83 | 102 | NULL); |
84 | 103 | gtk_tree_view_append_column(GTK_TREE_VIEW(self->availimview), column); |
85 | 104 | |
124 | 143 | |
125 | 144 | label = gtk_label_new(_("Current Input Method")); |
126 | 145 | |
127 | self->imstore = gtk_list_store_new(N_COLUMNS, G_TYPE_STRING, G_TYPE_POINTER); | |
146 | self->imstore = gtk_list_store_new(IM_N_COLUMNS, G_TYPE_STRING, G_TYPE_POINTER); | |
128 | 147 | self->imview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(self->imstore)); |
129 | 148 | |
130 | 149 | renderer = gtk_cell_renderer_text_new(); |
131 | 150 | column = gtk_tree_view_column_new_with_attributes( |
132 | 151 | _("Input Method"), renderer, |
133 | "text", LIST_IM_STRING, | |
152 | "text", IM_LIST_IM_STRING, | |
134 | 153 | NULL); |
135 | 154 | gtk_tree_view_append_column(GTK_TREE_VIEW(self->imview), column); |
136 | 155 | |
171 | 190 | g_signal_connect(G_OBJECT(self->movedownbutton), "clicked", G_CALLBACK(_fcitx_im_widget_movedown_button_clicked), self); |
172 | 191 | g_signal_connect(G_OBJECT(self->filterentry), "changed", G_CALLBACK(_fcitx_im_widget_filtertext_changed), self); |
173 | 192 | g_signal_connect(G_OBJECT(self->onlycurlangcheckbox), "toggled", G_CALLBACK(_fcitx_im_widget_onlycurlangcheckbox_toggled), self); |
193 | g_signal_connect(G_OBJECT(self->filterentry), "icon-press", G_CALLBACK (icon_press_cb), NULL); | |
174 | 194 | |
175 | 195 | |
176 | 196 | _fcitx_im_widget_connect(self); |
188 | 208 | |
189 | 209 | void fcitx_im_widget_finalize(GObject* object) |
190 | 210 | { |
191 | ||
211 | FcitxImWidget* self = FCITX_IM_WIDGET(object); | |
212 | if (self->array) { | |
213 | g_ptr_array_set_free_func(self->array, fcitx_inputmethod_item_free); | |
214 | g_ptr_array_free(self->array, FALSE); | |
215 | self->array = NULL; | |
216 | } | |
217 | g_free(self->focus); | |
192 | 218 | } |
193 | 219 | |
194 | 220 | void _fcitx_im_widget_imlist_changed_cb(FcitxInputMethod* im, gpointer user_data) |
217 | 243 | |
218 | 244 | void _fcitx_im_widget_load(FcitxImWidget* self) |
219 | 245 | { |
220 | gtk_list_store_clear(self->availimstore); | |
246 | gtk_tree_store_clear(self->availimstore); | |
221 | 247 | gtk_list_store_clear(self->imstore); |
222 | 248 | |
223 | 249 | if (self->array) { |
228 | 254 | |
229 | 255 | self->array = fcitx_inputmethod_get_imlist(self->improxy); |
230 | 256 | |
231 | if (self->array) | |
232 | g_ptr_array_foreach(self->array, _fcitx_inputmethod_insert_foreach_cb, self); | |
257 | if (self->array) { | |
258 | foreach_ct ct; | |
259 | ct.widget = self; | |
260 | ct.langTable = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); | |
261 | g_ptr_array_foreach(self->array, _fcitx_inputmethod_insert_foreach_cb, &ct); | |
262 | g_hash_table_unref(ct.langTable); | |
263 | ||
264 | _fcitx_im_widget_im_selection_changed(gtk_tree_view_get_selection(GTK_TREE_VIEW(self->imview)), self); | |
265 | g_free(self->focus); | |
266 | self->focus = NULL; | |
267 | ||
268 | if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(self->onlycurlangcheckbox))) | |
269 | gtk_tree_view_expand_all (GTK_TREE_VIEW(self->availimview)); | |
270 | } | |
233 | 271 | } |
234 | 272 | |
235 | 273 | void _fcitx_inputmethod_insert_foreach_cb(gpointer data, |
236 | 274 | gpointer user_data) |
237 | 275 | { |
276 | foreach_ct* ct = user_data; | |
238 | 277 | FcitxIMItem* item = data; |
239 | FcitxImWidget* self = user_data; | |
240 | GtkTreeIter iter; | |
241 | ||
242 | if (item->enable) { | |
243 | gtk_list_store_append(self->imstore, &iter); | |
244 | gtk_list_store_set(self->imstore, &iter, LIST_IM_STRING, item->name, -1); | |
245 | gtk_list_store_set(self->imstore, &iter, LIST_IM, item, -1); | |
246 | } else { | |
278 | FcitxImWidget* self = ct->widget; | |
279 | GtkTreeIter iter; | |
280 | ||
281 | GtkTreeIter* langIter = g_hash_table_lookup(ct->langTable, item->langcode); | |
282 | ||
283 | if (langIter == NULL) { | |
284 | langIter = g_new(GtkTreeIter, 1); | |
285 | gtk_tree_store_append(self->availimstore, langIter, NULL); | |
286 | ||
247 | 287 | char* lang = NULL; |
248 | 288 | if (strlen(item->langcode) != 0) |
249 | 289 | lang = gdm_get_language_from_name(item->langcode, NULL); |
250 | if (!lang) | |
251 | lang = g_strdup_printf("%s", _("Unknown")); | |
252 | ||
253 | gchar* string = g_strdup_printf(_("%s - %s"), lang, item->name); | |
290 | if (!lang) { | |
291 | if (strcmp(item->langcode, "*") == 0) | |
292 | lang = g_strdup_printf("%s", _("Unknown")); | |
293 | else | |
294 | lang = g_strdup_printf("%s", _("Unknown")); | |
295 | } | |
296 | gtk_tree_store_set(self->availimstore, langIter, AVAIL_TREE_IM_STRING, lang, -1); | |
297 | gtk_tree_store_set(self->availimstore, langIter, AVAIL_TREE_LANG, item->langcode, -1); | |
298 | gtk_tree_store_set(self->availimstore, langIter, AVAIL_TREE_IM, NULL, -1); | |
299 | g_free(lang); | |
254 | 300 | |
255 | gtk_list_store_append(self->availimstore, &iter); | |
256 | gtk_list_store_set(self->availimstore, &iter, LIST_IM_STRING, string, -1); | |
257 | gtk_list_store_set(self->availimstore, &iter, LIST_IM, item, -1); | |
301 | g_hash_table_insert(ct->langTable, g_strdup(item->langcode), langIter); | |
302 | } | |
303 | ||
304 | if (item->enable) { | |
305 | gtk_list_store_append(self->imstore, &iter); | |
306 | gtk_list_store_set(self->imstore, &iter, IM_LIST_IM_STRING, item->name, -1); | |
307 | gtk_list_store_set(self->imstore, &iter, IM_LIST_IM, item, -1); | |
308 | if (g_strcmp0(self->focus, item->unique_name) == 0) { | |
309 | GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self->imview)); | |
310 | gtk_tree_selection_select_iter(selection, &iter); | |
311 | } | |
312 | } else { | |
258 | 313 | |
259 | g_free(lang); | |
260 | g_free(string); | |
314 | gtk_tree_store_append(self->availimstore, &iter, langIter); | |
315 | gtk_tree_store_set(self->availimstore, &iter, AVAIL_TREE_IM_STRING, item->name, -1); | |
316 | gtk_tree_store_set(self->availimstore, &iter, AVAIL_TREE_LANG, NULL, -1); | |
317 | gtk_tree_store_set(self->availimstore, &iter, AVAIL_TREE_IM, item, -1); | |
261 | 318 | } |
262 | 319 | |
263 | 320 | } |
295 | 352 | GtkTreeModel *model = gtk_tree_view_get_model(treeView); |
296 | 353 | GtkTreeIter iter; |
297 | 354 | |
355 | FcitxIMItem* item = NULL; | |
298 | 356 | if (gtk_tree_selection_get_selected(selection, &model, &iter)) { |
357 | gtk_tree_model_get(model, | |
358 | &iter, | |
359 | AVAIL_TREE_IM, &item, | |
360 | -1); | |
361 | } | |
362 | if (item) { | |
299 | 363 | gtk_widget_set_sensitive(self->addimbutton, TRUE); |
300 | 364 | } else { |
301 | 365 | gtk_widget_set_sensitive(self->addimbutton, FALSE); |
315 | 379 | FcitxIMItem* item = NULL; |
316 | 380 | gtk_tree_model_get(model, |
317 | 381 | &iter, |
318 | LIST_IM, &item, | |
382 | AVAIL_TREE_IM, &item, | |
319 | 383 | -1); |
384 | if (item == NULL) | |
385 | return; | |
320 | 386 | item->enable = true; |
321 | 387 | |
322 | 388 | g_ptr_array_remove(self->array, item); |
323 | 389 | g_ptr_array_add(self->array, item); |
390 | ||
391 | g_free(self->focus); | |
392 | self->focus = g_strdup(item->unique_name); | |
324 | 393 | |
325 | 394 | fcitx_inputmethod_set_imlist(self->improxy, self->array); |
326 | 395 | } |
338 | 407 | FcitxIMItem* item = NULL; |
339 | 408 | gtk_tree_model_get(model, |
340 | 409 | &iter, |
341 | LIST_IM, &item, | |
410 | IM_LIST_IM, &item, | |
342 | 411 | -1); |
343 | 412 | item->enable = false; |
344 | 413 | |
359 | 428 | FcitxIMItem* item = NULL; |
360 | 429 | gtk_tree_model_get(model, |
361 | 430 | &iter, |
362 | LIST_IM, &item, | |
431 | IM_LIST_IM, &item, | |
363 | 432 | -1); |
364 | 433 | |
365 | 434 | int i; |
377 | 446 | gpointer temp = g_ptr_array_index(self->array, i); |
378 | 447 | g_ptr_array_index(self->array, i) = g_ptr_array_index(self->array, switch_index); |
379 | 448 | g_ptr_array_index(self->array, switch_index) = temp; |
449 | g_free(self->focus); | |
450 | self->focus = g_strdup(item->unique_name); | |
380 | 451 | |
381 | 452 | fcitx_inputmethod_set_imlist(self->improxy, self->array); |
382 | 453 | } |
395 | 466 | FcitxIMItem* item = NULL; |
396 | 467 | gtk_tree_model_get(model, |
397 | 468 | &iter, |
398 | LIST_IM, &item, | |
469 | IM_LIST_IM, &item, | |
399 | 470 | -1); |
400 | 471 | |
401 | 472 | int i; |
413 | 484 | gpointer temp = g_ptr_array_index(self->array, i); |
414 | 485 | g_ptr_array_index(self->array, i) = g_ptr_array_index(self->array, switch_index); |
415 | 486 | g_ptr_array_index(self->array, switch_index) = temp; |
487 | g_free(self->focus); | |
488 | self->focus = g_strdup(item->unique_name); | |
416 | 489 | |
417 | 490 | fcitx_inputmethod_set_imlist(self->improxy, self->array); |
418 | 491 | } |
441 | 514 | FcitxIMItem* item = NULL; |
442 | 515 | gtk_tree_model_get(GTK_TREE_MODEL(self->availimstore), |
443 | 516 | iter, |
444 | LIST_IM, &item, | |
517 | AVAIL_TREE_IM, &item, | |
445 | 518 | -1); |
446 | 519 | |
520 | gboolean flag = TRUE; | |
447 | 521 | if (item) { |
448 | gboolean flag = TRUE; | |
449 | flag &= (strlen(filter_text) == 0 | |
522 | flag = flag && (strlen(filter_text) == 0 | |
450 | 523 | || strstr(item->name, filter_text) |
451 | 524 | || strstr(item->unique_name, filter_text) |
452 | 525 | || strstr(item->langcode, filter_text)); |
453 | flag &= (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(self->onlycurlangcheckbox)) ? | |
526 | flag = flag && (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(self->onlycurlangcheckbox)) ? | |
454 | 527 | strncmp(item->langcode, _get_current_lang() , 2) == 0 : TRUE) ; |
455 | 528 | return flag; |
456 | } else | |
457 | return FALSE; | |
529 | } else { | |
530 | gchar* lang = NULL; | |
531 | gtk_tree_model_get(GTK_TREE_MODEL(self->availimstore), | |
532 | iter, | |
533 | AVAIL_TREE_LANG, &lang, | |
534 | -1); | |
535 | flag = (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(self->onlycurlangcheckbox)) ? | |
536 | lang != NULL && strncmp(lang, _get_current_lang() , 2) == 0 : TRUE) ; | |
537 | g_free(lang); | |
538 | return flag; | |
539 | } | |
458 | 540 | } |
459 | 541 | |
460 | 542 | static const gchar* _get_current_lang() |
467 | 549 | if (!lang) |
468 | 550 | lang = "C"; |
469 | 551 | return lang; |
552 | } | |
553 | ||
554 | static void | |
555 | icon_press_cb (GtkEntry *entry, | |
556 | gint position, | |
557 | GdkEventButton *event, | |
558 | gpointer data) | |
559 | { | |
560 | gtk_entry_set_text (entry, ""); | |
470 | 561 | }⏎ |
0 | 0 | /*************************************************************************** |
1 | * Copyright (C) 2010~2011 by CSSlayer * | |
1 | * Copyright (C) 2010~2012 by CSSlayer * | |
2 | 2 | * * |
3 | 3 | * This program is free software; you can redistribute it and/or modify * |
4 | 4 | * it under the terms of the GNU General Public License as published by * |
46 | 46 | |
47 | 47 | typedef struct { |
48 | 48 | GtkHBox parent; |
49 | GtkListStore* availimstore; | |
49 | GtkTreeStore* availimstore; | |
50 | 50 | GtkListStore* imstore; |
51 | 51 | GtkWidget* availimview; |
52 | 52 | GtkWidget* imview; |
61 | 61 | GtkTreeModel* filtermodel; |
62 | 62 | GtkWidget* onlycurlangcheckbox; |
63 | 63 | GtkTreeModel* sortmodel; |
64 | gchar* focus; | |
64 | 65 | } FcitxImWidget; |
65 | 66 | |
66 | 67 | typedef struct { |
0 | 0 | /*************************************************************************** |
1 | * Copyright (C) 2010~2011 by CSSlayer * | |
1 | * Copyright (C) 2010~2012 by CSSlayer * | |
2 | 2 | * * |
3 | 3 | * This program is free software; you can redistribute it and/or modify * |
4 | 4 | * it under the terms of the GNU General Public License as published by * |
0 | 0 | /*************************************************************************** |
1 | * Copyright (C) 2010~2011 by CSSlayer * | |
1 | * Copyright (C) 2010~2012 by CSSlayer * | |
2 | 2 | * * |
3 | 3 | * This program is free software; you can redistribute it and/or modify * |
4 | 4 | * it under the terms of the GNU General Public License as published by * |
0 | 0 | /*************************************************************************** |
1 | * Copyright (C) 2010~2011 by CSSlayer * | |
1 | * Copyright (C) 2010~2012 by CSSlayer * | |
2 | 2 | * * |
3 | 3 | * This program is free software; you can redistribute it and/or modify * |
4 | 4 | * it under the terms of the GNU General Public License as published by * |
0 | 0 | /*************************************************************************** |
1 | * Copyright (C) 2010~2011 by CSSlayer * | |
1 | * Copyright (C) 2010~2012 by CSSlayer * | |
2 | 2 | * * |
3 | 3 | * This program is free software; you can redistribute it and/or modify * |
4 | 4 | * it under the terms of the GNU General Public License as published by * |
36 | 36 | N_COLUMNS |
37 | 37 | }; |
38 | 38 | |
39 | enum { | |
40 | PAGE_LIST_NAME, | |
41 | PAGE_LIST_PAGE, | |
42 | PAGE_LIST_ICON, | |
43 | PAGE_N_COLUMNS | |
44 | }; | |
45 | ||
39 | 46 | G_DEFINE_TYPE(FcitxMainWindow, fcitx_main_window, GTK_TYPE_WINDOW) |
40 | 47 | |
41 | 48 | static void fcitx_main_window_finalize(GObject* object); |
48 | 55 | |
49 | 56 | static void _fcitx_main_window_add_im_page(FcitxMainWindow* self); |
50 | 57 | |
51 | static int _fcitx_main_window_close_cb(GtkWidget *theWindow, gpointer data); | |
52 | ||
53 | static void _fcitx_main_window_selection_changed_cb(GtkTreeSelection *selection, gpointer data); | |
54 | ||
55 | static ConfigPage* _fcitx_main_window_add_page(FcitxMainWindow* self, const char* name, GtkWidget* widget); | |
58 | static void _fcitx_main_window_selection_changed_cb(GtkIconView *iconview, gpointer data); | |
59 | ||
60 | static ConfigPage* _fcitx_main_window_add_page(FcitxMainWindow* self, const char* name, GtkWidget* widget, const char* stock); | |
56 | 61 | |
57 | 62 | static void _fcitx_main_window_addon_selection_changed(GtkTreeSelection *selection, gpointer data); |
58 | 63 | |
89 | 94 | static void |
90 | 95 | fcitx_main_window_init(FcitxMainWindow* self) |
91 | 96 | { |
92 | GtkWidget *vbox = gtk_vbox_new(FALSE, 0); | |
93 | ||
94 | GtkCellRenderer *renderer; | |
95 | GtkTreeViewColumn *column; | |
97 | GtkWidget* vbox = gtk_vbox_new(FALSE, 0); | |
98 | GtkWidget* hbox = gtk_hbox_new(FALSE, 0); | |
96 | 99 | |
97 | 100 | self->pagestore = _fcitx_main_window_create_model(); |
98 | self->pageview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(self->pagestore)); | |
99 | ||
100 | renderer = gtk_cell_renderer_text_new(); | |
101 | column = gtk_tree_view_column_new_with_attributes( | |
102 | _("Config"), renderer, | |
103 | "text", 0, | |
104 | NULL); | |
105 | gtk_tree_view_append_column(GTK_TREE_VIEW(self->pageview), column); | |
106 | ||
107 | gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(self->pageview), FALSE); | |
101 | self->pageview = gtk_icon_view_new_with_model(GTK_TREE_MODEL(self->pagestore)); | |
102 | ||
103 | gtk_icon_view_set_pixbuf_column(GTK_ICON_VIEW(self->pageview), PAGE_LIST_ICON); | |
104 | gtk_icon_view_set_text_column(GTK_ICON_VIEW(self->pageview), PAGE_LIST_NAME); | |
105 | gtk_icon_view_set_item_orientation(GTK_ICON_VIEW(self->pageview), GTK_ORIENTATION_VERTICAL); | |
108 | 106 | |
109 | 107 | _fcitx_main_window_add_im_page(self); |
110 | 108 | _fcitx_main_window_add_config_file_page(self); |
111 | 109 | _fcitx_main_window_add_addon_page(self); |
112 | 110 | |
113 | gtk_widget_set_size_request(self->pageview, 170, -1); | |
114 | 111 | gtk_widget_set_size_request(GTK_WIDGET(self), -1, 500); |
115 | 112 | |
116 | self->hpaned = gtk_hpaned_new(); | |
117 | GtkWidget *treescroll = gtk_scrolled_window_new(NULL, NULL); | |
118 | gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(treescroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); | |
119 | ||
120 | gtk_container_add(GTK_CONTAINER(treescroll), self->pageview); | |
121 | gtk_paned_add1(GTK_PANED(self->hpaned), treescroll); | |
122 | ||
123 | gtk_box_pack_start(GTK_BOX(vbox), self->hpaned, TRUE, TRUE, 0); | |
113 | self->vbox = gtk_vbox_new(FALSE, 0); | |
114 | self->pagelabel = gtk_label_new(""); | |
115 | gtk_label_set_use_markup(GTK_LABEL(self->pagelabel), true); | |
116 | gtk_misc_set_alignment(GTK_MISC(self->pagelabel), 0, 0.5); | |
117 | ||
118 | gtk_box_pack_start(GTK_BOX(self->vbox), self->pagelabel, FALSE, FALSE, 14); | |
119 | GtkWidget* scrolledwindow = gtk_scrolled_window_new(NULL, NULL); | |
120 | gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwindow), GTK_POLICY_NEVER, GTK_POLICY_NEVER); | |
121 | gtk_container_add(GTK_CONTAINER(scrolledwindow), self->pageview); | |
122 | gtk_box_pack_start(GTK_BOX(hbox), scrolledwindow, FALSE, TRUE, 4); | |
123 | gtk_box_pack_start(GTK_BOX(hbox), self->vbox, TRUE, TRUE, 8); | |
124 | gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 8); | |
124 | 125 | |
125 | 126 | gtk_container_add(GTK_CONTAINER(self), vbox); |
126 | 127 | |
127 | GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self->pageview)); | |
128 | gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE); | |
129 | ||
130 | g_signal_connect(G_OBJECT(self), "destroy", G_CALLBACK(_fcitx_main_window_close_cb), NULL); | |
131 | g_signal_connect(G_OBJECT(selection), "changed", | |
128 | gtk_icon_view_set_selection_mode(GTK_ICON_VIEW(self->pageview), GTK_SELECTION_SINGLE); | |
129 | gtk_icon_view_set_item_padding(GTK_ICON_VIEW(self->pageview), 0); | |
130 | gtk_icon_view_set_margin(GTK_ICON_VIEW(self->pageview), 0); | |
131 | gtk_icon_view_set_column_spacing(GTK_ICON_VIEW(self->pageview), 0); | |
132 | gtk_icon_view_set_row_spacing(GTK_ICON_VIEW(self->pageview), 0); | |
133 | gtk_icon_view_set_item_width(GTK_ICON_VIEW(self->pageview), 96); | |
134 | ||
135 | g_signal_connect_swapped(G_OBJECT(self), "destroy", G_CALLBACK(gtk_main_quit), NULL); | |
136 | g_signal_connect(G_OBJECT(self->pageview), "selection-changed", | |
132 | 137 | G_CALLBACK(_fcitx_main_window_selection_changed_cb), self); |
133 | 138 | |
134 | gtk_tree_selection_select_iter(selection, &self->impage->iter); | |
139 | GtkTreePath* path = gtk_tree_model_get_path(GTK_TREE_MODEL(self->pagestore), &self->impage->iter); | |
140 | gtk_icon_view_select_path(GTK_ICON_VIEW(self->pageview), path); | |
141 | gtk_tree_path_free(path); | |
135 | 142 | |
136 | 143 | gtk_window_set_icon_name(GTK_WINDOW(self), "fcitx-configtool"); |
137 | 144 | gtk_window_set_title(GTK_WINDOW(self), _("Fcitx Config")); |
153 | 160 | utarray_free(self->addons); |
154 | 161 | } |
155 | 162 | |
156 | int _fcitx_main_window_close_cb(GtkWidget *theWindow, gpointer data) | |
157 | { | |
158 | gtk_main_quit(); | |
159 | return 0; | |
160 | } | |
161 | ||
162 | ConfigPage* _fcitx_main_window_add_page(FcitxMainWindow* self, const char* name, GtkWidget* widget) | |
163 | ConfigPage* _fcitx_main_window_add_page(FcitxMainWindow* self, const char* name, GtkWidget* widget, const char* stock) | |
163 | 164 | { |
164 | 165 | ConfigPage *page = (ConfigPage*) malloc(sizeof(ConfigPage)); |
165 | 166 | memset(page, 0, sizeof(ConfigPage)); |
171 | 172 | gtk_widget_show_all(widget); |
172 | 173 | |
173 | 174 | gtk_list_store_append(self->pagestore, &page->iter); |
174 | gtk_list_store_set(self->pagestore, &page->iter, 0, name, 1, page, -1); | |
175 | gtk_list_store_set(self->pagestore, &page->iter, | |
176 | 0, name, | |
177 | 1, page, | |
178 | 2, gtk_widget_render_icon(self->pageview, stock, GTK_ICON_SIZE_DND, NULL), | |
179 | -1); | |
175 | 180 | |
176 | 181 | return page; |
177 | 182 | } |
178 | 183 | |
179 | void _fcitx_main_window_selection_changed_cb(GtkTreeSelection *selection, gpointer data) | |
180 | { | |
181 | GtkTreeView *treeView = gtk_tree_selection_get_tree_view(selection); | |
182 | GtkTreeModel *model = gtk_tree_view_get_model(treeView); | |
184 | void _fcitx_main_window_selection_changed_cb(GtkIconView* iconview, gpointer data) | |
185 | { | |
186 | GtkTreeModel *model = gtk_icon_view_get_model(iconview); | |
183 | 187 | GtkTreeIter iter; |
184 | 188 | ConfigPage* page; |
185 | 189 | FcitxMainWindow* self = data; |
186 | ||
187 | if (gtk_tree_selection_get_selected(selection, &model, &iter)) { | |
190 | ||
191 | GList* list = gtk_icon_view_get_selected_items(iconview); | |
192 | ||
193 | if (list) { | |
194 | gchar* title; | |
195 | gtk_tree_model_get_iter(GTK_TREE_MODEL(self->pagestore), &iter, (GtkTreePath*)(list->data)); | |
188 | 196 | gtk_tree_model_get(model, &iter, |
189 | 1, &page, | |
197 | PAGE_LIST_NAME, &title, | |
198 | PAGE_LIST_PAGE, &page, | |
190 | 199 | -1); |
200 | ||
201 | gchar* text = g_strdup_printf("<b>%s</b>", title); | |
202 | gtk_label_set_markup(GTK_LABEL(self->pagelabel), text); | |
203 | g_free(text); | |
204 | g_free(title); | |
191 | 205 | |
192 | 206 | if (self->lastpage) |
193 | gtk_container_remove(GTK_CONTAINER(self->hpaned), self->lastpage->page); | |
194 | gtk_paned_add2(GTK_PANED(self->hpaned), page->page); | |
207 | gtk_container_remove(GTK_CONTAINER(self->vbox), self->lastpage->page); | |
208 | gtk_box_pack_end(GTK_BOX(self->vbox), page->page, TRUE, TRUE, 0); | |
195 | 209 | gtk_widget_show_all(GTK_WIDGET(self)); |
196 | 210 | |
197 | 211 | self->lastpage = page; |
198 | 212 | } else { |
199 | gtk_tree_selection_select_iter(selection, &self->configpage->iter); | |
213 | GtkTreePath* path = gtk_tree_model_get_path(GTK_TREE_MODEL(self->pagestore), &self->impage->iter); | |
214 | gtk_icon_view_select_path(GTK_ICON_VIEW(self->pageview), path); | |
215 | gtk_tree_path_free(path); | |
200 | 216 | } |
217 | ||
218 | g_list_foreach (list, (GFunc)gtk_tree_path_free, NULL); | |
219 | g_list_free (list); | |
201 | 220 | } |
202 | 221 | |
203 | 222 | |
228 | 247 | |
229 | 248 | static GtkListStore *_fcitx_main_window_create_model() |
230 | 249 | { |
231 | GtkListStore* store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_POINTER); | |
250 | GtkListStore* store = gtk_list_store_new(PAGE_N_COLUMNS, G_TYPE_STRING, G_TYPE_POINTER, GDK_TYPE_PIXBUF); | |
232 | 251 | return store; |
233 | 252 | } |
234 | 253 | |
252 | 271 | g_signal_connect(G_OBJECT(applybutton), "clicked", G_CALLBACK(_fcitx_main_window_apply_button_clicked), config_widget); |
253 | 272 | |
254 | 273 | |
255 | self->configpage = _fcitx_main_window_add_page(self, _("Global Config"), vbox); | |
274 | self->configpage = _fcitx_main_window_add_page(self, _("Global Config"), vbox, GTK_STOCK_PREFERENCES); | |
256 | 275 | } |
257 | 276 | |
258 | 277 | void _fcitx_main_window_add_im_page(FcitxMainWindow* self) |
259 | 278 | { |
260 | 279 | GtkWidget* imwidget = fcitx_im_widget_new(); |
261 | self->impage = _fcitx_main_window_add_page(self, _("Input Method Configuration"), imwidget); | |
280 | self->impage = _fcitx_main_window_add_page(self, _("Input Method"), imwidget, GTK_STOCK_EDIT); | |
262 | 281 | } |
263 | 282 | |
264 | 283 | void _fcitx_main_window_add_addon_page(FcitxMainWindow* self) |
329 | 348 | g_signal_connect(G_OBJECT(selection), "changed", |
330 | 349 | G_CALLBACK(_fcitx_main_window_addon_selection_changed), self); |
331 | 350 | |
332 | self->addonpage = _fcitx_main_window_add_page(self, _("Addon Configuration"), vbox); | |
351 | self->addonpage = _fcitx_main_window_add_page(self, _("Addon"), vbox, GTK_STOCK_ADD); | |
333 | 352 | } |
334 | 353 | |
335 | 354 | static void |
0 | 0 | /*************************************************************************** |
1 | * Copyright (C) 2010~2011 by CSSlayer * | |
1 | * Copyright (C) 2010~2012 by CSSlayer * | |
2 | 2 | * * |
3 | 3 | * This program is free software; you can redistribute it and/or modify * |
4 | 4 | * it under the terms of the GNU General Public License as published by * |
53 | 53 | GtkWindow parent; |
54 | 54 | GtkWidget* pageview; |
55 | 55 | GtkListStore *pagestore; |
56 | GtkWidget* hpaned; | |
56 | GtkWidget* vbox; | |
57 | GtkWidget* pagelabel; | |
57 | 58 | ConfigPage* impage; |
58 | 59 | ConfigPage* configpage; |
59 | 60 | ConfigPage* lastpage; |
0 | 0 | /*************************************************************************** |
1 | * Copyright (C) 2010~2011 by CSSlayer * | |
1 | * Copyright (C) 2010~2012 by CSSlayer * | |
2 | 2 | * * |
3 | 3 | * This program is free software; you can redistribute it and/or modify * |
4 | 4 | * it under the terms of the GNU General Public License as published by * |
193 | 193 | { |
194 | 194 | size_t size, i; |
195 | 195 | GList* result = NULL; |
196 | #if FCITX_CHECK_VERSION(4,2,1) | |
197 | char** xdgpath = FcitxXDGGetPathWithPrefix(&size, ""); | |
198 | #else | |
196 | 199 | char** xdgpath = FcitxXDGGetPath(&size, "XDG_CONFIG_HOME", ".config" , PACKAGE , DATADIR, PACKAGE); |
200 | #endif | |
197 | 201 | |
198 | 202 | for (i = 0; i < size; i ++) { |
199 | 203 | char* dirpath = realpath(xdgpath[i], NULL); |
0 | 0 | /*************************************************************************** |
1 | * Copyright (C) 2010~2011 by CSSlayer * | |
1 | * Copyright (C) 2010~2012 by CSSlayer * | |
2 | 2 | * * |
3 | 3 | * This program is free software; you can redistribute it and/or modify * |
4 | 4 | * it under the terms of the GNU General Public License as published by * |
0 | 0 | /*************************************************************************** |
1 | * Copyright (C) 2010~2011 by CSSlayer * | |
1 | * Copyright (C) 2010~2012 by CSSlayer * | |
2 | 2 | * * |
3 | 3 | * This program is free software; you can redistribute it and/or modify * |
4 | 4 | * it under the terms of the GNU General Public License as published by * |
0 | 0 | /*************************************************************************** |
1 | * Copyright (C) 2010~2011 by CSSlayer * | |
1 | * Copyright (C) 2010~2012 by CSSlayer * | |
2 | 2 | * * |
3 | 3 | * This program is free software; you can redistribute it and/or modify * |
4 | 4 | * it under the terms of the GNU General Public License as published by * |
0 | 0 | PKG_CHECK_MODULES (GTK3 "gtk+-3.0" REQUIRED) |
1 | PKG_CHECK_MODULES(UNIQUE3 "unique-3.0") | |
2 | 1 | |
3 | 2 | include_directories ( |
4 | 3 | ${GTK3_INCLUDE_DIRS} |
4 | ${GLIB2_INCLUDE_DIRS} | |
5 | 5 | ${GIO2_INCLUDE_DIRS} |
6 | 6 | ${FCITX4_FCITX_INCLUDE_DIRS} |
7 | 7 | ${FCITX4_FCITX_UTILS_INCLUDE_DIRS} |
8 | 8 | ${FCITX4_FCITX_CONFIG_INCLUDE_DIRS} |
9 | ../gtk/ | |
10 | 9 | ) |
11 | 10 | |
12 | 11 | link_directories ( |
13 | 12 | ${GTK3_LIBRARY_DIRS} |
13 | ${GLIB2_LIBRARY_DIRS} | |
14 | 14 | ${GIO2_LIBRARY_DIRS} |
15 | 15 | ${FCITX4_FCITX_LIBRARY_DIRS} |
16 | 16 | ${FCITX4_FCITX_UTILS_LIBRARY_DIRS} |
17 | 17 | ${FCITX4_FCITX_CONFIG_LIBRARY_DIRS} |
18 | 18 | ) |
19 | if (UNIQUE3_FOUND) | |
20 | include_directories (${UNIQUE3_INCLUDE_DIRS}) | |
21 | link_directories (${UNIQUE3_LIBRARY_DIRS}) | |
22 | endif (UNIQUE3_FOUND) | |
23 | 19 | |
24 | 20 | set( fcitx_config_gtk3_sources |
25 | ../gtk/sub_config_parser.c | |
26 | ../gtk/sub_config_widget.c | |
27 | ../gtk/config_widget.c | |
28 | ../gtk/configdesc.c | |
29 | ../gtk/keygrab.c | |
30 | ../gtk/main.c | |
31 | ../gtk/main_window.c | |
32 | ../gtk/im_widget.c | |
33 | ../gtk/im.c | |
34 | ../gtk/gdm-languages.c | |
21 | sub_config_parser.c | |
22 | sub_config_widget.c | |
23 | config_widget.c | |
24 | configdesc.c | |
25 | keygrab.c | |
26 | main.c | |
27 | main_window.c | |
28 | im_widget.c | |
29 | im.c | |
30 | gdm-languages.c | |
35 | 31 | ) |
36 | 32 | |
37 | 33 | add_executable( fcitx-config-gtk3 ${fcitx_config_gtk3_sources} ) |
43 | 39 | ${FCITX4_FCITX_UTILS_LIBRARIES} |
44 | 40 | ${FCITX4_FCITX_CONFIG_LIBRARIES} |
45 | 41 | ${FCITX4_FCITX_LIBRARIES} |
42 | ${GLIB2_LIBRARIES} | |
46 | 43 | ${GIO2_LIBRARIES} |
47 | 44 | ) |
48 | 45 | |
49 | if (UNIQUE3_FOUND) | |
50 | target_link_libraries (fcitx-config-gtk3 ${UNIQUE3_LIBRARIES}) | |
51 | endif (UNIQUE3_FOUND) | |
52 |
0 | /*************************************************************************** | |
1 | * Copyright (C) 2010~2012 by CSSlayer * | |
2 | * * | |
3 | * This program is free software; you can redistribute it and/or modify * | |
4 | * it under the terms of the GNU General Public License as published by * | |
5 | * the Free Software Foundation; either version 2 of the License, or * | |
6 | * (at your option) any later version. * | |
7 | * * | |
8 | * This program is distributed in the hope that it will be useful, * | |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * | |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * | |
11 | * GNU General Public License for more details. * | |
12 | * * | |
13 | * You should have received a copy of the GNU General Public License * | |
14 | * along with this program; if not, write to the * | |
15 | * Free Software Foundation, Inc., * | |
16 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * | |
17 | ***************************************************************************/ | |
18 | ||
19 | #ifndef COMMON_H | |
20 | #define COMMON_H | |
21 | ||
22 | #include <libintl.h> | |
23 | ||
24 | #define _(x) gettext(x) | |
25 | ||
26 | #endif⏎ |
0 | /*************************************************************************** | |
1 | * Copyright (C) 2010~2012 by CSSlayer * | |
2 | * * | |
3 | * This program is free software; you can redistribute it and/or modify * | |
4 | * it under the terms of the GNU General Public License as published by * | |
5 | * the Free Software Foundation; either version 2 of the License, or * | |
6 | * (at your option) any later version. * | |
7 | * * | |
8 | * This program is distributed in the hope that it will be useful, * | |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * | |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * | |
11 | * GNU General Public License for more details. * | |
12 | * * | |
13 | * You should have received a copy of the GNU General Public License * | |
14 | * along with this program; if not, write to the * | |
15 | * Free Software Foundation, Inc., * | |
16 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * | |
17 | ***************************************************************************/ | |
18 | ||
19 | #include "config.h" | |
20 | ||
21 | #include <gtk/gtk.h> | |
22 | #include <libintl.h> | |
23 | #include <stdlib.h> | |
24 | #include <libgen.h> | |
25 | #include <sys/stat.h> | |
26 | #include <unistd.h> | |
27 | ||
28 | #include <fcitx-config/fcitx-config.h> | |
29 | #include <fcitx-utils/uthash.h> | |
30 | #include <fcitx-config/hotkey.h> | |
31 | #include <fcitx-config/xdg.h> | |
32 | ||
33 | #include "config_widget.h" | |
34 | #include "keygrab.h" | |
35 | #include "sub_config_widget.h" | |
36 | ||
37 | #define _(s) gettext(s) | |
38 | #define D_(d, x) dgettext (d, x) | |
39 | #define RoundColor(c) ((c)>=0?((c)<=255?c:255):0) | |
40 | ||
41 | G_DEFINE_TYPE(FcitxConfigWidget, fcitx_config_widget, GTK_TYPE_BOX) | |
42 | ||
43 | typedef struct { | |
44 | int i; | |
45 | FcitxConfigWidget* widget; | |
46 | GtkWidget* grid; | |
47 | } HashForeachContext; | |
48 | ||
49 | enum { | |
50 | PROP_0, | |
51 | ||
52 | PROP_CONFIG_DESC, | |
53 | PROP_PREFIX, | |
54 | PROP_NAME, | |
55 | PROP_SUBCONFIG | |
56 | }; | |
57 | ||
58 | static void | |
59 | fcitx_config_widget_set_property(GObject *gobject, | |
60 | guint prop_id, | |
61 | const GValue *value, | |
62 | GParamSpec *pspec); | |
63 | ||
64 | ||
65 | static void sync_filter(FcitxGenericConfig* gconfig, FcitxConfigGroup *group, FcitxConfigOption *option, void *value, FcitxConfigSync sync, void *arg); | |
66 | ||
67 | static void set_none_font_clicked(GtkWidget *button, gpointer arg); | |
68 | ||
69 | static void hash_foreach_cb(gpointer key, | |
70 | gpointer value, | |
71 | gpointer user_data); | |
72 | ||
73 | static void | |
74 | fcitx_config_widget_setup_ui(FcitxConfigWidget *self); | |
75 | ||
76 | static void | |
77 | fcitx_config_widget_finalize(GObject *object); | |
78 | ||
79 | static void | |
80 | fcitx_config_widget_class_init(FcitxConfigWidgetClass *klass) | |
81 | { | |
82 | GObjectClass *gobject_class = G_OBJECT_CLASS(klass); | |
83 | gobject_class->set_property = fcitx_config_widget_set_property; | |
84 | gobject_class->finalize = fcitx_config_widget_finalize; | |
85 | g_object_class_install_property(gobject_class, | |
86 | PROP_CONFIG_DESC, | |
87 | g_param_spec_pointer("cfdesc", | |
88 | "Configuration Description", | |
89 | "Configuration Description for this widget", | |
90 | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); | |
91 | ||
92 | g_object_class_install_property(gobject_class, | |
93 | PROP_PREFIX, | |
94 | g_param_spec_string("prefix", | |
95 | "Prefix of path", | |
96 | "Prefix of configuration path", | |
97 | NULL, | |
98 | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); | |
99 | ||
100 | g_object_class_install_property(gobject_class, | |
101 | PROP_NAME, | |
102 | g_param_spec_string("name", | |
103 | "File name", | |
104 | "File name of configuration file", | |
105 | NULL, | |
106 | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); | |
107 | ||
108 | g_object_class_install_property(gobject_class, | |
109 | PROP_SUBCONFIG, | |
110 | g_param_spec_string("subconfig", | |
111 | "subconfig", | |
112 | "subconfig", | |
113 | NULL, | |
114 | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); | |
115 | } | |
116 | ||
117 | static void | |
118 | fcitx_config_widget_init(FcitxConfigWidget *self) | |
119 | { | |
120 | } | |
121 | ||
122 | static void | |
123 | fcitx_config_widget_setup_ui(FcitxConfigWidget *self) | |
124 | { | |
125 | FcitxConfigFileDesc* cfdesc = self->cfdesc; | |
126 | GtkWidget *cvbox = GTK_WIDGET(self); | |
127 | GtkWidget *configNotebook = gtk_notebook_new(); | |
128 | gtk_box_pack_start(GTK_BOX(cvbox), configNotebook, TRUE, TRUE, 0); | |
129 | if (cfdesc) { | |
130 | bindtextdomain(cfdesc->domain, LOCALEDIR); | |
131 | bind_textdomain_codeset(cfdesc->domain, "UTF-8"); | |
132 | ||
133 | FILE *fp; | |
134 | fp = FcitxXDGGetFileWithPrefix(self->prefix, self->name, "r", NULL); | |
135 | self->gconfig.configFile = FcitxConfigParseConfigFileFp(fp, cfdesc); | |
136 | ||
137 | FcitxConfigGroupDesc *cgdesc = NULL; | |
138 | FcitxConfigOptionDesc *codesc = NULL; | |
139 | for (cgdesc = cfdesc->groupsDesc; | |
140 | cgdesc != NULL; | |
141 | cgdesc = (FcitxConfigGroupDesc*)cgdesc->hh.next) { | |
142 | codesc = cgdesc->optionsDesc; | |
143 | if (codesc == NULL) | |
144 | continue; | |
145 | ||
146 | GtkWidget* hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); | |
147 | GtkWidget *grid = gtk_grid_new(); | |
148 | gtk_widget_set_margin_left(grid, 12); | |
149 | gtk_widget_set_margin_top(grid, 6); | |
150 | gtk_grid_set_row_spacing(GTK_GRID(grid), 12); | |
151 | gtk_grid_set_column_spacing(GTK_GRID(grid), 6); | |
152 | GtkWidget *plabel = gtk_label_new(D_(cfdesc->domain, cgdesc->groupName)); | |
153 | GtkWidget *scrollwnd = gtk_scrolled_window_new(NULL, NULL); | |
154 | ||
155 | gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollwnd), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); | |
156 | gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrollwnd), grid); | |
157 | gtk_box_pack_start(GTK_BOX(hbox), scrollwnd, TRUE, TRUE, 0); | |
158 | gtk_notebook_append_page(GTK_NOTEBOOK(configNotebook), | |
159 | hbox, | |
160 | plabel); | |
161 | ||
162 | int i = 0; | |
163 | for (; codesc != NULL; | |
164 | codesc = (FcitxConfigOptionDesc*)codesc->hh.next, i++) { | |
165 | const char *s; | |
166 | if (codesc->desc && strlen(codesc->desc) != 0) | |
167 | s = D_(cfdesc->domain, codesc->desc); | |
168 | else | |
169 | s = D_(cfdesc->domain, codesc->optionName); | |
170 | ||
171 | GtkWidget *inputWidget = NULL; | |
172 | void *argument = NULL; | |
173 | ||
174 | switch (codesc->type) { | |
175 | case T_Integer: | |
176 | inputWidget = gtk_spin_button_new_with_range(-1.0, 10000.0, 1.0); | |
177 | argument = inputWidget; | |
178 | break; | |
179 | case T_Color: | |
180 | inputWidget = gtk_color_button_new(); | |
181 | argument = inputWidget; | |
182 | break; | |
183 | case T_Boolean: | |
184 | inputWidget = gtk_check_button_new(); | |
185 | argument = inputWidget; | |
186 | break; | |
187 | case T_Font: { | |
188 | inputWidget = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); | |
189 | argument = gtk_font_button_new(); | |
190 | GtkWidget *button = gtk_button_new_with_label(_("Clear font setting")); | |
191 | gtk_box_pack_start(GTK_BOX(inputWidget), argument, TRUE, TRUE, 0); | |
192 | gtk_box_pack_start(GTK_BOX(inputWidget), button, FALSE, FALSE, 0); | |
193 | gtk_font_button_set_use_size(GTK_FONT_BUTTON(argument), FALSE); | |
194 | gtk_font_button_set_show_size(GTK_FONT_BUTTON(argument), FALSE); | |
195 | g_signal_connect(G_OBJECT(button), "clicked", (GCallback) set_none_font_clicked, argument); | |
196 | } | |
197 | break; | |
198 | case T_Enum: { | |
199 | int i; | |
200 | FcitxConfigEnum *e = &codesc->configEnum; | |
201 | #if GTK_CHECK_VERSION(2, 24, 0) | |
202 | inputWidget = gtk_combo_box_text_new(); | |
203 | for (i = 0; i < e->enumCount; i ++) { | |
204 | gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(inputWidget), D_(cfdesc->domain, e->enumDesc[i])); | |
205 | } | |
206 | #else | |
207 | inputWidget = gtk_combo_box_new_text(); | |
208 | for (i = 0; i < e->enumCount; i ++) | |
209 | { | |
210 | gtk_combo_box_append_text(GTK_COMBO_BOX(inputWidget), D_(cfdesc->domain, e->enumDesc[i])); | |
211 | } | |
212 | #endif | |
213 | argument = inputWidget; | |
214 | } | |
215 | break; | |
216 | case T_Hotkey: { | |
217 | GtkWidget *button[2]; | |
218 | button[0] = keygrab_button_new(); | |
219 | button[1] = keygrab_button_new(); | |
220 | inputWidget = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); | |
221 | gtk_box_pack_start(GTK_BOX(inputWidget), button[0], FALSE, TRUE, 0); | |
222 | gtk_box_pack_start(GTK_BOX(inputWidget), button[1], FALSE, TRUE, 0); | |
223 | argument = g_array_new(FALSE, FALSE, sizeof(void*)); | |
224 | g_array_append_val(argument, button[0]); | |
225 | g_array_append_val(argument, button[1]); | |
226 | } | |
227 | break; | |
228 | case T_File: | |
229 | case T_Char: | |
230 | case T_String: | |
231 | inputWidget = gtk_entry_new(); | |
232 | argument = inputWidget; | |
233 | break; | |
234 | default: | |
235 | break; | |
236 | } | |
237 | ||
238 | if (inputWidget) { | |
239 | GtkWidget* label = gtk_label_new(s); | |
240 | g_object_set(label, "xalign", 0.0f, NULL); | |
241 | gtk_grid_attach(GTK_GRID(grid), label, 0, i, 1, 1); | |
242 | gtk_grid_attach(GTK_GRID(grid), inputWidget, 1, i, 1, 1); | |
243 | FcitxConfigBindValue(self->gconfig.configFile, cgdesc->groupName, codesc->optionName, NULL, sync_filter, argument); | |
244 | } | |
245 | } | |
246 | } | |
247 | ||
248 | FcitxConfigBindSync(&self->gconfig); | |
249 | } | |
250 | ||
251 | if (self->parser) { | |
252 | GHashTable* subconfigs = self->parser->subconfigs; | |
253 | if (g_hash_table_size(subconfigs) != 0) { | |
254 | GtkWidget *grid = gtk_grid_new(); | |
255 | GtkWidget *plabel = gtk_label_new(_("Other")); | |
256 | GtkWidget *scrollwnd = gtk_scrolled_window_new(NULL, NULL); | |
257 | GtkWidget *viewport = gtk_viewport_new(NULL, NULL); | |
258 | ||
259 | gtk_container_set_border_width(GTK_CONTAINER(grid), 4); | |
260 | gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollwnd), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); | |
261 | gtk_container_add(GTK_CONTAINER(scrollwnd), viewport); | |
262 | gtk_container_add(GTK_CONTAINER(viewport), grid); | |
263 | gtk_notebook_append_page(GTK_NOTEBOOK(configNotebook), | |
264 | scrollwnd, | |
265 | plabel); | |
266 | ||
267 | HashForeachContext context; | |
268 | context.i = 0; | |
269 | context.grid = grid; | |
270 | context.widget = self; | |
271 | g_hash_table_foreach(subconfigs, hash_foreach_cb, &context); | |
272 | } | |
273 | } | |
274 | ||
275 | gtk_widget_set_size_request(configNotebook, 500, -1); | |
276 | gtk_notebook_set_scrollable(GTK_NOTEBOOK(configNotebook), TRUE); | |
277 | } | |
278 | FcitxConfigWidget* | |
279 | fcitx_config_widget_new(FcitxConfigFileDesc* cfdesc, const gchar* prefix, const gchar* name, const char* subconfig) | |
280 | { | |
281 | FcitxConfigWidget* widget = | |
282 | g_object_new(FCITX_TYPE_CONFIG_WIDGET, | |
283 | "cfdesc", cfdesc, | |
284 | "prefix", prefix, | |
285 | "name", name, | |
286 | "subconfig", subconfig, | |
287 | NULL | |
288 | ); | |
289 | fcitx_config_widget_setup_ui(widget); | |
290 | return widget; | |
291 | } | |
292 | ||
293 | static void | |
294 | fcitx_config_widget_set_property(GObject *gobject, | |
295 | guint prop_id, | |
296 | const GValue *value, | |
297 | GParamSpec *pspec) | |
298 | { | |
299 | FcitxConfigWidget* config_widget = FCITX_CONFIG_WIDGET(gobject); | |
300 | switch (prop_id) { | |
301 | case PROP_CONFIG_DESC: | |
302 | config_widget->cfdesc = g_value_get_pointer(value); | |
303 | break; | |
304 | case PROP_PREFIX: | |
305 | if (config_widget->prefix) | |
306 | g_free(config_widget->prefix); | |
307 | config_widget->prefix = g_strdup(g_value_get_string(value)); | |
308 | break; | |
309 | case PROP_NAME: | |
310 | if (config_widget->name) | |
311 | g_free(config_widget->name); | |
312 | config_widget->name = g_strdup(g_value_get_string(value)); | |
313 | break; | |
314 | case PROP_SUBCONFIG: | |
315 | if (config_widget->parser) | |
316 | sub_config_parser_free(config_widget->parser); | |
317 | config_widget->parser = sub_config_parser_new(g_value_get_string(value)); | |
318 | break; | |
319 | default: | |
320 | G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, pspec); | |
321 | break; | |
322 | } | |
323 | } | |
324 | ||
325 | static void set_none_font_clicked(GtkWidget *button, gpointer arg) | |
326 | { | |
327 | gtk_font_button_set_font_name(GTK_FONT_BUTTON(arg), ""); | |
328 | } | |
329 | ||
330 | void sync_filter(FcitxGenericConfig* gconfig, FcitxConfigGroup *group, FcitxConfigOption *option, void *value, FcitxConfigSync sync, void *arg) | |
331 | { | |
332 | FcitxConfigOptionDesc *codesc = option->optionDesc; | |
333 | if (!codesc) | |
334 | return; | |
335 | if (sync == Raw2Value) { | |
336 | switch (codesc->type) { | |
337 | case T_I18NString: | |
338 | break; | |
339 | case T_Integer: { | |
340 | int value = atoi(option->rawValue); | |
341 | gtk_spin_button_set_value(GTK_SPIN_BUTTON(arg), value); | |
342 | } | |
343 | break; | |
344 | case T_Color: { | |
345 | int r = 0, g = 0, b = 0; | |
346 | char scolor[9]; | |
347 | sscanf(option->rawValue, "%d %d %d", &r, &g, &b); | |
348 | r = RoundColor(r); | |
349 | g = RoundColor(g); | |
350 | b = RoundColor(b); | |
351 | snprintf(scolor, 8 , "#%02X%02X%02X", r, g, b); | |
352 | GdkRGBA color; | |
353 | gdk_rgba_parse(&color, scolor); | |
354 | #if GTK_CHECK_VERSION(3,3,0) | |
355 | gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(arg), &color); | |
356 | #else | |
357 | gtk_color_button_set_rgba(GTK_COLOR_BUTTON(arg), &color); | |
358 | #endif | |
359 | } | |
360 | break; | |
361 | case T_Boolean: { | |
362 | gboolean bl; | |
363 | if (strcmp(option->rawValue, "True") == 0) | |
364 | bl = TRUE; | |
365 | else | |
366 | bl = FALSE; | |
367 | ||
368 | gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(arg), bl); | |
369 | } | |
370 | break; | |
371 | case T_Font: { | |
372 | gtk_font_button_set_font_name(GTK_FONT_BUTTON(arg), option->rawValue); | |
373 | } | |
374 | break; | |
375 | case T_Enum: { | |
376 | FcitxConfigEnum* cenum = &codesc->configEnum; | |
377 | int index = 0, i; | |
378 | for (i = 0; i < cenum->enumCount; i++) { | |
379 | if (strcmp(cenum->enumDesc[i], option->rawValue) == 0) { | |
380 | index = i; | |
381 | } | |
382 | } | |
383 | gtk_combo_box_set_active(GTK_COMBO_BOX(arg), index); | |
384 | } | |
385 | break; | |
386 | case T_Hotkey: { | |
387 | FcitxHotkey hotkey[2]; | |
388 | int j; | |
389 | FcitxHotkeySetKey(option->rawValue, hotkey); | |
390 | GArray *array = (GArray*) arg; | |
391 | ||
392 | for (j = 0; j < 2; j ++) { | |
393 | GtkWidget *button = g_array_index(array, GtkWidget*, j); | |
394 | keygrab_button_set_key(KEYGRAB_BUTTON(button), hotkey[j].sym, hotkey[j].state); | |
395 | if (hotkey[j].desc) | |
396 | free(hotkey[j].desc); | |
397 | } | |
398 | } | |
399 | break; | |
400 | case T_File: | |
401 | case T_Char: | |
402 | case T_String: { | |
403 | gtk_entry_set_text(GTK_ENTRY(arg), option->rawValue); | |
404 | } | |
405 | break; | |
406 | } | |
407 | } else { | |
408 | if (codesc->type != T_I18NString && option->rawValue) { | |
409 | free(option->rawValue); | |
410 | option->rawValue = NULL; | |
411 | } | |
412 | switch (codesc->type) { | |
413 | case T_I18NString: | |
414 | break; | |
415 | case T_Integer: { | |
416 | int value; | |
417 | value = gtk_spin_button_get_value(GTK_SPIN_BUTTON(arg)); | |
418 | option->rawValue = g_strdup_printf("%d", value); | |
419 | } | |
420 | break; | |
421 | case T_Color: { | |
422 | int r = 0, g = 0, b = 0; | |
423 | GdkRGBA color; | |
424 | #if GTK_CHECK_VERSION(3,3,0) | |
425 | gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(arg), &color); | |
426 | #else | |
427 | gtk_color_button_get_rgba(GTK_COLOR_BUTTON(arg), &color); | |
428 | #endif | |
429 | r = color.red / 256; | |
430 | g = color.green / 256; | |
431 | b = color.blue / 256; | |
432 | r = RoundColor(r); | |
433 | g = RoundColor(g); | |
434 | b = RoundColor(b); | |
435 | option->rawValue = g_strdup_printf("%d %d %d", r, g, b); | |
436 | } | |
437 | break; | |
438 | case T_Boolean: { | |
439 | gboolean bl; | |
440 | bl = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(arg)); | |
441 | if (bl) | |
442 | option->rawValue = strdup("True"); | |
443 | else | |
444 | option->rawValue = strdup("False"); | |
445 | } | |
446 | break; | |
447 | case T_Font: { | |
448 | const char *font = gtk_font_button_get_font_name(GTK_FONT_BUTTON(arg)); | |
449 | PangoFontDescription *fontdesc = pango_font_description_from_string(font); | |
450 | if (fontdesc) { | |
451 | const char *family = pango_font_description_get_family(fontdesc); | |
452 | if (family) | |
453 | option->rawValue = strdup(family); | |
454 | else | |
455 | option->rawValue = strdup(""); | |
456 | pango_font_description_free(fontdesc); | |
457 | } else | |
458 | option->rawValue = strdup(""); | |
459 | } | |
460 | break; | |
461 | case T_Enum: { | |
462 | FcitxConfigEnum* cenum = &codesc->configEnum; | |
463 | int index = 0; | |
464 | index = gtk_combo_box_get_active(GTK_COMBO_BOX(arg)); | |
465 | option->rawValue = strdup(cenum->enumDesc[index]); | |
466 | } | |
467 | break; | |
468 | case T_Hotkey: { | |
469 | GArray *array = (GArray*) arg; | |
470 | GtkWidget *button; | |
471 | guint key; | |
472 | GdkModifierType mods; | |
473 | char *strkey[2] = { NULL, NULL }; | |
474 | int j = 0, k = 0; | |
475 | ||
476 | for (j = 0; j < 2 ; j ++) { | |
477 | button = g_array_index(array, GtkWidget*, j); | |
478 | keygrab_button_get_key(KEYGRAB_BUTTON(button), &key, &mods); | |
479 | strkey[k] = FcitxHotkeyGetKeyString(key, mods); | |
480 | if (strkey[k]) | |
481 | k ++; | |
482 | } | |
483 | if (strkey[1]) | |
484 | option->rawValue = g_strdup_printf("%s %s", strkey[0], strkey[1]); | |
485 | else if (strkey[0]) { | |
486 | option->rawValue = strdup(strkey[0]); | |
487 | } else | |
488 | option->rawValue = strdup(""); | |
489 | ||
490 | for (j = 0 ; j < k ; j ++) | |
491 | free(strkey[j]); | |
492 | ||
493 | } | |
494 | break; | |
495 | case T_File: | |
496 | case T_Char: | |
497 | case T_String: { | |
498 | option->rawValue = strdup(gtk_entry_get_text(GTK_ENTRY(arg))); | |
499 | } | |
500 | break; | |
501 | } | |
502 | ||
503 | } | |
504 | } | |
505 | ||
506 | void fcitx_config_widget_response( | |
507 | FcitxConfigWidget* config_widget, | |
508 | ConfigWidgetAction action | |
509 | ) | |
510 | { | |
511 | if (!config_widget->cfdesc) | |
512 | return; | |
513 | ||
514 | if (action == CONFIG_WIDGET_DEFAULT) { | |
515 | FcitxConfigResetConfigToDefaultValue(&config_widget->gconfig); | |
516 | FcitxConfigBindSync(&config_widget->gconfig); | |
517 | } else if (action == CONFIG_WIDGET_SAVE) { | |
518 | FILE* fp = FcitxXDGGetFileUserWithPrefix(config_widget->prefix, config_widget->name, "w", NULL); | |
519 | ||
520 | if (fp) { | |
521 | FcitxConfigSaveConfigFileFp(fp, &config_widget->gconfig, config_widget->cfdesc); | |
522 | fclose(fp); | |
523 | ||
524 | GError* error; | |
525 | gchar* argv[3]; | |
526 | argv[0] = EXEC_PREFIX "/bin/fcitx-remote"; | |
527 | argv[1] = "-r"; | |
528 | argv[2] = 0; | |
529 | g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, &error); | |
530 | } | |
531 | } | |
532 | } | |
533 | ||
534 | void fcitx_config_widget_finalize(GObject *object) | |
535 | { | |
536 | FcitxConfigWidget* config_widget = FCITX_CONFIG_WIDGET(object); | |
537 | g_free(config_widget->name); | |
538 | g_free(config_widget->prefix); | |
539 | sub_config_parser_free(config_widget->parser); | |
540 | G_OBJECT_CLASS(fcitx_config_widget_parent_class)->finalize(object); | |
541 | } | |
542 | ||
543 | void hash_foreach_cb(gpointer key, | |
544 | gpointer value, | |
545 | gpointer user_data) | |
546 | { | |
547 | HashForeachContext* context = user_data; | |
548 | FcitxConfigWidget* widget = context->widget; | |
549 | ||
550 | FcitxSubConfigPattern* pattern = value; | |
551 | FcitxSubConfig* subconfig = sub_config_new(key, pattern); | |
552 | ||
553 | if (subconfig == NULL) | |
554 | return; | |
555 | ||
556 | int i = context->i; | |
557 | ||
558 | GtkWidget* label = gtk_label_new(dgettext(widget->parser->domain, subconfig->name)); | |
559 | g_object_set(label, "xalign", 0.0f, NULL); | |
560 | ||
561 | GtkWidget *inputWidget = GTK_WIDGET(fcitx_sub_config_widget_new(subconfig)); | |
562 | ||
563 | gtk_grid_attach(GTK_GRID(context->grid), label, 0, i, 1, 1); | |
564 | gtk_grid_attach(GTK_GRID(context->grid), inputWidget, 1, i, 1, 1); | |
565 | context->i ++; | |
566 | } | |
567 | ||
568 | gboolean fcitx_config_widget_response_cb(GtkDialog *dialog, | |
569 | gint response, | |
570 | gpointer user_data) | |
571 | { | |
572 | if (response == GTK_RESPONSE_OK) { | |
573 | FcitxConfigWidget* config_widget = (FcitxConfigWidget*) user_data; | |
574 | fcitx_config_widget_response(config_widget, CONFIG_WIDGET_SAVE); | |
575 | } | |
576 | gtk_widget_destroy(GTK_WIDGET(dialog)); | |
577 | return FALSE; | |
578 | } |
0 | /*************************************************************************** | |
1 | * Copyright (C) 2010~2012 by CSSlayer * | |
2 | * * | |
3 | * This program is free software; you can redistribute it and/or modify * | |
4 | * it under the terms of the GNU General Public License as published by * | |
5 | * the Free Software Foundation; either version 2 of the License, or * | |
6 | * (at your option) any later version. * | |
7 | * * | |
8 | * This program is distributed in the hope that it will be useful, * | |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * | |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * | |
11 | * GNU General Public License for more details. * | |
12 | * * | |
13 | * You should have received a copy of the GNU General Public License * | |
14 | * along with this program; if not, write to the * | |
15 | * Free Software Foundation, Inc., * | |
16 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * | |
17 | ***************************************************************************/ | |
18 | ||
19 | /* fcitx-config-widget.h */ | |
20 | ||
21 | #ifndef _FCITX_CONFIG_WIDGET | |
22 | #define _FCITX_CONFIG_WIDGET | |
23 | ||
24 | #include <gtk/gtk.h> | |
25 | #include <glib.h> | |
26 | #include <fcitx-config/fcitx-config.h> | |
27 | #include "sub_config_parser.h" | |
28 | ||
29 | G_BEGIN_DECLS | |
30 | ||
31 | #define FCITX_TYPE_CONFIG_WIDGET fcitx_config_widget_get_type() | |
32 | ||
33 | #define FCITX_CONFIG_WIDGET(obj) \ | |
34 | (G_TYPE_CHECK_INSTANCE_CAST ((obj), FCITX_TYPE_CONFIG_WIDGET, FcitxConfigWidget)) | |
35 | ||
36 | #define FCITX_CONFIG_WIDGET_CLASS(klass) \ | |
37 | (G_TYPE_CHECK_CLASS_CAST ((klass), FCITX_TYPE_CONFIG_WIDGET, FcitxConfigWidgetClass)) | |
38 | ||
39 | #define FCITX_IS_CONFIG_WIDGET(obj) \ | |
40 | (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FCITX_TYPE_CONFIG_WIDGET)) | |
41 | ||
42 | #define FCITX_IS_CONFIG_WIDGET_CLASS(klass) \ | |
43 | (G_TYPE_CHECK_CLASS_TYPE ((klass), FCITX_TYPE_CONFIG_WIDGET)) | |
44 | ||
45 | #define FCITX_CONFIG_WIDGET_GET_CLASS(obj) \ | |
46 | (G_TYPE_INSTANCE_GET_CLASS ((obj), FCITX_TYPE_CONFIG_WIDGET, FcitxConfigWidgetClass)) | |
47 | ||
48 | typedef struct { | |
49 | GtkBox parent; | |
50 | FcitxConfigFileDesc* cfdesc; | |
51 | gchar* prefix; | |
52 | gchar* name; | |
53 | FcitxSubConfigParser* parser; | |
54 | FcitxGenericConfig gconfig; | |
55 | } FcitxConfigWidget; | |
56 | ||
57 | typedef struct { | |
58 | GtkBoxClass parent_class; | |
59 | } FcitxConfigWidgetClass; | |
60 | ||
61 | typedef enum { | |
62 | CONFIG_WIDGET_SAVE, | |
63 | CONFIG_WIDGET_CANCEL, | |
64 | CONFIG_WIDGET_DEFAULT | |
65 | } ConfigWidgetAction; | |
66 | ||
67 | GType fcitx_config_widget_get_type(void); | |
68 | ||
69 | FcitxConfigWidget* fcitx_config_widget_new(FcitxConfigFileDesc* cfdesc, const gchar* prefix, const gchar* name, const char* subconfig); | |
70 | ||
71 | void fcitx_config_widget_response(FcitxConfigWidget* config_widget, ConfigWidgetAction action); | |
72 | ||
73 | gboolean fcitx_config_widget_response_cb(GtkDialog *dialog, | |
74 | gint response, | |
75 | gpointer user_data); | |
76 | ||
77 | G_END_DECLS | |
78 | ||
79 | #endif /* _FCITX_CONFIG_WIDGET */ |
0 | /*************************************************************************** | |
1 | * Copyright (C) 2010~2012 by CSSlayer * | |
2 | * * | |
3 | * This program is free software; you can redistribute it and/or modify * | |
4 | * it under the terms of the GNU General Public License as published by * | |
5 | * the Free Software Foundation; either version 2 of the License, or * | |
6 | * (at your option) any later version. * | |
7 | * * | |
8 | * This program is distributed in the hope that it will be useful, * | |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * | |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * | |
11 | * GNU General Public License for more details. * | |
12 | * * | |
13 | * You should have received a copy of the GNU General Public License * | |
14 | * along with this program; if not, write to the * | |
15 | * Free Software Foundation, Inc., * | |
16 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * | |
17 | ***************************************************************************/ | |
18 | ||
19 | #include <fcitx-utils/uthash.h> | |
20 | #include <fcitx-config/fcitx-config.h> | |
21 | #include <fcitx-config/xdg.h> | |
22 | #include <stdlib.h> | |
23 | #include <stdio.h> | |
24 | ||
25 | #include "configdesc.h" | |
26 | ||
27 | typedef struct ConfigDescSet { | |
28 | char *filename; | |
29 | FcitxConfigFileDesc *cfdesc; | |
30 | UT_hash_handle hh; | |
31 | } ConfigDescSet; | |
32 | ||
33 | static ConfigDescSet* cdset = NULL; | |
34 | ||
35 | FcitxConfigFileDesc *get_config_desc(char *filename) | |
36 | { | |
37 | ConfigDescSet *desc = NULL; | |
38 | HASH_FIND_STR(cdset, filename, desc); | |
39 | if (!desc) { | |
40 | FILE * tmpfp = FcitxXDGGetFileWithPrefix("configdesc", filename, "r", NULL); | |
41 | if (tmpfp) { | |
42 | desc = malloc(sizeof(ConfigDescSet)); | |
43 | memset(desc, 0 , sizeof(ConfigDescSet)); | |
44 | desc->filename = strdup(filename); | |
45 | desc->cfdesc = FcitxConfigParseConfigFileDescFp(tmpfp); | |
46 | fclose(tmpfp); | |
47 | ||
48 | HASH_ADD_KEYPTR(hh, cdset, desc->filename, strlen(desc->filename), desc); | |
49 | } else | |
50 | return NULL; | |
51 | } | |
52 | ||
53 | return desc->cfdesc; | |
54 | } |
0 | /*************************************************************************** | |
1 | * Copyright (C) 2010~2012 by CSSlayer * | |
2 | * * | |
3 | * This program is free software; you can redistribute it and/or modify * | |
4 | * it under the terms of the GNU General Public License as published by * | |
5 | * the Free Software Foundation; either version 2 of the License, or * | |
6 | * (at your option) any later version. * | |
7 | * * | |
8 | * This program is distributed in the hope that it will be useful, * | |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * | |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * | |
11 | * GNU General Public License for more details. * | |
12 | * * | |
13 | * You should have received a copy of the GNU General Public License * | |
14 | * along with this program; if not, write to the * | |
15 | * Free Software Foundation, Inc., * | |
16 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * | |
17 | ***************************************************************************/ | |
18 | ||
19 | #ifndef CONFIG_DESC_H | |
20 | #define CONFIG_DESC_H | |
21 | ||
22 | #include <fcitx-config/fcitx-config.h> | |
23 | ||
24 | FcitxConfigFileDesc *get_config_desc(char *filename); | |
25 | ||
26 | #endif |
0 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- | |
1 | * | |
2 | * Copyright 2008 Red Hat, Inc, | |
3 | * 2007 William Jon McCann <mccann@jhu.edu> | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License as published by | |
7 | * the Free Software Foundation; either version 2 of the License, or | |
8 | * (at your option) any later version. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License | |
16 | * along with this program; if not, write to the Free Software | |
17 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
18 | * | |
19 | * Written by : William Jon McCann <mccann@jhu.edu> | |
20 | * Ray Strode <rstrode@redhat.com> | |
21 | */ | |
22 | ||
23 | #include "config.h" | |
24 | #include "common.h" | |
25 | ||
26 | #include <stdlib.h> | |
27 | #include <stdio.h> | |
28 | #include <unistd.h> | |
29 | #include <string.h> | |
30 | #include <errno.h> | |
31 | #include <dirent.h> | |
32 | #include <locale.h> | |
33 | #include <langinfo.h> | |
34 | #include <sys/stat.h> | |
35 | ||
36 | #include <glib.h> | |
37 | ||
38 | #include "gdm-languages.h" | |
39 | ||
40 | #include <langinfo.h> | |
41 | #ifndef __LC_LAST | |
42 | #define __LC_LAST 13 | |
43 | #endif | |
44 | #include "locarchive.h" | |
45 | ||
46 | #define ALIASES_FILE DATADIR "/gdm/locale.alias" | |
47 | #define ARCHIVE_FILE LIBLOCALEDIR "/locale-archive" | |
48 | #define ISO_CODES_DATADIR ISO_CODES_PREFIX "/share/xml/iso-codes" | |
49 | #define ISO_CODES_LOCALESDIR ISO_CODES_PREFIX "/share/locale" | |
50 | ||
51 | typedef struct _GdmLocale { | |
52 | char *id; | |
53 | char *name; | |
54 | char *language_code; | |
55 | char *territory_code; | |
56 | char *codeset; | |
57 | char *modifier; | |
58 | } GdmLocale; | |
59 | ||
60 | static GHashTable *gdm_languages_map; | |
61 | static GHashTable *gdm_territories_map; | |
62 | static GHashTable *gdm_available_locales_map; | |
63 | ||
64 | static char * construct_language_name(const char *language, | |
65 | const char *territory, | |
66 | const char *codeset, | |
67 | const char *modifier); | |
68 | ||
69 | static gboolean language_name_is_valid(const char *language_name); | |
70 | ||
71 | static void | |
72 | gdm_locale_free(GdmLocale *locale) | |
73 | { | |
74 | if (locale == NULL) { | |
75 | return; | |
76 | } | |
77 | ||
78 | g_free(locale->id); | |
79 | g_free(locale->name); | |
80 | g_free(locale->codeset); | |
81 | g_free(locale->modifier); | |
82 | g_free(locale); | |
83 | } | |
84 | ||
85 | static char * | |
86 | normalize_codeset(const char *codeset) | |
87 | { | |
88 | char *normalized_codeset; | |
89 | const char *p; | |
90 | char *q; | |
91 | ||
92 | normalized_codeset = g_strdup(codeset); | |
93 | ||
94 | if (codeset != NULL) { | |
95 | for (p = codeset, q = normalized_codeset; | |
96 | *p != '\0'; p++) { | |
97 | ||
98 | if (*p == '-' || *p == '_') { | |
99 | continue; | |
100 | } | |
101 | ||
102 | *q = g_ascii_tolower(*p); | |
103 | q++; | |
104 | } | |
105 | *q = '\0'; | |
106 | } | |
107 | ||
108 | return normalized_codeset; | |
109 | } | |
110 | ||
111 | /* | |
112 | * According to http://en.wikipedia.org/wiki/Locale | |
113 | * locale names are of the form: | |
114 | * [language[_territory][.codeset][@modifier]] | |
115 | */ | |
116 | void | |
117 | gdm_parse_language_name(const char *name, | |
118 | char **language_codep, | |
119 | char **territory_codep, | |
120 | char **codesetp, | |
121 | char **modifierp) | |
122 | { | |
123 | GRegex *re; | |
124 | GMatchInfo *match_info; | |
125 | gboolean res; | |
126 | GError *error; | |
127 | gchar *normalized_codeset = NULL; | |
128 | gchar *normalized_name = NULL; | |
129 | ||
130 | match_info = NULL; | |
131 | ||
132 | error = NULL; | |
133 | re = g_regex_new("^(?P<language>[^_.@[:space:]]+)" | |
134 | "(_(?P<territory>[[:upper:]]+))?" | |
135 | "(\\.(?P<codeset>[-_0-9a-zA-Z]+))?" | |
136 | "(@(?P<modifier>[[:ascii:]]+))?$", | |
137 | 0, 0, &error); | |
138 | if (re == NULL) { | |
139 | g_warning("%s", error->message); | |
140 | goto out; | |
141 | } | |
142 | ||
143 | if (!g_regex_match(re, name, 0, &match_info) || | |
144 | g_match_info_is_partial_match(match_info)) { | |
145 | g_warning("locale %s isn't valid\n", name); | |
146 | goto out; | |
147 | } | |
148 | ||
149 | res = g_match_info_matches(match_info); | |
150 | if (! res) { | |
151 | g_warning("Unable to parse locale: %s", name); | |
152 | goto out; | |
153 | } | |
154 | ||
155 | if (language_codep != NULL) { | |
156 | *language_codep = g_match_info_fetch_named(match_info, "language"); | |
157 | } | |
158 | ||
159 | if (territory_codep != NULL) { | |
160 | *territory_codep = g_match_info_fetch_named(match_info, "territory"); | |
161 | ||
162 | if (*territory_codep != NULL && | |
163 | *territory_codep[0] == '\0') { | |
164 | g_free(*territory_codep); | |
165 | *territory_codep = NULL; | |
166 | } | |
167 | } | |
168 | ||
169 | if (codesetp != NULL) { | |
170 | *codesetp = g_match_info_fetch_named(match_info, "codeset"); | |
171 | ||
172 | if (*codesetp != NULL && | |
173 | *codesetp[0] == '\0') { | |
174 | g_free(*codesetp); | |
175 | *codesetp = NULL; | |
176 | } | |
177 | } | |
178 | ||
179 | if (modifierp != NULL) { | |
180 | *modifierp = g_match_info_fetch_named(match_info, "modifier"); | |
181 | ||
182 | if (*modifierp != NULL && | |
183 | *modifierp[0] == '\0') { | |
184 | g_free(*modifierp); | |
185 | *modifierp = NULL; | |
186 | } | |
187 | } | |
188 | ||
189 | if (codesetp != NULL && *codesetp != NULL) { | |
190 | normalized_codeset = normalize_codeset(*codesetp); | |
191 | normalized_name = construct_language_name(language_codep ? *language_codep : NULL, | |
192 | territory_codep ? *territory_codep : NULL, | |
193 | normalized_codeset, | |
194 | modifierp ? *modifierp : NULL); | |
195 | ||
196 | if (language_name_is_valid(normalized_name)) { | |
197 | g_free(*codesetp); | |
198 | *codesetp = normalized_codeset; | |
199 | } else { | |
200 | g_free(normalized_codeset); | |
201 | } | |
202 | g_free(normalized_name); | |
203 | } | |
204 | ||
205 | out: | |
206 | g_match_info_free(match_info); | |
207 | g_regex_unref(re); | |
208 | } | |
209 | ||
210 | static char * | |
211 | construct_language_name(const char *language, | |
212 | const char *territory, | |
213 | const char *codeset, | |
214 | const char *modifier) | |
215 | { | |
216 | char *name; | |
217 | ||
218 | g_assert(language[0] != 0); | |
219 | g_assert(territory == NULL || territory[0] != 0); | |
220 | g_assert(codeset == NULL || codeset[0] != 0); | |
221 | g_assert(modifier == NULL || modifier[0] != 0); | |
222 | ||
223 | name = g_strdup_printf("%s%s%s%s%s%s%s", | |
224 | language, | |
225 | territory != NULL ? "_" : "", | |
226 | territory != NULL ? territory : "", | |
227 | codeset != NULL ? "." : "", | |
228 | codeset != NULL ? codeset : "", | |
229 | modifier != NULL ? "@" : "", | |
230 | modifier != NULL ? modifier : ""); | |
231 | ||
232 | return name; | |
233 | } | |
234 | ||
235 | char * | |
236 | gdm_normalize_language_name(const char *name) | |
237 | { | |
238 | char *normalized_name; | |
239 | char *language_code; | |
240 | char *territory_code; | |
241 | char *codeset; | |
242 | char *modifier; | |
243 | ||
244 | if (name[0] == '\0') { | |
245 | return NULL; | |
246 | } | |
247 | ||
248 | gdm_parse_language_name(name, | |
249 | &language_code, | |
250 | &territory_code, | |
251 | &codeset, &modifier); | |
252 | ||
253 | normalized_name = construct_language_name(language_code, | |
254 | territory_code, | |
255 | codeset, modifier); | |
256 | g_free(language_code); | |
257 | g_free(territory_code); | |
258 | g_free(codeset); | |
259 | g_free(modifier); | |
260 | ||
261 | return normalized_name; | |
262 | } | |
263 | ||
264 | static gboolean | |
265 | language_name_is_valid(const char *language_name) | |
266 | { | |
267 | char *old_locale; | |
268 | gboolean is_valid; | |
269 | #ifdef WITH_INCOMPLETE_LOCALES | |
270 | int lc_type_id = LC_CTYPE; | |
271 | #else | |
272 | int lc_type_id = LC_MESSAGES; | |
273 | #endif | |
274 | ||
275 | old_locale = g_strdup(setlocale(lc_type_id, NULL)); | |
276 | is_valid = setlocale(lc_type_id, language_name) != NULL; | |
277 | setlocale(lc_type_id, old_locale); | |
278 | g_free(old_locale); | |
279 | ||
280 | return is_valid; | |
281 | } | |
282 | ||
283 | static void | |
284 | language_name_get_codeset_details(const char *language_name, | |
285 | char **pcodeset, | |
286 | gboolean *is_utf8) | |
287 | { | |
288 | char *old_locale; | |
289 | char *codeset; | |
290 | ||
291 | old_locale = g_strdup(setlocale(LC_CTYPE, NULL)); | |
292 | ||
293 | if (setlocale(LC_CTYPE, language_name) == NULL) { | |
294 | g_free(old_locale); | |
295 | return; | |
296 | } | |
297 | ||
298 | codeset = nl_langinfo(CODESET); | |
299 | ||
300 | if (pcodeset != NULL) { | |
301 | *pcodeset = g_strdup(codeset); | |
302 | } | |
303 | ||
304 | if (is_utf8 != NULL) { | |
305 | codeset = normalize_codeset(codeset); | |
306 | ||
307 | *is_utf8 = strcmp(codeset, "utf8") == 0; | |
308 | g_free(codeset); | |
309 | } | |
310 | ||
311 | setlocale(LC_CTYPE, old_locale); | |
312 | g_free(old_locale); | |
313 | } | |
314 | ||
315 | static gboolean | |
316 | language_name_has_translations(const char *language_name) | |
317 | { | |
318 | GDir *dir; | |
319 | char *path; | |
320 | const char *name; | |
321 | gboolean has_translations; | |
322 | ||
323 | path = g_build_filename(LOCALEDIR, language_name, "LC_MESSAGES", NULL); | |
324 | ||
325 | has_translations = FALSE; | |
326 | dir = g_dir_open(path, 0, NULL); | |
327 | g_free(path); | |
328 | ||
329 | if (dir == NULL) { | |
330 | goto out; | |
331 | } | |
332 | ||
333 | do { | |
334 | name = g_dir_read_name(dir); | |
335 | ||
336 | if (name == NULL) { | |
337 | break; | |
338 | } | |
339 | ||
340 | if (g_str_has_suffix(name, ".mo")) { | |
341 | has_translations = TRUE; | |
342 | break; | |
343 | } | |
344 | } while (name != NULL); | |
345 | g_dir_close(dir); | |
346 | ||
347 | out: | |
348 | return has_translations; | |
349 | } | |
350 | ||
351 | static gboolean | |
352 | add_locale(const char *language_name, | |
353 | gboolean utf8_only) | |
354 | { | |
355 | GdmLocale *locale; | |
356 | GdmLocale *old_locale; | |
357 | char *name; | |
358 | gboolean is_utf8; | |
359 | ||
360 | g_return_val_if_fail(language_name != NULL, FALSE); | |
361 | ||
362 | language_name_get_codeset_details(language_name, NULL, &is_utf8); | |
363 | ||
364 | if (is_utf8) { | |
365 | name = g_strdup(language_name); | |
366 | } else if (utf8_only) { | |
367 | name = g_strdup_printf("%s.utf8", language_name); | |
368 | ||
369 | language_name_get_codeset_details(name, NULL, &is_utf8); | |
370 | if (is_utf8) { | |
371 | g_free(name); | |
372 | return FALSE; | |
373 | } | |
374 | } else { | |
375 | name = g_strdup(language_name); | |
376 | } | |
377 | ||
378 | if (!language_name_is_valid(name)) { | |
379 | g_warning("Your locale '%s' was failed by setlocale()", name); | |
380 | g_free(name); | |
381 | return FALSE; | |
382 | } | |
383 | ||
384 | locale = g_new0(GdmLocale, 1); | |
385 | gdm_parse_language_name(name, | |
386 | &locale->language_code, | |
387 | &locale->territory_code, | |
388 | &locale->codeset, | |
389 | &locale->modifier); | |
390 | g_free(name); | |
391 | name = NULL; | |
392 | ||
393 | #ifdef WITH_INCOMPLETE_LOCALES | |
394 | if (utf8_only) { | |
395 | if (locale->territory_code == NULL || locale->modifier) { | |
396 | gdm_locale_free(locale); | |
397 | return FALSE; | |
398 | } | |
399 | } | |
400 | #endif | |
401 | ||
402 | locale->id = construct_language_name(locale->language_code, locale->territory_code, | |
403 | NULL, locale->modifier); | |
404 | locale->name = construct_language_name(locale->language_code, locale->territory_code, | |
405 | locale->codeset, locale->modifier); | |
406 | ||
407 | #ifndef WITH_INCOMPLETE_LOCALES | |
408 | if (!language_name_has_translations(locale->name) && | |
409 | !language_name_has_translations(locale->id) && | |
410 | !language_name_has_translations(locale->language_code) && | |
411 | utf8_only) { | |
412 | g_warning("Your locale '%s' doesn't have message catalog files", | |
413 | language_name); | |
414 | gdm_locale_free(locale); | |
415 | return FALSE; | |
416 | } | |
417 | #endif | |
418 | ||
419 | if (!utf8_only) { | |
420 | g_free(locale->id); | |
421 | locale->id = g_strdup(locale->name); | |
422 | } | |
423 | ||
424 | old_locale = g_hash_table_lookup(gdm_available_locales_map, locale->id); | |
425 | if (old_locale != NULL) { | |
426 | if (strlen(old_locale->name) > strlen(locale->name)) { | |
427 | gdm_locale_free(locale); | |
428 | return FALSE; | |
429 | } | |
430 | } | |
431 | ||
432 | g_hash_table_insert(gdm_available_locales_map, g_strdup(locale->id), locale); | |
433 | ||
434 | return TRUE; | |
435 | } | |
436 | ||
437 | struct nameent { | |
438 | char *name; | |
439 | uint32_t locrec_offset; | |
440 | }; | |
441 | ||
442 | static gboolean | |
443 | collect_locales_from_archive(void) | |
444 | { | |
445 | GMappedFile *mapped; | |
446 | GError *error; | |
447 | char *addr; | |
448 | struct locarhead *head; | |
449 | struct namehashent *namehashtab; | |
450 | struct nameent *names; | |
451 | uint32_t used; | |
452 | uint32_t cnt; | |
453 | gsize len; | |
454 | gboolean locales_collected; | |
455 | ||
456 | error = NULL; | |
457 | mapped = g_mapped_file_new(ARCHIVE_FILE, FALSE, &error); | |
458 | if (mapped == NULL) { | |
459 | g_warning("Mapping failed for %s: %s", ARCHIVE_FILE, error->message); | |
460 | g_error_free(error); | |
461 | return FALSE; | |
462 | } | |
463 | ||
464 | locales_collected = FALSE; | |
465 | ||
466 | addr = g_mapped_file_get_contents(mapped); | |
467 | len = g_mapped_file_get_length(mapped); | |
468 | ||
469 | head = (struct locarhead *) addr; | |
470 | if (head->namehash_offset + head->namehash_size > len | |
471 | || head->string_offset + head->string_size > len | |
472 | || head->locrectab_offset + head->locrectab_size > len | |
473 | || head->sumhash_offset + head->sumhash_size > len) { | |
474 | goto out; | |
475 | } | |
476 | ||
477 | namehashtab = (struct namehashent *)(addr + head->namehash_offset); | |
478 | ||
479 | names = (struct nameent *) g_new0(struct nameent, head->namehash_used); | |
480 | for (cnt = used = 0; cnt < head->namehash_size; ++cnt) { | |
481 | if (namehashtab[cnt].locrec_offset != 0) { | |
482 | names[used].name = addr + namehashtab[cnt].name_offset; | |
483 | names[used++].locrec_offset = namehashtab[cnt].locrec_offset; | |
484 | } | |
485 | } | |
486 | ||
487 | for (cnt = 0; cnt < used; ++cnt) { | |
488 | add_locale(names[cnt].name, TRUE); | |
489 | } | |
490 | ||
491 | g_free(names); | |
492 | ||
493 | locales_collected = TRUE; | |
494 | out: | |
495 | ||
496 | g_mapped_file_unref(mapped); | |
497 | return locales_collected; | |
498 | } | |
499 | ||
500 | static int | |
501 | select_dirs(const struct dirent *dirent) | |
502 | { | |
503 | int result = 0; | |
504 | ||
505 | if (strcmp(dirent->d_name, ".") != 0 && strcmp(dirent->d_name, "..") != 0) { | |
506 | mode_t mode = 0; | |
507 | ||
508 | #ifdef _DIRENT_HAVE_D_TYPE | |
509 | if (dirent->d_type != DT_UNKNOWN && dirent->d_type != DT_LNK) { | |
510 | mode = DTTOIF(dirent->d_type); | |
511 | } else | |
512 | #endif | |
513 | { | |
514 | struct stat st; | |
515 | char *path; | |
516 | ||
517 | path = g_build_filename(LIBLOCALEDIR, dirent->d_name, NULL); | |
518 | if (stat(path, &st) == 0) { | |
519 | mode = st.st_mode; | |
520 | } | |
521 | g_free(path); | |
522 | } | |
523 | ||
524 | result = S_ISDIR(mode); | |
525 | } | |
526 | ||
527 | return result; | |
528 | } | |
529 | ||
530 | static void | |
531 | collect_locales_from_directory(void) | |
532 | { | |
533 | struct dirent **dirents; | |
534 | int ndirents; | |
535 | int cnt; | |
536 | ||
537 | ndirents = scandir(LIBLOCALEDIR, &dirents, select_dirs, alphasort); | |
538 | ||
539 | for (cnt = 0; cnt < ndirents; ++cnt) { | |
540 | add_locale(dirents[cnt]->d_name, TRUE); | |
541 | } | |
542 | ||
543 | if (ndirents > 0) { | |
544 | free(dirents); | |
545 | } | |
546 | } | |
547 | ||
548 | static void | |
549 | collect_locales_from_locale_file(const char *locale_file) | |
550 | { | |
551 | FILE *langlist; | |
552 | char curline[256]; | |
553 | char *getsret; | |
554 | ||
555 | if (locale_file == NULL) | |
556 | return; | |
557 | ||
558 | langlist = fopen(locale_file, "r"); | |
559 | ||
560 | if (langlist == NULL) | |
561 | return; | |
562 | ||
563 | for (;;) { | |
564 | char *name; | |
565 | char *lang; | |
566 | char **lang_list; | |
567 | int i; | |
568 | ||
569 | getsret = fgets(curline, sizeof(curline), langlist); | |
570 | if (getsret == NULL) | |
571 | break; | |
572 | ||
573 | if (curline[0] <= ' ' || | |
574 | curline[0] == '#') | |
575 | continue; | |
576 | ||
577 | name = strtok(curline, " \t\r\n"); | |
578 | if (name == NULL) | |
579 | continue; | |
580 | ||
581 | lang = strtok(NULL, " \t\r\n"); | |
582 | if (lang == NULL) | |
583 | continue; | |
584 | ||
585 | lang_list = g_strsplit(lang, ",", -1); | |
586 | if (lang_list == NULL) | |
587 | continue; | |
588 | ||
589 | lang = NULL; | |
590 | for (i = 0; lang_list[i] != NULL; i++) { | |
591 | if (add_locale(lang_list[i], FALSE)) { | |
592 | break; | |
593 | } | |
594 | } | |
595 | g_strfreev(lang_list); | |
596 | } | |
597 | ||
598 | fclose(langlist); | |
599 | } | |
600 | ||
601 | static void | |
602 | collect_locales(void) | |
603 | { | |
604 | ||
605 | if (gdm_available_locales_map == NULL) { | |
606 | gdm_available_locales_map = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify) gdm_locale_free); | |
607 | } | |
608 | ||
609 | if (!collect_locales_from_archive()) { | |
610 | #ifndef WITH_INCOMPLETE_LOCALES | |
611 | g_warning("Could not read list of available locales from libc, " | |
612 | "guessing possible locales from available translations, " | |
613 | "but list may be incomplete!"); | |
614 | #endif | |
615 | ||
616 | collect_locales_from_directory(); | |
617 | } | |
618 | collect_locales_from_locale_file(ALIASES_FILE); | |
619 | } | |
620 | ||
621 | static gboolean | |
622 | is_fallback_language(const char *code) | |
623 | { | |
624 | const char *fallback_language_names[] = { "C", "POSIX", NULL }; | |
625 | int i; | |
626 | ||
627 | for (i = 0; fallback_language_names[i] != NULL; i++) { | |
628 | if (strcmp(code, fallback_language_names[i]) == 0) { | |
629 | return TRUE; | |
630 | } | |
631 | } | |
632 | ||
633 | return FALSE; | |
634 | } | |
635 | ||
636 | static const char * | |
637 | get_language(const char *code) | |
638 | { | |
639 | const char *name; | |
640 | int len; | |
641 | ||
642 | g_assert(code != NULL); | |
643 | ||
644 | if (is_fallback_language(code)) { | |
645 | return "Unspecified"; | |
646 | } | |
647 | ||
648 | len = strlen(code); | |
649 | if (len != 2 && len != 3) { | |
650 | return NULL; | |
651 | } | |
652 | ||
653 | name = (const char *) g_hash_table_lookup(gdm_languages_map, code); | |
654 | ||
655 | return name; | |
656 | } | |
657 | ||
658 | static char * | |
659 | get_first_item_in_semicolon_list(const char *list) | |
660 | { | |
661 | char **items; | |
662 | char *item; | |
663 | ||
664 | /* Some entries in iso codes have multiple values, separated | |
665 | * by semicolons. Not really sure which one to pick, so | |
666 | * we just arbitrarily pick the first one. | |
667 | */ | |
668 | items = g_strsplit(list, "; ", 2); | |
669 | ||
670 | item = g_strdup(items[0]); | |
671 | g_strfreev(items); | |
672 | ||
673 | return item; | |
674 | } | |
675 | ||
676 | static char * | |
677 | get_translated_language(const char *code, | |
678 | const char *locale) | |
679 | { | |
680 | const char *language; | |
681 | char *name; | |
682 | ||
683 | language = get_language(code); | |
684 | ||
685 | name = NULL; | |
686 | if (language != NULL) { | |
687 | const char *translated_name; | |
688 | char *old_locale; | |
689 | ||
690 | if (locale != NULL) { | |
691 | old_locale = g_strdup(setlocale(LC_MESSAGES, NULL)); | |
692 | setlocale(LC_MESSAGES, locale); | |
693 | } | |
694 | ||
695 | if (is_fallback_language(code)) { | |
696 | name = g_strdup(_("Unspecified")); | |
697 | } else { | |
698 | translated_name = dgettext("iso_639", language); | |
699 | name = get_first_item_in_semicolon_list(translated_name); | |
700 | } | |
701 | ||
702 | if (locale != NULL) { | |
703 | setlocale(LC_MESSAGES, old_locale); | |
704 | g_free(old_locale); | |
705 | } | |
706 | } | |
707 | ||
708 | return name; | |
709 | } | |
710 | ||
711 | static const char * | |
712 | get_territory(const char *code) | |
713 | { | |
714 | const char *name; | |
715 | int len; | |
716 | ||
717 | g_assert(code != NULL); | |
718 | ||
719 | len = strlen(code); | |
720 | if (len != 2 && len != 3) { | |
721 | return NULL; | |
722 | } | |
723 | ||
724 | name = (const char *) g_hash_table_lookup(gdm_territories_map, code); | |
725 | ||
726 | return name; | |
727 | } | |
728 | ||
729 | static char * | |
730 | get_translated_territory(const char *code, | |
731 | const char *locale) | |
732 | { | |
733 | const char *territory; | |
734 | char *name; | |
735 | ||
736 | territory = get_territory(code); | |
737 | ||
738 | name = NULL; | |
739 | if (territory != NULL) { | |
740 | const char *translated_territory; | |
741 | char *old_locale; | |
742 | ||
743 | if (locale != NULL) { | |
744 | old_locale = g_strdup(setlocale(LC_MESSAGES, NULL)); | |
745 | setlocale(LC_MESSAGES, locale); | |
746 | } | |
747 | ||
748 | translated_territory = dgettext("iso_3166", territory); | |
749 | name = get_first_item_in_semicolon_list(translated_territory); | |
750 | ||
751 | if (locale != NULL) { | |
752 | setlocale(LC_MESSAGES, old_locale); | |
753 | g_free(old_locale); | |
754 | } | |
755 | } | |
756 | ||
757 | return name; | |
758 | } | |
759 | ||
760 | static void | |
761 | languages_parse_start_tag(GMarkupParseContext *ctx, | |
762 | const char *element_name, | |
763 | const char **attr_names, | |
764 | const char **attr_values, | |
765 | gpointer user_data, | |
766 | GError **error) | |
767 | { | |
768 | const char *ccode_longB; | |
769 | const char *ccode_longT; | |
770 | const char *ccode; | |
771 | const char *lang_name; | |
772 | ||
773 | if (! g_str_equal(element_name, "iso_639_entry") || attr_names == NULL || attr_values == NULL) { | |
774 | return; | |
775 | } | |
776 | ||
777 | ccode = NULL; | |
778 | ccode_longB = NULL; | |
779 | ccode_longT = NULL; | |
780 | lang_name = NULL; | |
781 | ||
782 | while (*attr_names && *attr_values) { | |
783 | if (g_str_equal(*attr_names, "iso_639_1_code")) { | |
784 | /* skip if empty */ | |
785 | if (**attr_values) { | |
786 | if (strlen(*attr_values) != 2) { | |
787 | return; | |
788 | } | |
789 | ccode = *attr_values; | |
790 | } | |
791 | } else if (g_str_equal(*attr_names, "iso_639_2B_code")) { | |
792 | /* skip if empty */ | |
793 | if (**attr_values) { | |
794 | if (strlen(*attr_values) != 3) { | |
795 | return; | |
796 | } | |
797 | ccode_longB = *attr_values; | |
798 | } | |
799 | } else if (g_str_equal(*attr_names, "iso_639_2T_code")) { | |
800 | /* skip if empty */ | |
801 | if (**attr_values) { | |
802 | if (strlen(*attr_values) != 3) { | |
803 | return; | |
804 | } | |
805 | ccode_longT = *attr_values; | |
806 | } | |
807 | } else if (g_str_equal(*attr_names, "name")) { | |
808 | lang_name = *attr_values; | |
809 | } | |
810 | ||
811 | ++attr_names; | |
812 | ++attr_values; | |
813 | } | |
814 | ||
815 | if (lang_name == NULL) { | |
816 | return; | |
817 | } | |
818 | ||
819 | if (ccode != NULL) { | |
820 | g_hash_table_insert(gdm_languages_map, | |
821 | g_strdup(ccode), | |
822 | g_strdup(lang_name)); | |
823 | } | |
824 | if (ccode_longB != NULL) { | |
825 | g_hash_table_insert(gdm_languages_map, | |
826 | g_strdup(ccode_longB), | |
827 | g_strdup(lang_name)); | |
828 | } | |
829 | if (ccode_longT != NULL) { | |
830 | g_hash_table_insert(gdm_languages_map, | |
831 | g_strdup(ccode_longT), | |
832 | g_strdup(lang_name)); | |
833 | } | |
834 | } | |
835 | ||
836 | static void | |
837 | territories_parse_start_tag(GMarkupParseContext *ctx, | |
838 | const char *element_name, | |
839 | const char **attr_names, | |
840 | const char **attr_values, | |
841 | gpointer user_data, | |
842 | GError **error) | |
843 | { | |
844 | const char *acode_2; | |
845 | const char *acode_3; | |
846 | const char *ncode; | |
847 | const char *territory_common_name; | |
848 | const char *territory_name; | |
849 | ||
850 | if (! g_str_equal(element_name, "iso_3166_entry") || attr_names == NULL || attr_values == NULL) { | |
851 | return; | |
852 | } | |
853 | ||
854 | acode_2 = NULL; | |
855 | acode_3 = NULL; | |
856 | ncode = NULL; | |
857 | territory_common_name = NULL; | |
858 | territory_name = NULL; | |
859 | ||
860 | while (*attr_names && *attr_values) { | |
861 | if (g_str_equal(*attr_names, "alpha_2_code")) { | |
862 | /* skip if empty */ | |
863 | if (**attr_values) { | |
864 | if (strlen(*attr_values) != 2) { | |
865 | return; | |
866 | } | |
867 | acode_2 = *attr_values; | |
868 | } | |
869 | } else if (g_str_equal(*attr_names, "alpha_3_code")) { | |
870 | /* skip if empty */ | |
871 | if (**attr_values) { | |
872 | if (strlen(*attr_values) != 3) { | |
873 | return; | |
874 | } | |
875 | acode_3 = *attr_values; | |
876 | } | |
877 | } else if (g_str_equal(*attr_names, "numeric_code")) { | |
878 | /* skip if empty */ | |
879 | if (**attr_values) { | |
880 | if (strlen(*attr_values) != 3) { | |
881 | return; | |
882 | } | |
883 | ncode = *attr_values; | |
884 | } | |
885 | } else if (g_str_equal(*attr_names, "common_name")) { | |
886 | /* skip if empty */ | |
887 | if (**attr_values) { | |
888 | territory_common_name = *attr_values; | |
889 | } | |
890 | } else if (g_str_equal(*attr_names, "name")) { | |
891 | territory_name = *attr_values; | |
892 | } | |
893 | ||
894 | ++attr_names; | |
895 | ++attr_values; | |
896 | } | |
897 | ||
898 | if (territory_common_name != NULL) { | |
899 | territory_name = territory_common_name; | |
900 | } | |
901 | ||
902 | if (territory_name == NULL) { | |
903 | return; | |
904 | } | |
905 | ||
906 | if (acode_2 != NULL) { | |
907 | g_hash_table_insert(gdm_territories_map, | |
908 | g_strdup(acode_2), | |
909 | g_strdup(territory_name)); | |
910 | } | |
911 | if (acode_3 != NULL) { | |
912 | g_hash_table_insert(gdm_territories_map, | |
913 | g_strdup(acode_3), | |
914 | g_strdup(territory_name)); | |
915 | } | |
916 | if (ncode != NULL) { | |
917 | g_hash_table_insert(gdm_territories_map, | |
918 | g_strdup(ncode), | |
919 | g_strdup(territory_name)); | |
920 | } | |
921 | } | |
922 | ||
923 | static void | |
924 | languages_init(void) | |
925 | { | |
926 | GError *error; | |
927 | gboolean res; | |
928 | char *buf; | |
929 | gsize buf_len; | |
930 | ||
931 | bindtextdomain("iso_639", ISO_CODES_LOCALESDIR); | |
932 | bind_textdomain_codeset("iso_639", "UTF-8"); | |
933 | ||
934 | gdm_languages_map = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); | |
935 | ||
936 | error = NULL; | |
937 | res = g_file_get_contents(ISO_CODES_DATADIR "/iso_639.xml", | |
938 | &buf, | |
939 | &buf_len, | |
940 | &error); | |
941 | if (res) { | |
942 | GMarkupParseContext *ctx; | |
943 | GMarkupParser parser = { languages_parse_start_tag, NULL, NULL, NULL, NULL }; | |
944 | ||
945 | ctx = g_markup_parse_context_new(&parser, 0, NULL, NULL); | |
946 | ||
947 | error = NULL; | |
948 | res = g_markup_parse_context_parse(ctx, buf, buf_len, &error); | |
949 | ||
950 | if (! res) { | |
951 | g_warning("Failed to parse '%s': %s\n", | |
952 | ISO_CODES_DATADIR "/iso_639.xml", | |
953 | error->message); | |
954 | g_error_free(error); | |
955 | } | |
956 | ||
957 | g_markup_parse_context_free(ctx); | |
958 | g_free(buf); | |
959 | } else { | |
960 | g_warning("Failed to load '%s': %s\n", | |
961 | ISO_CODES_DATADIR "/iso_639.xml", | |
962 | error->message); | |
963 | g_error_free(error); | |
964 | } | |
965 | } | |
966 | ||
967 | static void | |
968 | territories_init(void) | |
969 | { | |
970 | GError *error; | |
971 | gboolean res; | |
972 | char *buf; | |
973 | gsize buf_len; | |
974 | ||
975 | bindtextdomain("iso_3166", ISO_CODES_LOCALESDIR); | |
976 | bind_textdomain_codeset("iso_3166", "UTF-8"); | |
977 | ||
978 | gdm_territories_map = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); | |
979 | ||
980 | error = NULL; | |
981 | res = g_file_get_contents(ISO_CODES_DATADIR "/iso_3166.xml", | |
982 | &buf, | |
983 | &buf_len, | |
984 | &error); | |
985 | if (res) { | |
986 | GMarkupParseContext *ctx; | |
987 | GMarkupParser parser = { territories_parse_start_tag, NULL, NULL, NULL, NULL }; | |
988 | ||
989 | ctx = g_markup_parse_context_new(&parser, 0, NULL, NULL); | |
990 | ||
991 | error = NULL; | |
992 | res = g_markup_parse_context_parse(ctx, buf, buf_len, &error); | |
993 | ||
994 | if (! res) { | |
995 | g_warning("Failed to parse '%s': %s\n", | |
996 | ISO_CODES_DATADIR "/iso_3166.xml", | |
997 | error->message); | |
998 | g_error_free(error); | |
999 | } | |
1000 | ||
1001 | g_markup_parse_context_free(ctx); | |
1002 | g_free(buf); | |
1003 | } else { | |
1004 | g_warning("Failed to load '%s': %s\n", | |
1005 | ISO_CODES_DATADIR "/iso_3166.xml", | |
1006 | error->message); | |
1007 | g_error_free(error); | |
1008 | } | |
1009 | } | |
1010 | ||
1011 | char * | |
1012 | gdm_get_language_from_name(const char *name, | |
1013 | const char *locale) | |
1014 | { | |
1015 | GString *full_language; | |
1016 | char *language_code; | |
1017 | char *territory_code; | |
1018 | char *codeset_code; | |
1019 | char *langinfo_codeset; | |
1020 | char *translated_language; | |
1021 | char *translated_territory; | |
1022 | gboolean is_utf8 = TRUE; | |
1023 | ||
1024 | translated_territory = NULL; | |
1025 | translated_language = NULL; | |
1026 | langinfo_codeset = NULL; | |
1027 | ||
1028 | full_language = g_string_new(NULL); | |
1029 | ||
1030 | if (gdm_languages_map == NULL) { | |
1031 | languages_init(); | |
1032 | } | |
1033 | ||
1034 | if (gdm_territories_map == NULL) { | |
1035 | territories_init(); | |
1036 | } | |
1037 | ||
1038 | language_code = NULL; | |
1039 | territory_code = NULL; | |
1040 | codeset_code = NULL; | |
1041 | ||
1042 | gdm_parse_language_name(name, | |
1043 | &language_code, | |
1044 | &territory_code, | |
1045 | &codeset_code, | |
1046 | NULL); | |
1047 | ||
1048 | if (language_code == NULL) { | |
1049 | goto out; | |
1050 | } | |
1051 | ||
1052 | translated_language = get_translated_language(language_code, locale); | |
1053 | if (translated_language == NULL) { | |
1054 | goto out; | |
1055 | } | |
1056 | ||
1057 | full_language = g_string_append(full_language, translated_language); | |
1058 | ||
1059 | if (territory_code != NULL) { | |
1060 | translated_territory = get_translated_territory(territory_code, locale); | |
1061 | } | |
1062 | if (translated_territory != NULL) { | |
1063 | g_string_append_printf(full_language, | |
1064 | " (%s)", | |
1065 | translated_territory); | |
1066 | } | |
1067 | ||
1068 | language_name_get_codeset_details(name, &langinfo_codeset, &is_utf8); | |
1069 | ||
1070 | if (codeset_code == NULL && langinfo_codeset != NULL) { | |
1071 | codeset_code = g_strdup(langinfo_codeset); | |
1072 | } | |
1073 | ||
1074 | out: | |
1075 | g_free(language_code); | |
1076 | g_free(territory_code); | |
1077 | g_free(codeset_code); | |
1078 | g_free(langinfo_codeset); | |
1079 | g_free(translated_language); | |
1080 | g_free(translated_territory); | |
1081 | ||
1082 | if (full_language->len == 0) { | |
1083 | g_string_free(full_language, TRUE); | |
1084 | return NULL; | |
1085 | } | |
1086 | ||
1087 | return g_string_free(full_language, FALSE); | |
1088 | } | |
1089 | ||
1090 | char ** | |
1091 | gdm_get_all_language_names(void) | |
1092 | { | |
1093 | GHashTableIter iter; | |
1094 | gpointer key, value; | |
1095 | GPtrArray *array; | |
1096 | ||
1097 | if (gdm_available_locales_map == NULL) { | |
1098 | collect_locales(); | |
1099 | } | |
1100 | ||
1101 | array = g_ptr_array_new(); | |
1102 | g_hash_table_iter_init(&iter, gdm_available_locales_map); | |
1103 | while (g_hash_table_iter_next(&iter, &key, &value)) { | |
1104 | GdmLocale *locale; | |
1105 | ||
1106 | locale = (GdmLocale *) value; | |
1107 | ||
1108 | g_ptr_array_add(array, g_strdup(locale->name)); | |
1109 | } | |
1110 | g_ptr_array_add(array, NULL); | |
1111 | ||
1112 | return (char **) g_ptr_array_free(array, FALSE); | |
1113 | } |
0 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- | |
1 | * | |
2 | * Copyright 2008 Red Hat, Inc. | |
3 | * Copyright 2007 William Jon McCann <mccann@jhu.edu> | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License as published by | |
7 | * the Free Software Foundation; either version 2 of the License, or | |
8 | * (at your option) any later version. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License | |
16 | * along with this program; if not, write to the Free Software | |
17 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
18 | * | |
19 | * Written by: Ray Strode | |
20 | * William Jon McCann | |
21 | */ | |
22 | ||
23 | #ifndef __GDM_LANGUAGES_H | |
24 | #define __GDM_LANGUAGES_H | |
25 | ||
26 | G_BEGIN_DECLS | |
27 | ||
28 | char * gdm_get_language_from_name(const char *name, | |
29 | const char *locale); | |
30 | char ** gdm_get_all_language_names(void); | |
31 | void gdm_parse_language_name(const char *name, | |
32 | char **language_codep, | |
33 | char **territory_codep, | |
34 | char **codesetp, | |
35 | char **modifierp); | |
36 | char * gdm_normalize_language_name(const char *name); | |
37 | ||
38 | G_END_DECLS | |
39 | ||
40 | #endif /* __GDM_LANGUAGE_CHOOSER_WIDGET_H */ |
0 | /*************************************************************************** | |
1 | * Copyright (C) 2010~2012 by CSSlayer * | |
2 | * * | |
3 | * This program is free software; you can redistribute it and/or modify * | |
4 | * it under the terms of the GNU General Public License as published by * | |
5 | * the Free Software Foundation; either version 2 of the License, or * | |
6 | * (at your option) any later version. * | |
7 | * * | |
8 | * This program is distributed in the hope that it will be useful, * | |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * | |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * | |
11 | * GNU General Public License for more details. * | |
12 | * * | |
13 | * You should have received a copy of the GNU General Public License * | |
14 | * along with this program; if not, write to the * | |
15 | * Free Software Foundation, Inc., * | |
16 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * | |
17 | ***************************************************************************/ | |
18 | ||
19 | #include <fcitx/module/dbus/dbusstuff.h> | |
20 | #include <fcitx/module/ipc/ipc.h> | |
21 | ||
22 | ||
23 | #include "im.h" | |
24 | ||
25 | static const gchar introspection_xml[] = | |
26 | "<node>" | |
27 | " <interface name=\"org.fcitx.Fcitx.InputMethod\">" | |
28 | " <property access=\"readwrite\" type=\"a(sssb)\" name=\"IMList\">" | |
29 | " <annotation name=\"org.freedesktop.DBus.Property.EmitsChangedSignal\" value=\"true\"/>" | |
30 | " </property>" | |
31 | " </interface>" | |
32 | "</node>"; | |
33 | ||
34 | ||
35 | enum { | |
36 | IMLIST_CHANGED_SIGNAL, | |
37 | LAST_SIGNAL | |
38 | }; | |
39 | ||
40 | static guint signals[LAST_SIGNAL] = {0}; | |
41 | ||
42 | ||
43 | G_DEFINE_TYPE(FcitxInputMethod, fcitx_inputmethod, G_TYPE_DBUS_PROXY); | |
44 | ||
45 | ||
46 | static GDBusInterfaceInfo * | |
47 | fcitx_inputmethod_get_interface_info(void); | |
48 | static void _fcitx_inputmethod_item_foreach_cb(gpointer data, gpointer user_data); | |
49 | ||
50 | static GDBusInterfaceInfo * | |
51 | fcitx_inputmethod_get_interface_info(void) | |
52 | { | |
53 | static gsize has_info = 0; | |
54 | static GDBusInterfaceInfo *info = NULL; | |
55 | if (g_once_init_enter(&has_info)) { | |
56 | GDBusNodeInfo *introspection_data; | |
57 | introspection_data = g_dbus_node_info_new_for_xml(introspection_xml, NULL); | |
58 | info = introspection_data->interfaces[0]; | |
59 | g_once_init_leave(&has_info, 1); | |
60 | } | |
61 | return info; | |
62 | } | |
63 | ||
64 | static void | |
65 | fcitx_inputmethod_finalize(GObject *object) | |
66 | { | |
67 | G_GNUC_UNUSED FcitxInputMethod *im = FCITX_INPUTMETHOD(object); | |
68 | ||
69 | if (G_OBJECT_CLASS(fcitx_inputmethod_parent_class)->finalize != NULL) | |
70 | G_OBJECT_CLASS(fcitx_inputmethod_parent_class)->finalize(object); | |
71 | } | |
72 | ||
73 | static void | |
74 | fcitx_inputmethod_init(FcitxInputMethod *im) | |
75 | { | |
76 | /* Sets the expected interface */ | |
77 | g_dbus_proxy_set_interface_info(G_DBUS_PROXY(im), fcitx_inputmethod_get_interface_info()); | |
78 | } | |
79 | ||
80 | GPtrArray * | |
81 | fcitx_inputmethod_get_imlist(FcitxInputMethod* im) | |
82 | { | |
83 | GPtrArray *array = NULL; | |
84 | GVariant* value; | |
85 | GVariantIter *iter; | |
86 | gchar *name, *unique_name, *langcode; | |
87 | gboolean enable; | |
88 | value = g_dbus_proxy_get_cached_property(G_DBUS_PROXY(im), "IMList"); | |
89 | ||
90 | if (value == NULL) { | |
91 | GError* error = NULL; | |
92 | GVariant* result = g_dbus_connection_call_sync(g_dbus_proxy_get_connection(G_DBUS_PROXY(im)), | |
93 | g_dbus_proxy_get_name(G_DBUS_PROXY(im)), | |
94 | FCITX_IM_DBUS_PATH, | |
95 | "org.freedesktop.DBus.Properties", | |
96 | "Get", | |
97 | g_variant_new("(ss)", FCITX_IM_DBUS_INTERFACE, "IMList"), | |
98 | G_VARIANT_TYPE("(v)"), | |
99 | G_DBUS_CALL_FLAGS_NONE, | |
100 | -1, /* timeout */ | |
101 | NULL, | |
102 | &error); | |
103 | ||
104 | if (error) { | |
105 | g_warning("%s", error->message); | |
106 | g_error_free(error); | |
107 | } else if (result) { | |
108 | g_variant_get(result, "(v)", &value); | |
109 | } | |
110 | } | |
111 | ||
112 | if (value) { | |
113 | array = g_ptr_array_new(); | |
114 | g_variant_get(value, "a(sssb)", &iter); | |
115 | while (g_variant_iter_next(iter, "(sssb)", &name, &unique_name, &langcode, &enable, NULL)) { | |
116 | FcitxIMItem* item = g_malloc0(sizeof(FcitxIMItem)); | |
117 | item->enable = enable; | |
118 | item->name = strdup(name); | |
119 | item->unique_name = strdup(unique_name); | |
120 | item->langcode = strdup(langcode); | |
121 | g_ptr_array_add(array, item); | |
122 | g_free(name); | |
123 | g_free(unique_name); | |
124 | g_free(langcode); | |
125 | } | |
126 | g_variant_iter_free(iter); | |
127 | ||
128 | g_variant_unref(value); | |
129 | } | |
130 | ||
131 | return array; | |
132 | } | |
133 | ||
134 | ||
135 | void | |
136 | fcitx_inputmethod_set_imlist(FcitxInputMethod *im, GPtrArray* array) | |
137 | { | |
138 | GVariantBuilder builder; | |
139 | g_variant_builder_init(&builder, G_VARIANT_TYPE("a(sssb)")); | |
140 | g_ptr_array_foreach(array, _fcitx_inputmethod_item_foreach_cb, &builder); | |
141 | GVariant* value = g_variant_builder_end(&builder); | |
142 | GError* error = NULL; | |
143 | GVariant* result = g_dbus_connection_call_sync(g_dbus_proxy_get_connection(G_DBUS_PROXY(im)), | |
144 | g_dbus_proxy_get_name(G_DBUS_PROXY(im)), | |
145 | FCITX_IM_DBUS_PATH, | |
146 | "org.freedesktop.DBus.Properties", | |
147 | "Set", | |
148 | g_variant_new("(ssv)", FCITX_IM_DBUS_INTERFACE, "IMList", value), | |
149 | G_VARIANT_TYPE_UNIT, | |
150 | G_DBUS_CALL_FLAGS_NONE, | |
151 | -1, /* timeout */ | |
152 | NULL, | |
153 | &error); | |
154 | ||
155 | if (error) { | |
156 | g_warning("%s", error->message); | |
157 | g_error_free(error); | |
158 | } | |
159 | ||
160 | g_variant_unref(result); | |
161 | g_variant_unref(value); | |
162 | } | |
163 | ||
164 | static void | |
165 | fcitx_inputmethod_g_properties_changed(GDBusProxy *proxy, | |
166 | GVariant *changed_properties, | |
167 | const gchar* const *invalidated_properties) | |
168 | { | |
169 | FcitxInputMethod *user = FCITX_INPUTMETHOD(proxy); | |
170 | GVariantIter *iter; | |
171 | const gchar *key; | |
172 | ||
173 | if (changed_properties != NULL) { | |
174 | g_variant_get(changed_properties, "a{sv}", &iter); | |
175 | while (g_variant_iter_next(iter, "{&sv}", &key, NULL)) { | |
176 | if (g_strcmp0(key, "IMList") == 0) | |
177 | g_signal_emit(user, signals[IMLIST_CHANGED_SIGNAL], 0); | |
178 | } | |
179 | g_variant_iter_free(iter); | |
180 | } | |
181 | ||
182 | if (invalidated_properties != NULL) { | |
183 | const gchar*const* item = invalidated_properties; | |
184 | while (*item) { | |
185 | if (g_strcmp0(*item, "IMList") == 0) | |
186 | g_signal_emit(user, signals[IMLIST_CHANGED_SIGNAL], 0); | |
187 | item++; | |
188 | } | |
189 | } | |
190 | } | |
191 | ||
192 | static void | |
193 | fcitx_inputmethod_g_signal(GDBusProxy *proxy, | |
194 | const gchar *sender_name, | |
195 | const gchar *signal_name, | |
196 | GVariant *parameters) | |
197 | { | |
198 | } | |
199 | ||
200 | static void | |
201 | fcitx_inputmethod_class_init(FcitxInputMethodClass *klass) | |
202 | { | |
203 | GObjectClass *gobject_class; | |
204 | GDBusProxyClass *proxy_class; | |
205 | ||
206 | gobject_class = G_OBJECT_CLASS(klass); | |
207 | gobject_class->finalize = fcitx_inputmethod_finalize; | |
208 | ||
209 | proxy_class = G_DBUS_PROXY_CLASS(klass); | |
210 | proxy_class->g_signal = fcitx_inputmethod_g_signal; | |
211 | proxy_class->g_properties_changed = fcitx_inputmethod_g_properties_changed; | |
212 | ||
213 | signals[IMLIST_CHANGED_SIGNAL] = g_signal_new("imlist-changed", | |
214 | FCITX_TYPE_INPUT_METHOD, | |
215 | G_SIGNAL_RUN_LAST, | |
216 | G_STRUCT_OFFSET(FcitxInputMethod, imlist_changed), | |
217 | NULL, | |
218 | NULL, | |
219 | g_cclosure_marshal_VOID__VOID, | |
220 | G_TYPE_NONE, | |
221 | 0); | |
222 | } | |
223 | ||
224 | FcitxInputMethod* | |
225 | fcitx_inputmethod_new(GBusType bus_type, | |
226 | GDBusProxyFlags flags, | |
227 | int display_number, | |
228 | GCancellable *cancellable, | |
229 | GError **error) | |
230 | { | |
231 | gchar servicename[64]; | |
232 | sprintf(servicename, "%s-%d", FCITX_DBUS_SERVICE, display_number); | |
233 | ||
234 | char* name = servicename; | |
235 | FcitxInputMethod* im = g_initable_new(FCITX_TYPE_INPUT_METHOD, | |
236 | cancellable, | |
237 | error, | |
238 | "g-flags", flags, | |
239 | "g-name", name, | |
240 | "g-bus-type", bus_type, | |
241 | "g-object-path", FCITX_IM_DBUS_PATH, | |
242 | "g-interface-name", FCITX_IM_DBUS_INTERFACE, | |
243 | NULL); | |
244 | ||
245 | if (im != NULL) | |
246 | return FCITX_INPUTMETHOD(im); | |
247 | else | |
248 | return NULL; | |
249 | return im; | |
250 | } | |
251 | ||
252 | void fcitx_inputmethod_item_free(gpointer data) | |
253 | { | |
254 | FcitxIMItem* item = data; | |
255 | g_free(item->name); | |
256 | g_free(item->unique_name); | |
257 | g_free(item->langcode); | |
258 | g_free(data); | |
259 | } | |
260 | ||
261 | void _fcitx_inputmethod_item_foreach_cb(gpointer data, | |
262 | gpointer user_data) | |
263 | { | |
264 | FcitxIMItem* item = data; | |
265 | GVariantBuilder* builder = user_data; | |
266 | ||
267 | g_variant_builder_add(builder, "(sssb)", item->name, item->unique_name, item->langcode, item->enable); | |
268 | }⏎ |
0 | /*************************************************************************** | |
1 | * Copyright (C) 2010~2012 by CSSlayer * | |
2 | * * | |
3 | * This program is free software; you can redistribute it and/or modify * | |
4 | * it under the terms of the GNU General Public License as published by * | |
5 | * the Free Software Foundation; either version 2 of the License, or * | |
6 | * (at your option) any later version. * | |
7 | * * | |
8 | * This program is distributed in the hope that it will be useful, * | |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * | |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * | |
11 | * GNU General Public License for more details. * | |
12 | * * | |
13 | * You should have received a copy of the GNU General Public License * | |
14 | * along with this program; if not, write to the * | |
15 | * Free Software Foundation, Inc., * | |
16 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * | |
17 | ***************************************************************************/ | |
18 | ||
19 | #ifndef _IM_H_ | |
20 | #define _IM_H_ | |
21 | ||
22 | #include <gio/gio.h> | |
23 | ||
24 | G_BEGIN_DECLS | |
25 | ||
26 | typedef struct _FcitxInputMethod FcitxInputMethod; | |
27 | typedef struct _FcitxInputMethodClass FcitxInputMethodClass; | |
28 | ||
29 | #define FCITX_TYPE_INPUT_METHOD (fcitx_inputmethod_get_type ()) | |
30 | #define FCITX_INPUTMETHOD(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), FCITX_TYPE_INPUT_METHOD, FcitxInputMethod)) | |
31 | #define FCITX_INPUTMETHOD_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), FCITX_TYPE_INPUT_METHOD, FcitxInputMethodClass)) | |
32 | #define FCITX_INPUTMETHOD_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), FCITX_TYPE_INPUT_METHOD, FcitxInputMethodClass)) | |
33 | ||
34 | struct _FcitxInputMethod { | |
35 | GDBusProxy parent_instance; | |
36 | void (*imlist_changed)(FcitxInputMethod* im); | |
37 | }; | |
38 | ||
39 | struct _FcitxInputMethodClass { | |
40 | GDBusProxyClass parent_class; | |
41 | }; | |
42 | ||
43 | typedef struct _FcitxIMItem { | |
44 | gchar* name; | |
45 | gchar* unique_name; | |
46 | gchar* langcode; | |
47 | gboolean enable; | |
48 | } FcitxIMItem; | |
49 | ||
50 | ||
51 | FcitxInputMethod* fcitx_inputmethod_new(GBusType bus_type, | |
52 | GDBusProxyFlags flags, | |
53 | int display_number, | |
54 | GCancellable *cancellable, | |
55 | GError **error); | |
56 | ||
57 | GType fcitx_inputmethod_get_type(void) G_GNUC_CONST; | |
58 | ||
59 | GPtrArray* fcitx_inputmethod_get_imlist(FcitxInputMethod* im); | |
60 | ||
61 | void fcitx_inputmethod_set_imlist(FcitxInputMethod* im, GPtrArray* array); | |
62 | ||
63 | void fcitx_inputmethod_item_free(gpointer data); | |
64 | ||
65 | G_END_DECLS | |
66 | ||
67 | #endif |
0 | /*************************************************************************** | |
1 | * Copyright (C) 2010~2012 by CSSlayer * | |
2 | * * | |
3 | * This program is free software; you can redistribute it and/or modify * | |
4 | * it under the terms of the GNU General Public License as published by * | |
5 | * the Free Software Foundation; either version 2 of the License, or * | |
6 | * (at your option) any later version. * | |
7 | * * | |
8 | * This program is distributed in the hope that it will be useful, * | |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * | |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * | |
11 | * GNU General Public License for more details. * | |
12 | * * | |
13 | * You should have received a copy of the GNU General Public License * | |
14 | * along with this program; if not, write to the * | |
15 | * Free Software Foundation, Inc., * | |
16 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * | |
17 | ***************************************************************************/ | |
18 | ||
19 | #include <fcitx-utils/utils.h> | |
20 | #include <fcitx/module/dbus/dbusstuff.h> | |
21 | #include <fcitx/module/ipc/ipc.h> | |
22 | ||
23 | #include "common.h" | |
24 | #include "im_widget.h" | |
25 | #include "im.h" | |
26 | #include "gdm-languages.h" | |
27 | ||
28 | G_DEFINE_TYPE(FcitxImWidget, fcitx_im_widget, GTK_TYPE_BOX) | |
29 | ||
30 | enum { | |
31 | AVAIL_TREE_IM_STRING, | |
32 | AVAIL_TREE_IM, | |
33 | AVAIL_TREE_LANG, | |
34 | AVAIL_N_COLUMNS | |
35 | }; | |
36 | ||
37 | enum { | |
38 | IM_LIST_IM_STRING, | |
39 | IM_LIST_IM, | |
40 | IM_N_COLUMNS | |
41 | }; | |
42 | ||
43 | typedef struct { | |
44 | FcitxImWidget* widget; | |
45 | GHashTable* langTable; | |
46 | } foreach_ct; | |
47 | ||
48 | static void fcitx_im_widget_finalize(GObject* object); | |
49 | static void _fcitx_im_widget_connect(FcitxImWidget* self); | |
50 | static void _fcitx_im_widget_load(FcitxImWidget* self); | |
51 | static void _fcitx_inputmethod_insert_foreach_cb(gpointer data, gpointer user_data); | |
52 | static void _fcitx_im_widget_availim_selection_changed(GtkTreeSelection *selection, gpointer data); | |
53 | static void _fcitx_im_widget_im_selection_changed(GtkTreeSelection *selection, gpointer data); | |
54 | static void _fcitx_im_widget_addim_button_clicked(GtkButton* button, gpointer user_data); | |
55 | static void _fcitx_im_widget_delim_button_clicked(GtkButton* button, gpointer user_data); | |
56 | static void _fcitx_im_widget_moveup_button_clicked(GtkButton* button, gpointer user_data); | |
57 | static void _fcitx_im_widget_movedown_button_clicked(GtkButton* button, gpointer user_data); | |
58 | static void _fcitx_im_widget_filtertext_changed(GtkEditable *editable, gpointer user_data); | |
59 | static void _fcitx_im_widget_onlycurlangcheckbox_toggled(GtkToggleButton *button, gpointer user_data); | |
60 | static gboolean _fcitx_im_widget_filter_func(GtkTreeModel *model, | |
61 | GtkTreeIter *iter, | |
62 | gpointer data); | |
63 | static const gchar* _get_current_lang(); | |
64 | static void icon_press_cb (GtkEntry *entry, | |
65 | gint position, | |
66 | GdkEventButton *event, | |
67 | gpointer data); | |
68 | ||
69 | static void | |
70 | fcitx_im_widget_class_init(FcitxImWidgetClass *klass) | |
71 | { | |
72 | GObjectClass *gobject_class = G_OBJECT_CLASS(klass); | |
73 | gobject_class->finalize = fcitx_im_widget_finalize; | |
74 | } | |
75 | ||
76 | static void | |
77 | fcitx_im_widget_init(FcitxImWidget* self) | |
78 | { | |
79 | self->availimstore = gtk_tree_store_new(AVAIL_N_COLUMNS, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_STRING); | |
80 | self->filtermodel = gtk_tree_model_filter_new(GTK_TREE_MODEL(self->availimstore), NULL); | |
81 | ||
82 | gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(self->filtermodel), | |
83 | (GtkTreeModelFilterVisibleFunc) _fcitx_im_widget_filter_func, | |
84 | self , | |
85 | NULL); | |
86 | self->sortmodel = gtk_tree_model_sort_new_with_model(self->filtermodel); | |
87 | gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(self->sortmodel), AVAIL_TREE_IM_STRING, GTK_SORT_ASCENDING); | |
88 | self->availimview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(self->sortmodel)); | |
89 | ||
90 | GtkWidget* label = gtk_label_new(_("Available Input Method")); | |
91 | self->filterentry = gtk_entry_new(); | |
92 | gtk_entry_set_icon_from_stock (GTK_ENTRY (self->filterentry), | |
93 | GTK_ENTRY_ICON_SECONDARY, | |
94 | GTK_STOCK_CLEAR); | |
95 | #if GTK_CHECK_VERSION(3,2,0) | |
96 | gtk_entry_set_placeholder_text(GTK_ENTRY (self->filterentry), _("Search Input Method")); | |
97 | #endif | |
98 | ||
99 | GtkCellRenderer* renderer; | |
100 | GtkTreeViewColumn* column; | |
101 | renderer = gtk_cell_renderer_text_new(); | |
102 | column = gtk_tree_view_column_new_with_attributes( | |
103 | _("Input Method"), renderer, | |
104 | "text", AVAIL_TREE_IM_STRING, | |
105 | NULL); | |
106 | gtk_tree_view_append_column(GTK_TREE_VIEW(self->availimview), column); | |
107 | ||
108 | GtkTreeSelection *selection; | |
109 | selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self->availimview)); | |
110 | gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE); | |
111 | g_signal_connect(G_OBJECT(selection), "changed", | |
112 | G_CALLBACK(_fcitx_im_widget_availim_selection_changed), self); | |
113 | ||
114 | gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(self->availimview), FALSE); | |
115 | ||
116 | self->onlycurlangcheckbox = gtk_check_button_new_with_label(_("Only Show Current Language")); | |
117 | gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->onlycurlangcheckbox), TRUE); | |
118 | ||
119 | GtkWidget* vbox; | |
120 | vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); | |
121 | gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 5); | |
122 | gtk_box_pack_start(GTK_BOX(vbox), self->filterentry, FALSE, TRUE, 5); | |
123 | GtkWidget* scrolledwindow = gtk_scrolled_window_new(NULL, NULL); | |
124 | gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwindow), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); | |
125 | gtk_container_add(GTK_CONTAINER(scrolledwindow), self->availimview); | |
126 | gtk_box_pack_start(GTK_BOX(vbox), scrolledwindow, TRUE, TRUE, 5); | |
127 | gtk_box_pack_start(GTK_BOX(vbox), self->onlycurlangcheckbox, FALSE, TRUE, 5); | |
128 | gtk_box_pack_start(GTK_BOX(self), vbox, TRUE, TRUE, 5); | |
129 | ||
130 | vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); | |
131 | ||
132 | self->addimbutton = gtk_button_new(); | |
133 | gtk_button_set_image(GTK_BUTTON(self->addimbutton), gtk_image_new_from_stock(GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_BUTTON)); | |
134 | gtk_widget_set_sensitive(self->addimbutton, FALSE); | |
135 | ||
136 | self->delimbutton = gtk_button_new(); | |
137 | gtk_button_set_image(GTK_BUTTON(self->delimbutton), gtk_image_new_from_stock(GTK_STOCK_GO_BACK, GTK_ICON_SIZE_BUTTON)); | |
138 | gtk_widget_set_sensitive(self->delimbutton, FALSE); | |
139 | ||
140 | gtk_box_pack_start(GTK_BOX(vbox), gtk_label_new(""), TRUE, TRUE, 0); | |
141 | gtk_box_pack_start(GTK_BOX(vbox), self->addimbutton, FALSE, FALSE, 0); | |
142 | gtk_box_pack_start(GTK_BOX(vbox), self->delimbutton, FALSE, FALSE, 0); | |
143 | gtk_box_pack_start(GTK_BOX(vbox), gtk_label_new(""), TRUE, TRUE, 0); | |
144 | ||
145 | gtk_box_pack_start(GTK_BOX(self), vbox, FALSE, TRUE, 5); | |
146 | ||
147 | label = gtk_label_new(_("Current Input Method")); | |
148 | ||
149 | self->imstore = gtk_list_store_new(IM_N_COLUMNS, G_TYPE_STRING, G_TYPE_POINTER); | |
150 | self->imview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(self->imstore)); | |
151 | ||
152 | renderer = gtk_cell_renderer_text_new(); | |
153 | column = gtk_tree_view_column_new_with_attributes( | |
154 | _("Input Method"), renderer, | |
155 | "text", IM_LIST_IM_STRING, | |
156 | NULL); | |
157 | gtk_tree_view_append_column(GTK_TREE_VIEW(self->imview), column); | |
158 | ||
159 | gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(self->imview), FALSE); | |
160 | selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self->imview)); | |
161 | gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE); | |
162 | g_signal_connect(G_OBJECT(selection), "changed", | |
163 | G_CALLBACK(_fcitx_im_widget_im_selection_changed), self); | |
164 | vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); | |
165 | gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 5); | |
166 | scrolledwindow = gtk_scrolled_window_new(NULL, NULL); | |
167 | gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwindow), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); | |
168 | gtk_container_add(GTK_CONTAINER(scrolledwindow), self->imview); | |
169 | gtk_box_pack_start(GTK_BOX(vbox), scrolledwindow, TRUE, TRUE, 5); | |
170 | ||
171 | gtk_box_pack_start(GTK_BOX(self), GTK_WIDGET(vbox), TRUE, TRUE, 5); | |
172 | ||
173 | vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); | |
174 | ||
175 | self->moveupbutton = gtk_button_new(); | |
176 | gtk_button_set_image(GTK_BUTTON(self->moveupbutton), gtk_image_new_from_stock(GTK_STOCK_GO_UP, GTK_ICON_SIZE_BUTTON)); | |
177 | gtk_widget_set_sensitive(self->moveupbutton, FALSE); | |
178 | ||
179 | self->movedownbutton = gtk_button_new(); | |
180 | gtk_button_set_image(GTK_BUTTON(self->movedownbutton), gtk_image_new_from_stock(GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_BUTTON)); | |
181 | gtk_widget_set_sensitive(self->movedownbutton, FALSE); | |
182 | ||
183 | gtk_box_pack_start(GTK_BOX(vbox), gtk_label_new(""), TRUE, TRUE, 0); | |
184 | gtk_box_pack_start(GTK_BOX(vbox), self->moveupbutton, FALSE, FALSE, 0); | |
185 | gtk_box_pack_start(GTK_BOX(vbox), self->movedownbutton, FALSE, FALSE, 0); | |
186 | gtk_box_pack_start(GTK_BOX(vbox), gtk_label_new(""), TRUE, TRUE, 0); | |
187 | ||
188 | gtk_box_pack_start(GTK_BOX(self), vbox, FALSE, TRUE, 5); | |
189 | ||
190 | g_signal_connect(G_OBJECT(self->addimbutton), "clicked", G_CALLBACK(_fcitx_im_widget_addim_button_clicked), self); | |
191 | g_signal_connect(G_OBJECT(self->delimbutton), "clicked", G_CALLBACK(_fcitx_im_widget_delim_button_clicked), self); | |
192 | g_signal_connect(G_OBJECT(self->moveupbutton), "clicked", G_CALLBACK(_fcitx_im_widget_moveup_button_clicked), self); | |
193 | g_signal_connect(G_OBJECT(self->movedownbutton), "clicked", G_CALLBACK(_fcitx_im_widget_movedown_button_clicked), self); | |
194 | g_signal_connect(G_OBJECT(self->filterentry), "changed", G_CALLBACK(_fcitx_im_widget_filtertext_changed), self); | |
195 | g_signal_connect(G_OBJECT(self->onlycurlangcheckbox), "toggled", G_CALLBACK(_fcitx_im_widget_onlycurlangcheckbox_toggled), self); | |
196 | g_signal_connect(G_OBJECT(self->filterentry), "icon-press", G_CALLBACK (icon_press_cb), NULL); | |
197 | ||
198 | ||
199 | _fcitx_im_widget_connect(self); | |
200 | } | |
201 | ||
202 | GtkWidget* | |
203 | fcitx_im_widget_new(void) | |
204 | { | |
205 | FcitxImWidget* widget = | |
206 | g_object_new(FCITX_TYPE_IM_WIDGET, | |
207 | NULL); | |
208 | ||
209 | return GTK_WIDGET(widget); | |
210 | } | |
211 | ||
212 | void fcitx_im_widget_finalize(GObject* object) | |
213 | { | |
214 | FcitxImWidget* self = FCITX_IM_WIDGET(object); | |
215 | if (self->array) { | |
216 | g_ptr_array_set_free_func(self->array, fcitx_inputmethod_item_free); | |
217 | g_ptr_array_free(self->array, FALSE); | |
218 | self->array = NULL; | |
219 | } | |
220 | g_free(self->focus); | |
221 | } | |
222 | ||
223 | void _fcitx_im_widget_imlist_changed_cb(FcitxInputMethod* im, gpointer user_data) | |
224 | { | |
225 | FcitxImWidget* self = user_data; | |
226 | _fcitx_im_widget_load(self); | |
227 | } | |
228 | ||
229 | void _fcitx_im_widget_connect(FcitxImWidget* self) | |
230 | { | |
231 | GError* error = NULL; | |
232 | self->improxy = fcitx_inputmethod_new(G_BUS_TYPE_SESSION, | |
233 | G_DBUS_PROXY_FLAGS_NONE, | |
234 | fcitx_utils_get_display_number(), | |
235 | NULL, | |
236 | &error | |
237 | ); | |
238 | if (self->improxy == NULL) { | |
239 | g_error_free(error); | |
240 | return; | |
241 | } | |
242 | g_signal_connect(self->improxy, "imlist-changed", G_CALLBACK(_fcitx_im_widget_imlist_changed_cb), self); | |
243 | ||
244 | _fcitx_im_widget_load(self); | |
245 | } | |
246 | ||
247 | void _fcitx_im_widget_load(FcitxImWidget* self) | |
248 | { | |
249 | gtk_tree_store_clear(self->availimstore); | |
250 | gtk_list_store_clear(self->imstore); | |
251 | ||
252 | if (self->array) { | |
253 | g_ptr_array_set_free_func(self->array, fcitx_inputmethod_item_free); | |
254 | g_ptr_array_free(self->array, FALSE); | |
255 | self->array = NULL; | |
256 | } | |
257 | ||
258 | self->array = fcitx_inputmethod_get_imlist(self->improxy); | |
259 | ||
260 | if (self->array) { | |
261 | foreach_ct ct; | |
262 | ct.widget = self; | |
263 | ct.langTable = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); | |
264 | g_ptr_array_foreach(self->array, _fcitx_inputmethod_insert_foreach_cb, &ct); | |
265 | g_hash_table_unref(ct.langTable); | |
266 | ||
267 | _fcitx_im_widget_im_selection_changed(gtk_tree_view_get_selection(GTK_TREE_VIEW(self->imview)), self); | |
268 | g_free(self->focus); | |
269 | self->focus = NULL; | |
270 | ||
271 | if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(self->onlycurlangcheckbox))) | |
272 | gtk_tree_view_expand_all (GTK_TREE_VIEW(self->availimview)); | |
273 | } | |
274 | } | |
275 | ||
276 | void _fcitx_inputmethod_insert_foreach_cb(gpointer data, | |
277 | gpointer user_data) | |
278 | { | |
279 | foreach_ct* ct = user_data; | |
280 | FcitxIMItem* item = data; | |
281 | FcitxImWidget* self = ct->widget; | |
282 | GtkTreeIter iter; | |
283 | ||
284 | GtkTreeIter* langIter = g_hash_table_lookup(ct->langTable, item->langcode); | |
285 | ||
286 | if (langIter == NULL) { | |
287 | langIter = g_new(GtkTreeIter, 1); | |
288 | gtk_tree_store_append(self->availimstore, langIter, NULL); | |
289 | ||
290 | char* lang = NULL; | |
291 | if (strlen(item->langcode) != 0) | |
292 | lang = gdm_get_language_from_name(item->langcode, NULL); | |
293 | if (!lang) { | |
294 | if (strcmp(item->langcode, "*") == 0) | |
295 | lang = g_strdup_printf("%s", _("Unknown")); | |
296 | else | |
297 | lang = g_strdup_printf("%s", _("Unknown")); | |
298 | } | |
299 | gtk_tree_store_set(self->availimstore, langIter, AVAIL_TREE_IM_STRING, lang, -1); | |
300 | gtk_tree_store_set(self->availimstore, langIter, AVAIL_TREE_LANG, item->langcode, -1); | |
301 | gtk_tree_store_set(self->availimstore, langIter, AVAIL_TREE_IM, NULL, -1); | |
302 | g_free(lang); | |
303 | ||
304 | g_hash_table_insert(ct->langTable, g_strdup(item->langcode), langIter); | |
305 | } | |
306 | ||
307 | if (item->enable) { | |
308 | gtk_list_store_append(self->imstore, &iter); | |
309 | gtk_list_store_set(self->imstore, &iter, IM_LIST_IM_STRING, item->name, -1); | |
310 | gtk_list_store_set(self->imstore, &iter, IM_LIST_IM, item, -1); | |
311 | if (g_strcmp0(self->focus, item->unique_name) == 0) { | |
312 | GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self->imview)); | |
313 | gtk_tree_selection_select_iter(selection, &iter); | |
314 | } | |
315 | } else { | |
316 | ||
317 | gtk_tree_store_append(self->availimstore, &iter, langIter); | |
318 | gtk_tree_store_set(self->availimstore, &iter, AVAIL_TREE_IM_STRING, item->name, -1); | |
319 | gtk_tree_store_set(self->availimstore, &iter, AVAIL_TREE_LANG, NULL, -1); | |
320 | gtk_tree_store_set(self->availimstore, &iter, AVAIL_TREE_IM, item, -1); | |
321 | } | |
322 | ||
323 | } | |
324 | ||
325 | void _fcitx_im_widget_im_selection_changed(GtkTreeSelection *selection, gpointer data) | |
326 | { | |
327 | FcitxImWidget* self = data; | |
328 | GtkTreeView *treeView = gtk_tree_selection_get_tree_view(selection); | |
329 | GtkTreeModel *model = gtk_tree_view_get_model(treeView); | |
330 | GtkTreeIter iter; | |
331 | ||
332 | if (gtk_tree_selection_get_selected(selection, &model, &iter)) { | |
333 | gtk_widget_set_sensitive(self->delimbutton, TRUE); | |
334 | GtkTreePath* path = gtk_tree_model_get_path(model, &iter); | |
335 | ||
336 | gint* ind = gtk_tree_path_get_indices(path); | |
337 | ||
338 | gint n = gtk_tree_model_iter_n_children(model, NULL); | |
339 | ||
340 | if (ind) { | |
341 | gtk_widget_set_sensitive(self->moveupbutton, (*ind != 0)); | |
342 | gtk_widget_set_sensitive(self->movedownbutton, (*ind != n - 1)); | |
343 | } | |
344 | ||
345 | gtk_tree_path_free(path); | |
346 | } else { | |
347 | gtk_widget_set_sensitive(self->delimbutton, FALSE); | |
348 | } | |
349 | } | |
350 | ||
351 | void _fcitx_im_widget_availim_selection_changed(GtkTreeSelection* selection, gpointer data) | |
352 | { | |
353 | FcitxImWidget* self = data; | |
354 | GtkTreeView *treeView = gtk_tree_selection_get_tree_view(selection); | |
355 | GtkTreeModel *model = gtk_tree_view_get_model(treeView); | |
356 | GtkTreeIter iter; | |
357 | ||
358 | FcitxIMItem* item = NULL; | |
359 | if (gtk_tree_selection_get_selected(selection, &model, &iter)) { | |
360 | gtk_tree_model_get(model, | |
361 | &iter, | |
362 | AVAIL_TREE_IM, &item, | |
363 | -1); | |
364 | } | |
365 | if (item) { | |
366 | gtk_widget_set_sensitive(self->addimbutton, TRUE); | |
367 | } else { | |
368 | gtk_widget_set_sensitive(self->addimbutton, FALSE); | |
369 | } | |
370 | ||
371 | } | |
372 | ||
373 | void _fcitx_im_widget_addim_button_clicked(GtkButton* button, gpointer user_data) | |
374 | { | |
375 | FcitxImWidget* self = user_data; | |
376 | GtkWidget *treeView = self->availimview; | |
377 | GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeView)); | |
378 | GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self->availimview)); | |
379 | GtkTreeIter iter; | |
380 | ||
381 | if (gtk_tree_selection_get_selected(selection, &model, &iter)) { | |
382 | FcitxIMItem* item = NULL; | |
383 | gtk_tree_model_get(model, | |
384 | &iter, | |
385 | AVAIL_TREE_IM, &item, | |
386 | -1); | |
387 | if (item == NULL) | |
388 | return; | |
389 | item->enable = true; | |
390 | ||
391 | g_ptr_array_remove(self->array, item); | |
392 | g_ptr_array_add(self->array, item); | |
393 | ||
394 | g_free(self->focus); | |
395 | self->focus = g_strdup(item->unique_name); | |
396 | ||
397 | fcitx_inputmethod_set_imlist(self->improxy, self->array); | |
398 | } | |
399 | } | |
400 | ||
401 | void _fcitx_im_widget_delim_button_clicked(GtkButton* button, gpointer user_data) | |
402 | { | |
403 | FcitxImWidget* self = user_data; | |
404 | GtkWidget *treeView = self->imview; | |
405 | GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeView)); | |
406 | GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self->imview)); | |
407 | GtkTreeIter iter; | |
408 | ||
409 | if (gtk_tree_selection_get_selected(selection, &model, &iter)) { | |
410 | FcitxIMItem* item = NULL; | |
411 | gtk_tree_model_get(model, | |
412 | &iter, | |
413 | IM_LIST_IM, &item, | |
414 | -1); | |
415 | item->enable = false; | |
416 | ||
417 | fcitx_inputmethod_set_imlist(self->improxy, self->array); | |
418 | } | |
419 | ||
420 | } | |
421 | ||
422 | void _fcitx_im_widget_moveup_button_clicked(GtkButton* button, gpointer user_data) | |
423 | { | |
424 | FcitxImWidget* self = user_data; | |
425 | GtkWidget *treeView = self->imview; | |
426 | GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeView)); | |
427 | GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self->imview)); | |
428 | GtkTreeIter iter; | |
429 | ||
430 | if (gtk_tree_selection_get_selected(selection, &model, &iter)) { | |
431 | FcitxIMItem* item = NULL; | |
432 | gtk_tree_model_get(model, | |
433 | &iter, | |
434 | IM_LIST_IM, &item, | |
435 | -1); | |
436 | ||
437 | int i; | |
438 | int switch_index = self->array->len; | |
439 | for (i = 0; i < self->array->len; i += 1) { | |
440 | if (g_ptr_array_index(self->array, i) == item) | |
441 | break; | |
442 | ||
443 | FcitxIMItem* temp_item = g_ptr_array_index(self->array, i); | |
444 | if (temp_item->enable) | |
445 | switch_index = i; | |
446 | } | |
447 | ||
448 | if (i != self->array->len && switch_index != self->array->len) { | |
449 | gpointer temp = g_ptr_array_index(self->array, i); | |
450 | g_ptr_array_index(self->array, i) = g_ptr_array_index(self->array, switch_index); | |
451 | g_ptr_array_index(self->array, switch_index) = temp; | |
452 | g_free(self->focus); | |
453 | self->focus = g_strdup(item->unique_name); | |
454 | ||
455 | fcitx_inputmethod_set_imlist(self->improxy, self->array); | |
456 | } | |
457 | } | |
458 | } | |
459 | ||
460 | void _fcitx_im_widget_movedown_button_clicked(GtkButton* button, gpointer user_data) | |
461 | { | |
462 | FcitxImWidget* self = user_data; | |
463 | GtkWidget *treeView = self->imview; | |
464 | GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeView)); | |
465 | GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self->imview)); | |
466 | GtkTreeIter iter; | |
467 | ||
468 | if (gtk_tree_selection_get_selected(selection, &model, &iter)) { | |
469 | FcitxIMItem* item = NULL; | |
470 | gtk_tree_model_get(model, | |
471 | &iter, | |
472 | IM_LIST_IM, &item, | |
473 | -1); | |
474 | ||
475 | int i; | |
476 | int switch_index = -1; | |
477 | for (i = self->array->len - 1; i >= 0; i -= 1) { | |
478 | if (g_ptr_array_index(self->array, i) == item) | |
479 | break; | |
480 | ||
481 | FcitxIMItem* temp_item = g_ptr_array_index(self->array, i); | |
482 | if (temp_item->enable) | |
483 | switch_index = i; | |
484 | } | |
485 | ||
486 | if (i != -1 && switch_index != -1) { | |
487 | gpointer temp = g_ptr_array_index(self->array, i); | |
488 | g_ptr_array_index(self->array, i) = g_ptr_array_index(self->array, switch_index); | |
489 | g_ptr_array_index(self->array, switch_index) = temp; | |
490 | g_free(self->focus); | |
491 | self->focus = g_strdup(item->unique_name); | |
492 | ||
493 | fcitx_inputmethod_set_imlist(self->improxy, self->array); | |
494 | } | |
495 | } | |
496 | } | |
497 | ||
498 | void _fcitx_im_widget_filtertext_changed(GtkEditable* editable, gpointer user_data) | |
499 | { | |
500 | FcitxImWidget* self = user_data; | |
501 | gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(self->filtermodel)); | |
502 | } | |
503 | ||
504 | void _fcitx_im_widget_onlycurlangcheckbox_toggled(GtkToggleButton* button, gpointer user_data) | |
505 | { | |
506 | FcitxImWidget* self = user_data; | |
507 | gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(self->filtermodel)); | |
508 | } | |
509 | ||
510 | ||
511 | gboolean _fcitx_im_widget_filter_func(GtkTreeModel *model, | |
512 | GtkTreeIter *iter, | |
513 | gpointer data) | |
514 | { | |
515 | FcitxImWidget* self = data; | |
516 | const gchar* filter_text = gtk_entry_get_text(GTK_ENTRY(self->filterentry)); | |
517 | FcitxIMItem* item = NULL; | |
518 | gtk_tree_model_get(GTK_TREE_MODEL(self->availimstore), | |
519 | iter, | |
520 | AVAIL_TREE_IM, &item, | |
521 | -1); | |
522 | ||
523 | gboolean flag = TRUE; | |
524 | if (item) { | |
525 | flag = flag && (strlen(filter_text) == 0 | |
526 | || strstr(item->name, filter_text) | |
527 | || strstr(item->unique_name, filter_text) | |
528 | || strstr(item->langcode, filter_text)); | |
529 | flag = flag && (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(self->onlycurlangcheckbox)) ? | |
530 | strncmp(item->langcode, _get_current_lang() , 2) == 0 : TRUE) ; | |
531 | return flag; | |
532 | } else { | |
533 | gchar* lang = NULL; | |
534 | gtk_tree_model_get(GTK_TREE_MODEL(self->availimstore), | |
535 | iter, | |
536 | AVAIL_TREE_LANG, &lang, | |
537 | -1); | |
538 | flag = (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(self->onlycurlangcheckbox)) ? | |
539 | lang != NULL && strncmp(lang, _get_current_lang() , 2) == 0 : TRUE) ; | |
540 | g_free(lang); | |
541 | return flag; | |
542 | } | |
543 | } | |
544 | ||
545 | static const gchar* _get_current_lang() | |
546 | { | |
547 | const gchar* lang = g_getenv("LC_ALL"); | |
548 | if (!lang) | |
549 | lang = g_getenv("LANG"); | |
550 | if (!lang) | |
551 | lang = g_getenv("LC_MESSAGES"); | |
552 | if (!lang) | |
553 | lang = "C"; | |
554 | return lang; | |
555 | } | |
556 | ||
557 | static void | |
558 | icon_press_cb (GtkEntry *entry, | |
559 | gint position, | |
560 | GdkEventButton *event, | |
561 | gpointer data) | |
562 | { | |
563 | gtk_entry_set_text (entry, ""); | |
564 | }⏎ |
0 | /*************************************************************************** | |
1 | * Copyright (C) 2010~2012 by CSSlayer * | |
2 | * * | |
3 | * This program is free software; you can redistribute it and/or modify * | |
4 | * it under the terms of the GNU General Public License as published by * | |
5 | * the Free Software Foundation; either version 2 of the License, or * | |
6 | * (at your option) any later version. * | |
7 | * * | |
8 | * This program is distributed in the hope that it will be useful, * | |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * | |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * | |
11 | * GNU General Public License for more details. * | |
12 | * * | |
13 | * You should have received a copy of the GNU General Public License * | |
14 | * along with this program; if not, write to the * | |
15 | * Free Software Foundation, Inc., * | |
16 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * | |
17 | ***************************************************************************/ | |
18 | ||
19 | #ifndef IM_WIDGET_H | |
20 | #define IM_WIDGET_H | |
21 | ||
22 | #include <gtk/gtk.h> | |
23 | #include <gio/gio.h> | |
24 | #include "im.h" | |
25 | ||
26 | G_BEGIN_DECLS | |
27 | ||
28 | #define FCITX_TYPE_IM_WIDGET fcitx_im_widget_get_type() | |
29 | ||
30 | #define FCITX_IM_WIDGET(obj) \ | |
31 | (G_TYPE_CHECK_INSTANCE_CAST ((obj), FCITX_TYPE_IM_WIDGET, FcitxImWidget)) | |
32 | ||
33 | #define FCITX_IM_WIDGET_CLASS(klass) \ | |
34 | (G_TYPE_CHECK_CLASS_CAST ((klass), FCITX_TYPE_IM_WIDGET, FcitxImWidgetClass)) | |
35 | ||
36 | #define FCITX_IS_IM_WIDGET(obj) \ | |
37 | (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FCITX_TYPE_IM_WIDGET)) | |
38 | ||
39 | #define FCITX_IS_IM_WIDGET_CLASS(klass) \ | |
40 | (G_TYPE_CHECK_CLASS_TYPE ((klass), FCITX_TYPE_IM_WIDGET)) | |
41 | ||
42 | #define FCITX_IM_WIDGET_GET_CLASS(obj) \ | |
43 | (G_TYPE_INSTANCE_GET_CLASS ((obj), FCITX_TYPE_IM_WIDGET, FcitxImWidgetClass)) | |
44 | ||
45 | #define IC_NAME_MAX 64 | |
46 | ||
47 | typedef struct { | |
48 | GtkBox parent; | |
49 | GtkTreeStore* availimstore; | |
50 | GtkListStore* imstore; | |
51 | GtkWidget* availimview; | |
52 | GtkWidget* imview; | |
53 | GtkWidget* addimbutton; | |
54 | GtkWidget* delimbutton; | |
55 | GtkWidget* moveupbutton; | |
56 | GtkWidget* movedownbutton; | |
57 | char servicename[IC_NAME_MAX]; | |
58 | FcitxInputMethod* improxy; | |
59 | GPtrArray* array; | |
60 | GtkWidget* filterentry; | |
61 | GtkTreeModel* filtermodel; | |
62 | GtkWidget* onlycurlangcheckbox; | |
63 | GtkTreeModel* sortmodel; | |
64 | gchar* focus; | |
65 | } FcitxImWidget; | |
66 | ||
67 | typedef struct { | |
68 | GtkBoxClass parent_class; | |
69 | } FcitxImWidgetClass; | |
70 | ||
71 | GtkWidget* | |
72 | fcitx_im_widget_new(void); | |
73 | ||
74 | G_END_DECLS | |
75 | ||
76 | ||
77 | #endif⏎ |
0 | /*************************************************************************** | |
1 | * Copyright (C) 2010~2012 by CSSlayer * | |
2 | * * | |
3 | * This program is free software; you can redistribute it and/or modify * | |
4 | * it under the terms of the GNU General Public License as published by * | |
5 | * the Free Software Foundation; either version 2 of the License, or * | |
6 | * (at your option) any later version. * | |
7 | * * | |
8 | * This program is distributed in the hope that it will be useful, * | |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * | |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * | |
11 | * GNU General Public License for more details. * | |
12 | * * | |
13 | * You should have received a copy of the GNU General Public License * | |
14 | * along with this program; if not, write to the * | |
15 | * Free Software Foundation, Inc., * | |
16 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * | |
17 | ***************************************************************************/ | |
18 | ||
19 | #include <gtk/gtk.h> | |
20 | #include <gdk/gdkkeysyms.h> | |
21 | #include <libintl.h> | |
22 | #include <string.h> | |
23 | #include <stdlib.h> | |
24 | #include "keygrab.h" | |
25 | #include <fcitx-config/hotkey.h> | |
26 | ||
27 | #define _(s) gettext(s) | |
28 | //定义枚举类型,说明信号的名称和次序 | |
29 | enum { | |
30 | KEYGRAB_BUTTON_CHANGED, | |
31 | KEYGRAB_BUTTON_CURRENT_CHANGED, | |
32 | LAST_SIGNAL | |
33 | }; | |
34 | static gint keygrab_button_signals[LAST_SIGNAL] = { 0 }; | |
35 | static void keygrab_button_init(KeyGrabButton *keygrab_button); | |
36 | static void keygrab_button_class_init(KeyGrabButtonClass *keygrabbuttonclass); | |
37 | static void begin_key_grab(KeyGrabButton* self, gpointer v); | |
38 | static void end_key_grab(KeyGrabButton *self); | |
39 | static GtkWidget* popup_new(GtkWidget* parent, const gchar* text, gboolean mouse); | |
40 | static void on_key_press_event(GtkWidget *self, GdkEventKey *event, gpointer v); | |
41 | ||
42 | G_DEFINE_TYPE(KeyGrabButton, keygrab_button, GTK_TYPE_BUTTON) | |
43 | ||
44 | static void keygrab_button_init(KeyGrabButton *keygrabbutton) | |
45 | { | |
46 | keygrab_button_set_key(keygrabbutton, 0, 0); | |
47 | g_signal_connect(G_OBJECT(keygrabbutton), "clicked", (GCallback) begin_key_grab, NULL); | |
48 | } | |
49 | ||
50 | static void keygrab_button_class_init(KeyGrabButtonClass *keygrabbuttonclass) | |
51 | { | |
52 | GObjectClass *object_class; | |
53 | object_class = (GObjectClass*)keygrabbuttonclass; | |
54 | keygrab_button_signals[KEYGRAB_BUTTON_CHANGED] = g_signal_new("changed", | |
55 | G_TYPE_FROM_CLASS(object_class), | |
56 | G_SIGNAL_RUN_FIRST, | |
57 | G_STRUCT_OFFSET(KeyGrabButtonClass, changed), | |
58 | NULL, NULL, | |
59 | g_cclosure_marshal_VOID__VOID, | |
60 | G_TYPE_NONE, 0, NULL); | |
61 | keygrab_button_signals[KEYGRAB_BUTTON_CURRENT_CHANGED] = g_signal_new("current-changed", | |
62 | G_TYPE_FROM_CLASS(object_class), | |
63 | G_SIGNAL_RUN_FIRST, | |
64 | G_STRUCT_OFFSET(KeyGrabButtonClass, current_changed), | |
65 | NULL, NULL, | |
66 | g_cclosure_marshal_VOID__VOID, | |
67 | G_TYPE_NONE, 0, NULL); | |
68 | } | |
69 | //创建新的自定义控件 | |
70 | GtkWidget* keygrab_button_new(void) | |
71 | { | |
72 | return GTK_WIDGET(g_object_new(TYPE_KEYGRAB_BUTTON, 0)); | |
73 | } | |
74 | ||
75 | ||
76 | void begin_key_grab(KeyGrabButton* self, gpointer v) | |
77 | { | |
78 | gtk_widget_add_events(GTK_WIDGET(self), GDK_KEY_PRESS_MASK); | |
79 | KeyGrabButton* b = KEYGRAB_BUTTON(self); | |
80 | b->popup = popup_new(GTK_WIDGET(self), _("Please press the new key combination"), FALSE); | |
81 | gtk_widget_show_all(b->popup); | |
82 | b->handler = g_signal_connect(G_OBJECT(b->popup), "key-press-event", (GCallback)on_key_press_event, b); | |
83 | ||
84 | GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(b->popup)); | |
85 | GdkDisplay* display = gdk_window_get_display (window); | |
86 | GdkDeviceManager* device_manager = gdk_display_get_device_manager (display); | |
87 | GdkDevice* pointer = gdk_device_manager_get_client_pointer (device_manager); | |
88 | GdkDevice* keyboard = gdk_device_get_associated_device (pointer); | |
89 | ||
90 | while (gdk_device_grab( | |
91 | keyboard, | |
92 | window, | |
93 | GDK_OWNERSHIP_NONE, FALSE, | |
94 | GDK_KEY_PRESS | GDK_KEY_RELEASE, | |
95 | NULL, | |
96 | GDK_CURRENT_TIME) != GDK_GRAB_SUCCESS) | |
97 | usleep(100); | |
98 | } | |
99 | ||
100 | void end_key_grab(KeyGrabButton *self) | |
101 | { | |
102 | KeyGrabButton* b = KEYGRAB_BUTTON(self); | |
103 | GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(b->popup)); | |
104 | GdkDisplay* display = gdk_window_get_display (window); | |
105 | GdkDeviceManager* device_manager = gdk_display_get_device_manager (display); | |
106 | GdkDevice* pointer = gdk_device_manager_get_client_pointer (device_manager); | |
107 | GdkDevice* keyboard = gdk_device_get_associated_device (pointer); | |
108 | gdk_device_ungrab(keyboard, gtk_get_current_event_time()); | |
109 | g_signal_handler_disconnect(b->popup, b->handler); | |
110 | gtk_widget_destroy(b->popup); | |
111 | } | |
112 | ||
113 | void on_key_press_event(GtkWidget *self, GdkEventKey *event, gpointer v) | |
114 | { | |
115 | KeyGrabButton* b = KEYGRAB_BUTTON(v); | |
116 | guint key; | |
117 | GdkModifierType mods = event->state & gtk_accelerator_get_default_mod_mask(); | |
118 | ||
119 | if ((event->keyval == GDK_KEY_Escape | |
120 | || event->keyval == GDK_KEY_Return) && !mods) { | |
121 | if (event->keyval == GDK_KEY_Escape) | |
122 | g_signal_emit_by_name(G_OBJECT(b), "changed", b->key, b->mods); | |
123 | end_key_grab(b); | |
124 | keygrab_button_set_key(b, 0, 0); | |
125 | return; | |
126 | } | |
127 | ||
128 | key = gdk_keyval_to_upper(event->keyval); | |
129 | if (key == GDK_KEY_ISO_Left_Tab) | |
130 | key = GDK_KEY_Tab; | |
131 | ||
132 | if (gtk_accelerator_valid(key, mods) | |
133 | || (key == GDK_KEY_Tab && mods)) { | |
134 | keygrab_button_set_key(b, key, mods); | |
135 | end_key_grab(b); | |
136 | b->key = key; | |
137 | b->mods = mods; | |
138 | g_signal_emit_by_name(G_OBJECT(b), "changed", b->key, b->mods); | |
139 | return; | |
140 | } | |
141 | ||
142 | keygrab_button_set_key(b, key, mods); | |
143 | } | |
144 | ||
145 | void keygrab_button_set_key(KeyGrabButton* self, guint key, GdkModifierType mods) | |
146 | { | |
147 | KeyGrabButton* b = KEYGRAB_BUTTON(self); | |
148 | gchar *label; | |
149 | b->key = key; | |
150 | b->mods = mods; | |
151 | ||
152 | label = FcitxHotkeyGetKeyString(key, mods); | |
153 | ||
154 | if (label == NULL || strlen(label) == 0) { | |
155 | gtk_button_set_label(GTK_BUTTON(b), _("Disabled")); | |
156 | } else { | |
157 | gchar* lb = label; | |
158 | gtk_button_set_label(GTK_BUTTON(b), lb); | |
159 | } | |
160 | ||
161 | if (label) | |
162 | free(label); | |
163 | } | |
164 | ||
165 | void keygrab_button_get_key(KeyGrabButton* self, guint* key, GdkModifierType* mods) | |
166 | { | |
167 | if (key) | |
168 | *key = self->key; | |
169 | if (mods) | |
170 | *mods = self->mods; | |
171 | } | |
172 | ||
173 | GtkWidget* popup_new(GtkWidget* parent, const gchar* text, gboolean mouse) | |
174 | { | |
175 | GtkWidget* w = gtk_window_new(GTK_WINDOW_TOPLEVEL); | |
176 | gtk_window_set_type_hint(GTK_WINDOW(w), GDK_WINDOW_TYPE_HINT_UTILITY); | |
177 | gtk_window_set_position(GTK_WINDOW(w), mouse ? GTK_WIN_POS_MOUSE : GTK_WIN_POS_CENTER_ALWAYS); | |
178 | if (parent) | |
179 | gtk_window_set_transient_for(GTK_WINDOW(w), GTK_WINDOW(gtk_widget_get_toplevel(parent))); | |
180 | gtk_window_set_modal(GTK_WINDOW(w), TRUE); | |
181 | gtk_window_set_decorated(GTK_WINDOW(w), TRUE); | |
182 | gtk_window_set_skip_taskbar_hint(GTK_WINDOW(w), TRUE); | |
183 | if (text) { | |
184 | GtkWidget* label = gtk_label_new(text); | |
185 | GtkWidget* align = gtk_alignment_new(0, 0, 1, 1); | |
186 | gtk_alignment_set_padding(GTK_ALIGNMENT(align), 20, 20, 20, 20); | |
187 | gtk_container_add(GTK_CONTAINER(align), label); | |
188 | gtk_container_add(GTK_CONTAINER(w), align); | |
189 | } | |
190 | ||
191 | return w; | |
192 | } |
0 | /*************************************************************************** | |
1 | * Copyright (C) 2010~2012 by CSSlayer * | |
2 | * * | |
3 | * This program is free software; you can redistribute it and/or modify * | |
4 | * it under the terms of the GNU General Public License as published by * | |
5 | * the Free Software Foundation; either version 2 of the License, or * | |
6 | * (at your option) any later version. * | |
7 | * * | |
8 | * This program is distributed in the hope that it will be useful, * | |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * | |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * | |
11 | * GNU General Public License for more details. * | |
12 | * * | |
13 | * You should have received a copy of the GNU General Public License * | |
14 | * along with this program; if not, write to the * | |
15 | * Free Software Foundation, Inc., * | |
16 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * | |
17 | ***************************************************************************/ | |
18 | ||
19 | #ifndef KEYGRAB_H | |
20 | #define KEYGRAB_H | |
21 | ||
22 | #include <gtk/gtk.h> | |
23 | //定义类型宏和转换宏 | |
24 | #define TYPE_KEYGRAB_BUTTON (keygrab_button_get_type()) | |
25 | #define KEYGRAB_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST(obj,TYPE_KEYGRAB_BUTTON,KeyGrabButton)) | |
26 | //定义实例结构和类结构 | |
27 | typedef struct _KeyGrabButton KeyGrabButton; | |
28 | typedef struct _KeyGrabButtonClass KeyGrabButtonClass; | |
29 | struct _KeyGrabButton { | |
30 | GtkButton parent; //父控件为横向盒状容器 | |
31 | GtkWidget* popup; | |
32 | gulong handler; | |
33 | guint key; | |
34 | GdkModifierType mods; | |
35 | }; | |
36 | struct _KeyGrabButtonClass { | |
37 | GtkButtonClass parent_class; | |
38 | void (*changed)(int, int); | |
39 | void (*current_changed)(int, int); | |
40 | }; | |
41 | ||
42 | GType keygrab_button_get_type(void); | |
43 | GtkWidget* keygrab_button_new(void); | |
44 | gchar *accelerator_to_fcitx_hotkey(const gchar* str); | |
45 | void keygrab_button_set_key(KeyGrabButton* self, guint key, GdkModifierType mods); | |
46 | void keygrab_button_get_key(KeyGrabButton* self, guint* key, GdkModifierType* mods); | |
47 | ||
48 | #endif |
0 | /* Definitions for locale archive handling. | |
1 | Copyright (C) 2002 Free Software Foundation, Inc. | |
2 | This file is part of the GNU C Library. | |
3 | ||
4 | The GNU C Library is free software; you can redistribute it and/or | |
5 | modify it under the terms of the GNU Lesser General Public | |
6 | License as published by the Free Software Foundation; either | |
7 | version 2.1 of the License, or (at your option) any later version. | |
8 | ||
9 | The GNU C Library is distributed in the hope that it will be useful, | |
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
12 | Lesser General Public License for more details. | |
13 | ||
14 | You should have received a copy of the GNU Lesser General Public | |
15 | License along with the GNU C Library; if not, write to the Free | |
16 | Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA | |
17 | 02111-1307 USA. */ | |
18 | ||
19 | #ifndef _LOCARCHIVE_H | |
20 | #define _LOCARCHIVE_H 1 | |
21 | ||
22 | #include <stdint.h> | |
23 | ||
24 | #define AR_MAGIC 0xde020109 | |
25 | ||
26 | struct locarhead { | |
27 | uint32_t magic; | |
28 | /* Serial number. */ | |
29 | uint32_t serial; | |
30 | /* Name hash table. */ | |
31 | uint32_t namehash_offset; | |
32 | uint32_t namehash_used; | |
33 | uint32_t namehash_size; | |
34 | /* String table. */ | |
35 | uint32_t string_offset; | |
36 | uint32_t string_used; | |
37 | uint32_t string_size; | |
38 | /* Table with locale records. */ | |
39 | uint32_t locrectab_offset; | |
40 | uint32_t locrectab_used; | |
41 | uint32_t locrectab_size; | |
42 | /* MD5 sum hash table. */ | |
43 | uint32_t sumhash_offset; | |
44 | uint32_t sumhash_used; | |
45 | uint32_t sumhash_size; | |
46 | }; | |
47 | ||
48 | ||
49 | struct namehashent { | |
50 | /* Hash value of the name. */ | |
51 | uint32_t hashval; | |
52 | /* Offset of the name in the string table. */ | |
53 | uint32_t name_offset; | |
54 | /* Offset of the locale record. */ | |
55 | uint32_t locrec_offset; | |
56 | }; | |
57 | ||
58 | ||
59 | struct sumhashent { | |
60 | /* MD5 sum. */ | |
61 | char sum[16]; | |
62 | /* Offset of the file in the archive. */ | |
63 | uint32_t file_offset; | |
64 | }; | |
65 | ||
66 | struct locrecent { | |
67 | uint32_t refs; /* # of namehashent records that point here */ | |
68 | struct { | |
69 | uint32_t offset; | |
70 | uint32_t len; | |
71 | } record[__LC_LAST]; | |
72 | }; | |
73 | ||
74 | ||
75 | struct locarhandle { | |
76 | int fd; | |
77 | void *addr; | |
78 | size_t len; | |
79 | }; | |
80 | ||
81 | ||
82 | /* In memory data for the locales with their checksums. */ | |
83 | typedef struct locale_category_data { | |
84 | off_t size; | |
85 | void *addr; | |
86 | char sum[16]; | |
87 | } locale_data_t[__LC_LAST]; | |
88 | ||
89 | #endif /* locarchive.h */ |
0 | /*************************************************************************** | |
1 | * Copyright (C) 2010~2012 by CSSlayer * | |
2 | * * | |
3 | * This program is free software; you can redistribute it and/or modify * | |
4 | * it under the terms of the GNU General Public License as published by * | |
5 | * the Free Software Foundation; either version 2 of the License, or * | |
6 | * (at your option) any later version. * | |
7 | * * | |
8 | * This program is distributed in the hope that it will be useful, * | |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * | |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * | |
11 | * GNU General Public License for more details. * | |
12 | * * | |
13 | * You should have received a copy of the GNU General Public License * | |
14 | * along with this program; if not, write to the * | |
15 | * Free Software Foundation, Inc., * | |
16 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * | |
17 | ***************************************************************************/ | |
18 | ||
19 | #include <gtk/gtk.h> | |
20 | #include <langinfo.h> | |
21 | #include <libintl.h> | |
22 | #include <locale.h> | |
23 | #include "config.h" | |
24 | #include "main_window.h" | |
25 | ||
26 | static void | |
27 | fcitx_config_app_activate (GApplication *application) | |
28 | { | |
29 | GList* list = gtk_application_get_windows (GTK_APPLICATION(application)); | |
30 | if (list) | |
31 | { | |
32 | gtk_window_present (GTK_WINDOW (list->data)); | |
33 | } | |
34 | else { | |
35 | GtkWidget *window; | |
36 | window = fcitx_main_window_new(); | |
37 | gtk_application_add_window(GTK_APPLICATION(application), GTK_WINDOW(window)); | |
38 | gtk_widget_show_all (GTK_WIDGET (window)); | |
39 | } | |
40 | } | |
41 | ||
42 | typedef GtkApplication FcitxConfigApp; | |
43 | typedef GtkApplicationClass FcitxConfigAppClass; | |
44 | ||
45 | G_DEFINE_TYPE (FcitxConfigApp, fcitx_config_app, GTK_TYPE_APPLICATION) | |
46 | ||
47 | static void | |
48 | fcitx_config_app_finalize (GObject *object) | |
49 | { | |
50 | G_OBJECT_CLASS (fcitx_config_app_parent_class)->finalize (object); | |
51 | } | |
52 | ||
53 | static void | |
54 | fcitx_config_app_init (FcitxConfigApp *app) | |
55 | { | |
56 | } | |
57 | ||
58 | static void | |
59 | fcitx_config_app_class_init (FcitxConfigAppClass *klass) | |
60 | { | |
61 | G_OBJECT_CLASS (klass)->finalize= fcitx_config_app_finalize; | |
62 | ||
63 | G_APPLICATION_CLASS (klass)->activate = fcitx_config_app_activate; | |
64 | } | |
65 | ||
66 | FcitxConfigApp * | |
67 | fcitx_config_app_new (void) | |
68 | { | |
69 | g_type_init (); | |
70 | ||
71 | return g_object_new (fcitx_config_app_get_type (), | |
72 | "application-id", "org.fcitx.FcitxConfigGtk3", | |
73 | "flags", G_APPLICATION_FLAGS_NONE, | |
74 | NULL); | |
75 | } | |
76 | ||
77 | int | |
78 | main(int argc, char **argv) | |
79 | { | |
80 | setlocale(LC_ALL, ""); | |
81 | bindtextdomain("fcitx-configtool", LOCALEDIR); | |
82 | bind_textdomain_codeset("fcitx-configtool", "UTF-8"); | |
83 | bindtextdomain("fcitx", LOCALEDIR); | |
84 | bind_textdomain_codeset("fcitx", "UTF-8"); | |
85 | textdomain("fcitx-configtool"); | |
86 | ||
87 | GtkApplication* app = fcitx_config_app_new(); | |
88 | ||
89 | int status = g_application_run (G_APPLICATION (app), argc, argv); | |
90 | g_object_unref (app); | |
91 | ||
92 | return status; | |
93 | } | |
94 | ||
95 |
0 | /*************************************************************************** | |
1 | * Copyright (C) 2010~2012 by CSSlayer * | |
2 | * * | |
3 | * This program is free software; you can redistribute it and/or modify * | |
4 | * it under the terms of the GNU General Public License as published by * | |
5 | * the Free Software Foundation; either version 2 of the License, or * | |
6 | * (at your option) any later version. * | |
7 | * * | |
8 | * This program is distributed in the hope that it will be useful, * | |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * | |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * | |
11 | * GNU General Public License for more details. * | |
12 | * * | |
13 | * You should have received a copy of the GNU General Public License * | |
14 | * along with this program; if not, write to the * | |
15 | * Free Software Foundation, Inc., * | |
16 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * | |
17 | ***************************************************************************/ | |
18 | ||
19 | #include <stdlib.h> | |
20 | #include <libintl.h> | |
21 | #include <errno.h> | |
22 | ||
23 | #include <fcitx/addon.h> | |
24 | #include <fcitx-utils/utarray.h> | |
25 | #include <fcitx-config/fcitx-config.h> | |
26 | #include <fcitx-config/xdg.h> | |
27 | ||
28 | #include "config.h" | |
29 | #include "main_window.h" | |
30 | #include "config_widget.h" | |
31 | #include "configdesc.h" | |
32 | #include "im_widget.h" | |
33 | ||
34 | enum { | |
35 | LIST_ADDON, | |
36 | N_COLUMNS | |
37 | }; | |
38 | ||
39 | enum { | |
40 | PAGE_LIST_NAME, | |
41 | PAGE_LIST_PAGE, | |
42 | PAGE_LIST_ICON, | |
43 | PAGE_N_COLUMNS | |
44 | }; | |
45 | ||
46 | G_DEFINE_TYPE(FcitxMainWindow, fcitx_main_window, GTK_TYPE_WINDOW) | |
47 | ||
48 | static void fcitx_main_window_finalize(GObject* object); | |
49 | ||
50 | static GtkListStore *_fcitx_main_window_create_model(); | |
51 | ||
52 | static void _fcitx_main_window_add_config_file_page(FcitxMainWindow* self); | |
53 | ||
54 | static void _fcitx_main_window_add_addon_page(FcitxMainWindow* self); | |
55 | ||
56 | static void _fcitx_main_window_add_im_page(FcitxMainWindow* self); | |
57 | ||
58 | static void _fcitx_main_window_selection_changed_cb(GtkIconView *iconview, gpointer data); | |
59 | ||
60 | static ConfigPage* _fcitx_main_window_add_page(FcitxMainWindow* self, const char* name, GtkWidget* widget, const char* stock); | |
61 | ||
62 | static void _fcitx_main_window_addon_selection_changed(GtkTreeSelection *selection, gpointer data); | |
63 | ||
64 | static void _fcitx_main_window_configure_button_clicked(GtkButton *button, gpointer data); | |
65 | ||
66 | static void _fcitx_main_window_enabled_data_func(GtkCellLayout *cell_layout, | |
67 | GtkCellRenderer *renderer, | |
68 | GtkTreeModel *tree_model, | |
69 | GtkTreeIter *iter, | |
70 | gpointer user_data); | |
71 | ||
72 | static void _fcitx_main_window_name_data_func(GtkCellLayout *cell_layout, | |
73 | GtkCellRenderer *renderer, | |
74 | GtkTreeModel *tree_model, | |
75 | GtkTreeIter *iter, | |
76 | gpointer user_data); | |
77 | ||
78 | static void _fcitx_main_window_apply_button_clicked(GtkButton *button, | |
79 | gpointer user_data); | |
80 | ||
81 | static void _fcitx_main_window_toggled_cb(GtkCellRenderer *renderer, | |
82 | gchar* str_path, | |
83 | gpointer user_data); | |
84 | ||
85 | const UT_icd addonicd = {sizeof(FcitxAddon), 0, 0, FcitxAddonFree}; | |
86 | ||
87 | static void | |
88 | fcitx_main_window_class_init(FcitxMainWindowClass *klass) | |
89 | { | |
90 | GObjectClass *gobject_class = G_OBJECT_CLASS(klass); | |
91 | gobject_class->finalize = fcitx_main_window_finalize; | |
92 | } | |
93 | ||
94 | static void | |
95 | fcitx_main_window_init(FcitxMainWindow* self) | |
96 | { | |
97 | GtkWidget* vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); | |
98 | GtkWidget* hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); | |
99 | ||
100 | self->pagestore = _fcitx_main_window_create_model(); | |
101 | self->pageview = gtk_icon_view_new_with_model(GTK_TREE_MODEL(self->pagestore)); | |
102 | ||
103 | gtk_icon_view_set_pixbuf_column(GTK_ICON_VIEW(self->pageview), PAGE_LIST_ICON); | |
104 | gtk_icon_view_set_text_column(GTK_ICON_VIEW(self->pageview), PAGE_LIST_NAME); | |
105 | gtk_icon_view_set_item_orientation(GTK_ICON_VIEW(self->pageview), GTK_ORIENTATION_VERTICAL); | |
106 | ||
107 | _fcitx_main_window_add_im_page(self); | |
108 | _fcitx_main_window_add_config_file_page(self); | |
109 | _fcitx_main_window_add_addon_page(self); | |
110 | ||
111 | gtk_widget_set_size_request(GTK_WIDGET(self), -1, 500); | |
112 | ||
113 | self->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); | |
114 | self->pagelabel = gtk_label_new(""); | |
115 | gtk_label_set_use_markup(GTK_LABEL(self->pagelabel), true); | |
116 | gtk_misc_set_alignment(GTK_MISC(self->pagelabel), 0, 0.5); | |
117 | ||
118 | gtk_box_pack_start(GTK_BOX(self->vbox), self->pagelabel, FALSE, FALSE, 14); | |
119 | GtkWidget* scrolledwindow = gtk_scrolled_window_new(NULL, NULL); | |
120 | gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwindow), GTK_POLICY_NEVER, GTK_POLICY_NEVER); | |
121 | gtk_container_add(GTK_CONTAINER(scrolledwindow), self->pageview); | |
122 | gtk_box_pack_start(GTK_BOX(hbox), scrolledwindow, FALSE, TRUE, 4); | |
123 | gtk_box_pack_start(GTK_BOX(hbox), self->vbox, TRUE, TRUE, 8); | |
124 | gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 8); | |
125 | ||
126 | gtk_container_add(GTK_CONTAINER(self), vbox); | |
127 | ||
128 | gtk_icon_view_set_selection_mode(GTK_ICON_VIEW(self->pageview), GTK_SELECTION_SINGLE); | |
129 | gtk_icon_view_set_item_padding(GTK_ICON_VIEW(self->pageview), 0); | |
130 | gtk_icon_view_set_margin(GTK_ICON_VIEW(self->pageview), 0); | |
131 | gtk_icon_view_set_column_spacing(GTK_ICON_VIEW(self->pageview), 0); | |
132 | gtk_icon_view_set_row_spacing(GTK_ICON_VIEW(self->pageview), 0); | |
133 | gtk_icon_view_set_item_width(GTK_ICON_VIEW(self->pageview), 96); | |
134 | ||
135 | g_signal_connect(G_OBJECT(self->pageview), "selection-changed", | |
136 | G_CALLBACK(_fcitx_main_window_selection_changed_cb), self); | |
137 | ||
138 | GtkTreePath* path = gtk_tree_model_get_path(GTK_TREE_MODEL(self->pagestore), &self->impage->iter); | |
139 | gtk_icon_view_select_path(GTK_ICON_VIEW(self->pageview), path); | |
140 | gtk_tree_path_free(path); | |
141 | ||
142 | gtk_window_set_icon_name(GTK_WINDOW(self), "fcitx-configtool"); | |
143 | gtk_window_set_title(GTK_WINDOW(self), _("Fcitx Config")); | |
144 | } | |
145 | ||
146 | GtkWidget* | |
147 | fcitx_main_window_new() | |
148 | { | |
149 | FcitxMainWindow* widget = | |
150 | g_object_new(FCITX_TYPE_MAIN_WINDOW, | |
151 | NULL); | |
152 | ||
153 | return GTK_WIDGET(widget); | |
154 | } | |
155 | ||
156 | void fcitx_main_window_finalize(GObject* object) | |
157 | { | |
158 | FcitxMainWindow* self = FCITX_MAIN_WINDOW(object); | |
159 | utarray_free(self->addons); | |
160 | } | |
161 | ||
162 | ConfigPage* _fcitx_main_window_add_page(FcitxMainWindow* self, const char* name, GtkWidget* widget, const char* stock) | |
163 | { | |
164 | ConfigPage *page = (ConfigPage*) malloc(sizeof(ConfigPage)); | |
165 | memset(page, 0, sizeof(ConfigPage)); | |
166 | ||
167 | page->page = widget; | |
168 | ||
169 | g_object_ref(page->page); | |
170 | ||
171 | gtk_widget_show_all(widget); | |
172 | ||
173 | gtk_list_store_append(self->pagestore, &page->iter); | |
174 | gtk_list_store_set(self->pagestore, &page->iter, | |
175 | 0, name, | |
176 | 1, page, | |
177 | 2, gtk_widget_render_icon(self->pageview, stock, GTK_ICON_SIZE_DND, NULL), | |
178 | -1); | |
179 | ||
180 | return page; | |
181 | } | |
182 | ||
183 | void _fcitx_main_window_selection_changed_cb(GtkIconView* iconview, gpointer data) | |
184 | { | |
185 | GtkTreeModel *model = gtk_icon_view_get_model(iconview); | |
186 | GtkTreeIter iter; | |
187 | ConfigPage* page; | |
188 | FcitxMainWindow* self = data; | |
189 | ||
190 | GList* list = gtk_icon_view_get_selected_items(iconview); | |
191 | ||
192 | if (list) { | |
193 | gchar* title; | |
194 | gtk_tree_model_get_iter(GTK_TREE_MODEL(self->pagestore), &iter, (GtkTreePath*)(list->data)); | |
195 | gtk_tree_model_get(model, &iter, | |
196 | PAGE_LIST_NAME, &title, | |
197 | PAGE_LIST_PAGE, &page, | |
198 | -1); | |
199 | ||
200 | gchar* text = g_strdup_printf("<b>%s</b>", title); | |
201 | gtk_label_set_markup(GTK_LABEL(self->pagelabel), text); | |
202 | g_free(text); | |
203 | g_free(title); | |
204 | ||
205 | if (self->lastpage) | |
206 | gtk_container_remove(GTK_CONTAINER(self->vbox), self->lastpage->page); | |
207 | gtk_box_pack_end(GTK_BOX(self->vbox), page->page, TRUE, TRUE, 0); | |
208 | gtk_widget_show_all(GTK_WIDGET(self)); | |
209 | ||
210 | self->lastpage = page; | |
211 | } else { | |
212 | GtkTreePath* path = gtk_tree_model_get_path(GTK_TREE_MODEL(self->pagestore), &self->impage->iter); | |
213 | gtk_icon_view_select_path(GTK_ICON_VIEW(self->pageview), path); | |
214 | gtk_tree_path_free(path); | |
215 | } | |
216 | ||
217 | g_list_foreach (list, (GFunc)gtk_tree_path_free, NULL); | |
218 | g_list_free (list); | |
219 | } | |
220 | ||
221 | ||
222 | void _fcitx_main_window_addon_selection_changed(GtkTreeSelection *selection, gpointer data) | |
223 | { | |
224 | GtkTreeView *treeView = gtk_tree_selection_get_tree_view(selection); | |
225 | GtkTreeModel *model = gtk_tree_view_get_model(treeView); | |
226 | GtkTreeIter iter; | |
227 | FcitxAddon *addon = NULL; | |
228 | FcitxMainWindow* self = data; | |
229 | ||
230 | if (!self->button) | |
231 | return; | |
232 | ||
233 | if (gtk_tree_selection_get_selected(selection, &model, &iter)) { | |
234 | gtk_tree_model_get(model, &iter, | |
235 | LIST_ADDON, &addon, | |
236 | -1); | |
237 | gchar* config_desc_name = g_strdup_printf("%s.desc", addon->name); | |
238 | FcitxConfigFileDesc* cfdesc = get_config_desc(config_desc_name); | |
239 | g_free(config_desc_name); | |
240 | gboolean configurable = (gboolean)(cfdesc != NULL || strlen(addon->subconfig) != 0); | |
241 | gtk_widget_set_sensitive(self->button, configurable); | |
242 | } else { | |
243 | gtk_widget_set_sensitive(self->button, FALSE); | |
244 | } | |
245 | } | |
246 | ||
247 | static GtkListStore *_fcitx_main_window_create_model() | |
248 | { | |
249 | GtkListStore* store = gtk_list_store_new(PAGE_N_COLUMNS, G_TYPE_STRING, G_TYPE_POINTER, GDK_TYPE_PIXBUF); | |
250 | return store; | |
251 | } | |
252 | ||
253 | void _fcitx_main_window_add_config_file_page(FcitxMainWindow* self) | |
254 | { | |
255 | GtkWidget* vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); | |
256 | ||
257 | FcitxConfigWidget* config_widget = fcitx_config_widget_new( | |
258 | get_config_desc("config.desc"), | |
259 | "", | |
260 | "config", | |
261 | NULL | |
262 | ); | |
263 | gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(config_widget), TRUE, TRUE, 0); | |
264 | ||
265 | GtkWidget* hbuttonbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL); | |
266 | gtk_box_pack_start(GTK_BOX(vbox), hbuttonbox, FALSE, TRUE, 0); | |
267 | ||
268 | GtkWidget* applybutton = gtk_button_new_from_stock(GTK_STOCK_APPLY); | |
269 | gtk_box_pack_start(GTK_BOX(hbuttonbox), applybutton, TRUE, TRUE, 0); | |
270 | g_signal_connect(G_OBJECT(applybutton), "clicked", G_CALLBACK(_fcitx_main_window_apply_button_clicked), config_widget); | |
271 | ||
272 | ||
273 | self->configpage = _fcitx_main_window_add_page(self, _("Global Config"), vbox, GTK_STOCK_PREFERENCES); | |
274 | } | |
275 | ||
276 | void _fcitx_main_window_add_im_page(FcitxMainWindow* self) | |
277 | { | |
278 | GtkWidget* imwidget = fcitx_im_widget_new(); | |
279 | self->impage = _fcitx_main_window_add_page(self, _("Input Method"), imwidget, GTK_STOCK_EDIT); | |
280 | } | |
281 | ||
282 | void _fcitx_main_window_add_addon_page(FcitxMainWindow* self) | |
283 | { | |
284 | FcitxAddon* addon; | |
285 | utarray_new(self->addons, &addonicd); | |
286 | FcitxAddonsLoad(self->addons); | |
287 | ||
288 | GtkWidget* vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); | |
289 | ||
290 | GtkListStore *store; | |
291 | store = gtk_list_store_new(N_COLUMNS, G_TYPE_POINTER); | |
292 | ||
293 | GtkWidget* swin = gtk_scrolled_window_new(NULL, NULL); | |
294 | gtk_box_pack_start(GTK_BOX(vbox), swin, TRUE, TRUE, 0); | |
295 | g_object_set(swin, "hscrollbar-policy", GTK_POLICY_NEVER, NULL); | |
296 | self->addonview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); | |
297 | g_object_set(self->addonview, "headers-visible", FALSE, NULL); | |
298 | gtk_container_add(GTK_CONTAINER(swin), self->addonview); | |
299 | GtkCellRenderer *renderer; | |
300 | GtkTreeViewColumn *column; | |
301 | ||
302 | renderer = gtk_cell_renderer_toggle_new(); | |
303 | column = gtk_tree_view_column_new_with_attributes("Enable", renderer, NULL); | |
304 | gtk_tree_view_append_column(GTK_TREE_VIEW(self->addonview), column); | |
305 | gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(column), | |
306 | renderer, | |
307 | _fcitx_main_window_enabled_data_func, | |
308 | self->addonview, | |
309 | NULL); | |
310 | g_signal_connect(G_OBJECT(renderer), "toggled", | |
311 | G_CALLBACK(_fcitx_main_window_toggled_cb), GTK_TREE_MODEL(store)); | |
312 | ||
313 | renderer = gtk_cell_renderer_text_new(); | |
314 | column = gtk_tree_view_column_new_with_attributes("Name", renderer, NULL); | |
315 | gtk_tree_view_append_column(GTK_TREE_VIEW(self->addonview), column); | |
316 | gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(column), | |
317 | renderer, | |
318 | _fcitx_main_window_name_data_func, | |
319 | self->addonview, | |
320 | NULL); | |
321 | ||
322 | gtk_tree_view_set_model(GTK_TREE_VIEW(self->addonview), | |
323 | GTK_TREE_MODEL(store)); | |
324 | ||
325 | g_object_unref(store); | |
326 | ||
327 | for (addon = (FcitxAddon *) utarray_front(self->addons); | |
328 | addon != NULL; | |
329 | addon = (FcitxAddon *) utarray_next(self->addons, addon)) { | |
330 | GtkTreeIter iter; | |
331 | store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(self->addonview))); | |
332 | gtk_list_store_append(store, &iter); | |
333 | gtk_list_store_set(store, &iter, LIST_ADDON, addon, -1); | |
334 | } | |
335 | ||
336 | GtkWidget* hbuttonbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL); | |
337 | gtk_box_pack_start(GTK_BOX(vbox), hbuttonbox, FALSE, TRUE, 0); | |
338 | ||
339 | self->button = gtk_button_new_with_label(_("Configure")); | |
340 | gtk_widget_set_sensitive(self->button, FALSE); | |
341 | gtk_button_set_image(GTK_BUTTON(self->button), gtk_image_new_from_stock(GTK_STOCK_PREFERENCES, GTK_ICON_SIZE_BUTTON)); | |
342 | gtk_box_pack_start(GTK_BOX(hbuttonbox), self->button, TRUE, TRUE, 0); | |
343 | g_signal_connect(G_OBJECT(self->button), "clicked", G_CALLBACK(_fcitx_main_window_configure_button_clicked), self); | |
344 | ||
345 | GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self->addonview)); | |
346 | gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE); | |
347 | g_signal_connect(G_OBJECT(selection), "changed", | |
348 | G_CALLBACK(_fcitx_main_window_addon_selection_changed), self); | |
349 | ||
350 | self->addonpage = _fcitx_main_window_add_page(self, _("Addon"), vbox, GTK_STOCK_ADD); | |
351 | } | |
352 | ||
353 | static void | |
354 | _fcitx_main_window_toggled_cb(GtkCellRenderer *renderer, | |
355 | gchar* str_path, | |
356 | gpointer user_data) | |
357 | { | |
358 | GtkTreeModel *model = (GtkTreeModel *)user_data; | |
359 | GtkTreePath *path = gtk_tree_path_new_from_string(str_path); | |
360 | GtkTreeIter iter; | |
361 | gtk_tree_model_get_iter(model, &iter, path); | |
362 | FcitxAddon* addon = NULL; | |
363 | gtk_tree_path_free(path); | |
364 | gtk_tree_model_get(model, | |
365 | &iter, | |
366 | LIST_ADDON, &addon, | |
367 | -1); | |
368 | ||
369 | if (!addon) | |
370 | return; | |
371 | ||
372 | addon->bEnabled = !addon->bEnabled; | |
373 | char *buf; | |
374 | asprintf(&buf, "%s.conf", addon->name); | |
375 | FILE* fp = FcitxXDGGetFileUserWithPrefix("addon", buf, "w", NULL); | |
376 | free(buf); | |
377 | if (fp) { | |
378 | fprintf(fp, "[Addon]\nEnabled=%s\n", addon->bEnabled ? "True" : "False"); | |
379 | fclose(fp); | |
380 | } | |
381 | g_object_set(renderer, | |
382 | "active", (gboolean) addon->bEnabled, | |
383 | NULL); | |
384 | } | |
385 | ||
386 | static void | |
387 | _fcitx_main_window_enabled_data_func(GtkCellLayout *cell_layout, | |
388 | GtkCellRenderer *renderer, | |
389 | GtkTreeModel *tree_model, | |
390 | GtkTreeIter *iter, | |
391 | gpointer user_data) | |
392 | { | |
393 | FcitxAddon* addon; | |
394 | ||
395 | gtk_tree_model_get(tree_model, | |
396 | iter, | |
397 | LIST_ADDON, &addon, | |
398 | -1); | |
399 | g_object_set(renderer, | |
400 | "active", (gboolean) addon->bEnabled, | |
401 | NULL); | |
402 | } | |
403 | ||
404 | static void | |
405 | _fcitx_main_window_name_data_func(GtkCellLayout *cell_layout, | |
406 | GtkCellRenderer *renderer, | |
407 | GtkTreeModel *tree_model, | |
408 | GtkTreeIter *iter, | |
409 | gpointer user_data) | |
410 | { | |
411 | FcitxAddon* addon; | |
412 | ||
413 | gtk_tree_model_get(tree_model, | |
414 | iter, | |
415 | LIST_ADDON, &addon, | |
416 | -1); | |
417 | gchar* string = g_strdup_printf("%s\n%s", addon->generalname, addon->comment); | |
418 | g_object_set(renderer, | |
419 | "text", string, | |
420 | NULL); | |
421 | ||
422 | g_free(string); | |
423 | } | |
424 | ||
425 | ||
426 | void _fcitx_main_window_apply_button_clicked(GtkButton* button, gpointer user_data) | |
427 | { | |
428 | FcitxConfigWidget* config_widget = user_data; | |
429 | fcitx_config_widget_response(config_widget, CONFIG_WIDGET_SAVE); | |
430 | } | |
431 | ||
432 | void _fcitx_main_window_configure_button_clicked(GtkButton* button, gpointer data) | |
433 | { | |
434 | FcitxMainWindow* self = data; | |
435 | GtkTreeView* view = GTK_TREE_VIEW(self->addonview); | |
436 | GtkTreeSelection *selection = gtk_tree_view_get_selection(view); | |
437 | GtkTreeModel *model = gtk_tree_view_get_model(view); | |
438 | GtkTreeIter iter; | |
439 | FcitxAddon *addon = NULL; | |
440 | if (gtk_tree_selection_get_selected(selection, &model, &iter)) { | |
441 | gtk_tree_model_get(model, &iter, | |
442 | LIST_ADDON, &addon, | |
443 | -1); | |
444 | gchar* config_desc_name = g_strdup_printf("%s.desc", addon->name); | |
445 | FcitxConfigFileDesc* cfdesc = get_config_desc(config_desc_name); | |
446 | g_free(config_desc_name); | |
447 | gboolean configurable = (gboolean)(cfdesc != NULL || strlen(addon->subconfig) != 0); | |
448 | if (configurable) { | |
449 | GtkWidget* dialog = gtk_dialog_new_with_buttons(addon->generalname, | |
450 | GTK_WINDOW(self), | |
451 | GTK_DIALOG_MODAL, | |
452 | GTK_STOCK_OK, | |
453 | GTK_RESPONSE_OK, | |
454 | GTK_STOCK_CANCEL, | |
455 | GTK_RESPONSE_CANCEL, | |
456 | NULL | |
457 | ); | |
458 | ||
459 | gchar* config_file_name = g_strdup_printf("%s.config", addon->name); | |
460 | FcitxConfigWidget* config_widget = fcitx_config_widget_new(cfdesc, "conf", config_file_name, addon->subconfig); | |
461 | GtkWidget* content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); | |
462 | gtk_box_pack_start(GTK_BOX(content_area), GTK_WIDGET(config_widget), TRUE, TRUE, 0); | |
463 | g_free(config_file_name); | |
464 | gtk_widget_set_size_request(GTK_WIDGET(config_widget), -1, 400); | |
465 | ||
466 | g_signal_connect(dialog, "response", | |
467 | G_CALLBACK(fcitx_config_widget_response_cb), | |
468 | config_widget); | |
469 | ||
470 | gtk_widget_show_all(GTK_WIDGET(dialog)); | |
471 | } | |
472 | } | |
473 | } |
0 | /*************************************************************************** | |
1 | * Copyright (C) 2010~2012 by CSSlayer * | |
2 | * * | |
3 | * This program is free software; you can redistribute it and/or modify * | |
4 | * it under the terms of the GNU General Public License as published by * | |
5 | * the Free Software Foundation; either version 2 of the License, or * | |
6 | * (at your option) any later version. * | |
7 | * * | |
8 | * This program is distributed in the hope that it will be useful, * | |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * | |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * | |
11 | * GNU General Public License for more details. * | |
12 | * * | |
13 | * You should have received a copy of the GNU General Public License * | |
14 | * along with this program; if not, write to the * | |
15 | * Free Software Foundation, Inc., * | |
16 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * | |
17 | ***************************************************************************/ | |
18 | ||
19 | #ifndef MAIN_WINDOW_H | |
20 | ||
21 | #define MAIN_WINDOW_H | |
22 | ||
23 | #include <gtk/gtk.h> | |
24 | #include <fcitx-config/fcitx-config.h> | |
25 | ||
26 | #include "common.h" | |
27 | ||
28 | G_BEGIN_DECLS | |
29 | ||
30 | #define FCITX_TYPE_MAIN_WINDOW fcitx_main_window_get_type() | |
31 | ||
32 | #define FCITX_MAIN_WINDOW(obj) \ | |
33 | (G_TYPE_CHECK_INSTANCE_CAST ((obj), FCITX_TYPE_MAIN_WINDOW, FcitxMainWindow)) | |
34 | ||
35 | #define FCITX_MAIN_WINDOW_CLASS(klass) \ | |
36 | (G_TYPE_CHECK_CLASS_CAST ((klass), FCITX_TYPE_MAIN_WINDOW, FcitxMainWindowClass)) | |
37 | ||
38 | #define FCITX_IS_MAIN_WINDOW(obj) \ | |
39 | (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FCITX_TYPE_MAIN_WINDOW)) | |
40 | ||
41 | #define FCITX_IS_MAIN_WINDOW_CLASS(klass) \ | |
42 | (G_TYPE_CHECK_CLASS_TYPE ((klass), FCITX_TYPE_MAIN_WINDOW)) | |
43 | ||
44 | #define FCITX_MAIN_WINDOW_GET_CLASS(obj) \ | |
45 | (G_TYPE_INSTANCE_GET_CLASS ((obj), FCITX_TYPE_MAIN_WINDOW, FcitxMainWindowClass)) | |
46 | ||
47 | typedef struct { | |
48 | GtkWidget* page; | |
49 | GtkTreeIter iter; | |
50 | } ConfigPage; | |
51 | ||
52 | typedef struct { | |
53 | GtkWindow parent; | |
54 | GtkWidget* pageview; | |
55 | GtkListStore *pagestore; | |
56 | GtkWidget* vbox; | |
57 | GtkWidget* pagelabel; | |
58 | ConfigPage* impage; | |
59 | ConfigPage* configpage; | |
60 | ConfigPage* lastpage; | |
61 | ConfigPage* addonpage; | |
62 | GtkWidget* button; | |
63 | GtkWidget* addonview; | |
64 | UT_array* addons; | |
65 | ||
66 | } FcitxMainWindow; | |
67 | ||
68 | typedef struct { | |
69 | GtkWindowClass parent_class; | |
70 | } FcitxMainWindowClass; | |
71 | ||
72 | GType fcitx_main_window_get_type(void); | |
73 | ||
74 | GtkWidget* fcitx_main_window_new(void); | |
75 | ||
76 | #endif |
0 | /*************************************************************************** | |
1 | * Copyright (C) 2010~2012 by CSSlayer * | |
2 | * * | |
3 | * This program is free software; you can redistribute it and/or modify * | |
4 | * it under the terms of the GNU General Public License as published by * | |
5 | * the Free Software Foundation; either version 2 of the License, or * | |
6 | * (at your option) any later version. * | |
7 | * * | |
8 | * This program is distributed in the hope that it will be useful, * | |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * | |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * | |
11 | * GNU General Public License for more details. * | |
12 | * * | |
13 | * You should have received a copy of the GNU General Public License * | |
14 | * along with this program; if not, write to the * | |
15 | * Free Software Foundation, Inc., * | |
16 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * | |
17 | ***************************************************************************/ | |
18 | ||
19 | #include <limits.h> | |
20 | #include <stdlib.h> | |
21 | #include <dirent.h> | |
22 | #include <string.h> | |
23 | #include <sys/stat.h> | |
24 | #include <glib.h> | |
25 | ||
26 | #include <fcitx-config/xdg.h> | |
27 | #include <fcitx-utils/log.h> | |
28 | ||
29 | #include "config.h" | |
30 | ||
31 | #include "sub_config_parser.h" | |
32 | ||
33 | static void sub_config_pattern_free(void* pattern); | |
34 | static GList* sub_config_pattern_get_filelist(FcitxSubConfigPattern* pattern); | |
35 | static GList* get_files_by_pattern(const gchar* dirpath, FcitxSubConfigPattern* pattern, int index); | |
36 | static void sub_file_list_free(gpointer data, gpointer user_data); | |
37 | ||
38 | static SubConfigType parse_type(const gchar* str); | |
39 | ||
40 | static SubConfigType parse_type(const gchar* str) | |
41 | { | |
42 | if (strcmp(str, "native") == 0) { | |
43 | return SC_NativeFile; | |
44 | } | |
45 | if (strcmp(str, "configfile") == 0) { | |
46 | return SC_ConfigFile; | |
47 | } | |
48 | return SC_None; | |
49 | } | |
50 | ||
51 | FcitxSubConfigParser* sub_config_parser_new(const gchar* subconfig) | |
52 | { | |
53 | if (subconfig == NULL) | |
54 | return NULL; | |
55 | ||
56 | FcitxSubConfigParser* parser = g_malloc0(sizeof(FcitxSubConfigParser)); | |
57 | parser->subconfigs = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, sub_config_pattern_free); | |
58 | gchar** strv = g_strsplit(subconfig, ",", 0); | |
59 | ||
60 | gchar** str; | |
61 | for (str = &strv[0]; *str != NULL; str++) { | |
62 | if (strchr(*str, ':') == NULL) | |
63 | continue; | |
64 | ||
65 | gchar** items = g_strsplit(*str, ":", 0); | |
66 | ||
67 | if (g_strv_length(items) < 2) | |
68 | goto end; | |
69 | if (strlen(items[0]) == 0) | |
70 | goto end; | |
71 | ||
72 | if (strcmp(items[1], "domain") == 0) { | |
73 | parser->domain = g_strdup(items[0]); | |
74 | goto end; | |
75 | } | |
76 | ||
77 | SubConfigType type = parse_type(items[1]); | |
78 | if (type == SC_None) | |
79 | goto end; | |
80 | if (g_hash_table_lookup(parser->subconfigs, items[0]) != NULL) | |
81 | continue; | |
82 | ||
83 | if (type == SC_ConfigFile) { | |
84 | if (g_strv_length(items) != 4) | |
85 | goto end; | |
86 | if (strlen(items[2]) == 0 || items[2][0] == '/') | |
87 | goto end; | |
88 | } else if (type == SC_NativeFile) { | |
89 | if (g_strv_length(items) != 3) | |
90 | goto end; | |
91 | if (strchr(items[2], '*') != NULL) | |
92 | goto end; | |
93 | } | |
94 | ||
95 | gchar** paths = g_strsplit(items[2], "/", 0); | |
96 | if (paths[0] == 0) { | |
97 | g_strfreev(paths); | |
98 | goto end; | |
99 | } | |
100 | gchar** path; | |
101 | for (path = &paths[0]; *path != NULL; path++) { | |
102 | if (strlen(*path) == 0) | |
103 | break; | |
104 | if (strcmp(*path, ".") == 0) | |
105 | break; | |
106 | if (strcmp(*path, "..") == 0) | |
107 | break; | |
108 | } | |
109 | if (*path != NULL) { | |
110 | g_strfreev(paths); | |
111 | goto end; | |
112 | } | |
113 | FcitxSubConfigPattern* pattern = g_malloc0(sizeof(FcitxSubConfigPattern)); | |
114 | pattern->type = type; | |
115 | pattern->patternlist = paths; | |
116 | if (type == SC_ConfigFile) | |
117 | pattern->configdesc = g_strdup(items[3]); | |
118 | else if (type == SC_NativeFile) | |
119 | pattern->nativepath = g_strdup(items[2]); | |
120 | ||
121 | g_hash_table_insert(parser->subconfigs, g_strdup(items[0]), pattern); | |
122 | end: | |
123 | g_strfreev(items); | |
124 | } | |
125 | g_strfreev(strv); | |
126 | if (g_hash_table_size(parser->subconfigs) == 0 || parser->domain == NULL) { | |
127 | sub_config_parser_free(parser); | |
128 | parser = NULL; | |
129 | } | |
130 | ||
131 | return parser; | |
132 | } | |
133 | ||
134 | void sub_config_parser_free(FcitxSubConfigParser* parser) | |
135 | { | |
136 | if (parser == NULL) | |
137 | return; | |
138 | ||
139 | g_hash_table_destroy(parser->subconfigs); | |
140 | if (parser->domain) | |
141 | g_free(parser->domain); | |
142 | } | |
143 | ||
144 | void sub_config_pattern_free(void* data) | |
145 | { | |
146 | FcitxSubConfigPattern* pattern = data; | |
147 | if (pattern->patternlist) | |
148 | g_strfreev(pattern->patternlist); | |
149 | ||
150 | if (pattern->configdesc) | |
151 | g_free(pattern->configdesc); | |
152 | ||
153 | if (pattern->nativepath) | |
154 | g_free(pattern->nativepath); | |
155 | ||
156 | g_free(pattern); | |
157 | } | |
158 | ||
159 | FcitxSubConfig* sub_config_new(const gchar* name, FcitxSubConfigPattern* pattern) | |
160 | { | |
161 | if (pattern->type == SC_None) | |
162 | return NULL; | |
163 | ||
164 | FcitxSubConfig* subconfig = g_malloc0(sizeof(FcitxSubConfig)); | |
165 | subconfig->type = pattern->type; | |
166 | subconfig->configdesc = g_strdup(pattern->configdesc); | |
167 | subconfig->nativepath = g_strdup(pattern->nativepath); | |
168 | subconfig->name = g_strdup(name); | |
169 | subconfig->filelist = sub_config_pattern_get_filelist(pattern); | |
170 | ||
171 | return subconfig; | |
172 | } | |
173 | ||
174 | void sub_file_list_free(gpointer data, gpointer user_data) | |
175 | { | |
176 | g_free(data); | |
177 | } | |
178 | ||
179 | void sub_config_free(FcitxSubConfig* subconfig) | |
180 | { | |
181 | if (!subconfig) | |
182 | return; | |
183 | ||
184 | g_free(subconfig->configdesc); | |
185 | g_free(subconfig->nativepath); | |
186 | g_free(subconfig->name); | |
187 | g_list_foreach(subconfig->filelist, sub_file_list_free, NULL); | |
188 | g_list_free(subconfig->filelist); | |
189 | g_free(subconfig); | |
190 | } | |
191 | ||
192 | GList* sub_config_pattern_get_filelist(FcitxSubConfigPattern* pattern) | |
193 | { | |
194 | size_t size, i; | |
195 | GList* result = NULL; | |
196 | #if FCITX_CHECK_VERSION(4,2,1) | |
197 | char** xdgpath = FcitxXDGGetPathWithPrefix(&size, ""); | |
198 | #else | |
199 | char** xdgpath = FcitxXDGGetPath(&size, "XDG_CONFIG_HOME", ".config" , PACKAGE , DATADIR, PACKAGE); | |
200 | #endif | |
201 | ||
202 | for (i = 0; i < size; i ++) { | |
203 | char* dirpath = realpath(xdgpath[i], NULL); | |
204 | ||
205 | if (!dirpath) | |
206 | continue; | |
207 | ||
208 | GList* list = get_files_by_pattern(dirpath, pattern, 0), *l; | |
209 | ||
210 | for (l = g_list_first(list); | |
211 | l != NULL; | |
212 | l = l->next) { | |
213 | if (strncmp(dirpath, (gchar*) l->data, strlen(dirpath)) == 0) { | |
214 | gchar* filename = (gchar*) l->data; | |
215 | gchar* name = filename + strlen(dirpath); | |
216 | while (name[0] == '/') | |
217 | name ++; | |
218 | result = g_list_append(result, g_strdup(name)); | |
219 | } | |
220 | } | |
221 | g_list_foreach(list, sub_file_list_free, NULL); | |
222 | g_list_free(list); | |
223 | ||
224 | free(dirpath); | |
225 | } | |
226 | ||
227 | FcitxXDGFreePath(xdgpath); | |
228 | ||
229 | return result; | |
230 | } | |
231 | ||
232 | GList* get_files_by_pattern(const gchar* dirpath, FcitxSubConfigPattern* pattern, int index) | |
233 | { | |
234 | GList* result = NULL; | |
235 | ||
236 | DIR* dir = opendir(dirpath); | |
237 | if (!dir) | |
238 | return result; | |
239 | ||
240 | gchar* filter = pattern->patternlist[index]; | |
241 | ||
242 | struct dirent* drt; | |
243 | GPatternSpec * patternspec = g_pattern_spec_new(filter); | |
244 | while ((drt = readdir(dir)) != NULL) { | |
245 | if (strcmp(drt->d_name , ".") == 0 || strcmp(drt->d_name, "..") == 0) | |
246 | continue; | |
247 | ||
248 | if (!g_pattern_match_string(patternspec, drt->d_name)) | |
249 | continue; | |
250 | ||
251 | if (pattern->patternlist[index + 1] == 0) { | |
252 | char *path; | |
253 | asprintf(&path, "%s/%s", dirpath, drt->d_name); | |
254 | struct stat statbuf; | |
255 | if (stat(path, &statbuf) == 0) { | |
256 | result = g_list_append(result, realpath(path, NULL)); | |
257 | } | |
258 | free(path); | |
259 | } else { | |
260 | char *path; | |
261 | asprintf(&path, "%s/%s", dirpath, drt->d_name); | |
262 | GList* r = get_files_by_pattern(path, pattern, index + 1); | |
263 | result = g_list_concat(result, r); | |
264 | free(path); | |
265 | } | |
266 | } | |
267 | ||
268 | closedir(dir); | |
269 | g_pattern_spec_free(patternspec); | |
270 | return result; | |
271 | } |
0 | /*************************************************************************** | |
1 | * Copyright (C) 2010~2012 by CSSlayer * | |
2 | * * | |
3 | * This program is free software; you can redistribute it and/or modify * | |
4 | * it under the terms of the GNU General Public License as published by * | |
5 | * the Free Software Foundation; either version 2 of the License, or * | |
6 | * (at your option) any later version. * | |
7 | * * | |
8 | * This program is distributed in the hope that it will be useful, * | |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * | |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * | |
11 | * GNU General Public License for more details. * | |
12 | * * | |
13 | * You should have received a copy of the GNU General Public License * | |
14 | * along with this program; if not, write to the * | |
15 | * Free Software Foundation, Inc., * | |
16 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * | |
17 | ***************************************************************************/ | |
18 | ||
19 | #ifndef _SUB_CONFIG_PARSER_H | |
20 | #define _SUB_CONFIG_PARSER_H | |
21 | #include <glib.h> | |
22 | ||
23 | typedef enum { | |
24 | SC_None, | |
25 | SC_ConfigFile, | |
26 | SC_NativeFile | |
27 | } SubConfigType; | |
28 | ||
29 | typedef struct { | |
30 | SubConfigType type; | |
31 | gchar* configdesc; | |
32 | gchar* nativepath; | |
33 | gchar** patternlist; | |
34 | } FcitxSubConfigPattern; | |
35 | ||
36 | typedef struct { | |
37 | gchar* name; | |
38 | SubConfigType type; | |
39 | GList* filelist; | |
40 | gchar* nativepath; | |
41 | gchar* configdesc; | |
42 | } FcitxSubConfig; | |
43 | ||
44 | typedef struct { | |
45 | GHashTable* subconfigs; | |
46 | gchar* domain; | |
47 | } FcitxSubConfigParser; | |
48 | ||
49 | FcitxSubConfigParser* sub_config_parser_new(const gchar* subconfig); | |
50 | void sub_config_parser_free(FcitxSubConfigParser* parser); | |
51 | FcitxSubConfig* sub_config_new(const gchar* name, FcitxSubConfigPattern* pattern); | |
52 | void sub_config_free(FcitxSubConfig* subconfig); | |
53 | ||
54 | #endif |
0 | /*************************************************************************** | |
1 | * Copyright (C) 2010~2012 by CSSlayer * | |
2 | * * | |
3 | * This program is free software; you can redistribute it and/or modify * | |
4 | * it under the terms of the GNU General Public License as published by * | |
5 | * the Free Software Foundation; either version 2 of the License, or * | |
6 | * (at your option) any later version. * | |
7 | * * | |
8 | * This program is distributed in the hope that it will be useful, * | |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * | |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * | |
11 | * GNU General Public License for more details. * | |
12 | * * | |
13 | * You should have received a copy of the GNU General Public License * | |
14 | * along with this program; if not, write to the * | |
15 | * Free Software Foundation, Inc., * | |
16 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * | |
17 | ***************************************************************************/ | |
18 | ||
19 | #include <fcitx-config/xdg.h> | |
20 | #include <stdlib.h> | |
21 | ||
22 | #include "sub_config_widget.h" | |
23 | #include "main_window.h" | |
24 | #include "configdesc.h" | |
25 | #include "config_widget.h" | |
26 | ||
27 | G_DEFINE_TYPE(FcitxSubConfigWidget, fcitx_sub_config_widget, GTK_TYPE_BOX) | |
28 | ||
29 | static void open_subconfig_file(GtkButton *button, gpointer user_data); | |
30 | static void open_native_file(GtkButton *button, gpointer user_data); | |
31 | static void push_into_store_cb(gpointer data, gpointer user_data); | |
32 | ||
33 | static void | |
34 | fcitx_sub_config_widget_get_property(GObject *object, guint property_id, | |
35 | GValue *value, GParamSpec *pspec) | |
36 | { | |
37 | switch (property_id) { | |
38 | default: | |
39 | G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); | |
40 | } | |
41 | } | |
42 | ||
43 | static void | |
44 | fcitx_sub_config_widget_set_property(GObject *object, guint property_id, | |
45 | const GValue *value, GParamSpec *pspec) | |
46 | { | |
47 | switch (property_id) { | |
48 | default: | |
49 | G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); | |
50 | } | |
51 | } | |
52 | ||
53 | static void | |
54 | fcitx_sub_config_widget_finalize(GObject *object) | |
55 | { | |
56 | FcitxSubConfigWidget* widget = FCITX_SUB_CONFIG_WIDGET(object); | |
57 | sub_config_free(widget->subconfig); | |
58 | G_OBJECT_CLASS(fcitx_sub_config_widget_parent_class)->finalize(object); | |
59 | } | |
60 | ||
61 | static void | |
62 | fcitx_sub_config_widget_class_init(FcitxSubConfigWidgetClass *klass) | |
63 | { | |
64 | GObjectClass *object_class = G_OBJECT_CLASS(klass); | |
65 | ||
66 | ||
67 | object_class->get_property = fcitx_sub_config_widget_get_property; | |
68 | object_class->set_property = fcitx_sub_config_widget_set_property; | |
69 | object_class->finalize = fcitx_sub_config_widget_finalize; | |
70 | } | |
71 | ||
72 | static void | |
73 | fcitx_sub_config_widget_init(FcitxSubConfigWidget *self) | |
74 | { | |
75 | } | |
76 | ||
77 | FcitxSubConfigWidget* | |
78 | fcitx_sub_config_widget_new(FcitxSubConfig* subconfig) | |
79 | { | |
80 | FcitxSubConfigWidget* widget = g_object_new(FCITX_TYPE_SUB_CONFIG_WIDGET, NULL); | |
81 | ||
82 | widget->subconfig = subconfig; | |
83 | switch (subconfig->type) { | |
84 | case SC_ConfigFile: { | |
85 | GtkWidget* view = gtk_tree_view_new(); | |
86 | gtk_box_pack_start(GTK_BOX(widget), view, FALSE, FALSE, 0); | |
87 | ||
88 | GtkCellRenderer* renderer = gtk_cell_renderer_text_new(); | |
89 | GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes("Name", renderer, "text", 0, NULL); | |
90 | gtk_tree_view_append_column(GTK_TREE_VIEW(view), column); | |
91 | GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view)); | |
92 | gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE); | |
93 | ||
94 | GtkListStore* store = gtk_list_store_new(1, G_TYPE_STRING); | |
95 | ||
96 | gtk_tree_view_set_model(GTK_TREE_VIEW(view), | |
97 | GTK_TREE_MODEL(store)); | |
98 | ||
99 | g_list_foreach(widget->subconfig->filelist, push_into_store_cb, store); | |
100 | ||
101 | GtkWidget* button = gtk_button_new(); | |
102 | gtk_button_set_image(GTK_BUTTON(button), gtk_image_new_from_stock(GTK_STOCK_PREFERENCES, GTK_ICON_SIZE_BUTTON)); | |
103 | g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(open_subconfig_file), widget); | |
104 | gtk_box_pack_start(GTK_BOX(widget), button, FALSE, FALSE, 0); | |
105 | widget->view = view; | |
106 | } | |
107 | break; | |
108 | case SC_NativeFile: { | |
109 | GtkWidget* button = gtk_button_new(); | |
110 | gtk_button_set_image(GTK_BUTTON(button), gtk_image_new_from_stock(GTK_STOCK_OPEN, GTK_ICON_SIZE_BUTTON)); | |
111 | g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(open_native_file), widget); | |
112 | gtk_box_pack_start(GTK_BOX(widget), button, FALSE, FALSE, 0); | |
113 | } | |
114 | break; | |
115 | default: | |
116 | break; | |
117 | } | |
118 | ||
119 | return widget; | |
120 | } | |
121 | ||
122 | void open_subconfig_file(GtkButton *button, | |
123 | gpointer user_data) | |
124 | { | |
125 | FcitxSubConfigWidget* widget = (FcitxSubConfigWidget*) user_data; | |
126 | GtkTreeView* view = GTK_TREE_VIEW(widget->view); | |
127 | GtkTreeSelection *selection = gtk_tree_view_get_selection(view); | |
128 | GtkTreeModel *model = gtk_tree_view_get_model(view); | |
129 | GtkTreeIter iter; | |
130 | gchar* configfile; | |
131 | if (gtk_tree_selection_get_selected(selection, &model, &iter)) { | |
132 | gtk_tree_model_get(model, &iter, | |
133 | 0, &configfile, | |
134 | -1); | |
135 | FcitxConfigFileDesc* cfdesc = get_config_desc(widget->subconfig->configdesc); | |
136 | if (cfdesc) { | |
137 | GtkWidget* dialog = gtk_dialog_new_with_buttons(configfile, | |
138 | GTK_WINDOW(gtk_widget_get_ancestor(GTK_WIDGET(widget), GTK_TYPE_WINDOW)), | |
139 | GTK_DIALOG_MODAL, | |
140 | GTK_STOCK_OK, | |
141 | GTK_RESPONSE_OK, | |
142 | GTK_STOCK_CANCEL, | |
143 | GTK_RESPONSE_CANCEL, | |
144 | NULL | |
145 | ); | |
146 | FcitxConfigWidget* config_widget = fcitx_config_widget_new(cfdesc, "", configfile, NULL); | |
147 | GtkWidget* content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); | |
148 | gtk_box_pack_start(GTK_BOX(content_area), GTK_WIDGET(config_widget), TRUE, TRUE, 0); | |
149 | gtk_widget_set_size_request(GTK_WIDGET(config_widget), -1, 400); | |
150 | ||
151 | g_signal_connect(dialog, "response", | |
152 | G_CALLBACK(fcitx_config_widget_response_cb), | |
153 | config_widget); | |
154 | ||
155 | gtk_widget_show_all(GTK_WIDGET(dialog)); | |
156 | } | |
157 | } | |
158 | } | |
159 | ||
160 | void open_native_file(GtkButton *button, | |
161 | gpointer user_data) | |
162 | { | |
163 | FcitxSubConfigWidget* widget = (FcitxSubConfigWidget*) user_data; | |
164 | char *newpath = NULL; | |
165 | if (g_list_length(widget->subconfig->filelist) > 0) { | |
166 | FILE* fp = FcitxXDGGetFileWithPrefix("", widget->subconfig->filelist->data, "r", &newpath); | |
167 | if (fp) | |
168 | fclose(fp); | |
169 | } else { | |
170 | FILE* fp = FcitxXDGGetFileUserWithPrefix("", widget->subconfig->nativepath, "w", &newpath); | |
171 | if (fp) { | |
172 | widget->subconfig->filelist = g_list_append(widget->subconfig->filelist, widget->subconfig->nativepath); | |
173 | fclose(fp); | |
174 | } | |
175 | } | |
176 | ||
177 | if (newpath) { | |
178 | GError* error; | |
179 | gchar* filename = newpath; | |
180 | gchar* argv[3]; | |
181 | argv[0] = "xdg-open"; | |
182 | argv[1] = filename; | |
183 | argv[2] = 0; | |
184 | g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, &error); | |
185 | free(newpath); | |
186 | } | |
187 | } | |
188 | ||
189 | ||
190 | void push_into_store_cb(gpointer data, | |
191 | gpointer user_data) | |
192 | { | |
193 | GtkListStore* store = user_data; | |
194 | ||
195 | GtkTreeIter iter; | |
196 | ||
197 | gtk_list_store_append(store, &iter); | |
198 | gtk_list_store_set(store, &iter, | |
199 | 0, data, | |
200 | -1); | |
201 | } |
0 | /*************************************************************************** | |
1 | * Copyright (C) 2010~2012 by CSSlayer * | |
2 | * * | |
3 | * This program is free software; you can redistribute it and/or modify * | |
4 | * it under the terms of the GNU General Public License as published by * | |
5 | * the Free Software Foundation; either version 2 of the License, or * | |
6 | * (at your option) any later version. * | |
7 | * * | |
8 | * This program is distributed in the hope that it will be useful, * | |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * | |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * | |
11 | * GNU General Public License for more details. * | |
12 | * * | |
13 | * You should have received a copy of the GNU General Public License * | |
14 | * along with this program; if not, write to the * | |
15 | * Free Software Foundation, Inc., * | |
16 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * | |
17 | ***************************************************************************/ | |
18 | ||
19 | #ifndef _FCITX_SUB_CONFIG_WIDGET | |
20 | #define _FCITX_SUB_CONFIG_WIDGET | |
21 | ||
22 | #include <gtk/gtk.h> | |
23 | #include "sub_config_parser.h" | |
24 | ||
25 | G_BEGIN_DECLS | |
26 | ||
27 | #define FCITX_TYPE_SUB_CONFIG_WIDGET fcitx_sub_config_widget_get_type() | |
28 | ||
29 | #define FCITX_SUB_CONFIG_WIDGET(obj) \ | |
30 | (G_TYPE_CHECK_INSTANCE_CAST ((obj), FCITX_TYPE_SUB_CONFIG_WIDGET, FcitxSubConfigWidget)) | |
31 | ||
32 | #define FCITX_SUB_CONFIG_WIDGET_CLASS(klass) \ | |
33 | (G_TYPE_CHECK_CLASS_CAST ((klass), FCITX_TYPE_SUB_CONFIG_WIDGET, FcitxSubConfigWidgetClass)) | |
34 | ||
35 | #define FCITX_IS_SUB_CONFIG_WIDGET(obj) \ | |
36 | (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FCITX_TYPE_SUB_CONFIG_WIDGET)) | |
37 | ||
38 | #define FCITX_IS_SUB_CONFIG_WIDGET_CLASS(klass) \ | |
39 | (G_TYPE_CHECK_CLASS_TYPE ((klass), FCITX_TYPE_SUB_CONFIG_WIDGET)) | |
40 | ||
41 | #define FCITX_SUB_CONFIG_WIDGET_GET_CLASS(obj) \ | |
42 | (G_TYPE_INSTANCE_GET_CLASS ((obj), FCITX_TYPE_SUB_CONFIG_WIDGET, FcitxSubConfigWidgetClass)) | |
43 | ||
44 | typedef struct { | |
45 | GtkBox parent; | |
46 | FcitxSubConfig* subconfig; | |
47 | GtkWidget* view; | |
48 | } FcitxSubConfigWidget; | |
49 | ||
50 | typedef struct { | |
51 | GtkBoxClass parent_class; | |
52 | } FcitxSubConfigWidgetClass; | |
53 | ||
54 | GType fcitx_sub_config_widget_get_type(void); | |
55 | ||
56 | FcitxSubConfigWidget* fcitx_sub_config_widget_new(FcitxSubConfig* subconfig); | |
57 | ||
58 | G_END_DECLS | |
59 | ||
60 | #endif /* _FCITX_SUB_CONFIG_WIDGET */ |
0 | 0 | file(GLOB PO_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.po) |
1 | file(RELATIVE_PATH REL_SOURCE_ROOT ${PROJECT_BINARY_DIR} ${PROJECT_SOURCE_DIR}) | |
2 | if ("${REL_SOURCE_ROOT}" STREQUAL "") | |
3 | set(REL_SOURCE_ROOT ".") | |
4 | endif("${REL_SOURCE_ROOT}" STREQUAL "") | |
1 | 5 | |
2 | set(POT_FILE ${CMAKE_CURRENT_SOURCE_DIR}/fcitx-configtool.pot) | |
6 | set(POT_FILE fcitx-configtool.pot) | |
3 | 7 | |
4 | add_custom_command( | |
5 | OUTPUT ${POT_FILE} | |
6 | COMMAND INTLTOOL_EXTRACT=${INTLTOOL_EXTRACT} srcdir=${CMAKE_CURRENT_SOURCE_DIR} ${INTLTOOL_UPDATE} --gettext-package fcitx-configtool --pot | |
7 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} | |
8 | ) | |
8 | configure_file(POTFILES.in.in ${CMAKE_CURRENT_BINARY_DIR}/POTFILES.in) | |
9 | ||
10 | add_custom_target( | |
11 | pot | |
12 | COMMAND INTLTOOL_EXTRACT=${INTLTOOL_EXTRACT} srcdir=${CMAKE_CURRENT_BINARY_DIR} ${INTLTOOL_UPDATE} --gettext-package fcitx-configtool --pot | |
13 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} | |
14 | ) | |
9 | 15 | |
10 | 16 | # Update .po files and compile them to binary .gmo files |
11 | 17 | gettext_create_translations(${POT_FILE} ALL ${PO_FILES}) |
0 | gtk/common.h | |
1 | gtk/configdesc.c | |
2 | gtk/configdesc.h | |
3 | gtk/config_widget.c | |
4 | gtk/config_widget.h | |
5 | gtk/im.c | |
6 | gtk/im.h | |
7 | gtk/im_widget.c | |
8 | gtk/im_widget.h | |
9 | gtk/keygrab.c | |
10 | gtk/keygrab.h | |
11 | gtk/main.c | |
12 | gtk/main_window.c | |
13 | gtk/main_window.h | |
14 | gtk/sub_config_parser.c | |
15 | gtk/sub_config_parser.h | |
16 | gtk/sub_config_widget.c | |
17 | gtk/sub_config_widget.h⏎ |
0 | ${REL_SOURCE_ROOT}/gtk/common.h | |
1 | ${REL_SOURCE_ROOT}/gtk/configdesc.c | |
2 | ${REL_SOURCE_ROOT}/gtk/configdesc.h | |
3 | ${REL_SOURCE_ROOT}/gtk/config_widget.c | |
4 | ${REL_SOURCE_ROOT}/gtk/config_widget.h | |
5 | ${REL_SOURCE_ROOT}/gtk/im.c | |
6 | ${REL_SOURCE_ROOT}/gtk/im.h | |
7 | ${REL_SOURCE_ROOT}/gtk/im_widget.c | |
8 | ${REL_SOURCE_ROOT}/gtk/im_widget.h | |
9 | ${REL_SOURCE_ROOT}/gtk/keygrab.c | |
10 | ${REL_SOURCE_ROOT}/gtk/keygrab.h | |
11 | ${REL_SOURCE_ROOT}/gtk/main.c | |
12 | ${REL_SOURCE_ROOT}/gtk/main_window.c | |
13 | ${REL_SOURCE_ROOT}/gtk/main_window.h | |
14 | ${REL_SOURCE_ROOT}/gtk/sub_config_parser.c | |
15 | ${REL_SOURCE_ROOT}/gtk/sub_config_parser.h | |
16 | ${REL_SOURCE_ROOT}/gtk/sub_config_widget.c | |
17 | ${REL_SOURCE_ROOT}/gtk/sub_config_widget.h | |
18 | ${REL_SOURCE_ROOT}/gtk3/common.h | |
19 | ${REL_SOURCE_ROOT}/gtk3/configdesc.c | |
20 | ${REL_SOURCE_ROOT}/gtk3/configdesc.h | |
21 | ${REL_SOURCE_ROOT}/gtk3/config_widget.c | |
22 | ${REL_SOURCE_ROOT}/gtk3/config_widget.h | |
23 | ${REL_SOURCE_ROOT}/gtk3/im.c | |
24 | ${REL_SOURCE_ROOT}/gtk3/im.h | |
25 | ${REL_SOURCE_ROOT}/gtk3/im_widget.c | |
26 | ${REL_SOURCE_ROOT}/gtk3/im_widget.h | |
27 | ${REL_SOURCE_ROOT}/gtk3/keygrab.c | |
28 | ${REL_SOURCE_ROOT}/gtk3/keygrab.h | |
29 | ${REL_SOURCE_ROOT}/gtk3/main.c | |
30 | ${REL_SOURCE_ROOT}/gtk3/main_window.c | |
31 | ${REL_SOURCE_ROOT}/gtk3/main_window.h | |
32 | ${REL_SOURCE_ROOT}/gtk3/sub_config_parser.c | |
33 | ${REL_SOURCE_ROOT}/gtk3/sub_config_parser.h | |
34 | ${REL_SOURCE_ROOT}/gtk3/sub_config_widget.c | |
35 | ${REL_SOURCE_ROOT}/gtk3/sub_config_widget.h⏎ |
7 | 7 | msgstr "" |
8 | 8 | "Project-Id-Version: PACKAGE VERSION\n" |
9 | 9 | "Report-Msgid-Bugs-To: \n" |
10 | "POT-Creation-Date: 2012-01-30 16:13+0800\n" | |
10 | "POT-Creation-Date: 2012-03-07 02:53+0800\n" | |
11 | 11 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" |
12 | 12 | "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" |
13 | 13 | "Language-Team: LANGUAGE <LL@li.org>\n" |
16 | 16 | "Content-Type: text/plain; charset=CHARSET\n" |
17 | 17 | "Content-Transfer-Encoding: 8bit\n" |
18 | 18 | |
19 | #: /home/saber/Develop/fcitx-config/po/../gtk/config_widget.c:188 | |
19 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk/config_widget.c:188 | |
20 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk3/config_widget.c:188 | |
20 | 21 | msgid "Clear font setting" |
21 | 22 | msgstr "" |
22 | 23 | |
23 | #: /home/saber/Develop/fcitx-config/po/../gtk/config_widget.c:245 | |
24 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk/config_widget.c:253 | |
25 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk3/config_widget.c:253 | |
24 | 26 | msgid "Other" |
25 | 27 | msgstr "" |
26 | 28 | |
27 | #: /home/saber/Develop/fcitx-config/po/../gtk/im_widget.c:75 | |
29 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk/im_widget.c:91 | |
30 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk3/im_widget.c:91 | |
28 | 31 | msgid "Available Input Method" |
29 | 32 | msgstr "" |
30 | 33 | |
31 | #: /home/saber/Develop/fcitx-config/po/../gtk/im_widget.c:82 | |
32 | #: /home/saber/Develop/fcitx-config/po/../gtk/im_widget.c:133 | |
34 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk/im_widget.c:101 | |
35 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk/im_widget.c:152 | |
36 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk/main_window.c:289 | |
37 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk3/im_widget.c:102 | |
38 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk3/im_widget.c:153 | |
39 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk3/main_window.c:289 | |
33 | 40 | msgid "Input Method" |
34 | 41 | msgstr "" |
35 | 42 | |
36 | #: /home/saber/Develop/fcitx-config/po/../gtk/im_widget.c:95 | |
43 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk/im_widget.c:114 | |
44 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk3/im_widget.c:115 | |
37 | 45 | msgid "Only Show Current Language" |
38 | 46 | msgstr "" |
39 | 47 | |
40 | #: /home/saber/Develop/fcitx-config/po/../gtk/im_widget.c:126 | |
48 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk/im_widget.c:145 | |
49 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk3/im_widget.c:146 | |
41 | 50 | msgid "Current Input Method" |
42 | 51 | msgstr "" |
43 | 52 | |
44 | #: /home/saber/Develop/fcitx-config/po/../gtk/im_widget.c:252 | |
53 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk/im_widget.c:293 | |
54 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk/im_widget.c:295 | |
55 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk3/im_widget.c:294 | |
56 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk3/im_widget.c:296 | |
45 | 57 | msgid "Unknown" |
46 | 58 | msgstr "" |
47 | 59 | |
48 | #: /home/saber/Develop/fcitx-config/po/../gtk/im_widget.c:254 | |
49 | #, c-format | |
50 | msgid "%s - %s" | |
51 | msgstr "" | |
52 | ||
53 | #: /home/saber/Develop/fcitx-config/po/../gtk/keygrab.c:81 | |
60 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk/keygrab.c:81 | |
61 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk3/keygrab.c:81 | |
54 | 62 | msgid "Please press the new key combination" |
55 | 63 | msgstr "" |
56 | 64 | |
57 | #: /home/saber/Develop/fcitx-config/po/../gtk/keygrab.c:139 | |
65 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk/keygrab.c:139 | |
66 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk3/keygrab.c:139 | |
58 | 67 | msgid "Disabled" |
59 | 68 | msgstr "" |
60 | 69 | |
61 | #: /home/saber/Develop/fcitx-config/po/../gtk/main_window.c:103 | |
62 | msgid "Config" | |
63 | msgstr "" | |
64 | ||
65 | #: /home/saber/Develop/fcitx-config/po/../gtk/main_window.c:138 | |
70 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk/main_window.c:147 | |
71 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk3/main_window.c:147 | |
66 | 72 | msgid "Fcitx Config" |
67 | 73 | msgstr "" |
68 | 74 | |
69 | #: /home/saber/Develop/fcitx-config/po/../gtk/main_window.c:256 | |
75 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk/main_window.c:283 | |
76 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk3/main_window.c:283 | |
70 | 77 | msgid "Global Config" |
71 | 78 | msgstr "" |
72 | 79 | |
73 | #: /home/saber/Develop/fcitx-config/po/../gtk/main_window.c:262 | |
74 | msgid "Input Method Configuration" | |
75 | msgstr "" | |
76 | ||
77 | #: /home/saber/Develop/fcitx-config/po/../gtk/main_window.c:322 | |
80 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk/main_window.c:349 | |
81 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk3/main_window.c:349 | |
78 | 82 | msgid "Configure" |
79 | 83 | msgstr "" |
80 | 84 | |
81 | #: /home/saber/Develop/fcitx-config/po/../gtk/main_window.c:333 | |
82 | msgid "Addon Configuration" | |
85 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk/main_window.c:360 | |
86 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk3/main_window.c:360 | |
87 | msgid "Addon" | |
83 | 88 | msgstr "" |
89 | ||
90 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk3/im_widget.c:96 | |
91 | msgid "Search Input Method" | |
92 | msgstr "" |
6 | 6 | msgstr "" |
7 | 7 | "Project-Id-Version: fcitx\n" |
8 | 8 | "Report-Msgid-Bugs-To: \n" |
9 | "POT-Creation-Date: 2012-01-30 16:13+0800\n" | |
10 | "PO-Revision-Date: 2012-01-27 00:32+0800\n" | |
11 | "Last-Translator: Weng Xuetian <wengxt@gmail.com>\n" | |
12 | "Language-Team: Chinese Simplified <fcitx-dev@googlegroups.com>\n" | |
9 | "POT-Creation-Date: 2012-03-07 02:53+0800\n" | |
10 | "PO-Revision-Date: 2012-03-07 02:53+0800\n" | |
11 | "Last-Translator: Xuetian Weng <wengxt@gmail.com>\n" | |
12 | "Language-Team: Chinese Simplified <kde-i18n-doc@kde.org>\n" | |
13 | 13 | "Language: zh_CN\n" |
14 | 14 | "MIME-Version: 1.0\n" |
15 | 15 | "Content-Type: text/plain; charset=UTF-8\n" |
17 | 17 | "Plural-Forms: nplurals=1; plural=0\n" |
18 | 18 | "X-Generator: Lokalize 1.4\n" |
19 | 19 | |
20 | #: /home/saber/Develop/fcitx-config/po/../gtk/im_widget.c:254 | |
21 | #, c-format | |
22 | msgid "%s - %s" | |
23 | msgstr "%s - %s" | |
20 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk/main_window.c:360 | |
21 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk3/main_window.c:360 | |
22 | msgid "Addon" | |
23 | msgstr "附加组件" | |
24 | 24 | |
25 | #: /home/saber/Develop/fcitx-config/po/../gtk/main_window.c:333 | |
26 | msgid "Addon Configuration" | |
27 | msgstr "附加组件配置" | |
28 | ||
29 | #: /home/saber/Develop/fcitx-config/po/../gtk/im_widget.c:75 | |
25 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk/im_widget.c:91 | |
26 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk3/im_widget.c:91 | |
30 | 27 | msgid "Available Input Method" |
31 | 28 | msgstr "可用的输入法" |
32 | 29 | |
33 | #: /home/saber/Develop/fcitx-config/po/../gtk/config_widget.c:188 | |
30 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk/config_widget.c:188 | |
31 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk3/config_widget.c:188 | |
34 | 32 | msgid "Clear font setting" |
35 | 33 | msgstr "清除字体设置" |
36 | 34 | |
37 | #: /home/saber/Develop/fcitx-config/po/../gtk/main_window.c:103 | |
38 | msgid "Config" | |
39 | msgstr "配置" | |
40 | ||
41 | #: /home/saber/Develop/fcitx-config/po/../gtk/main_window.c:322 | |
35 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk/main_window.c:349 | |
36 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk3/main_window.c:349 | |
42 | 37 | msgid "Configure" |
43 | 38 | msgstr "配置 " |
44 | 39 | |
45 | #: /home/saber/Develop/fcitx-config/po/../gtk/im_widget.c:126 | |
40 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk/im_widget.c:145 | |
41 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk3/im_widget.c:146 | |
46 | 42 | msgid "Current Input Method" |
47 | 43 | msgstr "当前的输入法" |
48 | 44 | |
49 | #: /home/saber/Develop/fcitx-config/po/../gtk/keygrab.c:139 | |
45 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk/keygrab.c:139 | |
46 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk3/keygrab.c:139 | |
50 | 47 | msgid "Disabled" |
51 | 48 | msgstr "禁用" |
52 | 49 | |
53 | #: /home/saber/Develop/fcitx-config/po/../gtk/main_window.c:138 | |
50 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk/main_window.c:147 | |
51 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk3/main_window.c:147 | |
54 | 52 | msgid "Fcitx Config" |
55 | 53 | msgstr "Fcitx配置" |
56 | 54 | |
57 | #: /home/saber/Develop/fcitx-config/po/../gtk/main_window.c:256 | |
55 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk/main_window.c:283 | |
56 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk3/main_window.c:283 | |
58 | 57 | msgid "Global Config" |
59 | 58 | msgstr "全局配置" |
60 | 59 | |
61 | #: /home/saber/Develop/fcitx-config/po/../gtk/im_widget.c:82 | |
62 | #: /home/saber/Develop/fcitx-config/po/../gtk/im_widget.c:133 | |
60 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk/im_widget.c:101 | |
61 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk/im_widget.c:152 | |
62 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk/main_window.c:289 | |
63 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk3/im_widget.c:102 | |
64 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk3/im_widget.c:153 | |
65 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk3/main_window.c:289 | |
63 | 66 | msgid "Input Method" |
64 | 67 | msgstr "输入法" |
65 | 68 | |
66 | #: /home/saber/Develop/fcitx-config/po/../gtk/main_window.c:262 | |
67 | msgid "Input Method Configuration" | |
68 | msgstr "输入法配置" | |
69 | ||
70 | #: /home/saber/Develop/fcitx-config/po/../gtk/im_widget.c:95 | |
69 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk/im_widget.c:114 | |
70 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk3/im_widget.c:115 | |
71 | 71 | msgid "Only Show Current Language" |
72 | 72 | msgstr "仅显示当前语言" |
73 | 73 | |
74 | #: /home/saber/Develop/fcitx-config/po/../gtk/config_widget.c:245 | |
74 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk/config_widget.c:253 | |
75 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk3/config_widget.c:253 | |
75 | 76 | msgid "Other" |
76 | 77 | msgstr "其他" |
77 | 78 | |
78 | #: /home/saber/Develop/fcitx-config/po/../gtk/keygrab.c:81 | |
79 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk/keygrab.c:81 | |
80 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk3/keygrab.c:81 | |
79 | 81 | msgid "Please press the new key combination" |
80 | 82 | msgstr "请按下新按键组合" |
81 | 83 | |
82 | #: /home/saber/Develop/fcitx-config/po/../gtk/im_widget.c:252 | |
84 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk3/im_widget.c:96 | |
85 | msgid "Search Input Method" | |
86 | msgstr "搜索输入法" | |
87 | ||
88 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk/im_widget.c:293 | |
89 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk/im_widget.c:295 | |
90 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk3/im_widget.c:294 | |
91 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk3/im_widget.c:296 | |
83 | 92 | msgid "Unknown" |
84 | 93 | msgstr "未知" |
94 | ||
95 | #~ msgid "Addon Configuration" | |
96 | #~ msgstr "附加组件配置" | |
97 | ||
98 | #~ msgid "Config" | |
99 | #~ msgstr "配置" | |
100 | ||
101 | #~ msgid "Input Method Configuration" | |
102 | #~ msgstr "输入法配置" | |
103 | ||
104 | #~ msgid "Multilingual" | |
105 | #~ msgstr "多语言" |
0 | # SOME DESCRIPTIVE TITLE. | |
1 | 0 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER |
2 | 1 | # This file is distributed under the same license as the PACKAGE package. |
3 | 2 | # |
4 | 3 | # Translators: |
4 | # Weng Xuetian <wengxt@gmail.com>, 2012. | |
5 | 5 | msgid "" |
6 | 6 | msgstr "" |
7 | 7 | "Project-Id-Version: fcitx\n" |
8 | 8 | "Report-Msgid-Bugs-To: \n" |
9 | "POT-Creation-Date: 2012-01-30 16:13+0800\n" | |
10 | "PO-Revision-Date: 2011-11-16 06:53+0000\n" | |
11 | "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" | |
12 | "Language-Team: Chinese (Taiwan) (http://www.transifex.net/projects/p/fcitx/" | |
13 | "team/zh_TW/)\n" | |
9 | "POT-Creation-Date: 2012-03-07 02:53+0800\n" | |
10 | "PO-Revision-Date: 2012-03-07 02:55+0800\n" | |
11 | "Last-Translator: Xuetian Weng <wengxt@gmail.com>\n" | |
12 | "Language-Team: Chinese Simplified <kde-i18n-doc@kde.org>\n" | |
14 | 13 | "Language: zh_TW\n" |
15 | 14 | "MIME-Version: 1.0\n" |
16 | 15 | "Content-Type: text/plain; charset=UTF-8\n" |
17 | 16 | "Content-Transfer-Encoding: 8bit\n" |
17 | "team/zh_TW/)\n" | |
18 | 18 | "Plural-Forms: nplurals=1; plural=0\n" |
19 | "X-Generator: Lokalize 1.4\n" | |
19 | 20 | |
20 | #: /home/saber/Develop/fcitx-config/po/../gtk/im_widget.c:254 | |
21 | #, c-format | |
22 | msgid "%s - %s" | |
23 | msgstr "" | |
21 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk/main_window.c:360 | |
22 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk3/main_window.c:360 | |
23 | msgid "Addon" | |
24 | msgstr "附加元件" | |
24 | 25 | |
25 | #: /home/saber/Develop/fcitx-config/po/../gtk/main_window.c:333 | |
26 | msgid "Addon Configuration" | |
27 | msgstr "" | |
26 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk/im_widget.c:91 | |
27 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk3/im_widget.c:91 | |
28 | msgid "Available Input Method" | |
29 | msgstr "可用輸入法" | |
28 | 30 | |
29 | #: /home/saber/Develop/fcitx-config/po/../gtk/im_widget.c:75 | |
30 | msgid "Available Input Method" | |
31 | msgstr "" | |
31 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk/config_widget.c:188 | |
32 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk3/config_widget.c:188 | |
33 | msgid "Clear font setting" | |
34 | msgstr "清除字體設定" | |
32 | 35 | |
33 | #: /home/saber/Develop/fcitx-config/po/../gtk/config_widget.c:188 | |
34 | msgid "Clear font setting" | |
35 | msgstr "" | |
36 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk/main_window.c:349 | |
37 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk3/main_window.c:349 | |
38 | msgid "Configure" | |
39 | msgstr "設定" | |
36 | 40 | |
37 | #: /home/saber/Develop/fcitx-config/po/../gtk/main_window.c:103 | |
38 | msgid "Config" | |
39 | msgstr "" | |
41 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk/im_widget.c:145 | |
42 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk3/im_widget.c:146 | |
43 | msgid "Current Input Method" | |
44 | msgstr "目前輸入法" | |
40 | 45 | |
41 | #: /home/saber/Develop/fcitx-config/po/../gtk/main_window.c:322 | |
42 | msgid "Configure" | |
43 | msgstr "" | |
46 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk/keygrab.c:139 | |
47 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk3/keygrab.c:139 | |
48 | msgid "Disabled" | |
49 | msgstr "禁用" | |
44 | 50 | |
45 | #: /home/saber/Develop/fcitx-config/po/../gtk/im_widget.c:126 | |
46 | msgid "Current Input Method" | |
47 | msgstr "" | |
51 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk/main_window.c:147 | |
52 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk3/main_window.c:147 | |
53 | msgid "Fcitx Config" | |
54 | msgstr "Fcitx 設定" | |
48 | 55 | |
49 | #: /home/saber/Develop/fcitx-config/po/../gtk/keygrab.c:139 | |
50 | msgid "Disabled" | |
51 | msgstr "" | |
56 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk/main_window.c:283 | |
57 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk3/main_window.c:283 | |
58 | msgid "Global Config" | |
59 | msgstr "全局設定" | |
52 | 60 | |
53 | #: /home/saber/Develop/fcitx-config/po/../gtk/main_window.c:138 | |
54 | msgid "Fcitx Config" | |
55 | msgstr "" | |
61 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk/im_widget.c:101 | |
62 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk/im_widget.c:152 | |
63 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk/main_window.c:289 | |
64 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk3/im_widget.c:102 | |
65 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk3/im_widget.c:153 | |
66 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk3/main_window.c:289 | |
67 | msgid "Input Method" | |
68 | msgstr "輸入法" | |
56 | 69 | |
57 | #: /home/saber/Develop/fcitx-config/po/../gtk/main_window.c:256 | |
58 | msgid "Global Config" | |
59 | msgstr "" | |
70 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk/im_widget.c:114 | |
71 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk3/im_widget.c:115 | |
72 | msgid "Only Show Current Language" | |
73 | msgstr "僅顯示当前語言" | |
60 | 74 | |
61 | #: /home/saber/Develop/fcitx-config/po/../gtk/im_widget.c:82 | |
62 | #: /home/saber/Develop/fcitx-config/po/../gtk/im_widget.c:133 | |
63 | msgid "Input Method" | |
64 | msgstr "" | |
75 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk/config_widget.c:253 | |
76 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk3/config_widget.c:253 | |
77 | msgid "Other" | |
78 | msgstr "其他" | |
65 | 79 | |
66 | #: /home/saber/Develop/fcitx-config/po/../gtk/main_window.c:262 | |
67 | msgid "Input Method Configuration" | |
68 | msgstr "" | |
80 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk/keygrab.c:81 | |
81 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk3/keygrab.c:81 | |
82 | msgid "Please press the new key combination" | |
83 | msgstr "請按下新按鍵組合" | |
69 | 84 | |
70 | #: /home/saber/Develop/fcitx-config/po/../gtk/im_widget.c:95 | |
71 | msgid "Only Show Current Language" | |
72 | msgstr "" | |
85 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk3/im_widget.c:96 | |
86 | msgid "Search Input Method" | |
87 | msgstr "搜尋輸入法" | |
73 | 88 | |
74 | #: /home/saber/Develop/fcitx-config/po/../gtk/config_widget.c:245 | |
75 | msgid "Other" | |
76 | msgstr "" | |
89 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk/im_widget.c:293 | |
90 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk/im_widget.c:295 | |
91 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk3/im_widget.c:294 | |
92 | #: /home/saber/Develop/fcitx-config/build/po/../..//gtk3/im_widget.c:296 | |
93 | msgid "Unknown" | |
94 | msgstr "未知" | |
77 | 95 | |
78 | #: /home/saber/Develop/fcitx-config/po/../gtk/keygrab.c:81 | |
79 | msgid "Please press the new key combination" | |
80 | msgstr "" | |
81 | ||
82 | #: /home/saber/Develop/fcitx-config/po/../gtk/im_widget.c:252 | |
83 | msgid "Unknown" | |
84 | msgstr "" | |
96 | #~ msgid "Multilingual" | |
97 | #~ msgstr "多語言" |