[Commits] .cvsignore NONE 1.1.2.1 Makefile.am NONE 1.1.2.1 gtk-hotkey-error.c NONE 1.1.2.1 gtk-hotkey-error.h NONE 1.1.2.1 gtk-hotkey-info.c NONE 1.1.2.1 gtk-hotkey-info.h NONE 1.1.2.1 gtk-hotkey-key-file-registry.c NONE 1.1.2.1 gtk-hotkey-key-file-registry.h NONE 1.1.2.1 gtk-hotkey-listener.c NONE 1.1.2.1 gtk-hotkey-listener.h NONE 1.1.2.1 gtk-hotkey-marshal.c NONE 1.1.2.1 gtk-hotkey-marshal.h NONE 1.1.2.1 gtk-hotkey-registry.c NONE 1.1.2.1 gtk-hotkey-registry.h NONE 1.1.2.1 gtk-hotkey-utils.c NONE 1.1.2.1 gtk-hotkey-utils.h NONE 1.1.2.1 gtk-hotkey-x11-listener.c NONE 1.1.2.1 gtk-hotkey-x11-listener.h NONE 1.1.2.1 gtkhotkey.h NONE 1.1.2.1

colin at claws-mail.org colin at claws-mail.org
Wed Feb 20 12:25:38 CET 2013


Update of /home/claws-mail/claws/src/plugins/notification/gtkhotkey
In directory srv:/tmp/cvs-serv11307/src/plugins/notification/gtkhotkey

Added Files:
      Tag: gtk2
	.cvsignore Makefile.am gtk-hotkey-error.c gtk-hotkey-error.h 
	gtk-hotkey-info.c gtk-hotkey-info.h 
	gtk-hotkey-key-file-registry.c gtk-hotkey-key-file-registry.h 
	gtk-hotkey-listener.c gtk-hotkey-listener.h 
	gtk-hotkey-marshal.c gtk-hotkey-marshal.h 
	gtk-hotkey-registry.c gtk-hotkey-registry.h gtk-hotkey-utils.c 
	gtk-hotkey-utils.h gtk-hotkey-x11-listener.c 
	gtk-hotkey-x11-listener.h gtkhotkey.h 
Log Message:
2013-02-20 [colin]	3.9.0cvs85

	* configure.ac
		Rework plugin enabling logic. More clean!
	* src/plugins/fancy/fancy_viewer.c
	* src/plugins/fancy/fancy_viewer.h
	* src/plugins/spam_report/spam_report.c
		Make curl dependancy mandatory
	* src/plugins/notification/notification_plugin.c
		Fix build with every possible thing disabled
	* src/plugins/notification/Makefile.am
	* src/plugins/notification/gtkhotkey/.cvsignore
	* src/plugins/notification/gtkhotkey/Makefile.am
	* src/plugins/notification/gtkhotkey/gtk-hotkey-error.c
	* src/plugins/notification/gtkhotkey/gtk-hotkey-error.h
	* src/plugins/notification/gtkhotkey/gtk-hotkey-info.c
	* src/plugins/notification/gtkhotkey/gtk-hotkey-info.h
	* src/plugins/notification/gtkhotkey/gtk-hotkey-key-file-registry.c
	* src/plugins/notification/gtkhotkey/gtk-hotkey-key-file-registry.h
	* src/plugins/notification/gtkhotkey/gtk-hotkey-listener.c
	* src/plugins/notification/gtkhotkey/gtk-hotkey-listener.h
	* src/plugins/notification/gtkhotkey/gtk-hotkey-marshal.c
	* src/plugins/notification/gtkhotkey/gtk-hotkey-marshal.h
	* src/plugins/notification/gtkhotkey/gtk-hotkey-registry.c
	* src/plugins/notification/gtkhotkey/gtk-hotkey-registry.h
	* src/plugins/notification/gtkhotkey/gtk-hotkey-utils.c
	* src/plugins/notification/gtkhotkey/gtk-hotkey-utils.h
	* src/plugins/notification/gtkhotkey/gtk-hotkey-x11-listener.c
	* src/plugins/notification/gtkhotkey/gtk-hotkey-x11-listener.h
	* src/plugins/notification/gtkhotkey/gtkhotkey.h
	* src/plugins/notification/gtkhotkey/x11/eggaccelerators.c
	* src/plugins/notification/gtkhotkey/x11/eggaccelerators.h
	* src/plugins/notification/gtkhotkey/x11/tomboykeybinder.c
	* src/plugins/notification/gtkhotkey/x11/tomboykeybinder.h
		Re-add hotkey support
	* src/plugins/spam_report/Makefile.am
	* src/plugins/tnef_parse/Makefile.am
		Remove gettext.h

--- NEW FILE: gtk-hotkey-utils.c ---
/*
 * This file is part of GtkHotkey.
 * Copyright Mikkel Kamstrup Erlandsen, March, 2008
 *
 *   GtkHotkey is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Lesser General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   GtkHotkey is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Lesser General Public License for more details.
 *
 *   You should have received a copy of the GNU Lesser General Public License
 *   along with GtkHotkey.  If not, see <http://www.gnu.org/licenses/>.
 */
 
#include <gio/gio.h>

GFileType
gtk_hotkey_g_file_get_type (GFile *file)
{
	GFileInfo   *info;
	GFileType   type;
	GError		*error;
	
	g_return_val_if_fail (G_IS_FILE(file), G_FILE_TYPE_UNKNOWN);
	
	if (!g_file_query_exists(file, NULL))
		return G_FILE_TYPE_UNKNOWN;
	
	g_return_val_if_fail (G_IS_FILE(file), G_FILE_TYPE_UNKNOWN);
	error = NULL;
	info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_TYPE,
							  0, NULL, &error);
	
	if (error) {
		g_critical ("Failed to create GFileInfo: %s", error->message);
		g_error_free (error);
		return G_FILE_TYPE_UNKNOWN;
	}
	
	type = g_file_info_get_file_type (info);
	g_object_unref (info);
	
	return type;
}

--- NEW FILE: .cvsignore ---
.deps
.libs
Makefile
Makefile.in
*.la
*.lo
*.o

--- NEW FILE: gtk-hotkey-key-file-registry.c ---
/*
 * This file is part of GtkHotkey.
 * Copyright Mikkel Kamstrup Erlandsen, March, 2008
 *
 *   GtkHotkey is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Lesser General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   GtkHotkey is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Lesser General Public License for more details.
 *
 *   You should have received a copy of the GNU Lesser General Public License
 *   along with GtkHotkey.  If not, see <http://www.gnu.org/licenses/>.
 */
 
#include "gtk-hotkey-key-file-registry.h"
#include "gtk-hotkey-utils.h"

#include <gio/gio.h>
#include <gio/gdesktopappinfo.h>

enum  {
	GTK_HOTKEY_KEY_FILE_REGISTRY_DUMMY_PROPERTY
};

/* Beware - the lengths of these strings are hardcoded throughout
 * this file (sorry) */
#define HOTKEY_HOME "~/.config/hotkeys"
#define HOTKEY_FILE_EXT ".hotkeys"
#define HOTKEY_GROUP "hotkey:"

static GtkHotkeyInfo*	gtk_hotkey_key_file_registry_real_get_hotkey	(GtkHotkeyRegistry	*base,
																	 const char			*app_id,
																	 const char			*key_id,
																	 GError				**error);

static GList*			gtk_hotkey_key_file_registry_real_get_application_hotkeys
																	(GtkHotkeyRegistry	*base,
																	 const char			*app_id,
																	 GError				**error);

static GList*			gtk_hotkey_key_file_registry_real_get_all_hotkeys
																	(GtkHotkeyRegistry	*base);

static gboolean			gtk_hotkey_key_file_registry_real_store_hotkey(GtkHotkeyRegistry	*base,
																	 GtkHotkeyInfo		*info,
																	 GError				**error);

static gboolean			gtk_hotkey_key_file_registry_real_delete_hotkey
																	(GtkHotkeyRegistry	*base,
																	 const gchar		*app_id,
																	 const gchar		*key_id,
																	 GError				**error);

static gboolean			gtk_hotkey_key_file_registry_real_has_hotkey  (GtkHotkeyRegistry	*base,
																	 const gchar		*app_id,
																	 const gchar		*key_id);

static GFile*			get_hotkey_home								(void);

static GFile*			get_hotkey_file								(const gchar		*app_id);

static GKeyFile*		get_hotkey_key_file							(const gchar		*app_id,
																	 GError				**error);

static GtkHotkeyInfo*   get_hotkey_info_from_key_file				(GKeyFile			*keyfile,
																	 const gchar		*app_id,
																	 const gchar		*key_id,
																	 GError				**error);

static GList*			get_all_hotkey_infos_from_key_file			(GKeyFile			*keyfile,
																	 const gchar		*app_id);

static gpointer gtk_hotkey_key_file_registry_parent_class = NULL;


static
GtkHotkeyInfo*
gtk_hotkey_key_file_registry_real_get_hotkey (GtkHotkeyRegistry	*base,
											const char			*app_id,
											const char			*key_id,
											GError				**error)
{
	GtkHotkeyKeyFileRegistry	*self;
	GKeyFile					*keyfile = NULL;
	GtkHotkeyInfo				*info = NULL;
	
	g_return_val_if_fail (GTK_HOTKEY_IS_KEY_FILE_REGISTRY(base), NULL);
	g_return_val_if_fail (app_id != NULL, NULL);
	g_return_val_if_fail (key_id != NULL, NULL);
	
	self = GTK_HOTKEY_KEY_FILE_REGISTRY (base);
	
	keyfile = get_hotkey_key_file (app_id, error);
	if (keyfile == NULL)
		goto clean_up;
	
	info = get_hotkey_info_from_key_file (keyfile, app_id, key_id, error);
	
	clean_up:
		if (keyfile) g_key_file_free (keyfile);
	
	return info;
	
}


static GList*
gtk_hotkey_key_file_registry_real_get_application_hotkeys (GtkHotkeyRegistry	*base,
														 const char			*app_id,
														 GError				**error)
{
	GtkHotkeyKeyFileRegistry		*self;
	GKeyFile					*keyfile;
	
	g_return_val_if_fail (app_id != NULL, NULL);
	
	self = GTK_HOTKEY_KEY_FILE_REGISTRY (base);
	keyfile = get_hotkey_key_file (app_id, error);
	
	if (keyfile == NULL)
		return NULL; /* error is set by get_hotkey_key_file() */
	
	return get_all_hotkey_infos_from_key_file (keyfile, app_id);
}


static GList*
gtk_hotkey_key_file_registry_real_get_all_hotkeys (GtkHotkeyRegistry *base)
{
	GtkHotkeyKeyFileRegistry *self;
	GFile					*home;
	GFileEnumerator			*dir;
	GFileInfo				*file_info;
	GError					*error;
	GList					*result = NULL;
	
	self = GTK_HOTKEY_KEY_FILE_REGISTRY (base);
	home = get_hotkey_home ();
	
	error = NULL;
	dir = g_file_enumerate_children (home, G_FILE_ATTRIBUTE_STANDARD_NAME,
									 0, NULL, &error);
	if (error) {
		gchar *path = g_file_get_path (home);
		g_critical ("Failed to read hotkey home directory '%s': %s",
					path, error->message);
		g_free (path);
		g_error_free (error);
		return NULL;
	}
	
	error = NULL;
	while ((file_info = g_file_enumerator_next_file (dir, NULL, &error)) != NULL) {
		const gchar *filename;
		GFile		*file;
		GString		*app_id;
		GList		*app_hotkeys;
		
		filename = g_file_info_get_name(file_info);
		
		if (g_str_has_suffix (filename, HOTKEY_FILE_EXT)) {
			file = g_file_get_child (home, filename);
			
			/* Extract app_id from file name */
			app_id = g_string_new (filename);
			g_string_erase (app_id, app_id->len - 8, 8);
			
			/* Load all hotkeys from the file, and append it to
			 * the total result */
			app_hotkeys = gtk_hotkey_registry_get_application_hotkeys (base,
																	  app_id->str,
																	  &error);
			if (error) {
				g_warning ("Failed to read hotkeys for application '%s': %s",
						   app_id->str, error->message);
				g_error_free (error);
				error = NULL;
			} else {
				result = g_list_concat (result, app_hotkeys);
			}
			
			g_string_free (app_id, TRUE);
			g_object_unref (file);
		}
		
		g_object_unref (file_info);
	}
	
	if (error) {
		gchar *path = g_file_get_path (home);
		g_warning ("Failed to read hotkey home directory '%s': %s",
				   path, error->message);
		g_free (path);
		g_error_free (error);
	}
	
	
	g_object_unref (dir);
	g_object_unref (home);
	
	return result;
}


