Fix main icon usage
[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
HJYP
21
22#ifdef HAVE_CONFIG_H
23#include <config.h>
24#endif
25
26#include <glib/gi18n.h>
27
28#include <stdio.h>
45bd719e 29#include <stdlib.h>
22242ed4
HJYP
30#include <ctype.h>
31
32#include "xkb.h"
33
18ecfe2a 34void panel_config_save(Panel * panel); /* defined in configurator.c */
35
d862b520 36static void xkb_active_window_event(FbEv * ev, gpointer data);
37static gboolean xkb_scroll_event(GtkWidget * widget, GdkEventScroll * event, gpointer data);
38static gboolean xkb_button_press_event(GtkWidget * widget, GdkEventButton * event, gpointer data);
39static int xkb_constructor(Plugin * plugin, char ** fp);
40static void xkb_destructor(Plugin * plugin);
41static void xkb_display_type_changed(GtkComboBox * cb, gpointer * data);
42static void xkb_enable_per_application_changed(GtkToggleButton * tb, gpointer * data);
43static void xkb_default_language_changed(GtkComboBox * cb, gpointer * data);
44static void xkb_configuration_response(GtkDialog * dialog, gint arg1, gpointer data);
45static void xkb_configure(Plugin * p, GtkWindow * parent);
46static void xkb_save_configuration(Plugin * p, FILE * fp);
47static void xkb_panel_configuration_changed(Plugin * p);
48
49/* Redraw the graphics. */
50void xkb_redraw(XkbPlugin * xkb)
22242ed4 51{
d862b520 52 /* Set the image. */
53 gboolean valid_image = FALSE;
54 if (xkb->display_type == IMAGE)
22242ed4 55 {
d862b520 56 int size = xkb->plugin->panel->icon_size;
438d9fdb
MJ
57 char * group_name = (char *) xkb_get_current_symbol_name_lowercase(xkb);
58 if (group_name != NULL)
d862b520 59 {
438d9fdb
MJ
60 char * filename = g_strdup_printf("%s/%s.png", FLAGSDIR, group_name);
61 GdkPixbuf * unscaled_pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
62 g_free(filename);
63 g_free(group_name);
64
65 if (unscaled_pixbuf != NULL)
d862b520 66 {
438d9fdb
MJ
67 /* Loaded successfully. */
68 int width = gdk_pixbuf_get_width(unscaled_pixbuf);
69 int height = gdk_pixbuf_get_height(unscaled_pixbuf);
70 GdkPixbuf * pixbuf = gdk_pixbuf_scale_simple(unscaled_pixbuf, size * width / height, size, GDK_INTERP_BILINEAR);
71 if (pixbuf != NULL)
72 {
73 gtk_image_set_from_pixbuf(GTK_IMAGE(xkb->image), pixbuf);
74 g_object_unref(G_OBJECT(pixbuf));
75 gtk_widget_hide(xkb->label);
76 gtk_widget_show(xkb->image);
77 gtk_widget_set_tooltip_text(xkb->btn, xkb_get_current_group_name(xkb));
78 valid_image = TRUE;
79 }
80 g_object_unref(unscaled_pixbuf);
22242ed4
HJYP
81 }
82 }
83 }
84
d862b520 85 /* Set the label. */
86 if ((xkb->display_type == TEXT) || ( ! valid_image))
87 {
438d9fdb
MJ
88 char * group_name = (char *) xkb_get_current_symbol_name(xkb);
89 if (group_name != NULL)
90 {
91 panel_draw_label_text(xkb->plugin->panel, xkb->label, (char *) group_name, TRUE, TRUE);
92 gtk_widget_hide(xkb->image);
93 gtk_widget_show(xkb->label);
94 gtk_widget_set_tooltip_text(xkb->btn, xkb_get_current_group_name(xkb));
95 }
d862b520 96 }
22242ed4
HJYP
97}
98
d862b520 99/* Handler for "active_window" event on root window listener. */
100static void xkb_active_window_event(FbEv * ev, gpointer data)
22242ed4 101{
d862b520 102 XkbPlugin * xkb = (XkbPlugin *) data;
103 if (xkb->enable_perapp)
104 {
105 Window * win = fb_ev_active_window(ev);
106 if (*win != None)
107 {
a6ef237e 108 xkb_active_window_changed(xkb, *win);
d862b520 109 xkb_redraw(xkb);
110 }
111 }
22242ed4
HJYP
112}
113
d862b520 114/* Handler for "scroll-event" on drawing area. */
115static gboolean xkb_scroll_event(GtkWidget * widget, GdkEventScroll * event, gpointer data)
22242ed4 116{
d862b520 117 XkbPlugin * xkb = (XkbPlugin *) data;
22242ed4 118
d862b520 119 /* Change to next or previous group. */
120 xkb_change_group(xkb,
121 (((event->direction == GDK_SCROLL_UP) || (event->direction == GDK_SCROLL_RIGHT)) ? 1 : -1));
122 return TRUE;
22242ed4
HJYP
123}
124
d862b520 125/* Handler for button-press-event on top level widget. */
126static gboolean xkb_button_press_event(GtkWidget * widget, GdkEventButton * event, gpointer data)
22242ed4 127{
d862b520 128 XkbPlugin * xkb = (XkbPlugin *) data;
22242ed4 129
d862b520 130 /* Standard right-click handling. */
131 if (plugin_button_press_event(widget, event, xkb->plugin))
132 return TRUE;
22242ed4 133
d862b520 134 /* Change to next group. */
135 xkb_change_group(xkb, 1);
18ecfe2a 136 return TRUE;
22242ed4
HJYP
137}
138
d862b520 139/* Plugin constructor. */
140static int xkb_constructor(Plugin * plugin, char ** fp)
22242ed4 141{
d862b520 142 /* Allocate plugin context and set into Plugin private data pointer. */
143 XkbPlugin * xkb = g_new0(XkbPlugin, 1);
144 xkb->plugin = plugin;
145 plugin->priv = xkb;
146
147 /* Initialize to defaults. */
148 xkb->display_type = IMAGE;
149 xkb->enable_perapp = TRUE;
150 xkb->default_group = 0;
151
152 /* Load parameters from the configuration file. */
153 line s;
154 s.len = 256;
155 if (fp != NULL)
156 {
157 while (lxpanel_get_line(fp, &s) != LINE_BLOCK_END)
158 {
159 if (s.type == LINE_NONE)
160 {
161 ERR( "xkb: illegal token %s\n", s.str);
162 return 0;
163 }
164 if (s.type == LINE_VAR)
165 {
166 if (g_ascii_strcasecmp(s.t[0], "DisplayType") == 0)
167 xkb->display_type = atoi(s.t[1]);
168 else if (g_ascii_strcasecmp(s.t[0], "PerAppLayout") == 0)
169 xkb->enable_perapp = str2num(bool_pair, s.t[1], 0);
170 else if (g_ascii_strcasecmp(s.t[0], "DefaultGroup") == 0)
171 xkb->default_group = atoi(s.t[1]);
172 else
173 ERR( "xkb: unknown var %s\n", s.t[0]);
174 }
175 else
176 {
177 ERR( "xkb: illegal in this context %s\n", s.str);
178 return 0;
179 }
180 }
181 }
2918994e 182
d862b520 183 /* Allocate top level widget and set into Plugin widget pointer. */
184 plugin->pwid = gtk_event_box_new();
185 gtk_widget_add_events(plugin->pwid, GDK_BUTTON_PRESS_MASK);
186
187 /* Create a button as the child of the event box. */
188 xkb->btn = gtk_button_new();
189 gtk_container_add(GTK_CONTAINER(plugin->pwid), xkb->btn);
190 gtk_button_set_relief(GTK_BUTTON(xkb->btn), GTK_RELIEF_NONE);
191 GTK_WIDGET_UNSET_FLAGS(xkb->btn, GTK_CAN_FOCUS);
192 GTK_WIDGET_UNSET_FLAGS(xkb->btn, GTK_CAN_DEFAULT);
193 gtk_widget_show(xkb->btn);
194
195 /* Create a horizontal box as the child of the button. */
196 GtkWidget * hbox = gtk_hbox_new(FALSE, 0);
197 gtk_container_add(GTK_CONTAINER(xkb->btn), hbox);
198 gtk_widget_show(hbox);
199
200 /* Create a label and an image as children of the horizontal box.
201 * Only one of these is visible at a time, controlled by user preference
202 * and the successful loading of the image. */
203 xkb->label = gtk_label_new("");
204 gtk_container_add(GTK_CONTAINER(hbox), xkb->label);
205 xkb->image = gtk_image_new();
206 gtk_container_add(GTK_CONTAINER(hbox), xkb->image);
207
208 /* Initialize the XKB interface. */
209 xkb_mechanism_constructor(xkb);
210
d862b520 211 /* Connect signals. */
212 g_signal_connect(xkb->btn, "button-press-event", G_CALLBACK(xkb_button_press_event), xkb);
213 g_signal_connect(xkb->btn, "scroll-event", G_CALLBACK(xkb_scroll_event), xkb);
214 g_signal_connect(G_OBJECT(fbev), "active_window", G_CALLBACK(xkb_active_window_event), xkb);
215
216 /* Show the widget and return. */
217 xkb_redraw(xkb);
218 gtk_widget_show(plugin->pwid);
219 return 1;
22242ed4
HJYP
220}
221
d862b520 222/* Plugin destructor. */
223static void xkb_destructor(Plugin * plugin)
22242ed4 224{
d862b520 225 XkbPlugin * xkb = (XkbPlugin *) plugin->priv;
22242ed4 226
d862b520 227 /* Disconnect root window event handler. */
228 g_signal_handlers_disconnect_by_func(G_OBJECT(fbev), xkb_active_window_event, xkb);
22242ed4 229
d862b520 230 /* Disconnect from the XKB mechanism. */
231 g_source_remove(xkb->source_id);
232 xkb_mechanism_destructor(xkb);
22242ed4 233
d862b520 234 /* Ensure that the configuration dialog is dismissed. */
235 if (xkb->config_dlg != NULL)
236 gtk_widget_destroy(xkb->config_dlg);
22242ed4 237
d862b520 238 /* Deallocate all memory. */
239 g_free(xkb);
22242ed4
HJYP
240}
241
d862b520 242/* Handler for "changed" event on default language combo box of configuration dialog. */
243static void xkb_display_type_changed(GtkComboBox * cb, gpointer * data)
22242ed4 244{
d862b520 245 /* Fetch the new value and redraw. */
246 XkbPlugin * xkb = (XkbPlugin *) data;
247 xkb->display_type = gtk_combo_box_get_active(cb);
248 xkb_redraw(xkb);
22242ed4
HJYP
249}
250
d862b520 251/* Handler for "toggled" event on per-application check box of configuration dialog. */
252static void xkb_enable_per_application_changed(GtkToggleButton * tb, gpointer * data)
22242ed4 253{
d862b520 254 /* Fetch the new value and redraw. */
255 XkbPlugin * xkb = (XkbPlugin *) data;
256 xkb->enable_perapp = gtk_toggle_button_get_active(tb);
257 gtk_widget_set_sensitive(xkb->per_app_default_layout_menu, xkb->enable_perapp);
258 xkb_redraw(xkb);
22242ed4
HJYP
259}
260
d862b520 261/* Handler for "changed" event on default language combo box of configuration dialog. */
262static void xkb_default_language_changed(GtkComboBox * cb, gpointer * data)
22242ed4 263{
d862b520 264 /* Fetch the new value and redraw. */
265 XkbPlugin * xkb = (XkbPlugin *) data;
266 xkb->default_group = gtk_combo_box_get_active(cb);
267 xkb_redraw(xkb);
22242ed4
HJYP
268}
269
d862b520 270/* Handler for "response" event on configuration dialog. */
271static void xkb_configuration_response(GtkDialog * dialog, int response, gpointer data)
22242ed4 272{
d862b520 273 XkbPlugin * xkb = (XkbPlugin *) data;
22242ed4 274
d862b520 275 /* Save the new configuration and redraw the plugin. */
276 panel_config_save(xkb->plugin->panel);
277 xkb_redraw(xkb);
22242ed4 278
d862b520 279 /* Destroy the dialog. */
280 gtk_widget_destroy(xkb->config_dlg);
281 xkb->config_dlg = NULL;
22242ed4
HJYP
282}
283
d862b520 284/* Callback when the configuration dialog is to be shown. */
285static void xkb_configure(Plugin * p, GtkWindow * parent)
22242ed4 286{
d862b520 287 XkbPlugin * xkb = (XkbPlugin *) p->priv;
288
289 /* Create dialog window. */
290 GtkWidget * dlg = gtk_dialog_new_with_buttons(
291 _("Configure Keyboard Layout Switcher"),
292 NULL,
293 GTK_DIALOG_NO_SEPARATOR,
294 GTK_STOCK_CLOSE,
295 GTK_RESPONSE_OK,
296 NULL);
297 xkb->config_dlg = dlg;
298 panel_apply_icon(GTK_WINDOW(dlg));
299
300 /* Create a vertical box as the child of the dialog. */
301 GtkWidget * vbox = gtk_vbox_new(FALSE, 2);
302 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dlg)->vbox), vbox);
303
304 /* Create a frame as the child of the vertical box. */
305 GtkWidget * display_type_frame = gtk_frame_new(NULL);
306 gtk_frame_set_label(GTK_FRAME(display_type_frame), _("Show layout as"));
307 gtk_box_pack_start(GTK_BOX(vbox), display_type_frame, TRUE, TRUE, 2);
308 gtk_container_set_border_width(GTK_CONTAINER(display_type_frame), 5);
309
310 /* Create an alignment as the child of the frame. */
311 GtkWidget * alignment2 = gtk_alignment_new(0.5, 0.5, 1, 1);
312 gtk_container_add(GTK_CONTAINER(display_type_frame), alignment2);
313 gtk_alignment_set_padding(GTK_ALIGNMENT(alignment2), 4, 4, 10, 10);
22242ed4 314
d862b520 315 /* Create a horizontal box as the child of the alignment. */
316 GtkWidget * hbox = gtk_hbox_new(FALSE, 2);
317 gtk_container_add(GTK_CONTAINER(alignment2), hbox);
318
319 /* Create a combo box as the child of the horizontal box. */
320 GtkWidget * display_type_optmenu = gtk_combo_box_new_text();
321 gtk_combo_box_append_text(GTK_COMBO_BOX(display_type_optmenu), _("image"));
322 gtk_combo_box_append_text(GTK_COMBO_BOX(display_type_optmenu), _("text"));
323 gtk_box_pack_start(GTK_BOX(hbox), display_type_optmenu, TRUE, TRUE, 2);
324 g_signal_connect(display_type_optmenu, "changed", G_CALLBACK(xkb_display_type_changed), xkb);
325 gtk_combo_box_set_active(GTK_COMBO_BOX(display_type_optmenu), xkb->display_type);
326
327 /* Create a frame as the child of the vertical box. */
328 GtkWidget * per_app_frame = gtk_frame_new(NULL);
329 gtk_frame_set_label(GTK_FRAME(per_app_frame), _("Per application settings"));
330 gtk_widget_show(per_app_frame);
331 gtk_box_pack_start(GTK_BOX(vbox), per_app_frame, TRUE, TRUE, 2);
332 gtk_container_set_border_width(GTK_CONTAINER(per_app_frame), 5);
333
334 /* Create an alignment as the child of the frame. */
335 GtkWidget * alignment1 = gtk_alignment_new(0.5, 0.5, 1, 1);
336 gtk_container_add(GTK_CONTAINER(per_app_frame), alignment1);
337 gtk_alignment_set_padding(GTK_ALIGNMENT(alignment1), 4, 4, 10, 10);
338
339 /* Create a vertical box as the child of the alignment. */
340 GtkWidget * per_app_vbox = gtk_vbox_new(FALSE, 2);
341 gtk_container_add(GTK_CONTAINER(alignment1), per_app_vbox);
342
343 /* Create a check button as the child of the vertical box. */
344 GtkWidget * per_app_checkbutton = gtk_check_button_new_with_mnemonic(_("_Remember layout for each application"));
345 gtk_box_pack_start(GTK_BOX(per_app_vbox), per_app_checkbutton, FALSE, FALSE, 2);
346 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(per_app_checkbutton), xkb->enable_perapp);
347 g_signal_connect(per_app_checkbutton, "toggled", G_CALLBACK(xkb_enable_per_application_changed), xkb);
348
349 /* Create a horizontal box as the child of the vertical box. */
350 GtkWidget * hbox3 = gtk_hbox_new(FALSE, 2);
351 gtk_box_pack_start(GTK_BOX(per_app_vbox), hbox3, TRUE, TRUE, 2);
352
353 /* Create a label as the child of the horizontal box. */
354 GtkWidget * label4 = gtk_label_new(_("Default layout:"));
355 gtk_box_pack_start(GTK_BOX(hbox3), label4, FALSE, FALSE, 2);
356
357 /* Create a combo box as the child of the horizontal box. */
358 xkb->per_app_default_layout_menu = gtk_combo_box_new_text();
359 gtk_box_pack_start(GTK_BOX(hbox3), xkb->per_app_default_layout_menu, FALSE, TRUE, 2);
360 gtk_widget_set_sensitive(xkb->per_app_default_layout_menu, xkb->enable_perapp);
361
362 /* Populate the combo box with the available choices. */
363 int i;
364 for (i = 0; i < xkb_get_group_count(xkb); i++)
365 {
366 gtk_combo_box_append_text(
367 GTK_COMBO_BOX(xkb->per_app_default_layout_menu),
368 xkb_get_symbol_name_by_res_no(xkb, i));
369 }
370 gtk_combo_box_set_active(GTK_COMBO_BOX(xkb->per_app_default_layout_menu), xkb->default_group);
371 g_signal_connect(xkb->per_app_default_layout_menu, "changed", G_CALLBACK(xkb_default_language_changed), xkb);
22242ed4 372
d862b520 373 /* Connect signals. */
374 g_signal_connect(xkb->config_dlg, "response", G_CALLBACK(xkb_configuration_response), xkb);
22242ed4 375
d862b520 376 /* Display the dialog. */
377 gtk_widget_set_size_request(GTK_WIDGET(xkb->config_dlg), 400, -1); /* Improve geometry */
378 gtk_widget_show_all(xkb->config_dlg);
379 gtk_window_present(GTK_WINDOW(xkb->config_dlg));
22242ed4
HJYP
380}
381
8f9e6256 382/* Callback when the configuration is to be saved. */
383static void xkb_save_configuration(Plugin * p, FILE * fp)
384{
d862b520 385 XkbPlugin * xkb = (XkbPlugin *) p->priv;
8f9e6256 386 lxpanel_put_int(fp, "DisplayType", xkb->display_type);
387 lxpanel_put_int(fp, "PerAppLayout", xkb->enable_perapp);
388 lxpanel_put_int(fp, "DefaultGroup", xkb->default_group);
389}
390
391/* Callback when panel configuration changes. */
392static void xkb_panel_configuration_changed(Plugin * p)
393{
394 /* Do a full redraw. */
d862b520 395 XkbPlugin * xkb = (XkbPlugin *) p->priv;
396 xkb_redraw(xkb);
8f9e6256 397}
22242ed4 398
d862b520 399/* Plugin descriptor. */
22242ed4 400PluginClass xkb_plugin_class = {
2918994e 401
402 PLUGINCLASS_VERSIONING,
22242ed4
HJYP
403
404 type : "xkb",
2918994e 405 name : N_("Keyboard Layout Switcher"),
22242ed4
HJYP
406 version: "1.0",
407 description : N_("Switch between available keyboard layouts"),
408
409 constructor : xkb_constructor,
410 destructor : xkb_destructor,
8f9e6256 411 config : xkb_configure,
412 save : xkb_save_configuration,
413 panel_configuration_changed : xkb_panel_configuration_changed
d862b520 414
22242ed4 415};