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