static gboolean
gtk_hotkey_key_file_registry_real_store_hotkey (GtkHotkeyRegistry	*base,
											  GtkHotkeyInfo		*info,
											  GError			**error)
{
	GtkHotkeyKeyFileRegistry	*self;
	GKeyFile					*keyfile;
	GFile						*file, *home;
	GError						*tmp_error;
	gchar						*file_path, *group;
	
	
	self = GTK_HOTKEY_KEY_FILE_REGISTRY (base);
	g_return_val_if_fail (GTK_HOTKEY_IS_INFO (info), FALSE);
	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
	
	/* Make sure we have our root dir */
	home = get_hotkey_home ();
	if (!g_file_query_exists(home, NULL)) {
		tmp_error = NULL;
		if (!g_file_make_directory (home, NULL, &tmp_error)) {
			g_set_error (error, GTK_HOTKEY_REGISTRY_ERROR,
						 GTK_HOTKEY_REGISTRY_ERROR_IO,
						 "Failed to create hotkey configuration dir "
						HOTKEY_HOME": %s", tmp_error->message);
			g_error_free (tmp_error);
			g_object_unref (home);
			return FALSE;
		}
	}
	
	/* Now load any old contents of the keyfile */
	file = get_hotkey_file (gtk_hotkey_info_get_application_id (info));
	file_path = g_file_get_path (file);
	keyfile = g_key_file_new ();
	
	tmp_error = NULL;
	if (!g_key_file_load_from_file (keyfile, file_path, 0, &tmp_error)) {
		if (tmp_error->code == G_KEY_FILE_ERROR_PARSE) {
			g_set_error (error, GTK_HOTKEY_REGISTRY_ERROR,
						 GTK_HOTKEY_REGISTRY_ERROR_MALFORMED_MEDIUM,
						 "The file %s is not in a valid key-file format: %s",
						 file_path, tmp_error->message);
			goto clean_up;
		}
		/* Ignore other errors */
		g_error_free (tmp_error);
	}
	
	/* Prepare keyfile data */
	group = g_strconcat (HOTKEY_GROUP, gtk_hotkey_info_get_key_id (info), NULL);
	
	g_key_file_set_string (keyfile, group, "Owner",
						   gtk_hotkey_info_get_application_id (info));
	g_key_file_set_string (keyfile, group, "Signature",
						   gtk_hotkey_info_get_signature (info));
	
	if (gtk_hotkey_info_get_description (info))
		g_key_file_set_string (keyfile, group, "Description",
							   gtk_hotkey_info_get_description (info));
	
	if (gtk_hotkey_info_get_app_info (info)) {
		GAppInfo *ai = gtk_hotkey_info_get_app_info (info);
		g_key_file_set_string (keyfile, group, "AppInfo",
							   g_app_info_get_id (ai));
	}
	
	gsize size;
	gchar *contents;
	tmp_error = NULL;
	contents = g_key_file_to_data (keyfile, &size, &tmp_error);
	if (tmp_error) {
		g_set_error (error, GTK_HOTKEY_REGISTRY_ERROR,
					 GTK_HOTKEY_REGISTRY_ERROR_UNKNOWN,
					 "Failed to generate keyfile contents: %s",
					 tmp_error->message);
		goto clean_up;
	}
	
	/* Write the actual data */
	g_file_set_contents (file_path, contents, size, &tmp_error);
	if (tmp_error) {
		g_set_error (error, GTK_HOTKEY_REGISTRY_ERROR,
					 GTK_HOTKEY_REGISTRY_ERROR_IO,
					 "Failed to write keyfile '%s': %s",
					 file_path, tmp_error->message);
		goto clean_up;
	}
	
	clean_up:
		if (tmp_error) g_error_free (tmp_error);
		g_free (file_path);
		if (group) g_free (group);
		g_key_file_free (keyfile);
		g_object_unref (file);
		g_object_unref (home);
	
	if (*error)
		return FALSE;
	
			self = GTK_HOTKEY_KEY_FILE_REGISTRY (base);
	g_return_val_if_fail (GTK_HOTKEY_IS_INFO (info), FALSE);
	gtk_hotkey_registry_hotkey_stored (base, info);
	return TRUE;
}

static gboolean
gtk_hotkey_key_file_registry_real_delete_hotkey (GtkHotkeyRegistry	*base,
											   const gchar		*app_id,
											   const gchar		*key_id,
											   GError			**error)
{
	GtkHotkeyKeyFileRegistry *self;
	GtkHotkeyInfo			*info = NULL;
	GFile					*file;
	GKeyFile				*keyfile;
	GError					*tmp_error;
	gboolean				is_error = FALSE;
	gchar					*path, *group;
	
	g_return_val_if_fail (app_id != NULL, FALSE);
	g_return_val_if_fail (key_id != NULL, FALSE);
	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
	
	self = GTK_HOTKEY_KEY_FILE_REGISTRY (base);
	group = NULL;
	
	file = get_hotkey_file (app_id);
	g_return_val_if_fail (G_IS_FILE(file), FALSE);
	
	path = g_file_get_path (file);
	keyfile = g_key_file_new ();
	
	/* Load the old keyfile */
	tmp_error = NULL;
	g_key_file_load_from_file (keyfile, path, 0, &tmp_error);
	if (tmp_error) {
		if ((tmp_error->domain == G_FILE_ERROR &&
			 tmp_error->code == G_FILE_ERROR_NOENT) ||
			(tmp_error->domain == G_KEY_FILE_ERROR &&
			 tmp_error->code == G_KEY_FILE_ERROR_NOT_FOUND))
			g_set_error (error, GTK_HOTKEY_REGISTRY_ERROR,
						 GTK_HOTKEY_REGISTRY_ERROR_UNKNOWN_APP,
						 "No such keyfile '%s'. Application '%s' has not "
						 "registered any hotkeys",
						 path, app_id);
		else
			g_set_error (error, GTK_HOTKEY_REGISTRY_ERROR,
						 GTK_HOTKEY_REGISTRY_ERROR_IO,
						 "Failed to load keyfile '%s': %s",
						 app_id, tmp_error->message);
		is_error = TRUE;
		goto clean_up;
	}
	
	/* Get a ref to the GtkHotkeyInfo so that we can emit it with the
	 * hotkey-deleted signal */
	tmp_error = NULL;
	info = get_hotkey_info_from_key_file (keyfile, app_id, key_id, error);
	if (info == NULL) {
		is_error = TRUE;
		goto clean_up;
	}
	
	/* Remove the group for key_id */
	group = g_strconcat (HOTKEY_GROUP, key_id, NULL);
	tmp_error = NULL;
	g_key_file_remove_group (keyfile, group, &tmp_error);
	if (tmp_error) {
		if (tmp_error->domain == G_KEY_FILE_ERROR &&
			 tmp_error->code == G_KEY_FILE_ERROR_GROUP_NOT_FOUND)
			g_set_error (error, GTK_HOTKEY_REGISTRY_ERROR,
						 GTK_HOTKEY_REGISTRY_ERROR_UNKNOWN_APP,
						 "Application '%s' has not registered a hotkey with"
						 "id '%s'", app_id, key_id);
		else
			g_set_error (error, GTK_HOTKEY_REGISTRY_ERROR,
						 GTK_HOTKEY_REGISTRY_ERROR_UNKNOWN,
						 "Failed to delete hotkey '%s' from application %s: %s",
						 key_id, app_id, tmp_error->message);
		is_error = TRUE;
		goto clean_up;
	}
	
	/* Check if the keyfile is empty. If it is we delete it */
	gsize count;
	GStrv groups;
	groups = g_key_file_get_groups (keyfile, &count);
	g_strfreev (groups);
	if (count == 0) {
		tmp_error = NULL;
		g_file_delete (file, NULL, &tmp_error);
		
		if (tmp_error) {
			g_set_error (error, GTK_HOTKEY_REGISTRY_ERROR,
						 GTK_HOTKEY_REGISTRY_ERROR_IO,
						 "Failed to delete empty keyfile '%s': %s",
						 path, tmp_error->message);
			is_error = TRUE;
		}
		/* File deleted, we should just clean up and exit */
		goto clean_up;
	}
	
	/* Write new keyfile */
	gsize size;
	gchar *contents;
	tmp_error = NULL;
	contents = g_key_file_to_data (keyfile, &size, &tmp_error);
	if (tmp_error) {
		g_set_error (error, GTK_HOTKEY_REGISTRY_ERROR,
					 GTK_HOTKEY_REGISTRY_ERROR_UNKNOWN,
					 "Failed to generate keyfile contents: %s",
					 tmp_error->message);
		is_error = TRUE;
		goto clean_up;
	}
	
	tmp_error = NULL;
	g_file_set_contents (path, contents, size, &tmp_error);
	if (tmp_error) {
		g_set_error (error, GTK_HOTKEY_REGISTRY_ERROR,
					 GTK_HOTKEY_REGISTRY_ERROR_IO,
					 "Failed to write keyfile '%s': %s",
					 path, tmp_error->message);
		is_error = TRUE;
		goto clean_up;
	}
	
	clean_up:
		if (tmp_error) g_error_free (tmp_error);
		g_object_unref (file);
		g_free (path);
		if (group) g_free (group);
		g_key_file_free (keyfile);
		
	if (is_error)
		return FALSE;
	
	gtk_hotkey_registry_hotkey_deleted (base, info);
	g_object_unref (info);
	return TRUE;
}

static gboolean
gtk_hotkey_key_file_registry_real_has_hotkey (GtkHotkeyRegistry	*base,
											const gchar			*app_id,
											const gchar			*key_id)
{
	GtkHotkeyKeyFileRegistry *self;
	GFile					*file;
	gboolean				exists;
	
	g_return_val_if_fail (app_id != NULL, FALSE);
	g_return_val_if_fail (key_id != NULL, FALSE);
	
	self = GTK_HOTKEY_KEY_FILE_REGISTRY (base);
	
	file = get_hotkey_file (app_id);
	g_return_val_if_fail (G_IS_FILE(file), FALSE);
	
	if (g_file_query_exists (file, NULL))
		exists = TRUE;
	else
		exists = FALSE;
	
	g_object_unref (file);
	return exists;
}

static void
gtk_hotkey_key_file_registry_class_init (GtkHotkeyKeyFileRegistryClass *klass)
{
	gtk_hotkey_key_file_registry_parent_class = g_type_class_peek_parent (klass);
	GTK_HOTKEY_REGISTRY_CLASS (klass)->get_hotkey = gtk_hotkey_key_file_registry_real_get_hotkey;
	GTK_HOTKEY_REGISTRY_CLASS (klass)->get_application_hotkeys = gtk_hotkey_key_file_registry_real_get_application_hotkeys;
	GTK_HOTKEY_REGISTRY_CLASS (klass)->get_all_hotkeys = gtk_hotkey_key_file_registry_real_get_all_hotkeys;
	GTK_HOTKEY_REGISTRY_CLASS (klass)->store_hotkey = gtk_hotkey_key_file_registry_real_store_hotkey;
	GTK_HOTKEY_REGISTRY_CLASS (klass)->delete_hotkey = gtk_hotkey_key_file_registry_real_delete_hotkey;
	GTK_HOTKEY_REGISTRY_CLASS (klass)->has_hotkey = gtk_hotkey_key_file_registry_real_has_hotkey;
}


static void
gtk_hotkey_key_file_registry_init (GtkHotkeyKeyFileRegistry *self)
{
	
}

static void
gtk_hotkey_key_file_registry_finalize (GtkHotkeyKeyFileRegistry *self)
{
	
}

GType
gtk_hotkey_key_file_registry_get_type (void)
{
	static GType gtk_hotkey_key_file_registry_type_id = 0;
	
	if (G_UNLIKELY (gtk_hotkey_key_file_registry_type_id == 0)) {
		static const GTypeInfo g_define_type_info = {
			sizeof (GtkHotkeyKeyFileRegistryClass),
			(GBaseInitFunc) gtk_hotkey_key_file_registry_init,
			(GBaseFinalizeFunc) gtk_hotkey_key_file_registry_finalize,
			(GClassInitFunc) gtk_hotkey_key_file_registry_class_init,
			(GClassFinalizeFunc) NULL,
			NULL,
			sizeof (GtkHotkeyKeyFileRegistry),
			0,
			(GInstanceInitFunc) gtk_hotkey_key_file_registry_init
		};
		
		gtk_hotkey_key_file_registry_type_id = g_type_register_static (GTK_HOTKEY_TYPE_STORAGE, "GtkHotkeyKeyFileRegistry", &g_define_type_info, 0);
	}
	return gtk_hotkey_key_file_registry_type_id;
}

static GFile*
get_hotkey_home (void)
{
	GFile   *home;
	
	home = g_file_parse_name (HOTKEY_HOME);
	
	if (g_file_query_exists(home, NULL) &&
		!gtk_hotkey_g_file_is_directory(home)) {
		g_critical (HOTKEY_HOME" exists but is not a directory");
		g_object_unref (home);
		return NULL;
	}
	
	return home;
}

/* It is not guaranteed that the keyfile exists */
static GFile*
get_hotkey_file (const gchar *app_id)
{
	GFile   *home, *file;
	gchar   *filename;
	
	g_return_val_if_fail (app_id != NULL, NULL);
	
	home = get_hotkey_home();
	g_return_val_if_fail (home != NULL, NULL);
	
	filename = g_strconcat (app_id, HOTKEY_FILE_EXT, NULL);
	file = g_file_get_child (home, filename);
	
	g_object_unref (home);
	g_free (filename);
	return file;
}

static GKeyFile*
get_hotkey_key_file (const gchar *app_id, GError **error)
{
	gchar		*path;
	GFile		*file;
	GKeyFile	*keyfile = NULL;
	GError		*tmp_error;
	
	g_return_val_if_fail (app_id != NULL, NULL);
	g_return_val_if_fail (error == NULL || *error == NULL, NULL);
	
	file = get_hotkey_file (app_id);
	if (!g_file_query_exists (file, NULL)) {
		g_set_error (error, GTK_HOTKEY_REGISTRY_ERROR,
					 GTK_HOTKEY_REGISTRY_ERROR_UNKNOWN_APP,
					 "Application '%s' has not registered any hotkeys", app_id);
		g_object_unref (file);
		return NULL;
	}
	
	path = g_file_get_path (file);
	keyfile = g_key_file_new ();
	
	tmp_error = NULL;
	g_key_file_load_from_file (keyfile, path, 0, &tmp_error);
	if (tmp_error) {
		g_set_error (error, GTK_HOTKEY_REGISTRY_ERROR,
					 GTK_HOTKEY_REGISTRY_ERROR_IO,
					 "Failed to load keyfile '%s': %s", path, tmp_error->message);
		goto clean_up;
	}
	
	clean_up:
		g_free (path);
		g_object_unref (file);
		if (tmp_error) g_error_free (tmp_error);
	
	if (*error) {
		g_key_file_free (keyfile);
		return NULL;
	}
	
	return keyfile;
}

