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