2
* Copyright (C) 2010 Canonical Ltd.
3
* Copyright (C) 2010-2011 Sergey V. Udaltsov <svu@gnome.org>
5
* Authors: Jan Arne Petersen <jpetersen@openismus.com>
6
* Sergey V. Udaltsov <svu@gnome.org>
8
* This library is free software; you can redistribute it and/or
9
* modify it under the terms of the GNU Lesser General Public
10
* License as published by the Free Software Foundation; either
11
* version 2 of the License, or (at your option) any later version.
13
* This library is distributed in the hope that it will be useful,
14
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16
* Lesser General Public License for more details.
18
* You should have received a copy of the GNU Lesser General Public
19
* License along with this library; if not, write to the
20
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21
* Boston, MA 02111-1307, USA.
27
#include <gdk/gdkkeysyms.h>
29
#include <glib/gi18n-lib.h>
31
#include <gkbd-configuration.h>
33
#include <gkbd-desktop-config.h>
35
struct _GkbdConfigurationPrivate {
37
XklConfigRegistry *registry;
39
GkbdDesktopConfig cfg;
40
GkbdIndicatorConfig ind_cfg;
41
GkbdKeyboardConfig kbd_cfg;
43
gchar **full_group_names;
44
gchar **short_group_names;
46
const gchar *tooltips_format;
48
gulong state_changed_handler;
49
gulong config_changed_handler;
51
GSList *widget_instances;
55
Atom scroll_lock_atom;
61
SIGNAL_INDICATORS_CHANGED,
65
static guint signals[LAST_SIGNAL] = { 0, };
67
#define GKBD_CONFIGURATION_GET_PRIVATE(o) \
68
(G_TYPE_INSTANCE_GET_PRIVATE ((o), GKBD_TYPE_CONFIGURATION, GkbdConfigurationPrivate))
70
G_DEFINE_TYPE (GkbdConfiguration, gkbd_configuration, G_TYPE_OBJECT)
72
/* Should be called once for all widgets */
74
gkbd_configuration_cfg_changed (GSettings * settings, gchar * key,
75
GkbdConfiguration * configuration)
77
GkbdConfigurationPrivate *priv = configuration->priv;
80
"General configuration changed in GConf - reiniting...\n");
81
gkbd_desktop_config_load (&priv->cfg);
82
gkbd_desktop_config_activate (&priv->cfg);
84
g_signal_emit (configuration, signals[SIGNAL_CHANGED], 0);
87
/* Should be called once for all widgets */
89
gkbd_configuration_ind_cfg_changed (GSettings * settings, gchar * key,
90
GkbdConfiguration * configuration)
92
GkbdConfigurationPrivate *priv = configuration->priv;
94
"Applet configuration changed in GConf - reiniting...\n");
95
gkbd_indicator_config_load (&priv->ind_cfg);
97
gkbd_indicator_config_free_image_filenames (&priv->ind_cfg);
98
gkbd_indicator_config_load_image_filenames (&priv->ind_cfg,
101
gkbd_indicator_config_activate (&priv->ind_cfg);
103
g_signal_emit (configuration, signals[SIGNAL_CHANGED], 0);
107
gkbd_configuration_load_group_names (GkbdConfiguration * configuration,
108
XklConfigRec * xklrec)
110
GkbdConfigurationPrivate *priv = configuration->priv;
112
if (!gkbd_desktop_config_load_group_descriptions (&priv->cfg,
118
&priv->short_group_names,
119
&priv->full_group_names))
121
/* We just populate no short names (remain NULL) -
122
* full names are going to be used anyway */
123
gint i, total_groups =
124
xkl_engine_get_num_groups (priv->engine);
125
xkl_debug (150, "group descriptions loaded: %d!\n",
128
if (xkl_engine_get_features (priv->engine) &
129
XKLF_MULTIPLE_LAYOUTS_SUPPORTED) {
130
priv->full_group_names =
131
g_strdupv (priv->kbd_cfg.layouts_variants);
133
priv->full_group_names =
134
g_new0 (char *, total_groups + 1);
135
for (i = total_groups; --i >= 0;) {
136
priv->full_group_names[i] =
137
g_strdup_printf ("Group %d", i);
143
/* Should be called once for all widgets */
145
gkbd_configuration_kbd_cfg_callback (XklEngine * engine,
146
GkbdConfiguration * configuration)
148
GkbdConfigurationPrivate *priv = configuration->priv;
149
XklConfigRec *xklrec = xkl_config_rec_new ();
151
"XKB configuration changed on X Server - reiniting...\n");
153
gkbd_keyboard_config_load_from_x_current (&priv->kbd_cfg, xklrec);
155
gkbd_indicator_config_free_image_filenames (&priv->ind_cfg);
156
gkbd_indicator_config_load_image_filenames (&priv->ind_cfg,
159
g_strfreev (priv->full_group_names);
160
priv->full_group_names = NULL;
162
g_strfreev (priv->short_group_names);
163
priv->short_group_names = NULL;
165
gkbd_configuration_load_group_names (configuration, xklrec);
167
g_signal_emit (configuration, signals[SIGNAL_CHANGED], 0);
169
g_object_unref (G_OBJECT (xklrec));
172
/* Should be called once for all applets */
174
gkbd_configuration_state_callback (XklEngine * engine,
175
XklEngineStateChange changeType,
176
gint group, gboolean restore,
177
GkbdConfiguration * configuration)
179
xkl_debug (150, "change type: %d, group is now %d, restore: %d\n",
180
changeType, group, restore);
182
switch (changeType) {
184
g_signal_emit (configuration,
185
signals[SIGNAL_GROUP_CHANGED], 0, group);
187
case INDICATORS_CHANGED:
188
g_signal_emit (configuration,
189
signals[SIGNAL_INDICATORS_CHANGED], 0);
195
gkbd_configuration_init (GkbdConfiguration * configuration)
198
GkbdConfigurationPrivate *priv;
199
XklConfigRec *xklrec = xkl_config_rec_new ();
201
xkl_debug (100, "The config startup process for %p started\n",
204
priv = GKBD_CONFIGURATION_GET_PRIVATE (configuration);
205
configuration->priv = priv;
207
/* Initing some global vars */
208
priv->tooltips_format = "%s";
210
display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
211
priv->engine = xkl_engine_get_instance (display);
212
if (priv->engine == NULL) {
213
xkl_debug (0, "Libxklavier initialization error");
217
priv->caps_lock_atom = XInternAtom (display, "Caps Lock", False);
218
priv->num_lock_atom = XInternAtom (display, "Num Lock", False);
219
priv->scroll_lock_atom =
220
XInternAtom (display, "Scroll Lock", False);
222
priv->state_changed_handler =
223
g_signal_connect (priv->engine, "X-state-changed",
225
(gkbd_configuration_state_callback),
227
priv->config_changed_handler =
228
g_signal_connect (priv->engine, "X-config-changed",
230
(gkbd_configuration_kbd_cfg_callback),
233
gkbd_desktop_config_init (&priv->cfg, priv->engine);
234
gkbd_keyboard_config_init (&priv->kbd_cfg, priv->engine);
235
gkbd_indicator_config_init (&priv->ind_cfg, priv->engine);
237
gkbd_desktop_config_load (&priv->cfg);
238
gkbd_desktop_config_activate (&priv->cfg);
240
priv->registry = xkl_config_registry_get_instance (priv->engine);
241
xkl_config_registry_load (priv->registry,
242
priv->cfg.load_extra_items);
244
gkbd_keyboard_config_load_from_x_current (&priv->kbd_cfg, xklrec);
246
gkbd_indicator_config_load (&priv->ind_cfg);
248
gkbd_indicator_config_load_image_filenames (&priv->ind_cfg,
251
gkbd_indicator_config_activate (&priv->ind_cfg);
253
gkbd_configuration_load_group_names (configuration, xklrec);
254
g_object_unref (G_OBJECT (xklrec));
256
gkbd_desktop_config_start_listen (&priv->cfg,
258
(gkbd_configuration_cfg_changed),
260
gkbd_indicator_config_start_listen (&priv->ind_cfg,
262
(gkbd_configuration_ind_cfg_changed),
264
xkl_engine_start_listen (priv->engine, XKLL_TRACK_KEYBOARD_STATE);
266
xkl_debug (100, "The config startup process for %p completed\n",
271
gkbd_configuration_finalize (GObject * obj)
273
GkbdConfiguration *configuration = GKBD_CONFIGURATION (obj);
274
GkbdConfigurationPrivate *priv = configuration->priv;
277
"Starting the gnome-kbd-configuration widget shutdown process for %p\n",
280
xkl_engine_stop_listen (priv->engine, XKLL_TRACK_KEYBOARD_STATE);
282
gkbd_desktop_config_stop_listen (&priv->cfg);
283
gkbd_indicator_config_stop_listen (&priv->ind_cfg);
285
gkbd_indicator_config_term (&priv->ind_cfg);
286
gkbd_keyboard_config_term (&priv->kbd_cfg);
287
gkbd_desktop_config_term (&priv->cfg);
289
if (g_signal_handler_is_connected (priv->engine,
290
priv->state_changed_handler)) {
291
g_signal_handler_disconnect (priv->engine,
292
priv->state_changed_handler);
293
priv->state_changed_handler = 0;
295
if (g_signal_handler_is_connected (priv->engine,
296
priv->config_changed_handler)) {
297
g_signal_handler_disconnect (priv->engine,
298
priv->config_changed_handler);
299
priv->config_changed_handler = 0;
302
g_object_unref (priv->registry);
303
priv->registry = NULL;
304
g_object_unref (priv->engine);
307
G_OBJECT_CLASS (gkbd_configuration_parent_class)->finalize (obj);
311
gkbd_configuration_class_init (GkbdConfigurationClass * klass)
313
GObjectClass *object_class = G_OBJECT_CLASS (klass);
316
object_class->finalize = gkbd_configuration_finalize;
319
signals[SIGNAL_CHANGED] = g_signal_new ("changed",
320
GKBD_TYPE_CONFIGURATION,
324
g_cclosure_marshal_VOID__VOID,
326
signals[SIGNAL_GROUP_CHANGED] = g_signal_new ("group-changed",
327
GKBD_TYPE_CONFIGURATION,
331
g_cclosure_marshal_VOID__INT,
334
signals[SIGNAL_INDICATORS_CHANGED] =
335
g_signal_new ("indicators-changed", GKBD_TYPE_CONFIGURATION,
336
G_SIGNAL_RUN_LAST, 0, NULL, NULL,
337
g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
339
g_type_class_add_private (klass,
340
sizeof (GkbdConfigurationPrivate));
344
* gkbd_configuration_get:
345
* Returns: (transfer full): singleton of GkbdConfiguration
348
gkbd_configuration_get (void)
350
static gpointer instance = NULL;
353
instance = g_object_new (GKBD_TYPE_CONFIGURATION, NULL);
354
g_object_add_weak_pointer (instance, &instance);
356
g_object_ref (instance);
363
gkbd_configuration_get_xkl_engine (GkbdConfiguration * configuration)
365
return configuration->priv->engine;
369
* gkbd_configuration_get_group_names:
370
* Returns: (transfer none) (array zero-terminated=1): full group names
373
gkbd_configuration_get_group_names (GkbdConfiguration * configuration)
375
return configuration->priv->full_group_names;
379
gkbd_configuration_get_image_filename (GkbdConfiguration * configuration,
382
if (!configuration->priv->ind_cfg.show_flags)
384
return (gchar *) g_slist_nth_data (configuration->priv->
385
ind_cfg.image_filenames, group);
389
* gkbd_configuration_get_short_group_names:
390
* Returns: (transfer none) (array zero-terminated=1): short group names
393
gkbd_configuration_get_short_group_names (GkbdConfiguration *
396
return configuration->priv->short_group_names;
400
gkbd_configuration_get_current_tooltip (GkbdConfiguration * configuration)
403
xkl_engine_get_current_state (configuration->priv->engine);
405
if (state == NULL || state->group < 0
407
g_strv_length (configuration->priv->full_group_names))
410
return g_strdup_printf (configuration->priv->tooltips_format,
412
priv->full_group_names[state->group]);
416
gkbd_configuration_if_flags_shown (GkbdConfiguration * configuration)
418
return configuration->priv->ind_cfg.show_flags;
422
gkbd_configuration_extract_layout_name (GkbdConfiguration * configuration,
425
char *layout_name = NULL;
426
gchar **short_group_names = configuration->priv->short_group_names;
427
gchar **full_group_names = configuration->priv->full_group_names;
428
XklEngine *engine = configuration->priv->engine;
429
if (group < g_strv_length (short_group_names)) {
430
if (xkl_engine_get_features (engine) &
431
XKLF_MULTIPLE_LAYOUTS_SUPPORTED) {
432
char *full_layout_name =
433
configuration->priv->
434
kbd_cfg.layouts_variants[group];
436
if (!gkbd_keyboard_config_split_items
437
(full_layout_name, &layout_name,
440
layout_name = full_layout_name;
442
/* make it freeable */
443
layout_name = g_strdup (layout_name);
445
if (short_group_names != NULL) {
446
char *short_group_name =
447
short_group_names[group];
448
if (short_group_name != NULL
449
&& *short_group_name != '\0') {
450
/* drop the long name */
451
g_free (layout_name);
453
g_strdup (short_group_name);
457
layout_name = g_strdup (full_group_names[group]);
461
if (layout_name == NULL)
462
layout_name = g_strdup ("");
468
gkbd_configuration_lock_next_group (GkbdConfiguration * configuration)
470
gkbd_desktop_config_lock_next_group (&configuration->priv->cfg);
474
gkbd_configuration_lock_group (GkbdConfiguration * configuration,
477
xkl_engine_lock_group (configuration->priv->engine, group);
481
gkbd_configuration_get_current_group (GkbdConfiguration * configuration)
484
xkl_engine_get_current_state (configuration->priv->engine);
485
return state ? state->group : 0;
489
* gkbd_configuration_get_indicator_config:
490
* Returns: (transfer none): indicator config
492
GkbdIndicatorConfig *
493
gkbd_configuration_get_indicator_config (GkbdConfiguration * configuration)
495
return &configuration->priv->ind_cfg;
499
* gkbd_configuration_get_keyboard_config:
500
* Returns: (transfer none): keyboard config
503
gkbd_configuration_get_keyboard_config (GkbdConfiguration * configuration)
505
return &configuration->priv->kbd_cfg;
509
* gkbd_configuration_get_all_objects:
510
* Returns: (transfer none): list of widgets/status icons/...
513
gkbd_configuration_get_all_objects (GkbdConfiguration * configuration)
515
return configuration->priv->widget_instances;
519
gkbd_configuration_append_object (GkbdConfiguration * configuration,
522
configuration->priv->widget_instances =
523
g_slist_append (configuration->priv->widget_instances, obj);
527
gkbd_configuration_remove_object (GkbdConfiguration * configuration,
530
configuration->priv->widget_instances =
531
g_slist_remove (configuration->priv->widget_instances, obj);
535
* gkbd_configuration_load_images:
536
* Returns: (transfer full) (element-type GdkPixbuf): list of images
539
gkbd_configuration_load_images (GkbdConfiguration * configuration)
542
GSList *image_filename, *images = NULL;
544
if (!configuration->priv->ind_cfg.show_flags)
547
image_filename = configuration->priv->ind_cfg.image_filenames;
550
xkl_engine_get_max_num_groups (configuration->priv->engine);
551
--i >= 0; image_filename = image_filename->next) {
552
GdkPixbuf *image = NULL;
553
char *image_file = (char *) image_filename->data;
555
if (image_file != NULL) {
556
GError *gerror = NULL;
558
gdk_pixbuf_new_from_file (image_file, &gerror);
560
"Image %d[%s] loaded -> %p[%dx%d]\n",
561
i, image_file, image,
562
gdk_pixbuf_get_width (image),
563
gdk_pixbuf_get_height (image));
565
/* We append the image anyway - even if it is NULL! */
566
images = g_slist_append (images, image);
572
gkbd_configuration_free_images (GkbdConfiguration * configuration,
578
while ((img_node = images) != NULL) {
579
pi = GDK_PIXBUF (img_node->data);
580
/* It can be NULL - some images may be missing */
584
images = g_slist_remove_link (images, img_node);
585
g_slist_free_1 (img_node);
590
gkbd_configuration_create_label_title (int group, GHashTable ** ln2cnt_map,
593
gpointer pcounter = NULL;
594
char *prev_layout_name = NULL;
595
char *lbl_title = NULL;
600
g_hash_table_new_full (g_str_hash, g_str_equal,
604
/* Process layouts with repeating description */
605
if (g_hash_table_lookup_extended
606
(*ln2cnt_map, layout_name, (gpointer *) & prev_layout_name,
608
/* "next" same description */
609
gchar appendix[10] = "";
612
counter = GPOINTER_TO_INT (pcounter);
613
/* Unicode subscript 2, 3, 4 */
614
cidx = 0x2081 + counter;
615
utf8length = g_unichar_to_utf8 (cidx, appendix);
616
appendix[utf8length] = '\0';
617
lbl_title = g_strconcat (layout_name, appendix, NULL);
619
/* "first" time this description */
620
lbl_title = g_strdup (layout_name);
622
g_hash_table_insert (*ln2cnt_map, layout_name,
623
GINT_TO_POINTER (counter + 1));
628
gkbd_configuration_if_any_object_exists (GkbdConfiguration * configuration)
630
return (configuration != NULL)
631
&& (g_slist_length (configuration->priv->widget_instances) !=
635
static GdkFilterReturn
636
gkbd_configuration_filter_x_evt (GdkXEvent * xev, GdkEvent * event,
637
GkbdConfiguration * configuration)
639
xkl_engine_filter_events (configuration->priv->engine,
641
return GDK_FILTER_CONTINUE;
645
gkbd_configuration_start_listen (GkbdConfiguration * configuration)
647
gdk_window_add_filter (NULL, (GdkFilterFunc)
648
gkbd_configuration_filter_x_evt,
650
gdk_window_add_filter (gdk_get_default_root_window (),
652
gkbd_configuration_filter_x_evt,
657
gkbd_configuration_stop_listen (GkbdConfiguration * configuration)
659
gdk_window_remove_filter (NULL, (GdkFilterFunc)
660
gkbd_configuration_filter_x_evt,
662
gdk_window_remove_filter (gdk_get_default_root_window (),
664
gkbd_configuration_filter_x_evt,
669
* gkbd_configuration_get_group_name:
670
* Returns: (transfer full): group name
673
gkbd_configuration_get_group_name (GkbdConfiguration * configuration,
676
gchar *layout, *variant;
679
if (configuration == NULL)
682
lv = configuration->priv->kbd_cfg.layouts_variants;
683
if (group >= g_strv_length (lv))
686
if (gkbd_keyboard_config_split_items
687
(lv[group], &layout, &variant)) {
688
return g_strdup (layout);
694
gkbd_configuration_get_caps_lock_state (GkbdConfiguration * configuration)
698
GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
699
XkbGetNamedIndicator (display, configuration->priv->caps_lock_atom,
700
NULL, &state, NULL, NULL);
705
gkbd_configuration_get_num_lock_state (GkbdConfiguration * configuration)
709
GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
710
XkbGetNamedIndicator (display, configuration->priv->num_lock_atom,
711
NULL, &state, NULL, NULL);
716
gkbd_configuration_get_scroll_lock_state (GkbdConfiguration *
721
GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
722
XkbGetNamedIndicator (display,
723
configuration->priv->scroll_lock_atom, NULL,