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