From 203ec385c5d4393b56e5ae7b1a0ce81e8a0e6072 Mon Sep 17 00:00:00 2001 From: Dan Winship Date: Mon, 29 Jun 2009 12:07:10 -0400 Subject: [PATCH] Add ShellGConf Although methods like gconf_client_get/set_bool() and such are usable from gjs, get_list/set_list is not, since there's only one method for all list types. So ShellGConf wraps GConfClient and adds separate typed list methods. Also, add a detailed "changed" signal that can easily be connected to from js, since we can't currently use gconf_client_notify_add() directly. --- src/Makefile.am | 2 + src/shell-gconf.c | 396 ++++++++++++++++++++++++++++++++++++++++++++++ src/shell-gconf.h | 95 +++++++++++ 3 files changed, 493 insertions(+) create mode 100644 src/shell-gconf.c create mode 100644 src/shell-gconf.h diff --git a/src/Makefile.am b/src/Makefile.am index e117e532f..44f9da7c4 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -61,6 +61,8 @@ libgnome_shell_la_SOURCES = \ shell-embedded-window.c \ shell-embedded-window.h \ shell-embedded-window-private.h \ + shell-gconf.c \ + shell-gconf.h \ shell-gtk-embed.c \ shell-gtk-embed.h \ shell-overflow-list.c \ diff --git a/src/shell-gconf.c b/src/shell-gconf.c new file mode 100644 index 000000000..fab0ac695 --- /dev/null +++ b/src/shell-gconf.c @@ -0,0 +1,396 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ + +#include "shell-gconf.h" + +#include +#include + +/** + * ShellGConf: + * + * A wrapper around #GConfClient that cleans up some of its + * non-gjs-bindable bits and makes a few gnome-shell-specific + * assumptions. + * + * For all #ShellGConf methods that take a GConf key path as an + * argument, you can pass either a full path (eg, + * "/desktop/gnome/shell/sidebar/visible"), or just a relative path + * starting from the root of the gnome-shell GConf key hierarchy (eg, + * "sidebar/visible"). + */ + +struct _ShellGConf +{ + GObject parent; + + GConfClient *client; +}; + +G_DEFINE_TYPE (ShellGConf, shell_gconf, G_TYPE_OBJECT); + +/* Signals */ +enum +{ + CHANGED, + + LAST_SIGNAL +}; + +static guint shell_gconf_signals [LAST_SIGNAL] = { 0 }; + +static void gconf_value_changed (GConfClient *client, const char *key, + GConfValue *new_value, gpointer user_data); + +static void +shell_gconf_init (ShellGConf *gconf) +{ + gconf->client = gconf_client_get_default (); + gconf_client_add_dir (gconf->client, SHELL_GCONF_DIR, + GCONF_CLIENT_PRELOAD_RECURSIVE, NULL); + g_signal_connect (gconf->client, "value_changed", + G_CALLBACK (gconf_value_changed), gconf); +} + +static void +shell_gconf_finalize (GObject *object) +{ + ShellGConf *gconf = SHELL_GCONF (object); + + g_signal_handlers_disconnect_by_func (gconf->client, + gconf_value_changed, gconf); + g_object_unref (gconf->client); + + G_OBJECT_CLASS (shell_gconf_parent_class)->finalize (object); +} + +static void +shell_gconf_class_init (ShellGConfClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->finalize = shell_gconf_finalize; + + /** + * ShellGConf::changed: + * @gconf: the #ShellGConf + * + * Emitted when a key in a watched directory is changed. The signal + * detail indicates which key changed. Eg, connect to + * "changed::sidebar/visible" to be notified when "sidebar/visible" + * changes. For gnome-shell's own GConf keys, the signal detail will + * be the relative path from the top of the gnome-shell GConf + * hierarchy ("/desktop/gnome/shell"). If you want to be notified + * about the value of a non-gnome-shell key, you must first call + * shell_gconf_watch_directory(), and then use the full GConf key path + * as the signal detail. + */ + shell_gconf_signals[CHANGED] = + g_signal_new ("changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + G_STRUCT_OFFSET (ShellGConfClass, changed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); +} + +/** + * shell_gconf_get_default: + * + * Gets the default #ShellGConf + * + * Return value: (transfer none): the default #ShellGConf + */ +ShellGConf * +shell_gconf_get_default (void) +{ + static ShellGConf *gconf = NULL; + + if (!gconf) + gconf = g_object_new (SHELL_TYPE_GCONF, NULL); + + return gconf; +} + +/** + * shell_gconf_watch_directory: + * @gconf: a #ShellGConf + * @directory: the path of a GConf directory to watch for changes in + * + * Adds @directory to the list of directories to watch; you must call + * this before connecting to #ShellGConf::changed for a key outside of + * the gnome-shell GConf tree. + */ +void +shell_gconf_watch_directory (ShellGConf *gconf, const char *directory) +{ + gconf_client_add_dir (gconf->client, directory, + GCONF_CLIENT_PRELOAD_NONE, NULL); +} + +static void +gconf_value_changed (GConfClient *client, const char *key, + GConfValue *new_value, gpointer user_data) +{ + ShellGConf *gconf = user_data; + GQuark detail; + + if (g_str_has_prefix (key, SHELL_GCONF_DIR "/")) + key += strlen (SHELL_GCONF_DIR "/"); + + /* This will create a lot of junk quarks, but it's the best we + * can do with gjs's current callback support. + */ + detail = g_quark_from_string (key); + g_signal_emit (gconf, shell_gconf_signals[CHANGED], detail); +} + +static char * +resolve_key (const char *key) +{ + if (*key == '/') + return g_strdup (key); + else + return g_build_filename (SHELL_GCONF_DIR, key, NULL); +} + + +#define SIMPLE_GETTER(NAME, TYPE, GCONF_GETTER) \ +TYPE \ +NAME (ShellGConf *gconf, const char *key, GError **error) \ +{ \ + char *get_key = resolve_key (key); \ + TYPE value; \ + \ + value = GCONF_GETTER (gconf->client, get_key, error); \ + g_free (get_key); \ + return value; \ +} + +#define LIST_GETTER(NAME, ELEMENT_TYPE) \ +GSList * \ +NAME (ShellGConf *gconf, const char *key, GError **error) \ +{ \ + char *get_key = resolve_key (key); \ + GSList *value; \ + \ + value = gconf_client_get_list (gconf->client, get_key, \ + ELEMENT_TYPE, error); \ + g_free (get_key); \ + return value; \ +} + +/** + * shell_gconf_get_boolean: + * @gconf: a #ShellGConf + * @key: a GConf key (as described in the #ShellGConf docs) + * @error: a #GError, which will be set on error + * + * Gets the value of @key, which must be boolean-valued. + * + * Return value: @key's value. If an error occurs, @error will be set + * and the return value is undefined. + **/ +SIMPLE_GETTER(shell_gconf_get_boolean, gboolean, gconf_client_get_bool) + +/** + * shell_gconf_get_int: + * @gconf: a #ShellGConf + * @key: a GConf key (as described in the #ShellGConf docs) + * @error: a #GError, which will be set on error + * + * Gets the value of @key, which must be integer-valued. + * + * Return value: @key's value. If an error occurs, @error will be set + * and the return value is undefined. + **/ +SIMPLE_GETTER(shell_gconf_get_int, int, gconf_client_get_int) + +/** + * shell_gconf_get_float: + * @gconf: a #ShellGConf + * @key: a GConf key (as described in the #ShellGConf docs) + * @error: a #GError, which will be set on error + * + * Gets the value of @key, which must be float-valued. + * + * Return value: @key's value. If an error occurs, @error will be set + * and the return value is undefined. + **/ +SIMPLE_GETTER(shell_gconf_get_float, float, gconf_client_get_float) + +/** + * shell_gconf_get_string: + * @gconf: a #ShellGConf + * @key: a GConf key (as described in the #ShellGConf docs) + * @error: a #GError, which will be set on error + * + * Gets the value of @key, which must be string-valued. + * + * Return value: (transfer full): @key's value, or %NULL if an error + * occurs. + **/ +SIMPLE_GETTER(shell_gconf_get_string, char *, gconf_client_get_string) + +/** + * shell_gconf_get_boolean_list: + * @gconf: a #ShellGConf + * @key: a GConf key (as described in the #ShellGConf docs) + * @error: a #GError, which will be set on error + * + * Gets the value of @key, which must be boolean-list-valued. + * + * Return value: (element-type gboolean) (transfer full): @key's + * value, or %NULL if an error occurs. + **/ +LIST_GETTER(shell_gconf_get_boolean_list, GCONF_VALUE_BOOL) + +/** + * shell_gconf_get_int_list: + * @gconf: a #ShellGConf + * @key: a GConf key (as described in the #ShellGConf docs) + * @error: a #GError, which will be set on error + * + * Gets the value of @key, which must be integer-list-valued. + * + * Return value: (element-type int) (transfer full): @key's + * value, or %NULL if an error occurs. + **/ +LIST_GETTER(shell_gconf_get_int_list, GCONF_VALUE_INT) + +/** + * shell_gconf_get_float_list: + * @gconf: a #ShellGConf + * @key: a GConf key (as described in the #ShellGConf docs) + * @error: a #GError, which will be set on error + * + * Gets the value of @key, which must be float-list-valued. + * + * Return value: (element-type float) (transfer full): @key's + * value, or %NULL if an error occurs. + **/ +LIST_GETTER(shell_gconf_get_float_list, GCONF_VALUE_FLOAT) + +/** + * shell_gconf_get_string_list: + * @gconf: a #ShellGConf + * @key: a GConf key (as described in the #ShellGConf docs) + * @error: a #GError, which will be set on error + * + * Gets the value of @key, which must be string-list-valued. + * + * Return value: (element-type utf8) (transfer full): @key's + * value, or %NULL if an error occurs. + **/ +LIST_GETTER(shell_gconf_get_string_list, GCONF_VALUE_STRING) + + +#define SIMPLE_SETTER(NAME, TYPE, GCONF_SETTER) \ +void \ +NAME (ShellGConf *gconf, const char *key, TYPE value, GError **error) \ +{ \ + char *set_key = resolve_key (key); \ + \ + GCONF_SETTER (gconf->client, set_key, value, error); \ + g_free (set_key); \ +} + +#define LIST_SETTER(NAME, ELEMENT_TYPE) \ +void \ +NAME (ShellGConf *gconf, const char *key, \ + GSList *value, GError **error) \ +{ \ + char *set_key = resolve_key (key); \ + \ + gconf_client_set_list (gconf->client, set_key, ELEMENT_TYPE, \ + value, error); \ + g_free (set_key); \ +} + +/** + * shell_gconf_set_boolean: + * @gconf: a #ShellGConf + * @key: a GConf key (as described in the #ShellGConf docs) + * @value: value to set @key to + * @error: a #GError, which will be set on error + * + * Sets the value of @key to @value. + **/ +SIMPLE_SETTER(shell_gconf_set_boolean, gboolean, gconf_client_set_bool) + +/** + * shell_gconf_set_int: + * @gconf: a #ShellGConf + * @key: a GConf key (as described in the #ShellGConf docs) + * @value: value to set @key to + * @error: a #GError, which will be set on error + * + * Sets the value of @key to @value. + **/ +SIMPLE_SETTER(shell_gconf_set_int, int, gconf_client_set_int) + +/** + * shell_gconf_set_float: + * @gconf: a #ShellGConf + * @key: a GConf key (as described in the #ShellGConf docs) + * @value: value to set @key to + * @error: a #GError, which will be set on error + * + * Sets the value of @key to @value. + **/ +SIMPLE_SETTER(shell_gconf_set_float, float, gconf_client_set_float) + +/** + * shell_gconf_set_string: + * @gconf: a #ShellGConf + * @key: a GConf key (as described in the #ShellGConf docs) + * @value: value to set @key to + * @error: a #GError, which will be set on error + * + * Sets the value of @key to @value. + **/ +SIMPLE_SETTER(shell_gconf_set_string, const char *, gconf_client_set_string) + +/** + * shell_gconf_set_boolean_list: + * @gconf: a #ShellGConf + * @key: a GConf key (as described in the #ShellGConf docs) + * @value: (transfer none): value to set @key to + * @error: a #GError, which will be set on error + * + * Sets the value of @key to @value. + **/ +LIST_SETTER(shell_gconf_set_boolean_list, GCONF_VALUE_BOOL) + +/** + * shell_gconf_set_int_list: + * @gconf: a #ShellGConf + * @key: a GConf key (as described in the #ShellGConf docs) + * @value: (transfer none): value to set @key to + * @error: a #GError, which will be set on error + * + * Sets the value of @key to @value. + **/ +LIST_SETTER(shell_gconf_set_int_list, GCONF_VALUE_INT) + +/** + * shell_gconf_set_float_list: + * @gconf: a #ShellGConf + * @key: a GConf key (as described in the #ShellGConf docs) + * @value: (transfer none): value to set @key to + * @error: a #GError, which will be set on error + * + * Sets the value of @key to @value. + **/ +LIST_SETTER(shell_gconf_set_float_list, GCONF_VALUE_FLOAT) + +/** + * shell_gconf_set_string_list: + * @gconf: a #ShellGConf + * @key: a GConf key (as described in the #ShellGConf docs) + * @value: (transfer none): value to set @key to + * @error: a #GError, which will be set on error + * + * Sets the value of @key to @value. + **/ +LIST_SETTER(shell_gconf_set_string_list, GCONF_VALUE_STRING) diff --git a/src/shell-gconf.h b/src/shell-gconf.h new file mode 100644 index 000000000..e8b1905e7 --- /dev/null +++ b/src/shell-gconf.h @@ -0,0 +1,95 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ + +#ifndef SHELL_GCONF_H +#define SHELL_GCONF_H + +#include + +G_BEGIN_DECLS + +typedef struct _ShellGConf ShellGConf; +typedef struct _ShellGConfClass ShellGConfClass; + +#define SHELL_TYPE_GCONF (shell_gconf_get_type ()) +#define SHELL_GCONF(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), SHELL_TYPE_GCONF, ShellGConf)) +#define SHELL_GCONF_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SHELL_TYPE_GCONF, ShellGConfClass)) +#define SHELL_IS_GCONF(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), SHELL_TYPE_GCONF)) +#define SHELL_IS_GCONF_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SHELL_TYPE_GCONF)) +#define SHELL_GCONF_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SHELL_TYPE_GCONF, ShellGConfClass)) + +struct _ShellGConfClass +{ + GObjectClass parent_class; + + /* signals */ + void (*changed) (ShellGConf *gconf); +}; + +#define SHELL_GCONF_DIR "/desktop/gnome/shell" + +GType shell_gconf_get_type (void) G_GNUC_CONST; +ShellGConf *shell_gconf_get_default (void); + +void shell_gconf_watch_directory (ShellGConf *gconf, + const char *directory); + +gboolean shell_gconf_get_boolean (ShellGConf *gconf, + const char *key, + GError **error); +int shell_gconf_get_int (ShellGConf *gconf, + const char *key, + GError **error); +float shell_gconf_get_float (ShellGConf *gconf, + const char *key, + GError **error); +char *shell_gconf_get_string (ShellGConf *gconf, + const char *key, + GError **error); +GSList *shell_gconf_get_boolean_list (ShellGConf *gconf, + const char *key, + GError **error); +GSList *shell_gconf_get_int_list (ShellGConf *gconf, + const char *key, + GError **error); +GSList *shell_gconf_get_float_list (ShellGConf *gconf, + const char *key, + GError **error); +GSList *shell_gconf_get_string_list (ShellGConf *gconf, + const char *key, + GError **error); + +void shell_gconf_set_boolean (ShellGConf *gconf, + const char *key, + gboolean value, + GError **error); +void shell_gconf_set_int (ShellGConf *gconf, + const char *key, + int value, + GError **error); +void shell_gconf_set_float (ShellGConf *gconf, + const char *key, + float value, + GError **error); +void shell_gconf_set_string (ShellGConf *gconf, + const char *key, + const char *value, + GError **error); +void shell_gconf_set_boolean_list (ShellGConf *gconf, + const char *key, + GSList *value, + GError **error); +void shell_gconf_set_int_list (ShellGConf *gconf, + const char *key, + GSList *value, + GError **error); +void shell_gconf_set_float_list (ShellGConf *gconf, + const char *key, + GSList *value, + GError **error); +void shell_gconf_set_string_list (ShellGConf *gconf, + const char *key, + GSList *value, + GError **error); + +#endif +