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