[nautilus-actions] Build a hierarchical menu for Nautilus plugin



commit 89a7f09b5532c742805c8f0c207b2ae48890814c
Author: Pierre Wieser <pwieser trychlos org>
Date:   Wed Sep 30 20:10:45 2009 +0200

    Build a hierarchical menu for Nautilus plugin
    
    Also defines the NAIAbout interface to be able to provide the actual application name at runtime.

 ChangeLog                     |   15 ++
 src/common/Makefile.am        |    4 +-
 src/common/na-about.c         |  127 --------------
 src/common/na-about.h         |   50 ------
 src/common/na-iabout.c        |  234 ++++++++++++++++++++++++++
 src/common/na-iabout.h        |   88 ++++++++++
 src/nact/nact-application.c   |    4 +-
 src/nact/nact-main-menubar.c  |    8 +-
 src/nact/nact-main-window.c   |   34 ++++
 src/plugin/nautilus-actions.c |  368 +++++++++++++++++++++++++++++------------
 10 files changed, 636 insertions(+), 296 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 74e5056..e22b55a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,18 @@
+2009-09-30 Pierre Wieser <pwieser trychlos org>
+
+	* src/common/na-about.c: Renamed as na-iabout.c.
+	* src/common/na-about.h: Renamed as na-iabout.h.
+	Defines the NAIAbout interface to let the implementor provides
+	the actual application name.
+
+	* src/common/Makefile.am:
+	* src/nact/nact-application.c:
+	* src/nact/nact-main-menubar.c:
+	* src/nact/nact-main-window.c: Updated accordingly.
+
+	* src/plugin/nautilus-actions.c:
+	Builds a hierachical menu for Nautilus context menu.
+
 2009-09-28 Pierre Wieser <pwieser trychlos org>
 
 	* data/nautilus-actions.schemas.in:
diff --git a/src/common/Makefile.am b/src/common/Makefile.am
index abf6560..382a81a 100644
--- a/src/common/Makefile.am
+++ b/src/common/Makefile.am
@@ -35,8 +35,6 @@ AM_CPPFLAGS += \
 	$(NULL)
 
 libnact_la_SOURCES = \
-	na-about.c									\
-	na-about.h									\
 	na-gconf-keys-base.h						\
 	na-gconf-keys-schemas.h						\
 	na-gconf-monitor.c							\
@@ -48,6 +46,8 @@ libnact_la_SOURCES = \
 	na-gconf-utils.h							\
 	na-gnome-vfs-uri.c							\
 	na-gnome-vfs-uri.h							\
+	na-iabout.c									\
+	na-iabout.h									\
 	na-iduplicable.c							\
 	na-iduplicable.h							\
 	na-iio-provider.c							\
