Adding upstream version 0.9.0.
[debian/lxpanel.git] / plugins / xkb / xkb-plugin.c
1 /*
2 * Copyright (C) 2008 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
3 * 2009-2010 Marty Jack <martyj19@comcast.net>
4 * 2012-2013 Giuseppe Penone <giuspen@gmail.com>
5 * 2013 Henry Gebhardt <hsggebhardt@gmail.com>
6 * 2014-2016 Andriy Grytsenko <andrej@rep.kiev.ua>
7 *
8 * This file is a part of LXPanel project.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 */
24
25 /* Originally derived from xfce4-xkb-plugin, Copyright 2004 Alexander Iliev,
26 * which credits Michael Glickman. */
27
28 /* Modified by Giuseppe Penone <giuspen@gmail.com> starting from 2012-07 and lxpanel 0.5.10 */
29
30 #ifdef HAVE_CONFIG_H
31 #include <config.h>
32 #endif
33
34 #include <glib/gi18n.h>
35
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <ctype.h>
39
40 #include "xkb.h"
41 #include "gtk-compat.h"
42
43 enum
44 {
45 COLUMN_ICON,
46 COLUMN_LAYOUT,
47 COLUMN_VARIANT,
48 NUM_COLUMNS
49 };
50
51 enum
52 {
53 COLUMN_ADD_ICON,
54 COLUMN_ADD_LAYOUT,
55 COLUMN_ADD_DESC,
56 NUM_ADD_COLUMNS
57 };
58
59 enum
60 {
61 COLUMN_MODEL_ID,
62 COLUMN_MODEL_DESC,
63 NUM_MODEL_COLUMNS
64 };
65
66 enum
67 {
68 COLUMN_CHANGE_ID,
69 COLUMN_CHANGE_DESC,
70 COLUMN_CHANGE_INCL,
71 COLUMN_CHANGE_WEIGHT,
72 NUM_CHANGE_COLUMNS
73 };
74
75 static void xkb_destructor(gpointer user_data);
76 static void xkb_settings_fill_layout_tree_model_with_config(XkbPlugin *p_xkb);
77 static void xkb_update_layouts_n_variants(XkbPlugin *p_xkb);
78 static void xkb_add_layout(XkbPlugin *p_xkb, gchar *layout, gchar*variant);
79 static int xkb_get_flag_size(XkbPlugin *p_xkb);
80
81 static void on_xkb_fbev_active_window_event(FbEv *ev, gpointer p_data);
82 static gboolean on_xkb_button_scroll_event(GtkWidget * widget, GdkEventScroll * event, gpointer p_data);
83 static void on_radiobutton_disp_type_image_toggled(GtkToggleButton *p_radiobutton, gpointer p_data);
84 static void on_radiobutton_disp_type_image_cust_toggled(GtkToggleButton *p_radiobutton, gpointer p_data);
85 static void on_radiobutton_disp_type_text_toggled(GtkToggleButton *p_radiobutton, gpointer p_data);
86 static void on_xkb_checkbutton_per_app_toggled(GtkToggleButton *tb, gpointer p_data);
87 static void on_xkb_checkbutton_no_reset_opt_toggled(GtkToggleButton *tb, gpointer p_data);
88 static void on_xkb_checkbutton_keep_system_layouts_toggled(GtkToggleButton *tb, gpointer p_data);
89 static void on_dialog_config_response(GtkDialog *p_dialog, gint response, gpointer p_data);
90 static void on_xkb_entry_advanced_opt_icon_press(GtkEntry *p_entry,
91 GtkEntryIconPosition icon_pos,
92 GdkEvent *p_event,
93 gpointer p_data);
94
95 static unsigned char user_active = FALSE;
96 static const gchar flag_filepath_generator[] = "%s/%s.png";
97
98 static int xkb_get_flag_size(XkbPlugin *p_xkb)
99 {
100 int size = panel_get_icon_size(p_xkb->panel);
101 switch(p_xkb->flag_size)
102 {
103 case 1: return size*0.5;
104 case 2: return size*0.6;
105 case 3: return size*0.7;
106 case 4: return size*0.8;
107 case 5: return size*0.9;
108 default: break;
109 }
110 return size;
111 }
112
113 /* Redraw the graphics. */
114 void xkb_redraw(XkbPlugin *p_xkb)
115 {
116 /* Set the image. */
117 gboolean valid_image = FALSE;
118 int size = xkb_get_flag_size(p_xkb);
119 if( (p_xkb->display_type == DISP_TYPE_IMAGE) || (p_xkb->display_type == DISP_TYPE_IMAGE_CUST) )
120 {
121 char * group_name = (char *)xkb_get_current_symbol_name_lowercase(p_xkb);
122 if(group_name != NULL)
123 {
124 gchar *flag_filepath = NULL;
125 gchar *flags_dir = (p_xkb->cust_dir_exists && (p_xkb->display_type == DISP_TYPE_IMAGE_CUST)) ? g_strdup(FLAGSCUSTDIR):g_strdup(FLAGSDIR);
126 if(strchr(group_name, '/') != NULL)
127 {
128 gchar *layout_mod = g_strdup(group_name);
129 layout_mod = g_strdelimit(layout_mod, "/", '-');
130 flag_filepath = g_strdup_printf(flag_filepath_generator, flags_dir, layout_mod);
131 g_free(layout_mod);
132 }
133 else
134 {
135 flag_filepath = g_strdup_printf(flag_filepath_generator, flags_dir, group_name);
136 }
137 GdkPixbuf * unscaled_pixbuf = gdk_pixbuf_new_from_file(flag_filepath, NULL);
138 g_free(flag_filepath);
139 g_free(flags_dir);
140 g_free(group_name);
141
142 if(unscaled_pixbuf != NULL)
143 {
144 /* Loaded successfully. */
145 int width = gdk_pixbuf_get_width(unscaled_pixbuf);
146 int height = gdk_pixbuf_get_height(unscaled_pixbuf);
147 GdkPixbuf * pixbuf = gdk_pixbuf_scale_simple(unscaled_pixbuf, size * width / height, size, GDK_INTERP_BILINEAR);
148 if(pixbuf != NULL)
149 {
150 gtk_image_set_from_pixbuf(GTK_IMAGE(p_xkb->p_image), pixbuf);
151 g_object_unref(G_OBJECT(pixbuf));
152 gtk_widget_hide(p_xkb->p_label);
153 gtk_widget_show(p_xkb->p_image);
154 gtk_widget_set_tooltip_text(p_xkb->p_plugin, xkb_get_current_group_name(p_xkb));
155 valid_image = TRUE;
156 }
157 g_object_unref(unscaled_pixbuf);
158 }
159 }
160 }
161
162 /* Set the label. */
163 if( (p_xkb->display_type == DISP_TYPE_TEXT) || ( ! valid_image) )
164 {
165 char *group_name = (char *)xkb_get_current_symbol_name(p_xkb);
166 if (group_name != NULL)
167 {
168 lxpanel_draw_label_text(p_xkb->panel, p_xkb->p_label, group_name,
169 TRUE, size*4/(5*10.0), TRUE);
170 gtk_widget_hide(p_xkb->p_image);
171 gtk_widget_show(p_xkb->p_label);
172 gtk_widget_set_tooltip_text(p_xkb->p_plugin, xkb_get_current_group_name(p_xkb));
173 }
174 }
175 }
176
177 /* Handler for "active_window" event on root window listener. */
178 static void on_xkb_fbev_active_window_event(FbEv * ev, gpointer p_data)
179 {
180 XkbPlugin * xkb = (XkbPlugin *)p_data;
181 if (xkb->enable_perwin)
182 {
183 Window * win = fb_ev_active_window(ev);
184 if (*win != None)
185 {
186 xkb_active_window_changed(xkb, *win);
187 xkb_redraw(xkb);
188 }
189 }
190 }
191
192 /* Handler for "scroll-event" on drawing area. */
193 static gboolean on_xkb_button_scroll_event(GtkWidget * widget, GdkEventScroll * event, gpointer p_data)
194 {
195 XkbPlugin * xkb = (XkbPlugin *)p_data;
196
197 /* Change to next or previous group. */
198 xkb_change_group(xkb,
199 (((event->direction == GDK_SCROLL_UP) || (event->direction == GDK_SCROLL_RIGHT)) ? 1 : -1));
200 return TRUE;
201 }
202
203 /* Handler for button-press-event on top level widget. */
204 static gboolean on_xkb_button_press_event(GtkWidget * widget, GdkEventButton * event, LXPanel * panel)
205 {
206 /* Change to next group. */
207 if (event->button != 1)
208 return FALSE;
209 xkb_change_group(lxpanel_plugin_get_data(widget), 1);
210 return TRUE;
211 }
212
213 static void on_xkb_entry_advanced_opt_icon_press(GtkEntry *p_entry,
214 GtkEntryIconPosition icon_pos,
215 GdkEvent *p_event,
216 gpointer p_data)
217 {
218 XkbPlugin *p_xkb = (XkbPlugin *)p_data;
219 g_free(p_xkb->kbd_advanced_options);
220 p_xkb->kbd_advanced_options = g_strdup(gtk_entry_get_text(p_entry));
221 config_group_set_string(p_xkb->settings, "AdvancedOpt", p_xkb->kbd_advanced_options);
222 xkb_setxkbmap(p_xkb);
223 }
224
225 /* Plugin constructor. */
226 static GtkWidget *xkb_constructor(LXPanel *panel, config_setting_t *settings)
227 {
228 /* Allocate plugin context and set into Plugin private data pointer. */
229 XkbPlugin * p_xkb = g_new0(XkbPlugin, 1);
230 GtkWidget * p;
231 const char *tmp;
232 int tmp_int;
233
234 p_xkb->panel = panel;
235 p_xkb->settings = settings;
236
237 /* Initialize to defaults. */
238 p_xkb->display_type = DISP_TYPE_IMAGE;
239 //p_xkb->enable_perwin = FALSE;
240 //p_xkb->do_not_reset_opt = FALSE;
241 p_xkb->keep_system_layouts = TRUE;
242 //p_xkb->kbd_model = NULL;
243 //p_xkb->kbd_layouts = NULL;
244 //p_xkb->kbd_variants = NULL;
245 //p_xkb->kbd_change_option = NULL;
246 //p_xkb->kbd_advanced_options = NULL;
247 p_xkb->flag_size = 3;
248 p_xkb->cust_dir_exists = g_file_test(FLAGSCUSTDIR, G_FILE_TEST_IS_DIR);
249
250 /* Load parameters from the configuration file. */
251 config_setting_lookup_int(settings, "DisplayType", &p_xkb->display_type);
252 if (config_setting_lookup_int(settings, "PerWinLayout", &tmp_int))
253 p_xkb->enable_perwin = tmp_int != 0;
254 if (config_setting_lookup_int(settings, "NoResetOpt", &tmp_int))
255 p_xkb->do_not_reset_opt = tmp_int != 0;
256 if (config_setting_lookup_int(settings, "KeepSysLayouts", &tmp_int))
257 p_xkb->keep_system_layouts = tmp_int != 0;
258 if (config_setting_lookup_string(settings, "Model", &tmp))
259 p_xkb->kbd_model = g_strdup(tmp);
260 if (config_setting_lookup_string(settings, "LayoutsList", &tmp))
261 p_xkb->kbd_layouts = g_strdup(tmp);
262 if (config_setting_lookup_string(settings, "VariantsList", &tmp))
263 p_xkb->kbd_variants = g_strdup(tmp);
264 if (config_setting_lookup_string(settings, "ToggleOpt", &tmp))
265 p_xkb->kbd_change_option = g_strdup(tmp);
266 if (config_setting_lookup_string(settings, "AdvancedOpt", &tmp))
267 p_xkb->kbd_advanced_options = g_strdup(tmp);
268 config_setting_lookup_int(settings, "FlagSize", &p_xkb->flag_size);
269
270 /* Allocate top level widget and set into Plugin widget pointer. */
271 p_xkb->p_plugin = p = gtk_event_box_new();
272 lxpanel_plugin_set_data(p, p_xkb, xkb_destructor);
273
274 /* Create a horizontal box as the child of the button. */
275 GtkWidget * hbox = gtk_hbox_new(FALSE, 0);
276 gtk_container_set_border_width(GTK_CONTAINER(hbox), 3);
277 gtk_container_add(GTK_CONTAINER(p), hbox);
278 gtk_widget_show(hbox);
279
280 /* Create a label and an image as children of the horizontal box.
281 * Only one of these is visible at a time, controlled by user preference
282 * and the successful loading of the image. */
283 p_xkb->p_label = gtk_label_new("");
284 gtk_container_add(GTK_CONTAINER(hbox), p_xkb->p_label);
285 p_xkb->p_image = gtk_image_new();
286 gtk_container_add(GTK_CONTAINER(hbox), p_xkb->p_image);
287
288 /* Check for first run */
289 if( (p_xkb->kbd_model == NULL) || (p_xkb->kbd_layouts == NULL) ||
290 (p_xkb->kbd_variants == NULL) || (p_xkb->kbd_change_option == NULL) )
291 {
292 /* This is a first run, read the current layout */
293 xkb_mechanism_constructor(p_xkb);
294
295 if(p_xkb->kbd_model != NULL) g_free(p_xkb->kbd_model);
296 if(p_xkb->kbd_layouts != NULL) g_free(p_xkb->kbd_layouts);
297 if(p_xkb->kbd_variants != NULL) g_free(p_xkb->kbd_variants);
298 if(p_xkb->kbd_change_option != NULL) g_free(p_xkb->kbd_change_option);
299
300 p_xkb->kbd_model = g_strdup("pc105");
301 gchar *symbol_name_lowercase = (char *)xkb_get_current_symbol_name_lowercase(p_xkb);
302 p_xkb->kbd_layouts = g_strdup(symbol_name_lowercase);
303 g_free(symbol_name_lowercase);
304 p_xkb->kbd_variants = g_strdup(",");
305 p_xkb->kbd_change_option = g_strdup("grp:shift_caps_toggle");
306 config_group_set_string(p_xkb->settings, "Model", p_xkb->kbd_model);
307 config_group_set_string(p_xkb->settings, "LayoutsList", p_xkb->kbd_layouts);
308 config_group_set_string(p_xkb->settings, "VariantsList", p_xkb->kbd_variants);
309 config_group_set_string(p_xkb->settings, "ToggleOpt", p_xkb->kbd_change_option);
310
311 xkb_mechanism_destructor(p_xkb);
312 }
313
314 /* Initialize the XKB interface. */
315 xkb_setxkbmap(p_xkb);
316 xkb_mechanism_constructor(p_xkb);
317
318 /* Connect signals. */
319 g_signal_connect(p, "scroll-event", G_CALLBACK(on_xkb_button_scroll_event), p_xkb);
320 g_signal_connect(G_OBJECT(fbev), "active-window", G_CALLBACK(on_xkb_fbev_active_window_event), p_xkb);
321
322 /* Show the widget and return. */
323 xkb_redraw(p_xkb);
324 return p;
325 }
326
327 /* Plugin destructor. */
328 static void xkb_destructor(gpointer user_data)
329 {
330 XkbPlugin *p_xkb = (XkbPlugin *)user_data;
331
332 /* Disconnect root window event handler. */
333 g_signal_handlers_disconnect_by_func(G_OBJECT(fbev), on_xkb_fbev_active_window_event, p_xkb);
334
335 /* Disconnect from the XKB mechanism. */
336 xkb_mechanism_destructor(p_xkb);
337
338 /* Deallocate all memory. */
339 g_free(p_xkb->kbd_model);
340 g_free(p_xkb->kbd_layouts);
341 g_free(p_xkb->kbd_variants);
342 g_free(p_xkb->kbd_change_option);
343 g_free(p_xkb->kbd_advanced_options);
344 g_free(p_xkb);
345 }
346
347 /* Handler for "toggled" event on per-application check box of configuration dialog. */
348 static void on_xkb_checkbutton_per_app_toggled(GtkToggleButton *tb, gpointer p_data)
349 {
350 if(user_active == TRUE)
351 {
352 /* Fetch the new value and redraw. */
353 XkbPlugin * xkb = (XkbPlugin *)p_data;
354 xkb->enable_perwin = gtk_toggle_button_get_active(tb);
355 if(!xkb->enable_perwin)
356 {
357 /* at deactivation clear the hash table */
358 if(xkb->p_hash_table_group != NULL)
359 g_hash_table_destroy(xkb->p_hash_table_group);
360 xkb->p_hash_table_group = g_hash_table_new(g_direct_hash, NULL);
361 }
362 config_group_set_int(xkb->settings, "PerWinLayout", xkb->enable_perwin);
363 xkb_redraw(xkb);
364 }
365 }
366
367 /* Handler for "toggled" event on no reset options check box of configuration dialog. */
368 static void on_xkb_checkbutton_no_reset_opt_toggled(GtkToggleButton *tb, gpointer p_data)
369 {
370 if(user_active == TRUE)
371 {
372 /* Fetch the new value and redraw. */
373 XkbPlugin * xkb = (XkbPlugin *)p_data;
374 xkb->do_not_reset_opt = gtk_toggle_button_get_active(tb);
375 config_group_set_int(xkb->settings, "NoResetOpt", xkb->do_not_reset_opt);
376 xkb_redraw(xkb);
377 }
378 }
379
380 /* Handler for "toggled" event on keep system layouts check box of configuration dialog. */
381 static void on_xkb_checkbutton_keep_system_layouts_toggled(GtkToggleButton *tb, gpointer p_data)
382 {
383 if(user_active == TRUE)
384 {
385 /* Fetch the new value and redraw. */
386 XkbPlugin * p_xkb = (XkbPlugin *)p_data;
387 p_xkb->keep_system_layouts = gtk_toggle_button_get_active(tb);
388 config_group_set_int(p_xkb->settings, "KeepSysLayouts", p_xkb->keep_system_layouts);
389 xkb_redraw(p_xkb);
390
391 gtk_widget_set_sensitive(p_xkb->p_frame_kbd_model, !p_xkb->keep_system_layouts);
392 gtk_widget_set_sensitive(p_xkb->p_frame_kbd_layouts, !p_xkb->keep_system_layouts);
393 gtk_widget_set_sensitive(p_xkb->p_frame_change_layout, !p_xkb->keep_system_layouts);
394 gtk_widget_set_sensitive(p_xkb->p_entry_advanced_opt, !p_xkb->keep_system_layouts);
395 gtk_widget_set_sensitive(p_xkb->p_checkbutton_no_reset_opt, !p_xkb->keep_system_layouts);
396
397 if(!p_xkb->keep_system_layouts)
398 {
399 gtk_entry_set_icon_from_stock(GTK_ENTRY(p_xkb->p_entry_advanced_opt), GTK_ENTRY_ICON_SECONDARY, "gtk-save");
400 xkb_update_layouts_n_variants(p_xkb);
401 }
402 else
403 {
404 GtkWidget *dialog;
405 dialog = gtk_message_dialog_new(p_xkb->p_dialog_config,
406 GTK_DIALOG_DESTROY_WITH_PARENT,
407 GTK_MESSAGE_WARNING,
408 GTK_BUTTONS_OK,
409 _("New session is required for this option to take effect"));
410 gtk_window_set_title(GTK_WINDOW(dialog), "Warning");
411 gtk_dialog_run(GTK_DIALOG(dialog));
412 gtk_widget_destroy(dialog);
413 }
414 }
415 }
416
417 static void on_radiobutton_flag_size_1_toggled(GtkToggleButton *p_radiobutton, gpointer p_data)
418 {
419 if( (user_active == TRUE) && gtk_toggle_button_get_active(p_radiobutton) )
420 {
421 XkbPlugin * p_xkb = (XkbPlugin *)p_data;
422 p_xkb->flag_size = 1;
423 config_group_set_int(p_xkb->settings, "FlagSize", 1);
424 xkb_redraw(p_xkb);
425 }
426 }
427
428 static void on_radiobutton_flag_size_2_toggled(GtkToggleButton *p_radiobutton, gpointer p_data)
429 {
430 if( (user_active == TRUE) && gtk_toggle_button_get_active(p_radiobutton) )
431 {
432 XkbPlugin * p_xkb = (XkbPlugin *)p_data;
433 p_xkb->flag_size = 2;
434 config_group_set_int(p_xkb->settings, "FlagSize", 2);
435 xkb_redraw(p_xkb);
436 }
437 }
438
439 static void on_radiobutton_flag_size_3_toggled(GtkToggleButton *p_radiobutton, gpointer p_data)
440 {
441 if( (user_active == TRUE) && gtk_toggle_button_get_active(p_radiobutton) )
442 {
443 XkbPlugin * p_xkb = (XkbPlugin *)p_data;
444 p_xkb->flag_size = 3;
445 config_group_set_int(p_xkb->settings, "FlagSize", 3);
446 xkb_redraw(p_xkb);
447 }
448 }
449
450 static void on_radiobutton_flag_size_4_toggled(GtkToggleButton *p_radiobutton, gpointer p_data)
451 {
452 if( (user_active == TRUE) && gtk_toggle_button_get_active(p_radiobutton) )
453 {
454 XkbPlugin * p_xkb = (XkbPlugin *)p_data;
455 p_xkb->flag_size = 4;
456 config_group_set_int(p_xkb->settings, "FlagSize", 4);
457 xkb_redraw(p_xkb);
458 }
459 }
460
461 static void on_radiobutton_flag_size_5_toggled(GtkToggleButton *p_radiobutton, gpointer p_data)
462 {
463 if( (user_active == TRUE) && gtk_toggle_button_get_active(p_radiobutton) )
464 {
465 XkbPlugin * p_xkb = (XkbPlugin *)p_data;
466 p_xkb->flag_size = 5;
467 config_group_set_int(p_xkb->settings, "FlagSize", 5);
468 xkb_redraw(p_xkb);
469 }
470 }
471
472 static void on_radiobutton_flag_size_6_toggled(GtkToggleButton *p_radiobutton, gpointer p_data)
473 {
474 if( (user_active == TRUE) && gtk_toggle_button_get_active(p_radiobutton) )
475 {
476 XkbPlugin * p_xkb = (XkbPlugin *)p_data;
477 p_xkb->flag_size = 6;
478 config_group_set_int(p_xkb->settings, "FlagSize", 6);
479 xkb_redraw(p_xkb);
480 }
481 }
482
483 /* Handler for "toggled" event on display type image radiobutton of configuration dialog. */
484 static void on_radiobutton_disp_type_image_toggled(GtkToggleButton *p_radiobutton, gpointer p_data)
485 {
486 if( (user_active == TRUE) && gtk_toggle_button_get_active(p_radiobutton) )
487 {
488 /* Fetch the new value and redraw. */
489 XkbPlugin * p_xkb = (XkbPlugin *)p_data;
490 p_xkb->display_type = DISP_TYPE_IMAGE;
491 config_group_set_int(p_xkb->settings, "DisplayType", p_xkb->display_type);
492 xkb_redraw(p_xkb);
493 }
494 }
495
496 /* Handler for "toggled" event on display type image radiobutton of configuration dialog. */
497 static void on_radiobutton_disp_type_image_cust_toggled(GtkToggleButton *p_radiobutton, gpointer p_data)
498 {
499 if( (user_active == TRUE) && gtk_toggle_button_get_active(p_radiobutton) )
500 {
501 /* Fetch the new value and redraw. */
502 XkbPlugin * p_xkb = (XkbPlugin *)p_data;
503 p_xkb->display_type = DISP_TYPE_IMAGE_CUST;
504 config_group_set_int(p_xkb->settings, "DisplayType", p_xkb->display_type);
505 xkb_redraw(p_xkb);
506 }
507 }
508
509 /* Handler for "toggled" event on display type image radiobutton of configuration dialog. */
510 static void on_radiobutton_disp_type_text_toggled(GtkToggleButton *p_radiobutton, gpointer p_data)
511 {
512 if( (user_active == TRUE) && gtk_toggle_button_get_active(p_radiobutton) )
513 {
514 /* Fetch the new value and redraw. */
515 XkbPlugin * p_xkb = (XkbPlugin *)p_data;
516 p_xkb->display_type = DISP_TYPE_TEXT;
517 config_group_set_int(p_xkb->settings, "DisplayType", p_xkb->display_type);
518 xkb_redraw(p_xkb);
519 }
520 }
521
522 /* Handler for "response" event on configuration dialog. */
523 static void on_dialog_config_response(GtkDialog *p_dialog, gint response, gpointer data)
524 {
525 XkbPlugin * p_xkb = (XkbPlugin *)data;
526
527 /* Reset pointer because dialog will be destroyed */
528 p_xkb->p_dialog_config = NULL;
529 }
530
531 static void on_cell_renderer_layout_change_incl_toggled(GtkCellRendererToggle *cell,
532 gchar *path_str,
533 gpointer data)
534 {
535 GtkTreeModel *p_model = (GtkTreeModel *)data;
536 GtkTreeIter tree_iter;
537 GtkTreePath *p_tree_path = gtk_tree_path_new_from_string(path_str);
538 gboolean included;
539
540 /* get toggled iter */
541 gtk_tree_model_get_iter(p_model, &tree_iter, p_tree_path);
542 gtk_tree_model_get(p_model, &tree_iter, COLUMN_CHANGE_INCL, &included, -1);
543
544 /* do something with the value */
545 included = !included;
546
547 /* set new value */
548 gtk_list_store_set(GTK_LIST_STORE(p_model), &tree_iter, COLUMN_CHANGE_INCL, included, -1);
549 gtk_list_store_set(GTK_LIST_STORE(p_model), &tree_iter, COLUMN_CHANGE_WEIGHT, included ? PANGO_WEIGHT_ULTRAHEAVY : PANGO_WEIGHT_NORMAL, -1);
550
551 /* clean up */
552 gtk_tree_path_free(p_tree_path);
553 }
554
555 static gboolean on_treeviews_lists_button_press_event(GtkWidget *p_widget,
556 GdkEventButton *p_event,
557 gpointer p_data)
558 {
559 if(p_event->button == 1)
560 {
561 if(p_event->type == GDK_2BUTTON_PRESS)
562 {
563 GtkButton *p_button_ok = (GtkButton *)p_data;
564 gtk_button_clicked(p_button_ok);
565 }
566 }
567 else if(p_event->button == 2)
568 {
569 GtkTreePath *p_tree_path;
570 if(gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(p_widget),
571 p_event->x, p_event->y,
572 &p_tree_path,
573 NULL, NULL, NULL))
574 {
575 if(gtk_tree_view_row_expanded(GTK_TREE_VIEW(p_widget), p_tree_path))
576 gtk_tree_view_collapse_row(GTK_TREE_VIEW(p_widget), p_tree_path);
577 else
578 gtk_tree_view_expand_row(GTK_TREE_VIEW(p_widget), p_tree_path, FALSE);
579 gtk_tree_path_free(p_tree_path);
580 }
581 }
582 return FALSE;
583 }
584
585 static void on_button_kbd_model_clicked(GtkButton *p_button, gpointer *p_data)
586 {
587 XkbPlugin *p_xkb = (XkbPlugin *)p_data;
588
589 // dialog
590 GtkWidget *p_dialog = gtk_dialog_new_with_buttons(_("Select Keyboard Model"),
591 p_xkb->p_dialog_config,
592 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
593 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
594 GTK_STOCK_OK, GTK_RESPONSE_OK,
595 NULL);
596
597 // scrolledwindow
598 GtkWidget * p_scrolledwindow_kbd_model = gtk_scrolled_window_new(NULL, NULL);
599 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(p_scrolledwindow_kbd_model),
600 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
601 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(p_dialog))),
602 p_scrolledwindow_kbd_model, TRUE, TRUE, 2);
603
604 // liststore
605 GtkListStore *p_liststore_kbd_model = gtk_list_store_new(NUM_MODEL_COLUMNS, G_TYPE_STRING, G_TYPE_STRING);
606 GtkWidget *p_treeview_kbd_model = gtk_tree_view_new_with_model(GTK_TREE_MODEL(p_liststore_kbd_model));
607 g_object_unref(G_OBJECT(p_liststore_kbd_model));
608 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(p_treeview_kbd_model), TRUE);
609 gtk_container_add(GTK_CONTAINER(p_scrolledwindow_kbd_model), p_treeview_kbd_model);
610 GtkCellRenderer *p_renderer;
611 GtkTreeViewColumn *p_column;
612 // model desc
613 p_renderer = gtk_cell_renderer_text_new();
614 p_column = gtk_tree_view_column_new_with_attributes(_("Description"), p_renderer, "text", COLUMN_MODEL_DESC, NULL);
615 gtk_tree_view_column_set_sort_column_id(p_column, COLUMN_MODEL_DESC);
616 gtk_tree_view_append_column(GTK_TREE_VIEW(p_treeview_kbd_model), p_column);
617 // model id
618 p_renderer = gtk_cell_renderer_text_new();
619 p_column = gtk_tree_view_column_new_with_attributes(_("Id"), p_renderer, "text", COLUMN_MODEL_ID, NULL);
620 gtk_tree_view_column_set_sort_column_id(p_column, COLUMN_MODEL_ID);
621 gtk_tree_view_append_column(GTK_TREE_VIEW(p_treeview_kbd_model), p_column);
622
623
624 // populate model
625 GKeyFile *p_keyfile = g_key_file_new();
626 gchar *xkbcfg_filepath = g_strdup_printf("%s/models.cfg", XKBCONFDIR);
627 if(g_key_file_load_from_file(p_keyfile, xkbcfg_filepath, 0, NULL))
628 {
629 gchar **keys_models = g_key_file_get_keys(p_keyfile, "MODELS", NULL, NULL);
630 guint model_idx = 0;
631 GtkTreeIter tree_iter;
632 gchar *p_model_desc;
633 while(keys_models[model_idx] != NULL)
634 {
635 p_model_desc = g_key_file_get_string(p_keyfile, "MODELS", keys_models[model_idx], NULL);
636 gtk_list_store_append(p_liststore_kbd_model, &tree_iter);
637 gtk_list_store_set(p_liststore_kbd_model, &tree_iter,
638 COLUMN_MODEL_ID, keys_models[model_idx],
639 COLUMN_MODEL_DESC, g_dgettext("xkeyboard-config", p_model_desc),
640 -1);
641 g_free(p_model_desc);
642 model_idx++;
643 }
644 g_strfreev(keys_models);
645 g_key_file_free(p_keyfile);
646 }
647 g_free(xkbcfg_filepath);
648
649 // callback for double click
650 g_signal_connect(p_treeview_kbd_model, "button-press-event",
651 G_CALLBACK(on_treeviews_lists_button_press_event),
652 gtk_dialog_get_widget_for_response(GTK_DIALOG(p_dialog), GTK_RESPONSE_OK));
653 // sort for description
654 gtk_tree_view_column_clicked(p_column);
655
656 gtk_widget_set_size_request(p_dialog, 700, 500);
657 gtk_widget_show_all(GTK_WIDGET(p_scrolledwindow_kbd_model));
658 gint response = gtk_dialog_run(GTK_DIALOG(p_dialog));
659 if(response == GTK_RESPONSE_OK)
660 {
661 GtkTreeIter tree_iter_sel;
662 GtkTreeSelection *p_treeselection_kbd_model = gtk_tree_view_get_selection(GTK_TREE_VIEW(p_treeview_kbd_model));
663 if(gtk_tree_selection_get_selected(p_treeselection_kbd_model,
664 (GtkTreeModel **)(&p_liststore_kbd_model),
665 &tree_iter_sel))
666 {
667 gchar *kbd_model_new;
668 gtk_tree_model_get(GTK_TREE_MODEL(p_liststore_kbd_model),
669 &tree_iter_sel, COLUMN_MODEL_ID, &kbd_model_new, -1);
670 g_free(p_xkb->kbd_model);
671 p_xkb->kbd_model = g_strdup(kbd_model_new);
672 config_group_set_string(p_xkb->settings, "Model", kbd_model_new);
673 gtk_button_set_label(GTK_BUTTON(p_xkb->p_button_kbd_model), p_xkb->kbd_model);
674 g_free(kbd_model_new);
675 xkb_setxkbmap(p_xkb);
676 xkb_redraw(p_xkb);
677 }
678 }
679 gtk_widget_destroy(p_dialog);
680 }
681
682 static gboolean change_opt_tree_model_foreach(GtkTreeModel *p_model,
683 GtkTreePath *p_path,
684 GtkTreeIter *p_iter,
685 gpointer p_data)
686 {
687 XkbPlugin *p_xkb = (XkbPlugin *)p_data;
688
689 gboolean included;
690
691 gtk_tree_model_get(p_model, p_iter, COLUMN_CHANGE_INCL, &included, -1);
692 if(included)
693 {
694 gchar *change_opt_id;
695 gtk_tree_model_get(p_model, p_iter, COLUMN_CHANGE_ID, &change_opt_id, -1);
696 if(strlen(p_xkb->p_gstring_change_opt_partial->str))
697 {
698 g_string_append_c(p_xkb->p_gstring_change_opt_partial, ',');
699 }
700 g_string_append(p_xkb->p_gstring_change_opt_partial, change_opt_id);
701
702 //g_printf("\npartial change opt = '%s'\n", p_xkb->p_gstring_change_opt_partial->str);
703
704 g_free(change_opt_id);
705 }
706 return FALSE;
707 }
708
709 static void on_button_kbd_change_layout_clicked(GtkButton *p_button, gpointer *p_data)
710 {
711 XkbPlugin *p_xkb = (XkbPlugin *)p_data;
712
713 // dialog
714 GtkWidget *p_dialog = gtk_dialog_new_with_buttons(_("Select Layout Change Type"),
715 p_xkb->p_dialog_config,
716 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
717 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
718 GTK_STOCK_OK, GTK_RESPONSE_OK,
719 NULL);
720
721 // scrolledwindow
722 GtkWidget * p_scrolledwindow_kbd_change = gtk_scrolled_window_new(NULL, NULL);
723 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(p_scrolledwindow_kbd_change),
724 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
725 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(p_dialog))),
726 p_scrolledwindow_kbd_change, TRUE, TRUE, 2);
727
728 // liststore
729 GtkListStore *p_liststore_kbd_change = gtk_list_store_new(NUM_CHANGE_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_INT);
730 GtkWidget *p_treeview_kbd_change = gtk_tree_view_new_with_model(GTK_TREE_MODEL(p_liststore_kbd_change));
731 g_object_unref(G_OBJECT(p_liststore_kbd_change));
732 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(p_treeview_kbd_change), TRUE);
733 gtk_container_add(GTK_CONTAINER(p_scrolledwindow_kbd_change), p_treeview_kbd_change);
734 GtkCellRenderer *p_renderer;
735 GtkTreeViewColumn *p_column;
736 // change included
737 p_renderer = gtk_cell_renderer_toggle_new();
738 g_signal_connect(p_renderer, "toggled", G_CALLBACK(on_cell_renderer_layout_change_incl_toggled), p_liststore_kbd_change);
739 p_column = gtk_tree_view_column_new_with_attributes("", p_renderer, "active", COLUMN_CHANGE_INCL, NULL);
740 gtk_tree_view_append_column(GTK_TREE_VIEW(p_treeview_kbd_change), p_column);
741 // change desc
742 p_renderer = gtk_cell_renderer_text_new();
743 p_column = gtk_tree_view_column_new_with_attributes(_("Description"), p_renderer, "text", COLUMN_CHANGE_DESC,
744 "weight", COLUMN_CHANGE_WEIGHT,
745 NULL);
746 gtk_tree_view_column_set_sort_column_id(p_column, COLUMN_CHANGE_DESC);
747 gtk_tree_view_append_column(GTK_TREE_VIEW(p_treeview_kbd_change), p_column);
748 // change id
749 p_renderer = gtk_cell_renderer_text_new();
750 p_column = gtk_tree_view_column_new_with_attributes(_("Id"), p_renderer, "text", COLUMN_CHANGE_ID,
751 "weight", COLUMN_CHANGE_WEIGHT,
752 NULL);
753 gtk_tree_view_column_set_sort_column_id(p_column, COLUMN_CHANGE_ID);
754 gtk_tree_view_append_column(GTK_TREE_VIEW(p_treeview_kbd_change), p_column);
755
756 // populate model
757 GKeyFile *p_keyfile = g_key_file_new();
758 gchar *xkbcfg_filepath = g_strdup_printf("%s/toggle.cfg", XKBCONFDIR);
759 if(g_key_file_load_from_file(p_keyfile, xkbcfg_filepath, 0, NULL))
760 {
761 char **change_opts = g_strsplit_set(p_xkb->kbd_change_option, ",", 0);
762 int num_change_opts;
763 gchar **keys_changes = g_key_file_get_keys(p_keyfile, "TOGGLE", NULL, NULL);
764 guint change_idx = 0;
765 GtkTreeIter tree_iter;
766 gchar *p_change_desc;
767 while(keys_changes[change_idx] != NULL)
768 {
769 p_change_desc = g_key_file_get_string(p_keyfile, "TOGGLE", keys_changes[change_idx], NULL);
770 gtk_list_store_append(p_liststore_kbd_change, &tree_iter);
771 gboolean included = FALSE;
772 num_change_opts = 0;
773 while(change_opts[num_change_opts] != NULL)
774 {
775 if(strcmp(change_opts[num_change_opts], keys_changes[change_idx]) == 0)
776 {
777 included = TRUE;
778 break;
779 }
780 num_change_opts++;
781 }
782 gtk_list_store_set(p_liststore_kbd_change, &tree_iter,
783 COLUMN_CHANGE_ID, keys_changes[change_idx],
784 COLUMN_CHANGE_DESC, g_dgettext("xkeyboard-config", p_change_desc),
785 COLUMN_CHANGE_INCL, included,
786 COLUMN_CHANGE_WEIGHT, included ? PANGO_WEIGHT_ULTRAHEAVY : PANGO_WEIGHT_NORMAL,
787 -1);
788 g_free(p_change_desc);
789 change_idx++;
790 }
791 g_strfreev(keys_changes);
792 g_key_file_free(p_keyfile);
793 g_strfreev(change_opts);
794 }
795 g_free(xkbcfg_filepath);
796
797 // callback for double click
798 //g_signal_connect(p_treeview_kbd_change, "button-press-event",
799 //G_CALLBACK(on_treeviews_lists_button_press_event),
800 //gtk_dialog_get_widget_for_response(GTK_DIALOG(p_dialog), GTK_RESPONSE_OK));
801 gtk_widget_set_size_request(p_dialog, 700, 500);
802 gtk_widget_show_all(GTK_WIDGET(p_scrolledwindow_kbd_change));
803 gint response = gtk_dialog_run(GTK_DIALOG(p_dialog));
804 if(response == GTK_RESPONSE_OK)
805 {
806 p_xkb->p_gstring_change_opt_partial = g_string_new("");
807 gtk_tree_model_foreach(GTK_TREE_MODEL(p_liststore_kbd_change),
808 change_opt_tree_model_foreach,
809 p_xkb);
810 if(!strlen(p_xkb->p_gstring_change_opt_partial->str))
811 {
812 g_string_append_c(p_xkb->p_gstring_change_opt_partial, ',');
813 }
814 g_free(p_xkb->kbd_change_option);
815 p_xkb->kbd_change_option = g_strdup(p_xkb->p_gstring_change_opt_partial->str);
816 config_group_set_string(p_xkb->settings, "ToggleOpt", p_xkb->kbd_change_option);
817 g_string_free(p_xkb->p_gstring_change_opt_partial, TRUE/*free also gstring->str*/);
818
819 gtk_button_set_label(GTK_BUTTON(p_xkb->p_button_change_layout), p_xkb->kbd_change_option);
820 xkb_setxkbmap(p_xkb);
821 xkb_redraw(p_xkb);
822 }
823 gtk_widget_destroy(p_dialog);
824 }
825
826 static void on_button_up_layout_clicked(GtkButton *p_button, gpointer *p_data)
827 {
828 XkbPlugin *p_xkb = (XkbPlugin *)p_data;
829 GtkTreeIter tree_iter_sel;
830 GtkTreeIter tree_iter_prev;
831 if(gtk_tree_selection_get_selected(p_xkb->p_treeselection_layout,
832 (GtkTreeModel **)(&p_xkb->p_liststore_layout),
833 &tree_iter_sel))
834 {
835 GtkTreePath *p_tree_path = gtk_tree_model_get_path(GTK_TREE_MODEL(p_xkb->p_liststore_layout),
836 &tree_iter_sel);
837 if( (gtk_tree_path_prev(p_tree_path)) &&
838 (gtk_tree_model_get_iter(GTK_TREE_MODEL(p_xkb->p_liststore_layout),
839 &tree_iter_prev,
840 p_tree_path)))
841 {
842 gtk_list_store_swap(p_xkb->p_liststore_layout, &tree_iter_sel, &tree_iter_prev);
843 xkb_update_layouts_n_variants(p_xkb);
844 }
845 gtk_tree_path_free(p_tree_path);
846 }
847 }
848
849 static void on_button_down_layout_clicked(GtkButton *p_button, gpointer *p_data)
850 {
851 XkbPlugin *p_xkb = (XkbPlugin *)p_data;
852 GtkTreeIter tree_iter_sel;
853 GtkTreeIter tree_iter_next;
854 if(gtk_tree_selection_get_selected(p_xkb->p_treeselection_layout,
855 (GtkTreeModel **)(&p_xkb->p_liststore_layout),
856 &tree_iter_sel))
857 {
858 tree_iter_next = tree_iter_sel;
859 if(gtk_tree_model_iter_next(GTK_TREE_MODEL(p_xkb->p_liststore_layout),
860 &tree_iter_next))
861 {
862 gtk_list_store_swap(p_xkb->p_liststore_layout, &tree_iter_sel, &tree_iter_next);
863 xkb_update_layouts_n_variants(p_xkb);
864 }
865 }
866 }
867
868 static void on_button_rm_layout_clicked(GtkButton *p_button, gpointer *p_data)
869 {
870 XkbPlugin *p_xkb = (XkbPlugin *)p_data;
871 GtkTreeIter tree_iter_sel;
872 if(gtk_tree_selection_get_selected(p_xkb->p_treeselection_layout,
873 (GtkTreeModel **)(&p_xkb->p_liststore_layout),
874 &tree_iter_sel))
875 {
876 gtk_list_store_remove(p_xkb->p_liststore_layout, &tree_iter_sel);
877 xkb_update_layouts_n_variants(p_xkb);
878 gtk_widget_set_sensitive(p_xkb->p_button_rm_layout, p_xkb->num_layouts > 1);
879 }
880 }
881
882 static void on_button_add_layout_clicked(GtkButton *p_button, gpointer *p_data)
883 {
884 XkbPlugin *p_xkb = (XkbPlugin *)p_data;
885
886 // dialog
887 GtkWidget *p_dialog = gtk_dialog_new_with_buttons(_("Add Keyboard Layout"),
888 p_xkb->p_dialog_config,
889 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
890 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
891 GTK_STOCK_OK, GTK_RESPONSE_OK,
892 NULL);
893
894 // scrolledwindow
895 GtkWidget * p_scrolledwindow_add_layout = gtk_scrolled_window_new(NULL, NULL);
896 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(p_scrolledwindow_add_layout),
897 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
898 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(p_dialog))),
899 p_scrolledwindow_add_layout, TRUE, TRUE, 2);
900
901 // treestore
902 GtkTreeStore *p_treestore_add_layout = gtk_tree_store_new(NUM_ADD_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING);
903 GtkWidget *p_treeview_add_layout = gtk_tree_view_new_with_model(GTK_TREE_MODEL(p_treestore_add_layout));
904 g_object_unref(G_OBJECT(p_treestore_add_layout));
905 gtk_tree_view_set_enable_tree_lines(GTK_TREE_VIEW(p_treeview_add_layout), TRUE);
906 gtk_container_add(GTK_CONTAINER(p_scrolledwindow_add_layout), p_treeview_add_layout);
907 GtkCellRenderer *p_renderer;
908 GtkTreeViewColumn *p_column, *p_column_desc;
909 // icon
910 p_renderer = gtk_cell_renderer_pixbuf_new();
911 p_column = gtk_tree_view_column_new_with_attributes(_("Flag"), p_renderer, "pixbuf", COLUMN_ADD_ICON, NULL);
912 gtk_tree_view_column_set_sort_column_id(p_column, COLUMN_ADD_DESC);
913 gtk_tree_view_append_column(GTK_TREE_VIEW(p_treeview_add_layout), p_column);
914 // layout
915 p_renderer = gtk_cell_renderer_text_new();
916 p_column = gtk_tree_view_column_new_with_attributes(_("Layout"), p_renderer, "text", COLUMN_ADD_LAYOUT, NULL);
917 gtk_tree_view_column_set_sort_column_id(p_column, COLUMN_ADD_LAYOUT);
918 gtk_tree_view_append_column(GTK_TREE_VIEW(p_treeview_add_layout), p_column);
919 // desc
920 p_renderer = gtk_cell_renderer_text_new();
921 p_column_desc = gtk_tree_view_column_new_with_attributes(_("Description"), p_renderer, "text", COLUMN_ADD_DESC, NULL);
922 gtk_tree_view_column_set_sort_column_id(p_column_desc, COLUMN_ADD_DESC);
923 gtk_tree_view_append_column(GTK_TREE_VIEW(p_treeview_add_layout), p_column_desc);
924 // search column
925 gtk_tree_view_set_search_column(GTK_TREE_VIEW(p_treeview_add_layout), COLUMN_ADD_DESC);
926
927 // populate model
928 GKeyFile *p_keyfile = g_key_file_new();
929 gchar *xkbcfg_filepath = g_strdup_printf("%s/layouts.cfg", XKBCONFDIR);
930 if(g_key_file_load_from_file(p_keyfile, xkbcfg_filepath, 0, NULL))
931 {
932 gchar **keys_layouts = g_key_file_get_keys(p_keyfile, "LAYOUTS", NULL, NULL);
933 guint layout_idx = 0;
934 GtkTreeIter tree_top, tree_child;
935 gchar *p_layout_desc;
936 while(keys_layouts[layout_idx] != NULL)
937 {
938 p_layout_desc = g_key_file_get_string(p_keyfile, "LAYOUTS", keys_layouts[layout_idx], NULL);
939 if(strchr(keys_layouts[layout_idx], '(') == NULL)
940 {
941 gchar *flag_filepath = NULL;
942 gchar *flags_dir = (p_xkb->cust_dir_exists && (p_xkb->display_type == DISP_TYPE_IMAGE_CUST)) ? g_strdup(FLAGSCUSTDIR):g_strdup(FLAGSDIR);
943 if(strchr(keys_layouts[layout_idx], '/') != NULL)
944 {
945 gchar *layout_mod = g_strdup(keys_layouts[layout_idx]);
946 layout_mod = g_strdelimit(layout_mod, "/", '-');
947 flag_filepath = g_strdup_printf(flag_filepath_generator, flags_dir, layout_mod);
948 g_free(layout_mod);
949 }
950 else
951 {
952 flag_filepath = g_strdup_printf(flag_filepath_generator, flags_dir, keys_layouts[layout_idx]);
953 }
954 GdkPixbuf *p_pixbuf = gdk_pixbuf_new_from_file_at_size(flag_filepath, -1, 16, NULL);
955 gtk_tree_store_append(p_treestore_add_layout, &tree_top, NULL);
956 if(p_pixbuf != NULL)
957 {
958 gtk_tree_store_set(p_treestore_add_layout, &tree_top,
959 COLUMN_ADD_ICON, p_pixbuf,
960 COLUMN_ADD_LAYOUT, keys_layouts[layout_idx],
961 COLUMN_ADD_DESC, g_dgettext("xkeyboard-config", p_layout_desc),
962 -1);
963 g_object_unref(G_OBJECT(p_pixbuf));
964 }
965 else
966 {
967 gtk_tree_store_set(p_treestore_add_layout, &tree_top,
968 COLUMN_ADD_LAYOUT, keys_layouts[layout_idx],
969 COLUMN_ADD_DESC, p_layout_desc,
970 -1);
971 }
972 g_free(flag_filepath);
973 g_free(flags_dir);
974 }
975 else
976 {
977
978 gtk_tree_store_append(p_treestore_add_layout, &tree_child, &tree_top);
979 gtk_tree_store_set(p_treestore_add_layout, &tree_child,
980 COLUMN_ADD_LAYOUT, keys_layouts[layout_idx],
981 COLUMN_ADD_DESC, p_layout_desc,
982 -1);
983 }
984 g_free(p_layout_desc);
985 layout_idx++;
986 }
987 g_strfreev(keys_layouts);
988 g_key_file_free(p_keyfile);
989 }
990 g_free(xkbcfg_filepath);
991
992 // callback for double click
993 g_signal_connect(p_treeview_add_layout, "button-press-event",
994 G_CALLBACK(on_treeviews_lists_button_press_event),
995 gtk_dialog_get_widget_for_response(GTK_DIALOG(p_dialog), GTK_RESPONSE_OK));
996 // sort for description
997 gtk_tree_view_column_clicked(p_column_desc);
998
999 gtk_widget_set_size_request(p_dialog, 700, 500);
1000 gtk_widget_show_all(GTK_WIDGET(p_scrolledwindow_add_layout));
1001 gint response = gtk_dialog_run(GTK_DIALOG(p_dialog));
1002 if(response == GTK_RESPONSE_OK)
1003 {
1004 GtkTreeIter tree_iter_sel;
1005 GtkTreeSelection *p_treeselection_add_layout = gtk_tree_view_get_selection(GTK_TREE_VIEW(p_treeview_add_layout));
1006 if(gtk_tree_selection_get_selected(p_treeselection_add_layout,
1007 (GtkTreeModel **)(&p_treestore_add_layout),
1008 &tree_iter_sel))
1009 {
1010 gchar *layout_add;
1011 GString *p_gstring_new_layout = g_string_new("");
1012 GString *p_gstring_new_variant = g_string_new("");
1013 gtk_tree_model_get(GTK_TREE_MODEL(p_treestore_add_layout),
1014 &tree_iter_sel, COLUMN_ADD_LAYOUT, &layout_add, -1);
1015
1016 if(strchr(layout_add, '(') == NULL)
1017 {
1018 g_string_append(p_gstring_new_layout, layout_add);
1019 }
1020 else
1021 {
1022 gboolean parsing_variant = FALSE;
1023 guchar i;
1024 for(i=0; layout_add[i]; i++)
1025 {
1026 if(!parsing_variant)
1027 {
1028 if(layout_add[i] == '(')
1029 parsing_variant = TRUE;
1030 else
1031 g_string_append_c(p_gstring_new_layout, layout_add[i]);
1032 }
1033 else
1034 {
1035 if(layout_add[i] == ')')
1036 break;
1037 else
1038 g_string_append_c(p_gstring_new_variant, layout_add[i]);
1039 }
1040 }
1041 }
1042 xkb_add_layout(p_xkb, p_gstring_new_layout->str, p_gstring_new_variant->str);
1043 xkb_update_layouts_n_variants(p_xkb);
1044
1045 gtk_widget_set_sensitive(p_xkb->p_button_rm_layout, p_xkb->num_layouts > 1);
1046
1047 g_free(layout_add);
1048 g_string_free(p_gstring_new_layout, TRUE/*free also gstring->str*/);
1049 g_string_free(p_gstring_new_variant, TRUE/*free also gstring->str*/);
1050 }
1051 }
1052 gtk_widget_destroy(p_dialog);
1053 }
1054
1055 void xkb_setxkbmap(XkbPlugin *p_xkb)
1056 {
1057 GString *p_gstring_syscmd;
1058 int rc;
1059
1060 if(p_xkb->keep_system_layouts) return;
1061
1062 p_gstring_syscmd = g_string_new("");
1063 g_string_printf(p_gstring_syscmd,
1064 "setxkbmap -model %s -layout %s -variant %s -option %s",
1065 p_xkb->kbd_model, p_xkb->kbd_layouts, p_xkb->kbd_variants,
1066 p_xkb->kbd_change_option);
1067 if( (p_xkb->kbd_advanced_options != NULL) && strlen(p_xkb->kbd_advanced_options) )
1068 {
1069 g_string_append(p_gstring_syscmd, " ");
1070 g_string_append(p_gstring_syscmd, p_xkb->kbd_advanced_options);
1071 }
1072 if(!p_xkb->do_not_reset_opt)
1073 {
1074 rc = system("setxkbmap -option"); // reset options
1075 if(rc)
1076 {
1077 g_warning("xkb: system(setxkbmap -option) returned %d", rc);
1078 }
1079 }
1080 rc = system(p_gstring_syscmd->str); // set new map
1081 if(rc)
1082 {
1083 g_warning("xkb: system(%s) returned %d", p_gstring_syscmd->str, rc);
1084 }
1085 //g_printf("\n%s\n", p_gstring_syscmd->str);
1086 g_string_free(p_gstring_syscmd, TRUE/*free also gstring->str*/);
1087 }
1088
1089 static gboolean layouts_tree_model_foreach(GtkTreeModel *p_model,
1090 GtkTreePath *p_path,
1091 GtkTreeIter *p_iter,
1092 gpointer p_data)
1093 {
1094 XkbPlugin *p_xkb = (XkbPlugin *)p_data;
1095 gchar *layout_val;
1096 gchar *variant_val;
1097
1098 gtk_tree_model_get(p_model, p_iter, COLUMN_LAYOUT, &layout_val, -1);
1099 gtk_tree_model_get(p_model, p_iter, COLUMN_VARIANT, &variant_val, -1);
1100
1101 if(strlen(p_xkb->p_gstring_layouts_partial->str))
1102 {
1103 g_string_append_c(p_xkb->p_gstring_layouts_partial, ',');
1104 g_string_append_c(p_xkb->p_gstring_variants_partial, ',');
1105 }
1106 g_string_append(p_xkb->p_gstring_layouts_partial, layout_val);
1107 g_string_append(p_xkb->p_gstring_variants_partial, variant_val);
1108
1109 //g_printf("\npartial layouts = '%s'\n", p_xkb->p_gstring_layouts_partial->str);
1110 //g_printf("partial variants = '%s'\n", p_xkb->p_gstring_variants_partial->str);
1111
1112 g_free(layout_val);
1113 g_free(variant_val);
1114 p_xkb->num_layouts++;
1115 return FALSE;
1116 }
1117
1118 static void xkb_update_layouts_n_variants(XkbPlugin *p_xkb)
1119 {
1120 p_xkb->p_gstring_layouts_partial = g_string_new("");
1121 p_xkb->p_gstring_variants_partial = g_string_new("");
1122 p_xkb->num_layouts = 0;
1123 gtk_tree_model_foreach(GTK_TREE_MODEL(p_xkb->p_liststore_layout),
1124 layouts_tree_model_foreach,
1125 p_xkb);
1126 if(!strlen(p_xkb->p_gstring_variants_partial->str))
1127 {
1128 g_string_append_c(p_xkb->p_gstring_variants_partial, ',');
1129 }
1130 g_free(p_xkb->kbd_layouts);
1131 g_free(p_xkb->kbd_variants);
1132 p_xkb->kbd_layouts = g_strdup(p_xkb->p_gstring_layouts_partial->str);
1133 p_xkb->kbd_variants = g_strdup(p_xkb->p_gstring_variants_partial->str);
1134 g_string_free(p_xkb->p_gstring_layouts_partial, TRUE/*free also gstring->str*/);
1135 g_string_free(p_xkb->p_gstring_variants_partial, TRUE/*free also gstring->str*/);
1136 xkb_setxkbmap(p_xkb);
1137 xkb_mechanism_destructor(p_xkb);
1138 xkb_mechanism_constructor(p_xkb);
1139 config_group_set_string(p_xkb->settings, "LayoutsList", p_xkb->kbd_layouts);
1140 config_group_set_string(p_xkb->settings, "VariantsList", p_xkb->kbd_variants);
1141 xkb_redraw(p_xkb);
1142 }
1143
1144 static void xkb_add_layout(XkbPlugin *p_xkb, gchar *layout, gchar*variant)
1145 {
1146 GtkTreeIter tree_iter;
1147 gtk_list_store_append(p_xkb->p_liststore_layout, &tree_iter);
1148 gchar *flag_filepath = NULL;
1149 gchar *flags_dir = (p_xkb->cust_dir_exists && (p_xkb->display_type == DISP_TYPE_IMAGE_CUST)) ? g_strdup(FLAGSCUSTDIR):g_strdup(FLAGSDIR);
1150 if(strchr(layout, '/') != NULL)
1151 {
1152 gchar *layout_mod = g_strdup(layout);
1153 layout_mod = g_strdelimit(layout_mod, "/", '-');
1154 flag_filepath = g_strdup_printf(flag_filepath_generator, flags_dir, layout_mod);
1155 g_free(layout_mod);
1156 }
1157 else
1158 {
1159 flag_filepath = g_strdup_printf(flag_filepath_generator, flags_dir, layout);
1160 }
1161 GdkPixbuf *p_pixbuf = gdk_pixbuf_new_from_file_at_size(flag_filepath, -1, 20, NULL);
1162 if(p_pixbuf != NULL)
1163 {
1164 gtk_list_store_set(p_xkb->p_liststore_layout, &tree_iter,
1165 COLUMN_ICON, p_pixbuf,
1166 COLUMN_LAYOUT, layout,
1167 COLUMN_VARIANT, variant,
1168 -1);
1169 g_object_unref(G_OBJECT(p_pixbuf));
1170 }
1171 else
1172 {
1173 gtk_list_store_set(p_xkb->p_liststore_layout, &tree_iter,
1174 COLUMN_LAYOUT, layout,
1175 COLUMN_VARIANT, variant,
1176 -1);
1177 }
1178 g_free(flag_filepath);
1179 g_free(flags_dir);
1180 }
1181
1182 static void xkb_settings_fill_layout_tree_model_with_config(XkbPlugin *p_xkb)
1183 {
1184 p_xkb->num_layouts = 0;
1185 if(strlen(p_xkb->kbd_layouts) && strlen(p_xkb->kbd_variants))
1186 {
1187 char **layouts = g_strsplit_set(p_xkb->kbd_layouts, ",", 0);
1188 char **variants = g_strsplit_set(p_xkb->kbd_variants, ",", 0);
1189
1190 while(layouts[p_xkb->num_layouts] != NULL)
1191 {
1192 xkb_add_layout(p_xkb, layouts[p_xkb->num_layouts], variants[p_xkb->num_layouts]);
1193 p_xkb->num_layouts++;
1194 }
1195 gtk_widget_set_sensitive(p_xkb->p_button_rm_layout, p_xkb->num_layouts > 1);
1196
1197 g_strfreev(layouts);
1198 g_strfreev(variants);
1199 }
1200 }
1201
1202 /* Callback when the configuration dialog is to be shown. */
1203 static GtkWidget *xkb_configure(LXPanel *panel, GtkWidget *p)
1204 {
1205 XkbPlugin * p_xkb = lxpanel_plugin_get_data(p);
1206 gchar markup_str[MAX_MARKUP_LEN];
1207
1208 user_active = FALSE;
1209
1210 // configuration dialog
1211 GtkWidget * dlg = gtk_dialog_new_with_buttons(
1212 _("Keyboard Layout Handler"),
1213 NULL,
1214 GTK_DIALOG_NO_SEPARATOR,
1215 GTK_STOCK_CLOSE,
1216 GTK_RESPONSE_OK,
1217 NULL);
1218 p_xkb->p_dialog_config = GTK_WINDOW(dlg);
1219 panel_apply_icon(p_xkb->p_dialog_config);
1220
1221 // main vbox of the config dialog
1222 GtkWidget * p_hbox_main = gtk_hbox_new(TRUE, 0);
1223 GtkWidget * p_vbox_left = gtk_vbox_new(FALSE, 0);
1224 GtkWidget * p_vbox_right = gtk_vbox_new(FALSE, 0);
1225 gtk_container_add(GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(dlg))),
1226 p_hbox_main);
1227 gtk_box_pack_start(GTK_BOX(p_hbox_main), p_vbox_left, FALSE, TRUE, 0);
1228 gtk_box_pack_start(GTK_BOX(p_hbox_main), p_vbox_right, FALSE, TRUE, 0);
1229
1230
1231 // 'KEYBOARD MODEL' frame
1232 p_xkb->p_frame_kbd_model = gtk_frame_new(NULL);
1233 gtk_widget_set_sensitive(p_xkb->p_frame_kbd_model, !p_xkb->keep_system_layouts);
1234 GtkWidget * p_label_kbd_model = gtk_label_new(NULL);
1235 snprintf(markup_str, MAX_MARKUP_LEN, "<b>%s</b>", _("Keyboard Model"));
1236 gtk_label_set_markup(GTK_LABEL(p_label_kbd_model), markup_str);
1237 gtk_misc_set_padding(GTK_MISC(p_label_kbd_model), 1, 0);
1238 gtk_frame_set_label_widget(GTK_FRAME(p_xkb->p_frame_kbd_model), p_label_kbd_model);
1239 gtk_frame_set_shadow_type(GTK_FRAME(p_xkb->p_frame_kbd_model), GTK_SHADOW_NONE);
1240 gtk_box_pack_start(GTK_BOX(p_vbox_left), p_xkb->p_frame_kbd_model, TRUE, TRUE, 2);
1241 gtk_container_set_border_width(GTK_CONTAINER(p_xkb->p_frame_kbd_model), 3);
1242
1243 // frame alignment
1244 GtkWidget * p_alignment_kbd_model = gtk_alignment_new(0.5, 0.5, 1, 1);
1245 gtk_container_add(GTK_CONTAINER(p_xkb->p_frame_kbd_model), p_alignment_kbd_model);
1246 gtk_alignment_set_padding(GTK_ALIGNMENT(p_alignment_kbd_model), 4, 4, 10, 10);
1247 p_xkb->p_button_kbd_model = gtk_button_new_with_label(p_xkb->kbd_model);
1248 g_signal_connect(p_xkb->p_button_kbd_model, "clicked", G_CALLBACK(on_button_kbd_model_clicked), p_xkb);
1249 gtk_container_add(GTK_CONTAINER(p_alignment_kbd_model), p_xkb->p_button_kbd_model);
1250
1251
1252 // 'KEYBOARD LAYOUTS' frame
1253 p_xkb->p_frame_kbd_layouts = gtk_frame_new(NULL);
1254 gtk_widget_set_sensitive(p_xkb->p_frame_kbd_layouts, !p_xkb->keep_system_layouts);
1255 GtkWidget * p_label_kbd_layouts = gtk_label_new(NULL);
1256 snprintf(markup_str, MAX_MARKUP_LEN, "<b>%s</b>", _("Keyboard Layouts"));
1257 gtk_label_set_markup(GTK_LABEL(p_label_kbd_layouts), markup_str);
1258 gtk_misc_set_padding(GTK_MISC(p_label_kbd_layouts), 1, 0);
1259 gtk_frame_set_label_widget(GTK_FRAME(p_xkb->p_frame_kbd_layouts), p_label_kbd_layouts);
1260 gtk_frame_set_shadow_type(GTK_FRAME(p_xkb->p_frame_kbd_layouts), GTK_SHADOW_NONE);
1261 gtk_box_pack_start(GTK_BOX(p_vbox_left), p_xkb->p_frame_kbd_layouts, TRUE, TRUE, 2);
1262 gtk_container_set_border_width(GTK_CONTAINER(p_xkb->p_frame_kbd_layouts), 3);
1263 gtk_widget_set_size_request(GTK_WIDGET(p_xkb->p_frame_kbd_layouts), 300, 180);
1264
1265 // frame alignment
1266 GtkWidget * p_alignment_kbd_layouts = gtk_alignment_new(0.5, 0.5, 1, 1);
1267 gtk_container_add(GTK_CONTAINER(p_xkb->p_frame_kbd_layouts), p_alignment_kbd_layouts);
1268 gtk_alignment_set_padding(GTK_ALIGNMENT(p_alignment_kbd_layouts), 4, 4, 10, 10);
1269 GtkWidget * p_hbox_kbd_layouts = gtk_hbox_new(FALSE, 0);
1270 gtk_container_add(GTK_CONTAINER(p_alignment_kbd_layouts), p_hbox_kbd_layouts);
1271
1272 // scrolledwindow and buttons
1273 GtkWidget * p_scrolledwindow_kbd_layouts = gtk_scrolled_window_new(NULL, NULL);
1274 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(p_scrolledwindow_kbd_layouts),
1275 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1276 gtk_box_pack_start(GTK_BOX(p_hbox_kbd_layouts), p_scrolledwindow_kbd_layouts, TRUE, TRUE, 2);
1277 GtkWidget * p_vbox_kbd_layouts = gtk_vbox_new(FALSE, 2);
1278 gtk_box_pack_start(GTK_BOX(p_hbox_kbd_layouts), p_vbox_kbd_layouts, FALSE, TRUE, 4);
1279 GtkWidget * p_button_add_layout = gtk_button_new_from_stock(GTK_STOCK_ADD);
1280 GtkWidget * p_button_up_layout = gtk_button_new_from_stock(GTK_STOCK_GO_UP);
1281 GtkWidget * p_button_down_layout = gtk_button_new_from_stock(GTK_STOCK_GO_DOWN);
1282 p_xkb->p_button_rm_layout = gtk_button_new_from_stock(GTK_STOCK_REMOVE);
1283 g_signal_connect(p_button_add_layout, "clicked", G_CALLBACK(on_button_add_layout_clicked), p_xkb);
1284 g_signal_connect(p_xkb->p_button_rm_layout, "clicked", G_CALLBACK(on_button_rm_layout_clicked), p_xkb);
1285 g_signal_connect(p_button_up_layout, "clicked", G_CALLBACK(on_button_up_layout_clicked), p_xkb);
1286 g_signal_connect(p_button_down_layout, "clicked", G_CALLBACK(on_button_down_layout_clicked), p_xkb);
1287 gtk_box_pack_start(GTK_BOX(p_vbox_kbd_layouts), p_button_add_layout, FALSE, TRUE, 0);
1288 gtk_box_pack_start(GTK_BOX(p_vbox_kbd_layouts), p_xkb->p_button_rm_layout, FALSE, TRUE, 0);
1289 gtk_box_pack_start(GTK_BOX(p_vbox_kbd_layouts), p_button_up_layout, FALSE, TRUE, 0);
1290 gtk_box_pack_start(GTK_BOX(p_vbox_kbd_layouts), p_button_down_layout, FALSE, TRUE, 0);
1291
1292 // liststore
1293 p_xkb->p_liststore_layout = gtk_list_store_new(NUM_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING);
1294 p_xkb->p_treeview_layout = gtk_tree_view_new_with_model(GTK_TREE_MODEL(p_xkb->p_liststore_layout));
1295 g_object_unref(G_OBJECT(p_xkb->p_liststore_layout));
1296 p_xkb->p_treeselection_layout = gtk_tree_view_get_selection(GTK_TREE_VIEW(p_xkb->p_treeview_layout));
1297 gtk_container_add(GTK_CONTAINER(p_scrolledwindow_kbd_layouts), p_xkb->p_treeview_layout);
1298 GtkCellRenderer *p_renderer;
1299 GtkTreeViewColumn *p_column;
1300 // icon
1301 p_renderer = gtk_cell_renderer_pixbuf_new();
1302 p_column = gtk_tree_view_column_new_with_attributes(_("Flag"), p_renderer, "pixbuf", COLUMN_ICON, NULL);
1303 gtk_tree_view_append_column(GTK_TREE_VIEW(p_xkb->p_treeview_layout), p_column);
1304 // layout
1305 p_renderer = gtk_cell_renderer_text_new();
1306 p_column = gtk_tree_view_column_new_with_attributes(_("Layout"), p_renderer, "text", COLUMN_LAYOUT, NULL);
1307 gtk_tree_view_append_column(GTK_TREE_VIEW(p_xkb->p_treeview_layout), p_column);
1308 // variant
1309 p_renderer = gtk_cell_renderer_text_new();
1310 p_column = gtk_tree_view_column_new_with_attributes(_("Variant"), p_renderer, "text", COLUMN_VARIANT, NULL);
1311 gtk_tree_view_append_column(GTK_TREE_VIEW(p_xkb->p_treeview_layout), p_column);
1312 xkb_settings_fill_layout_tree_model_with_config(p_xkb);
1313
1314
1315 // 'CHANGE LAYOUT OPTION' frame
1316 p_xkb->p_frame_change_layout = gtk_frame_new(NULL);
1317 gtk_widget_set_sensitive(p_xkb->p_frame_change_layout, !p_xkb->keep_system_layouts);
1318 GtkWidget * p_label_change_layout = gtk_label_new(NULL);
1319 snprintf(markup_str, MAX_MARKUP_LEN, "<b>%s</b>", _("Change Layout Option"));
1320 gtk_label_set_markup(GTK_LABEL(p_label_change_layout), markup_str);
1321 gtk_misc_set_padding(GTK_MISC(p_label_change_layout), 1, 0);
1322 gtk_frame_set_label_widget(GTK_FRAME(p_xkb->p_frame_change_layout), p_label_change_layout);
1323 gtk_frame_set_shadow_type(GTK_FRAME(p_xkb->p_frame_change_layout), GTK_SHADOW_NONE);
1324 gtk_box_pack_start(GTK_BOX(p_vbox_left), p_xkb->p_frame_change_layout, TRUE, TRUE, 2);
1325 gtk_container_set_border_width(GTK_CONTAINER(p_xkb->p_frame_change_layout), 3);
1326
1327 // frame alignment
1328 GtkWidget * p_alignment_change_layout = gtk_alignment_new(0.5, 0.5, 1, 1);
1329 gtk_container_add(GTK_CONTAINER(p_xkb->p_frame_change_layout), p_alignment_change_layout);
1330 gtk_alignment_set_padding(GTK_ALIGNMENT(p_alignment_change_layout), 4, 4, 10, 10);
1331 p_xkb->p_button_change_layout = gtk_button_new_with_label(p_xkb->kbd_change_option);
1332 g_signal_connect(p_xkb->p_button_change_layout, "clicked", G_CALLBACK(on_button_kbd_change_layout_clicked), p_xkb);
1333 gtk_container_add(GTK_CONTAINER(p_alignment_change_layout), p_xkb->p_button_change_layout);
1334
1335
1336 // 'ADVANCED setxkbmap OPTIONS' frame
1337 GtkWidget * p_frame_advanced_opt = gtk_frame_new(NULL);
1338 GtkWidget * p_label_advanced_opt = gtk_label_new(NULL);
1339 snprintf(markup_str, MAX_MARKUP_LEN, "<b>%s</b>", _("Advanced setxkbmap Options"));
1340 gtk_label_set_markup(GTK_LABEL(p_label_advanced_opt), markup_str);
1341 gtk_misc_set_padding(GTK_MISC(p_label_advanced_opt), 1, 0);
1342 gtk_frame_set_label_widget(GTK_FRAME(p_frame_advanced_opt), p_label_advanced_opt);
1343 gtk_frame_set_shadow_type(GTK_FRAME(p_frame_advanced_opt), GTK_SHADOW_NONE);
1344 gtk_box_pack_start(GTK_BOX(p_vbox_right), p_frame_advanced_opt, TRUE, TRUE, 2);
1345 gtk_container_set_border_width(GTK_CONTAINER(p_frame_advanced_opt), 3);
1346
1347 // frame alignment
1348 GtkWidget * p_alignment_advanced_opt = gtk_alignment_new(0.5, 0.5, 1, 1);
1349 gtk_container_add(GTK_CONTAINER(p_frame_advanced_opt), p_alignment_advanced_opt);
1350 gtk_alignment_set_padding(GTK_ALIGNMENT(p_alignment_advanced_opt), 4, 4, 10, 10);
1351 GtkWidget * p_vbox_advanced_opt = gtk_vbox_new(FALSE, 0);
1352 gtk_container_add(GTK_CONTAINER(p_alignment_advanced_opt), p_vbox_advanced_opt);
1353 p_xkb->p_entry_advanced_opt = gtk_entry_new();
1354 gtk_widget_set_sensitive(p_xkb->p_entry_advanced_opt, !p_xkb->keep_system_layouts);
1355 if (p_xkb->kbd_advanced_options)
1356 gtk_entry_set_text(GTK_ENTRY(p_xkb->p_entry_advanced_opt), p_xkb->kbd_advanced_options);
1357 gtk_entry_set_icon_from_stock(GTK_ENTRY(p_xkb->p_entry_advanced_opt), GTK_ENTRY_ICON_SECONDARY, "gtk-save");
1358 g_signal_connect(p_xkb->p_entry_advanced_opt, "icon-press", G_CALLBACK(on_xkb_entry_advanced_opt_icon_press), p_xkb);
1359 gtk_box_pack_start(GTK_BOX(p_vbox_advanced_opt), p_xkb->p_entry_advanced_opt, FALSE, TRUE, 0);
1360 p_xkb->p_checkbutton_no_reset_opt = gtk_check_button_new_with_mnemonic(_("Do _not reset existing options"));
1361 gtk_widget_set_sensitive(p_xkb->p_checkbutton_no_reset_opt, !p_xkb->keep_system_layouts);
1362 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p_xkb->p_checkbutton_no_reset_opt), p_xkb->do_not_reset_opt);
1363 g_signal_connect(p_xkb->p_checkbutton_no_reset_opt, "toggled", G_CALLBACK(on_xkb_checkbutton_no_reset_opt_toggled), p_xkb);
1364 gtk_box_pack_start(GTK_BOX(p_vbox_advanced_opt), p_xkb->p_checkbutton_no_reset_opt, FALSE, TRUE, 0);
1365 GtkWidget *p_checkbutton_keep_system_layouts = gtk_check_button_new_with_mnemonic(_("Keep _system layouts"));
1366 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p_checkbutton_keep_system_layouts), p_xkb->keep_system_layouts);
1367 g_signal_connect(p_checkbutton_keep_system_layouts, "toggled", G_CALLBACK(on_xkb_checkbutton_keep_system_layouts_toggled), p_xkb);
1368 gtk_box_pack_start(GTK_BOX(p_vbox_advanced_opt), p_checkbutton_keep_system_layouts, FALSE, TRUE, 0);
1369
1370
1371 // 'PER WINDOW SETTINGS' frame
1372 GtkWidget * p_frame_perapp_layout = gtk_frame_new(NULL);
1373 GtkWidget * p_label_perapp_layout = gtk_label_new(NULL);
1374 snprintf(markup_str, MAX_MARKUP_LEN, "<b>%s</b>", _("Per Window Settings"));
1375 gtk_label_set_markup(GTK_LABEL(p_label_perapp_layout), markup_str);
1376 gtk_misc_set_padding(GTK_MISC(p_label_perapp_layout), 1, 0);
1377 gtk_frame_set_label_widget(GTK_FRAME(p_frame_perapp_layout), p_label_perapp_layout);
1378 gtk_frame_set_shadow_type(GTK_FRAME(p_frame_perapp_layout), GTK_SHADOW_NONE);
1379 gtk_box_pack_start(GTK_BOX(p_vbox_right), p_frame_perapp_layout, TRUE, TRUE, 2);
1380 gtk_container_set_border_width(GTK_CONTAINER(p_frame_perapp_layout), 3);
1381
1382 // frame alignment
1383 GtkWidget * p_alignment_perapp_layout = gtk_alignment_new(0.5, 0.5, 1, 1);
1384 gtk_container_add(GTK_CONTAINER(p_frame_perapp_layout), p_alignment_perapp_layout);
1385 gtk_alignment_set_padding(GTK_ALIGNMENT(p_alignment_perapp_layout), 4, 4, 10, 10);
1386 GtkWidget *p_checkbutton_per_app = gtk_check_button_new_with_mnemonic(_("_Remember layout for each window"));
1387 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p_checkbutton_per_app), p_xkb->enable_perwin);
1388 g_signal_connect(p_checkbutton_per_app, "toggled", G_CALLBACK(on_xkb_checkbutton_per_app_toggled), p_xkb);
1389 gtk_container_add(GTK_CONTAINER(p_alignment_perapp_layout), p_checkbutton_per_app);
1390
1391
1392 // 'SHOW LAYOUT AS' frame
1393 GtkWidget * p_frame_display_type = gtk_frame_new(NULL);
1394 GtkWidget * p_label_show_layout_as = gtk_label_new(NULL);
1395 snprintf(markup_str, MAX_MARKUP_LEN, "<b>%s</b>", _("Show Layout as"));
1396 gtk_label_set_markup(GTK_LABEL(p_label_show_layout_as), markup_str);
1397 gtk_misc_set_padding(GTK_MISC(p_label_show_layout_as), 1, 0);
1398 gtk_frame_set_label_widget(GTK_FRAME(p_frame_display_type), p_label_show_layout_as);
1399 gtk_frame_set_shadow_type(GTK_FRAME(p_frame_display_type), GTK_SHADOW_NONE);
1400 gtk_box_pack_start(GTK_BOX(p_vbox_right), p_frame_display_type, TRUE, TRUE, 2);
1401 gtk_container_set_border_width(GTK_CONTAINER(p_frame_display_type), 3);
1402
1403 // frame alignment
1404 GtkWidget * p_alignment_display_type = gtk_alignment_new(0.5, 0.5, 1, 1);
1405 gtk_container_add(GTK_CONTAINER(p_frame_display_type), p_alignment_display_type);
1406 gtk_alignment_set_padding(GTK_ALIGNMENT(p_alignment_display_type), 4, 4, 10, 10);
1407 GtkWidget *p_table = gtk_table_new(3, 2, FALSE);
1408 gtk_table_set_col_spacings(GTK_TABLE(p_table), 10);
1409 gtk_container_add(GTK_CONTAINER(p_alignment_display_type), p_table);
1410
1411 // radiobuttons
1412 GtkWidget * p_image_disp_type_image = gtk_image_new();
1413 GtkWidget * p_image_disp_type_image_cust = NULL;
1414 gchar *symbol_name_lowercase = (char *)xkb_get_current_symbol_name_lowercase(p_xkb);
1415 gchar *flag_filepath = NULL;
1416 gchar *flag_filepath_cust = NULL;
1417 if(strchr(symbol_name_lowercase, '/') != NULL)
1418 {
1419 gchar *layout_mod = g_strdup(symbol_name_lowercase);
1420 layout_mod = g_strdelimit(layout_mod, "/", '-');
1421 flag_filepath = g_strdup_printf(flag_filepath_generator, FLAGSDIR, layout_mod);
1422 if(p_xkb->cust_dir_exists)
1423 flag_filepath_cust = g_strdup_printf(flag_filepath_generator, FLAGSCUSTDIR, layout_mod);
1424 g_free(layout_mod);
1425 }
1426 else
1427 {
1428 flag_filepath = g_strdup_printf(flag_filepath_generator, FLAGSDIR, symbol_name_lowercase);
1429 if(p_xkb->cust_dir_exists)
1430 flag_filepath_cust = g_strdup_printf(flag_filepath_generator, FLAGSCUSTDIR, symbol_name_lowercase);
1431 }
1432 GdkPixbuf *p_pixbuf = gdk_pixbuf_new_from_file_at_size(flag_filepath, -1, 16, NULL);
1433 if(p_pixbuf != NULL)
1434 {
1435 gtk_image_set_from_pixbuf(GTK_IMAGE(p_image_disp_type_image), p_pixbuf);
1436 g_object_unref(G_OBJECT(p_pixbuf));
1437 }
1438 if(flag_filepath_cust != NULL)
1439 {
1440 p_image_disp_type_image_cust = gtk_image_new();
1441 GdkPixbuf *p_pixbuf_cust = gdk_pixbuf_new_from_file_at_size(flag_filepath_cust, -1, 16, NULL);
1442 if(p_pixbuf_cust != NULL)
1443 {
1444 gtk_image_set_from_pixbuf(GTK_IMAGE(p_image_disp_type_image_cust), p_pixbuf_cust);
1445 g_object_unref(G_OBJECT(p_pixbuf_cust));
1446 }
1447 }
1448 g_free(flag_filepath);
1449 if(flag_filepath_cust != NULL)
1450 g_free(flag_filepath_cust);
1451 g_free(symbol_name_lowercase);
1452 GtkWidget * p_label_disp_type_text = gtk_label_new(NULL);
1453 snprintf(markup_str, MAX_MARKUP_LEN, "<span font='%d' font_weight='heavy'>%s</span>", 16, xkb_get_current_symbol_name(p_xkb));
1454 gtk_label_set_markup(GTK_LABEL(p_label_disp_type_text), markup_str);
1455 GtkWidget * p_radiobutton_disp_type_image = gtk_radio_button_new_with_label(NULL, (const gchar *)_("Image"));
1456 GtkWidget * p_radiobutton_disp_type_image_cust = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(p_radiobutton_disp_type_image), (const gchar *)_("Custom Image"));
1457 GtkWidget * p_radiobutton_disp_type_text = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(p_radiobutton_disp_type_image), (const gchar *)_("Text"));
1458
1459 gtk_table_attach(GTK_TABLE(p_table), p_image_disp_type_image, 0, 1, 0, 1, GTK_FILL, GTK_FILL, 0, 0);
1460 gtk_table_attach(GTK_TABLE(p_table), p_radiobutton_disp_type_image, 1, 2, 0, 1, GTK_FILL, GTK_FILL, 0, 0);
1461 if(p_xkb->cust_dir_exists)
1462 {
1463 gtk_table_attach(GTK_TABLE(p_table), p_image_disp_type_image_cust, 0, 1, 1, 2, GTK_FILL, GTK_FILL, 0, 0);
1464 gtk_table_attach(GTK_TABLE(p_table), p_radiobutton_disp_type_image_cust, 1, 2, 1, 2, GTK_FILL, GTK_FILL, 0, 0);
1465 }
1466 gtk_table_attach(GTK_TABLE(p_table), p_label_disp_type_text, 0, 1, 2, 3, GTK_FILL, GTK_FILL, 0, 0);
1467 gtk_table_attach(GTK_TABLE(p_table), p_radiobutton_disp_type_text, 1, 2, 2, 3, GTK_FILL, GTK_FILL, 0, 0);
1468
1469 g_signal_connect(p_radiobutton_disp_type_image, "toggled", G_CALLBACK(on_radiobutton_disp_type_image_toggled), p_xkb);
1470 if(p_xkb->cust_dir_exists)
1471 g_signal_connect(p_radiobutton_disp_type_image_cust, "toggled", G_CALLBACK(on_radiobutton_disp_type_image_cust_toggled), p_xkb);
1472 g_signal_connect(p_radiobutton_disp_type_text, "toggled", G_CALLBACK(on_radiobutton_disp_type_text_toggled), p_xkb);
1473 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p_radiobutton_disp_type_image), (p_xkb->display_type == DISP_TYPE_IMAGE) ||
1474 ( (p_xkb->display_type == DISP_TYPE_IMAGE_CUST) && (!p_xkb->cust_dir_exists) ) );
1475 if(p_xkb->cust_dir_exists)
1476 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p_radiobutton_disp_type_image_cust), p_xkb->display_type == DISP_TYPE_IMAGE_CUST);
1477 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p_radiobutton_disp_type_text), p_xkb->display_type == DISP_TYPE_TEXT);
1478
1479
1480 // 'PANEL ICON SIZE' frame
1481 GtkWidget * p_frame_flag_size = gtk_frame_new(NULL);
1482 GtkWidget * p_label_flag_size = gtk_label_new(NULL);
1483 snprintf(markup_str, MAX_MARKUP_LEN, "<b>%s</b>", _("Panel Icon Size"));
1484 gtk_label_set_markup(GTK_LABEL(p_label_flag_size), markup_str);
1485 gtk_misc_set_padding(GTK_MISC(p_label_flag_size), 1, 0);
1486 gtk_frame_set_label_widget(GTK_FRAME(p_frame_flag_size), p_label_flag_size);
1487 gtk_frame_set_shadow_type(GTK_FRAME(p_frame_flag_size), GTK_SHADOW_NONE);
1488 gtk_box_pack_start(GTK_BOX(p_vbox_right), p_frame_flag_size, TRUE, TRUE, 2);
1489 gtk_container_set_border_width(GTK_CONTAINER(p_frame_flag_size), 3);
1490
1491 // frame alignment
1492 GtkWidget * p_alignment_flag_size = gtk_alignment_new(0.5, 0.5, 1, 1);
1493 gtk_container_add(GTK_CONTAINER(p_frame_flag_size), p_alignment_flag_size);
1494 gtk_alignment_set_padding(GTK_ALIGNMENT(p_alignment_flag_size), 4, 4, 10, 10);
1495 GtkWidget *p_hbox_flag_size = gtk_hbox_new(FALSE, 2);
1496 gtk_container_add(GTK_CONTAINER(p_alignment_flag_size), p_hbox_flag_size);
1497
1498 GtkWidget *p_radiobutton_flag_size_1 = gtk_radio_button_new_with_label(NULL, "1");
1499 GtkWidget *p_radiobutton_flag_size_2 = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(p_radiobutton_flag_size_1), "2");
1500 GtkWidget *p_radiobutton_flag_size_3 = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(p_radiobutton_flag_size_1), "3");
1501 GtkWidget *p_radiobutton_flag_size_4 = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(p_radiobutton_flag_size_1), "4");
1502 GtkWidget *p_radiobutton_flag_size_5 = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(p_radiobutton_flag_size_1), "5");
1503 GtkWidget *p_radiobutton_flag_size_6 = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(p_radiobutton_flag_size_1), "6");
1504 g_signal_connect(p_radiobutton_flag_size_1, "toggled", G_CALLBACK(on_radiobutton_flag_size_1_toggled), p_xkb);
1505 g_signal_connect(p_radiobutton_flag_size_2, "toggled", G_CALLBACK(on_radiobutton_flag_size_2_toggled), p_xkb);
1506 g_signal_connect(p_radiobutton_flag_size_3, "toggled", G_CALLBACK(on_radiobutton_flag_size_3_toggled), p_xkb);
1507 g_signal_connect(p_radiobutton_flag_size_4, "toggled", G_CALLBACK(on_radiobutton_flag_size_4_toggled), p_xkb);
1508 g_signal_connect(p_radiobutton_flag_size_5, "toggled", G_CALLBACK(on_radiobutton_flag_size_5_toggled), p_xkb);
1509 g_signal_connect(p_radiobutton_flag_size_6, "toggled", G_CALLBACK(on_radiobutton_flag_size_6_toggled), p_xkb);
1510 gtk_box_pack_start(GTK_BOX(p_hbox_flag_size), p_radiobutton_flag_size_1, TRUE, TRUE, 2);
1511 gtk_box_pack_start(GTK_BOX(p_hbox_flag_size), p_radiobutton_flag_size_2, TRUE, TRUE, 2);
1512 gtk_box_pack_start(GTK_BOX(p_hbox_flag_size), p_radiobutton_flag_size_3, TRUE, TRUE, 2);
1513 gtk_box_pack_start(GTK_BOX(p_hbox_flag_size), p_radiobutton_flag_size_4, TRUE, TRUE, 2);
1514 gtk_box_pack_start(GTK_BOX(p_hbox_flag_size), p_radiobutton_flag_size_5, TRUE, TRUE, 2);
1515 gtk_box_pack_start(GTK_BOX(p_hbox_flag_size), p_radiobutton_flag_size_6, TRUE, TRUE, 2);
1516 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p_radiobutton_flag_size_1), p_xkb->flag_size == 1);
1517 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p_radiobutton_flag_size_2), p_xkb->flag_size == 2);
1518 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p_radiobutton_flag_size_3), p_xkb->flag_size == 3);
1519 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p_radiobutton_flag_size_4), p_xkb->flag_size == 4);
1520 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p_radiobutton_flag_size_5), p_xkb->flag_size == 5);
1521 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p_radiobutton_flag_size_6), p_xkb->flag_size == 6);
1522
1523 /* Connect signals. */
1524 g_signal_connect(dlg, "response", G_CALLBACK(on_dialog_config_response), p_xkb);
1525
1526 /* Display the dialog. */
1527 gtk_widget_show_all(p_hbox_main);
1528
1529 user_active = TRUE;
1530 return dlg;
1531 }
1532
1533 /* Callback when panel configuration changes. */
1534 static void xkb_panel_configuration_changed(LXPanel *panel, GtkWidget *p)
1535 {
1536 /* Do a full redraw. */
1537 xkb_redraw(lxpanel_plugin_get_data(p));
1538 }
1539
1540 FM_DEFINE_MODULE(lxpanel_gtk, xkb)
1541
1542 /* Plugin descriptor. */
1543 LXPanelPluginInit fm_module_init_lxpanel_gtk = {
1544 .name = N_("Keyboard Layout Handler"),
1545 .description = N_("Handle keyboard layouts"),
1546
1547 .new_instance = xkb_constructor,
1548 .config = xkb_configure,
1549 .reconfigure = xkb_panel_configuration_changed,
1550 .button_press_event = on_xkb_button_press_event
1551 };