[nautilus-actions: 4/19] Install action changed notification system
- From: Pierre Wieser <pwieser src gnome org>
- To: svn-commits-list gnome org
- Subject: [nautilus-actions: 4/19] Install action changed notification system
- Date: Tue, 9 Jun 2009 16:55:18 -0400 (EDT)
commit 0721ec9055c38b91743d536f2917ce594da3d5ec
Author: Pierre Wieser <pwieser trychlos org>
Date: Sun Jun 7 22:13:28 2009 +0200
Install action changed notification system
---
ChangeLog | 16 +
src/common/nact-action-profile.c | 50 +++-
src/common/nact-action-profile.h | 2 +
src/common/nact-action.c | 57 +++-
src/common/nact-action.h | 4 +-
src/common/nact-gconf.c | 606 +++++++++++++++++-----------------
src/common/nact-gconf.h | 2 +-
src/common/nact-iio-provider.c | 2 +
src/common/nact-object.c | 10 +-
src/common/nact-pivot.c | 394 ++++++++++------------
src/common/nact-pivot.h | 48 ++--
src/common/nact-uti-lists.h | 1 +
src/common/nautilus-actions-config.c | 2 +-
src/plugin/nautilus-actions.c | 267 ++++++++++------
14 files changed, 794 insertions(+), 667 deletions(-)
diff --git a/ChangeLog b/ChangeLog
index 2500c74..593c435 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,19 @@
+2009-06-07 Pierre Wieser <pwieser trychlos org>
+
+ Install the notification system.
+
+ * src/common/nautilus-actions.c:
+ Define new message "notify_nautilus_of_action_changed".
+
+ * src/common/nact-pivot.c:
+ Define new message "notify_pivot_of_action_changed".
+
+ * src/common/nact-pivot.h:
+ Rename and update nactPivotValue structure to NactPivotNotify.
+
+ * src/common/gconf.c:
+ Send message to NactPivot on action changed.
+
2009-06-06 Pierre Wieser <pwieser trychlos org>
Remove NactIIOClient interface.
diff --git a/src/common/nact-action-profile.c b/src/common/nact-action-profile.c
index c4b65ca..0db7238 100644
--- a/src/common/nact-action-profile.c
+++ b/src/common/nact-action-profile.c
@@ -40,6 +40,13 @@
#include "nact-action-profile.h"
#include "nact-uti-lists.h"
+/* private class data
+ */
+struct NactActionProfileClassPrivate {
+};
+
+/* private instance data
+ */
struct NactActionProfilePrivate {
gboolean dispose_has_run;
@@ -62,9 +69,10 @@ struct NactActionProfilePrivate {
GSList *schemes;
};
-struct NactActionProfileClassPrivate {
-};
-
+/* private instance properties
+ * please note that property names must have the same spelling as the
+ * NactIIOProvider parameters
+ */
enum {
PROP_ACTION = 1,
PROP_PROFILE_NAME,
@@ -80,9 +88,6 @@ enum {
PROP_SCHEMES
};
-/* please note that property names must have the same spelling as the
- * NactIIOProvider parameters
- */
#define PROP_ACTION_STR "action"
#define PROP_PROFILE_NAME_STR "name"
#define PROP_LABEL_STR "desc-name"
@@ -489,6 +494,39 @@ do_dump_list( const gchar *thisfn, const gchar *label, GSList *list )
g_string_free( str, TRUE );
}
+/*
+ * Check if the given profile is empty, i.e. all its attributes are
+ * empty.
+ */
+gboolean
+nact_action_profile_is_empty( const NactActionProfile *profile )
+{
+ g_assert( NACT_IS_ACTION_PROFILE( profile ));
+
+ if( profile->private->name && strlen( profile->private->name )){
+ return( FALSE );
+ }
+ if( profile->private->label && strlen( profile->private->label )){
+ return( FALSE );
+ }
+ if( profile->private->path && strlen( profile->private->path )){
+ return( FALSE );
+ }
+ if( profile->private->parameters && strlen( profile->private->parameters )){
+ return( FALSE );
+ }
+ if( !nactuti_is_empty_string_list( profile->private->basenames )){
+ return( FALSE );
+ }
+ if( !nactuti_is_empty_string_list( profile->private->mimetypes )){
+ return( FALSE );
+ }
+ if( !nactuti_is_empty_string_list( profile->private->schemes )){
+ return( FALSE );
+ }
+ return( TRUE );
+}
+
/**
* Returns a pointer to the action for this profile.
*/
diff --git a/src/common/nact-action-profile.h b/src/common/nact-action-profile.h
index eede629..315b921 100644
--- a/src/common/nact-action-profile.h
+++ b/src/common/nact-action-profile.h
@@ -74,6 +74,8 @@ NactActionProfile *nact_action_profile_new( const NactObject *action, const gcha
void nact_action_profile_load( NactObject *profile );
+gboolean nact_action_profile_is_empty( const NactActionProfile *profile );
+
NactObject *nact_action_profile_get_action( const NactActionProfile *profile );
gchar *nact_action_profile_get_name( const NactActionProfile *profile );
gchar *nact_action_profile_get_path( const NactActionProfile *profile );
diff --git a/src/common/nact-action.c b/src/common/nact-action.c
index 61addad..3113ca2 100644
--- a/src/common/nact-action.c
+++ b/src/common/nact-action.c
@@ -38,6 +38,13 @@
#include "nact-action-profile.h"
#include "nact-uti-lists.h"
+/* private class data
+ */
+struct NactActionClassPrivate {
+};
+
+/* private instance data
+ */
struct NactActionPrivate {
gboolean dispose_has_run;
@@ -55,9 +62,10 @@ struct NactActionPrivate {
GSList *profiles;
};
-struct NactActionClassPrivate {
-};
-
+/* private instance properties
+ * please note that property names must have the same spelling as the
+ * NactIIOProvider parameters
+ */
enum {
PROP_UUID = 1,
PROP_VERSION,
@@ -66,9 +74,6 @@ enum {
PROP_ICON
};
-/* please note that property names must have the same spelling as the
- * NactIIOProvider parameters
- */
#define PROP_UUID_STR "uuid"
#define PROP_VERSION_STR "version"
#define PROP_LABEL_STR "label"
@@ -338,7 +343,7 @@ free_profiles( NactAction *action )
*
* Note that the parm may actually be a profile's parm.
*/
-NactAction *
+/*NactAction *
nact_action_create( const gchar *key, const gchar *parm, const NactPivotValue *value )
{
static const gchar *thisfn = "nact_action_create";
@@ -347,17 +352,17 @@ nact_action_create( const gchar *key, const gchar *parm, const NactPivotValue *v
NactAction *action = g_object_new( NACT_ACTION_TYPE, NULL );
nact_action_update( action, parm, value );
return( action );
-}
+}*/
/**
* Update the given parameter of an action.
*/
-void
+/*void
nact_action_update( NactAction *action, const gchar *parm, const NactPivotValue *value )
{
static const gchar *thisfn = "nact_action_update";
g_debug( "%s: action=%p, parm='%s', value=%p", thisfn, action, parm, value );
-}
+}*/
static void
do_dump( const NactObject *action )
@@ -386,6 +391,38 @@ do_dump( const NactObject *action )
}
/**
+ * Check if the given action is empty, i.e. all its attributes are empty.
+ */
+gboolean
+nact_action_is_empty( const NactAction *action )
+{
+ g_assert( NACT_IS_ACTION( action ));
+
+ if( action->private->uuid && strlen( action->private->uuid )){
+ return( FALSE );
+ }
+ if( action->private->version && strlen( action->private->version )){
+ return( FALSE );
+ }
+ if( action->private->label && strlen( action->private->label )){
+ return( FALSE );
+ }
+ if( action->private->tooltip && strlen( action->private->tooltip )){
+ return( FALSE );
+ }
+ if( action->private->icon && strlen( action->private->icon )){
+ return( FALSE );
+ }
+ GSList *ip;
+ for( ip = action->private->profiles ; ip ; ip = ip->next ){
+ if( !nact_action_profile_is_empty( NACT_ACTION_PROFILE( ip->data ))){
+ return( FALSE );
+ }
+ }
+ return( TRUE );
+}
+
+/**
* Return the globally unique identifier (UUID) of the action.
*
* @action: an NactAction object.
diff --git a/src/common/nact-action.h b/src/common/nact-action.h
index 5e017bb..3702e5c 100644
--- a/src/common/nact-action.h
+++ b/src/common/nact-action.h
@@ -71,8 +71,8 @@ GType nact_action_get_type( void );
NactAction *nact_action_new( const gchar *uuid );
-NactAction *nact_action_create( const gchar *key, const gchar *parm, const NactPivotValue *value );
-void nact_action_update( NactAction *action, const gchar *parm, const NactPivotValue *value );
+/*NactAction *nact_action_create( const gchar *key, const gchar *parm, const NactPivotValue *value );
+void nact_action_update( NactAction *action, const gchar *parm, const NactPivotValue *value );*/
gboolean nact_action_is_empty( const NactAction *action );
diff --git a/src/common/nact-gconf.c b/src/common/nact-gconf.c
index 9939eb4..9fb244a 100644
--- a/src/common/nact-gconf.c
+++ b/src/common/nact-gconf.c
@@ -44,54 +44,62 @@
#include "nact-iio-provider.h"
#include "nact-uti-lists.h"
+/* private class data
+ */
+struct NactGConfClassPrivate {
+};
+
+/* private instance data
+ */
struct NactGConfPrivate {
gboolean dispose_has_run;
- GObject *notification_handler;
+
+ /* instance to be notified of an action modification
+ */
+ gpointer notified;
+
GConfClient *gconf;
guint notify_id;
};
-struct NactGConfClassPrivate {
-};
-
+/* private instance properties
+ */
enum {
- PROP_NOTIFICATION_HANDLER = 1
+ PROP_NOTIFIED = 1
};
-#define PROP_NOTIFICATION_HANDLER_STR "notification-handler"
+#define PROP_NOTIFIED_STR "to-be-notified"
static GObjectClass *st_parent_class = NULL;
-static GType register_type( void );
-static void class_init( NactGConfClass *klass );
-static void iio_provider_iface_init( NactIIOProviderInterface *iface );
-static void instance_init( GTypeInstance *instance, gpointer klass );
-static void instance_get_property( GObject *object, guint property_id, GValue *value, GParamSpec *spec );
-static void instance_set_property( GObject *object, guint property_id, const GValue *value, GParamSpec *spec );
-static void instance_dispose( GObject *object );
-static void instance_finalize( GObject *object );
-static guint install_gconf_watch( NactGConf *gconf );
-static void remove_gconf_watch( NactGConf *gconf );
-
-static void action_changed_cb( GConfClient *client, guint cnxn_id, GConfEntry *entry, gpointer user_data );
-
-static void free_keys_values( GSList *keys );
-static GSList *load_keys_values( const NactGConf *gconf, const gchar *path );
-static GSList *load_subdirs( const NactGConf *gconf, const gchar *path );
-static gchar *path_to_key( const gchar *path );
-/*static NactGConfIO *path_to_struct( const gchar *path );*/
-static void set_item_properties( NactObject *object, GSList *properties );
-static NactPivotValue *value_to_pivot( const GConfValue *value );
-
-static void load_action_properties( NactGConf *gconf, NactAction *action );
-static GSList *load_profiles( NactGConf *gconf, NactAction *action );
-static void load_profile_properties( NactGConf *gconf, NactActionProfile *profile );
-static GSList *do_load_actions( NactIIOProvider *provider );
+static GType register_type( void );
+static void class_init( NactGConfClass *klass );
+static void iio_provider_iface_init( NactIIOProviderInterface *iface );
+static void instance_init( GTypeInstance *instance, gpointer klass );
+static void instance_get_property( GObject *object, guint property_id, GValue *value, GParamSpec *spec );
+static void instance_set_property( GObject *object, guint property_id, const GValue *value, GParamSpec *spec );
+static void instance_dispose( GObject *object );
+static void instance_finalize( GObject *object );
+
+static GSList *do_load_actions( NactIIOProvider *provider );
+static void load_action_properties( NactGConf *gconf, NactAction *action );
+static GSList *load_profiles( NactGConf *gconf, NactAction *action );
+static void load_profile_properties( NactGConf *gconf, NactActionProfile *profile );
+static GSList *load_subdirs( const NactGConf *gconf, const gchar *path );
+static GSList *load_keys_values( const NactGConf *gconf, const gchar *path );
+static void free_keys_values( GSList *keys );
+static gchar *path_to_key( const gchar *path );
+static void set_item_properties( NactObject *object, GSList *properties );
+static NactPivotNotify *entry_to_notify( const GConfEntry *entry );
+
+static guint install_gconf_watch( NactGConf *gconf );
+static void remove_gconf_watch( NactGConf *gconf );
+static void action_changed_cb( GConfClient *client, guint cnxn_id, GConfEntry *entry, gpointer user_data );
NactGConf *
nact_gconf_new( const GObject *handler )
{
- return( g_object_new( NACT_GCONF_TYPE, PROP_NOTIFICATION_HANDLER_STR, handler, NULL ));
+ return( g_object_new( NACT_GCONF_TYPE, PROP_NOTIFIED_STR, handler, NULL ));
}
GType
@@ -150,11 +158,11 @@ class_init( NactGConfClass *klass )
GParamSpec *spec;
spec = g_param_spec_pointer(
- PROP_NOTIFICATION_HANDLER_STR,
- PROP_NOTIFICATION_HANDLER_STR,
+ PROP_NOTIFIED_STR,
+ PROP_NOTIFIED_STR,
"A pointer to a GObject which will receive action_changed notifications",
G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE );
- g_object_class_install_property( object_class, PROP_NOTIFICATION_HANDLER, spec );
+ g_object_class_install_property( object_class, PROP_NOTIFIED, spec );
klass->private = g_new0( NactGConfClassPrivate, 1 );
}
@@ -191,8 +199,8 @@ instance_get_property( GObject *object, guint property_id, GValue *value, GParam
NactGConf *self = NACT_GCONF( object );
switch( property_id ){
- case PROP_NOTIFICATION_HANDLER:
- g_value_set_pointer( value, self->private->notification_handler );
+ case PROP_NOTIFIED:
+ g_value_set_pointer( value, self->private->notified );
break;
default:
@@ -208,8 +216,8 @@ instance_set_property( GObject *object, guint property_id, const GValue *value,
NactGConf *self = NACT_GCONF( object );
switch( property_id ){
- case PROP_NOTIFICATION_HANDLER:
- self->private->notification_handler = g_value_get_pointer( value );
+ case PROP_NOTIFIED:
+ self->private->notified = g_value_get_pointer( value );
break;
default:
@@ -250,148 +258,130 @@ instance_finalize( GObject *object )
}
/*
- * note that we need the NactPivot object in action_changed_cb handler
- * but it is initialized as a construction property, and this watch is
- * installed from instance_init, i.e. before properties are set..
- *
- * we so pass NactGConf pointer which is already valid at this time.
+ * NactIIOProviderInterface implementation
+ * load the list of actions and returns them as a GSList
*/
-static guint
-install_gconf_watch( NactGConf *gconf )
+static GSList *
+do_load_actions( NactIIOProvider *provider )
{
- static const gchar *thisfn = "install_gconf_watch";
- GError *error = NULL;
+ static const gchar *thisfn = "nacf_gconf_do_load_actions";
+ g_debug( "%s: provider=%p", thisfn, provider );
- gconf_client_add_dir(
- gconf->private->gconf, NACT_GCONF_CONFIG_PATH, GCONF_CLIENT_PRELOAD_RECURSIVE, &error );
- if( error ){
- g_error( "%s: error=%s", thisfn, error->message );
- g_error_free( error );
- return( 0 );
- }
+ g_assert( NACT_IS_IIO_PROVIDER( provider ));
+ g_assert( NACT_IS_GCONF( provider ));
+ NactGConf *self = NACT_GCONF( provider );
- guint notify_id =
- gconf_client_notify_add(
- gconf->private->gconf,
- NACT_GCONF_CONFIG_PATH,
- ( GConfClientNotifyFunc ) action_changed_cb,
- gconf,
- NULL,
- &error
- );
- if( error ){
- g_error( "%s: error=%s", thisfn, error->message );
- g_error_free( error );
- return( 0 );
- }
+ GSList *items = NULL;
+ GSList *ip;
+ GSList *listpath = load_subdirs( self, NACT_GCONF_CONFIG_PATH );
- return( notify_id );
-}
+ for( ip = listpath ; ip ; ip = ip->next ){
-static void
-remove_gconf_watch( NactGConf *gconf )
-{
- static const gchar *thisfn = "remove_gconf_watch";
- GError *error = NULL;
+ gchar *key = path_to_key(( const gchar * ) ip->data );
- if( gconf->private->notify_id ){
- gconf_client_notify_remove( gconf->private->gconf, gconf->private->notify_id );
- }
+ NactAction *action = nact_action_new( key );
+ load_action_properties( self, action );
+ nact_action_set_profiles( action, load_profiles( self, action ));
- gconf_client_remove_dir( gconf->private->gconf, NACT_GCONF_CONFIG_PATH, &error );
- if( error ){
- g_error( "%s: error=%s", thisfn, error->message );
- g_error_free( error );
+#ifdef NACT_MAINTAINER_MODE
+ nact_object_dump( NACT_OBJECT( action ));
+#endif
+
+ items = g_slist_prepend( items, action );
+ g_free( key );
}
+
+ nactuti_free_string_list( listpath );
+
+ return( items );
}
/*
- * this callback is triggered each time a value is changed under our
- * actions directory
- *
- * if the modification is made from nautilus-actions-config ui, then
- * the callback is triggered several times (one time for each rewritten
- * property) as action/profile are edited as blocs of data ; in this
- * case, the ui takes care (aso of 1.10) of also writing at last a
- * particular key of the form xxx:yyyyyyyy-yyyy-yyyy-..., where :
- * xxx is a sequential number (inside of the ui session)
- * yyyyyyyy-yyyy-yyyy-... is the uuid of the involved action
- *
- * this is so a sort of hack which simplifies a lot the notification
- * system (take the new action, replace it in the current global list)
- * but doesn't work if the modification is made from outside of the ui
- *
- * if the modification is made elsewhere (an action is imported as a
- * xml file in gconf, of gconf is directly edited), we'd have to rely
- * on the standard watch mechanism
+ * load and set the properties of the specified action
*/
static void
-action_changed_cb( GConfClient *client, guint cnxn_id, GConfEntry *entry, gpointer user_data )
+load_action_properties( NactGConf *gconf, NactAction *action )
{
- /*static const gchar *thisfn = "action_changed_cb";
- g_debug( "%s: client=%p, cnxnid=%u, entry=%p, user_data=%p", thisfn, client, cnxn_id, entry, user_data );*/
+ /*static const gchar *thisfn = "nacf_gconf_load_action_properties";
+ g_debug( "%s: gconf=%p, action=%p", thisfn, gconf, action );*/
- g_assert( NACT_IS_GCONF( user_data ));
- NactGConf *gconf = NACT_GCONF( user_data );
+ g_assert( NACT_IS_GCONF( gconf ));
+ g_assert( NACT_IS_ACTION( action ));
- const gchar *path = gconf_entry_get_key( entry );
- const gchar *subpath = path + strlen( NACT_GCONF_CONFIG_PATH ) + 1;
- gchar **split = g_strsplit( subpath, "/", 2 );
- gchar *key = g_strdup( split[0] );
- gchar *parm = g_strdup( split[1] );
- g_strfreev( split );
+ gchar *uuid = nact_action_get_uuid( action );
+ gchar *path = g_strdup_printf( "%s/%s", NACT_GCONF_CONFIG_PATH, uuid );
- NactPivotValue *value = value_to_pivot( gconf_entry_get_value( entry ));
+ GSList *properties = load_keys_values( gconf, path );
- nact_pivot_on_action_changed( NACT_PIVOT( gconf->private->notification_handler ), key, parm, value );
+ set_item_properties( NACT_OBJECT( action ), properties );
- g_free( key );
- nact_pivot_free_pivot_value( value );
+ free_keys_values( properties );
+ g_free( uuid );
+ g_free( path );
}
-static void
-free_keys_values( GSList *list )
+/*
+ * load the list of profiles for an action and returns them as a GSList
+ */
+static GSList *
+load_profiles( NactGConf *gconf, NactAction *action )
{
- GSList *item;
- for( item = list ; item != NULL ; item = item->next ){
- GConfEntry *entry = ( GConfEntry * ) item->data;
- gconf_entry_unref( entry );
+ /*static const gchar *thisfn = "nacf_gconf_load_profiles";
+ g_debug( "%s: gconf=%p, action=%p", thisfn, gconf, action );*/
+
+ g_assert( NACT_IS_GCONF( gconf ));
+ g_assert( NACT_IS_ACTION( action ));
+
+ gchar *uuid = nact_action_get_uuid( action );
+ gchar *path = g_strdup_printf( "%s/%s", NACT_GCONF_CONFIG_PATH, uuid );
+
+ GSList *ip;
+ GSList *items = NULL;
+ GSList *listpath = load_subdirs( gconf, path );
+
+ for( ip = listpath ; ip ; ip = ip->next ){
+
+ gchar *key = path_to_key(( const gchar * ) ip->data );
+ NactActionProfile *profile = nact_action_profile_new( NACT_OBJECT( action ), key );
+ load_profile_properties( gconf, profile );
+
+ items = g_slist_prepend( items, profile );
}
- g_slist_free( list );
+
+ nactuti_free_string_list( listpath );
+ g_free( path );
+ g_free( uuid );
+
+ return( items );
}
/*
- * load all the key=value pairs of this key (specified as a full path)
- * The list is not recursive, it contains only the immediate children of
- * path.
- * To free the returned hash table, call free_key_values
+ * load and set the properties of the specified profile
*/
-static GSList *
-load_keys_values( const NactGConf *gconf, const gchar *path )
+static void
+load_profile_properties( NactGConf *gconf, NactActionProfile *profile )
{
- static const gchar *thisfn = "nact_gconf_load_keys_values";
+ /*static const gchar *thisfn = "nacf_gconf_load_profile_properties";
+ g_debug( "%s: gconf=%p, profile=%p", thisfn, gconf, profile );*/
- GError *error = NULL;
- GSList *list_path = gconf_client_all_entries( gconf->private->gconf, path, &error );
- if( error ){
- g_error( "%s: path=%s, error=%s", thisfn, path, error->message );
- g_error_free( error );
- return(( GSList * ) NULL );
- }
+ g_assert( NACT_IS_GCONF( gconf ));
+ g_assert( NACT_IS_ACTION_PROFILE( profile ));
- GSList *list_keys = NULL;
- GSList *item;
- for( item = list_path ; item != NULL ; item = item->next ){
- GConfEntry *entry = ( GConfEntry * ) item->data;
- gchar *key = path_to_key( gconf_entry_get_key( entry ));
- GConfEntry *entry_new = gconf_entry_new( key, gconf_entry_get_value( entry ));
- g_free( key );
- list_keys = g_slist_prepend( list_keys, entry_new );
- }
+ NactAction *action =
+ NACT_ACTION( nact_action_profile_get_action( NACT_ACTION_PROFILE( profile )));
+ g_assert( NACT_IS_ACTION( action ));
- free_keys_values( list_path );
+ gchar *uuid = nact_action_get_uuid( action );
+ gchar *path = g_strdup_printf(
+ "%s/%s/%s", NACT_GCONF_CONFIG_PATH, uuid, nact_action_profile_get_name( profile ));
- return( list_keys );
+ GSList *properties = load_keys_values( gconf, path );
+
+ set_item_properties( NACT_OBJECT( profile ), properties );
+
+ free_keys_values( properties );
+ g_free( path );
+ g_free( uuid );
}
/*
@@ -415,6 +405,39 @@ load_subdirs( const NactGConf *gconf, const gchar *path )
}
/*
+ * load all the key=value pairs of this key (specified as a full path)
+ * The list is not recursive, it contains only the immediate children of
+ * path.
+ * To free the returned list, call free_key_values
+ */
+static GSList *
+load_keys_values( const NactGConf *gconf, const gchar *path )
+{
+ static const gchar *thisfn = "nact_gconf_load_keys_values";
+
+ GError *error = NULL;
+ GSList *list_path = gconf_client_all_entries( gconf->private->gconf, path, &error );
+ if( error ){
+ g_error( "%s: path=%s, error=%s", thisfn, path, error->message );
+ g_error_free( error );
+ return(( GSList * ) NULL );
+ }
+
+ return( list_path );
+}
+
+static void
+free_keys_values( GSList *list )
+{
+ GSList *item;
+ for( item = list ; item != NULL ; item = item->next ){
+ GConfEntry *entry = ( GConfEntry * ) item->data;
+ gconf_entry_unref( entry );
+ }
+ g_slist_free( list );
+}
+
+/*
* extract the key part (the last part) of a full path
* returns a newly allocated string which must be g_freed by the caller
*/
@@ -429,20 +452,9 @@ path_to_key( const gchar *path )
}
/*
- * allocate a new NactGConfIO structure
- * to be freed via nact_gconf_dispose
- */
-/*static NactGConfIO *
-path_to_struct( const gchar *path )
-{
- NactGConfIO *io = g_new0( NactGConfIO, 1 );
- io->key = path_to_key( path );
- io->path = g_strdup( path );
- return( io );
-}*/
-
-/*
* set the item properties into the object
+ * as we make use of NactPivotNotify structure, 'uuid' field must be
+ * read as the GConf key, i.e. the parameter name
*/
static void
set_item_properties( NactObject *object, GSList *properties )
@@ -453,199 +465,175 @@ set_item_properties( NactObject *object, GSList *properties )
for( item = properties ; item != NULL ; item = item->next ){
GConfEntry *entry = ( GConfEntry * ) item->data;
- const char *key = gconf_entry_get_key( entry );
- NactPivotValue *value = value_to_pivot( gconf_entry_get_value( entry ));
+ NactPivotNotify *npn = entry_to_notify( entry );
+ if( npn ){
- if( value ){
- switch( value->type ){
+ gchar **split = g_strsplit( npn->parm, "/", -1 );
+ gchar *parm = g_strdup( split[ g_strv_length( split ) -1 ] );
+ g_strfreev( split );
+
+ switch( npn->type ){
case NACT_PIVOT_STR:
case NACT_PIVOT_BOOL:
case NACT_PIVOT_STRLIST:
- g_object_set( G_OBJECT( object ), key, value->data, NULL );
+ g_object_set( G_OBJECT( object ), parm, npn->data, NULL );
break;
default:
g_assert_not_reached();
break;
}
- nact_pivot_free_pivot_value( value );
+ nact_pivot_free_notify( npn );
+ g_free( parm );
}
}
}
/*
- * convert a GConfValue to our internal data type
+ * convert a GConfEntry to a structure suitable to notify NactPivot
*/
-static NactPivotValue *
-value_to_pivot( const GConfValue *value )
+static NactPivotNotify *
+entry_to_notify( const GConfEntry *entry )
{
- NactPivotValue *pivot_value = NULL;
+ /*static const gchar *thisfn = "nact_gconf_entry_to_notify";*/
GSList *listvalues, *iv, *strings;
- if( !value ){
- return(( NactPivotValue * ) NULL );
- }
-
- switch( value->type ){
+ g_assert( entry );
- case GCONF_VALUE_STRING:
- pivot_value = g_new0( NactPivotValue, 1 );
- pivot_value->type = NACT_PIVOT_STR;
- pivot_value->data = ( gpointer ) g_strdup( gconf_value_get_string( value ));
- break;
-
- case GCONF_VALUE_BOOL:
- pivot_value = g_new0( NactPivotValue, 1 );
- pivot_value->type = NACT_PIVOT_BOOL;
- pivot_value->data = ( gpointer ) gconf_value_get_bool( value );
- break;
-
- case GCONF_VALUE_LIST:
- listvalues = gconf_value_get_list( value );
- strings = NULL;
- for( iv = listvalues ; iv != NULL ; iv = iv->next ){
- strings = g_slist_prepend( strings,
- ( gpointer ) gconf_value_get_string(( GConfValue * ) iv->data ));
- }
+ const gchar *path = gconf_entry_get_key( entry );
+ g_assert( path );
- pivot_value = g_new0( NactPivotValue, 1 );
- pivot_value->type = NACT_PIVOT_STRLIST;
- pivot_value->data = ( gpointer ) nactuti_duplicate_string_list( strings );
- /*nactuti_free_string_list( strings );*/
- break;
+ NactPivotNotify *npn = g_new0( NactPivotNotify, 1 );
- default:
- g_assert_not_reached();
- break;
+ const gchar *subpath = path + strlen( NACT_GCONF_CONFIG_PATH ) + 1;
+ gchar **split = g_strsplit( subpath, "/", 2 );
+ /*g_debug( "%s: [0]=%s, [1]=%s", thisfn, split[0], split[1] );*/
+ npn->uuid = g_strdup( split[0] );
+ if( split[1] ){
+ npn->parm = g_strdup( split[1] );
}
+ g_strfreev( split );
- return( pivot_value );
-}
-
-/*
- * load and set the properties of the specified action
- */
-static void
-load_action_properties( NactGConf *gconf, NactAction *action )
-{
- /*static const gchar *thisfn = "nacf_gconf_load_action_properties";
- g_debug( "%s: gconf=%p, action=%p", thisfn, gconf, action );*/
-
- g_assert( NACT_IS_GCONF( gconf ));
- g_assert( NACT_IS_ACTION( action ));
-
- gchar *uuid = nact_action_get_uuid( action );
- gchar *path = g_strdup_printf( "%s/%s", NACT_GCONF_CONFIG_PATH, uuid );
-
- GSList *properties = load_keys_values( gconf, path );
-
- set_item_properties( NACT_OBJECT( action ), properties );
-
- free_keys_values( properties );
- g_free( uuid );
- g_free( path );
+ const GConfValue *value = gconf_entry_get_value( entry );
+ if( value ){
+ switch( value->type ){
+
+ case GCONF_VALUE_STRING:
+ npn->type = NACT_PIVOT_STR;
+ npn->data = ( gpointer ) g_strdup( gconf_value_get_string( value ));
+ break;
+
+ case GCONF_VALUE_BOOL:
+ npn->type = NACT_PIVOT_BOOL;
+ npn->data = ( gpointer ) gconf_value_get_bool( value );
+ break;
+
+ case GCONF_VALUE_LIST:
+ listvalues = gconf_value_get_list( value );
+ strings = NULL;
+ for( iv = listvalues ; iv != NULL ; iv = iv->next ){
+ strings = g_slist_prepend( strings,
+ ( gpointer ) gconf_value_get_string(( GConfValue * ) iv->data ));
+ }
+
+ npn->type = NACT_PIVOT_STRLIST;
+ npn->data = ( gpointer ) nactuti_duplicate_string_list( strings );
+ /*nactuti_free_string_list( strings );*/
+ break;
+
+ default:
+ g_assert_not_reached();
+ break;
+ }
+ }
+ return( npn );
}
/*
- * load the list of profiles for an action and returns them as a GSList
+ * note that we need the NactPivot object in action_changed_cb handler
+ * but it is initialized as a construction property, and this watch is
+ * installed from instance_init, i.e. before properties are set..
+ * we so pass NactGConf pointer which is already valid at this time.
*/
-static GSList *
-load_profiles( NactGConf *gconf, NactAction *action )
+static guint
+install_gconf_watch( NactGConf *gconf )
{
- /*static const gchar *thisfn = "nacf_gconf_load_profiles";
- g_debug( "%s: gconf=%p, action=%p", thisfn, gconf, action );*/
-
- g_assert( NACT_IS_GCONF( gconf ));
- g_assert( NACT_IS_ACTION( action ));
-
- gchar *uuid = nact_action_get_uuid( action );
- gchar *path = g_strdup_printf( "%s/%s", NACT_GCONF_CONFIG_PATH, uuid );
-
- GSList *ip;
- GSList *items = NULL;
- GSList *listpath = load_subdirs( gconf, path );
-
- for( ip = listpath ; ip ; ip = ip->next ){
-
- gchar *key = path_to_key(( const gchar * ) ip->data );
- NactActionProfile *profile = nact_action_profile_new( NACT_OBJECT( action ), key );
- load_profile_properties( gconf, profile );
+ static const gchar *thisfn = "nact_gconf_install_gconf_watch";
+ GError *error = NULL;
- items = g_slist_prepend( items, profile );
+ gconf_client_add_dir(
+ gconf->private->gconf, NACT_GCONF_CONFIG_PATH, GCONF_CLIENT_PRELOAD_RECURSIVE, &error );
+ if( error ){
+ g_error( "%s: error=%s", thisfn, error->message );
+ g_error_free( error );
+ return( 0 );
}
- nactuti_free_string_list( listpath );
- g_free( path );
- g_free( uuid );
+ guint notify_id =
+ gconf_client_notify_add(
+ gconf->private->gconf,
+ NACT_GCONF_CONFIG_PATH,
+ ( GConfClientNotifyFunc ) action_changed_cb,
+ gconf,
+ NULL,
+ &error
+ );
+ if( error ){
+ g_error( "%s: error=%s", thisfn, error->message );
+ g_error_free( error );
+ return( 0 );
+ }
- return( items );
+ return( notify_id );
}
-/*
- * load and set the properties of the specified profile
- */
static void
-load_profile_properties( NactGConf *gconf, NactActionProfile *profile )
+remove_gconf_watch( NactGConf *gconf )
{
- /*static const gchar *thisfn = "nacf_gconf_load_profile_properties";
- g_debug( "%s: gconf=%p, profile=%p", thisfn, gconf, profile );*/
-
- g_assert( NACT_IS_GCONF( gconf ));
- g_assert( NACT_IS_ACTION_PROFILE( profile ));
-
- NactAction *action =
- NACT_ACTION( nact_action_profile_get_action( NACT_ACTION_PROFILE( profile )));
- g_assert( NACT_IS_ACTION( action ));
-
- gchar *uuid = nact_action_get_uuid( action );
- gchar *path = g_strdup_printf(
- "%s/%s/%s", NACT_GCONF_CONFIG_PATH, uuid, nact_action_profile_get_name( profile ));
-
- GSList *properties = load_keys_values( gconf, path );
+ static const gchar *thisfn = "nact_gconf_remove_gconf_watch";
+ GError *error = NULL;
- set_item_properties( NACT_OBJECT( profile ), properties );
+ if( gconf->private->notify_id ){
+ gconf_client_notify_remove( gconf->private->gconf, gconf->private->notify_id );
+ }
- free_keys_values( properties );
- g_free( path );
- g_free( uuid );
+ gconf_client_remove_dir( gconf->private->gconf, NACT_GCONF_CONFIG_PATH, &error );
+ if( error ){
+ g_error( "%s: error=%s", thisfn, error->message );
+ g_error_free( error );
+ }
}
/*
- * NactIIOProviderInterface implementation
- * load the list of actions and returns them as a GSList
+ * this callback is triggered each time a value is changed under our
+ * actions directory
+ *
+ * if the modification is made from nautilus-actions-config ui, then
+ * the callback is triggered several times (one time for each rewritten
+ * property) as action/profile are edited as blocs of data ; in this
+ * case, the ui takes care (aso of 1.10) of also writing at last a
+ * particular key of the form xxx:yyyyyyyy-yyyy-yyyy-..., where :
+ * xxx is a sequential number (inside of the ui session)
+ * yyyyyyyy-yyyy-yyyy-... is the uuid of the involved action
+ *
+ * this is so a sort of hack which simplifies a lot the notification
+ * system (take the new action, replace it in the current global list)
+ * but doesn't work if the modification is made from outside of the ui
+ *
+ * if the modification is made elsewhere (an action is imported as a
+ * xml file in gconf, or gconf is directly edited), we'd have to rely
+ * only on the standard watch mechanism
*/
-static GSList *
-do_load_actions( NactIIOProvider *provider )
+static void
+action_changed_cb( GConfClient *client, guint cnxn_id, GConfEntry *entry, gpointer user_data )
{
- static const gchar *thisfn = "nacf_gconf_do_load_actions";
- g_debug( "%s: provider=%p", thisfn, provider );
-
- g_assert( NACT_IS_IIO_PROVIDER( provider ));
- g_assert( NACT_IS_GCONF( provider ));
- NactGConf *self = NACT_GCONF( provider );
-
- GSList *items = NULL;
- GSList *ip;
- GSList *listpath = load_subdirs( self, NACT_GCONF_CONFIG_PATH );
-
- for( ip = listpath ; ip ; ip = ip->next ){
-
- gchar *key = path_to_key(( const gchar * ) ip->data );
-
- NactAction *action = nact_action_new( key );
- load_action_properties( self, action );
- nact_action_set_profiles( action, load_profiles( self, action ));
-
-#ifdef NACT_MAINTAINER_MODE
- nact_object_dump( NACT_OBJECT( action ));
-#endif
-
- items = g_slist_prepend( items, action );
- g_free( key );
- }
+ /*static const gchar *thisfn = "action_changed_cb";
+ g_debug( "%s: client=%p, cnxnid=%u, entry=%p, user_data=%p", thisfn, client, cnxn_id, entry, user_data );*/
- nactuti_free_string_list( listpath );
+ g_assert( NACT_IS_GCONF( user_data ));
+ NactGConf *gconf = NACT_GCONF( user_data );
- return( items );
+ NactPivotNotify *npn = entry_to_notify( entry );
+ g_signal_emit_by_name( gconf->private->notified, "notify_pivot_of_action_changed", npn );
}
diff --git a/src/common/nact-gconf.h b/src/common/nact-gconf.h
index b04d821..4062e2a 100644
--- a/src/common/nact-gconf.h
+++ b/src/common/nact-gconf.h
@@ -66,7 +66,7 @@ typedef struct {
GType nact_gconf_get_type( void );
-NactGConf *nact_gconf_new( const GObject *notification_handler );
+NactGConf *nact_gconf_new( const GObject *notified );
G_END_DECLS
diff --git a/src/common/nact-iio-provider.c b/src/common/nact-iio-provider.c
index 2c5bfe8..47ae424 100644
--- a/src/common/nact-iio-provider.c
+++ b/src/common/nact-iio-provider.c
@@ -35,6 +35,8 @@
#include "nact-iio-provider.h"
#include "nact-pivot.h"
+/* private interface data
+ */
struct NactIIOProviderInterfacePrivate {
};
diff --git a/src/common/nact-object.c b/src/common/nact-object.c
index b13b6d4..57c0d3f 100644
--- a/src/common/nact-object.c
+++ b/src/common/nact-object.c
@@ -35,11 +35,15 @@
#include "nact-object.h"
#include "nact-uti-lists.h"
-struct NactObjectPrivate {
- gboolean dispose_has_run;
+/* private class data
+ */
+struct NactObjectClassPrivate {
};
-struct NactObjectClassPrivate {
+/* private instance data
+ */
+struct NactObjectPrivate {
+ gboolean dispose_has_run;
};
static GObjectClass *st_parent_class = NULL;
diff --git a/src/common/nact-pivot.c b/src/common/nact-pivot.c
index 25803ce..d007296 100644
--- a/src/common/nact-pivot.c
+++ b/src/common/nact-pivot.c
@@ -32,25 +32,28 @@
#include <config.h>
#endif
+#include <string.h>
+
#include "nact-action.h"
#include "nact-gconf.h"
#include "nact-pivot.h"
#include "nact-iio-provider.h"
#include "nact-uti-lists.h"
-/* action_changed_cb send events which are stacked in a static GSList
- * we so hope to optimize updating the global list of actions
+/* private class data
*/
-typedef struct {
- gchar *uuid;
- gchar *parm;
- NactPivotValue *value;
-}
- stackItem;
+struct NactPivotClassPrivate {
+};
+/* private instance data
+ */
struct NactPivotPrivate {
gboolean dispose_has_run;
+ /* instance to be notified of an action modification
+ */
+ gpointer notified;
+
/* list of interface providers
* needs to be in the instance rather than in the class to be able
* to pass NactPivot object to the IO provider, so that the later
@@ -63,35 +66,48 @@ struct NactPivotPrivate {
GSList *actions;
};
-struct NactPivotClassPrivate {
+/* private instance properties
+ */
+enum {
+ PROP_NOTIFIED = 1
};
+#define PROP_NOTIFIED_STR "to-be-notified"
+
+/* signal definition
+ */
+enum {
+ ACTION_CHANGED,
+ LAST_SIGNAL
+};
+
+#define SIGNAL_ACTION_CHANGED_NAME "notify_pivot_of_action_changed"
+
static GObjectClass *st_parent_class = NULL;
-static GSList *st_stack_events = NULL;
+static gint st_signals[ LAST_SIGNAL ] = { 0 };
static GTimeVal st_last_event;
static guint st_event_source_id = 0;
+static gint st_timeout_usec = 500000;
static GType register_type( void );
static void class_init( NactPivotClass *klass );
static void instance_init( GTypeInstance *instance, gpointer klass );
static GSList *register_interface_providers( const NactPivot *pivot );
+static void instance_get_property( GObject *object, guint property_id, GValue *value, GParamSpec *spec );
+static void instance_set_property( GObject *object, guint property_id, const GValue *value, GParamSpec *spec );
static void instance_dispose( GObject *object );
static void instance_finalize( GObject *object );
-static void check_for_remove_action( NactPivot *pivot, NactAction *action );
-static gint cmp_events( gconstpointer a, gconstpointer b );
-static void free_stack_events( GSList *stack );
+static void action_changed_handler( NactPivot *pivot, gpointer user_data );
+static void update_action( GSList *actions, NactPivotNotify *notify );
+static NactAction *get_action( GSList *list, const gchar *uuid );
static gboolean on_action_changed_timeout( gpointer user_data );
-static stackItem *stack_item_new( const gchar *uuid, const gchar *parm, const NactPivotValue *value );
-static void stack_item_free( stackItem *item );
static gulong time_val_diff( const GTimeVal *recent, const GTimeVal *old );
-static void update_actions( NactPivot *pivot, GSList *stack );
-static NactAction *get_action( GSList *list, const gchar *uuid );
NactPivot *
-nact_pivot_new( void )
+nact_pivot_new( const GObject *target )
{
- return( g_object_new( NACT_PIVOT_TYPE, NULL ));
+ return( g_object_new( NACT_PIVOT_TYPE, PROP_NOTIFIED_STR, target, NULL ));
}
GType
@@ -135,8 +151,33 @@ class_init( NactPivotClass *klass )
GObjectClass *object_class = G_OBJECT_CLASS( klass );
object_class->dispose = instance_dispose;
object_class->finalize = instance_finalize;
+ object_class->set_property = instance_set_property;
+ object_class->get_property = instance_get_property;
+
+ GParamSpec *spec;
+ spec = g_param_spec_pointer(
+ PROP_NOTIFIED_STR,
+ PROP_NOTIFIED_STR,
+ "A pointer to a GObject which will receive action_changed notifications",
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE );
+ g_object_class_install_property( object_class, PROP_NOTIFIED, spec );
klass->private = g_new0( NactPivotClassPrivate, 1 );
+
+ /* see nautilus_actions_class_init for why we use this function
+ */
+ st_signals[ ACTION_CHANGED ] = g_signal_new_class_handler(
+ SIGNAL_ACTION_CHANGED_NAME,
+ G_TYPE_FROM_CLASS( klass ),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
+ ( GCallback ) action_changed_handler,
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_POINTER
+ );
}
static void
@@ -154,6 +195,40 @@ instance_init( GTypeInstance *instance, gpointer klass )
self->private->actions = nact_iio_provider_load_actions( G_OBJECT( self ));
}
+static void
+instance_get_property( GObject *object, guint property_id, GValue *value, GParamSpec *spec )
+{
+ g_assert( NACT_IS_PIVOT( object ));
+ NactPivot *self = NACT_PIVOT( object );
+
+ switch( property_id ){
+ case PROP_NOTIFIED:
+ g_value_set_pointer( value, self->private->notified );
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID( object, property_id, spec );
+ break;
+ }
+}
+
+static void
+instance_set_property( GObject *object, guint property_id, const GValue *value, GParamSpec *spec )
+{
+ g_assert( NACT_IS_PIVOT( object ));
+ NactPivot *self = NACT_PIVOT( object );
+
+ switch( property_id ){
+ case PROP_NOTIFIED:
+ self->private->notified = g_value_get_pointer( value );
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID( object, property_id, spec );
+ break;
+ }
+}
+
static GSList *
register_interface_providers( const NactPivot *pivot )
{
@@ -218,6 +293,13 @@ instance_finalize( GObject *object )
/**
* Returns the list of providers of the required interface.
+ *
+ * This function is called by interfaces API in order to find the
+ * list of providers registered for this given interface.
+ *
+ * @pivot: this instance.
+ *
+ * @type: the type of searched interface.
*/
GSList *
nact_pivot_get_providers( const NactPivot *pivot, GType type )
@@ -238,136 +320,71 @@ nact_pivot_get_providers( const NactPivot *pivot, GType type )
return( list );
}
-/**
- * This function should be called when a storage subsystem detects that
- * a stored action has changed.
- * As a Nautilus extension, NactPivot will take care of updating menu
- * characteristics accordingly.
- *
- * @pivot: the NactPivot object.
- *
- * @uuid: identifiant of the action.
- *
- * @parm: the parameter path (e.g. "profile-main/path")
- *
- * @value: the new value as a NactPivotValue structure ; do not free it
- * here as it is the responsability of the allocater subsystem.
- *
- * Depending of the sort of update which occurs, we may receive many
- * notifications for the same action. We so stack the notifications and
- * start a one sec. timeout before updating the whole stack.
- */
-void
-nact_pivot_on_action_changed( NactPivot *pivot, const gchar *uuid, const gchar *parm, NactPivotValue *value )
+static void
+action_changed_handler( NactPivot *self, gpointer user_data )
{
- static const gchar *thisfn = "nact_pivot_on_action_changed";
- g_debug( "%s: pivot=%p, uuid='%s', parm='%s', value=%p", thisfn, pivot, uuid, parm, value );
+ /*static const gchar *thisfn = "nact_pivot_action_changed_handler";
+ g_debug( "%s: self=%p, data=%p", thisfn, self, user_data );*/
+
+ g_assert( NACT_IS_PIVOT( self ));
+ g_assert( user_data );
+ if( self->private->dispose_has_run ){
+ return;
+ }
- stackItem *item = ( stackItem * ) stack_item_new( uuid, parm, value );
- st_stack_events = g_slist_prepend( st_stack_events, item );
+ /* apply the change to the list of actions */
+ update_action( self->private->actions, ( NactPivotNotify * ) user_data );
+ /* set a timeout to notify nautilus at the end of the serie */
g_get_current_time( &st_last_event );
-
if( !st_event_source_id ){
- st_event_source_id = g_timeout_add_seconds( 1, ( GSourceFunc ) on_action_changed_timeout, pivot );
+ st_event_source_id = g_timeout_add_seconds( 1, ( GSourceFunc ) on_action_changed_timeout, self );
}
}
-/**
- * Duplicate a NactPivotValue structure and its content.
- */
-NactPivotValue *
-nact_pivot_duplicate_pivot_value( const NactPivotValue *value )
+static void
+update_action( GSList *actions, NactPivotNotify *notify )
{
- if( !value ){
- return(( NactPivotValue * ) NULL );
- }
-
- NactPivotValue *newvalue = g_new0( NactPivotValue, 1 );
-
- switch( value->type ){
-
- case NACT_PIVOT_STR:
- newvalue->data = g_strdup(( gchar * ) value->data );
- break;
+ g_assert( notify );
+ if( notify->uuid && strlen( notify->uuid )){
- case NACT_PIVOT_BOOL:
- newvalue->data = value->data;
- break;
-
- case NACT_PIVOT_STRLIST:
- newvalue->data = nactuti_duplicate_string_list(( GSList * ) value->data );
- break;
+ NactAction *action = get_action( actions, notify->uuid );
+ g_debug( "nact_pivot_update_action: uuid='%s', parm='%s', action=%p", notify->uuid, notify->parm, action );
+ if( !action ){
+ /* this is a creation */
- default:
- g_assert_not_reached();
- break;
+ } else {
+ /* this is an update or a deletion */
+ }
}
- return( newvalue );
+ nact_pivot_free_notify( notify );
}
-/**
- * Free a NactPivotValue structure and its content.
- */
-void
-nact_pivot_free_pivot_value( NactPivotValue *value )
+static NactAction *
+get_action( GSList *list, const gchar *uuid )
{
- if( value ){
- switch( value->type ){
-
- case NACT_PIVOT_STR:
- g_free(( gchar * ) value->data );
- break;
-
- case NACT_PIVOT_BOOL:
- break;
-
- case NACT_PIVOT_STRLIST:
- nactuti_free_string_list(( GSList * ) value->data );
- break;
-
- default:
- g_assert_not_reached();
- break;
+ NactAction *found = NULL;
+ GSList *ia;
+ for( ia = list ; ia && !found ; ia = ia->next ){
+ NactAction *action = ( NactAction * ) ia->data;
+ gchar *id = nact_action_get_uuid( action );
+ if( !g_strcmp0( id, uuid )){
+ found = action;
}
- g_free( value );
- }
-}
-
-static void
-check_for_remove_action( NactPivot *pivot, NactAction *action )
-{
- static const gchar *thisfn ="check_for_remove_action";
-
- g_assert( NACT_IS_PIVOT( pivot ));
- g_assert( NACT_IS_ACTION( action ));
-
- if( nact_action_is_empty( action )){
- g_debug( "%s: removing action %p", thisfn, action );
- pivot->private->actions = g_slist_remove( pivot->private->actions, action );
+ g_free( id );
}
+ return( found );
}
-/*
- * comparaison function between two stack items
+/**
+ * Returns the searched NactAction, or NULL.
*/
-static gint
-cmp_events( gconstpointer a, gconstpointer b )
-{
- stackItem *sa = ( stackItem * ) a;
- stackItem *sb = ( stackItem * ) b;
- return( g_strcmp0( sa->uuid, sb->uuid ));
-}
-
-static void
-free_stack_events( GSList *stack )
+GObject *
+nact_pivot_get_action( NactPivot *pivot, const gchar *uuid )
{
- GSList *is;
- for( is = stack ; is ; is = is->next ){
- stack_item_free(( stackItem * ) is->data );
- }
- g_slist_free( stack );
+ g_assert( NACT_IS_PIVOT( pivot ));
+ return( G_OBJECT( get_action( pivot->private->actions, uuid )));
}
/*
@@ -376,47 +393,29 @@ free_stack_events( GSList *stack )
* second old
*
* there is no race condition here as we are not multithreaded
+ * or .. is there ?
*/
static gboolean
on_action_changed_timeout( gpointer user_data )
{
- static const gchar *thisfn = "on_action_changed_timeout";
+ /*static const gchar *thisfn = "nact_pivot_on_action_changed_timeout";
+ g_debug( "%s: pivot=%p", thisfn, user_data );*/
+
GTimeVal now;
g_assert( NACT_IS_PIVOT( user_data ));
+ NactPivot *pivot = NACT_PIVOT( user_data );
g_get_current_time( &now );
gulong diff = time_val_diff( &now, &st_last_event );
- if( diff < 500000 ){
+ if( diff < st_timeout_usec ){
return( TRUE );
}
- g_debug( "%s: treating stack with %d events", thisfn, g_slist_length( st_stack_events ));
- update_actions( NACT_PIVOT( user_data ), st_stack_events );
-
+ g_signal_emit_by_name( G_OBJECT( pivot->private->notified ), "notify_nautilus_of_action_changed" );
st_event_source_id = 0;
- free_stack_events( st_stack_events );
- st_stack_events = NULL;
- return( FALSE );
-}
-static stackItem *
-stack_item_new( const gchar *uuid, const gchar *parm, const NactPivotValue *value )
-{
- stackItem *item = g_new0( stackItem, 1 );
- item->uuid = g_strdup( uuid );
- item->parm = g_strdup( parm );
- item->value = nact_pivot_duplicate_pivot_value( value );
- return( item );
-}
-
-static void
-stack_item_free( stackItem *item )
-{
- g_free( item->uuid );
- g_free( item->parm );
- nact_pivot_free_pivot_value( item->value );
- g_free( item );
+ return( FALSE );
}
/*
@@ -430,70 +429,35 @@ time_val_diff( const GTimeVal *recent, const GTimeVal *old )
return( microsec );
}
-/*
- * iterate through the list of events, sorted by action id
- * on new action, add it to the list, creating the object
- * when all events have been treated, check to see if the action was
- * actually removed (all fields, including key, are blank or null)
- *
- * remove = key + parm=null and value=null
+/**
+ * Free a NactPivotValue structure and its content.
*/
-static void
-update_actions( NactPivot *pivot, GSList *stack )
-{
- GSList *it;
- NactAction *action = NULL;
- gchar *previd = NULL;
-
- GSList *sorted = g_slist_sort( stack, cmp_events );
-
- for( it = sorted ; it ; it = it->next ){
- stackItem *item = ( stackItem * ) it->data;
-
- if( action && g_strcmp0( previd, item->uuid )){
- g_assert( action && NACT_IS_ACTION( action ));
- check_for_remove_action( pivot, action );
- g_free( previd );
- }
- previd = g_strdup( item->uuid );
- action = get_action( pivot->private->actions, item->uuid );
-
- if( action ){
- nact_action_update( action, item->parm, item->value );
- } else {
- action = nact_action_create( item->uuid, item->parm, item->value );
- pivot->private->actions = g_slist_prepend( pivot->private->actions, action );
- }
- }
- if( action ){
- g_assert( action && NACT_IS_ACTION( action ));
- check_for_remove_action( pivot, action );
- g_free( previd );
- }
-}
-
-static NactAction *
-get_action( GSList *list, const gchar *uuid )
+void
+nact_pivot_free_notify( NactPivotNotify *npn )
{
- NactAction *found = NULL;
- GSList *ia;
- for( ia = list ; ia && !found ; ia = ia->next ){
- NactAction *action = ( NactAction * ) ia->data;
- gchar *id = nact_action_get_uuid( action );
- if( !g_strcmp0( id, uuid )){
- found = action;
+ if( npn ){
+ if( npn->type ){
+ switch( npn->type ){
+
+ case NACT_PIVOT_STR:
+ g_free(( gchar * ) npn->data );
+ break;
+
+ case NACT_PIVOT_BOOL:
+ break;
+
+ case NACT_PIVOT_STRLIST:
+ nactuti_free_string_list(( GSList * ) npn->data );
+ break;
+
+ default:
+ g_debug( "nact_pivot_free_notify: uuid=%s, parm=%s, type=%d", npn->uuid, npn->parm, npn->type );
+ g_assert_not_reached();
+ break;
+ }
}
- g_free( id );
+ g_free( npn->uuid );
+ g_free( npn->parm );
+ g_free( npn );
}
- return( found );
-}
-
-/**
- * Returns the searched NactAction, or NULL.
- */
-GObject *
-nact_pivot_get_action( NactPivot *pivot, const gchar *uuid )
-{
- g_assert( NACT_IS_PIVOT( pivot ));
- return( G_OBJECT( get_action( pivot->private->actions, uuid )));
}
diff --git a/src/common/nact-pivot.h b/src/common/nact-pivot.h
index 0ea8b0e..6f8ff91 100644
--- a/src/common/nact-pivot.h
+++ b/src/common/nact-pivot.h
@@ -44,24 +44,6 @@
G_BEGIN_DECLS
-/*
- * We would want have a sort of GConfValue, but which is not named with
- * GConf, in order to propose this same structure to other storage
- * subsystems.
- * We so define this, with only the data types we need.
- */
-enum {
- NACT_PIVOT_STR = 1,
- NACT_PIVOT_BOOL,
- NACT_PIVOT_STRLIST
-};
-
-typedef struct {
- guint type;
- gpointer data;
-}
- NactPivotValue;
-
#define NACT_PIVOT_TYPE ( nact_pivot_get_type())
#define NACT_PIVOT( object ) ( G_TYPE_CHECK_INSTANCE_CAST( object, NACT_PIVOT_TYPE, NactPivot ))
#define NACT_PIVOT_CLASS( klass ) ( G_TYPE_CHECK_CLASS_CAST( klass, NACT_PIVOT_TYPE, NactPivotClass ))
@@ -85,17 +67,33 @@ typedef struct {
}
NactPivotClass;
-GType nact_pivot_get_type( void );
+GType nact_pivot_get_type( void );
-NactPivot *nact_pivot_new( void );
+NactPivot *nact_pivot_new( const GObject *notified );
-GSList *nact_pivot_get_providers( const NactPivot *pivot, GType type );
+GSList *nact_pivot_get_providers( const NactPivot *pivot, GType type );
-GObject *nact_pivot_get_action( NactPivot *pivot, const gchar *uuid );
-void nact_pivot_on_action_changed( NactPivot *pivot, const gchar *uuid, const gchar *parm, NactPivotValue *value );
+GObject *nact_pivot_get_action( NactPivot *pivot, const gchar *uuid );
+
+/*void nact_pivot_on_action_changed( NactPivot *pivot, const gchar *uuid, const gchar *parm, NactPivotValue *value );*/
+
+/* data passed from the storage subsystem when an action is changed
+ */
+enum {
+ NACT_PIVOT_STR = 1,
+ NACT_PIVOT_BOOL,
+ NACT_PIVOT_STRLIST
+};
+
+typedef struct {
+ gchar *uuid;
+ gchar *parm;
+ guint type;
+ gpointer data;
+}
+ NactPivotNotify;
-NactPivotValue *nact_pivot_duplicate_pivot_value( const NactPivotValue *value );
-void nact_pivot_free_pivot_value( NactPivotValue *value );
+void nact_pivot_free_notify( NactPivotNotify *data );
G_END_DECLS
diff --git a/src/common/nact-uti-lists.h b/src/common/nact-uti-lists.h
index 42772d3..bd178e3 100644
--- a/src/common/nact-uti-lists.h
+++ b/src/common/nact-uti-lists.h
@@ -41,6 +41,7 @@ G_BEGIN_DECLS
void nactuti_free_object_list( GSList *list );
+gboolean nactuti_is_empty_string_list( GSList *list );
GSList *nactuti_duplicate_string_list( GSList *list );
void nactuti_free_string_list( GSList *list );
diff --git a/src/common/nautilus-actions-config.c b/src/common/nautilus-actions-config.c
index bff7756..92ecbc1 100644
--- a/src/common/nautilus-actions-config.c
+++ b/src/common/nautilus-actions-config.c
@@ -94,7 +94,7 @@ nautilus_actions_config_class_init (NautilusActionsConfigClass *klass)
NULL, NULL,
g_cclosure_marshal_VOID__POINTER,
G_TYPE_NONE, 1, G_TYPE_POINTER);
- signals[ACTION_CHANGED] = g_signal_new ("action_changed",
+ signals[ACTION_CHANGED] = g_signal_new ("action_changed_old",
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
G_STRUCT_OFFSET (NautilusActionsConfigClass, action_changed),
diff --git a/src/plugin/nautilus-actions.c b/src/plugin/nautilus-actions.c
index 3876642..bf38a80 100644
--- a/src/plugin/nautilus-actions.c
+++ b/src/plugin/nautilus-actions.c
@@ -51,6 +51,13 @@
#include "nautilus-actions-test.h"
#include "nautilus-actions-utils.h"
+/* private class data
+ */
+struct NautilusActionsClassPrivate {
+};
+
+/* private instance data
+ */
struct NautilusActionsPrivate {
gboolean dispose_has_run;
@@ -62,11 +69,31 @@ struct NautilusActionsPrivate {
GSList* config_list;
};
-struct NautilusActionsClassPrivate {
+/* We have a double stage notification system :
+ *
+ * 1. when the storage subsystems detects a change on an action, it must
+ * emit a signal to notify us of this change ; we so have to update
+ * accordingly the list of actions we maintain
+ *
+ * 2. when we have successfully updated the list of actions, we have to
+ * notify nautilus to update its contextual menu ; this is left to
+ * NautilusActions class
+ *
+ * This same signal is then first emitted by the IIOProvider to the
+ * NactPivot object which handles it. When all modifications have been
+ * treated, NactPivot notifies NautilusActions which itself asks
+ * Nautilus for updating its menu
+ */
+enum {
+ ACTION_CHANGED,
+ LAST_SIGNAL
};
+#define SIGNAL_ACTION_CHANGED_NAME "notify_nautilus_of_action_changed"
+
static GObjectClass *st_parent_class = NULL;
static GType st_actions_type = 0;
+static gint st_signals[ LAST_SIGNAL ] = { 0 };
static void class_init( NautilusActionsClass *klass );
static void menu_provider_iface_init( NautilusMenuProviderIface *iface );
@@ -74,12 +101,13 @@ static void instance_init( GTypeInstance *instance, gpointer klass );
static void instance_dispose( GObject *object );
static void instance_finalize( GObject *object );
-static void action_changed_handler( NautilusActionsConfig* config, NautilusActionsConfigAction* action, gpointer user_data );
-static NautilusMenuItem *create_menu_item( NautilusActionsConfigAction *action, GList *files, NautilusActionsConfigActionProfile* action_profile );
-static void execute_action( NautilusMenuItem *item, NautilusActionsConfigActionProfile *action_profile );
static GList *get_background_items( NautilusMenuProvider *provider, GtkWidget *window, NautilusFileInfo *current_folder );
static GList *get_file_items( NautilusMenuProvider *provider, GtkWidget *window, GList *files );
static const gchar *get_verified_icon_name( const gchar* icon_name );
+static NautilusMenuItem *create_menu_item( NautilusActionsConfigAction *action, GList *files, NautilusActionsConfigActionProfile* action_profile );
+static void execute_action( NautilusMenuItem *item, NautilusActionsConfigActionProfile *action_profile );
+static void action_changed_handler( NautilusActions *instance, gpointer user_data );
+static void action_changed_handler_old( NautilusActionsConfig* config, NautilusActionsConfigAction* action, gpointer user_data );
GType
nautilus_actions_get_type( void )
@@ -132,6 +160,26 @@ class_init( NautilusActionsClass *klass )
gobject_class->finalize = instance_finalize;
klass->private = g_new0( NautilusActionsClassPrivate, 1 );
+
+ /* we could have set a default handler here, which have been
+ * avoided us to connect to the signal ; but a default handler is
+ * addressed via a class structure offset, and thus cannot work
+ * when defined in a private structure
+ *
+ * the previous point applies to g_signal_new
+ * g_signal_new_class_handler let us specify a standard C callback
+ */
+ st_signals[ ACTION_CHANGED ] = g_signal_new_class_handler(
+ SIGNAL_ACTION_CHANGED_NAME,
+ G_TYPE_FROM_CLASS( klass ),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
+ ( GCallback ) action_changed_handler,
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0
+ );
}
static void
@@ -170,7 +218,19 @@ instance_init( GTypeInstance *instance, gpointer klass )
self->private = g_new0( NautilusActionsPrivate, 1 );
/* from nact-pivot */
- self->private->pivot = nact_pivot_new();
+ self->private->pivot = nact_pivot_new( G_OBJECT( self ));
+
+ /* see nautilus_actions_class_init for why we have to connect an
+ * handler to our signal instead of relying on default handler
+ * see also nautilus_actions_class_init for why g_signal_connect is
+ * no more needed
+ */
+ /*g_signal_connect(
+ G_OBJECT( self ),
+ SIGNAL_ACTION_CHANGED_NAME,
+ ( GCallback ) action_changed_handler,
+ NULL
+ );*/
self->private->configs = NULL;
self->private->configs = nautilus_actions_config_gconf_reader_get ();
@@ -181,19 +241,19 @@ instance_init( GTypeInstance *instance, gpointer klass )
g_signal_connect_after(
G_OBJECT( self->private->configs ),
"action_added",
- ( GCallback ) action_changed_handler,
+ ( GCallback ) action_changed_handler_old,
self
);
g_signal_connect_after(
G_OBJECT( self->private->configs ),
- "action_changed",
- ( GCallback ) action_changed_handler,
+ "action_changed_old",
+ ( GCallback ) action_changed_handler_old,
self
);
g_signal_connect_after(
G_OBJECT( self->private->configs ),
"action_removed",
- ( GCallback ) action_changed_handler,
+ ( GCallback ) action_changed_handler_old,
self
);
}
@@ -240,92 +300,6 @@ static void nautilus_menu_provider_emit_items_updated_signal (NautilusMenuProvid
}
#endif
-static void
-action_changed_handler( NautilusActionsConfig* config,
- NautilusActionsConfigAction* action,
- gpointer user_data )
-{
- static const gchar *thisfn = "nautilus_actions_action_changed_handler";
- g_debug( "%s", thisfn );
-
- NautilusActions* self = NAUTILUS_ACTIONS (user_data);
-
- g_return_if_fail (NAUTILUS_IS_ACTIONS (self));
-
- if (!self->private->dispose_has_run)
- {
- nautilus_menu_provider_emit_items_updated_signal(( NautilusMenuProvider * ) self );
-
- nautilus_actions_config_free_actions_list (self->private->config_list);
- self->private->config_list = nautilus_actions_config_get_actions (NAUTILUS_ACTIONS_CONFIG (self->private->configs));
- }
-}
-
-static NautilusMenuItem *
-create_menu_item( NautilusActionsConfigAction *action, GList *files, NautilusActionsConfigActionProfile* action_profile )
-{
- static const gchar *thisfn = "nautilus_actions_create_menu_item";
- g_debug( "%s", thisfn );
-
- NautilusMenuItem *item;
- gchar* name;
- const gchar* icon_name = get_verified_icon_name (g_strstrip (action->icon));
- NautilusActionsConfigActionProfile* action_profile4menu = nautilus_actions_config_action_profile_dup (action_profile);
-
- name = g_strdup_printf ("NautilusActions::%s", action->uuid);
-
- item = nautilus_menu_item_new (name,
- action->label,
- action->tooltip,
- icon_name);
-
- g_signal_connect_data (item,
- "activate",
- G_CALLBACK (execute_action),
- action_profile4menu,
- (GClosureNotify)nautilus_actions_config_action_profile_free,
- 0);
-
- g_object_set_data_full (G_OBJECT (item),
- "files",
- nautilus_file_info_list_copy (files),
- (GDestroyNotify) nautilus_file_info_list_free);
-
-
- g_free (name);
-
- return item;
-}
-
-static void
-execute_action( NautilusMenuItem *item, NautilusActionsConfigActionProfile *action_profile )
-{
- static const gchar *thisfn = "nautilus_actions_execute_action";
- g_debug( "%s", thisfn );
-
- GList *files;
- GString *cmd;
- gchar* param = NULL;
-
- files = (GList*)g_object_get_data (G_OBJECT (item), "files");
-
- cmd = g_string_new (action_profile->path);
-
- param = nautilus_actions_utils_parse_parameter (action_profile->parameters, files);
-
- if (param != NULL)
- {
- g_string_append_printf (cmd, " %s", param);
- g_free (param);
- }
-
- g_spawn_command_line_async (cmd->str, NULL);
- g_debug( "%s: commande='%s'", thisfn, cmd->str );
-
- g_string_free (cmd, TRUE);
-
-}
-
/*
* this function is called when nautilus has to paint a folder background
* one of the first calls is with current_folder = 'x-nautilus-desktop:///'
@@ -418,3 +392,106 @@ get_verified_icon_name( const gchar* icon_name )
return icon_name;
}
+
+static NautilusMenuItem *
+create_menu_item( NautilusActionsConfigAction *action, GList *files, NautilusActionsConfigActionProfile* action_profile )
+{
+ static const gchar *thisfn = "nautilus_actions_create_menu_item";
+ g_debug( "%s", thisfn );
+
+ NautilusMenuItem *item;
+ gchar* name;
+ const gchar* icon_name = get_verified_icon_name (g_strstrip (action->icon));
+ NautilusActionsConfigActionProfile* action_profile4menu = nautilus_actions_config_action_profile_dup (action_profile);
+
+ name = g_strdup_printf ("NautilusActions::%s", action->uuid);
+
+ item = nautilus_menu_item_new (name,
+ action->label,
+ action->tooltip,
+ icon_name);
+
+ g_signal_connect_data (item,
+ "activate",
+ G_CALLBACK (execute_action),
+ action_profile4menu,
+ (GClosureNotify)nautilus_actions_config_action_profile_free,
+ 0);
+
+ g_object_set_data_full (G_OBJECT (item),
+ "files",
+ nautilus_file_info_list_copy (files),
+ (GDestroyNotify) nautilus_file_info_list_free);
+
+
+ g_free (name);
+
+ return item;
+}
+
+static void
+execute_action( NautilusMenuItem *item, NautilusActionsConfigActionProfile *action_profile )
+{
+ static const gchar *thisfn = "nautilus_actions_execute_action";
+ g_debug( "%s", thisfn );
+
+ GList *files;
+ GString *cmd;
+ gchar* param = NULL;
+
+ files = (GList*)g_object_get_data (G_OBJECT (item), "files");
+
+ cmd = g_string_new (action_profile->path);
+
+ param = nautilus_actions_utils_parse_parameter (action_profile->parameters, files);
+
+ if (param != NULL)
+ {
+ g_string_append_printf (cmd, " %s", param);
+ g_free (param);
+ }
+
+ g_spawn_command_line_async (cmd->str, NULL);
+ g_debug( "%s: commande='%s'", thisfn, cmd->str );
+
+ g_string_free (cmd, TRUE);
+
+}
+
+static void
+action_changed_handler( NautilusActions *self, gpointer user_data )
+{
+ static const gchar *thisfn = "nautilus_actions_action_changed_handler";
+ g_debug( "%s: self=%p, user_data=%p", thisfn, self, user_data );
+
+ g_return_if_fail( NAUTILUS_IS_ACTIONS( self ));
+
+ if( !self->private->dispose_has_run ){
+
+ nautilus_menu_provider_emit_items_updated_signal( NAUTILUS_MENU_PROVIDER( self ));
+
+ /*nautilus_actions_config_free_actions_list (self->private->config_list);
+ self->private->config_list = nautilus_actions_config_get_actions (NAUTILUS_ACTIONS_CONFIG (self->private->configs));*/
+ }
+}
+
+static void
+action_changed_handler_old( NautilusActionsConfig* config,
+ NautilusActionsConfigAction* action,
+ gpointer user_data )
+{
+ static const gchar *thisfn = "nautilus_actions_action_changed_handler_old";
+ g_debug( "%s", thisfn );
+
+ NautilusActions* self = NAUTILUS_ACTIONS (user_data);
+
+ g_return_if_fail (NAUTILUS_IS_ACTIONS (self));
+
+ if (!self->private->dispose_has_run)
+ {
+ nautilus_menu_provider_emit_items_updated_signal(( NautilusMenuProvider * ) self );
+
+ nautilus_actions_config_free_actions_list (self->private->config_list);
+ self->private->config_list = nautilus_actions_config_get_actions (NAUTILUS_ACTIONS_CONFIG (self->private->configs));
+ }
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]