diff --git a/src/common/na-iabout.c b/src/common/na-iabout.c
new file mode 100644
index 0000000..8427bfb
--- /dev/null
+++ b/src/common/na-iabout.c
@@ -0,0 +1,234 @@
+/*
+ * 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 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 <glib/gi18n.h>
+
+#include "na-iabout.h"
+
+/* private interface data
+ */
+struct NAIAboutInterfacePrivate {
+	void *empty;						/* so that gcc -pedantic is happy */
+};
+
+static gboolean st_initialized = FALSE;
+static gboolean st_finalized = FALSE;
+
+static GType      register_type( void );
+static void       interface_base_init( NAIAboutInterface *klass );
+static void       interface_base_finalize( NAIAboutInterface *klass );
+
+static gchar     *v_get_application_name( NAIAbout *instance );
+static GtkWindow *v_get_toplevel( NAIAbout *instance );
+
+GType
+na_iabout_get_type( void )
+{
+	static GType iface_type = 0;
+
+	if( !iface_type ){
+		iface_type = register_type();
+	}
+
+	return( iface_type );
+}
+
+static GType
+register_type( void )
+{
+	static const gchar *thisfn = "na_iabout_register_type";
+	GType type;
+
+	static const GTypeInfo info = {
+		sizeof( NAIAboutInterface ),
+		( GBaseInitFunc ) interface_base_init,
+		( GBaseFinalizeFunc ) interface_base_finalize,
+		NULL,
+		NULL,
+		NULL,
+		0,
+		0,
+		NULL
+	};
+
+	g_debug( "%s", thisfn );
+
+	type = g_type_register_static( G_TYPE_INTERFACE, "NAIAbout", &info, 0 );
+
+	g_type_interface_add_prerequisite( type, G_TYPE_OBJECT );
+
+	return( type );
+}
+
+static void
+interface_base_init( NAIAboutInterface *klass )
+{
+	static const gchar *thisfn = "na_iabout_interface_base_init";
+
+	if( !st_initialized ){
+
+		g_debug( "%s: klass=%p", thisfn, ( void * ) klass );
+
+		klass->private = g_new0( NAIAboutInterfacePrivate, 1 );
+
+		st_initialized = TRUE;
+	}
+}
+
+static void
+interface_base_finalize( NAIAboutInterface *klass )
+{
+	static const gchar *thisfn = "na_iabout_interface_base_finalize";
+
+	if( !st_finalized ){
+
+		st_finalized = TRUE;
+
+		g_debug( "%s: klass=%p", thisfn, ( void * ) klass );
+
+		g_free( klass->private );
+	}
+}
+
+static gchar *
+v_get_application_name( NAIAbout *instance )
+{
+	if( NA_IABOUT_GET_INTERFACE( instance )->get_application_name ){
+		return( NA_IABOUT_GET_INTERFACE( instance )->get_application_name( instance ));
+	}
+
+	return( NULL );
+}
+
+static GtkWindow *
+v_get_toplevel( NAIAbout *instance )
+{
+	if( NA_IABOUT_GET_INTERFACE( instance )->get_toplevel ){
+		return( NA_IABOUT_GET_INTERFACE( instance )->get_toplevel( instance ));
+	}
+
+	return( NULL );
+}
+
+/* TODO: make the website url and the mail addresses clickables
+ */
+/**
+ * na_iabout_display:
+ * @instance: the #NAIAbout implementor.
+ *
+ * Displays the About dialog.
+ */
+void
+na_iabout_display( NAIAbout *instance )
+{
+	static const gchar *thisfn = "na_iabout_display";
+	gchar *application_name;
+	gchar *icon_name, *license_i18n;
+	GtkWindow *toplevel;
+
+	static const gchar *artists[] = {
+		N_( "Ulisse Perusin <uli peru gmail com>" ),
+		NULL
+	};
+
+	static const gchar *authors[] = {
+		N_( "Frederic Ruaudel <grumz grumz net>" ),
+		N_( "Rodrigo Moya <rodrigo gnome-db org>" ),
+		N_( "Pierre Wieser <pwieser trychlos org>" ),
+		NULL
+	};
+
+	static const gchar *documenters[] = {
+		NULL
+	};
+
+	static gchar *license[] = {
+		N_( "Nautilus Actions Configuration Tool 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." ),
+		N_( "Nautilus Actions Configuration Tool 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." ),
+		N_( "You should have received a copy of the GNU General Public License along "
+			"with Nautilus Actions Configuration Tool ; if not, write to the Free "
+			"Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, "
+			"MA 02110-1301, USA." ),
+		NULL
+	};
+
+	g_debug( "%s: instance=%p (%s)", thisfn, ( void * ) instance, G_OBJECT_TYPE_NAME( instance ));
+	g_return_if_fail( NA_IS_IABOUT( instance ));
+
+	if( st_initialized && !st_finalized ){
+
+		application_name = v_get_application_name( instance );
+		toplevel = v_get_toplevel( instance );
+
+		icon_name = na_iabout_get_icon_name();
+
+		license_i18n = g_strjoinv( "\n\n", license );
+
+		gtk_show_about_dialog( toplevel,
+				"artists", artists,
+				"authors", authors,
+				"comments", _( "A graphical interface to create and edit your Nautilus actions." ),
+				"copyright", _( "Copyright \xc2\xa9 2005-2007 Frederic Ruaudel <grumz grumz net>\nCopyright \xc2\xa9 2009 Pierre Wieser <pwieser trychlos org>" ),
+				"documenters", documenters,
+				"license", license_i18n,
+				"logo-icon-name", icon_name,
+				"program-name", application_name,
+				"translator-credits", _( "The GNOME Translation Project <gnome-i18n gnome org>" ),
+				"version", PACKAGE_VERSION,
+				"website", "http://www.nautilus-actions.org";,
+				"wrap-license", TRUE,
+				NULL );
+
+		g_free( application_name );
+		g_free( license_i18n );
+		g_free( icon_name );
+	}
+}
+
+/**
+ * na_iabout_get_icon_name:
+ *
+ * Returns the name of the default icon for the application.
+ */
+gchar *
+na_iabout_get_icon_name( void )
+{
+	return( g_strdup( PACKAGE ));
+}
diff --git a/src/common/na-iabout.h b/src/common/na-iabout.h
new file mode 100644
index 0000000..0dda955
--- /dev/null
+++ b/src/common/na-iabout.h
@@ -0,0 +1,88 @@
+/*
+ * 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 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 __NA_IABOUT_H__
+#define __NA_IABOUT_H__
+
+/**
+ * SECTION: na_iabout
+ * @short_description: NAIAbout interface definition.
+ * @include: common/na-iabout.h
+ *
+ * This interface displays the 'About Nautilus Actions' dialog box.
+ * The application name may be provided by the implementor.
+ */
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define NA_IABOUT_TYPE						( na_iabout_get_type())
+#define NA_IABOUT( object )					( G_TYPE_CHECK_INSTANCE_CAST( object, NA_IABOUT_TYPE, NAIAbout ))
+#define NA_IS_IABOUT( object )				( G_TYPE_CHECK_INSTANCE_TYPE( object, NA_IABOUT_TYPE ))
+#define NA_IABOUT_GET_INTERFACE( instance )	( G_TYPE_INSTANCE_GET_INTERFACE(( instance ), NA_IABOUT_TYPE, NAIAboutInterface ))
+
+typedef struct NAIAbout NAIAbout;
+
+typedef struct NAIAboutInterfacePrivate NAIAboutInterfacePrivate;
+
+typedef struct {
+	GTypeInterface            parent;
+	NAIAboutInterfacePrivate *private;
+
+	/**
+	 * get_application_name:
+	 * @iabout: this #NAIAbout implementor.
+	 *
+	 * Returns the application name as a newly allocated string.
+	 *
+	 * The application name will be g_free() by the interface.
+	 */
+	gchar *     ( *get_application_name )( NAIAbout *instance );
+
+	/**
+	 * get_toplevel:
+	 * @iabout: this #NAIAbout implementor.
+	 *
+	 * Returns the toplevel parent of the displayed dialog box.
+	 */
+	GtkWindow * ( *get_toplevel )        ( NAIAbout *instance );
+}
+	NAIAboutInterface;
+
+GType  na_iabout_get_type( void );
+
+void   na_iabout_display( NAIAbout *instance );
+
+gchar *na_iabout_get_icon_name( void );
+
+G_END_DECLS
+
+#endif /* __NA_IABOUT_H__ */
diff --git a/src/nact/nact-application.c b/src/nact/nact-application.c
index ee36a5e..1c1859f 100644
--- a/src/nact/nact-application.c
+++ b/src/nact/nact-application.c
@@ -35,7 +35,7 @@
 #include <glib/gi18n.h>
 #include <gtk/gtk.h>
 
