[nautilus-actions] Expand NAObject fields which may embed parameters



commit 9c5f2ed407f00a596055e27c459d275ae6e0e04b
Author: Pierre Wieser <pwieser trychlos org>
Date:   Wed May 26 22:06:03 2010 +0200

    Expand NAObject fields which may embed parameters

 ChangeLog                          |   14 +
 TODO                               |    5 +
 src/api/na-core-utils.h            |    1 +
 src/core/Makefile.am               |    2 +
 src/core/na-core-utils.c           |   47 ++++-
 src/core/na-tokens.c               |  484 ++++++++++++++++++++++++++++++++++++
 src/core/na-tokens.h               |   91 +++++++
 src/plugin-menu/nautilus-actions.c |  146 +++++++++--
 8 files changed, 766 insertions(+), 24 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 99b0d25..d298a2e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -6,6 +6,20 @@
 
 	* src/core/na-iabout.c: Update copyright notice.
 
+2010-05-26 Pierre Wieser <pwieser trychlos org>
+
+	* src/api/na-core-utils.h:
+	* src/core/na-core-utils.c (na_core_utils_dir_split_ext):
+	New function.
+
+	* src/core/na-tokens.h:
+	* src/core/na-tokens.c: New files.
+
+	* src/core/Makefile.am: Udated accordingly.
+
+	* src/plugin-menu/nautilus-actions.c:
+	Expand tokens which may embed parameters.
+
 2010-05-20 Pierre Wieser <pwieser trychlos org>
 
 	* src/io-gconf/nagp-reader.c (read_done_profile_attach_profile):
diff --git a/TODO b/TODO
index 4a82554..44894a5 100644
--- a/TODO
+++ b/TODO
@@ -454,3 +454,8 @@ Parameter 	Description
 %x 	(first) extension
 %X 	space-separated list of extensions
 %% 	the « % » character
