2 * This file is part of GtkHotkey.
3 * Copyright Mikkel Kamstrup Erlandsen, March, 2008
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.
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.
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/>.
23 #include "gtk-hotkey-key-file-registry.h"
24 #include "gtk-hotkey-utils.h"
27 #include <gio/gdesktopappinfo.h>
30 GTK_HOTKEY_KEY_FILE_REGISTRY_DUMMY_PROPERTY
33 /* Beware - the lengths of these strings are hardcoded throughout
34 * this file (sorry) */
35 #define HOTKEY_HOME "~/.config/hotkeys"
36 #define HOTKEY_FILE_EXT ".hotkeys"
37 #define HOTKEY_GROUP "hotkey:"
39 static GtkHotkeyInfo
* gtk_hotkey_key_file_registry_real_get_hotkey (GtkHotkeyRegistry
*base
,
44 static GList
* gtk_hotkey_key_file_registry_real_get_application_hotkeys
45 (GtkHotkeyRegistry
*base
,
49 static GList
* gtk_hotkey_key_file_registry_real_get_all_hotkeys
50 (GtkHotkeyRegistry
*base
);
52 static gboolean
gtk_hotkey_key_file_registry_real_store_hotkey(GtkHotkeyRegistry
*base
,
56 static gboolean gtk_hotkey_key_file_registry_real_delete_hotkey
57 (GtkHotkeyRegistry
*base
,
62 static gboolean
gtk_hotkey_key_file_registry_real_has_hotkey (GtkHotkeyRegistry
*base
,
66 static GFile
* get_hotkey_home (void);
68 static GFile
* get_hotkey_file (const gchar
*app_id
);
70 static GKeyFile
* get_hotkey_key_file (const gchar
*app_id
,
73 static GtkHotkeyInfo
* get_hotkey_info_from_key_file (GKeyFile
*keyfile
,
78 static GList
* get_all_hotkey_infos_from_key_file (GKeyFile
*keyfile
,
81 static gpointer gtk_hotkey_key_file_registry_parent_class
= NULL
;
86 gtk_hotkey_key_file_registry_real_get_hotkey (GtkHotkeyRegistry
*base
,
91 GKeyFile
*keyfile
= NULL
;
92 GtkHotkeyInfo
*info
= NULL
;
94 g_return_val_if_fail (GTK_HOTKEY_IS_KEY_FILE_REGISTRY(base
), NULL
);
95 g_return_val_if_fail (app_id
!= NULL
, NULL
);
96 g_return_val_if_fail (key_id
!= NULL
, NULL
);
98 keyfile
= get_hotkey_key_file (app_id
, error
);
102 info
= get_hotkey_info_from_key_file (keyfile
, app_id
, key_id
, error
);
105 if (keyfile
) g_key_file_free (keyfile
);
113 gtk_hotkey_key_file_registry_real_get_application_hotkeys (GtkHotkeyRegistry
*base
,
119 g_return_val_if_fail (app_id
!= NULL
, NULL
);
121 keyfile
= get_hotkey_key_file (app_id
, error
);
124 return NULL
; /* error is set by get_hotkey_key_file() */
126 return get_all_hotkey_infos_from_key_file (keyfile
, app_id
);
131 gtk_hotkey_key_file_registry_real_get_all_hotkeys (GtkHotkeyRegistry
*base
)
134 GFileEnumerator
*dir
;
135 GFileInfo
*file_info
;
137 GList
*result
= NULL
;
139 home
= get_hotkey_home ();
142 dir
= g_file_enumerate_children (home
, G_FILE_ATTRIBUTE_STANDARD_NAME
,
145 gchar
*path
= g_file_get_path (home
);
146 g_critical ("Failed to read hotkey home directory '%s': %s",
147 path
, error
->message
);
149 g_error_free (error
);
154 while ((file_info
= g_file_enumerator_next_file (dir
, NULL
, &error
)) != NULL
) {
155 const gchar
*filename
;
160 filename
= g_file_info_get_name(file_info
);
162 if (g_str_has_suffix (filename
, HOTKEY_FILE_EXT
)) {
163 file
= g_file_get_child (home
, filename
);
165 /* Extract app_id from file name */
166 app_id
= g_string_new (filename
);
167 g_string_erase (app_id
, app_id
->len
- 8, 8);
169 /* Load all hotkeys from the file, and append it to
170 * the total result */
171 app_hotkeys
= gtk_hotkey_registry_get_application_hotkeys (base
,
175 g_warning("failed to read hotkeys for application '%s': %s",
176 app_id
->str
, error
->message
);
177 g_error_free (error
);
180 result
= g_list_concat (result
, app_hotkeys
);
183 g_string_free (app_id
, TRUE
);
184 g_object_unref (file
);
187 g_object_unref (file_info
);
191 gchar
*path
= g_file_get_path (home
);
192 g_warning("failed to read hotkey home directory '%s': %s",
193 path
, error
->message
);
195 g_error_free (error
);
199 g_object_unref (dir
);
200 g_object_unref (home
);
207 gtk_hotkey_key_file_registry_real_store_hotkey (GtkHotkeyRegistry
*base
,
214 gchar
*file_path
, *group
= NULL
;
217 g_return_val_if_fail (GTK_HOTKEY_IS_INFO (info
), FALSE
);
218 g_return_val_if_fail (error
== NULL
|| *error
== NULL
, FALSE
);
220 /* Make sure we have our root dir */
221 home
= get_hotkey_home ();
222 if (!g_file_query_exists(home
, NULL
)) {
224 if (!g_file_make_directory (home
, NULL
, &tmp_error
)) {
225 g_set_error (error
, GTK_HOTKEY_REGISTRY_ERROR
,
226 GTK_HOTKEY_REGISTRY_ERROR_IO
,
227 "Failed to create hotkey configuration dir "
228 HOTKEY_HOME
": %s", tmp_error
->message
);
229 g_error_free (tmp_error
);
230 g_object_unref (home
);
235 /* Now load any old contents of the keyfile */
236 file
= get_hotkey_file (gtk_hotkey_info_get_application_id (info
));
237 file_path
= g_file_get_path (file
);
238 keyfile
= g_key_file_new ();
241 if (!g_key_file_load_from_file (keyfile
, file_path
, 0, &tmp_error
)) {
242 if (tmp_error
->code
== G_KEY_FILE_ERROR_PARSE
) {
243 g_set_error (error
, GTK_HOTKEY_REGISTRY_ERROR
,
244 GTK_HOTKEY_REGISTRY_ERROR_MALFORMED_MEDIUM
,
245 "The file %s is not in a valid key-file format: %s",
246 file_path
, tmp_error
->message
);
249 /* Ignore other errors */
250 g_error_free (tmp_error
);
253 /* Prepare keyfile data */
254 group
= g_strconcat (HOTKEY_GROUP
, gtk_hotkey_info_get_key_id (info
), NULL
);
256 g_key_file_set_string (keyfile
, group
, "Owner",
257 gtk_hotkey_info_get_application_id (info
));
258 g_key_file_set_string (keyfile
, group
, "Signature",
259 gtk_hotkey_info_get_signature (info
));
261 if (gtk_hotkey_info_get_description (info
))
262 g_key_file_set_string (keyfile
, group
, "Description",
263 gtk_hotkey_info_get_description (info
));
265 if (gtk_hotkey_info_get_app_info (info
)) {
266 GAppInfo
*ai
= gtk_hotkey_info_get_app_info (info
);
267 g_key_file_set_string (keyfile
, group
, "AppInfo",
268 g_app_info_get_id (ai
));
274 contents
= g_key_file_to_data (keyfile
, &size
, &tmp_error
);
276 g_set_error (error
, GTK_HOTKEY_REGISTRY_ERROR
,
277 GTK_HOTKEY_REGISTRY_ERROR_UNKNOWN
,
278 "Failed to generate keyfile contents: %s",
283 /* Write the actual data */
284 g_file_set_contents (file_path
, contents
, size
, &tmp_error
);
286 g_set_error (error
, GTK_HOTKEY_REGISTRY_ERROR
,
287 GTK_HOTKEY_REGISTRY_ERROR_IO
,
288 "Failed to write keyfile '%s': %s",
289 file_path
, tmp_error
->message
);
294 if (tmp_error
) g_error_free (tmp_error
);
296 if (group
) g_free (group
);
297 g_key_file_free (keyfile
);
298 g_object_unref (file
);
299 g_object_unref (home
);
304 g_return_val_if_fail (GTK_HOTKEY_IS_INFO (info
), FALSE
);
305 gtk_hotkey_registry_hotkey_stored (base
, info
);
310 gtk_hotkey_key_file_registry_real_delete_hotkey (GtkHotkeyRegistry
*base
,
315 GtkHotkeyInfo
*info
= NULL
;
319 gboolean is_error
= FALSE
;
322 g_return_val_if_fail (app_id
!= NULL
, FALSE
);
323 g_return_val_if_fail (key_id
!= NULL
, FALSE
);
324 g_return_val_if_fail (error
== NULL
|| *error
== NULL
, FALSE
);
328 file
= get_hotkey_file (app_id
);
329 g_return_val_if_fail (G_IS_FILE(file
), FALSE
);
331 path
= g_file_get_path (file
);
332 keyfile
= g_key_file_new ();
334 /* Load the old keyfile */
336 g_key_file_load_from_file (keyfile
, path
, 0, &tmp_error
);
338 if ((tmp_error
->domain
== G_FILE_ERROR
&&
339 tmp_error
->code
== G_FILE_ERROR_NOENT
) ||
340 (tmp_error
->domain
== G_KEY_FILE_ERROR
&&
341 tmp_error
->code
== G_KEY_FILE_ERROR_NOT_FOUND
))
342 g_set_error (error
, GTK_HOTKEY_REGISTRY_ERROR
,
343 GTK_HOTKEY_REGISTRY_ERROR_UNKNOWN_APP
,
344 "No such keyfile '%s'. Application '%s' has not "
345 "registered any hotkeys",
348 g_set_error (error
, GTK_HOTKEY_REGISTRY_ERROR
,
349 GTK_HOTKEY_REGISTRY_ERROR_IO
,
350 "Failed to load keyfile '%s': %s",
351 app_id
, tmp_error
->message
);
356 /* Get a ref to the GtkHotkeyInfo so that we can emit it with the
357 * hotkey-deleted signal */
359 info
= get_hotkey_info_from_key_file (keyfile
, app_id
, key_id
, error
);
365 /* Remove the group for key_id */
366 group
= g_strconcat (HOTKEY_GROUP
, key_id
, NULL
);
368 g_key_file_remove_group (keyfile
, group
, &tmp_error
);
370 if (tmp_error
->domain
== G_KEY_FILE_ERROR
&&
371 tmp_error
->code
== G_KEY_FILE_ERROR_GROUP_NOT_FOUND
)
372 g_set_error (error
, GTK_HOTKEY_REGISTRY_ERROR
,
373 GTK_HOTKEY_REGISTRY_ERROR_UNKNOWN_APP
,
374 "Application '%s' has not registered a hotkey with"
375 "id '%s'", app_id
, key_id
);
377 g_set_error (error
, GTK_HOTKEY_REGISTRY_ERROR
,
378 GTK_HOTKEY_REGISTRY_ERROR_UNKNOWN
,
379 "Failed to delete hotkey '%s' from application %s: %s",
380 key_id
, app_id
, tmp_error
->message
);
385 /* Check if the keyfile is empty. If it is we delete it */
388 groups
= g_key_file_get_groups (keyfile
, &count
);
392 g_file_delete (file
, NULL
, &tmp_error
);
395 g_set_error (error
, GTK_HOTKEY_REGISTRY_ERROR
,
396 GTK_HOTKEY_REGISTRY_ERROR_IO
,
397 "Failed to delete empty keyfile '%s': %s",
398 path
, tmp_error
->message
);
401 /* File deleted, we should just clean up and exit */
405 /* Write new keyfile */
409 contents
= g_key_file_to_data (keyfile
, &size
, &tmp_error
);
411 g_set_error (error
, GTK_HOTKEY_REGISTRY_ERROR
,
412 GTK_HOTKEY_REGISTRY_ERROR_UNKNOWN
,
413 "Failed to generate keyfile contents: %s",
420 if (g_file_set_contents (path
, contents
, size
, &tmp_error
) != TRUE
)
421 FILE_OP_ERROR(path
, "g_file_set_contents");
423 g_set_error (error
, GTK_HOTKEY_REGISTRY_ERROR
,
424 GTK_HOTKEY_REGISTRY_ERROR_IO
,
425 "Failed to write keyfile '%s': %s",
426 path
, tmp_error
->message
);
432 if (tmp_error
) g_error_free (tmp_error
);
433 g_object_unref (file
);
435 if (group
) g_free (group
);
436 g_key_file_free (keyfile
);
441 gtk_hotkey_registry_hotkey_deleted (base
, info
);
442 g_object_unref (info
);
447 gtk_hotkey_key_file_registry_real_has_hotkey (GtkHotkeyRegistry
*base
,
454 g_return_val_if_fail (app_id
!= NULL
, FALSE
);
455 g_return_val_if_fail (key_id
!= NULL
, FALSE
);
457 file
= get_hotkey_file (app_id
);
458 g_return_val_if_fail (G_IS_FILE(file
), FALSE
);
460 if (g_file_query_exists (file
, NULL
))
465 g_object_unref (file
);
470 gtk_hotkey_key_file_registry_class_init (GtkHotkeyKeyFileRegistryClass
*klass
)
472 gtk_hotkey_key_file_registry_parent_class
= g_type_class_peek_parent (klass
);
473 GTK_HOTKEY_REGISTRY_CLASS (klass
)->get_hotkey
= gtk_hotkey_key_file_registry_real_get_hotkey
;
474 GTK_HOTKEY_REGISTRY_CLASS (klass
)->get_application_hotkeys
= gtk_hotkey_key_file_registry_real_get_application_hotkeys
;
475 GTK_HOTKEY_REGISTRY_CLASS (klass
)->get_all_hotkeys
= gtk_hotkey_key_file_registry_real_get_all_hotkeys
;
476 GTK_HOTKEY_REGISTRY_CLASS (klass
)->store_hotkey
= gtk_hotkey_key_file_registry_real_store_hotkey
;
477 GTK_HOTKEY_REGISTRY_CLASS (klass
)->delete_hotkey
= gtk_hotkey_key_file_registry_real_delete_hotkey
;
478 GTK_HOTKEY_REGISTRY_CLASS (klass
)->has_hotkey
= gtk_hotkey_key_file_registry_real_has_hotkey
;
483 gtk_hotkey_key_file_registry_init (GtkHotkeyKeyFileRegistry
*self
)
489 gtk_hotkey_key_file_registry_finalize (GtkHotkeyKeyFileRegistry
*self
)
495 gtk_hotkey_key_file_registry_get_type (void)
497 static GType gtk_hotkey_key_file_registry_type_id
= 0;
499 if (G_UNLIKELY (gtk_hotkey_key_file_registry_type_id
== 0)) {
500 static const GTypeInfo g_define_type_info
= {
501 sizeof (GtkHotkeyKeyFileRegistryClass
),
502 (GBaseInitFunc
) gtk_hotkey_key_file_registry_init
,
503 (GBaseFinalizeFunc
) gtk_hotkey_key_file_registry_finalize
,
504 (GClassInitFunc
) gtk_hotkey_key_file_registry_class_init
,
505 (GClassFinalizeFunc
) NULL
,
507 sizeof (GtkHotkeyKeyFileRegistry
),
509 (GInstanceInitFunc
) gtk_hotkey_key_file_registry_init
,
510 (const GTypeValueTable
*) NULL
/* value table */
513 gtk_hotkey_key_file_registry_type_id
= g_type_register_static (GTK_HOTKEY_TYPE_STORAGE
, "GtkHotkeyKeyFileRegistry", &g_define_type_info
, 0);
515 return gtk_hotkey_key_file_registry_type_id
;
519 get_hotkey_home (void)
523 home
= g_file_parse_name (HOTKEY_HOME
);
525 if (g_file_query_exists(home
, NULL
) &&
526 !gtk_hotkey_g_file_is_directory(home
)) {
527 g_critical (HOTKEY_HOME
" exists but is not a directory");
528 g_object_unref (home
);
535 /* It is not guaranteed that the keyfile exists */
537 get_hotkey_file (const gchar
*app_id
)
542 g_return_val_if_fail (app_id
!= NULL
, NULL
);
544 home
= get_hotkey_home();
545 g_return_val_if_fail (home
!= NULL
, NULL
);
547 filename
= g_strconcat (app_id
, HOTKEY_FILE_EXT
, NULL
);
548 file
= g_file_get_child (home
, filename
);
550 g_object_unref (home
);
556 get_hotkey_key_file (const gchar
*app_id
, GError
**error
)
560 GKeyFile
*keyfile
= NULL
;
563 g_return_val_if_fail (app_id
!= NULL
, NULL
);
564 g_return_val_if_fail (error
== NULL
|| *error
== NULL
, NULL
);
566 file
= get_hotkey_file (app_id
);
567 if (!g_file_query_exists (file
, NULL
)) {
568 g_set_error (error
, GTK_HOTKEY_REGISTRY_ERROR
,
569 GTK_HOTKEY_REGISTRY_ERROR_UNKNOWN_APP
,
570 "Application '%s' has not registered any hotkeys", app_id
);
571 g_object_unref (file
);
575 path
= g_file_get_path (file
);
576 keyfile
= g_key_file_new ();
579 g_key_file_load_from_file (keyfile
, path
, 0, &tmp_error
);
581 g_set_error (error
, GTK_HOTKEY_REGISTRY_ERROR
,
582 GTK_HOTKEY_REGISTRY_ERROR_IO
,
583 "Failed to load keyfile '%s': %s", path
, tmp_error
->message
);
589 g_object_unref (file
);
590 if (tmp_error
) g_error_free (tmp_error
);
593 g_key_file_free (keyfile
);
600 static GtkHotkeyInfo
*
601 get_hotkey_info_from_key_file (GKeyFile
*keyfile
,
606 GtkHotkeyInfo
*info
= NULL
;
607 gchar
*group
, *description
, *app_info_id
, *signature
;
608 GAppInfo
*app_info
= NULL
;
610 g_return_val_if_fail (keyfile
!= NULL
, NULL
);
611 g_return_val_if_fail (error
== NULL
|| *error
== NULL
, NULL
);
612 g_return_val_if_fail (app_id
!= NULL
, NULL
);
613 g_return_val_if_fail (key_id
!= NULL
, NULL
);
615 group
= g_strconcat (HOTKEY_GROUP
, key_id
, NULL
);
616 description
= g_key_file_get_string (keyfile
, group
, "Description", NULL
);
617 app_info_id
= g_key_file_get_string (keyfile
, group
, "AppInfo", NULL
);
618 signature
= g_key_file_get_string (keyfile
, group
, "Signature", NULL
);
620 if (!g_key_file_has_group (keyfile
, group
)) {
621 g_set_error (error
, GTK_HOTKEY_REGISTRY_ERROR
,
622 GTK_HOTKEY_REGISTRY_ERROR_UNKNOWN_KEY
,
623 "Keyfile has no group "HOTKEY_GROUP
"%s", key_id
);
628 g_set_error (error
, GTK_HOTKEY_REGISTRY_ERROR
,
629 GTK_HOTKEY_REGISTRY_ERROR_BAD_SIGNATURE
,
630 "No 'Signature' defined for hotkey '%s' for application '%s'",
636 app_info
= G_APP_INFO(g_desktop_app_info_new (app_info_id
));
637 if (!G_IS_APP_INFO(app_info
)) {
638 g_set_error (error
, GTK_HOTKEY_REGISTRY_ERROR
,
639 GTK_HOTKEY_REGISTRY_ERROR_MISSING_APP
,
640 "Keyfile referring to 'AppInfo = %s', but no application"
641 "by that id is registered on the system", app_info_id
);
646 info
= gtk_hotkey_info_new (app_id
, key_id
, signature
, app_info
);
648 gtk_hotkey_info_set_description (info
, description
);
652 if (signature
) g_free (signature
);
653 if (description
) g_free (description
);
654 if (app_info_id
) g_free (app_info_id
);
655 if (app_info
) g_object_unref (app_info
);
661 get_all_hotkey_infos_from_key_file (GKeyFile
*keyfile
,
665 GtkHotkeyInfo
*hotkey
;
672 g_return_val_if_fail (keyfile
!= NULL
, NULL
);
673 g_return_val_if_fail (app_id
!= NULL
, NULL
);
676 groups
= g_key_file_get_groups (keyfile
, &count
);
679 for (i
= 0; i
< count
; i
++) {
681 key_id
= g_string_new (group
);
683 /* Ignore non hotkey groups */
684 if (!g_str_has_prefix (key_id
->str
, HOTKEY_GROUP
)) {
685 g_warning("hotkey file for %s contains non 'hotkey:' group '%s'",
687 g_string_free (key_id
, TRUE
);
691 /* Strip 'hotkey:' prefix from key_id */
692 g_string_erase (key_id
, 0, 7);
695 hotkey
= get_hotkey_info_from_key_file (keyfile
, app_id
, key_id
->str
, &error
);
697 g_warning("failed to read hotkey '%s' for application '%s': %s",
698 key_id
->str
, app_id
, error
->message
);
699 g_error_free (error
);
700 g_string_free (key_id
, TRUE
);
704 hotkeys
= g_list_prepend (hotkeys
, hotkey
);
706 g_string_free (key_id
, TRUE
);