-#include <common/na-about.h>
+#include <common/na-iabout.h>
 #include <common/na-iduplicable.h>
 #include <common/na-ipivot-consumer.h>
 
@@ -383,7 +383,7 @@ appli_get_icon_name( BaseApplication *application )
 
 	g_debug( "%s: application=%p", thisfn, ( void * ) application );
 
-	return( na_about_get_icon_name());
+	return( na_iabout_get_icon_name());
 }
 
 static gchar *
diff --git a/src/nact/nact-main-menubar.c b/src/nact/nact-main-menubar.c
index c55a33d..c715ef9 100644
--- a/src/nact/nact-main-menubar.c
+++ b/src/nact/nact-main-menubar.c
@@ -34,10 +34,10 @@
 
 #include <glib/gi18n.h>
 
-#include <common/na-about.h>
 #include <common/na-object-api.h>
 #include <common/na-object-action.h>
 #include <common/na-object-menu.h>
+#include <common/na-iabout.h>
 #include <common/na-ipivot-consumer.h>
 
 #include "base-iprefs.h"
@@ -693,11 +693,7 @@ on_help_activated( GtkAction *gtk_action, NactMainWindow *window )
 static void
 on_about_activated( GtkAction *gtk_action, NactMainWindow *window )
 {
-	GtkWindow *toplevel;
-
-	toplevel = base_window_get_toplevel( BASE_WINDOW( window ));
-
-	na_about_display( G_OBJECT( toplevel ));
+	na_iabout_display( NA_IABOUT( window ));
 }
 
 static void
diff --git a/src/nact/nact-main-window.c b/src/nact/nact-main-window.c
index 282f496..624e24c 100644
--- a/src/nact/nact-main-window.c
+++ b/src/nact/nact-main-window.c
@@ -39,6 +39,7 @@
 
 #include <common/na-object-api.h>
 #include <common/na-pivot.h>
+#include <common/na-iabout.h>
 #include <common/na-iio-provider.h>
 #include <common/na-ipivot-consumer.h>
 #include <common/na-iprefs.h>
