[nautilus-actions] Implement execution of singular vs. plural command forms



commit 129e5110f63db1e54df9126495247ab0c817f26f
Author: Pierre Wieser <pwieser trychlos org>
Date:   Thu Jul 29 23:59:32 2010 +0200

    Implement execution of singular vs. plural command forms

 ChangeLog                          |   19 +++
 src/api/na-object-action.h         |    2 -
 src/api/na-object-profile.h        |    2 -
 src/core/na-object-action.c        |   29 -----
 src/core/na-object-profile.c       |  232 ------------------------------------
 src/core/na-tokens.c               |  144 +++++++++++++++++++++--
 src/core/na-tokens.h               |   13 ++-
 src/plugin-menu/nautilus-actions.c |  194 ++++++++++++++++++++-----------
 src/utils/nautilus-actions-run.c   |   40 ++-----
 9 files changed, 295 insertions(+), 380 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 698d642..a895631 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,24 @@
 2010-07-29 Pierre Wieser <pwieser trychlos org>
 
+	Do some cleanup in NAObjectAction and NAObjectProfile code.
+	Implement execution of singular vs. plural command forms.
+
+	* src/api/na-object-action.h:
+	* src/core/na-object-action.c (na_object_action_is_candidate):
+	Removed function.
+
+	* src/api/na-object-profile.h:
+	* src/core/na-object-profile.c (na_object_profile_parse_parameters):
+	Removed function.
+
+	* src/core/na-tokens.c:
+	* src/core/na-tokens.h (na_tokens_execute_action): New function.
+
+	* src/plugin-menu/nautilus-actions.c (execute_action):
+	Move action execution to NATokens class.
+
+	* src/utils/nautilus-actions-run.c: Now use NATokens class.
+
 	* doc/: Renamed as docs/
 
 	* configure.ac:
diff --git a/src/api/na-object-action.h b/src/api/na-object-action.h
index f9e4ff7..dd1965d 100644
--- a/src/api/na-object-action.h
+++ b/src/api/na-object-action.h
@@ -84,8 +84,6 @@ gchar          *na_object_action_get_new_profile_name( const NAObjectAction *act
 void            na_object_action_attach_profile( NAObjectAction *action, NAObjectProfile *profile );
 void            na_object_action_set_last_version( NAObjectAction *action );
 
-gboolean        na_object_action_is_candidate( const NAObjectAction *action, guint target, GList *selection );
-
 G_END_DECLS
 
 #endif /* __NAUTILUS_ACTIONS_API_NA_OBJECT_ACTION_H__ */
diff --git a/src/api/na-object-profile.h b/src/api/na-object-profile.h
index a633bf3..8106234 100644
--- a/src/api/na-object-profile.h
+++ b/src/api/na-object-profile.h
@@ -79,8 +79,6 @@ GType            na_object_profile_get_type( void );
 NAObjectProfile *na_object_profile_new( void );
 NAObjectProfile *na_object_profile_new_with_defaults( void );
 
-gchar           *na_object_profile_parse_parameters( const NAObjectProfile *profile, gint target, GList *selected );
-
 G_END_DECLS
 
 #endif /* __NAUTILUS_ACTIONS_API_NA_OBJECT_PROFILE_H__ */
diff --git a/src/core/na-object-action.c b/src/core/na-object-action.c
index ec5c9f1..96ce4a1 100644
--- a/src/core/na-object-action.c
+++ b/src/core/na-object-action.c
@@ -761,32 +761,3 @@ na_object_action_set_last_version( NAObjectAction *action )
 		na_object_set_version( action, "2.0" );
 	}
 }
-
-/**
- * na_object_action_is_candidate:
- * @action: the #NAObjectAction to be tested.
- * @target: the current target.
- * @selection: the current Nautilus selection.
- *
- * Returns: %TRUE if the @action may be candidate for this @target.
- *
- * Note that this public function will become NAIContext::is_candidate
- * when NAObjectAction will implement the interface.
- */
-gboolean
-na_object_action_is_candidate( const NAObjectAction *action, guint target, GList *selection )
-{
-	gboolean is_candidate = FALSE;
-
-	g_return_val_if_fail( NA_IS_OBJECT_ACTION( action ), is_candidate );
-
-	if( !action->private->dispose_has_run ){
-
-		is_candidate =
-			( na_object_is_target_selection( action ) && target == ITEM_TARGET_SELECTION ) ||
-			( na_object_is_target_location( action ) && target == ITEM_TARGET_LOCATION ) ||
-			( na_object_is_target_toolbar( action ) && target == ITEM_TARGET_TOOLBAR );
-	}
-
-	return( is_candidate );
-}
diff --git a/src/core/na-object-profile.c b/src/core/na-object-profile.c
index cd4d1ee..ae084fb 100644
--- a/src/core/na-object-profile.c
+++ b/src/core/na-object-profile.c
@@ -683,235 +683,3 @@ na_object_profile_new_with_defaults( void )
 
 	return( profile );
 }