static GtkHotkeyInfo*
get_hotkey_info_from_key_file (GKeyFile	*keyfile,
							   const gchar *app_id,
							   const gchar *key_id,
							   GError **error)
{
	GtkHotkeyInfo   *info = NULL;
	gchar			*group, *description, *app_info_id, *signature;
	GAppInfo		*app_info = NULL;
	
	g_return_val_if_fail (keyfile != NULL, NULL);
	g_return_val_if_fail (error == NULL || *error == NULL, NULL);
	g_return_val_if_fail (app_id != NULL, NULL);
	g_return_val_if_fail (key_id != NULL, NULL);
	
	group = g_strconcat (HOTKEY_GROUP, key_id, NULL);
	description = g_key_file_get_string (keyfile, group, "Description", NULL);
	app_info_id = g_key_file_get_string (keyfile, group, "AppInfo", NULL);
	signature = g_key_file_get_string (keyfile, group, "Signature", NULL);
	
	if (!g_key_file_has_group (keyfile, group)) {
		g_set_error (error, GTK_HOTKEY_REGISTRY_ERROR,
					 GTK_HOTKEY_REGISTRY_ERROR_UNKNOWN_KEY,
					 "Keyfile has no group "HOTKEY_GROUP"%s", key_id);
		goto clean_up;
	}
	
	if (!signature) {
		g_set_error (error, GTK_HOTKEY_REGISTRY_ERROR,
					 GTK_HOTKEY_REGISTRY_ERROR_BAD_SIGNATURE,
					 "No 'Signature' defined for hotkey '%s' for application '%s'",
					 key_id, app_id);
		goto clean_up;
	}
	
	if (app_info_id) {
		app_info = G_APP_INFO(g_desktop_app_info_new (app_info_id));
		if (!G_IS_APP_INFO(app_info)) {
			g_set_error (error, GTK_HOTKEY_REGISTRY_ERROR,
						 GTK_HOTKEY_REGISTRY_ERROR_MISSING_APP,
						 "Keyfile refering to 'AppInfo = %s', but no application"
						 "by that id is registered on the system", app_info_id);
			goto clean_up;
		}	
	}
	
	info = gtk_hotkey_info_new (app_id, key_id, signature, app_info);
	if (description)
		gtk_hotkey_info_set_description (info, description);
	
	clean_up:
		g_free (group);
		if (signature) g_free (signature);
		if (description) g_free (description);
		if (app_info_id) g_free (app_info_id);
		if (app_info) g_object_unref (app_info);
			
	return info;
}

static GList*
get_all_hotkey_infos_from_key_file (GKeyFile	*keyfile,
									const gchar	*app_id)
{
	GList			*hotkeys;
	GtkHotkeyInfo	*hotkey;
	GStrv			groups;
	gsize			count;
	gchar			*group;
	GString			*key_id;
	GError			*error;
	
	g_return_val_if_fail (keyfile != NULL, NULL);
	g_return_val_if_fail (app_id != NULL, NULL);
	
	hotkeys = NULL;
	groups = g_key_file_get_groups (keyfile, &count);
	
	int i;
	for (i = 0; i < count; i++) {
		group = groups[i];
		key_id = g_string_new (group);
		
		/* Ignore non hotkey groups */
		if (!g_str_has_prefix (key_id->str, HOTKEY_GROUP)) {
			g_warning ("Hotkey file for %s contains non 'hotkey:' group '%s'",
					   app_id, group);
			g_string_free (key_id, TRUE);
			continue;
		}
		
		/* Strip 'hotkey:' prefix from key_id */
		g_string_erase (key_id, 0, 7);
		
		error = NULL;
		hotkey = get_hotkey_info_from_key_file (keyfile, app_id, key_id->str, &error);
		if (error) {
			g_warning ("Failed to read hotkey '%s' for application '%s': %s",
					   key_id->str, app_id, error->message);
			g_error_free (error);
			g_string_free (key_id, TRUE);
			continue;
		}
		
		hotkeys = g_list_prepend (hotkeys, hotkey);
			
		g_string_free (key_id, TRUE);
	}
	
	g_strfreev (groups);
	return hotkeys;
}

--- NEW FILE: gtk-hotkey-marshal.h ---

#if !defined (__GTK_HOTKEY_H__) && !defined (GTK_HOTKEY_COMPILATION)
#error "Only <gtkhotkey.h> can be included directly."
#endif

#ifndef __gtk_hotkey_marshal_MARSHAL_H__
#define __gtk_hotkey_marshal_MARSHAL_H__

#include	<glib-object.h>

G_BEGIN_DECLS

/* VOID:OBJECT,UINT (src/marshal.list:2) */
extern void gtk_hotkey_marshal_VOID__OBJECT_UINT (GClosure     *closure,
                                                  GValue       *return_value,
                                                  guint         n_param_values,
                                                  const GValue *param_values,
                                                  gpointer      invocation_hint,
                                                  gpointer      marshal_data);

G_END_DECLS

#endif /* __gtk_hotkey_marshal_MARSHAL_H__ */


--- NEW FILE: gtk-hotkey-listener.h ---
/*
 * This file is part of GtkHotkey.
 * Copyright Mikkel Kamstrup Erlandsen, March, 2008
 *
 *   GtkHotkey is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Lesser General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   GtkHotkey is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Lesser General Public License for more details.
 *
 *   You should have received a copy of the GNU Lesser General Public License
 *   along with GtkHotkey.  If not, see <http://www.gnu.org/licenses/>.
 */

#if !defined (__GTK_HOTKEY_H__) && !defined (GTK_HOTKEY_COMPILATION)
#error "Only <gtkhotkey.h> can be included directly."
#endif

#ifndef __GTK_HOTKEY_LISTENER_H__
#define __GTK_HOTKEY_LISTENER_H__

#include <glib.h>
#include <glib-object.h>
#include "gtk-hotkey-info.h"

G_BEGIN_DECLS


#define GTK_HOTKEY_TYPE_LISTENER (gtk_hotkey_listener_get_type ())
#define GTK_HOTKEY_LISTENER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_HOTKEY_TYPE_LISTENER, GtkHotkeyListener))
#define GTK_HOTKEY_LISTENER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_HOTKEY_TYPE_LISTENER, GtkHotkeyListenerClass))
#define GTK_HOTKEY_IS_LISTENER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_HOTKEY_TYPE_LISTENER))
#define GTK_HOTKEY_IS_LISTENER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_HOTKEY_TYPE_LISTENER))
#define GTK_HOTKEY_LISTENER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_HOTKEY_TYPE_LISTENER, GtkHotkeyListenerClass))

typedef struct _GtkHotkeyListener GtkHotkeyListener;
typedef struct _GtkHotkeyListenerClass GtkHotkeyListenerClass;
typedef struct _GtkHotkeyListenerPrivate GtkHotkeyListenerPrivate;

struct _GtkHotkeyListener {
	GObject						parent;
	GtkHotkeyListenerPrivate	*priv;
};

struct _GtkHotkeyListenerClass {
	GObjectClass	parent;
	gboolean		(*bind_hotkey)	  (GtkHotkeyListener	*self,
									   GtkHotkeyInfo		*hotkey,
									   GError				**error);
	gboolean		(*unbind_hotkey) (GtkHotkeyListener		*self,
									  GtkHotkeyInfo			*hotkey,
									  GError				**error);
};

GtkHotkeyListener*  gtk_hotkey_listener_get_default			(void);

void				gtk_hotkey_listener_activated			(GtkHotkeyListener	*self,
															 GtkHotkeyInfo		*hotkey,
															 guint				event_time);

gboolean			gtk_hotkey_listener_bind_hotkey		(GtkHotkeyListener  *self,
														 GtkHotkeyInfo		*hotkey,
														 GError				**error);

gboolean			gtk_hotkey_listener_unbind_hotkey   (GtkHotkeyListener  *self,
														 GtkHotkeyInfo		*hotkey,
														 GError				**error);

GType				gtk_hotkey_listener_get_type			(void);

G_END_DECLS

#endif /* __GTK_HOTKEY_LISTENER_H__ */

--- NEW FILE: gtk-hotkey-utils.h ---
/*
 * This file is part of GtkHotkey.
 * Copyright Mikkel Kamstrup Erlandsen, March, 2008
 *
 *   GtkHotkey is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Lesser General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   GtkHotkey is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Lesser General Public License for more details.
 *
 *   You should have received a copy of the GNU Lesser General Public License
 *   along with GtkHotkey.  If not, see <http://www.gnu.org/licenses/>.
 */

#if !defined (__GTK_HOTKEY_H__) && !defined (GTK_HOTKEY_COMPILATION)
#error "Only <gtkhotkey.h> can be included directly."
#endif

#ifndef __GTK_HOTKEY_UTILS_H__
#define __GTK_HOTKEY_UTILS_H__

#include <gio/gio.h>

G_BEGIN_DECLS

#define			gtk_hotkey_g_file_is_directory(file) (gtk_hotkey_g_file_get_type(file) == G_FILE_TYPE_DIRECTORY)
#define			gtk_hotkey_g_file_is_regular(file) (gtk_hotkey_g_file_get_type(file) == G_FILE_TYPE_REGULAR)

GFileType		gtk_hotkey_g_file_get_type			(GFile *file);

G_END_DECLS

#endif /* __GTK_HOTKEY_UTILS_H__ */

--- NEW FILE: gtk-hotkey-listener.c ---
/*
 * This file is part of GtkHotkey.
 * Copyright Mikkel Kamstrup Erlandsen, March, 2008
 *
 *   GtkHotkey is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Lesser General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   GtkHotkey is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Lesser General Public License for more details.
 *
 *   You should have received a copy of the GNU Lesser General Public License
 *   along with GtkHotkey.  If not, see <http://www.gnu.org/licenses/>.
 */
 
#include "gtk-hotkey-listener.h"
#include "gtk-hotkey-x11-listener.h"
#include "gtk-hotkey-marshal.h"

/* FIXME: The default listener is hardcoded to x11, should be compilation target dependent */

enum  {
	ACTIVATED,
	
	LAST_SIGNAL
};

enum  {
	GTK_HOTKEY_LISTENER_DUMMY_PROPERTY
};

guint						listener_signals[LAST_SIGNAL] = { 0 };

static  gpointer			gtk_hotkey_listener_parent_class = NULL;

static  GtkHotkeyListener	*default_listener = NULL;
static  GType				default_listener_type = G_TYPE_INVALID;

/**
 * SECTION:gtk-hotkey-listener
 * @short_description: Abstract base class providing platform independent hotkey listening capabilities
 * @see_also: #GtkHotkeyRegistry, #GtkHotkeyInfo
 *
 * #GtkHotkeyListener is an abstract base class for implementing platform
 * specific hotkey listeners - ie objects able to register when the user enters
 * a certain keyboard combination, regardless of which window has focus.
 *
 * Unless you have very special needs you should use the factory method
 * gtk_hotkey_listener_get_default() to get a reference to a #GtkHotkeyListener
 * matching your platform. Although most applications will not need.
 *
 * This class is part of the advanced API of GtkHotkey. Applications will not
 * normally use a #GtkHotkeyListener directly, since gtk_hotkey_info_bind()
 * will call into gtk_hotkey_listener_bind() on the default listener for you.
 **/											

/**
 * gtk_hotkey_listener_get_default
 * @returns: A new reference to the default hotkey listener for the platform
 *
 * Static factory method to get a reference to the default #GtkHotkeyListener
 * for the current platform.
 *
 * FIXME: Currently hardcoded to X11
 */
GtkHotkeyListener*
gtk_hotkey_listener_get_default ()
{
	/* FIXME: This method should be changedd to use the same approach as
	 * gtk_hotkey_registry_get_default() */
	
	if (default_listener) {
		g_return_val_if_fail (GTK_HOTKEY_IS_LISTENER(default_listener), NULL);
		return g_object_ref (default_listener);
	}
	gtk_hotkey_listener_get_type (); /* This call makes sure the default type ise set */
	g_debug ("Listener Type: %s", g_type_name (default_listener_type));
	
	default_listener = g_object_new (default_listener_type, NULL);
	g_return_val_if_fail (GTK_HOTKEY_IS_LISTENER(default_listener), NULL);
	
	return g_object_ref (default_listener);
}

/**
 * gtk_hotkey_listener_bind_hotkey
 * @self: The hotkey listener on which to bind a hotkey
 * @hotkey: The #GtkHotkeyInfo to bind. See #GtkHotkeyInfo:signature
 * @error: #GError in which to store errors, or %NULL to ignore
 * @returns: %TRUE if the binding succeeded, or %FALSE otherwise. In case of
 *           runtime errors @error will be set
 *
 * This method must be implemented by any child class of #GtkHotkeyListener.
 *
 * Start listening for keypresses matching the signature of @hotkey.
 * This method is notmally accessed indirectly by calling gtk_hotkey_info_bind().
 */
gboolean
gtk_hotkey_listener_bind_hotkey (GtkHotkeyListener  *self,
								 GtkHotkeyInfo		*hotkey,
								 GError				**error)
{
	g_return_val_if_fail (GTK_HOTKEY_IS_LISTENER(self), FALSE);
	
	return GTK_HOTKEY_LISTENER_GET_CLASS (self)->bind_hotkey (self, hotkey, error);
}

/**
 * gtk_hotkey_listener_unbind_hotkey
 * @self: The hotkey listener on which to bind a hotkey
 * @hotkey: The #GtkHotkeyInfo to bind. See #GtkHotkeyInfo:signature
 * @error: #GError in which to store errors, or %NULL to ignore
 * @returns: %TRUE if the binding has been removed, or %FALSE otherwise.
 *           In case of runtime errors @error will be set
 *
 * This method must be implemented by any child class of #GtkHotkeyListener.
 *
 * Stop listening for keypresses matching the signature of @hotkey.  This method
 * is notmally accessed indirectly by calling gtk_hotkey_info_unbind().
 */
gboolean
gtk_hotkey_listener_unbind_hotkey (GtkHotkeyListener	*self,
								   GtkHotkeyInfo		*hotkey,
								   GError				**error)
{
	g_return_val_if_fail (GTK_HOTKEY_IS_LISTENER(self), FALSE);
	
	return GTK_HOTKEY_LISTENER_GET_CLASS (self)->unbind_hotkey (self, hotkey, error);
}

/**
 * gtk_hotkey_listener_activated
 * @self: #GtkHotkeyListener to emit the #GtkHotkeyListener::activated signal
 * @hotkey: The #GtkHotkeyInfo the event happened for
 * @event_time: The system time the event happened on. This is useful for
 *              applications to pass through focus stealing prevention when
 *              mapping windows
 *
 * Emit the #GtkHotkeyInfo::activated signal on a hotkey listener.
 */
void
gtk_hotkey_listener_activated (GtkHotkeyListener	*self,
							   GtkHotkeyInfo		*hotkey,
							   guint				event_time)
{
	g_return_if_fail (GTK_HOTKEY_IS_LISTENER(self));
	g_return_if_fail (GTK_HOTKEY_IS_INFO(hotkey));
	
	g_signal_emit (self, listener_signals[ACTIVATED], 0, hotkey, event_time);
}