@@ -122,6 +123,7 @@ static void     iaction_tab_iface_init( NactIActionTabInterface *iface );
 static void     icommand_tab_iface_init( NactICommandTabInterface *iface );
 static void     iconditions_tab_iface_init( NactIConditionsTabInterface *iface );
 static void     iadvanced_tab_iface_init( NactIAdvancedTabInterface *iface );
+static void     iabout_iface_init( NAIAboutInterface *iface );
 static void     ipivot_consumer_iface_init( NAIPivotConsumerInterface *iface );
 static void     iprefs_base_iface_init( BaseIPrefsInterface *iface );
 static void     instance_init( GTypeInstance *instance, gpointer klass );
@@ -152,6 +154,8 @@ static void     ipivot_consumer_on_display_about_changed( NAIPivotConsumer *inst
 static void     ipivot_consumer_on_display_order_changed( NAIPivotConsumer *instance, gint order_mode );
 static void     reload( NactMainWindow *window );
 
+static gchar   *iabout_get_application_name( NAIAbout *instance );
+
 GType
 nact_main_window_get_type( void )
 {
@@ -212,6 +216,12 @@ register_type( void )
 		NULL
 	};
 
+	static const GInterfaceInfo iabout_iface_info = {
+		( GInterfaceInitFunc ) iabout_iface_init,
+		NULL,
+		NULL
+	};
+
 	static const GInterfaceInfo ipivot_consumer_iface_info = {
 		( GInterfaceInitFunc ) ipivot_consumer_iface_init,
 		NULL,
@@ -238,6 +248,8 @@ register_type( void )
 
 	g_type_add_interface_static( type, NACT_IADVANCED_TAB_TYPE, &iadvanced_tab_iface_info );
 
+	g_type_add_interface_static( type, NA_IABOUT_TYPE, &iabout_iface_info );
+
 	g_type_add_interface_static( type, NA_IPIVOT_CONSUMER_TYPE, &ipivot_consumer_iface_info );
 
 	g_type_add_interface_static( type, BASE_IPREFS_TYPE, &iprefs_base_iface_info );
@@ -376,6 +388,16 @@ iadvanced_tab_iface_init( NactIAdvancedTabInterface *iface )
 }
 
 static void
+iabout_iface_init( NAIAboutInterface *iface )
+{
+	static const gchar *thisfn = "nact_main_window_iabout_iface_init";
+
+	g_debug( "%s: iface=%p", thisfn, ( void * ) iface );
+
+	iface->get_application_name = iabout_get_application_name;
+}
+
+static void
 ipivot_consumer_iface_init( NAIPivotConsumerInterface *iface )
 {
 	static const gchar *thisfn = "nact_main_window_ipivot_consumer_iface_init";
@@ -1178,3 +1200,15 @@ ipivot_consumer_on_display_order_changed( NAIPivotConsumer *instance, gint order
 
 	nact_iactions_list_display_order_change( NACT_IACTIONS_LIST( instance ), order_mode );
 }
+
+static gchar *
+iabout_get_application_name( NAIAbout *instance )
+{
+	BaseApplication *application;
+
+	g_return_val_if_fail( NA_IS_IABOUT( instance ), NULL );
+	g_return_val_if_fail( BASE_IS_WINDOW( instance ), NULL );
+
+	application = base_window_get_application( BASE_WINDOW( instance ));
+	return( base_application_get_application_name( application ));
+}
diff --git a/src/plugin/nautilus-actions.c b/src/plugin/nautilus-actions.c
index 766798f..83f5253 100644
--- a/src/plugin/nautilus-actions.c
+++ b/src/plugin/nautilus-actions.c
@@ -38,11 +38,12 @@
 #include <libnautilus-extension/nautilus-file-info.h>
 #include <libnautilus-extension/nautilus-menu-provider.h>
 
-#include <common/na-about.h>
 #include <common/na-object-api.h>
+#include <common/na-object-menu.h>
 #include <common/na-object-action.h>
 #include <common/na-object-profile.h>
 #include <common/na-pivot.h>
+#include <common/na-iabout.h>
 #include <common/na-iprefs.h>
 #include <common/na-ipivot-consumer.h>
 
@@ -66,6 +67,7 @@ static GType         st_actions_type = 0;
 
 static void              class_init( NautilusActionsClass *klass );
 static void              menu_provider_iface_init( NautilusMenuProviderIface *iface );
+static void              iabout_iface_init( NAIAboutInterface *iface );
 static void              ipivot_consumer_iface_init( NAIPivotConsumerInterface *iface );
 static void              instance_init( GTypeInstance *instance, gpointer klass );
 static void              instance_dispose( GObject *object );
@@ -73,11 +75,19 @@ static void              instance_finalize( GObject *object );
 
 static GList            *get_background_items( NautilusMenuProvider *provider, GtkWidget *window, NautilusFileInfo *current_folder );
 static GList            *get_file_items( NautilusMenuProvider *provider, GtkWidget *window, GList *files );
-static NautilusMenuItem *create_menu_item( NAObjectAction *action, NAObjectProfile *profile, GList *files );
-/*static NautilusMenuItem *create_sub_menu( NautilusMenu **menu );*/
-static void              add_about_item( NautilusMenu *menu );
+static GList            *build_nautilus_menus( NautilusActions *plugin, GList *tree, GList *files );
+static NAObjectProfile  *is_action_candidate( NautilusActions *plugin, NAObjectAction *action, GList *files );
+static NautilusMenuItem *create_item_from_profile( NAObjectProfile *profile, GList *files );
+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              execute_action( NautilusMenuItem *item, NAObjectProfile *profile );
 
+static GList            *add_about_item( NautilusActions *plugin, GList *nautilus_menu );
+static gchar            *iabout_get_application_name( NAIAbout *instance );
+static void              execute_about( NautilusMenuItem *item, NautilusActions *plugin );
+
 static void              actions_changed_handler( NAIPivotConsumer *instance, gpointer user_data );
 static void              display_about_changed_handler( NAIPivotConsumer *instance, gboolean enabled );
 static void              display_order_changed_handler( NAIPivotConsumer *instance, gint order_mode );
@@ -112,6 +122,12 @@ nautilus_actions_register_type( GTypeModule *module )
 		NULL
 	};
 
+	static const GInterfaceInfo iabout_iface_info = {
+		( GInterfaceInitFunc ) iabout_iface_init,
+		NULL,
+		NULL
+	};
+
 	static const GInterfaceInfo ipivot_consumer_iface_info = {
 		( GInterfaceInitFunc ) ipivot_consumer_iface_init,
 		NULL,
@@ -125,6 +141,8 @@ nautilus_actions_register_type( GTypeModule *module )
 
 	g_type_module_add_interface( module, st_actions_type, NAUTILUS_TYPE_MENU_PROVIDER, &menu_provider_iface_info );
 
+	g_type_module_add_interface( module, st_actions_type, NA_IABOUT_TYPE, &iabout_iface_info );
+
 	g_type_module_add_interface( module, st_actions_type, NA_IPIVOT_CONSUMER_TYPE, &ipivot_consumer_iface_info );
 }
 
@@ -157,6 +175,16 @@ menu_provider_iface_init( NautilusMenuProviderIface *iface )
 }
 
 static void