+
+- an item may become invalid at runtime
+  e.g. when the label contains only a list of extension (%X)
+  but selected files do not have extension - most likely, there should be other examples..
+  => label must be expanded and checked and kept before testing for candidate
diff --git a/src/api/na-core-utils.h b/src/api/na-core-utils.h
index 9964b66..bb15faa 100644
--- a/src/api/na-core-utils.h
+++ b/src/api/na-core-utils.h
@@ -76,6 +76,7 @@ gchar   *na_core_utils_gstring_joinv( const gchar *start, const gchar *separator
  */
 gboolean na_core_utils_dir_is_writable_path( const gchar *path );
 gboolean na_core_utils_dir_is_writable_uri( const gchar *uri );
+void     na_core_utils_dir_split_ext( const gchar *string, gchar **first, gchar **ext );
 
 /* file management
  */
diff --git a/src/core/Makefile.am b/src/core/Makefile.am
index 94125b7..3abe580 100644
--- a/src/core/Makefile.am
+++ b/src/core/Makefile.am
@@ -96,6 +96,8 @@ libna_core_la_SOURCES = \
 	na-selected-info.h									\
 	na-updater.c										\
 	na-updater.h										\
+	na-tokens.c											\
+	na-tokens.h											\
 	$(NULL)
 
 libna_core_la_LIBADD = \
diff --git a/src/core/na-core-utils.c b/src/core/na-core-utils.c
index 4ab06ad..37e49b7 100644
--- a/src/core/na-core-utils.c
+++ b/src/core/na-core-utils.c
@@ -327,7 +327,10 @@ na_core_utils_slist_join_at_end( GSList *slist, const gchar *link )
 	str = g_string_new( "" );
 
 	for( is = slist ; is ; is = is->next ){
-		g_string_append_printf( str, "%s%s", ( const gchar * ) is->data, link );
+		if( str->len ){
+			g_string_append_printf( str, "%s", link );
+		}
+		g_string_append_printf( str, "%s", ( const gchar * ) is->data );
 	}
 
 	return( g_string_free( str, FALSE ));
@@ -672,6 +675,48 @@ info_dir_is_writable( GFile *file, const gchar *path_or_uri )
 }
 
 /**
+ * na_core_utils_dir_split_ext:
+ * @string: the input path or URI to be splitted.
+ * @first: a pointer to a buffer which will contain the first part of the split.
+ * @ext: a pointer to a buffer which will contain the extension part of the path.
+ *
+ * Split the given @string, returning the first part and the extension in newly
+ * allocated buffers which should be g_free() by the caller.
+ *
+ * Returns an empty string as extension if no extension is detected.
+ */
+void
+na_core_utils_dir_split_ext( const gchar *string, gchar **first, gchar **ext )
+{
+	gchar *dupped;
+	gchar **array, **iter;
+
+	dupped = g_strreverse( g_strdup( string ));
+	array = g_strsplit( dupped, ".", 2 );
+
+	if( g_strv_length( array ) == 1 ){
+		if( ext ){
+			*ext = g_strdup( "" );
+		}
+		if( first ){
+			*first = g_strreverse( g_strdup(( const gchar * ) *array ));
+		}
+	} else {
+		if( ext ){
+			*ext = g_strreverse( g_strdup(( const gchar * ) *array ));
+		}
+		iter = array;
+		++iter;
+		if( first ){
+			*first = g_strreverse( g_strdup(( const gchar * ) *iter ));
+		}
+	}
+
+	g_strfreev( array );
+	g_free( dupped );
+}
+
+/**
  * na_core_utils_file_delete:
  * @path: the path of the file to be deleted.
  *
diff --git a/src/core/na-tokens.c b/src/core/na-tokens.c
new file mode 100644
index 0000000..54c44df
--- /dev/null
+++ b/src/core/na-tokens.c
@@ -0,0 +1,484 @@
+/*
+ * Nautilus Actions
+ * A Nautilus extension which offers configurable context menu actions.
+ *
+ * Copyright (C) 2005 The GNOME Foundation
+ * Copyright (C) 2006, 2007, 2008 Frederic Ruaudel and others (see AUTHORS)
+ * Copyright (C) 2009, 2010 Pierre Wieser and others (see AUTHORS)
+ *
+ * This Program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this Library; see the file COPYING.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place,
+ * Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Authors:
+ *   Frederic Ruaudel <grumz grumz net>
+ *   Rodrigo Moya <rodrigo gnome-db org>
+ *   Pierre Wieser <pwieser trychlos org>
+ *   ... and many others (see AUTHORS)
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <api/na-core-utils.h>
+
+#include "na-gnome-vfs-uri.h"
+#include "na-selected-info.h"
+#include "na-tokens.h"
+
+/* private class data
+ */
+struct NATokensClassPrivate {
+	void *empty;						/* so that gcc -pedantic is happy */
+};
+
+/* private instance data
+ */
+struct NATokensPrivate {
+	gboolean dispose_has_run;
+
+	guint    count;
+
+	GSList  *uris;
+	gchar   *uris_str;
+	GSList  *filenames;
+	gchar   *filenames_str;
+	GSList  *basedirs;
+	gchar   *basedirs_str;
+	GSList  *basenames;
+	gchar   *basenames_str;
+	GSList  *basenames_woext;
+	gchar   *basenames_woext_str;
+	GSList  *exts;
+	gchar   *exts_str;
+
+	gchar   *hostname;
+	gchar   *username;
+	guint    port;
+	gchar   *scheme;
+};
+
+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 );
+
+GType
+na_tokens_get_type( void )
+{
+	static GType object_type = 0;
+
+	if( !object_type ){
+		object_type = register_type();
+	}
+
+	return( object_type );
+}
+
+static GType
+register_type( void )
+{
+	static const gchar *thisfn = "na_tokens_register_type";
+	GType type;
+
+	static GTypeInfo info = {
+		sizeof( NATokensClass ),
+		( GBaseInitFunc ) NULL,
+		( GBaseFinalizeFunc ) NULL,
+		( GClassInitFunc ) class_init,
+		NULL,
+		NULL,
+		sizeof( NATokens ),
+		0,
+		( GInstanceInitFunc ) instance_init
+	};
+
+	g_debug( "%s", thisfn );
+
+	type = g_type_register_static( G_TYPE_OBJECT, "NATokens", &info, 0 );
+
+	return( type );
+}
+
+static void
+class_init( NATokensClass *klass )
+{
+	static const gchar *thisfn = "na_tokens_class_init";
+	GObjectClass *object_class;
+
+	g_debug( "%s: klass=%p", thisfn, ( void * ) klass );
+
+	st_parent_class = g_type_class_peek_parent( klass );
+
+	object_class = G_OBJECT_CLASS( klass );
+	object_class->dispose = instance_dispose;
+	object_class->finalize = instance_finalize;
+
+	klass->private = g_new0( NATokensClassPrivate, 1 );
+}
+
+static void
+instance_init( GTypeInstance *instance, gpointer klass )
+{
+	static const gchar *thisfn = "na_tokens_instance_init";
+	NATokens *self;
+
+	g_debug( "%s: instance=%p (%s), klass=%p",
+			thisfn, ( void * ) instance, G_OBJECT_TYPE_NAME( instance ), ( void * ) klass );
+	g_return_if_fail( NA_IS_TOKENS( instance ));
+	self = NA_TOKENS( instance );
+
+	self->private = g_new0( NATokensPrivate, 1 );
+
+	self->private->uris = NULL;
+	self->private->uris_str = NULL;
+	self->private->filenames = NULL;
+	self->private->filenames_str = NULL;
+	self->private->basedirs = NULL;
+	self->private->basedirs_str = NULL;
+	self->private->basenames = NULL;
+	self->private->basenames_str = NULL;
+	self->private->basenames_woext = NULL;
+	self->private->basenames_woext_str = NULL;
+	self->private->exts = NULL;
+	self->private->exts_str = NULL;
+
+	self->private->hostname = NULL;
+	self->private->username = NULL;
+	self->private->port = 0;
+	self->private->scheme = NULL;
+
+	self->private->dispose_has_run = FALSE;
+}
+
+static void
+instance_dispose( GObject *object )
+{
+	static const gchar *thisfn = "na_tokens_instance_dispose";
+	NATokens *self;
+
+	g_debug( "%s: object=%p (%s)", thisfn, ( void * ) object, G_OBJECT_TYPE_NAME( object ));
+	g_return_if_fail( NA_IS_TOKENS( object ));
+	self = NA_TOKENS( object );
+
+	if( !self->private->dispose_has_run ){
+
+		self->private->dispose_has_run = TRUE;
+
+		if( G_OBJECT_CLASS( st_parent_class )->dispose ){
+			G_OBJECT_CLASS( st_parent_class )->dispose( object );
+		}
+	}
+}
+
+static void
+instance_finalize( GObject *object )
+{
+	static const gchar *thisfn = "na_tokens_instance_finalize";
+	NATokens *self;
+
+	g_debug( "%s: object=%p", thisfn, ( void * ) object );
+	g_return_if_fail( NA_IS_TOKENS( object ));
+	self = NA_TOKENS( object );
+
+	g_free( self->private->scheme );
+	g_free( self->private->username );
+	g_free( self->private->hostname );
+
+	g_free( self->private->exts_str );
+	na_core_utils_slist_free( self->private->exts );
+	g_free( self->private->basenames_woext_str );
+	na_core_utils_slist_free( self->private->basenames_woext );
+	g_free( self->private->basenames_str );
+	na_core_utils_slist_free( self->private->basenames );
+	g_free( self->private->basedirs_str );
+	na_core_utils_slist_free( self->private->basedirs );
+	g_free( self->private->filenames_str );
+	na_core_utils_slist_free( self->private->filenames );
+	g_free( self->private->uris_str );
+	na_core_utils_slist_free( self->private->uris );
+
+	g_free( self->private );
+
+	/* chain call to parent class */
+	if( G_OBJECT_CLASS( st_parent_class )->finalize ){
+		G_OBJECT_CLASS( st_parent_class )->finalize( object );
+	}
+}
+
+/**
+ * na_tokens_new_from_selection:
+ * @selection: a #GList list of #NASelectedInfo objects.
+ *
+ * Returns: a new #NATokens object which holds all possible tokens.
+ */
+NATokens *
+na_tokens_new_from_selection( GList *selection )
+{
+	NATokens *tokens;
+	GList *it;
+	gchar *uri, *filename, *basedir, *basename, *bname_woext, *ext;
+	GFile *location;
+	gboolean first;
+	NAGnomeVFSURI *vfs;
+
+	first = TRUE;
+	tokens = g_object_new( NA_TOKENS_TYPE, NULL );
+
+	tokens->private->count = g_list_length( selection );
+
+	for( it = selection ; it ; it = it->next ){
+		location = na_selected_info_get_location( NA_SELECTED_INFO( it->data ));
+
+		uri = na_selected_info_get_uri( NA_SELECTED_INFO( it->data ));
+		filename = g_file_get_path( location );
+		basedir = g_path_get_dirname( filename );
+		basename = g_file_get_basename( location );
+		na_core_utils_dir_split_ext( basename, &bname_woext, &ext );
+
+		if( first ){
+			vfs = g_new0( NAGnomeVFSURI, 1 );
+			na_gnome_vfs_uri_parse( vfs, uri );
+
+			tokens->private->hostname = g_strdup( vfs->host_name );
+			tokens->private->username = g_strdup( vfs->user_name );
+			tokens->private->port = vfs->host_port;
+			tokens->private->scheme = g_strdup( vfs->scheme );
+
+			na_gnome_vfs_uri_free( vfs );
+			first = FALSE;
+		}
+
+		tokens->private->uris = g_slist_prepend( tokens->private->uris, uri );
+		tokens->private->filenames = g_slist_prepend( tokens->private->filenames, filename );
+		tokens->private->basedirs = g_slist_prepend( tokens->private->basedirs, basedir );
+		tokens->private->basenames = g_slist_prepend( tokens->private->basenames, basename );
+		tokens->private->basenames_woext = g_slist_prepend( tokens->private->basenames_woext, bname_woext );
+		tokens->private->exts = g_slist_prepend( tokens->private->exts, ext );
+
+		g_object_unref( location );
+	}
+
+	tokens->private->uris_str = na_core_utils_slist_join_at_end( tokens->private->uris, " " );
+	tokens->private->filenames_str = na_core_utils_slist_join_at_end( tokens->private->filenames, " " );
+	tokens->private->basedirs_str = na_core_utils_slist_join_at_end( tokens->private->basedirs, " " );
+	tokens->private->basenames_str = na_core_utils_slist_join_at_end( tokens->private->basenames, " " );
+	tokens->private->basenames_woext_str = na_core_utils_slist_join_at_end( tokens->private->basenames_woext, " " );
+	tokens->private->exts_str = na_core_utils_slist_join_at_end( tokens->private->exts, " " );
+
+	return( tokens );
+}
+
+/**
+ * na_tokens_parse_parameters:
+ * @tokens: a #NATokens object.
+ * @input: the input string, may or may not contain tokens.
+ * @utf8: whether the @input string is UTF-8 encoded, or a standard ASCII string.
+ *
+ * Expands the parameters in the given string.
+ *
+ * Valid parameters are :
+ *
+ * %b: (first) basename
+ * %B: space-separated list of basenames
+ * %c: count of selected items
+ * %d: (first) base directory
+ * %D: space-separated list of base directory of each selected items
+ * %f: (first) file name
+ * %F: space-separated list of selected file names
+ * %h: hostname of the (first) URI
+ * %n: username of the (first) URI
+ * %p: port number of the (first) URI
+ * %s: scheme of the (first) URI
+ * %u: (first) URI
+ * %U: space-separated list of selected URIs
+ * %w: (first) basename without the extension
+ * %W: space-separated list of basenames without their extension
+ * %x: (first) extension
+ * %X: space-separated list of extensions
+ * %%: the « % » character
+ *
+ * Returns: a copy of @input string with tokens expanded, as a newly
+ * allocated string which should be g_free() by the caller.
+ */
+gchar *
+na_tokens_parse_parameters( const NATokens *tokens, const gchar *input, gboolean utf8 )
+{
+	GString *output;
+	gchar *iter, *prev_iter, *tmp;
+
+	output = g_string_new( "" );
+
+	/* return NULL if input is NULL
+	 */
+	if( !input ){
+		return( g_string_free( output, TRUE ));
+	}
+
+	/* return an empty string if input is empty
+	 */
+	if( utf8 ){
+		if( !g_utf8_strlen( input, -1 )){
+			return( g_string_free( output, FALSE ));
+		}
+	} else {
+		if( !strlen( input )){
+			return( g_string_free( output, FALSE ));
+		}
+	}
+
+	iter = ( gchar * ) input;
+	prev_iter = iter;
+
+	while(( iter = g_strstr_len( iter, -1, "%" ))){
+		output = g_string_append_len( output, prev_iter, strlen( prev_iter ) - strlen( iter ));
+
+		switch( iter[1] ){
+			case 'b':
+				if( tokens->private->basenames ){
+					tmp = g_shell_quote( tokens->private->basenames->data );
+					output = g_string_append( output, tmp );
+					g_free( tmp );
+				}
+				break;
+
+			case 'B':
+				if( tokens->private->basenames ){
+					output = g_string_append( output, tokens->private->basenames_str );
+				}
+				break;
+
+			case 'c':
+				g_string_append_printf( output, "%d", tokens->private->count );
+				break;
+
+			case 'd':
+				if( tokens->private->basedirs ){
+					tmp = g_shell_quote( tokens->private->basedirs->data );
+					output = g_string_append( output, tmp );
+					g_free( tmp );
+				}
+				break;
+
+			case 'D':
+				if( tokens->private->basedirs ){
+					output = g_string_append( output, tokens->private->basedirs_str );
+				}
+				break;
+
+			case 'f':
+				if( tokens->private->filenames ){
+					tmp = g_shell_quote( tokens->private->filenames->data );
+					output = g_string_append( output, tmp );
+					g_free( tmp );
+				}
+				break;
+
+			case 'F':
+				if( tokens->private->filenames ){
+					output = g_string_append( output, tokens->private->filenames_str );
+				}
+				break;
+
+			case 'h':
+				if( tokens->private->hostname ){
+					tmp = g_shell_quote( tokens->private->hostname );
+					output = g_string_append( output, tmp );
+					g_free( tmp );
+				}
+				break;
+
+			case 'n':
+				if( tokens->private->username ){
+					tmp = g_shell_quote( tokens->private->username );
+					output = g_string_append( output, tmp );
+					g_free( tmp );
+				}
+				break;
+
+			case 'p':
+				g_string_append_printf( output, "%d", tokens->private->port );
+				break;
+
+			case 's':
+				if( tokens->private->scheme ){
+					tmp = g_shell_quote( tokens->private->scheme );
+					output = g_string_append( output, tmp );
+					g_free( tmp );
+				}
+				break;
+
+			case 'u':
+				if( tokens->private->uris ){
+					tmp = g_shell_quote( tokens->private->uris->data );
+					output = g_string_append( output, tmp );
+					g_free( tmp );
+				}
+				break;
+
+			case 'U':
+				if( tokens->private->uris ){
+					output = g_string_append( output, tokens->private->uris_str );
+				}
+				break;
+
+			case 'w':
+				if( tokens->private->basenames_woext ){
+					tmp = g_shell_quote( tokens->private->basenames_woext->data );
+					output = g_string_append( output, tmp );
+					g_free( tmp );
+				}
+				break;
+
+			case 'W':
+				if( tokens->private->basenames_woext ){
+					output = g_string_append( output, tokens->private->basenames_woext_str );
+				}
+				break;
+
+			case 'x':
+				if( tokens->private->exts ){
+					tmp = g_shell_quote( tokens->private->exts->data );
+					output = g_string_append( output, tmp );
+					g_free( tmp );
+				}
+				break;
+
+			case 'X':
+				if( tokens->private->exts ){
+					output = g_string_append( output, tokens->private->exts_str );
+				}
+				break;
+
+			/* a percent sign
+			 */
+			case '%':
+				output = g_string_append_c( output, '%' );
+				break;
+		}
+
+		iter += 2;			/* skip the % sign and the character after */
+		prev_iter = iter;	/* store the new start of the string */
+	}
+
+	output = g_string_append_len( output, prev_iter, strlen( prev_iter ));
+
+	return( g_string_free( output, FALSE ));
+}
diff --git a/src/core/na-tokens.h b/src/core/na-tokens.h
new file mode 100644
index 0000000..c54bf99
--- /dev/null
+++ b/src/core/na-tokens.h
@@ -0,0 +1,91 @@
+/*
+ * Nautilus Actions
+ * A Nautilus extension which offers configurable context menu modules.
+ *
+ * Copyright (C) 2005 The GNOME Foundation
+ * Copyright (C) 2006, 2007, 2008 Frederic Ruaudel and others (see AUTHORS)
+ * Copyright (C) 2009, 2010 Pierre Wieser and others (see AUTHORS)
+ *
+ * This Program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this Library; see the file COPYING.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place,
+ * Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Authors:
+ *   Frederic Ruaudel <grumz grumz net>
+ *   Rodrigo Moya <rodrigo gnome-db org>
+ *   Pierre Wieser <pwieser trychlos org>
+ *   ... and many others (see AUTHORS)
+ */
+
+#ifndef __CORE_NA_TOKENS_H__
+#define __CORE_NA_TOKENS_H__
+
+/**
+ * SECTION: na_tokens
+ * @short_description: #NATokens class definition.
+ * @include: core/na-tokens.h
+ *
+ * The #NATokens class manages the tokens which are to be replaced with
+ * elements of the current selection at runtime.
+ *
+ * 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 tokenss 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()
+ * - nautilus-actions/nact/nact-icommand-tab.c:parse_parameters()
+ * - nautilus-actions/nact/nautilus-actions-config-tool.ui:LegendDialog
+ */
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define NA_TOKENS_TYPE					( na_tokens_get_type())
+#define NA_TOKENS( object )				( G_TYPE_CHECK_INSTANCE_CAST( object, NA_TOKENS_TYPE, NATokens ))
+#define NA_TOKENS_CLASS( klass )		( G_TYPE_CHECK_CLASS_CAST( klass, NA_TOKENS_TYPE, NATokensClass ))
+#define NA_IS_TOKENS( object )			( G_TYPE_CHECK_INSTANCE_TYPE( object, NA_TOKENS_TYPE ))
+#define NA_IS_TOKENS_CLASS( klass )		( G_TYPE_CHECK_CLASS_TYPE(( klass ), NA_TOKENS_TYPE ))
+#define NA_TOKENS_GET_CLASS( object )	( G_TYPE_INSTANCE_GET_CLASS(( object ), NA_TOKENS_TYPE, NATokensClass ))
+
+typedef struct NATokensPrivate      NATokensPrivate;
+
+typedef struct {
+	GObject          parent;
+	NATokensPrivate *private;
+}
+	NATokens;
+
+typedef struct NATokensClassPrivate NATokensClassPrivate;
+
+typedef struct {
+	GObjectClass          parent;
+	NATokensClassPrivate *private;
+}
+	NATokensClass;
+
+GType     na_tokens_get_type( void );
+
+NATokens *na_tokens_new_from_selection( GList *selection );
+
+gchar    *na_tokens_parse_parameters( const NATokens *tokens, const gchar *string, gboolean utf8 );
+
+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 5e1bccc..e1c3048 100644
--- a/src/plugin-menu/nautilus-actions.c
+++ b/src/plugin-menu/nautilus-actions.c
@@ -40,6 +40,7 @@
 #include <libnautilus-extension/nautilus-file-info.h>
 #include <libnautilus-extension/nautilus-menu-provider.h>
 
