Add XAppKbdLayoutController - this is a wrapper around libgnomekbd api to
provide unique icons for each layout.
Michael Webster
7 years ago
46 | 46 | GDK_PIXBUF_REQUIRED=2.22.0 |
47 | 47 | GTK_REQUIRED=3.3.16 |
48 | 48 | GLIB_REQUIRED=2.37.3 |
49 | CLUTTER_REQUIRED=1.10 | |
49 | 50 | |
50 | 51 | AC_SUBST(GTK_REQUIRED) |
51 | 52 | AC_SUBST(GLIB_REQUIRED) |
68 | 69 | dnl pkg-config dependency checks |
69 | 70 | |
70 | 71 | PKG_CHECK_MODULES(XAPP, gdk-pixbuf-2.0 >= $GDK_PIXBUF_REQUIRED |
71 | gtk+-3.0 >= $GTK_REQUIRED | |
72 | glib-2.0 >= $GLIB_REQUIRED | |
73 | gio-2.0 >= $GLIB_REQUIRED) | |
72 | gtk+-3.0 >= $GTK_REQUIRED | |
73 | glib-2.0 >= $GLIB_REQUIRED | |
74 | gio-2.0 >= $GLIB_REQUIRED | |
75 | cairo | |
76 | libgnomekbdui | |
77 | clutter-1.0 >= CLUTTER_REQUIRED) | |
74 | 78 | |
75 | 79 | dnl Language Support |
76 | 80 |
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
13 | 13 | AM_CFLAGS = $(WARN_CFLAGS) |
14 | 14 | |
15 | 15 | introspection_sources = \ |
16 | xapp-display.c | |
16 | xapp-display.c \ | |
17 | xapp-kbd-layout-controller.c | |
17 | 18 | |
18 | 19 | libxapp_la_SOURCES = \ |
19 | 20 | $(introspection_sources) |
33 | 34 | |
34 | 35 | libxappdir = $(includedir)/xapp/libxapp |
35 | 36 | libxapp_HEADERS = \ |
36 | xapp-display.h | |
37 | xapp-display.h \ | |
38 | xapp-kbd-layout-controller.h | |
37 | 39 | |
38 | 40 | -include $(INTROSPECTION_MAKEFILE) |
39 | 41 | INTROSPECTION_GIRS = |
43 | 45 | if HAVE_INTROSPECTION |
44 | 46 | |
45 | 47 | XApp-1.0.gir: libxapp.la |
46 | XApp_1_0_gir_INCLUDES = GObject-2.0 Gtk-3.0 | |
47 | XApp_1_0_gir_PACKAGES = gdk-pixbuf-2.0 glib-2.0 gobject-2.0 gio-2.0 gtk+-3.0 | |
48 | XApp_1_0_gir_INCLUDES = GObject-2.0 Gtk-3.0 Clutter-1.0 | |
49 | XApp_1_0_gir_PACKAGES = gdk-pixbuf-2.0 glib-2.0 gobject-2.0 gio-2.0 gtk+-3.0 clutter-1.0 | |
48 | 50 | XApp_1_0_gir_EXPORT_PACKAGES = xapp |
49 | 51 | XApp_1_0_gir_CFLAGS = -DGNOME_DESKTOP_USE_UNSTABLE_API -I$(top_srcdir) |
50 | 52 | XApp_1_0_gir_LIBS = libxapp.la |
0 | ||
1 | #include <config.h> | |
2 | ||
3 | #include <stdio.h> | |
4 | #include <stdlib.h> | |
5 | #include <string.h> | |
6 | #include <math.h> | |
7 | ||
8 | #include <glib/gstdio.h> | |
9 | #include <gdk/gdk.h> | |
10 | #include <gtk/gtk.h> | |
11 | #include <cairo.h> | |
12 | ||
13 | #include <libgnomekbd/gkbd-configuration.h> | |
14 | ||
15 | #include "xapp-kbd-layout-controller.h" | |
16 | ||
17 | enum | |
18 | { | |
19 | PROP_0, | |
20 | ||
21 | PROP_ENABLED, | |
22 | PROP_USE_CAPS, | |
23 | }; | |
24 | ||
25 | enum | |
26 | { | |
27 | KBD_LAYOUT_CHANGED, | |
28 | KBD_CONFIG_CHANGED, | |
29 | ||
30 | LAST_SIGNAL | |
31 | }; | |
32 | ||
33 | static guint signals[LAST_SIGNAL] = { 0, }; | |
34 | ||
35 | struct _XAppKbdLayoutControllerPrivate | |
36 | { | |
37 | GkbdConfiguration *config; | |
38 | ||
39 | gint num_groups; | |
40 | gchar *flag_dir; | |
41 | gchar *temp_flag_theme_dir; | |
42 | ||
43 | gchar *icon_names[4]; | |
44 | gchar *text_store[4]; | |
45 | ||
46 | gulong changed_id; | |
47 | gulong group_changed_id; | |
48 | guint idle_changed_id; | |
49 | ||
50 | gboolean enabled; | |
51 | }; | |
52 | ||
53 | G_DEFINE_TYPE (XAppKbdLayoutController, xapp_kbd_layout_controller, G_TYPE_OBJECT); | |
54 | ||
55 | static void | |
56 | initialize_flag_dir (XAppKbdLayoutController *controller) | |
57 | { | |
58 | XAppKbdLayoutControllerPrivate *priv = controller->priv; | |
59 | gint i; | |
60 | ||
61 | const char * const * data_dirs; | |
62 | ||
63 | data_dirs = g_get_system_data_dirs (); | |
64 | ||
65 | for (i = 0; data_dirs[i] != NULL; i++) | |
66 | { | |
67 | gchar *try_path = g_build_filename (data_dirs[i], "xapps", "flags", NULL); | |
68 | ||
69 | if (g_file_test (try_path, G_FILE_TEST_EXISTS)) | |
70 | { | |
71 | priv->flag_dir = g_strdup (try_path); | |
72 | break; | |
73 | } | |
74 | ||
75 | g_free (try_path); | |
76 | } | |
77 | } | |
78 | ||
79 | static void | |
80 | initialize_icon_theme (XAppKbdLayoutController *controller) | |
81 | { | |
82 | XAppKbdLayoutControllerPrivate *priv = controller->priv; | |
83 | ||
84 | const gchar *cache_dir = g_get_user_cache_dir (); | |
85 | ||
86 | gchar *path = g_build_filename (cache_dir, "xapp-kbd-layout-controller", NULL); | |
87 | ||
88 | g_mkdir_with_parents (path, 0700); | |
89 | ||
90 | priv->temp_flag_theme_dir = path; | |
91 | ||
92 | gtk_icon_theme_append_search_path (gtk_icon_theme_get_default (), path); | |
93 | } | |
94 | ||
95 | static void | |
96 | clear_stores (XAppKbdLayoutController *controller) | |
97 | { | |
98 | XAppKbdLayoutControllerPrivate *priv = controller->priv; | |
99 | ||
100 | gint i; | |
101 | ||
102 | for (i = 0; i < 4; i++) | |
103 | { | |
104 | g_clear_pointer (&priv->text_store[i], g_free); | |
105 | g_clear_pointer (&priv->icon_names[i], g_free); | |
106 | } | |
107 | } | |
108 | ||
109 | typedef struct | |
110 | { | |
111 | gchar *group; | |
112 | gint id; | |
113 | } GroupData; | |
114 | ||
115 | static void | |
116 | group_data_free (GroupData *data) | |
117 | { | |
118 | g_clear_pointer (&data->group, g_free); | |
119 | data->id = 0; | |
120 | ||
121 | g_slice_free (GroupData, data); | |
122 | } | |
123 | ||
124 | static gchar * | |
125 | create_text (XAppKbdLayoutController *controller, | |
126 | const gchar *name, | |
127 | gint id) | |
128 | { | |
129 | if (g_utf8_validate (name, -1, NULL)) | |
130 | { | |
131 | gchar utf8[20]; | |
132 | gchar *utf8_cased = NULL; | |
133 | ||
134 | g_utf8_strncpy (utf8, name, 2); | |
135 | ||
136 | utf8_cased = g_utf8_strdown (utf8, -1); | |
137 | ||
138 | GString *string = g_string_new (utf8_cased); | |
139 | ||
140 | g_free (utf8_cased); | |
141 | ||
142 | if (id > 0) | |
143 | { | |
144 | string = g_string_append_unichar (string, 0x2080 + id); | |
145 | } | |
146 | ||
147 | return g_string_free (string, FALSE); | |
148 | } | |
149 | ||
150 | return NULL; | |
151 | } | |
152 | ||
153 | static GdkPixbuf * | |
154 | add_notation (GdkPixbuf *original, gint id) | |
155 | { | |
156 | gint width, height; | |
157 | cairo_surface_t *surface; | |
158 | cairo_t *cr; | |
159 | ||
160 | width = gdk_pixbuf_get_width (original); | |
161 | height = gdk_pixbuf_get_height (original); | |
162 | ||
163 | surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); | |
164 | ||
165 | if (surface == NULL) | |
166 | { | |
167 | return original; | |
168 | } | |
169 | ||
170 | cr = cairo_create (surface); | |
171 | ||
172 | gdk_cairo_set_source_pixbuf (cr, original, 0, 0); | |
173 | cairo_paint (cr); | |
174 | ||
175 | gint rx, ry, rw, rh; | |
176 | ||
177 | rx = rw = width / 2; | |
178 | ry = rh = height / 2; | |
179 | ||
180 | cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, .5); | |
181 | cairo_rectangle (cr, rx, ry, rw, rh); | |
182 | cairo_fill (cr); | |
183 | ||
184 | cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, .8); | |
185 | cairo_rectangle (cr, rx - 1, ry - 1, rw, rh); | |
186 | cairo_fill (cr); | |
187 | ||
188 | cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 1.0); | |
189 | ||
190 | gchar *num_string = g_strdup_printf ("%d", id); | |
191 | cairo_select_font_face (cr, "sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); | |
192 | cairo_set_font_size (cr, height / 2); | |
193 | ||
194 | cairo_text_extents_t ext; | |
195 | cairo_text_extents (cr, num_string, &ext); | |
196 | ||
197 | cairo_move_to (cr, rx + (rw / 2) - (ext.width / 2) - 1, ry + (rh / 2) + (ext.height / 2) - 1); | |
198 | ||
199 | cairo_show_text (cr, num_string); | |
200 | g_free (num_string); | |
201 | ||
202 | GdkPixbuf *ret = gdk_pixbuf_get_from_surface (surface, 0, 0, width, height); | |
203 | ||
204 | cairo_surface_destroy (surface); | |
205 | cairo_destroy (cr); | |
206 | ||
207 | if (ret == NULL) | |
208 | { | |
209 | return original; | |
210 | } | |
211 | ||
212 | g_object_unref (original); | |
213 | ||
214 | return ret; | |
215 | } | |
216 | ||
217 | static gchar * | |
218 | create_pixbuf (XAppKbdLayoutController *controller, | |
219 | guint group, | |
220 | const gchar *name, | |
221 | gint id) | |
222 | { | |
223 | XAppKbdLayoutControllerPrivate *priv = controller->priv; | |
224 | GdkPixbuf *pixbuf = NULL; | |
225 | ||
226 | GdkPixbuf *flag_pixbuf; | |
227 | ||
228 | gchar *filename = g_strdup_printf ("%s.png", name); | |
229 | gchar *full_path = g_build_filename (priv->flag_dir, filename, NULL); | |
230 | ||
231 | flag_pixbuf = gdk_pixbuf_new_from_file (full_path, NULL); | |
232 | ||
233 | g_free (filename); | |
234 | g_free (full_path); | |
235 | ||
236 | if (flag_pixbuf != NULL) | |
237 | { | |
238 | if (id == 0) | |
239 | { | |
240 | pixbuf = flag_pixbuf; | |
241 | } | |
242 | else | |
243 | { | |
244 | pixbuf = add_notation (flag_pixbuf, id); | |
245 | } | |
246 | } | |
247 | ||
248 | if (flag_pixbuf != NULL) | |
249 | { | |
250 | gchar *save_name = g_strdup_printf ("xapp-kbd-layout-%d.png", group); | |
251 | ||
252 | gchar *path = g_build_filename (priv->temp_flag_theme_dir, save_name, NULL); | |
253 | g_remove (path); | |
254 | ||
255 | gdk_pixbuf_save (pixbuf, | |
256 | path, | |
257 | "png", | |
258 | NULL, | |
259 | NULL); | |
260 | ||
261 | g_object_unref (pixbuf); | |
262 | ||
263 | g_free (save_name); | |
264 | g_free (path); | |
265 | } | |
266 | else | |
267 | { | |
268 | return NULL; | |
269 | } | |
270 | ||
271 | return g_strdup_printf ("xapp-kbd-layout-%d", group); | |
272 | } | |
273 | ||
274 | static void | |
275 | load_stores (XAppKbdLayoutController *controller) | |
276 | { | |
277 | XAppKbdLayoutControllerPrivate *priv = controller->priv; | |
278 | ||
279 | gchar **group_names = gkbd_configuration_get_group_names (priv->config); | |
280 | priv->num_groups = g_strv_length (group_names); | |
281 | ||
282 | /* We do nothing if there's only one keyboard layout enabled */ | |
283 | if (priv->num_groups == 1) | |
284 | { | |
285 | priv->enabled = FALSE; | |
286 | return; | |
287 | } | |
288 | ||
289 | priv->enabled = TRUE; | |
290 | ||
291 | /* Make a list of [name, id] tuples, where name is the group/flag name, | |
292 | * and id is either 0, or, if a flag name is duplicated, a 1, 2, 3, etc... | |
293 | */ | |
294 | gint i, j, id; | |
295 | GPtrArray *list = g_ptr_array_new_with_free_func ((GDestroyNotify) group_data_free); | |
296 | ||
297 | for (i = 0; i < priv->num_groups; i++) | |
298 | { | |
299 | GroupData *data = g_slice_new0 (GroupData); | |
300 | ||
301 | gchar *name = gkbd_configuration_get_group_name (priv->config, i); | |
302 | id = 0; | |
303 | ||
304 | for (j = 0; j < list->len; j++) | |
305 | { | |
306 | GroupData *iter = g_ptr_array_index (list, j); | |
307 | ||
308 | if (g_strcmp0 (name, iter->group) == 0) | |
309 | { | |
310 | id++; | |
311 | iter->id = id; | |
312 | } | |
313 | } | |
314 | ||
315 | if (id > 0) | |
316 | { | |
317 | id++; | |
318 | } | |
319 | ||
320 | data->group = name; | |
321 | data->id = id; | |
322 | ||
323 | g_ptr_array_add (list, data); | |
324 | } | |
325 | ||
326 | for (i = 0; i < list->len; i++) | |
327 | { | |
328 | GroupData *data = g_ptr_array_index (list, i); | |
329 | ||
330 | priv->icon_names[i] = create_pixbuf (controller, i, data->group, data->id); | |
331 | priv->text_store[i] = create_text (controller, data->group, data->id); | |
332 | } | |
333 | ||
334 | gtk_icon_theme_rescan_if_needed (gtk_icon_theme_get_default ()); | |
335 | ||
336 | g_ptr_array_unref (list); | |
337 | } | |
338 | ||
339 | static gboolean | |
340 | idle_config_changed (XAppKbdLayoutController *controller) | |
341 | { | |
342 | XAppKbdLayoutControllerPrivate *priv = controller->priv; | |
343 | ||
344 | clear_stores (controller); | |
345 | load_stores (controller); | |
346 | ||
347 | if (gkbd_configuration_get_current_group (priv->config) >= priv->num_groups) | |
348 | { | |
349 | xapp_kbd_layout_controller_set_current_group (controller, 0); | |
350 | } | |
351 | ||
352 | g_signal_emit (controller, signals[KBD_CONFIG_CHANGED], 0); | |
353 | ||
354 | priv->idle_changed_id = 0; | |
355 | return FALSE; | |
356 | } | |
357 | ||
358 | static void | |
359 | on_configuration_changed (GkbdConfiguration *config, | |
360 | XAppKbdLayoutController *controller) | |
361 | { | |
362 | XAppKbdLayoutControllerPrivate *priv = controller->priv; | |
363 | ||
364 | if (priv->idle_changed_id != 0) | |
365 | { | |
366 | g_source_remove (priv->idle_changed_id); | |
367 | priv->idle_changed_id = 0; | |
368 | } | |
369 | ||
370 | priv->idle_changed_id = g_idle_add ((GSourceFunc) idle_config_changed, controller); | |
371 | } | |
372 | ||
373 | ||
374 | static void | |
375 | on_configuration_group_changed (GkbdConfiguration *config, | |
376 | gint group, | |
377 | XAppKbdLayoutController *controller) | |
378 | { | |
379 | g_signal_emit (controller, signals[KBD_LAYOUT_CHANGED], 0, (guint) group); | |
380 | } | |
381 | ||
382 | static void | |
383 | xapp_kbd_layout_controller_init (XAppKbdLayoutController *controller) | |
384 | { | |
385 | controller->priv = G_TYPE_INSTANCE_GET_PRIVATE (controller, XAPP_TYPE_KBD_LAYOUT_CONTROLLER, XAppKbdLayoutControllerPrivate); | |
386 | ||
387 | XAppKbdLayoutControllerPrivate *priv = controller->priv; | |
388 | ||
389 | priv->config = gkbd_configuration_get (); | |
390 | priv->enabled = FALSE; | |
391 | priv->flag_dir = NULL; | |
392 | priv->temp_flag_theme_dir = NULL; | |
393 | priv->idle_changed_id = 0; | |
394 | } | |
395 | ||
396 | static void | |
397 | xapp_kbd_layout_controller_constructed (GObject *object) | |
398 | { | |
399 | G_OBJECT_CLASS (xapp_kbd_layout_controller_parent_class)->constructed (object); | |
400 | ||
401 | XAppKbdLayoutController *controller = XAPP_KBD_LAYOUT_CONTROLLER (object); | |
402 | XAppKbdLayoutControllerPrivate *priv = controller->priv; | |
403 | ||
404 | initialize_flag_dir (controller); | |
405 | ||
406 | initialize_icon_theme (controller); | |
407 | ||
408 | gkbd_configuration_start_listen (priv->config); | |
409 | ||
410 | priv->changed_id = g_signal_connect_object (priv->config, | |
411 | "changed", | |
412 | G_CALLBACK (on_configuration_changed), | |
413 | controller, 0); | |
414 | ||
415 | priv->group_changed_id = g_signal_connect_object (priv->config, | |
416 | "group-changed", | |
417 | G_CALLBACK (on_configuration_group_changed), | |
418 | controller, 0); | |
419 | clear_stores (controller); | |
420 | load_stores (controller); | |
421 | } | |
422 | ||
423 | static void | |
424 | xapp_kbd_layout_controller_get_property (GObject *gobject, | |
425 | guint prop_id, | |
426 | GValue *value, | |
427 | GParamSpec *pspec) | |
428 | { | |
429 | XAppKbdLayoutController *controller = XAPP_KBD_LAYOUT_CONTROLLER (gobject); | |
430 | XAppKbdLayoutControllerPrivate *priv = controller->priv; | |
431 | ||
432 | switch (prop_id) | |
433 | { | |
434 | case PROP_ENABLED: | |
435 | g_value_set_boolean (value, priv->enabled); | |
436 | break; | |
437 | default: | |
438 | G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); | |
439 | break; | |
440 | } | |
441 | } | |
442 | ||
443 | static void | |
444 | xapp_kbd_layout_controller_dispose (GObject *object) | |
445 | { | |
446 | XAppKbdLayoutController *controller = XAPP_KBD_LAYOUT_CONTROLLER (object); | |
447 | XAppKbdLayoutControllerPrivate *priv = controller->priv; | |
448 | ||
449 | gkbd_configuration_stop_listen (priv->config); | |
450 | ||
451 | clear_stores (controller); | |
452 | ||
453 | if (priv->changed_id > 0) | |
454 | { | |
455 | g_signal_handler_disconnect (priv->config, priv->changed_id); | |
456 | priv->changed_id = 0; | |
457 | } | |
458 | ||
459 | if (priv->group_changed_id > 0) | |
460 | { | |
461 | g_signal_handler_disconnect (priv->config, priv->group_changed_id); | |
462 | priv->group_changed_id = 0; | |
463 | } | |
464 | ||
465 | if (priv->idle_changed_id != 0) | |
466 | { | |
467 | g_source_remove (priv->idle_changed_id); | |
468 | priv->idle_changed_id = 0; | |
469 | } | |
470 | ||
471 | G_OBJECT_CLASS (xapp_kbd_layout_controller_parent_class)->dispose (object); | |
472 | } | |
473 | ||
474 | static void | |
475 | xapp_kbd_layout_controller_finalize (GObject *object) | |
476 | { | |
477 | XAppKbdLayoutController *controller = XAPP_KBD_LAYOUT_CONTROLLER (object); | |
478 | XAppKbdLayoutControllerPrivate *priv = controller->priv; | |
479 | ||
480 | g_clear_object (&priv->config); | |
481 | g_clear_pointer (&priv->flag_dir, g_free); | |
482 | g_clear_pointer (&priv->temp_flag_theme_dir, g_free); | |
483 | ||
484 | G_OBJECT_CLASS (xapp_kbd_layout_controller_parent_class)->finalize (object); | |
485 | } | |
486 | ||
487 | static void | |
488 | xapp_kbd_layout_controller_class_init (XAppKbdLayoutControllerClass *klass) | |
489 | { | |
490 | GObjectClass *gobject_class = G_OBJECT_CLASS (klass); | |
491 | ||
492 | gobject_class->dispose = xapp_kbd_layout_controller_dispose; | |
493 | gobject_class->finalize = xapp_kbd_layout_controller_finalize; | |
494 | gobject_class->get_property = xapp_kbd_layout_controller_get_property; | |
495 | gobject_class->constructed = xapp_kbd_layout_controller_constructed; | |
496 | ||
497 | g_type_class_add_private (gobject_class, sizeof (XAppKbdLayoutControllerPrivate)); | |
498 | ||
499 | g_object_class_install_property (gobject_class, PROP_ENABLED, | |
500 | g_param_spec_boolean ("enabled", | |
501 | "Enabled", | |
502 | "Whether we're enabled (more than one keyboard layout is installed)", | |
503 | FALSE, | |
504 | G_PARAM_READABLE) | |
505 | ); | |
506 | ||
507 | signals[KBD_LAYOUT_CHANGED] = g_signal_new ("layout-changed", | |
508 | G_TYPE_FROM_CLASS (gobject_class), | |
509 | G_SIGNAL_RUN_LAST, | |
510 | 0, | |
511 | NULL, NULL, | |
512 | g_cclosure_marshal_VOID__UINT, | |
513 | G_TYPE_NONE, | |
514 | 1, G_TYPE_UINT); | |
515 | ||
516 | signals[KBD_CONFIG_CHANGED] = g_signal_new ("config-changed", | |
517 | G_TYPE_FROM_CLASS (gobject_class), | |
518 | G_SIGNAL_RUN_LAST, | |
519 | 0, | |
520 | NULL, NULL, | |
521 | g_cclosure_marshal_VOID__VOID, | |
522 | G_TYPE_NONE, | |
523 | 0, G_TYPE_NONE); | |
524 | } | |
525 | ||
526 | XAppKbdLayoutController * | |
527 | xapp_kbd_layout_controller_new (void) | |
528 | { | |
529 | return g_object_new (XAPP_TYPE_KBD_LAYOUT_CONTROLLER, NULL); | |
530 | } | |
531 | ||
532 | gboolean | |
533 | xapp_kbd_layout_controller_get_enabled (XAppKbdLayoutController *controller) | |
534 | { | |
535 | return controller->priv->enabled; | |
536 | } | |
537 | ||
538 | guint | |
539 | xapp_kbd_layout_controller_get_current_group (XAppKbdLayoutController *controller) | |
540 | { | |
541 | g_return_val_if_fail (controller->priv->enabled, 0); | |
542 | ||
543 | return gkbd_configuration_get_current_group (controller->priv->config); | |
544 | } | |
545 | ||
546 | void | |
547 | xapp_kbd_layout_controller_set_current_group (XAppKbdLayoutController *controller, | |
548 | guint group) | |
549 | { | |
550 | g_return_if_fail (controller->priv->enabled); | |
551 | g_return_if_fail (group <= controller->priv->num_groups); | |
552 | ||
553 | guint current = gkbd_configuration_get_current_group (controller->priv->config); | |
554 | ||
555 | if (current != group) | |
556 | { | |
557 | gkbd_configuration_lock_group (controller->priv->config, group); | |
558 | } | |
559 | } | |
560 | ||
561 | void | |
562 | xapp_kbd_layout_controller_next_group (XAppKbdLayoutController *controller) | |
563 | { | |
564 | g_return_if_fail (controller->priv->enabled); | |
565 | ||
566 | gkbd_configuration_lock_next_group (controller->priv->config); | |
567 | } | |
568 | ||
569 | void | |
570 | xapp_kbd_layout_controller_previous_group (XAppKbdLayoutController *controller) | |
571 | { | |
572 | g_return_if_fail (controller->priv->enabled); | |
573 | ||
574 | XAppKbdLayoutControllerPrivate *priv = controller->priv; | |
575 | ||
576 | gint current = gkbd_configuration_get_current_group (priv->config); | |
577 | ||
578 | if (current > 0) | |
579 | { | |
580 | current--; | |
581 | } | |
582 | else | |
583 | { | |
584 | current = priv->num_groups - 1; | |
585 | } | |
586 | ||
587 | gkbd_configuration_lock_group (controller->priv->config, current); | |
588 | } | |
589 | ||
590 | /** | |
591 | * xapp_kbd_layout_controller_get_current_name: | |
592 | * | |
593 | * Returns the full name of the current keyboard layout. | |
594 | * | |
595 | * Returns: (transfer full): the newly created string or NULL | |
596 | * if something went wrong. | |
597 | */ | |
598 | gchar * | |
599 | xapp_kbd_layout_controller_get_current_name (XAppKbdLayoutController *controller) | |
600 | { | |
601 | g_return_val_if_fail (controller->priv->enabled, NULL); | |
602 | ||
603 | return gkbd_configuration_get_current_tooltip (controller->priv->config); | |
604 | } | |
605 | ||
606 | /** | |
607 | * xapp_kbd_layout_controller_get_all_names: | |
608 | * | |
609 | * Returns an array of all full layout names | |
610 | * | |
611 | * Returns: (transfer none) (array zero-terminated=1): array of names | |
612 | */ | |
613 | gchar ** | |
614 | xapp_kbd_layout_controller_get_all_names (XAppKbdLayoutController *controller) | |
615 | { | |
616 | g_return_val_if_fail (controller->priv->enabled, NULL); | |
617 | ||
618 | return gkbd_configuration_get_group_names (controller->priv->config); | |
619 | } | |
620 | ||
621 | /** | |
622 | * xapp_kbd_layout_controller_get_current_icon_name: | |
623 | * | |
624 | * Returns the icon name to use for the current layout | |
625 | * | |
626 | * Returns: (transfer full): a new string with the icon name. | |
627 | */ | |
628 | gchar * | |
629 | xapp_kbd_layout_controller_get_current_icon_name (XAppKbdLayoutController *controller) | |
630 | { | |
631 | g_return_val_if_fail (controller->priv->enabled, NULL); | |
632 | ||
633 | XAppKbdLayoutControllerPrivate *priv = controller->priv; | |
634 | ||
635 | guint current = gkbd_configuration_get_current_group (priv->config); | |
636 | ||
637 | return g_strdup (priv->icon_names[current]); | |
638 | } | |
639 | ||
640 | ||
641 | /** | |
642 | * xapp_kbd_layout_controller_get_icon_name_for_group: | |
643 | * | |
644 | * Returns the icon name to use for the specified layout. | |
645 | * | |
646 | * Returns: (transfer full): a new string with the icon name. | |
647 | */ | |
648 | gchar * | |
649 | xapp_kbd_layout_controller_get_icon_name_for_group (XAppKbdLayoutController *controller, guint group) | |
650 | { | |
651 | g_return_val_if_fail (controller->priv->enabled, NULL); | |
652 | g_return_val_if_fail (group <= controller->priv->num_groups, NULL); | |
653 | ||
654 | XAppKbdLayoutControllerPrivate *priv = controller->priv; | |
655 | ||
656 | return g_strdup (priv->icon_names[group]); | |
657 | } | |
658 | ||
659 | /** | |
660 | * xapp_kbd_layout_controller_get_current_short_name: | |
661 | * | |
662 | * Returns the short name (and subscript, if any) of the current layout | |
663 | * | |
664 | * Returns: (transfer full): a new string or NULL. | |
665 | */ | |
666 | gchar * | |
667 | xapp_kbd_layout_controller_get_short_name (XAppKbdLayoutController *controller) | |
668 | { | |
669 | g_return_val_if_fail (controller->priv->enabled, NULL); | |
670 | ||
671 | XAppKbdLayoutControllerPrivate *priv = controller->priv; | |
672 | ||
673 | guint current = gkbd_configuration_get_current_group (priv->config); | |
674 | ||
675 | return g_strdup (priv->text_store[current]); | |
676 | } | |
677 | ||
678 | /** | |
679 | * xapp_kbd_layout_controller_get_short_name_for_group: | |
680 | * | |
681 | * Returns the short name and subscript of the specified group. | |
682 | * | |
683 | * Returns: (transfer full): a new string or NULL. | |
684 | */ | |
685 | gchar * | |
686 | xapp_kbd_layout_controller_get_short_name_for_group (XAppKbdLayoutController *controller, | |
687 | guint group) | |
688 | { | |
689 | g_return_val_if_fail (controller->priv->enabled, NULL); | |
690 | ||
691 | XAppKbdLayoutControllerPrivate *priv = controller->priv; | |
692 | ||
693 | g_return_val_if_fail (group < controller->priv->num_groups, NULL); | |
694 | ||
695 | return g_strdup (priv->text_store[group]); | |
696 | } |
0 | #ifndef __XAPP_KBD_LAYOUT_CONTROLLER_H__ | |
1 | #define __XAPP_KBD_LAYOUT_CONTROLLER_H__ | |
2 | ||
3 | #include <stdio.h> | |
4 | #include <gdk-pixbuf/gdk-pixbuf.h> | |
5 | #include <clutter/clutter.h> | |
6 | ||
7 | #include <glib-object.h> | |
8 | ||
9 | G_BEGIN_DECLS | |
10 | ||
11 | #define XAPP_TYPE_KBD_LAYOUT_CONTROLLER (xapp_kbd_layout_controller_get_type ()) | |
12 | #define XAPP_KBD_LAYOUT_CONTROLLER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), XAPP_TYPE_KBD_LAYOUT_CONTROLLER, XAppKbdLayoutController)) | |
13 | #define XAPP_KBD_LAYOUT_CONTROLLER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), XAPP_TYPE_KBD_LAYOUT_CONTROLLER, XAppKbdLayoutControllerClass)) | |
14 | #define XAPP_IS_KBD_LAYOUT_CONTROLLER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), XAPP_TYPE_KBD_LAYOUT_CONTROLLER)) | |
15 | #define XAPP_IS_KBD_LAYOUT_CONTROLLER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), XAPP_TYPE_KBD_LAYOUT_CONTROLLER)) | |
16 | #define XAPP_KBD_LAYOUT_CONTROLLER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), XAPP_TYPE_KBD_LAYOUT_CONTROLLER, XAppKbdLayoutControllerClass)) | |
17 | ||
18 | typedef struct _XAppKbdLayoutControllerPrivate XAppKbdLayoutControllerPrivate; | |
19 | typedef struct _XAppKbdLayoutController XAppKbdLayoutController; | |
20 | typedef struct _XAppKbdLayoutControllerClass XAppKbdLayoutControllerClass; | |
21 | ||
22 | struct _XAppKbdLayoutController | |
23 | { | |
24 | GObject parent_object; | |
25 | ||
26 | XAppKbdLayoutControllerPrivate *priv; | |
27 | }; | |
28 | ||
29 | struct _XAppKbdLayoutControllerClass | |
30 | { | |
31 | GObjectClass parent_class; | |
32 | }; | |
33 | ||
34 | GType xapp_kbd_layout_controller_get_type (void); | |
35 | XAppKbdLayoutController *xapp_kbd_layout_controller_new (void); | |
36 | gboolean xapp_kbd_layout_controller_get_enabled (XAppKbdLayoutController *controller); | |
37 | guint xapp_kbd_layout_controller_get_current_group (XAppKbdLayoutController *controller); | |
38 | void xapp_kbd_layout_controller_set_current_group (XAppKbdLayoutController *controller, | |
39 | guint group); | |
40 | void xapp_kbd_layout_controller_next_group (XAppKbdLayoutController *controller); | |
41 | void xapp_kbd_layout_controller_previous_group (XAppKbdLayoutController *controller); | |
42 | gchar *xapp_kbd_layout_controller_get_current_name (XAppKbdLayoutController *controller); | |
43 | gchar **xapp_kbd_layout_controller_get_all_names (XAppKbdLayoutController *controller); | |
44 | gchar *xapp_kbd_layout_controller_get_current_icon_name (XAppKbdLayoutController *controller); | |
45 | gchar *xapp_kbd_layout_controller_get_icon_name_for_group (XAppKbdLayoutController *controller, | |
46 | guint group); | |
47 | gchar *xapp_kbd_layout_controller_get_short_name (XAppKbdLayoutController *controller); | |
48 | gchar *xapp_kbd_layout_controller_get_short_name_for_group (XAppKbdLayoutController *controller, | |
49 | guint group); | |
50 | ||
51 | G_END_DECLS | |
52 | ||
53 | #endif /* __XAPP_KBD_LAYOUT_CONTROLLER_H__ */ |
4 | 4 | |
5 | 5 | Name: xapp |
6 | 6 | Description: Utility library for loading .desktop files |
7 | Requires: gtk+-3.0 | |
7 | Requires: gtk+-3.0 clutter-1.0 | |
8 | 8 | Version: @VERSION@ |
9 | 9 | Libs: ${pc_top_builddir}/${pcfiledir}/libxapp.la |
10 | 10 | Cflags: -I${pc_top_builddir}/${pcfiledir}/.. |
4 | 4 | |
5 | 5 | Name: xapp |
6 | 6 | Description: Utility library for loading .desktop files |
7 | Requires: gtk+-3.0 | |
7 | Requires: gtk+-3.0 clutter-1.0 | |
8 | 8 | Requires.private: xkbfile |
9 | 9 | Version: @VERSION@ |
10 | 10 | Libs: -L${libdir} -lxapp |
0 | #! /usr/bin/python3 | |
1 | ||
2 | """ | |
3 | A demo/test script for the XAppKbdLayoutController class | |
4 | """ | |
5 | import sys, os | |
6 | import signal | |
7 | import gettext | |
8 | ||
9 | import gi | |
10 | gi.require_version('Gtk', '3.0') | |
11 | gi.require_version('XApp', '1.0') | |
12 | ||
13 | from gi.repository import Gtk, XApp, GObject | |
14 | ||
15 | signal.signal(signal.SIGINT, signal.SIG_DFL) | |
16 | ||
17 | class Main: | |
18 | def __init__(self): | |
19 | win = Gtk.Window() | |
20 | frame = Gtk.Frame() | |
21 | frame.set_margin_start(6) | |
22 | frame.set_margin_end(6) | |
23 | frame.set_margin_top(6) | |
24 | frame.set_margin_bottom(6) | |
25 | ||
26 | win.add(frame) | |
27 | ||
28 | box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) | |
29 | frame.add(box) | |
30 | ||
31 | self.show_flags = False | |
32 | self.use_caps = False | |
33 | ||
34 | self.controller = XApp.KbdLayoutController() | |
35 | self.controller.connect("layout-changed", self.on_layout_changed) | |
36 | self.controller.connect("config-changed", self.on_config_changed) | |
37 | ||
38 | hbox = Gtk.HBox() | |
39 | box.pack_start(hbox, True, True, 4) | |
40 | ||
41 | self.button = Gtk.Button() | |
42 | self.button.set_size_request(48, 48) | |
43 | self.button.connect("clicked", self.on_button_clicked) | |
44 | hbox.pack_start(self.button, False, False, 4) | |
45 | ||
46 | self.label = Gtk.Label() | |
47 | self.label.set_text(self.controller.get_current_name()) | |
48 | hbox.pack_start(self.label, True, True, 4) | |
49 | ||
50 | check = Gtk.CheckButton.new_with_label("Show flags") | |
51 | check.connect("toggled", self.on_flag_toggled) | |
52 | box.pack_start(check, True, True, 4) | |
53 | ||
54 | check = Gtk.CheckButton.new_with_label("Use caps") | |
55 | check.connect("toggled", self.on_caps_toggled) | |
56 | box.pack_start(check, True, True, 4) | |
57 | ||
58 | frame.show_all() | |
59 | ||
60 | win.connect("delete-event", lambda w, e: Gtk.main_quit()) | |
61 | ||
62 | self.on_layout_changed(self.controller) | |
63 | ||
64 | win.present() | |
65 | ||
66 | Gtk.main() | |
67 | ||
68 | def on_flag_toggled(self, widget): | |
69 | self.show_flags = widget.get_active() | |
70 | self.on_layout_changed(self.controller) | |
71 | ||
72 | def on_caps_toggled(self, widget): | |
73 | self.use_caps = widget.get_active() | |
74 | self.on_layout_changed(self.controller) | |
75 | ||
76 | def on_button_clicked(self, widget, data=None): | |
77 | self.controller.next_group() | |
78 | ||
79 | def on_layout_changed(self, controller, group=None): | |
80 | handled = False | |
81 | if self.show_flags: | |
82 | name = self.controller.get_current_icon_name() | |
83 | if name != None: | |
84 | image = Gtk.Image.new_from_icon_name(name, Gtk.IconSize.DIALOG) | |
85 | self.button.set_image(image) | |
86 | handled = True | |
87 | ||
88 | if not handled: | |
89 | name = self.controller.get_short_name() | |
90 | if self.use_caps: | |
91 | name = name.upper() | |
92 | label = Gtk.Label(name) | |
93 | label.show() | |
94 | self.button.set_image(label) | |
95 | ||
96 | self.label.set_text(self.controller.get_current_name()) | |
97 | ||
98 | def on_config_changed(self, controller): | |
99 | GObject.idle_add(self.on_layout_changed, controller) | |
100 | ||
101 | if __name__ == "__main__": | |
102 | main = Main() |