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