1 /*
2 * This file is part of GtkHotkey.
3 * Copyright Mikkel Kamstrup Erlandsen, March, 2008
4 *
5 * GtkHotkey is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * GtkHotkey is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with GtkHotkey. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "gtk-hotkey-key-file-registry.h"
20 #include "gtk-hotkey-utils.h"
21
22 #include <gio/gio.h>
23 #include <gio/gdesktopappinfo.h>
24
25 enum {
26 GTK_HOTKEY_KEY_FILE_REGISTRY_DUMMY_PROPERTY
27 };
28
29 /* Beware - the lengths of these strings are hardcoded throughout
30 * this file (sorry) */
31 #define HOTKEY_HOME "~/.config/hotkeys"
32 #define HOTKEY_FILE_EXT ".hotkeys"
33 #define HOTKEY_GROUP "hotkey:"
34
35 static GtkHotkeyInfo* gtk_hotkey_key_file_registry_real_get_hotkey (GtkHotkeyRegistry *base,
36 const char *app_id,
37 const char *key_id,
38 GError **error);
39
40 static GList* gtk_hotkey_key_file_registry_real_get_application_hotkeys
41 (GtkHotkeyRegistry *base,
42 const char *app_id,
43 GError **error);
44
45 static GList* gtk_hotkey_key_file_registry_real_get_all_hotkeys
46 (GtkHotkeyRegistry *base);
47
48 static gboolean gtk_hotkey_key_file_registry_real_store_hotkey(GtkHotkeyRegistry *base,
49 GtkHotkeyInfo *info,
50 GError **error);
51
52 static gboolean gtk_hotkey_key_file_registry_real_delete_hotkey
53 (GtkHotkeyRegistry *base,
54 const gchar *app_id,
55 const gchar *key_id,
56 GError **error);
57
58 static gboolean gtk_hotkey_key_file_registry_real_has_hotkey (GtkHotkeyRegistry *base,
59 const gchar *app_id,
60 const gchar *key_id);
61
62 static GFile* get_hotkey_home (void);
63
64 static GFile* get_hotkey_file (const gchar *app_id);
65
66 static GKeyFile* get_hotkey_key_file (const gchar *app_id,
67 GError **error);
68
69 static GtkHotkeyInfo* get_hotkey_info_from_key_file (GKeyFile *keyfile,
70 const gchar *app_id,
71 const gchar *key_id,
72 GError **error);
73
74 static GList* get_all_hotkey_infos_from_key_file (GKeyFile *keyfile,
75 const gchar *app_id);
76
77 static gpointer gtk_hotkey_key_file_registry_parent_class = NULL;
78
79
80 static
81 GtkHotkeyInfo*
gtk_hotkey_key_file_registry_real_get_hotkey(GtkHotkeyRegistry * base,const char * app_id,const char * key_id,GError ** error)82 gtk_hotkey_key_file_registry_real_get_hotkey (GtkHotkeyRegistry *base,
83 const char *app_id,
84 const char *key_id,
85 GError **error)
86 {
87 GKeyFile *keyfile = NULL;
88 GtkHotkeyInfo *info = NULL;
89
90 g_return_val_if_fail (GTK_HOTKEY_IS_KEY_FILE_REGISTRY(base), NULL);
91 g_return_val_if_fail (app_id != NULL, NULL);
92 g_return_val_if_fail (key_id != NULL, NULL);
93
94 keyfile = get_hotkey_key_file (app_id, error);
95 if (keyfile == NULL)
96 goto clean_up;
97
98 info = get_hotkey_info_from_key_file (keyfile, app_id, key_id, error);
99
100 clean_up:
101 if (keyfile) g_key_file_free (keyfile);
102
103 return info;
104
105 }
106
107
108 static GList*
gtk_hotkey_key_file_registry_real_get_application_hotkeys(GtkHotkeyRegistry * base,const char * app_id,GError ** error)109 gtk_hotkey_key_file_registry_real_get_application_hotkeys (GtkHotkeyRegistry *base,
110 const char *app_id,
111 GError **error)
112 {
113 GKeyFile *keyfile;
114
115 g_return_val_if_fail (app_id != NULL, NULL);
116
117 keyfile = get_hotkey_key_file (app_id, error);
118
119 if (keyfile == NULL)
120 return NULL; /* error is set by get_hotkey_key_file() */
121
122 return get_all_hotkey_infos_from_key_file (keyfile, app_id);
123 }
124
125
126 static GList*
gtk_hotkey_key_file_registry_real_get_all_hotkeys(GtkHotkeyRegistry * base)127 gtk_hotkey_key_file_registry_real_get_all_hotkeys (GtkHotkeyRegistry *base)
128 {
129 GFile *home;
130 GFileEnumerator *dir;
131 GFileInfo *file_info;
132 GError *error;
133 GList *result = NULL;
134
135 home = get_hotkey_home ();
136
137 error = NULL;
138 dir = g_file_enumerate_children (home, G_FILE_ATTRIBUTE_STANDARD_NAME,
139 0, NULL, &error);
140 if (error) {
141 gchar *path = g_file_get_path (home);
142 g_critical ("Failed to read hotkey home directory '%s': %s",
143 path, error->message);
144 g_free (path);
145 g_error_free (error);
146 return NULL;
147 }
148
149 error = NULL;
150 while ((file_info = g_file_enumerator_next_file (dir, NULL, &error)) != NULL) {
151 const gchar *filename;
152 GFile *file;
153 GString *app_id;
154 GList *app_hotkeys;
155
156 filename = g_file_info_get_name(file_info);
157
158 if (g_str_has_suffix (filename, HOTKEY_FILE_EXT)) {
159 file = g_file_get_child (home, filename);
160
161 /* Extract app_id from file name */
162 app_id = g_string_new (filename);
163 g_string_erase (app_id, app_id->len - 8, 8);
164
165 /* Load all hotkeys from the file, and append it to
166 * the total result */
167 app_hotkeys = gtk_hotkey_registry_get_application_hotkeys (base,
168 app_id->str,
169 &error);
170 if (error) {
171 g_warning ("Failed to read hotkeys for application '%s': %s",
172 app_id->str, error->message);
173 g_error_free (error);
174 error = NULL;
175 } else {
176 result = g_list_concat (result, app_hotkeys);
177 }
178
179 g_string_free (app_id, TRUE);
180 g_object_unref (file);
181 }
182
183 g_object_unref (file_info);
184 }
185
186 if (error) {
187 gchar *path = g_file_get_path (home);
188 g_warning ("Failed to read hotkey home directory '%s': %s",
189 path, error->message);
190 g_free (path);
191 g_error_free (error);
192 }
193
194
195 g_object_unref (dir);
196 g_object_unref (home);
197
198 return result;
199 }
200
201
202 static gboolean
gtk_hotkey_key_file_registry_real_store_hotkey(GtkHotkeyRegistry * base,GtkHotkeyInfo * info,GError ** error)203 gtk_hotkey_key_file_registry_real_store_hotkey (GtkHotkeyRegistry *base,
204 GtkHotkeyInfo *info,
205 GError **error)
206 {
207 GKeyFile *keyfile;
208 GFile *file, *home;
209 GError *tmp_error;
210 gchar *file_path, *group = NULL;
211
212
213 g_return_val_if_fail (GTK_HOTKEY_IS_INFO (info), FALSE);
214 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
215
216 /* Make sure we have our root dir */
217 home = get_hotkey_home ();
218 if (!g_file_query_exists(home, NULL)) {
219 tmp_error = NULL;
220 if (!g_file_make_directory (home, NULL, &tmp_error)) {
221 g_set_error (error, GTK_HOTKEY_REGISTRY_ERROR,
222 GTK_HOTKEY_REGISTRY_ERROR_IO,
223 "Failed to create hotkey configuration dir "
224 HOTKEY_HOME": %s", tmp_error->message);
225 g_error_free (tmp_error);
226 g_object_unref (home);
227 return FALSE;
228 }
229 }
230
231 /* Now load any old contents of the keyfile */
232 file = get_hotkey_file (gtk_hotkey_info_get_application_id (info));
233 file_path = g_file_get_path (file);
234 keyfile = g_key_file_new ();
235
236 tmp_error = NULL;
237 if (!g_key_file_load_from_file (keyfile, file_path, 0, &tmp_error)) {
238 if (tmp_error->code == G_KEY_FILE_ERROR_PARSE) {
239 g_set_error (error, GTK_HOTKEY_REGISTRY_ERROR,
240 GTK_HOTKEY_REGISTRY_ERROR_MALFORMED_MEDIUM,
241 "The file %s is not in a valid key-file format: %s",
242 file_path, tmp_error->message);
243 goto clean_up;
244 }
245 /* Ignore other errors */
246 g_error_free (tmp_error);
247 }
248
249 /* Prepare keyfile data */
250 group = g_strconcat (HOTKEY_GROUP, gtk_hotkey_info_get_key_id (info), NULL);
251
252 g_key_file_set_string (keyfile, group, "Owner",
253 gtk_hotkey_info_get_application_id (info));
254 g_key_file_set_string (keyfile, group, "Signature",
255 gtk_hotkey_info_get_signature (info));
256
257 if (gtk_hotkey_info_get_description (info))
258 g_key_file_set_string (keyfile, group, "Description",
259 gtk_hotkey_info_get_description (info));
260
261 if (gtk_hotkey_info_get_app_info (info)) {
262 GAppInfo *ai = gtk_hotkey_info_get_app_info (info);
263 g_key_file_set_string (keyfile, group, "AppInfo",
264 g_app_info_get_id (ai));
265 }
266
267 gsize size;
268 gchar *contents;
269 tmp_error = NULL;
270 contents = g_key_file_to_data (keyfile, &size, &tmp_error);
271 if (tmp_error) {
272 g_set_error (error, GTK_HOTKEY_REGISTRY_ERROR,
273 GTK_HOTKEY_REGISTRY_ERROR_UNKNOWN,
274 "Failed to generate keyfile contents: %s",
275 tmp_error->message);
276 goto clean_up;
277 }
278
279 /* Write the actual data */
280 g_file_set_contents (file_path, contents, size, &tmp_error);
281 if (tmp_error) {
282 g_set_error (error, GTK_HOTKEY_REGISTRY_ERROR,
283 GTK_HOTKEY_REGISTRY_ERROR_IO,
284 "Failed to write keyfile '%s': %s",
285 file_path, tmp_error->message);
286 goto clean_up;
287 }
288
289 clean_up:
290 if (tmp_error) g_error_free (tmp_error);
291 g_free (file_path);
292 if (group) g_free (group);
293 g_key_file_free (keyfile);
294 g_object_unref (file);
295 g_object_unref (home);
296
297 if (*error)
298 return FALSE;
299
300 g_return_val_if_fail (GTK_HOTKEY_IS_INFO (info), FALSE);
301 gtk_hotkey_registry_hotkey_stored (base, info);
302 return TRUE;
303 }
304
305 static gboolean
gtk_hotkey_key_file_registry_real_delete_hotkey(GtkHotkeyRegistry * base,const gchar * app_id,const gchar * key_id,GError ** error)306 gtk_hotkey_key_file_registry_real_delete_hotkey (GtkHotkeyRegistry *base,
307 const gchar *app_id,
308 const gchar *key_id,
309 GError **error)
310 {
311 GtkHotkeyInfo *info = NULL;
312 GFile *file;
313 GKeyFile *keyfile;
314 GError *tmp_error;
315 gboolean is_error = FALSE;
316 gchar *path, *group;
317
318 g_return_val_if_fail (app_id != NULL, FALSE);
319 g_return_val_if_fail (key_id != NULL, FALSE);
320 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
321
322 group = NULL;
323
324 file = get_hotkey_file (app_id);
325 g_return_val_if_fail (G_IS_FILE(file), FALSE);
326
327 path = g_file_get_path (file);
328 keyfile = g_key_file_new ();
329
330 /* Load the old keyfile */
331 tmp_error = NULL;
332 g_key_file_load_from_file (keyfile, path, 0, &tmp_error);
333 if (tmp_error) {
334 if ((tmp_error->domain == G_FILE_ERROR &&
335 tmp_error->code == G_FILE_ERROR_NOENT) ||
336 (tmp_error->domain == G_KEY_FILE_ERROR &&
337 tmp_error->code == G_KEY_FILE_ERROR_NOT_FOUND))
338 g_set_error (error, GTK_HOTKEY_REGISTRY_ERROR,
339 GTK_HOTKEY_REGISTRY_ERROR_UNKNOWN_APP,
340 "No such keyfile '%s'. Application '%s' has not "
341 "registered any hotkeys",
342 path, app_id);
343 else
344 g_set_error (error, GTK_HOTKEY_REGISTRY_ERROR,
345 GTK_HOTKEY_REGISTRY_ERROR_IO,
346 "Failed to load keyfile '%s': %s",
347 app_id, tmp_error->message);
348 is_error = TRUE;
349 goto clean_up;
350 }
351
352 /* Get a ref to the GtkHotkeyInfo so that we can emit it with the
353 * hotkey-deleted signal */
354 tmp_error = NULL;
355 info = get_hotkey_info_from_key_file (keyfile, app_id, key_id, error);
356 if (info == NULL) {
357 is_error = TRUE;
358 goto clean_up;
359 }
360
361 /* Remove the group for key_id */
362 group = g_strconcat (HOTKEY_GROUP, key_id, NULL);
363 tmp_error = NULL;
364 g_key_file_remove_group (keyfile, group, &tmp_error);
365 if (tmp_error) {
366 if (tmp_error->domain == G_KEY_FILE_ERROR &&
367 tmp_error->code == G_KEY_FILE_ERROR_GROUP_NOT_FOUND)
368 g_set_error (error, GTK_HOTKEY_REGISTRY_ERROR,
369 GTK_HOTKEY_REGISTRY_ERROR_UNKNOWN_APP,
370 "Application '%s' has not registered a hotkey with"
371 "id '%s'", app_id, key_id);
372 else
373 g_set_error (error, GTK_HOTKEY_REGISTRY_ERROR,
374 GTK_HOTKEY_REGISTRY_ERROR_UNKNOWN,
375 "Failed to delete hotkey '%s' from application %s: %s",
376 key_id, app_id, tmp_error->message);
377 is_error = TRUE;
378 goto clean_up;
379 }
380
381 /* Check if the keyfile is empty. If it is we delete it */
382 gsize count;
383 GStrv groups;
384 groups = g_key_file_get_groups (keyfile, &count);
385 g_strfreev (groups);
386 if (count == 0) {
387 tmp_error = NULL;
388 g_file_delete (file, NULL, &tmp_error);
389
390 if (tmp_error) {
391 g_set_error (error, GTK_HOTKEY_REGISTRY_ERROR,
392 GTK_HOTKEY_REGISTRY_ERROR_IO,
393 "Failed to delete empty keyfile '%s': %s",
394 path, tmp_error->message);
395 is_error = TRUE;
396 }
397 /* File deleted, we should just clean up and exit */
398 goto clean_up;
399 }
400
401 /* Write new keyfile */
402 gsize size;
403 gchar *contents;
404 tmp_error = NULL;
405 contents = g_key_file_to_data (keyfile, &size, &tmp_error);
406 if (tmp_error) {
407 g_set_error (error, GTK_HOTKEY_REGISTRY_ERROR,
408 GTK_HOTKEY_REGISTRY_ERROR_UNKNOWN,
409 "Failed to generate keyfile contents: %s",
410 tmp_error->message);
411 is_error = TRUE;
412 goto clean_up;
413 }
414
415 tmp_error = NULL;
416 g_file_set_contents (path, contents, size, &tmp_error);
417 if (tmp_error) {
418 g_set_error (error, GTK_HOTKEY_REGISTRY_ERROR,
419 GTK_HOTKEY_REGISTRY_ERROR_IO,
420 "Failed to write keyfile '%s': %s",
421 path, tmp_error->message);
422 is_error = TRUE;
423 goto clean_up;
424 }
425
426 clean_up:
427 if (tmp_error) g_error_free (tmp_error);
428 g_object_unref (file);
429 g_free (path);
430 if (group) g_free (group);
431 g_key_file_free (keyfile);
432
433 if (is_error)
434 return FALSE;
435
436 gtk_hotkey_registry_hotkey_deleted (base, info);
437 g_object_unref (info);
438 return TRUE;
439 }
440
441 static gboolean
gtk_hotkey_key_file_registry_real_has_hotkey(GtkHotkeyRegistry * base,const gchar * app_id,const gchar * key_id)442 gtk_hotkey_key_file_registry_real_has_hotkey (GtkHotkeyRegistry *base,
443 const gchar *app_id,
444 const gchar *key_id)
445 {
446 GFile *file;
447 gboolean exists;
448
449 g_return_val_if_fail (app_id != NULL, FALSE);
450 g_return_val_if_fail (key_id != NULL, FALSE);
451
452 file = get_hotkey_file (app_id);
453 g_return_val_if_fail (G_IS_FILE(file), FALSE);
454
455 if (g_file_query_exists (file, NULL))
456 exists = TRUE;
457 else
458 exists = FALSE;
459
460 g_object_unref (file);
461 return exists;
462 }
463
464 static void
gtk_hotkey_key_file_registry_class_init(GtkHotkeyKeyFileRegistryClass * klass)465 gtk_hotkey_key_file_registry_class_init (GtkHotkeyKeyFileRegistryClass *klass)
466 {
467 gtk_hotkey_key_file_registry_parent_class = g_type_class_peek_parent (klass);
468 GTK_HOTKEY_REGISTRY_CLASS (klass)->get_hotkey = gtk_hotkey_key_file_registry_real_get_hotkey;
469 GTK_HOTKEY_REGISTRY_CLASS (klass)->get_application_hotkeys = gtk_hotkey_key_file_registry_real_get_application_hotkeys;
470 GTK_HOTKEY_REGISTRY_CLASS (klass)->get_all_hotkeys = gtk_hotkey_key_file_registry_real_get_all_hotkeys;
471 GTK_HOTKEY_REGISTRY_CLASS (klass)->store_hotkey = gtk_hotkey_key_file_registry_real_store_hotkey;
472 GTK_HOTKEY_REGISTRY_CLASS (klass)->delete_hotkey = gtk_hotkey_key_file_registry_real_delete_hotkey;
473 GTK_HOTKEY_REGISTRY_CLASS (klass)->has_hotkey = gtk_hotkey_key_file_registry_real_has_hotkey;
474 }
475
476
477 static void
gtk_hotkey_key_file_registry_init(GtkHotkeyKeyFileRegistry * self)478 gtk_hotkey_key_file_registry_init (GtkHotkeyKeyFileRegistry *self)
479 {
480
481 }
482
483 static void
gtk_hotkey_key_file_registry_finalize(GtkHotkeyKeyFileRegistry * self)484 gtk_hotkey_key_file_registry_finalize (GtkHotkeyKeyFileRegistry *self)
485 {
486
487 }
488
489 GType
gtk_hotkey_key_file_registry_get_type(void)490 gtk_hotkey_key_file_registry_get_type (void)
491 {
492 static GType gtk_hotkey_key_file_registry_type_id = 0;
493
494 if (G_UNLIKELY (gtk_hotkey_key_file_registry_type_id == 0)) {
495 static const GTypeInfo g_define_type_info = {
496 sizeof (GtkHotkeyKeyFileRegistryClass),
497 (GBaseInitFunc) gtk_hotkey_key_file_registry_init,
498 (GBaseFinalizeFunc) gtk_hotkey_key_file_registry_finalize,
499 (GClassInitFunc) gtk_hotkey_key_file_registry_class_init,
500 (GClassFinalizeFunc) NULL,
501 NULL,
502 sizeof (GtkHotkeyKeyFileRegistry),
503 0,
504 (GInstanceInitFunc) gtk_hotkey_key_file_registry_init,
505 (const GTypeValueTable *) NULL /* value table */
506 };
507
508 gtk_hotkey_key_file_registry_type_id = g_type_register_static (GTK_HOTKEY_TYPE_STORAGE, "GtkHotkeyKeyFileRegistry", &g_define_type_info, 0);
509 }
510 return gtk_hotkey_key_file_registry_type_id;
511 }
512
513 static GFile*
get_hotkey_home(void)514 get_hotkey_home (void)
515 {
516 GFile *home;
517
518 home = g_file_parse_name (HOTKEY_HOME);
519
520 if (g_file_query_exists(home, NULL) &&
521 !gtk_hotkey_g_file_is_directory(home)) {
522 g_critical (HOTKEY_HOME" exists but is not a directory");
523 g_object_unref (home);
524 return NULL;
525 }
526
527 return home;
528 }
529
530 /* It is not guaranteed that the keyfile exists */
531 static GFile*
get_hotkey_file(const gchar * app_id)532 get_hotkey_file (const gchar *app_id)
533 {
534 GFile *home, *file;
535 gchar *filename;
536
537 g_return_val_if_fail (app_id != NULL, NULL);
538
539 home = get_hotkey_home();
540 g_return_val_if_fail (home != NULL, NULL);
541
542 filename = g_strconcat (app_id, HOTKEY_FILE_EXT, NULL);
543 file = g_file_get_child (home, filename);
544
545 g_object_unref (home);
546 g_free (filename);
547 return file;
548 }
549
550 static GKeyFile*
get_hotkey_key_file(const gchar * app_id,GError ** error)551 get_hotkey_key_file (const gchar *app_id, GError **error)
552 {
553 gchar *path;
554 GFile *file;
555 GKeyFile *keyfile = NULL;
556 GError *tmp_error;
557
558 g_return_val_if_fail (app_id != NULL, NULL);
559 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
560
561 file = get_hotkey_file (app_id);
562 if (!g_file_query_exists (file, NULL)) {
563 g_set_error (error, GTK_HOTKEY_REGISTRY_ERROR,
564 GTK_HOTKEY_REGISTRY_ERROR_UNKNOWN_APP,
565 "Application '%s' has not registered any hotkeys", app_id);
566 g_object_unref (file);
567 return NULL;
568 }
569
570 path = g_file_get_path (file);
571 keyfile = g_key_file_new ();
572
573 tmp_error = NULL;
574 g_key_file_load_from_file (keyfile, path, 0, &tmp_error);
575 if (tmp_error) {
576 g_set_error (error, GTK_HOTKEY_REGISTRY_ERROR,
577 GTK_HOTKEY_REGISTRY_ERROR_IO,
578 "Failed to load keyfile '%s': %s", path, tmp_error->message);
579 goto clean_up;
580 }
581
582 clean_up:
583 g_free (path);
584 g_object_unref (file);
585 if (tmp_error) g_error_free (tmp_error);
586
587 if (*error) {
588 g_key_file_free (keyfile);
589 return NULL;
590 }
591
592 return keyfile;
593 }
594
595 static GtkHotkeyInfo*
get_hotkey_info_from_key_file(GKeyFile * keyfile,const gchar * app_id,const gchar * key_id,GError ** error)596 get_hotkey_info_from_key_file (GKeyFile *keyfile,
597 const gchar *app_id,
598 const gchar *key_id,
599 GError **error)
600 {
601 GtkHotkeyInfo *info = NULL;
602 gchar *group, *description, *app_info_id, *signature;
603 GAppInfo *app_info = NULL;
604
605 g_return_val_if_fail (keyfile != NULL, NULL);
606 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
607 g_return_val_if_fail (app_id != NULL, NULL);
608 g_return_val_if_fail (key_id != NULL, NULL);
609
610 group = g_strconcat (HOTKEY_GROUP, key_id, NULL);
611 description = g_key_file_get_string (keyfile, group, "Description", NULL);
612 app_info_id = g_key_file_get_string (keyfile, group, "AppInfo", NULL);
613 signature = g_key_file_get_string (keyfile, group, "Signature", NULL);
614
615 if (!g_key_file_has_group (keyfile, group)) {
616 g_set_error (error, GTK_HOTKEY_REGISTRY_ERROR,
617 GTK_HOTKEY_REGISTRY_ERROR_UNKNOWN_KEY,
618 "Keyfile has no group "HOTKEY_GROUP"%s", key_id);
619 goto clean_up;
620 }
621
622 if (!signature) {
623 g_set_error (error, GTK_HOTKEY_REGISTRY_ERROR,
624 GTK_HOTKEY_REGISTRY_ERROR_BAD_SIGNATURE,
625 "No 'Signature' defined for hotkey '%s' for application '%s'",
626 key_id, app_id);
627 goto clean_up;
628 }
629
630 if (app_info_id) {
631 app_info = G_APP_INFO(g_desktop_app_info_new (app_info_id));
632 if (!G_IS_APP_INFO(app_info)) {
633 g_set_error (error, GTK_HOTKEY_REGISTRY_ERROR,
634 GTK_HOTKEY_REGISTRY_ERROR_MISSING_APP,
635 "Keyfile referring to 'AppInfo = %s', but no application"
636 "by that id is registered on the system", app_info_id);
637 goto clean_up;
638 }
639 }
640
641 info = gtk_hotkey_info_new (app_id, key_id, signature, app_info);
642 if (description)
643 gtk_hotkey_info_set_description (info, description);
644
645 clean_up:
646 g_free (group);
647 if (signature) g_free (signature);
648 if (description) g_free (description);
649 if (app_info_id) g_free (app_info_id);
650 if (app_info) g_object_unref (app_info);
651
652 return info;
653 }
654
655 static GList*
get_all_hotkey_infos_from_key_file(GKeyFile * keyfile,const gchar * app_id)656 get_all_hotkey_infos_from_key_file (GKeyFile *keyfile,
657 const gchar *app_id)
658 {
659 GList *hotkeys;
660 GtkHotkeyInfo *hotkey;
661 GStrv groups;
662 gsize count;
663 gchar *group;
664 GString *key_id;
665 GError *error;
666
667 g_return_val_if_fail (keyfile != NULL, NULL);
668 g_return_val_if_fail (app_id != NULL, NULL);
669
670 hotkeys = NULL;
671 groups = g_key_file_get_groups (keyfile, &count);
672
673 int i;
674 for (i = 0; i < count; i++) {
675 group = groups[i];
676 key_id = g_string_new (group);
677
678 /* Ignore non hotkey groups */
679 if (!g_str_has_prefix (key_id->str, HOTKEY_GROUP)) {
680 g_warning ("Hotkey file for %s contains non 'hotkey:' group '%s'",
681 app_id, group);
682 g_string_free (key_id, TRUE);
683 continue;
684 }
685
686 /* Strip 'hotkey:' prefix from key_id */
687 g_string_erase (key_id, 0, 7);
688
689 error = NULL;
690 hotkey = get_hotkey_info_from_key_file (keyfile, app_id, key_id->str, &error);
691 if (error) {
692 g_warning ("Failed to read hotkey '%s' for application '%s': %s",
693 key_id->str, app_id, error->message);
694 g_error_free (error);
695 g_string_free (key_id, TRUE);
696 continue;
697 }
698
699 hotkeys = g_list_prepend (hotkeys, hotkey);
700
701 g_string_free (key_id, TRUE);
702 }
703
704 g_strfreev (groups);
705 return hotkeys;
706 }
707