static void
gtk_hotkey_listener_class_init (GtkHotkeyListenerClass * klass)
{
	gtk_hotkey_listener_parent_class = g_type_class_peek_parent (klass);
	
	/**
	 * GtkHotkeyListener::activated
	 * @listener: The object that emitted the signal
	 * @hotkey: a #GtkHotkeyInfo for the hotkey that was activated
	 * @event_time: Time for event triggering the keypress. This is mainly
	 *              used to pass to window management functions to pass through
	 *              focus stealing prevention
	 *
	 * Emitted when a registered hotkey has been activated.
	 */
	listener_signals[ACTIVATED] = \
	g_signal_new ("activated",
				  GTK_HOTKEY_TYPE_LISTENER,
				  G_SIGNAL_RUN_LAST,
				  0, NULL, NULL,
				  gtk_hotkey_marshal_VOID__OBJECT_UINT,
				  G_TYPE_NONE, 2,
				  GTK_HOTKEY_TYPE_INFO,
				  G_TYPE_UINT);
}


static void
gtk_hotkey_listener_init (GtkHotkeyListener * self)
{
}


GType
gtk_hotkey_listener_get_type (void)
{
	static GType gtk_hotkey_listener_type_id = 0;
	
	if (G_UNLIKELY (gtk_hotkey_listener_type_id == 0)) {
		static const GTypeInfo g_define_type_info = {
			sizeof (GtkHotkeyListenerClass),
			(GBaseInitFunc) NULL,
			(GBaseFinalizeFunc) NULL,
			(GClassInitFunc) gtk_hotkey_listener_class_init,
			(GClassFinalizeFunc) NULL,
			NULL,
			sizeof (GtkHotkeyListener),
			0,
			(GInstanceInitFunc) gtk_hotkey_listener_init
		};
		
		gtk_hotkey_listener_type_id = g_type_register_static (G_TYPE_OBJECT,
															  "GtkHotkeyListener",
															  &g_define_type_info,
															  G_TYPE_FLAG_ABSTRACT);
		
		default_listener_type = gtk_hotkey_x11_listener_get_type ();
	}
	return gtk_hotkey_listener_type_id;
}





--- NEW FILE: gtk-hotkey-marshal.c ---

#include	<glib-object.h>


#ifdef G_ENABLE_DEBUG
#define g_marshal_value_peek_boolean(v)  g_value_get_boolean (v)
#define g_marshal_value_peek_char(v)     g_value_get_char (v)
#define g_marshal_value_peek_uchar(v)    g_value_get_uchar (v)
#define g_marshal_value_peek_int(v)      g_value_get_int (v)
#define g_marshal_value_peek_uint(v)     g_value_get_uint (v)
#define g_marshal_value_peek_long(v)     g_value_get_long (v)
#define g_marshal_value_peek_ulong(v)    g_value_get_ulong (v)
#define g_marshal_value_peek_int64(v)    g_value_get_int64 (v)
#define g_marshal_value_peek_uint64(v)   g_value_get_uint64 (v)
#define g_marshal_value_peek_enum(v)     g_value_get_enum (v)
#define g_marshal_value_peek_flags(v)    g_value_get_flags (v)
#define g_marshal_value_peek_float(v)    g_value_get_float (v)
#define g_marshal_value_peek_double(v)   g_value_get_double (v)
#define g_marshal_value_peek_string(v)   (char*) g_value_get_string (v)
#define g_marshal_value_peek_param(v)    g_value_get_param (v)
#define g_marshal_value_peek_boxed(v)    g_value_get_boxed (v)
#define g_marshal_value_peek_pointer(v)  g_value_get_pointer (v)
#define g_marshal_value_peek_object(v)   g_value_get_object (v)
#else /* !G_ENABLE_DEBUG */
/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API.
 *          Do not access GValues directly in your code. Instead, use the
 *          g_value_get_*() functions
 */
#define g_marshal_value_peek_boolean(v)  (v)->data[0].v_int
#define g_marshal_value_peek_char(v)     (v)->data[0].v_int
#define g_marshal_value_peek_uchar(v)    (v)->data[0].v_uint
#define g_marshal_value_peek_int(v)      (v)->data[0].v_int
#define g_marshal_value_peek_uint(v)     (v)->data[0].v_uint
#define g_marshal_value_peek_long(v)     (v)->data[0].v_long
#define g_marshal_value_peek_ulong(v)    (v)->data[0].v_ulong
#define g_marshal_value_peek_int64(v)    (v)->data[0].v_int64
#define g_marshal_value_peek_uint64(v)   (v)->data[0].v_uint64
#define g_marshal_value_peek_enum(v)     (v)->data[0].v_long
#define g_marshal_value_peek_flags(v)    (v)->data[0].v_ulong
#define g_marshal_value_peek_float(v)    (v)->data[0].v_float
#define g_marshal_value_peek_double(v)   (v)->data[0].v_double
#define g_marshal_value_peek_string(v)   (v)->data[0].v_pointer
#define g_marshal_value_peek_param(v)    (v)->data[0].v_pointer
#define g_marshal_value_peek_boxed(v)    (v)->data[0].v_pointer
#define g_marshal_value_peek_pointer(v)  (v)->data[0].v_pointer
#define g_marshal_value_peek_object(v)   (v)->data[0].v_pointer
#endif /* !G_ENABLE_DEBUG */


/* VOID:OBJECT,UINT (src/marshal.list:2) */
void
gtk_hotkey_marshal_VOID__OBJECT_UINT (GClosure     *closure,
                                      GValue       *return_value G_GNUC_UNUSED,
                                      guint         n_param_values,
                                      const GValue *param_values,
                                      gpointer      invocation_hint G_GNUC_UNUSED,
                                      gpointer      marshal_data)
{
  typedef void (*GMarshalFunc_VOID__OBJECT_UINT) (gpointer     data1,
                                                  gpointer     arg_1,
                                                  guint        arg_2,
                                                  gpointer     data2);
  register GMarshalFunc_VOID__OBJECT_UINT callback;
  register GCClosure *cc = (GCClosure*) closure;
  register gpointer data1, data2;

  g_return_if_fail (n_param_values == 3);

  if (G_CCLOSURE_SWAP_DATA (closure))
    {
      data1 = closure->data;
      data2 = g_value_peek_pointer (param_values + 0);
    }
  else
    {
      data1 = g_value_peek_pointer (param_values + 0);
      data2 = closure->data;
    }
  callback = (GMarshalFunc_VOID__OBJECT_UINT) (marshal_data ? marshal_data : cc->callback);

  callback (data1,
            g_marshal_value_peek_object (param_values + 1),
            g_marshal_value_peek_uint (param_values + 2),
            data2);
}


--- NEW FILE: gtk-hotkey-x11-listener.h ---
/*
 * This file is part of GtkHotkey.
 * Copyright Mikkel Kamstrup Erlandsen, March, 2008
 *
 *   GtkHotkey is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Lesser General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   GtkHotkey is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Lesser General Public License for more details.
 *
 *   You should have received a copy of the GNU Lesser General Public License
 *   along with GtkHotkey.  If not, see <http://www.gnu.org/licenses/>.
 */

#if !defined (__GTK_HOTKEY_H__) && !defined (GTK_HOTKEY_COMPILATION)
#error "Only <gtkhotkey.h> can be included directly."
#endif

#ifndef __GTK_HOTKEY_X11_LISTENER_H__
#define __GTK_HOTKEY_X11_LISTENER_H__

#include <glib.h>
#include <glib-object.h>
#include "gtk-hotkey-listener.h"
#include "gtk-hotkey-info.h"

G_BEGIN_DECLS


#define GTK_HOTKEY_TYPE_X11_LISTENER (gtk_hotkey_x11_listener_get_type ())
#define GTK_HOTKEY_X11_LISTENER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_HOTKEY_TYPE_X11_LISTENER, GtkHotkeyX11Listener))
#define GTK_HOTKEY_X11_LISTENER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_HOTKEY_TYPE_X11_LISTENER, GtkHotkeyX11ListenerClass))
#define GTK_HOTKEY_IS_X11_LISTENER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_HOTKEY_TYPE_X11_LISTENER))
#define GTK_HOTKEY_IS_X11_LISTENER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_HOTKEY_TYPE_X11_LISTENER))
#define GTK_HOTKEY_X11_LISTENER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_HOTKEY_TYPE_X11_LISTENER, GtkHotkeyX11ListenerClass))

typedef struct _GtkHotkeyX11Listener GtkHotkeyX11Listener;
typedef struct _GtkHotkeyX11ListenerClass GtkHotkeyX11ListenerClass;
typedef struct _GtkHotkeyX11ListenerPrivate GtkHotkeyX11ListenerPrivate;

struct _GtkHotkeyX11Listener {
	GtkHotkeyListener parent;
	GtkHotkeyX11ListenerPrivate * priv;
};
struct _GtkHotkeyX11ListenerClass {
	GtkHotkeyListenerClass parent;
};

GType gtk_hotkey_x11_listener_get_type (void);

G_END_DECLS

#endif

--- NEW FILE: gtk-hotkey-error.c ---
/*
 * This file is part of GtkHotkey.
 * Copyright Mikkel Kamstrup Erlandsen, March, 2008
 *
 *   GtkHotkey is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Lesser General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   GtkHotkey is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Lesser General Public License for more details.
 *
 *   You should have received a copy of the GNU Lesser General Public License
 *   along with GtkHotkey.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "gtk-hotkey-error.h"

GQuark
gtk_hotkey_listener_error_quark (void)
{
  return g_quark_from_static_string ("gtk-hotkey-listener-error-quark");
}

GQuark
gtk_hotkey_registry_error_quark (void)
{
  return g_quark_from_static_string ("gtk-hotkey-storage-error-quark");
}

--- NEW FILE: gtk-hotkey-info.h ---
/*
 * This file is part of GtkHotkey.
 * Copyright Mikkel Kamstrup Erlandsen, March, 2008
 *
 *   GtkHotkey is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Lesser General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   GtkHotkey is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Lesser General Public License for more details.
 *
 *   You should have received a copy of the GNU Lesser General Public License
 *   along with GtkHotkey.  If not, see <http://www.gnu.org/licenses/>.
 */

#if !defined (__GTK_HOTKEY_H__) && !defined (GTK_HOTKEY_COMPILATION)
#error "Only <gtkhotkey.h> can be included directly."
#endif

#ifndef __GTK_HOTKEY_INFO_H__
#define __GTK_HOTKEY_INFO_H__

#include <glib.h>
#include <glib-object.h>
#include <gio/gio.h>
#include <stdlib.h>
#include <string.h>

G_BEGIN_DECLS


#define GTK_HOTKEY_TYPE_INFO (gtk_hotkey_info_get_type ())
#define GTK_HOTKEY_INFO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_HOTKEY_TYPE_INFO, GtkHotkeyInfo))
#define GTK_HOTKEY_INFO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_HOTKEY_TYPE_INFO, GtkHotkeyInfoClass))
#define GTK_HOTKEY_IS_INFO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_HOTKEY_TYPE_INFO))
#define GTK_HOTKEY_IS_INFO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_HOTKEY_TYPE_INFO))
#define GTK_HOTKEY_INFO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_HOTKEY_TYPE_INFO, GtkHotkeyInfoClass))

typedef struct _GtkHotkeyInfo GtkHotkeyInfo;
typedef struct _GtkHotkeyInfoClass GtkHotkeyInfoClass;
typedef struct _GtkHotkeyInfoPrivate GtkHotkeyInfoPrivate;

struct _GtkHotkeyInfo {
	GObject parent;
	GtkHotkeyInfoPrivate * priv;
};
struct _GtkHotkeyInfoClass {
	GObjectClass parent;
};

gboolean		gtk_hotkey_info_bind (GtkHotkeyInfo* self, GError **error);

gboolean		gtk_hotkey_info_unbind (GtkHotkeyInfo* self, GError **error);

gboolean		gtk_hotkey_info_is_bound (GtkHotkeyInfo* self);

const gchar*	gtk_hotkey_info_get_application_id (GtkHotkeyInfo* self);

const gchar*	gtk_hotkey_info_get_key_id (GtkHotkeyInfo* self);

GAppInfo*		gtk_hotkey_info_get_app_info (GtkHotkeyInfo* self);

const gchar*	gtk_hotkey_info_get_application_id (GtkHotkeyInfo* self);

const gchar*	gtk_hotkey_info_get_signature (GtkHotkeyInfo* self);

const gchar*	gtk_hotkey_info_get_key_id (GtkHotkeyInfo* self);

const gchar*	gtk_hotkey_info_get_description (GtkHotkeyInfo* self);

void			gtk_hotkey_info_set_description (GtkHotkeyInfo* self, const gchar *description);

gboolean		gtk_hotkey_info_equals (GtkHotkeyInfo *hotkey1, GtkHotkeyInfo *hotkey2, gboolean sloppy_equals);

void			gtk_hotkey_info_activated (GtkHotkeyInfo* self, guint event_time);

GtkHotkeyInfo*  gtk_hotkey_info_new						(const gchar	*app_id,
														 const gchar	*key_id,
														 const gchar	*signature,
														 GAppInfo		*app_info);

GType			gtk_hotkey_info_get_type (void);

G_END_DECLS

#endif

--- NEW FILE: Makefile.am ---
noinst_LTLIBRARIES = libcmnpgtkhotkey.la

libcmnpgtkhotkey_la_SOURCES = \
	gtk-hotkey-info.c \
	gtk-hotkey-error.c \
	gtk-hotkey-listener.c \
	gtk-hotkey-marshal.c \
	gtk-hotkey-x11-listener.c \
	x11/tomboykeybinder.c \
	x11/eggaccelerators.c \
	gtk-hotkey-registry.c \
	gtk-hotkey-key-file-registry.c \
	gtk-hotkey-utils.c
	
cmnpgtkhotkeyincludedir = $(includedir)/claws-mail/plugins/@PACKAGE@/gtkhotkey
cmnpgtkhotkeyinclude_HEADERS = \
	gtk-hotkey-error.h \
	gtkhotkey.h \
	gtk-hotkey-info.h \
	gtk-hotkey-key-file-registry.h \
	gtk-hotkey-listener.h \
	gtk-hotkey-registry.h \
	gtk-hotkey-x11-listener.h \
	gtk-hotkey-marshal.h \
	x11/tomboykeybinder.h \
	x11/eggaccelerators.h \
	gtk-hotkey-utils.h

libcmnpgtkhotkey_la_LIBADD = \
	$(GTK_LIBS) \
	$(CM_NP_HOTKEY_LIBS)

AM_CPPFLAGS = \
	-DGTK_HOTKEY_COMPILATION \
	$(GLIB_CFLAGS) \
	$(GTK_CFLAGS) \
	$(CM_NP_HOTKEY_CFLAGS)