+#include <api/na-core-utils.h>
 #include <api/na-object-api.h>
 
 #include <core/na-pivot.h>
@@ -47,6 +48,7 @@
 #include <core/na-iprefs.h>
 #include <core/na-ipivot-consumer.h>
 #include <core/na-selected-info.h>
+#include <core/na-tokens.h>
 
 #include "nautilus-actions.h"
 
@@ -86,8 +88,10 @@ static GList            *menu_provider_get_background_items( NautilusMenuProvide
 static GList            *menu_provider_get_file_items( NautilusMenuProvider *provider, GtkWidget *window, GList *files );
 static GList            *menu_provider_get_toolbar_items( NautilusMenuProvider *provider, GtkWidget *window, NautilusFileInfo *current_folder );
 
-static GList            *get_file_or_background_items( NautilusActions *plugin, guint target, GList *selection );
-static GList            *build_nautilus_menus( NautilusActions *plugin, GList *tree, guint target, GList *files );
+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 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_menu( NAObjectMenu *menu, GList *subitems );
@@ -417,8 +421,7 @@ menu_provider_get_background_items( NautilusMenuProvider *provider, GtkWidget *w
 
 		selected = na_selected_info_get_list_from_item( current_folder );
 
-		nautilus_menus_list = get_file_or_background_items(
-				NAUTILUS_ACTIONS( provider ), ITEM_TARGET_LOCATION, selected );
+		nautilus_menus_list = get_menus_items( NAUTILUS_ACTIONS( provider ), ITEM_TARGET_LOCATION, selected );
 
 		na_selected_info_free_list( selected );
 	}
@@ -453,8 +456,7 @@ menu_provider_get_file_items( NautilusMenuProvider *provider, GtkWidget *window,
 
 		selected = na_selected_info_get_list_from_list(( GList * ) files );
 
-		nautilus_menus_list = get_file_or_background_items(
-				NAUTILUS_ACTIONS( provider ), ITEM_TARGET_SELECTION, selected );
+		nautilus_menus_list = get_menus_items( NAUTILUS_ACTIONS( provider ), ITEM_TARGET_SELECTION, selected );
 
 		na_selected_info_free_list( selected );
 	}
@@ -474,7 +476,6 @@ menu_provider_get_toolbar_items( NautilusMenuProvider *provider, GtkWidget *wind
 	GList *nautilus_menus_list = NULL;
 	gchar *uri;
 	GList *selected;
-	GList *pivot_tree;
 
 	g_return_val_if_fail( NAUTILUS_IS_ACTIONS( provider ), NULL );
 
@@ -485,12 +486,9 @@ menu_provider_get_toolbar_items( NautilusMenuProvider *provider, GtkWidget *wind
 				thisfn, ( void * ) provider, ( void * ) window, ( void * ) current_folder, uri );
 		g_free( uri );
 
-		pivot_tree = na_pivot_get_items( NAUTILUS_ACTIONS( provider )->private->pivot );
-
 		selected = na_selected_info_get_list_from_item( current_folder );
 
-		nautilus_menus_list = build_nautilus_menus(
-				NAUTILUS_ACTIONS( provider ), pivot_tree, ITEM_TARGET_TOOLBAR, selected );
+		nautilus_menus_list = get_menus_items( NAUTILUS_ACTIONS( provider ), ITEM_TARGET_TOOLBAR, selected );
 
 		na_selected_info_free_list( selected );
 	}
@@ -499,38 +497,140 @@ menu_provider_get_toolbar_items( NautilusMenuProvider *provider, GtkWidget *wind
 }
 
 static GList *
-get_file_or_background_items( NautilusActions *plugin, guint target, GList *selection )
+get_menus_items( NautilusActions *plugin, guint target, GList *selection )
 {
 	GList *menus_list;
-	GList *pivot_tree;
+	NATokens *tokens;
+	GList *pivot_tree, *copy_tree;
 	gboolean root_menu;
 	gboolean add_about;
 
 	g_return_val_if_fail( NA_IS_PIVOT( plugin->private->pivot ), NULL );
 
+	tokens = na_tokens_new_from_selection( selection );
 	pivot_tree = na_pivot_get_items( plugin->private->pivot );
+	copy_tree = expand_tokens( pivot_tree, tokens );
 
-	menus_list = build_nautilus_menus( plugin, pivot_tree, target, selection );
+	menus_list = build_nautilus_menus( plugin, pivot_tree, target, selection, tokens );
 
-	root_menu = na_iprefs_read_bool( NA_IPREFS( plugin->private->pivot ), IPREFS_CREATE_ROOT_MENU, FALSE );
-	if( root_menu ){
-		menus_list = create_root_menu( plugin, menus_list );
-	}
+	na_object_unref_items( copy_tree );
+	g_object_unref( tokens );
+
+	if( target != ITEM_TARGET_TOOLBAR ){
+
+		root_menu = na_iprefs_read_bool( NA_IPREFS( plugin->private->pivot ), IPREFS_CREATE_ROOT_MENU, FALSE );
+		if( root_menu ){
+			menus_list = create_root_menu( plugin, menus_list );
+		}
 
-	add_about = na_iprefs_read_bool( NA_IPREFS( plugin->private->pivot ), IPREFS_ADD_ABOUT_ITEM, TRUE );
-	if( add_about ){
-		menus_list = add_about_item( plugin, menus_list );
+		add_about = na_iprefs_read_bool( NA_IPREFS( plugin->private->pivot ), IPREFS_ADD_ABOUT_ITEM, TRUE );
+		if( add_about ){
+			menus_list = add_about_item( plugin, menus_list );
+		}
 	}
 
 	return( menus_list );
 }
 
 /*
+ * create a copy of the tree where all fields which may embed parameters
+ * have been expanded
+ */
+static GList *
+expand_tokens( GList *pivot_tree, NATokens *tokens )
+{
+	GList *tree, *it;
+
+	tree = NULL;
+
+	for( it = pivot_tree ; it ; it = it->next ){
+		NAObjectItem *item = NA_OBJECT_ITEM( na_object_duplicate( it->data ));
+		tree = g_list_prepend( tree, expand_tokens_item( item, tokens ));
+	}
+
+	return( g_list_reverse( tree ));
+}
+
+static NAObjectItem *
+expand_tokens_item( NAObjectItem *item, NATokens *tokens )
+{
+	gchar *old, *new;
+	GSList *subitems_slist, *its, *new_slist;
+	GList *subitems, *it, *new_list;
+
+	old = na_object_get_label( item );
+	new = na_tokens_parse_parameters( tokens, old, TRUE );
+	na_object_set_label( item, new );
+	g_free( old );
+	g_free( new );
+
+	old = na_object_get_tooltip( item );
+	new = na_tokens_parse_parameters( tokens, old, TRUE );
+	na_object_set_tooltip( item, new );
+	g_free( old );
+	g_free( new );
+
+	old = na_object_get_icon( item );
+	new = na_tokens_parse_parameters( tokens, old, TRUE );
+	na_object_set_icon( item, new );
+	g_free( old );
+	g_free( new );
+
+	if( NA_IS_OBJECT_ACTION( item )){
+		old = na_object_get_toolbar_label( item );
+		new = na_tokens_parse_parameters( tokens, old, TRUE );
+		na_object_set_toolbar_label( item, new );
+		g_free( old );
+		g_free( new );
+	}
+
+	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 );
+		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 );
+
+	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 );
+
+			old = na_object_get_parameters( it->data );
+			new = na_tokens_parse_parameters( tokens, old, FALSE );
+			na_object_set_parameters( it->data, new );
+			g_free( old );
+			g_free( new );
+		}
+	}
+
+	return( item );
+}
+
+/*
  * when building a menu for the toolbar, do not use menus hierarchy
  * files is a GList of NASelectedInfo items
+ *
+ * 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
+ * re-checked for validity !
  */
 static GList *
-build_nautilus_menus( NautilusActions *plugin, GList *tree, guint target, GList *files )
+build_nautilus_menus( NautilusActions *plugin, GList *tree, guint target, GList *files, NATokens *tokens )
 {
 	static const gchar *thisfn = "nautilus_actions_build_nautilus_menus";
 	GList *menus_list = NULL;
@@ -569,7 +669,7 @@ build_nautilus_menus( NautilusActions *plugin, GList *tree, guint target, GList
 		 */
 		if( NA_IS_OBJECT_MENU( it->data )){
 			subitems = na_object_get_items( it->data );
-			submenu = build_nautilus_menus( plugin, subitems, target, files );
+			submenu = build_nautilus_menus( plugin, subitems, target, files, tokens );
 			/*g_debug( "%s: submenu has %d items", thisfn, g_list_length( submenu ));*/
 
 			if( submenu ){



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