+iabout_iface_init( NAIAboutInterface *iface )
+{
+	static const gchar *thisfn = "nautilus_actions_iabout_iface_init";
+
+	g_debug( "%s: iface=%p", thisfn, ( void * ) iface );
+
+	iface->get_application_name = iabout_get_application_name;
+}
+
+static void
 ipivot_consumer_iface_init( NAIPivotConsumerInterface *iface )
 {
 	static const gchar *thisfn = "nautilus_actions_ipivot_consumer_iface_init";
@@ -271,15 +299,10 @@ static GList *
 get_file_items( NautilusMenuProvider *provider, GtkWidget *window, GList *files )
 {
 	static const gchar *thisfn = "nautilus_actions_get_file_items";
+	GList *nautilus_menus_list = NULL;
 	NautilusActions *self;
-	GList *items = NULL;
-	GList *profiles, *ia, *ip;
-	NautilusMenu *menu = NULL;
-	NautilusMenuItem *item;
-	GList *tree = NULL;
-	gchar *label, *uuid;
-	gint submenus = 0;
-	gboolean add_about;
+	GList *pivot_tree;
+ 	gboolean add_about;
 
 	g_debug( "%s: provider=%p, window=%p, files=%p, count=%d",
 			thisfn, ( void * ) provider, ( void * ) window, ( void * ) files, g_list_length( files ));
@@ -287,97 +310,131 @@ get_file_items( NautilusMenuProvider *provider, GtkWidget *window, GList *files
 	g_return_val_if_fail( NAUTILUS_IS_ACTIONS( provider ), NULL );
 	self = NAUTILUS_ACTIONS( provider );
 
+	/* no need to go further if there is no files in the list */
+	if( !g_list_length( files )){
+		return(( GList * ) NULL );
+	}
+
 	if( !self->private->dispose_has_run ){
 
-		/* no need to go further if there is no files in the list */
-		if( !g_list_length( files )){
-			return(( GList * ) NULL );
+		pivot_tree = na_pivot_get_items( self->private->pivot );
+
+		nautilus_menus_list = build_nautilus_menus( self, pivot_tree, files );
+		g_debug( "%s: menus has %d level zero items", thisfn, g_list_length( nautilus_menus_list ));
+
+		add_about = na_iprefs_should_add_about_item( NA_IPREFS( self->private->pivot ));
+		g_debug( "%s: add_about=%s", thisfn, add_about ? "True":"False" );
+		if( add_about ){
+			nautilus_menus_list = add_about_item( self, nautilus_menus_list );
 		}
+	}
 
-		if( !self->private->dispose_has_run ){
-			tree = na_pivot_get_items( self->private->pivot );
+	return( nautilus_menus_list );
+}
 
-			for( ia = tree ; ia ; ia = ia->next ){
+static GList *
+build_nautilus_menus( NautilusActions *plugin, GList *tree, GList *files )
+{
+	static const gchar *thisfn = "nautilus_actions_build_nautilus_menus";
+	GList *menus_list = NULL;
+	GList *subitems, *submenu;
+	GList *it;
+	NAObjectProfile *profile;
+	NautilusMenuItem *item;
+	gchar *label;
 
-				NAObjectAction *action = NA_OBJECT_ACTION( ia->data );
+	g_debug( "%s: plugin=%p, tree=%p, files=%p",
+			thisfn, ( void * ) plugin, ( void * ) tree, ( void * ) files );
 
-				if( !na_object_is_enabled( action )){
-					continue;
-				}
+	for( it = tree ; it ; it = it->next ){
 
-				label = na_object_get_label( action );
+		g_return_val_if_fail( NA_IS_OBJECT_ITEM( it->data ), NULL );
 
-				if( !label || !g_utf8_strlen( label, -1 )){
-					uuid = na_object_get_id( action );
-					g_warning( "%s: label null or empty for uuid=%s", thisfn, uuid );
-					g_free( uuid );
-					continue;
-				}
+		if( !na_object_is_enabled( it->data )){
+			continue;
+		}
 
-				g_debug( "%s: examining '%s' action", thisfn, label );
-				g_free( label );
+		label = na_object_get_label( it->data );
+		g_debug( "%s: %s - %s", thisfn, G_OBJECT_TYPE_NAME( it->data ), label );
+		g_free( label );
+
+		if( NA_IS_OBJECT_MENU( it->data )){
+			subitems = na_object_get_items( it->data );
+			submenu = build_nautilus_menus( plugin, subitems, files );
+			/*g_debug( "%s: submenu has %d items", thisfn, g_list_length( submenu ));*/
+			na_object_free_items( subitems );
+			if( submenu ){
+				item = create_item_from_menu( NA_OBJECT_MENU( it->data ), submenu );
+				menus_list = g_list_append( menus_list, item );
+			}
+			continue;
+		}
 
-				profiles = na_object_get_items( action );
+		g_return_val_if_fail( NA_IS_OBJECT_ACTION( it->data ), NULL );
 
-				for( ip = profiles ; ip ; ip = ip->next ){
+		profile = is_action_candidate( plugin, NA_OBJECT_ACTION( it->data ), files );
+		if( profile ){
+			item = create_item_from_profile( profile, files );
+			menus_list = g_list_append( menus_list, item );
+		}
+	}
 
-					NAObjectProfile *profile = NA_OBJECT_PROFILE( ip->data );
+	return( menus_list );
+}
 
-#ifdef NA_MAINTAINER_MODE
-					label = na_object_get_label( profile );
-					g_debug( "%s: examining '%s' profile", thisfn, label );
-					g_free( label );
-#endif
+/*
+ * could also be a NAObjectAction method - but this is not used elsewhere
+ */
+static NAObjectProfile *
+is_action_candidate( NautilusActions *plugin, NAObjectAction *action, GList *files )
+{
+	static const gchar *thisfn = "nautilus_actions_is_action_candidate";
+	NAObjectProfile *candidate = NULL;
+	gchar *action_label, *uuid;
+	gchar *profile_label;
+	GList *profiles, *ip;
+
+	action_label = na_object_get_label( action );
+
+	if( !action_label || !g_utf8_strlen( action_label, -1 )){
+		uuid = na_object_get_id( action );
+		g_warning( "%s: label null or empty for uuid=%s", thisfn, uuid );
+		g_free( uuid );
+		return( NULL );
+	}
 
-					if( na_object_profile_is_candidate( profile, files )){
-						item = create_menu_item( action, profile, files );
-						items = g_list_append( items, item );
+	profiles = na_object_get_items( action );
+	for( ip = profiles ; ip && !candidate ; ip = ip->next ){
 
-						/*if( have_submenu ){
-							if( !menu ){
-								items = g_list_append( items, create_sub_menu( &menu ));
-							}
-							nautilus_menu_append_item( menu, item );
+		NAObjectProfile *profile = NA_OBJECT_PROFILE( ip->data );
+		if( na_object_profile_is_candidate( profile, files )){
 
-						} else {
-						}*/
-						break;
-					}
-				}
+			profile_label = na_object_get_label( profile );
+			g_debug( "%s: selecting %s - %s", thisfn, action_label, profile_label );
+			g_free( profile_label );
 
-				na_object_free_items( profiles );
-			}
+			candidate = profile;
+ 		}
+ 	}
 
-			add_about = FALSE; /*na_iprefs_get_add_about_item( NA_IPREFS( self ));*/
-			if( submenus == 1 && add_about ){
-				add_about_item( menu );
-			}
-		}
-	}
+	g_free( action_label );
 
-	return( items );
+	return( candidate );
 }
 
 static NautilusMenuItem *
-create_menu_item( NAObjectAction *action, NAObjectProfile *profile, GList *files )
+create_item_from_profile( NAObjectProfile *profile, GList *files )
 {
-	static const gchar *thisfn = "nautilus_actions_create_menu_item";
 	NautilusMenuItem *item;
-	gchar *uuid, *name, *label, *tooltip, *icon_name;
+	NAObjectAction *action;
 	NAObjectProfile *dup4menu;
 
-	g_debug( "%s", thisfn );
+	action = na_object_profile_get_action( profile );
 
-	uuid = na_object_get_id( action );
-	name = g_strdup_printf( "NautilusActions::%s", uuid );
-	label = na_object_get_label( action );
-	tooltip = na_object_get_tooltip( action );
-	icon_name = na_object_item_get_verified_icon_name( NA_OBJECT_ITEM( action ));
+	item = create_menu_item( NA_OBJECT_ITEM( action ));
 
 	dup4menu = NA_OBJECT_PROFILE( na_object_duplicate( profile ));
 
-	item = nautilus_menu_item_new( name, label, tooltip, icon_name );
-
 	g_signal_connect_data( item,
 				"activate",
 				G_CALLBACK( execute_action ),
@@ -390,58 +447,63 @@ create_menu_item( NAObjectAction *action, NAObjectProfile *profile, GList *files
 			nautilus_file_info_list_copy( files ),
 			( GDestroyNotify ) nautilus_file_info_list_free );
 
-	g_free( icon_name );
-	g_free( tooltip );
-	g_free( label );
-	g_free( name );
-	g_free( uuid );
-
 	return( item );
 }
 
-/*static NautilusMenuItem *
-create_sub_menu( NautilusMenu **menu )
+/*
+ * note that each appended NautilusMenuItem is ref-ed by the NautilusMenu
+ * we can so safely release our own ref on subitems after this function
+ */
+static NautilusMenuItem *
+create_item_from_menu( NAObjectMenu *menu, GList *subitems )
 {
+	/*static const gchar *thisfn = "nautilus_actions_create_item_from_menu";*/
 	NautilusMenuItem *item;
 
-	gchar *icon_name = na_about_get_icon_name();
+	item = create_menu_item( NA_OBJECT_ITEM( menu ));
+	attach_submenu_to_item( item, subitems );
+	nautilus_menu_item_list_free( subitems );
+
+	/*g_debug( "%s: returning item=%p", thisfn, ( void * ) item );*/
+	return( item );
+}
 
-	item = nautilus_menu_item_new( "NautilusActionsExtensions",
-			_( "Nautilus-Actions extensions" ),
-			_( "A submenu which embeds the currently available Nautilus-Actions extensions" ),
-			icon_name );
+static NautilusMenuItem *
+create_menu_item( NAObjectItem *item )
+{
+	NautilusMenuItem *menu_item;
+	gchar *uuid, *name, *label, *tooltip, *icon;
 
-	if( menu ){
-		*menu = nautilus_menu_new();
-		nautilus_menu_item_set_submenu( item, *menu );
-	}
+	uuid = na_object_get_id( item );
+	name = g_strdup_printf( "%s-%s-%s", PACKAGE, G_OBJECT_TYPE_NAME( item ), uuid );
+	label = na_object_get_label( item );
+	/*g_debug( "nautilus_actions_create_menu_item: %s - %s", name, label );*/
+	tooltip = na_object_get_tooltip( item );
+	icon = na_object_get_icon( item );
 
-	g_free( icon_name );
+	menu_item = nautilus_menu_item_new( name, label, tooltip, icon );
 
-	return( item );
-}*/
+	g_free( icon );
+ 	g_free( tooltip );
+ 	g_free( label );
+ 	g_free( name );
+ 	g_free( uuid );
+
+	return( menu_item );
+}
 
 static void
-add_about_item( NautilusMenu *menu )
+attach_submenu_to_item( NautilusMenuItem *item, GList *subitems )
 {
-	gchar *icon_name = na_about_get_icon_name();
+	NautilusMenu *submenu;
+	GList *it;
 
-	NautilusMenuItem *item = nautilus_menu_item_new(
-			"AboutNautilusActions",
-			_( "About Nautilus Actions" ),
-			_( "Display information about Nautilus Actions" ),
-			icon_name );
+	submenu = nautilus_menu_new();
+	nautilus_menu_item_set_submenu( item, submenu );
 
-	g_signal_connect_data( item,
-				"activate",
-				G_CALLBACK( na_about_display ),
-				NULL,
-				NULL,
-				0 );
-
-	nautilus_menu_append_item( menu, item );
-
-	g_free( icon_name );
+	for( it = subitems ; it ; it = it->next ){
+		nautilus_menu_append_item( submenu, NAUTILUS_MENU_ITEM( it->data ));
+	}
 }
 
 static void
@@ -474,6 +536,94 @@ execute_action( NautilusMenuItem *item, NAObjectProfile *profile )
 
 }
 