--- NEW FILE: gtk-hotkey-registry.c ---
/*
 * This file is part of GtkHotkey.
 * Copyright Mikkel Kamstrup Erlandsen, March, 2008
 *
 *   GtkHotkey is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Lesser General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   GtkHotkey is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Lesser General Public License for more details.
 *
 *   You should have received a copy of the GNU Lesser General Public License
 *   along with GtkHotkey.  If not, see <http://www.gnu.org/licenses/>.
 */
 
#include "gtk-hotkey-registry.h"
#include "gtk-hotkey-key-file-registry.h"

enum  {
	GTK_HOTKEY_REGISTRY_DUMMY_PROPERTY
};

enum {
	HOTKEY_STORED,
	HOTKEY_DELETED,
	
	LAST_SIGNAL
};

static gpointer		gtk_hotkey_registry_parent_class = NULL;

static GType		default_registry_type = G_TYPE_INVALID;

static GtkHotkeyRegistry
					*default_registry = NULL;

#define DEFAULT_REGISTRY_TYPE GTK_HOTKEY_TYPE_KEY_FILE_REGISTRY

guint				storage_signals[LAST_SIGNAL] = { 0 };

/**
 * SECTION:gtk-hotkey-registry
 * @short_description: Abstract base class for services storing and loading hotkeys
 * @see_also: #GtkHotkeyKeyFileRegistry
 *
 * #GtkHotkeyRegistry is an abstract base class for implementing a platform
 * specific service for storing and loading hotkey configurations.
 *
 * The actual binding of the hotkey into the environment is done by a
 * #GtkHotkeyListener. This class is only meant to do the management part of the
 * hotkey handling.
 *
 * The reasong why applications should use a #GtkHotkeyRegistry and not just a
 * flat text file with the hotkey signature is to make sure we don't overwrite
 * or interfere with the hotkeys of other applications. And possibly providing
 * a unified user interface for managing the hotkeys of all applications.
 *
 * To obtain a #GtkHotkeyRegistry matching your desktop environment use
 * the factory method gtk_hotkey_registry_get_default().
 *
 **/	

/**
 * gtk_hotkey_registry_get_default
 * @returns: A reference to a #GtkHotkeyRegistry matching your platform
 *
 * Currently the only implementation of this class is #GtkHotkeyKeyFileRegistry.
 */
GtkHotkeyRegistry*
gtk_hotkey_registry_get_default (void)
{	
	if (G_UNLIKELY(default_registry == NULL)) {
		
		/* Set the default type of registry to create */
		if (default_registry_type == G_TYPE_INVALID)
			default_registry_type = DEFAULT_REGISTRY_TYPE;
		
		default_registry = GTK_HOTKEY_REGISTRY (g_object_new (GTK_HOTKEY_TYPE_KEY_FILE_REGISTRY,
															NULL));
		g_return_val_if_fail (GTK_HOTKEY_IS_REGISTRY(default_registry), NULL);
		/* We always keep a ref to the registry here */
	}
	return g_object_ref(default_registry);
}

/**
 * gtk_hotkey_registry_get_hotkey
 * @self: The registry to search in
 * @app_id: The name under which the application has registered it self
 *          when it created the #GtkHotkeyInfo in the first place.
 * @key_id: The id assignedd to the actual hotkey on the moment of its creation
 * @error: Place to store a #GError in case of errors, or %NULL to ignore
 * @returns: The #GtkHotkeyInfo for the requested parameters or %NULL is none
 *           where found. In the case %NULL is returned @error will be set
 *           accordingly. Free the hotkey with g_object_unref() when you are done
 *           using it.
 *
 * Look up a hotkey given its id and application id.
 */
GtkHotkeyInfo*
gtk_hotkey_registry_get_hotkey (GtkHotkeyRegistry	 *self,
							   const char		*app_id,
							   const char		*key_id,
							   GError			**error)
{
	g_return_val_if_fail (GTK_HOTKEY_IS_REGISTRY(self), NULL);
	return GTK_HOTKEY_REGISTRY_GET_CLASS (self)->get_hotkey (self, app_id, key_id,
															error);
}

/**
 * gtk_hotkey_registry_get_application_hotkeys
 * @self: The #GtkHotkeyRegistry to look hotkeys up in
 * @app_id: Unique application id
 * @error: Place to return a #GError or %NULL
 * @returns: A list of #GtkHotkeyInfo objects. The list should be with freed with
 *           g_list_free() and the hotkey objects should be freed with
 *           g_object_unref().
 * 
 * Look up all hotkeys registered by a given application.
 */
GList*
gtk_hotkey_registry_get_application_hotkeys (GtkHotkeyRegistry	*self,
											const char			*app_id,
											GError				**error)
{
	g_return_val_if_fail (GTK_HOTKEY_IS_REGISTRY(self), NULL);
	return GTK_HOTKEY_REGISTRY_GET_CLASS (self)->get_application_hotkeys (self, app_id, error);
}

/**
 * gtk_hotkey_registry_get_all_hotkeys
 * @self: The #GtkHotkeyRegistry to look hotkeys up in
 * @returns: A list of all valid #GtkHotkeyInfo<!-- -->s stored in the registry. 
 *           The list should be with freed with g_list_free() and the hotkey 
 *           objects should be freed with g_object_unref().
 * 
 * Look up all hotkeys registered by a given application.
 */
GList*
gtk_hotkey_registry_get_all_hotkeys (GtkHotkeyRegistry	*self)
{
	g_return_val_if_fail (GTK_HOTKEY_IS_REGISTRY(self), NULL);
	return GTK_HOTKEY_REGISTRY_GET_CLASS (self)->get_all_hotkeys (self);
}

/**
 * gtk_hotkey_registry_store_hotkey
 * @self: The #GtkHotkeyRegistry in which to store the hotkey
 * @info: The #GtkHotkeyInfo to store
 * @error: Place to return a #GError or %NULL to ignore
 * @returns: %TRUE on success and %FALS otherwise. In case of errors @error
 *           will be set accordingly.
 *           
 * Store a hotkey in the registry for later usage. In case of success the
 * #GtkHotkeyRegistry::hotkey-stored signal will be emitted.
 */
gboolean
gtk_hotkey_registry_store_hotkey (GtkHotkeyRegistry   *self,
								 GtkHotkeyInfo		*info,
								 GError				**error)
{
	g_return_val_if_fail (GTK_HOTKEY_IS_REGISTRY(self), FALSE);
	return GTK_HOTKEY_REGISTRY_GET_CLASS (self)->store_hotkey (self, info, error);
}

/**
 * gtk_hotkey_registry_delete_hotkey
 * @self: The #GtkHotkeyRegistry from which to delete the hotkey
 * @app_id: The value of the #GtkHotkeyInfo:application-id property of the stored
 *          hotkey
 * @key_id: The value of the #GtkHotkeyInfo:key-id property of the stored hotkey
 * @error: Place to return a #GError or %NULL to ignore
 * @returns: %TRUE on success and %FALS otherwise. In case of errors @error
 *           will be set accordingly.
 *           
 * Delete a hotkey from the registry. In case of success the
 * #GtkHotkeyRegistry::hotkey-deleted signal will be emitted.
 */
gboolean
gtk_hotkey_registry_delete_hotkey (GtkHotkeyRegistry	*self,
								  const gchar		*app_id,
								  const gchar		*key_id,
								  GError			**error)
{
	g_return_val_if_fail (GTK_HOTKEY_IS_REGISTRY(self), FALSE);
	return GTK_HOTKEY_REGISTRY_GET_CLASS (self)->delete_hotkey (self, app_id,
															   key_id, error);
}

/**
 * gtk_hotkey_registry_has_hotkey
 * @self: The #GtkHotkeyRegistry to look hotkeys up in
 * @app_id: The value of the #GtkHotkeyInfo:application-id property of the stored
 *          hotkey
 * @key_id: The value of the #GtkHotkeyInfo:key-id property of the stored hotkey
 * @returns: %TRUE if the registry has stored a hotkey with with application id
 *           @app_id and hotkey id @key_id.
 * 
 * Look up all hotkeys registered by a given application.
 */
gboolean
gtk_hotkey_registry_has_hotkey (GtkHotkeyRegistry		*self,
							   const gchar			*app_id,
							   const gchar			*key_id)
{
	g_return_val_if_fail (GTK_HOTKEY_IS_REGISTRY(self), FALSE);
	return GTK_HOTKEY_REGISTRY_GET_CLASS (self)->has_hotkey (self, app_id, key_id);
}

/**
 * gtk_hotkey_registry_hotkey_stored
 * @self: The #GtkHotkeyRegistry to emit the signal on
 * @info: The #GtkHotkeyInfo that was stored
 * 
 * Emit the #GtkHotkeyRegistry::hotkey-stored signal on @self. This method should
 * only be used by child classes of #GtkHotkeyRegistry.
 */
void
gtk_hotkey_registry_hotkey_stored (GtkHotkeyRegistry	*self,
								 GtkHotkeyInfo		*info)
{
	g_return_if_fail (GTK_HOTKEY_IS_REGISTRY(self));
	g_return_if_fail (GTK_HOTKEY_IS_INFO(info));
	
	GTK_HOTKEY_REGISTRY_GET_CLASS (self)->hotkey_stored (self, info);
}

/**
 * gtk_hotkey_registry_hotkey_deleted
 * @self: The #GtkHotkeyRegistry to emit the signal on
 * @info: The #GtkHotkeyInfo that was deleted
 * 
 * Emit the #GtkHotkeyRegistry::hotkey-deleted signal on @self. This method should
 * only be used by child classes of #GtkHotkeyRegistry.
 */
void
gtk_hotkey_registry_hotkey_deleted (GtkHotkeyRegistry		*self,
								   GtkHotkeyInfo		*info)
{
	g_return_if_fail (GTK_HOTKEY_IS_REGISTRY(self));
	GTK_HOTKEY_REGISTRY_GET_CLASS (self)->hotkey_deleted (self, info);
}

static void
gtk_hotkey_registry_hotkey_stored_real (GtkHotkeyRegistry	*self,
									   GtkHotkeyInfo	*info)
{
	g_return_if_fail (GTK_HOTKEY_IS_INFO(info));
	g_return_if_fail (GTK_HOTKEY_IS_REGISTRY(self));
	
	g_signal_emit (self, storage_signals[HOTKEY_STORED], 0, info);
}

static void
gtk_hotkey_registry_hotkey_deleted_real (GtkHotkeyRegistry	*self,
										GtkHotkeyInfo		*info)
{
	g_return_if_fail (GTK_HOTKEY_IS_INFO(info));
	g_return_if_fail (GTK_HOTKEY_IS_REGISTRY(self));
	
	g_signal_emit (self, storage_signals[HOTKEY_DELETED], 0, info);
}

static void
gtk_hotkey_registry_class_init (GtkHotkeyRegistryClass *klass)
{
	gtk_hotkey_registry_parent_class = g_type_class_peek_parent (klass);
	
	klass->hotkey_stored = gtk_hotkey_registry_hotkey_stored_real;
	klass->hotkey_deleted = gtk_hotkey_registry_hotkey_deleted_real;
	
	/**
	 * GtkHotkeyRegistry::hotkey-stored
	 * @hotkey:The hotkey that was stored
	 *
	 * Emitted when a hotkey has been stored in the registry
	 */
	storage_signals[HOTKEY_STORED] = \
	g_signal_new ("hotkey_stored",
				  GTK_HOTKEY_TYPE_STORAGE,
				  G_SIGNAL_RUN_LAST,
				  0, NULL, NULL,
				  g_cclosure_marshal_VOID__OBJECT,
				  G_TYPE_NONE, 1,
				  G_TYPE_OBJECT);
	
	/**
	 * GtkHotkeyRegistry::hotkey-deleted
	 * @hotkey:The hotkey that was deleted
	 *
	 * Emitted when a hotkey has been deleted from the registry
	 */
	storage_signals[HOTKEY_DELETED] = \
	g_signal_new ("hotkey_deleted",
				  GTK_HOTKEY_TYPE_STORAGE,
				  G_SIGNAL_RUN_LAST,
				  0, NULL, NULL,
				  g_cclosure_marshal_VOID__OBJECT,
				  G_TYPE_NONE, 1,
				  G_TYPE_OBJECT);
}


static void
gtk_hotkey_registry_init (GtkHotkeyRegistry * self)
{
	
}

static void
gtk_hotkey_registry_finalize (GtkHotkeyRegistry * self)
{
	
}

GType
gtk_hotkey_registry_get_type (void)
{
	static GType gtk_hotkey_registry_type_id = 0;
	
	if (G_UNLIKELY (gtk_hotkey_registry_type_id == 0)) {
		static const GTypeInfo g_define_type_info = {
			sizeof (GtkHotkeyRegistryClass),
			(GBaseInitFunc) gtk_hotkey_registry_init,
			(GBaseFinalizeFunc) gtk_hotkey_registry_finalize,
			(GClassInitFunc) gtk_hotkey_registry_class_init,
			(GClassFinalizeFunc) NULL,
			NULL,
			sizeof (GtkHotkeyRegistry),
			0,
			(GInstanceInitFunc) gtk_hotkey_registry_init 
		};
		
		gtk_hotkey_registry_type_id = g_type_register_static (G_TYPE_OBJECT, "GtkHotkeyRegistry", &g_define_type_info, G_TYPE_FLAG_ABSTRACT);
	}
	return gtk_hotkey_registry_type_id;
}

--- NEW FILE: gtkhotkey.h ---

#ifndef __GTK_HOTKEY_H__
#define __GTK_HOTKEY_H__

#include <gtk-hotkey-info.h>
#include <gtk-hotkey-registry.h>
#include <gtk-hotkey-key-file-registry.h>
#include <gtk-hotkey-listener.h>
#include <gtk-hotkey-x11-listener.h>
#include <gtk-hotkey-error.h>

#endif /* __GTK_HOTKEY_H__ */

--- NEW FILE: gtk-hotkey-key-file-registry.h ---
/*
 * This file is part of GtkHotkey.
 * Copyright Mikkel Kamstrup Erlandsen, March, 2008
 *
 *   GtkHotkey is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Lesser General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   GtkHotkey is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Lesser General Public License for more details.
 *
 *   You should have received a copy of the GNU Lesser General Public License
 *   along with GtkHotkey.  If not, see <http://www.gnu.org/licenses/>.
 */