-
-/**
- * Expands the parameters path, in function of the found tokens.
- *
- * @profile: the selected profile.
- * @target: the current target.
- * @files: the list of currently selected #NASelectedInfo items.
- *
- * Valid parameters are :
- *
- * %d : base dir of the (first) selected file(s)/folder(s)
- * %f : the name of the (first) selected file/folder
- * %h : hostname of the (first) URI
- * %m : list of the basename of the selected files/directories separated by space.
- * %M : list of the selected files/directories with their complete path separated by space.
- * %p : port number from the (first) URI
- * %R : space-separated list of URIs
- * %s : scheme of the (first) URI
- * %u : (first) URI
- * %U : username of the (first) URI
- * %% : a percent sign
- *
- * Adding a parameter requires updating of :
- * - nautilus-actions/core/na-object-profile.c::na_object_profile_parse_parameters()
- * - nautilus-actions/nact/nact-icommand-tab.c:parse_parameters()
- * - nautilus-actions/nact/nautilus-actions-config-tool.ui:LegendDialog
- */
-gchar *
-na_object_profile_parse_parameters( const NAObjectProfile *profile, gint target, GList* files )
-{
-	gchar *parsed = NULL;
-	GString *string;
-	GList *ifi;
-	gboolean first;
-	gchar *iuri, *ipath, *ibname;
-	GFile *iloc;
-	gchar *uri = NULL;
-	gchar *scheme = NULL;
-	gchar *dirname = NULL;
-	gchar *filename = NULL;
-	gchar *hostname = NULL;
-	gchar *username = NULL;
-	gint port_number = 0;
-	GString *basename_list, *pathname_list, *uris_list;
-	gchar *tmp, *iter, *old_iter;
-	NAGnomeVFSURI *vfs;
-
-	g_return_val_if_fail( NA_IS_OBJECT_PROFILE( profile ), NULL );
-
-	if( profile->private->dispose_has_run ){
-		return( NULL );
-	}
-
-	string = g_string_new( "" );
-	basename_list = g_string_new( "" );
-	pathname_list = g_string_new( "" );
-	uris_list = g_string_new( "" );
-	first = TRUE;
-
-	for( ifi = files ; ifi ; ifi = ifi->next ){
-
-		iuri = na_selected_info_get_uri( NA_SELECTED_INFO( ifi->data ));
-		iloc = na_selected_info_get_location( NA_SELECTED_INFO( ifi->data ));
-		ipath = g_file_get_path( iloc );
-		ibname = g_file_get_basename( iloc );
-
-		if( first ){
-
-			vfs = g_new0( NAGnomeVFSURI, 1 );
-			na_gnome_vfs_uri_parse( vfs, iuri );
-
-			uri = g_strdup( iuri );
-			dirname = ipath ? g_path_get_dirname( ipath ) : NULL;
-			scheme = g_strdup( vfs->scheme );
-			filename = g_strdup( ibname );
-			hostname = g_strdup( vfs->host_name );
-			username = g_strdup( vfs->user_name );
-			port_number = vfs->host_port;
-
-			first = FALSE;
-			na_gnome_vfs_uri_free( vfs );
-		}
-
-		if( ibname ){
-			if( strlen( basename_list->str )){
-				basename_list = g_string_append( basename_list, " " );
-			}
-			tmp = g_shell_quote( ibname );
-			g_string_append_printf( basename_list, "%s", tmp );
-			g_free( tmp );
-		}
-
-		if( ipath ){
-			if( strlen( pathname_list->str )){
-				pathname_list = g_string_append( pathname_list, " " );
-			}
-			tmp = g_shell_quote( ipath );
-			g_string_append_printf( pathname_list, "%s", tmp );
-			g_free( tmp );
-		}
-
-		if( strlen( uris_list->str )){
-			uris_list = g_string_append( uris_list, " " );
-		}
-		tmp = g_shell_quote( iuri );
-		g_string_append_printf( uris_list, "%s", tmp );
-		g_free( tmp );
-
-		g_free( ibname );
-		g_free( ipath );
-		g_object_unref( iloc );
-		g_free( iuri );
-	}
-
-	iter = na_object_get_parameters( profile );
-	old_iter = iter;
-
-	while(( iter = g_strstr_len( iter, strlen( iter ), "%" ))){
-
-		string = g_string_append_len( string, old_iter, strlen( old_iter ) - strlen( iter ));
-		switch( iter[1] ){
-
-			/* base dir of the (first) selected item
-			 */
-			case 'd':
-				if( dirname ){
-					tmp = g_shell_quote( dirname );
-					string = g_string_append( string, tmp );
-					g_free( tmp );
-				}
-				break;
-
-			/* basename of the (first) selected item
-			 */
-			case 'f':
-				if( filename ){
-					tmp = g_shell_quote( filename );
-					string = g_string_append( string, tmp );
-					g_free( tmp );
-				}
-				break;
-
-			/* hostname of the (first) URI
-			 */
-			case 'h':
-				if( hostname ){
-					string = g_string_append( string, hostname );
-				}
-				break;
-
-			/* space-separated list of the basenames
-			 */
-			case 'm':
-				if( basename_list->str ){
-					string = g_string_append( string, basename_list->str );
-				}
-				break;
-
-			/* space-separated list of full pathnames
-			 */
-			case 'M':
-				if( pathname_list->str ){
-					string = g_string_append( string, pathname_list->str );
-				}
-				break;
-
-			/* port number of the (first) URI
-			 */
-			case 'p':
-				if( port_number > 0 ){
-					g_string_append_printf( string, "%d", port_number );
-				}
-				break;
-
-			/* list of URIs
-			 */
-			case 'R':
-				if( uris_list->str ){
-					string = g_string_append( string, uris_list->str );
-				}
-				break;
-
-			/* scheme of the (first) URI
-			 */
-			case 's':
-				if( scheme ){
-					string = g_string_append( string, scheme );
-				}
-				break;
-
-			/* URI of the first item
-			 */
-			case 'u':
-				if( uri ){
-					string = g_string_append( string, uri );
-				}
-				break;
-
-			/* username of the (first) URI
-			 */
-			case 'U':
-				if( username ){
-					string = g_string_append( string, username );
-				}
-				break;
-
-			/* a percent sign
-			 */
-			case '%':
-				string = g_string_append_c( string, '%' );
-				break;
-		}
-
-		iter += 2;			/* skip the % sign and the character after */
-		old_iter = iter;	/* store the new start of the string */
-	}
-
-	string = g_string_append_len( string, old_iter, strlen( old_iter ));
-
-	g_free( uri );
-	g_free( dirname );
-	g_free( scheme );
-	g_free( hostname );
-	g_free( username );
-	g_free( iter );
-	g_string_free( uris_list, TRUE );
-	g_string_free( basename_list, TRUE );
-	g_string_free( pathname_list, TRUE );
-
-	parsed = g_string_free( string, FALSE );
-	return( parsed );
-}
diff --git a/src/core/na-tokens.c b/src/core/na-tokens.c
index 719c13f..4789596 100644
--- a/src/core/na-tokens.c
+++ b/src/core/na-tokens.c
@@ -35,6 +35,7 @@
 #include <string.h>
 
 #include <api/na-core-utils.h>