+/*
+ * if there is a root submenu,
+ * then add the about item to the end of the first level of this menu
+ * else create a root submenu
+ */
+static GList *
+add_about_item( NautilusActions *plugin, GList *menu )
+{
+	static const gchar *thisfn = "nautilus_actions_add_about_item";
+	GList *nautilus_menu;
+	gboolean have_root_menu;
+	NautilusMenuItem *root_item;
+	NautilusMenuItem *about_item;
+	NautilusMenu *first;
+	gchar *icon;
+
+	g_debug( "%s: plugin=%p, menu=%p (%d items)",
+			thisfn, ( void * ) plugin, ( void * ) menu, g_list_length( menu ));
+
+	if( !menu || !g_list_length( menu )){
+		return( NULL );
+	}
+
+	icon = na_iabout_get_icon_name();
+	have_root_menu = FALSE;
+
+	if( g_list_length( menu ) == 1 ){
+		root_item = NAUTILUS_MENU_ITEM( menu->data );
+		g_object_get( G_OBJECT( root_item ), "menu", &first, NULL );
+		if( first ){
+			g_return_val_if_fail( NAUTILUS_IS_MENU( first ), NULL );
+			have_root_menu = TRUE;
+		}
+	}
+
+	if( have_root_menu ){
+		nautilus_menu = menu;
+
+	} else {
+		root_item = nautilus_menu_item_new( "NautilusActionsExtensions",
+				/* i18n: label of an automagic root submenu */
+				_( "Nautilus Actions" ),
+				/* i18n: tooltip of an automagic root submenu */
+				_( "A submenu which embeds the currently available Nautilus-Actions extensions" ),
+				icon );
+		attach_submenu_to_item( root_item, menu );
+		nautilus_menu = g_list_append( NULL, root_item );
+		g_object_get( G_OBJECT( root_item ), "menu", &first, NULL );
+	}
+
+	g_return_val_if_fail( g_list_length( nautilus_menu ) == 1, NULL );
+	g_return_val_if_fail( NAUTILUS_IS_MENU( first ), NULL );
+
+	about_item = nautilus_menu_item_new( "AboutNautilusActions",
+				_( "About Nautilus Actions" ),
+				_( "Display information about Nautilus Actions" ),
+				icon );
+
+	g_signal_connect_data( about_item,
+				"activate",
+				G_CALLBACK( execute_about ),
+				plugin,
+				NULL,
+				0 );
+
+	nautilus_menu_append_item( first, about_item );
+
+	g_free( icon );
+
+	return( nautilus_menu );
+}
+
+static gchar *
+iabout_get_application_name( NAIAbout *instance )
+{
+	/* i18n: title of the About dialog box, when seen from Nautilus file manager */
+	return( g_strdup( _( "Nautilus Actions" )));
+}
+
+static void
+execute_about( NautilusMenuItem *item, NautilusActions *plugin )
+{
+	g_return_if_fail( NAUTILUS_IS_ACTIONS( plugin ));
+	g_return_if_fail( NA_IS_IABOUT( plugin ));
+
+	na_iabout_display( NA_IABOUT( plugin ));
+}
+
 static void
 actions_changed_handler( NAIPivotConsumer *instance, gpointer user_data )
 {



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