#if !defined (__GTK_HOTKEY_H__) && !defined (GTK_HOTKEY_COMPILATION)
#error "Only <gtkhotkey.h> can be included directly."
#endif

#ifndef __GTK_HOTKEY_KEY_FILE_REGISTRY_H__
#define __GTK_HOTKEY_KEY_FILE_REGISTRY_H__

#include <glib.h>
#include <glib-object.h>
#include <stdlib.h>
#include <string.h>
#include "gtk-hotkey-registry.h"
#include "gtk-hotkey-info.h"

G_BEGIN_DECLS


#define GTK_HOTKEY_TYPE_KEY_FILE_REGISTRY (gtk_hotkey_key_file_registry_get_type ())
#define GTK_HOTKEY_KEY_FILE_REGISTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_HOTKEY_TYPE_KEY_FILE_REGISTRY, GtkHotkeyKeyFileRegistry))
#define GTK_HOTKEY_KEY_FILE_REGISTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_HOTKEY_TYPE_KEY_FILE_REGISTRY, GtkHotkeyKeyFileRegistryClass))
#define GTK_HOTKEY_IS_KEY_FILE_REGISTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_HOTKEY_TYPE_KEY_FILE_REGISTRY))
#define GTK_HOTKEY_IS_KEY_FILE_REGISTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_HOTKEY_TYPE_KEY_FILE_REGISTRY))
#define GTK_HOTKEY_KEY_FILE_REGISTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_HOTKEY_TYPE_KEY_FILE_REGISTRY, GtkHotkeyKeyFileRegistryClass))

typedef struct _GtkHotkeyKeyFileRegistry GtkHotkeyKeyFileRegistry;
typedef struct _GtkHotkeyKeyFileRegistryClass GtkHotkeyKeyFileRegistryClass;
typedef struct _GtkHotkeyKeyFileRegistryPrivate GtkHotkeyKeyFileRegistryPrivate;

struct _GtkHotkeyKeyFileRegistry {
	GtkHotkeyRegistry parent;
	GtkHotkeyKeyFileRegistryPrivate * priv;
};
struct _GtkHotkeyKeyFileRegistryClass {
	GtkHotkeyRegistryClass parent;
};

GType gtk_hotkey_key_file_registry_get_type (void);

G_END_DECLS

#endif

--- NEW FILE: gtk-hotkey-error.h ---
/*
 * This file is part of GtkHotkey.
 * Copyright Mikkel Kamstrup Erlandsen, March, 2008
 *
 *   GtkHotkey is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Lesser General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   GtkHotkey is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Lesser General Public License for more details.
 *
 *   You should have received a copy of the GNU Lesser General Public License
 *   along with GtkHotkey.  If not, see <http://www.gnu.org/licenses/>.
 */

#if !defined (__GTK_HOTKEY_H__) && !defined (GTK_HOTKEY_COMPILATION)
#error "Only <gtkhotkey.h> can be included directly."
#endif

#ifndef __GTK_HOTKEY_ERROR_H__
#define __GTK_HOTKEY_ERROR_H__

#include <glib.h>

G_BEGIN_DECLS

/**
 * GTK_HOTKEY_LISTENER_ERROR:
 *  
 * Error domain for #GtkHotkeyListener.
 */
#define GTK_HOTKEY_LISTENER_ERROR gtk_hotkey_listener_error_quark()
GQuark gtk_hotkey_listener_error_quark (void);

/**
 * GTK_HOTKEY_REGISTRY_ERROR:
 *  
 * Error domain for #GtkHotkeyRegistry.
 */
#define GTK_HOTKEY_REGISTRY_ERROR gtk_hotkey_registry_error_quark()
GQuark gtk_hotkey_registry_error_quark (void);

/**
 * GtkHotkeyListenerError:
 * @GTK_HOTKEY_LISTENER_ERROR_BIND: An error occured when binding a hotkey with
 *                                  the listener
 * @GTK_HOTKEY_LISTENER_ERROR_UNBIND: An error occured when unbinding a hotkey 
 *                                    with the listener
 * 
 * Error codes for #GError<!-- -->s related to #GtkHotkeyListener<!-- -->s
 */
typedef enum
{
	GTK_HOTKEY_LISTENER_ERROR_BIND,
	GTK_HOTKEY_LISTENER_ERROR_UNBIND,
} GtkHotkeyListenerError;

/**
 * GtkHotkeyRegistryError:
 * @GTK_HOTKEY_REGISTRY_ERROR_UNKNOWN_APP: The application which is the involved
 *                                         in the transaction has not registered
 *                                         any hotkeys
 * @GTK_HOTKEY_REGISTRY_ERROR_UNKNOWN_KEY: The hotkey key-id (the identifier that
 *                                         identifies the hotkey among those 
 *                                         belonging to an application) is not
 *                                         known.
 * @GTK_HOTKEY_REGISTRY_ERROR_MALFORMED_MEDIUM: The medium from which to read
 *                                              or write is in an unrecoverable
 *                                              state. For example a file
 *                                              containing syntax errors
 * @GTK_HOTKEY_REGISTRY_ERROR_IO: There was some problem with the actual io
 *                                operation. Missing file permissions, disk full,
 *                                etc.
 * @GTK_HOTKEY_REGISTRY_ERROR_UNKNOWN: Unexpected and uncharacterized error
 * @GTK_HOTKEY_REGISTRY_ERROR_BAD_SIGNATURE: The hotkey signature is not valid.
 *                                           See #GtkHotkeyInfo:signature.
 * @GTK_HOTKEY_REGISTRY_ERROR_MISSING_APP: A #GtkHotkeyInfo is referring an
 *                                         application via its #GtkHotkeyInfo:app-info
 *                                         property, but the application can not
 *                                         be found.
 * 
 * Error codes for #GError<!-- -->s related to #GtkHotkeyRegistry<!-- -->s
 */
typedef enum
{
	GTK_HOTKEY_REGISTRY_ERROR_UNKNOWN_APP,
	GTK_HOTKEY_REGISTRY_ERROR_UNKNOWN_KEY,
	GTK_HOTKEY_REGISTRY_ERROR_MALFORMED_MEDIUM,
	GTK_HOTKEY_REGISTRY_ERROR_IO,
	GTK_HOTKEY_REGISTRY_ERROR_UNKNOWN,
	GTK_HOTKEY_REGISTRY_ERROR_BAD_SIGNATURE,
	GTK_HOTKEY_REGISTRY_ERROR_MISSING_APP,
} GtkHotkeyRegistryError;

G_END_DECLS

#endif /* __GTK_HOTKEY_ERROR_H__ */

--- NEW FILE: gtk-hotkey-x11-listener.c ---
/*
 * This file is part of GtkHotkey.
 * Copyright Mikkel Kamstrup Erlandsen, March, 2008
 *
 *   GtkHotkey is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Lesser General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   GtkHotkey is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Lesser General Public License for more details.
 *
 *   You should have received a copy of the GNU Lesser General Public License
 *   along with GtkHotkey.  If not, see <http://www.gnu.org/licenses/>.
 */
 
#include "gtk-hotkey-error.h"
#include "gtk-hotkey-x11-listener.h"
#include "gtk-hotkey-listener.h"
#include "gtk-hotkey-info.h"
#include "x11/tomboykeybinder.h"

struct _GtkHotkeyX11ListenerPrivate {
	GList   *hotkeys;
};

enum  {
	GTK_HOTKEY_X11_LISTENER_DUMMY_PROPERTY
};
static gboolean	gtk_hotkey_x11_listener_real_bind_hotkey	(GtkHotkeyListener  *base,
															 GtkHotkeyInfo		*hotkey,
															 GError				**error);

static gboolean	gtk_hotkey_x11_listener_real_unbind_hotkey  (GtkHotkeyListener  *base,
															 GtkHotkeyInfo		*hotkey,
															 GError				**error);

static void		hotkey_activated_cb								(char				*signature,
																 gpointer			user_data);

static GtkHotkeyInfo*
				find_hotkey_from_key_id							(GtkHotkeyX11Listener	*self,
																 const gchar			*key_id);

static gpointer gtk_hotkey_x11_listener_parent_class = NULL;


/**
 * SECTION:gtk-hotkey-x11-listener
 * @short_description: Implementation of #GtkHotkeyListener for a standard X11
 *                     environment
 * @see_also: #GtkHotkeyRegistry, #GtkHotkeyInfo
 *
 * This implementation of a #GtkHotkeyListener should work in any X11
 * environment.
 **/	

static gboolean
gtk_hotkey_x11_listener_real_bind_hotkey (GtkHotkeyListener *base,
											  GtkHotkeyInfo		*hotkey,
											  GError			**error)
{
	GtkHotkeyX11Listener	*self;
	
	g_return_val_if_fail (GTK_HOTKEY_IS_X11_LISTENER(base), FALSE);
	g_return_val_if_fail (GTK_HOTKEY_IS_INFO (hotkey), FALSE);
	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
	
	self = GTK_HOTKEY_X11_LISTENER (base);
	
	if (find_hotkey_from_key_id(self, 
								gtk_hotkey_info_get_key_id (hotkey))) {
		g_warning ("Hotkey '%s' already registered. Ignoring register request.",
				   gtk_hotkey_info_get_key_id (hotkey));
		return FALSE;
	}
	
	if (tomboy_keybinder_bind (gtk_hotkey_info_get_signature(hotkey),
							   hotkey_activated_cb,
							   self)) {
		self->priv->hotkeys = g_list_prepend (self->priv->hotkeys, hotkey);
		g_object_ref (hotkey);
		return TRUE;
	}
	
	/* Bugger, we failed to bind */
	g_set_error (error, GTK_HOTKEY_LISTENER_ERROR,
				 GTK_HOTKEY_LISTENER_ERROR_BIND,
				 "Failed to register hotkey '%s' with signature '%s'",
				 gtk_hotkey_info_get_key_id (hotkey),
				 gtk_hotkey_info_get_signature (hotkey));
	
	return FALSE;
}


static gboolean
gtk_hotkey_x11_listener_real_unbind_hotkey (GtkHotkeyListener   *base,
												GtkHotkeyInfo		*hotkey,
												GError				**error)
{
	GtkHotkeyX11Listener	*self;
	GtkHotkeyInfo			*saved_hk;
	const gchar				*signature;
	
	g_return_val_if_fail (GTK_HOTKEY_IS_X11_LISTENER (base), FALSE);
	g_return_val_if_fail (GTK_HOTKEY_IS_INFO (hotkey), FALSE);
	
	self = GTK_HOTKEY_X11_LISTENER (base);
	signature = gtk_hotkey_info_get_signature (hotkey);
	saved_hk = find_hotkey_from_key_id (self, gtk_hotkey_info_get_key_id(hotkey));
	
	if (!saved_hk) {
		g_set_error (error, GTK_HOTKEY_LISTENER_ERROR,
					 GTK_HOTKEY_LISTENER_ERROR_UNBIND,
					 "Failed to unregister hotkey '%s' with signature '%s'. "
					 "No hotkey with that signature is known",
					 gtk_hotkey_info_get_key_id (hotkey),
					 signature);
		return FALSE;
	}
	
	/* Remove actual keybinding */
	tomboy_keybinder_unbind (signature, hotkey_activated_cb);
	
	/* Clean up refs */
	self->priv->hotkeys = g_list_remove (self->priv->hotkeys, saved_hk);
	g_object_unref (saved_hk);
	
	/* Clean up signal handler */
	gulong handler = g_signal_handler_find (self,
											G_SIGNAL_MATCH_DATA | G_SIGNAL_MATCH_FUNC,
											0, 0, NULL, gtk_hotkey_info_activated,
											hotkey);
	if (handler == 0) {
		g_warning ("Failed to disconnect signal handler for hotkey '%s'",
				   gtk_hotkey_info_get_key_id (hotkey));
	} else {
		g_signal_handler_disconnect (self, handler);
	}
	
	return TRUE;
											
}

static void
hotkey_activated_cb	(char		   *signature,
					 gpointer		user_data)
{
	GtkHotkeyX11Listener	*self;
	GtkHotkeyInfo			*hotkey;
	GList					*iter;
	guint					event_time;
	
	g_return_if_fail (GTK_HOTKEY_IS_X11_LISTENER(user_data));
	g_return_if_fail (signature != NULL);
	
	self = GTK_HOTKEY_X11_LISTENER(user_data);
	event_time = tomboy_keybinder_get_current_event_time ();
	
	/* Trigger signals for hotkeys with matching signature */
	for (iter = self->priv->hotkeys; iter; iter = iter->next) {
		hotkey = GTK_HOTKEY_INFO (iter->data); 		
		if (g_str_equal (signature, gtk_hotkey_info_get_signature (hotkey))) {
				gtk_hotkey_listener_activated (GTK_HOTKEY_LISTENER(self),
											   hotkey, event_time);
				gtk_hotkey_info_activated (hotkey, event_time);
		}
	}
	
}

static GtkHotkeyInfo*
find_hotkey_from_key_id (GtkHotkeyX11Listener	*self,
						 const gchar			*key_id)
{
	GList			*iter;
	GtkHotkeyInfo   *hotkey;
	
	g_return_val_if_fail (GTK_HOTKEY_IS_X11_LISTENER(self), NULL);
	g_return_val_if_fail (key_id != NULL, NULL);
	
	for (iter = self->priv->hotkeys; iter; iter = iter->next) {
		hotkey = GTK_HOTKEY_INFO (iter->data);
		
		if (g_str_equal (gtk_hotkey_info_get_key_id(hotkey), key_id))
			return hotkey;
	}
	
	return NULL;
}

static void
gtk_hotkey_x11_listener_class_init (GtkHotkeyX11ListenerClass * klass)
{
	gtk_hotkey_x11_listener_parent_class = g_type_class_peek_parent (klass);
	
	GTK_HOTKEY_LISTENER_CLASS (klass)->bind_hotkey =
								gtk_hotkey_x11_listener_real_bind_hotkey;
	GTK_HOTKEY_LISTENER_CLASS (klass)->unbind_hotkey =
								gtk_hotkey_x11_listener_real_unbind_hotkey;
	
	/* Initialize the tomboy keybinder */
	tomboy_keybinder_init ();
}


static void
gtk_hotkey_x11_listener_init (GtkHotkeyX11Listener * self)
{
	self->priv = g_new0 (GtkHotkeyX11ListenerPrivate, 1);
}

static void
gtk_hotkey_x11_listener_finalize (GtkHotkeyX11Listener * self)
{
	g_free(self->priv);
}