+#include <api/na-object-api.h>
 
 #include "na-gnome-vfs-uri.h"
 #include "na-selected-info.h"
@@ -74,11 +75,15 @@ struct NATokensPrivate {
 
 static GObjectClass *st_parent_class = NULL;
 
-static GType register_type( void );
-static void  class_init( NATokensClass *klass );
-static void  instance_init( GTypeInstance *instance, gpointer klass );
-static void  instance_dispose( GObject *object );
-static void  instance_finalize( GObject *object );
+static GType    register_type( void );
+static void     class_init( NATokensClass *klass );
+static void     instance_init( GTypeInstance *instance, gpointer klass );
+static void     instance_dispose( GObject *object );
+static void     instance_finalize( GObject *object );
+
+static void     execute_action_command( const gchar *command, const NAObjectProfile *profile );
+static gboolean is_singular_exec( const NATokens *tokens, const gchar *exec );
+static gchar   *parse_singular( const NATokens *tokens, const gchar *input, guint i, gboolean utf8 );
 
 GType
 na_tokens_get_type( void )
@@ -327,6 +332,123 @@ na_tokens_new_from_selection( GList *selection )
 gchar *
 na_tokens_parse_parameters( const NATokens *tokens, const gchar *input, gboolean utf8 )
 {
+	return( parse_singular( tokens, input, 0, utf8 ));
+}
+
+/**
+ * na_tokens_execute_action:
+ * @tokens: a #NATokens object.
+ * @profile: the #NAObjectProfile to be executed.
+ *
+ * Execute the given action, regarding the context described by @tokens.
+ */
+void
+na_tokens_execute_action( const NATokens *tokens, const NAObjectProfile *profile )
+{
+	gchar *path, *parameters, *exec, *command;
+	gboolean singular;
+	guint i;
+
+	path = na_object_get_path( profile );
+	parameters = na_object_get_parameters( profile );
+	exec = g_strdup_printf( "%s %s", path, parameters );
+	g_free( parameters );
+	g_free( path );
+
+	singular = is_singular_exec( tokens, exec );
+
+	if( singular ){
+		for( i = 0 ; i < tokens->private->count ; ++i ){
+			command = parse_singular( tokens, exec, i, FALSE );
+			execute_action_command( command, profile );
+			g_free( command );
+		}
+
+	} else {
+		command = na_tokens_parse_parameters( tokens, exec, FALSE );
+		execute_action_command( command, profile );
+		g_free( command );
+	}
+
+
+	g_free( exec );
+}
+
+static void
+execute_action_command( const gchar *command, const NAObjectProfile *profile )
+{
+	static const gchar *thisfn = "nautilus_actions_execute_action_command";
+
+	g_debug( "%s: command=%s, profile=%p", thisfn, command, ( void * ) profile );
+
+	g_spawn_command_line_async( command, NULL );
+}
+
+/*
+ * na_tokens_is_singular_exec:
+ * @tokens: the current #NATokens object.
+ * @exec: the to be executed command-line before having been parsed
+ *
+ * Returns: %TRUE if the first parameter found in @exec command-line is
+ * of singular form, %FALSE else.
+ *
+ * %% and %c are considered here as singular parameters. This function
+ * so defaults to %TRUE as long as no plural form parameter is found.
+ */
+static gboolean
+is_singular_exec( const NATokens *tokens, const gchar *exec )
+{
+	gboolean singular;
+	gchar *found;
+
+	singular = TRUE;
+	found = g_strstr_len( exec, -1, "%" );
+
+	if( found ){
+		switch( found[1] ){
+			case 'b':
+			case 'c':
+			case 'd':
+			case 'f':
+			case 'h':
+			case 'n':
+			case 'p':
+			case 's':
+			case 'u':
+			case 'w':
+			case 'x':
+			case '%':
+				break;
+
+			case 'B':
+			case 'D':
+			case 'F':
+			case 'U':
+			case 'W':
+			case 'X':
+				singular = FALSE;
+				break;
+		}
+	}
+
+	return( singular );
+}
+
+/*
+ * na_tokens_parse_singular:
+ * @tokens: a #NATokens object.
+ * @input: the input string, may or may not contain tokens.
+ * @i: the number of the iteration in a multiple selection, starting with zero.
+ * @utf8: whether the @input string is UTF-8 encoded, or a standard ASCII
+ *  string.
+ *
+ * A command is said of 'singular form' when its first parameter is not
+ * of plural form. In the case of a multiple selection, singular form
+ * commands are executed one time for each element of the selection
+ */
+static gchar *
+parse_singular( const NATokens *tokens, const gchar *input, guint i, gboolean utf8 )
+{
 	GString *output;
 	gchar *iter, *prev_iter, *tmp;
 
@@ -359,7 +481,7 @@ na_tokens_parse_parameters( const NATokens *tokens, const gchar *input, gboolean
 		switch( iter[1] ){
 			case 'b':
 				if( tokens->private->basenames ){
-					tmp = g_shell_quote( tokens->private->basenames->data );
+					tmp = g_shell_quote( g_slist_nth_data( tokens->private->basenames, i ));
 					output = g_string_append( output, tmp );
 					g_free( tmp );
 				}
@@ -377,7 +499,7 @@ na_tokens_parse_parameters( const NATokens *tokens, const gchar *input, gboolean
 
 			case 'd':
 				if( tokens->private->basedirs ){
-					tmp = g_shell_quote( tokens->private->basedirs->data );
+					tmp = g_shell_quote( g_slist_nth_data( tokens->private->basedirs, i ));
 					output = g_string_append( output, tmp );
 					g_free( tmp );
 				}
@@ -391,7 +513,7 @@ na_tokens_parse_parameters( const NATokens *tokens, const gchar *input, gboolean
 
 			case 'f':
 				if( tokens->private->filenames ){
-					tmp = g_shell_quote( tokens->private->filenames->data );
+					tmp = g_shell_quote( g_slist_nth_data( tokens->private->filenames, i ));
 					output = g_string_append( output, tmp );
 					g_free( tmp );
 				}
@@ -433,7 +555,7 @@ na_tokens_parse_parameters( const NATokens *tokens, const gchar *input, gboolean
 
 			case 'u':
 				if( tokens->private->uris ){
-					tmp = g_shell_quote( tokens->private->uris->data );
+					tmp = g_shell_quote( g_slist_nth_data( tokens->private->uris, i ));
 					output = g_string_append( output, tmp );
 					g_free( tmp );
 				}
@@ -447,7 +569,7 @@ na_tokens_parse_parameters( const NATokens *tokens, const gchar *input, gboolean
 
 			case 'w':
 				if( tokens->private->basenames_woext ){
-					tmp = g_shell_quote( tokens->private->basenames_woext->data );
+					tmp = g_shell_quote( g_slist_nth_data( tokens->private->basenames_woext, i ));
 					output = g_string_append( output, tmp );
 					g_free( tmp );
 				}
@@ -461,7 +583,7 @@ na_tokens_parse_parameters( const NATokens *tokens, const gchar *input, gboolean
 
 			case 'x':
 				if( tokens->private->exts ){
-					tmp = g_shell_quote( tokens->private->exts->data );
+					tmp = g_shell_quote( g_slist_nth_data( tokens->private->exts, i ));
 					output = g_string_append( output, tmp );
 					g_free( tmp );
 				}
diff --git a/src/core/na-tokens.h b/src/core/na-tokens.h
index 52eb198..802799f 100644
--- a/src/core/na-tokens.h
+++ b/src/core/na-tokens.h
@@ -42,18 +42,19 @@
  * Note that until v2.30, tokens were parsed against selection list only
  * when an item was selected in the Nautilus context menu (i.e. at
  * execution time).
- * Starting with v2.32 (v3.0 ?), this same parsing may occur for each
- * displayed label (as new specs accept tokens in labels) - we so
+ * Starting with unstable v2.99 (stable v3.0), this same parsing may occur
+ * for each displayed label (as new specs accept tokens in labels) - we so
  * factorize this parsing one time for each new selection in the Nautilus
  * plugin, attaching the result to each item in the context menu.
  *
  * Adding a parameter requires updating of :
- * - nautilus-actions/core/na-object-profile.c::na_object_profile_parse_parameters()
+ * - src/core/na-tokens.c::na_tokens_is_singular_exec()
+ * - src/core/na-tokens.c::na_tokens_parse_parameters()
  * - nautilus-actions/nact/nact-icommand-tab.c:parse_parameters()
- * - nautilus-actions/nact/nautilus-actions-config-tool.ui:LegendDialog
+ * - src/nact/nautilus-actions-config-tool.ui:LegendDialog
  */
 
-#include <glib-object.h>
+#include <api/na-object-profile.h>
 
 G_BEGIN_DECLS
 
@@ -86,6 +87,8 @@ NATokens *na_tokens_new_from_selection( GList *selection );
 
 gchar    *na_tokens_parse_parameters( const NATokens *tokens, const gchar *string, gboolean utf8 );
 
+void      na_tokens_execute_action  ( const NATokens *tokens, const NAObjectProfile *profile );
+
 G_END_DECLS
 
 #endif /* __CORE_NA_TOKENS_H__ */
diff --git a/src/plugin-menu/nautilus-actions.c b/src/plugin-menu/nautilus-actions.c
index 02477e8..3868696 100644
--- a/src/plugin-menu/nautilus-actions.c
+++ b/src/plugin-menu/nautilus-actions.c
@@ -91,14 +91,14 @@ static GList            *menu_provider_get_toolbar_items( NautilusMenuProvider *
 static GList            *get_menus_items( NautilusActions *plugin, guint target, GList *selection );
 static GList            *expand_tokens( GList *tree, NATokens *tokens );
 static NAObjectItem     *expand_tokens_item( NAObjectItem *item, NATokens *tokens );
+static void              expand_tokens_context( NAIContext *context, NATokens *tokens );
 static GList            *build_nautilus_menus( NautilusActions *plugin, GList *tree, guint target, GList *files, NATokens *tokens );
 static NAObjectProfile  *get_candidate_profile( NautilusActions *plugin, NAObjectAction *action, guint target, GList *files );
-static NautilusMenuItem *create_item_from_profile( NAObjectProfile *profile, guint target, GList *files );
+static NautilusMenuItem *create_item_from_profile( NAObjectProfile *profile, guint target, GList *files, NATokens *tokens );
 static NautilusMenuItem *create_item_from_menu( NAObjectMenu *menu, GList *subitems );
 static NautilusMenuItem *create_menu_item( NAObjectItem *item );
 static void              attach_submenu_to_item( NautilusMenuItem *item, GList *subitems );
 static void              weak_notify_profile( NAObjectProfile *profile, NautilusMenuItem *item );
-static void              destroy_notify_file_list( GList *list);
 
 static void              execute_action( NautilusMenuItem *item, NAObjectProfile *profile );
 
@@ -540,8 +540,15 @@ get_menus_items( NautilusActions *plugin, guint target, GList *selection )
 }
 
 /*
- * create a copy of the tree where all fields which may embed parameters
- * have been expanded
+ * create a copy of the tree where almost all fields which may embed
+ * parameters have been expanded
+ * here, 'almost' should be readen as:
+ * - all displayable fields, or fields which may have an impact on the display
+ *   (e.g. label, tooltip, icon name)
+ * - all fields which we do not need later
+ *
+ * we keep until the last item activation the Exec key, whose first parameter
+ * actualy determines the form (singular or plural) of the execution..
  */
 static GList *
 expand_tokens( GList *pivot_tree, NATokens *tokens )
@@ -564,7 +571,11 @@ expand_tokens_item( NAObjectItem *item, NATokens *tokens )
 	gchar *old, *new;
 	GSList *subitems_slist, *its, *new_slist;
 	GList *subitems, *it, *new_list;
+	NAObjectItem *expanded_item;
 
+	/* label, tooltip and icon name
+	 * plus the toolbar label if this is an action
+	 */
 	old = na_object_get_label( item );
 	new = na_tokens_parse_parameters( tokens, old, TRUE );
 	na_object_set_label( item, new );
@@ -591,50 +602,110 @@ expand_tokens_item( NAObjectItem *item, NATokens *tokens )
 		g_free( new );
 	}
 
+	/* A NAObjectItem, whether it is an action or a menu, is also a NAIContext
+	 */
+	expand_tokens_context( NA_ICONTEXT( item ), tokens );
+
+	/* subitems lists, whether this is the profiles list of an action
+	 * or the items list of a menu, may be dynamic and embed a command;
+	 * this command itself may embed parameters
+	 */
 	subitems_slist = na_object_get_items_slist( item );
 	new_slist = NULL;
 	for( its = subitems_slist ; its ; its = its->next ){
 		old = ( gchar * ) its->data;
-		new = na_tokens_parse_parameters( tokens, old, FALSE );
+		if( old[0] == '[' && old[strlen(old)-1] == ']' ){
+			new = na_tokens_parse_parameters( tokens, old, FALSE );
+		} else {
+			new = g_strdup( old );
+		}
 		new_slist = g_slist_prepend( new_slist, new );
 	}
 	na_object_set_items_slist( item, new_slist );
 	na_core_utils_slist_free( subitems_slist );
 	na_core_utils_slist_free( new_slist );
 
+	/* last, deal with subitems
+	 */
 	subitems = na_object_get_items( item );
-	new_list = NULL;
-	for( it = subitems ; it ; it = it->next ){
-		if( NA_IS_OBJECT_MENU( item )){
-			expand_tokens_item( NA_OBJECT_ITEM( it->data ), tokens );
 
-		} else {
-			old = na_object_get_path( it->data );
-			new = na_tokens_parse_parameters( tokens, old, FALSE );
-			na_object_set_path( it->data, new );
-			g_free( old );
-			g_free( new );
+	if( NA_IS_OBJECT_MENU( item )){
+		new_list = NULL;
+
+		for( it = subitems ; it ; it = it->next ){
+			expanded_item = expand_tokens_item( NA_OBJECT_ITEM( it->data ), tokens );
+			new_list = g_list_prepend( new_list, expanded_item );
+		}
+
+		na_object_set_items( item, g_list_reverse( new_list ));
 
-			old = na_object_get_parameters( it->data );
+	} else {
+		g_return_val_if_fail( NA_IS_OBJECT_ACTION( item ), NULL );
+
+		for( it = subitems ; it ; it = it->next ){
+
+			/* desktop Exec key = GConf path+parameters
+			 * do not touch them here
+			 */
+			old = na_object_get_working_dir( it->data );
 			new = na_tokens_parse_parameters( tokens, old, FALSE );
-			na_object_set_parameters( it->data, new );
+			na_object_set_working_dir( it->data, new );
 			g_free( old );
 			g_free( new );
+
+			/* a NAObjectProfile is also a NAIContext
+			 */
+			expand_tokens_context( NA_ICONTEXT( it->data ), tokens );
 		}
 	}
 
 	return( item );
 }
 
+static void
+expand_tokens_context( NAIContext *context, NATokens *tokens )
+{
+	gchar *old, *new;
+
+	old = na_object_get_try_exec( context );
+	new = na_tokens_parse_parameters( tokens, old, FALSE );
+	na_object_set_try_exec( context, new );
+	g_free( old );
+	g_free( new );
+
+	old = na_object_get_show_if_registered( context );
+	new = na_tokens_parse_parameters( tokens, old, FALSE );
+	na_object_set_show_if_registered( context, new );
+	g_free( old );
+	g_free( new );
+
+	old = na_object_get_show_if_true( context );
+	new = na_tokens_parse_parameters( tokens, old, FALSE );
+	na_object_set_show_if_true( context, new );
+	g_free( old );
+	g_free( new );
+
+	old = na_object_get_show_if_running( context );
+	new = na_tokens_parse_parameters( tokens, old, FALSE );
+	na_object_set_show_if_running( context, new );
+	g_free( old );
+	g_free( new );
+}
+
 /*
- * when building a menu for the toolbar, do not use menus hierarchy
- * files is a GList of NASelectedInfo items
+ * @plugin: this #NautilusActions module instance.
+ * @tree: a copy of the #NAPivot tree, where all fields - but
+ *  displayable parameters expanded
+ * @target: whether we target location or context menu, or toolbar.
+ * @files: the current selection in the file-manager, as a #GList of #NASelectedInfo items
+ * @tokens: a #NATokens object which embeds all possible values, regarding the
+ *  current selection, for all parameters
+ *
+ * When building a menu for the toolbar, do not use menus hierarchy
  *
- * TODO: menus, actions and profiles may embed parameters in their data.
- * This mainly means that the objects have to be re-parsed for each new
- * selection (e.g. because a label may change if it depends of the current
- * selection). Thus, all the hierarchy must be recursively re-parsed, and
- * should be re-checked for validity !
+ * As menus, actions and profiles may embed parameters in their data,
+ * all the hierarchy must be recursively re-parsed, and should be
+ * re-checked for validity !
  */
 static GList *
 build_nautilus_menus( NautilusActions *plugin, GList *tree, guint target, GList *files, NATokens *tokens )
@@ -658,7 +729,7 @@ build_nautilus_menus( NautilusActions *plugin, GList *tree, guint target, GList
 		/* check this here as a security though NAPivot should only have
 		 * loaded valid and enabled items
 		 */
-		if( !na_object_is_enabled( it->data ) || !na_object_is_valid( it->data )){
+		if( !na_object_is_enabled( it->data )){
 			gchar *label = na_object_get_label( it->data );
 			g_warning( "%s: '%s' item: enabled=%s, valid=%s", thisfn, label,
 					na_object_is_enabled( it->data ) ? "True":"False",
@@ -668,6 +739,13 @@ build_nautilus_menus( NautilusActions *plugin, GList *tree, guint target, GList
 		}
 #endif
 
+		/* but we have to re-check for validity as a label may become
+		 * dynamically empty - thus the NAObjectItem invalid :(
+		 */
+		if( !na_object_is_valid( it->data )){
+			continue;
+		}
+
 		if( !na_icontext_is_candidate( NA_ICONTEXT( it->data ), target, files )){
 			continue;
 		}
@@ -680,12 +758,12 @@ build_nautilus_menus( NautilusActions *plugin, GList *tree, guint target, GList
 			/*g_debug( "%s: submenu has %d items", thisfn, g_list_length( submenu ));*/
 
 			if( submenu ){
-				if( target != ITEM_TARGET_TOOLBAR ){
-					item = create_item_from_menu( NA_OBJECT_MENU( it->data ), submenu );
-					menus_list = g_list_append( menus_list, item );
+				if( target == ITEM_TARGET_TOOLBAR ){
+					menus_list = g_list_concat( menus_list, submenu );
 
 				} else {
-					menus_list = g_list_concat( menus_list, submenu );
+					item = create_item_from_menu( NA_OBJECT_MENU( it->data ), submenu );
+					menus_list = g_list_append( menus_list, item );
 				}
 			}
 			continue;
@@ -695,7 +773,7 @@ build_nautilus_menus( NautilusActions *plugin, GList *tree, guint target, GList
 
 		profile = get_candidate_profile( plugin, NA_OBJECT_ACTION( it->data ), target, files );
 		if( profile ){
-			item = create_item_from_profile( profile, target, files );
+			item = create_item_from_profile( profile, target, files, tokens );
 			menus_list = g_list_append( menus_list, item );
 		}
 	}
@@ -736,7 +814,7 @@ get_candidate_profile( NautilusActions *plugin, NAObjectAction *action, guint ta
 }
 
 static NautilusMenuItem *
-create_item_from_profile( NAObjectProfile *profile, guint target, GList *files )
+create_item_from_profile( NAObjectProfile *profile, guint target, GList *files, NATokens *tokens )
 {
 	NautilusMenuItem *item;
 	NAObjectAction *action;
@@ -759,30 +837,23 @@ create_item_from_profile( NAObjectProfile *profile, guint target, GList *files )
 	g_object_weak_ref( G_OBJECT( item ), ( GWeakNotify ) weak_notify_profile, duplicate );
 
 	g_object_set_data_full( G_OBJECT( item ),
-			"nautilus-actions-files",
-			na_selected_info_copy_list( files ),
-			( GDestroyNotify ) destroy_notify_file_list );
-
-	g_object_set_data( G_OBJECT( item ),
-			"nautilus-actions-target",
-			GUINT_TO_POINTER( target ));
+			"nautilus-actions-tokens",
+			g_object_ref( tokens ),
+			( GDestroyNotify ) g_object_unref );
 
 	return( item );
 }
 
+/*
+ * called _after_ the NautilusMenuItem has been finalized
+ */
 static void
 weak_notify_profile( NAObjectProfile *profile, NautilusMenuItem *item )
 {
 	g_debug( "nautilus_actions_weak_notify_profile: profile=%p (ref_count=%d)",
 			( void * ) profile, G_OBJECT( profile )->ref_count );
-	g_object_unref( profile );
-}
 
-static void
-destroy_notify_file_list( GList *list)
-{
-	g_debug( "nautilus_actions_destroy_notify_file_list" );
-	na_selected_info_free_list( list );
+	g_object_unref( profile );
 }
 
 /*
@@ -842,35 +913,24 @@ attach_submenu_to_item( NautilusMenuItem *item, GList *subitems )
 	}
 }
 
+/*
+ * callback triggered when an item is activated
+ * path and parameters must yet been parsed against tokens
+ *
+ * note that if first parameter if of singular form, then we have to loop
+ * againt the selected, each time replacing the singular parameters with
+ * the current item of the selection
+ */
 static void
 execute_action( NautilusMenuItem *item, NAObjectProfile *profile )
 {
 	static const gchar *thisfn = "nautilus_actions_execute_action";
-	GList *files;
-	GString *cmd;
-	gchar *param, *path;
-	guint target;
+	NATokens *tokens;
 
 	g_debug( "%s: item=%p, profile=%p", thisfn, ( void * ) item, ( void * ) profile );
 
-	files = ( GList * ) g_object_get_data( G_OBJECT( item ), "nautilus-actions-files" );
-	target = GPOINTER_TO_UINT( g_object_get_data( G_OBJECT( item ), "nautilus-actions-target" ));
-
-	path = na_object_get_path( profile );
-	cmd = g_string_new( path );
-
-	param = na_object_profile_parse_parameters( profile, target, files );
-
-	if( param != NULL ){
-		g_string_append_printf( cmd, " %s", param );
-		g_free( param );
-	}
-
-	g_debug( "%s: executing '%s'", thisfn, cmd->str );
-	g_spawn_command_line_async( cmd->str, NULL );
-
-	g_string_free (cmd, TRUE);
-	g_free( path );
+	tokens = NA_TOKENS( g_object_get_data( G_OBJECT( item ), "nautilus-actions-tokens" ));
+	na_tokens_execute_action( tokens, profile );
 }
 
 /*
diff --git a/src/utils/nautilus-actions-run.c b/src/utils/nautilus-actions-run.c
index 68d15e8..fa04404 100644
--- a/src/utils/nautilus-actions-run.c
+++ b/src/utils/nautilus-actions-run.c
@@ -43,6 +43,7 @@
 
 #include <core/na-pivot.h>
 #include <core/na-selected-info.h>
+#include <core/na-tokens.h>
 
 #include <plugin-tracker/na-tracker.h>
 #include <plugin-tracker/na-tracker-dbus.h>
@@ -78,7 +79,6 @@ static GList           *get_selection_from_strv( const gchar **strv, gboolean ha
 static NAObjectProfile *get_profile_for_targets( NAObjectAction *action, GList *targets );
 static void             execute_action( NAObjectAction *action, NAObjectProfile *profile, GList *targets );
 static void             dump_targets( GList *targets );
-static void             free_targets( GList *targets );
 static void             exit_with_usage( void );
 
 int
@@ -152,7 +152,7 @@ main( int argc, char** argv )
 		exit( status );
 	}
 
-	if( !na_object_action_is_candidate( action, ITEM_TARGET_SELECTION, targets )){
+	if( !na_icontext_is_candidate( NA_ICONTEXT( action ), ITEM_TARGET_SELECTION, targets )){
 		g_printerr( _( "Action %s is not a valid candidate. Exiting.\n" ), id );
 		exit( status );
 	}
@@ -166,7 +166,7 @@ main( int argc, char** argv )
 
 	execute_action( action, profile, targets );
 
-	free_targets( targets );
+	na_selected_info_free_list( targets );
 	exit( status );
 }
 
@@ -367,8 +367,8 @@ get_profile_for_targets( NAObjectAction *action, GList *targets )
 
 	candidate = NULL;
 	profiles = na_object_get_items( action );
-	for( ip = profiles ; ip && !candidate ; ip = ip->next ){
 
+	for( ip = profiles ; ip && !candidate ; ip = ip->next ){
 		if( na_icontext_is_candidate( NA_ICONTEXT( ip->data ), ITEM_TARGET_SELECTION, targets )){
 			candidate = NA_OBJECT_PROFILE( ip->data );
 		}
@@ -380,25 +380,11 @@ get_profile_for_targets( NAObjectAction *action, GList *targets )
 static void
 execute_action( NAObjectAction *action, NAObjectProfile *profile, GList *targets )
 {
-	static const gchar *thisfn = "nautilus_action_run_execute_action";
-	GString *cmd;
-	gchar *param, *path;
-
-	path = na_object_get_path( profile );
-	cmd = g_string_new( path );
-
-	param = na_object_profile_parse_parameters( profile, ITEM_TARGET_SELECTION, targets );
-
-	if( param != NULL ){
-		g_string_append_printf( cmd, " %s", param );
-		g_free( param );
-	}
-
-	g_debug( "%s: executing '%s'", thisfn, cmd->str );
-	g_spawn_command_line_async( cmd->str, NULL );
+	/*static const gchar *thisfn = "nautilus_action_run_execute_action";*/
+	NATokens *tokens;
 
-	g_string_free (cmd, TRUE);
-	g_free( path );
+	tokens = na_tokens_new_from_selection( targets );
+	na_tokens_execute_action( tokens, profile );
 }
 
 /*
@@ -421,16 +407,6 @@ dump_targets( GList *targets )
 }
 
 /*
- *
- */
-static void
-free_targets( GList *targets )
-{
-	g_list_foreach( targets, ( GFunc ) g_object_unref, NULL );
-	g_list_free( targets );
-}
-
-/*
  * print a help message and exit with failure
  */
 static void



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]