GType
gtk_hotkey_x11_listener_get_type (void)
{
	static GType gtk_hotkey_x11_listener_type_id = 0;
	
	if (G_UNLIKELY (gtk_hotkey_x11_listener_type_id == 0)) {
		static const GTypeInfo g_define_type_info = {
			sizeof (GtkHotkeyX11ListenerClass),
			(GBaseInitFunc) NULL,
			(GBaseFinalizeFunc) gtk_hotkey_x11_listener_finalize,
			(GClassInitFunc) gtk_hotkey_x11_listener_class_init,
			(GClassFinalizeFunc) NULL,
			NULL,
			sizeof (GtkHotkeyX11Listener),
			0,
			(GInstanceInitFunc) gtk_hotkey_x11_listener_init
		};
		
		gtk_hotkey_x11_listener_type_id = g_type_register_static (GTK_HOTKEY_TYPE_LISTENER, "GtkHotkeyX11Listener", &g_define_type_info, 0);
	}
	return gtk_hotkey_x11_listener_type_id;
}

--- NEW FILE: gtk-hotkey-info.c ---
/*
 * This file is part of GtkHotkey.
 * Copyright Mikkel Kamstrup Erlandsen, March, 2008
 *
 *   GtkHotkey is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Lesser General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   GtkHotkey is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Lesser General Public License for more details.
 *
 *   You should have received a copy of the GNU Lesser General Public License
 *   along with GtkHotkey.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "gtk-hotkey-info.h"
#include "gtk-hotkey-error.h"
#include "gtk-hotkey-listener.h"

struct _GtkHotkeyInfoPrivate {
	gchar		*app_id;
	gchar		*key_id;
	GAppInfo	*app_info;
	gchar		*signature;
	gchar		*description;
	GtkHotkeyListener	*listener;
};
#define GTK_HOTKEY_INFO_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_HOTKEY_TYPE_INFO, GtkHotkeyInfoPrivate))

enum  {
	GTK_HOTKEY_INFO_DUMMY_PROPERTY,
	GTK_HOTKEY_INFO_BOUND,
	GTK_HOTKEY_INFO_APPLICATION_ID,
	GTK_HOTKEY_INFO_KEY_ID,
	GTK_HOTKEY_INFO_APP_INFO,
	GTK_HOTKEY_INFO_SIGNATURE,
	GTK_HOTKEY_INFO_DESCRIPTION,
};

enum {
	ACTIVATED,
	
	LAST_SIGNAL
};

guint				info_signals[LAST_SIGNAL] = { 0 };

static gpointer		gtk_hotkey_info_parent_class = NULL;

static void			gtk_hotkey_info_finalize (GObject * obj);

/**
 * SECTION:gtk-hotkey-info
 * @short_description: Primary representation of a hotkey
 * @see_also: #GtkHotkeyRegistry
 *
 * #GtkHotkeyInfo is the primary object around which the GtkHotkey library
 * revolves.
 *
 * Hotkeys are stored and managed via a #GtkHotkeyRegistry, while the actual
 * binding and listening for key-presses is done by a #GtkHotkeyListener.
 **/


/**
 * gtk_hotkey_info_bind:
 * @self: The hotkey to bind
 * @error: Place to return a #GError, or %NULL to ignore
 *
 * Register the hotkey with the system. The #GtkHotkeyInfo::activated signal
 * will now be emitted when the user presses the keyboard combination
 * matching the hotkey's signature.
 *
 * Returns: %TRUE on success, and %FALSE on error in which case @error
 *          is also set
 **/
gboolean
gtk_hotkey_info_bind (GtkHotkeyInfo* self, GError **error)
{
	gboolean result;
	
	g_return_val_if_fail (GTK_HOTKEY_IS_INFO (self), FALSE);
	
	if (gtk_hotkey_info_is_bound(self)) {
		g_set_error (error, GTK_HOTKEY_LISTENER_ERROR,
					 GTK_HOTKEY_LISTENER_ERROR_BIND,
					 "Can not bind hotkey '%s' with signature '%s'. "
					 "It is already bound",
					 gtk_hotkey_info_get_key_id(self),
					 gtk_hotkey_info_get_signature(self));
		return FALSE;
	}
	
	if (!self->priv->listener)
		self->priv->listener = gtk_hotkey_listener_get_default ();
	
	g_return_val_if_fail (GTK_HOTKEY_IS_LISTENER(self->priv->listener), FALSE);
	
	result = gtk_hotkey_listener_bind_hotkey (self->priv->listener, self, error);
	if (!result) {
		g_object_unref (self->priv->listener);
		self->priv->listener = NULL;
	}
	
	if (result)
		g_object_notify (G_OBJECT(self), "bound");
	
	return result;
}

/**
 * gtk_hotkey_info_unbind
 * @self: The hotkey to unbind
 * @error: Place to return a #GError, or %NULL to ignore
 * @returns: %TRUE on success, and %FALSE on error in which case @error
 *          is also set
 *
 * Remove the hotkey binding from the system. The #GtkHotkeyInfo::activated
 * signal will no longer be emitted when the hotkey is pressed.
 */
gboolean
gtk_hotkey_info_unbind (GtkHotkeyInfo* self, GError **error)
{	
	gboolean result;
	
	g_return_val_if_fail (GTK_HOTKEY_IS_INFO (self), FALSE);
	
	if (!gtk_hotkey_info_is_bound(self)) {
		g_set_error (error, GTK_HOTKEY_LISTENER_ERROR,
					 GTK_HOTKEY_LISTENER_ERROR_UNBIND,
					 "Can not unbind hotkey '%s' with signature '%s'."
					 "It is not bound",
					 gtk_hotkey_info_get_key_id(self),
					 gtk_hotkey_info_get_signature(self));
		return FALSE;
	}
	
	g_return_val_if_fail (GTK_HOTKEY_IS_LISTENER(self->priv->listener), FALSE);
	
	result = gtk_hotkey_listener_unbind_hotkey (self->priv->listener, self,
												error);
	
	g_object_unref (self->priv->listener);
	self->priv->listener = NULL;
	
	if (result)
		g_object_notify (G_OBJECT(self), "bound");
	
	return result;
}

/**
 * gtk_hotkey_info_is_bound
 * @self: The hotkey to inspect
 * @returns: %TRUE if gtk_hotkey_info_bind() has been called and returned %TRUE
 *           on this hotkey
 *
 * Check whether the hotkey has been succesfully bound to a #GtkHotkeyListener.
 */
gboolean
gtk_hotkey_info_is_bound (GtkHotkeyInfo* self)
{	
	return (self->priv->listener != NULL);
}

/**
 * gtk_hotkey_info_get_application_id
 * @self:
 *
 * Get the unique system identifier for the hotkey. See 
 * #GtkHotkeyInfo:application-id for details.
 */
const gchar*
gtk_hotkey_info_get_application_id (GtkHotkeyInfo* self)
{
	g_return_val_if_fail (GTK_HOTKEY_IS_INFO (self), NULL);
	return self->priv->app_id;
}

/**
 * gtk_hotkey_info_get_key_id
 * @self:
 *
 * Get the identifier the owning application use to identify this hotkey.
 * See #GtkHotkeyInfo:key-id for details.
 */
const gchar*
gtk_hotkey_info_get_key_id (GtkHotkeyInfo* self)
{
	g_return_val_if_fail (GTK_HOTKEY_IS_INFO (self), NULL);
	return self->priv->key_id;
}

/**
 * gtk_hotkey_info_get_app_info
 * @self:
 *
 * Not to be confused with the value of the #GtkHotkeyInfo:application-id
 * property. The hotkey can be associated with an installed desktop application
 * via a #GAppInfo. This is not mandatory and this method returns %NULL
 * if no desktop application has been associated with this hotkey.
 *
 * See the #GtkHotkeyInfo:app-info property for details.
 */
GAppInfo*
gtk_hotkey_info_get_app_info (GtkHotkeyInfo* self)
{
	g_return_val_if_fail (GTK_HOTKEY_IS_INFO (self), NULL);
	return self->priv->app_info;
}

/**
 * gtk_hotkey_info_get_signature
 * @self:
 *
 * Get the keyboard signature of the hotkey. This could for example be
 * '<Alt>F3' or '<Control><Shift>G'.
 */
const gchar*
gtk_hotkey_info_get_signature (GtkHotkeyInfo* self)
{
	g_return_val_if_fail (GTK_HOTKEY_IS_INFO (self), NULL);
	return self->priv->signature;
}

/**
 * gtk_hotkey_info_get_description
 * @self:
 * @returns: The description of the hotkey or %NULL if none is set
 *
 * Get the free form description of the hotkey. The description is not guaranteed
 * to be set and may be %NULL.
 *
 * FIXME: Do we need to take i18n into account here?
 */
const gchar*
gtk_hotkey_info_get_description (GtkHotkeyInfo* self)
{
	g_return_val_if_fail (GTK_HOTKEY_IS_INFO(self), NULL);
	return self->priv->description;
}

/**
 * gtk_hotkey_info_set_description
 * @self:
 *
 * Set a description for the hotkey. See also gtk_hotkey_info_get_description().
 */
void
gtk_hotkey_info_set_description (GtkHotkeyInfo* self, const gchar *description)
{
	g_return_if_fail (GTK_HOTKEY_IS_INFO(self));
	g_object_set (self, "description", description, NULL); 
}

/**
 * gtk_hotkey_info_equals
 * @hotkey1: The first hotkey to compare
 * @hotkey2: Second hotkey to compare to
 * @sloppy_equals: If %TRUE sloppy equality will be used. This ignores
 *                 the #GtkHotkeyInfo:description and #GtkHotkeyInfo:app-info
 *                 properties of the objects.
 * @returns: %TRUE if all the properties of the hotkeys match. Two %NULL hotkeys
 *           are also considered equal.
 *
 * Compare two #GtkHotkeyInfo<!-- -->s to see if they are equal. This method
 * allows an optional 'sloppy equality' which ignores #GtkHotkeyInfo:description
 * and #GtkHotkeyInfo:app-info.
 */
gboolean
gtk_hotkey_info_equals (GtkHotkeyInfo *hotkey1,
						GtkHotkeyInfo *hotkey2,
						gboolean 			sloppy_equals)
{	
	if (hotkey1 == hotkey2) return TRUE;
	
	g_return_val_if_fail (GTK_HOTKEY_IS_INFO (hotkey1), FALSE);
	g_return_val_if_fail (GTK_HOTKEY_IS_INFO (hotkey2), FALSE);
	
	if (!g_str_equal (gtk_hotkey_info_get_application_id (hotkey1),
					  gtk_hotkey_info_get_application_id (hotkey2)))
		return FALSE;
	
	if (!g_str_equal (gtk_hotkey_info_get_key_id (hotkey1),
					  gtk_hotkey_info_get_key_id (hotkey2)))
		return FALSE;
	
	if (!g_str_equal (gtk_hotkey_info_get_signature (hotkey1),
					  gtk_hotkey_info_get_signature (hotkey2)))
		return FALSE;
	
	/* For sloppy equality this is good enough */
	if (sloppy_equals)
		return TRUE;
	
	const gchar	*d1, *d2;
	d1 = gtk_hotkey_info_get_description (hotkey1);
	d2 = gtk_hotkey_info_get_description (hotkey2);
	if (d1 != NULL && d2 != NULL) {
		if (!g_str_equal (gtk_hotkey_info_get_description (hotkey1),
						  gtk_hotkey_info_get_description (hotkey2)))
			return FALSE;
	} else if (d1 != d2)
		return FALSE;
	/* The case d1 == d2 == NULL will pass through the above */
	
	GAppInfo	*app1, *app2;
	app1 = gtk_hotkey_info_get_app_info (hotkey1);
	app2 = gtk_hotkey_info_get_app_info (hotkey2);
	if (app1 != NULL && app2 != NULL) {
		if (!g_app_info_equal (app1, app2))
			return FALSE;
	} else if (app1 != app2)
		return FALSE;
	/* As above, if app1 == app2 == NULL we count equality */
	
	return TRUE;
}

/**
 * gtk_hotkey_info_activated
 * @self: #GtkHotkeyInfo to emit the #GtkHotkeyInfo::activated signal
 * @event_time: The system time the event happened on. This is useful for
 *              applications to pass through focus stealing prevention when
 *              mapping windows
 *
 * Emit the #GtkHotkeyInfo::activated signal on a hotkey. Mainly called
 * by #GtkHotkeyListener implementations. This method should not normally be
 * used by applications.
 */
void
gtk_hotkey_info_activated (GtkHotkeyInfo* self, guint event_time)
{
	g_return_if_fail (GTK_HOTKEY_IS_INFO(self));
	
	g_signal_emit (self, info_signals[ACTIVATED], 0, event_time);
}

/**
 * gtk_hotkey_info_new:
 * @app_id: Unique identifier the running application can choose for it self. 
 *          May be a free form string, but a descriptive name is encouraged
 * @key_id: A key the application uses to recognize the hotkey. May be a free 
 *          form string, but a descriptive name is encouraged
 * @signature: A key press signature parsable by gtk_accelerator_parse(). For 
 *             examplpe '<Alt>F3' or '<Control><Shift>G'.
 * @app_info: An optional #GAppInfo to associate with the hotkey. Pass %NULL to
 *            ignore this
 * @returns: A new #GtkHotkeyInfo or %NULL on error. Error conditions could for 
 *           example be invalid an invalid @signature, or %NULL arguments.
 *
 * Create a new hotkey. To actually trigger the hotkey when the user enters
 * the right keyboard combination call gtk_hotkey_info_bind(). To save and
 * load your hotkey settings use the #GtkHotkeyRegistry provided by
 * gtk_hotkey_registry_get_default().
 **/
GtkHotkeyInfo*
gtk_hotkey_info_new (const gchar	*app_id,
					 const gchar	*key_id,
					 const gchar	*signature,
					 GAppInfo		*app_info)
{
	GtkHotkeyInfo * self;
	
	g_return_val_if_fail (app_id != NULL, NULL);
	g_return_val_if_fail (key_id != NULL, NULL);
	
	/* A NULL app_info is ok, but it better be a GAppInfo then */
	if (app_info != NULL)
		g_return_val_if_fail (G_IS_APP_INFO(app_info), NULL);
	
	self = g_object_new (GTK_HOTKEY_TYPE_INFO, "application-id", app_id,
											   "key-id", key_id,
											   "signature", signature,
											   "app-info", app_info,
												NULL);
	return self;
}

static void
gtk_hotkey_info_get_property (GObject * object, guint property_id, GValue * value, GParamSpec * pspec)
{
	GtkHotkeyInfo * self;
	
	self = GTK_HOTKEY_INFO (object);
	
	switch (property_id) {
		case GTK_HOTKEY_INFO_BOUND:
			g_value_set_boolean (value,
								 (self->priv->listener != NULL));
			break;
		case GTK_HOTKEY_INFO_APPLICATION_ID:
			g_value_set_string (value,
								gtk_hotkey_info_get_application_id (self));
			break;
		case GTK_HOTKEY_INFO_KEY_ID:
			g_value_set_string (value,
								gtk_hotkey_info_get_key_id (self));
			break;
		case GTK_HOTKEY_INFO_APP_INFO:
			g_value_set_object (value,
								gtk_hotkey_info_get_app_info (self));
			break;
		case GTK_HOTKEY_INFO_SIGNATURE:
			g_value_set_string (value,
								gtk_hotkey_info_get_signature (self));
			break;
		case GTK_HOTKEY_INFO_DESCRIPTION:
			g_value_set_string (value,
								gtk_hotkey_info_get_description (self));
			break;
		default:
			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
			break;
	}
}


static void
gtk_hotkey_info_set_property (GObject * object, guint property_id, const GValue * value, GParamSpec * pspec)
{
	GtkHotkeyInfo			*self;
	GtkHotkeyInfoPrivate	*priv;
	
	self = GTK_HOTKEY_INFO (object);
	priv = self->priv;
	
	switch (property_id) {
		case GTK_HOTKEY_INFO_BOUND:
			g_critical ("Writing to read only property 'bound'");
			break;
		case GTK_HOTKEY_INFO_APPLICATION_ID:
			if (priv->app_id)
				g_critical ("Overwriting construct only property 'application-id'");
			priv->app_id = g_value_dup_string (value);
			break;
		case GTK_HOTKEY_INFO_KEY_ID:
			if (priv->key_id)
				g_critical ("Overwriting construct only property 'key-id'");
			priv->key_id = g_value_dup_string (value);
			break;
		case GTK_HOTKEY_INFO_APP_INFO:
			if (priv->app_info)
				g_critical ("Overwriting construct only property 'app-info'");
			priv->app_info = g_value_dup_object (value);
			break;
		case GTK_HOTKEY_INFO_SIGNATURE:
			if (priv->signature)
				g_critical ("Overwriting construct only property 'signature'");
			priv->signature = g_value_dup_string (value);
			break;
		case GTK_HOTKEY_INFO_DESCRIPTION:
			if (priv->description)
				g_free(priv->description);
			priv->description = g_value_dup_string (value);
			break;
		default:
			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
			break;
	}
}


static void
gtk_hotkey_info_class_init (GtkHotkeyInfoClass * klass)
{
	gtk_hotkey_info_parent_class = g_type_class_peek_parent (klass);
	g_type_class_add_private (klass, sizeof (GtkHotkeyInfoPrivate));
	
	G_OBJECT_CLASS (klass)->get_property = gtk_hotkey_info_get_property;
	G_OBJECT_CLASS (klass)->set_property = gtk_hotkey_info_set_property;
	G_OBJECT_CLASS (klass)->finalize = gtk_hotkey_info_finalize;
	
	/**
	 * GtkHotkeyInfo:bound
	 *
	 * Property reflecting whether or not this hotkey has been bound to
	 * a #GtkHotkeyListener. If this property is %TRUE you will receive
	 * #GtkHotkeyInfo::activated signals when the hotkey is triggered
	 * by the user.
	 */
	g_object_class_install_property (G_OBJECT_CLASS (klass),
									 GTK_HOTKEY_INFO_BOUND,
									 g_param_spec_boolean ("bound",
														  "Is Bound",
														  "Whether or not the hotkey is bound to a GtkHotkeyListener",
														  FALSE, 
														  G_PARAM_READABLE));
	
	/**
	 * GtkHotkeyInfo:application-id
	 *
	 * A free form string chosen by the application using the hotkey, under
	 * which the application identifies itself.
	 */
	g_object_class_install_property (G_OBJECT_CLASS (klass),
									 GTK_HOTKEY_INFO_APPLICATION_ID,
									 g_param_spec_string ("application-id",
														  "Application Id",
														  "Globally unique application id",
														  NULL, 
														  G_PARAM_READABLE | G_PARAM_WRITABLE |
														  G_PARAM_CONSTRUCT_ONLY));
	
	/**
	 * GtkHotkeyInfo:key-id
	 *
	 * A free form string the application using the hotkey has attributed
	 * the hotkey so that it can be identified later on. Applications are
	 * encouraged to choose descriptive key ids.
	 */
	g_object_class_install_property (G_OBJECT_CLASS (klass),
									 GTK_HOTKEY_INFO_KEY_ID,
									 g_param_spec_string ("key-id",
														  "Hotkey Id",
														  "Globally unique identifier for the hotkey",
														  NULL, 
														  G_PARAM_READABLE | G_PARAM_WRITABLE |
														  G_PARAM_CONSTRUCT_ONLY));
	
	/**
	 * GtkHotkeyInfo:app-info
	 *
	 * A #GAppInfo associated with the key. This is mainly useful for external
	 * applications which can use the information provided by the #GAppInfo
	 * to display meaningful messages to the user. Like 'The keyboard 
	 * combination <Alt>F3' is already assigned to the application
	 * "Deskbar Applet", please select another'.
	 */
	g_object_class_install_property (G_OBJECT_CLASS (klass),
									 GTK_HOTKEY_INFO_APP_INFO,
									 g_param_spec_object ("app-info",
														  "Application Information",
														  "Object holding metadata about "
														  "the hotkey's application",
														  G_TYPE_APP_INFO, 
														  G_PARAM_READABLE | G_PARAM_WRITABLE |
														  G_PARAM_CONSTRUCT_ONLY));
	
	/**
	 * GtkHotkeyInfo:signature
	 *
	 * The keyboard signature of the hotkey. This could for example by
	 * '<Alt>F3' or '<Control><Shift>G'. The signature should be parsable by
	 * gtk_accelerator_parse().
	 */
	g_object_class_install_property (G_OBJECT_CLASS (klass),
									 GTK_HOTKEY_INFO_SIGNATURE,
									 g_param_spec_string ("signature",
														  "Signature",
														  "String defining the keyboard shortcut",
														  NULL, 
														  G_PARAM_READABLE | G_PARAM_WRITABLE |
														  G_PARAM_CONSTRUCT_ONLY));
	
	/**
	 * GtkHotkeyInfo:description
	 *
	 * An optional free form description of the hotkey.
	 */
	g_object_class_install_property (G_OBJECT_CLASS (klass),
									 GTK_HOTKEY_INFO_DESCRIPTION,
									 g_param_spec_string ("description",
														  "Description",
														  "Short description of what happens upon activation",
														  "", 
														  G_PARAM_READABLE | G_PARAM_WRITABLE));
	
	/**
	 * GtkHotkeyInfo::activated:
	 * @hotkey: a #GtkHotkeyInfo for the hotkey that was activated
	 * @event_time: Time for event triggering the keypress. This is mainly
	 *              used to pass to window management functions to pass through
	 *              focus stealing prevention
	 *
	 * Emitted when a hotkey has been activated.
	 */
	info_signals[ACTIVATED] = \
	g_signal_new ("activated",
				  GTK_HOTKEY_TYPE_INFO,
				  G_SIGNAL_RUN_LAST,
				  0, NULL, NULL,
				  g_cclosure_marshal_VOID__UINT,
				  G_TYPE_NONE, 1,
				  G_TYPE_UINT);
}


static void
gtk_hotkey_info_init (GtkHotkeyInfo * self)
{
	self->priv = GTK_HOTKEY_INFO_GET_PRIVATE (self);
	
	self->priv->app_id = NULL;
	self->priv->key_id = NULL;
	self->priv->app_info = NULL;
}


static void
gtk_hotkey_info_finalize (GObject * obj)
{
	GtkHotkeyInfo			*self;
	GtkHotkeyInfoPrivate	*priv;
	
	self = GTK_HOTKEY_INFO (obj);
	priv = self->priv;
	
	if (priv->app_id)
		g_free (priv->app_id);
	if (priv->key_id)
		g_free (priv->key_id);
	if (priv->app_info)
		g_object_unref (priv->app_info);
	if (priv->signature)
		g_free (priv->signature);
	if (priv->description)
		g_free (priv->description);
	if (GTK_HOTKEY_IS_LISTENER (priv->listener))
		g_object_unref (priv->listener);
	
	G_OBJECT_CLASS (gtk_hotkey_info_parent_class)->finalize (obj);
}


GType
gtk_hotkey_info_get_type (void)
{
	static GType gtk_hotkey_info_type_id = 0;
	
	if (G_UNLIKELY (gtk_hotkey_info_type_id == 0)) {
		static const GTypeInfo g_define_type_info = {
			sizeof (GtkHotkeyInfoClass),
			(GBaseInitFunc) NULL,
			(GBaseFinalizeFunc) NULL,
			(GClassInitFunc) gtk_hotkey_info_class_init,
			(GClassFinalizeFunc) NULL,
			NULL,
			sizeof (GtkHotkeyInfo),
			0,
			(GInstanceInitFunc) gtk_hotkey_info_init
		};
		
		gtk_hotkey_info_type_id = g_type_register_static (G_TYPE_OBJECT,
														  "GtkHotkeyInfo",
														  &g_define_type_info,
														  0);
	}
	
	return gtk_hotkey_info_type_id;
}





--- NEW FILE: gtk-hotkey-registry.h ---
/*
 * This file is part of GtkHotkey.
 * Copyright Mikkel Kamstrup Erlandsen, March, 2008
 *
 *   GtkHotkey is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Lesser General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   GtkHotkey is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Lesser General Public License for more details.
 *
 *   You should have received a copy of the GNU Lesser General Public License
 *   along with GtkHotkey.  If not, see <http://www.gnu.org/licenses/>.
 */

#if !defined (__GTK_HOTKEY_H__) && !defined (GTK_HOTKEY_COMPILATION)
#error "Only <gtkhotkey.h> can be included directly."
#endif

#ifndef __GTK_HOTKEY_REGISTRY_H__
#define __GTK_HOTKEY_REGISTRY_H__

#include <glib.h>
#include <glib-object.h>
#include <stdlib.h>
#include <string.h>
#include "gtk-hotkey-info.h"
#include "gtk-hotkey-error.h"

G_BEGIN_DECLS


#define GTK_HOTKEY_TYPE_STORAGE (gtk_hotkey_registry_get_type ())
#define GTK_HOTKEY_REGISTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_HOTKEY_TYPE_STORAGE, GtkHotkeyRegistry))
#define GTK_HOTKEY_REGISTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_HOTKEY_TYPE_STORAGE, GtkHotkeyRegistryClass))
#define GTK_HOTKEY_IS_REGISTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_HOTKEY_TYPE_STORAGE))
#define GTK_HOTKEY_IS_REGISTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_HOTKEY_TYPE_STORAGE))
#define GTK_HOTKEY_REGISTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_HOTKEY_TYPE_STORAGE, GtkHotkeyRegistryClass))

typedef struct _GtkHotkeyRegistry GtkHotkeyRegistry;
typedef struct _GtkHotkeyRegistryClass GtkHotkeyRegistryClass;
typedef struct _GtkHotkeyRegistryPrivate GtkHotkeyRegistryPrivate;

struct _GtkHotkeyRegistry {
	GObject parent;
	GtkHotkeyRegistryPrivate * priv;
};
struct _GtkHotkeyRegistryClass {
	GObjectClass	parent;
	GtkHotkeyInfo*  (*get_hotkey)					(GtkHotkeyRegistry   *self,
													 const char			*app_id,
													 const char			*key_id,
													 GError				**error);
	GList*			(*get_application_hotkeys)		(GtkHotkeyRegistry   *self,
													 const char			*app_id,
													 GError				**error);
	GList*			(*get_all_hotkeys)				(GtkHotkeyRegistry   *self);
	gboolean		(*store_hotkey)					(GtkHotkeyRegistry   *self,
													 GtkHotkeyInfo		*info,
													 GError				**error);
	gboolean		(*delete_hotkey)				(GtkHotkeyRegistry   *self,
													 const gchar		*app_id,
													 const gchar		*key_id,
													 GError				**error);
	gboolean		(*has_hotkey)					(GtkHotkeyRegistry   *self,
													 const gchar		*app_id,
													 const gchar		*key_id);
	void			(*hotkey_stored)				(GtkHotkeyRegistry   *self,
													 GtkHotkeyInfo		*info);
	void			(*hotkey_deleted)				(GtkHotkeyRegistry   *self,
													 GtkHotkeyInfo		*info);
};

GtkHotkeyRegistry*		gtk_hotkey_registry_get_default		(void);

GtkHotkeyInfo*			gtk_hotkey_registry_get_hotkey		(GtkHotkeyRegistry		*self,
															 const char				*app_id,
															 const char				*key_id,
															 GError					**error);
															 
GList*					gtk_hotkey_registry_get_application_hotkeys
															(GtkHotkeyRegistry		*self,
															 const char				*app_id,
															 GError					**error);
															 
GList*					gtk_hotkey_registry_get_all_hotkeys  (GtkHotkeyRegistry		*self);

gboolean				gtk_hotkey_registry_store_hotkey		(GtkHotkeyRegistry		*self,
															 GtkHotkeyInfo			*info,
															 GError					**error);

gboolean				gtk_hotkey_registry_delete_hotkey	(GtkHotkeyRegistry		*self,
															 const gchar			*app_id,
															 const gchar			*key_id,
															 GError					**error);
															 
gboolean				gtk_hotkey_registry_has_hotkey		(GtkHotkeyRegistry		*self,
															 const gchar			*app_id,
															 const gchar			*key_id);

void					gtk_hotkey_registry_hotkey_stored	(GtkHotkeyRegistry		*self,
															 GtkHotkeyInfo			*hotkey);

void					gtk_hotkey_registry_hotkey_deleted	(GtkHotkeyRegistry		*self,
															 GtkHotkeyInfo			*hotkey);

GType					gtk_hotkey_registry_get_type			(void);

G_END_DECLS

#endif



More information about the Commits mailing list