? libnautilus-private/apps_nautilus_preferences.schemas
Index: ChangeLog
===================================================================
RCS file: /cvs/gnome/nautilus/ChangeLog,v
retrieving revision 1.5817
diff -u -r1.5817 ChangeLog
--- ChangeLog	19 May 2003 21:19:10 -0000	1.5817
+++ ChangeLog	20 May 2003 11:01:56 -0000
@@ -1,3 +1,19 @@
+2003-05-20  Jon Svendsen  <jon-sven@frisurf.no>
+
+	* acconfig.h: 
+	Template definition for startup notification
+
+	* configure.in: 
+	Check for startup-notification
+	
+	* libnautilus-private/nautilus-program-choosing: 
+	(sn_error_trap_push), (sn_error_trap_pop), 
+	(make_spawn_environment_for_sn_context), (free_startup_timeout),
+	(startup_timeout), (add_startup_timeout), (nautilus_launch_show_file),
+	(nautilus_launch_application): Add support for using startup 
+	notification for applications that claim to support it in their
+	application registry entry.
+
 2003-05-19  Christophe Merlet  <redfox@redfoxcenter.org>
 
 	* THANKS: Updated my email.
Index: acconfig.h
===================================================================
RCS file: /cvs/gnome/nautilus/acconfig.h,v
retrieving revision 1.30
diff -u -r1.30 acconfig.h
--- acconfig.h	2 Oct 2002 05:36:47 -0000	1.30
+++ acconfig.h	20 May 2003 11:01:56 -0000
@@ -21,3 +21,4 @@
 #undef bzread
 #undef bzwrite
 #define HAVE_GTK_MULTIHEAD /* needed for egg-screen-exec functions */
+#undef HAVE_STARTUP_NOTIFICATION
Index: configure.in
===================================================================
RCS file: /cvs/gnome/nautilus/configure.in,v
retrieving revision 1.517
diff -u -r1.517 configure.in
--- configure.in	19 May 2003 12:32:40 -0000	1.517
+++ configure.in	20 May 2003 11:01:57 -0000
@@ -19,6 +19,7 @@
 MEDUSA_REQUIRED=0.5.1
 RSVG_REQUIRED=2.0.1
 XML_REQUIRED=2.4.7
+STARTUP_NOTIFICATION_REQUIRED=0.5
 
 AC_SUBST(ART_REQUIRED)
 AC_SUBST(BONOBO_ACTIVATION_REQUIRED)
@@ -37,6 +38,7 @@
 AC_SUBST(MEDUSA_REQUIRED)
 AC_SUBST(RSVG_REQUIRED)
 AC_SUBST(XML_REQUIRED)
+AC_SUBST(STARTUP_NOTIFICATION_REQUIRED)
 
 dnl ===========================================================================
 
@@ -57,6 +59,20 @@
 AM_DISABLE_STATIC
 AC_LIBTOOL_WIN32_DLL
 AM_PROG_LIBTOOL
+AC_PATH_PROG(PKG_CONFIG, pkg-config, no)
+
+if $PKG_CONFIG --atleast-version $STARTUP_NOTIFICATION_REQUIRED libstartup-notification-1.0; then
+  echo "Building with libstartup-notification"
+  AC_DEFINE(HAVE_STARTUP_NOTIFICATION)
+  with_startup_notification=yes
+  STARTUP_NOTIFICATION_PACKAGE=libstartup-notification-1.0
+else
+  echo "***** WARNING: Building without libstartup-notification"
+  with_startup_notification=no
+  STARTUP_NOTIFICATION_PACKAGE=
+fi
+
+AC_SUBST(STARTUP_NOTIFICATION_PACKAGE)
 
 PKG_CHECK_MODULES(ALL, \
 	esound >= $ESOUND_REQUIRED \
@@ -74,7 +90,8 @@
 	libgnome-2.0 >= $GNOME_REQUIRED \
 	libgnomeui-2.0 >= $GNOME_UI_REQUIRED \
 	librsvg-2.0 >= $RSVG_REQUIRED \
-	libxml-2.0 >= $XML_REQUIRED)
+	libxml-2.0 >= $XML_REQUIRED \
+	$STARTUP_NOTIFICATION_PACKAGE)
 dnl ==========================================================================
 
 ALL_LINGUAS="am ar az be bg ca cs da de el en_GB es et eu fa fi fr ga gl he hi hu it ja ko lt lv mk ml mn ms nl nn no pl pt pt_BR ro ru sk sl sq sr sr@Latn sv ta th tr uk vi wa yi zh_CN zh_TW"
Index: libnautilus-private/nautilus-program-choosing.c
===================================================================
RCS file: /cvs/gnome/nautilus/libnautilus-private/nautilus-program-choosing.c,v
retrieving revision 1.66
diff -u -r1.66 nautilus-program-choosing.c
--- libnautilus-private/nautilus-program-choosing.c	28 Apr 2003 20:48:59 -0000	1.66
+++ libnautilus-private/nautilus-program-choosing.c	20 May 2003 11:01:58 -0000
@@ -29,6 +29,7 @@
 #include "nautilus-mime-actions.h"
 #include "nautilus-program-chooser.h"
 #include "nautilus-global-preferences.h"
+#include "nautilus-icon-factory.h"
 #include <libegg/egg-screen-exec.h>
 #include <eel/eel-glib-extensions.h>
 #include <eel/eel-gnome-extensions.h>
@@ -43,10 +44,18 @@
 #include <libgnome/gnome-desktop-item.h>
 #include <libgnome/gnome-url.h>
 #include <libgnomeui/gnome-uidefs.h>
+#include <libgnomevfs/gnome-vfs-application-registry.h>
 #include <libgnomevfs/gnome-vfs-mime-handlers.h>
 #include <libgnomevfs/gnome-vfs-utils.h>
 #include <stdlib.h>
 
+#ifdef HAVE_STARTUP_NOTIFICATION
+#define SN_API_NOT_YET_FROZEN
+#include <libsn/sn.h>
+#include <gdk/gdk.h>
+#include <gdk/gdkx.h>
+#endif
+
 typedef struct {
 	NautilusFile *file;
 	GtkWindow *parent_window;
@@ -541,6 +550,173 @@
 	g_free (message);
 }
 
+#ifdef HAVE_STARTUP_NOTIFICATION
+static void
+sn_error_trap_push (SnDisplay *display,
+		    Display   *xdisplay)
+{
+	gdk_error_trap_push ();
+}
+
+static void
+sn_error_trap_pop (SnDisplay *display,
+		   Display   *xdisplay)
+{
+	gdk_error_trap_pop ();
+}
+
+extern char **environ;
+
+static char **
+make_spawn_environment_for_sn_context (SnLauncherContext *sn_context,
+				       char             **envp)
+{
+	char **retval = NULL;
+	int    i, j;
+	int    desktop_startup_id_len;
+
+	if (envp == NULL)
+		envp = environ;
+	
+	for (i = 0; envp[i]; i++)
+		;
+
+	retval = g_new (char *, i + 2);
+
+	desktop_startup_id_len = strlen ("DESKTOP_STARTUP_ID");
+	
+	for (i = 0, j = 0; envp[i]; i++) {
+		if (strncmp (envp[i], "DESKTOP_STARTUP_ID", desktop_startup_id_len) != 0)
+		{
+			retval[j] = g_strdup (envp[i]);
+			++j;
+	        }
+	}
+
+	retval[j] = g_strdup_printf ("DESKTOP_STARTUP_ID=%s",
+				     sn_launcher_context_get_startup_id (sn_context));
+	++j;
+	retval[j] = NULL;
+
+	return retval;
+}
+
+/* This should be fairly long, as it's confusing to users if a startup
+ * ends when it shouldn't (it appears that the startup failed, and
+ * they have to relaunch the app). Also the timeout only matters when
+ * there are bugs and apps don't end their own startup sequence.
+ *
+ * This timeout is a "last resort" timeout that ignores whether the
+ * startup sequence has shown activity or not.  Metacity and the
+ * tasklist have smarter, and correspondingly able-to-be-shorter
+ * timeouts. The reason our timeout is dumb is that we don't monitor
+ * the sequence (don't use an SnMonitorContext)
+ */
+#define STARTUP_TIMEOUT_LENGTH (30 /* seconds */ * 1000)
+
+typedef struct
+{
+	GdkScreen *screen;
+	GSList *contexts;
+	guint timeout_id;
+} StartupTimeoutData;
+
+static void
+free_startup_timeout (void *data)
+{
+	StartupTimeoutData *std = data;
+
+	g_slist_foreach (std->contexts,
+			 (GFunc) sn_launcher_context_unref,
+			 NULL);
+	g_slist_free (std->contexts);
+
+	if (std->timeout_id != 0) {
+		g_source_remove (std->timeout_id);
+		std->timeout_id = 0;
+	}
+
+	g_free (std);
+}
+
+static gboolean
+startup_timeout (void *data)
+{
+	StartupTimeoutData *std = data;
+	GSList *tmp;
+	GTimeVal now;
+	int min_timeout;
+
+	min_timeout = STARTUP_TIMEOUT_LENGTH;
+	
+	g_get_current_time (&now);
+	
+	tmp = std->contexts;
+	while (tmp != NULL) {
+		SnLauncherContext *sn_context = tmp->data;
+		GSList *next = tmp->next;
+		long tv_sec, tv_usec;
+		double elapsed;
+		
+		sn_launcher_context_get_last_active_time (sn_context,
+							  &tv_sec, &tv_usec);
+
+		elapsed =
+			((((double)now.tv_sec - tv_sec) * G_USEC_PER_SEC +
+			  (now.tv_usec - tv_usec))) / 1000.0;
+
+		if (elapsed >= STARTUP_TIMEOUT_LENGTH) {
+			std->contexts = g_slist_remove (std->contexts,
+							sn_context);
+			sn_launcher_context_complete (sn_context);
+			sn_launcher_context_unref (sn_context);
+		} else {
+			min_timeout = MIN (min_timeout, (STARTUP_TIMEOUT_LENGTH - elapsed));
+		}
+		
+		tmp = next;
+	}
+
+	if (std->contexts == NULL) {
+		std->timeout_id = 0;
+	} else {
+		std->timeout_id = g_timeout_add (min_timeout,
+						 startup_timeout,
+						 std);
+	}
+
+	/* always remove this one, but we may have reinstalled another one. */
+	return FALSE;
+}
+
+static void
+add_startup_timeout (GdkScreen         *screen,
+		     SnLauncherContext *sn_context)
+{
+	StartupTimeoutData *data;
+
+	data = g_object_get_data (G_OBJECT (screen), "gnome-startup-data");
+	if (data == NULL) {
+		data = g_new (StartupTimeoutData, 1);
+		data->screen = screen;
+		data->contexts = NULL;
+		data->timeout_id = 0;
+		
+		g_object_set_data_full (G_OBJECT (screen), "gnome-startup-data",
+					data, free_startup_timeout);		
+	}
+
+	sn_launcher_context_ref (sn_context);
+	data->contexts = g_slist_prepend (data->contexts, sn_context);
+	
+	if (data->timeout_id == 0) {
+		data->timeout_id = g_timeout_add (STARTUP_TIMEOUT_LENGTH,
+						  startup_timeout,
+						  data);		
+	}
+}
+#endif /* HAVE_STARTUP_NOTIFICATION */
+
 /**
  * nautilus_launch_show_file:
  *
@@ -553,10 +729,14 @@
                                 GtkWindow    *parent_window)
 {
 	GnomeVFSResult result;
-	GnomeVFSMimeActionType action;
+	GnomeVFSMimeAction *action;
 	GdkScreen *screen;
 	char **envp;
 	char *uri, *uri_scheme;
+#ifdef HAVE_STARTUP_NOTIFICATION
+	SnLauncherContext *sn_context;
+	SnDisplay *sn_display;
+#endif
 
 	uri = NULL;
 	if (nautilus_file_is_nautilus_link (file)) {
@@ -567,12 +747,80 @@
 		uri = nautilus_file_get_uri (file);
 	}
 		
-	action = nautilus_mime_get_default_action_type_for_file (file);
+	action = nautilus_mime_get_default_action_for_file (file);
 	screen = gtk_window_get_screen (parent_window);
 	envp = egg_screen_exec_environment (screen);	
 	
+#ifdef HAVE_STARTUP_NOTIFICATION
+	sn_display = sn_display_new (gdk_display,
+				     sn_error_trap_push,
+				     sn_error_trap_pop);
+	
+	/* Only initiate notification if application supports it. */
+	if (action->action_type == GNOME_VFS_MIME_ACTION_TYPE_APPLICATION &&
+	    gnome_vfs_application_registry_get_bool_value (action->action.application->id, 
+							   GNOME_VFS_APPLICATION_REGISTRY_STARTUP_NOTIFY,
+							   NULL)) {
+		const char *name;
+		const char *icon;
+
+		sn_context = sn_launcher_context_new (sn_display,
+						      screen ? gdk_screen_get_number (screen) :
+						      DefaultScreen (gdk_display));
+		
+		name = nautilus_file_get_display_name (file);
+
+		if (name != NULL) {
+			char *description;
+			
+			sn_launcher_context_set_name (sn_context, name);
+			
+			description = g_strdup_printf (_("Opening %s"), name);
+			
+			sn_launcher_context_set_description (sn_context, description);
+			
+			g_free (description);
+		}
+
+		icon = nautilus_icon_factory_get_icon_for_file (file, FALSE);
+		
+		if (icon != NULL)
+			sn_launcher_context_set_icon_name (sn_context, icon);
+		
+		if (!sn_launcher_context_get_initiated (sn_context)) {
+			const char *binary_name = action->action.application->command;
+		
+			sn_launcher_context_set_binary_name (sn_context,
+						     binary_name);
+			
+			sn_launcher_context_initiate (sn_context,
+						      g_get_prgname () ? g_get_prgname () : "unknown",
+						      binary_name,
+						      CurrentTime);
+
+			envp = make_spawn_environment_for_sn_context (sn_context, envp);
+		}
+	} else {
+		sn_context = NULL;
+	}
+#endif /* HAVE_STARTUP_NOTIFICATION */
+	
 	result = gnome_vfs_url_show_with_env (uri, envp);
 
+#ifdef HAVE_STARTUP_NOTIFICATION
+	if (sn_context != NULL) {
+		if (result != GNOME_VFS_OK)
+			sn_launcher_context_complete (sn_context); /* end sequence */
+		else
+			add_startup_timeout (screen ? screen :
+					     gdk_display_get_default_screen (gdk_display_get_default ()),
+					     sn_context);
+		sn_launcher_context_unref (sn_context);
+	}
+	
+	sn_display_unref (sn_display);
+#endif /* HAVE_STARTUP_NOTIFICATION */
+	
 	switch (result) {
 	case GNOME_VFS_OK:
 		break;
@@ -589,14 +837,15 @@
 	case GNOME_VFS_ERROR_NO_DEFAULT:
 	case GNOME_VFS_ERROR_NO_HANDLER:
 		nautilus_program_chooser_show_no_choices_message
-					(action, file, parent_window);
+					(action->action_type, file, parent_window);
 		break;
 		
 	default:
 		nautilus_program_chooser_show_invalid_message
-					(action, file, parent_window);
+					(action->action_type, file, parent_window);
 	}
 	
+	gnome_vfs_mime_action_free (action);
 	g_strfreev (envp);
 	g_free (uri);
 }
@@ -689,6 +938,10 @@
 	GList            uris;
 	char           **envp;
 	GnomeVFSResult   result;
+#ifdef HAVE_STARTUP_NOTIFICATION
+	SnLauncherContext *sn_context;
+	SnDisplay *sn_display;
+#endif
 
 	uri = NULL;
 	if (nautilus_file_is_nautilus_link (file)) {
@@ -706,7 +959,75 @@
 	screen = gtk_window_get_screen (parent_window);
 	envp = egg_screen_exec_environment (screen);
 	
+#ifdef HAVE_STARTUP_NOTIFICATION
+	sn_display = sn_display_new (gdk_display,
+				     sn_error_trap_push,
+				     sn_error_trap_pop);
+
+	
+	/* Only initiate notification if application supports it. */
+	if (gnome_vfs_application_registry_get_bool_value (application->id, 
+							   GNOME_VFS_APPLICATION_REGISTRY_STARTUP_NOTIFY,
+							   NULL)) {
+		const char *name;
+		const char *icon = NULL;
+
+		sn_context = sn_launcher_context_new (sn_display,
+						      screen ? gdk_screen_get_number (screen) :
+						      DefaultScreen (gdk_display));
+		
+		name = nautilus_file_get_name (file);
+
+		if (name != NULL) {
+			char *description;
+			
+			sn_launcher_context_set_name (sn_context, name);
+			
+			description = g_strdup_printf (_("Opening %s"), name);
+			
+			sn_launcher_context_set_description (sn_context, description);
+			
+			g_free (description);
+		}
+
+		icon = nautilus_icon_factory_get_icon_for_file (file, FALSE);
+		
+		if (icon != NULL)
+			sn_launcher_context_set_icon_name (sn_context, icon);
+		
+		if (!sn_launcher_context_get_initiated (sn_context)) {
+			const char *binary_name = application->command;
+		
+			sn_launcher_context_set_binary_name (sn_context,
+							     binary_name);
+			
+			sn_launcher_context_initiate (sn_context,
+						      g_get_prgname () ? g_get_prgname () : "unknown",
+						      binary_name,
+						      CurrentTime);
+
+			envp = make_spawn_environment_for_sn_context (sn_context, envp);
+		}
+	} else {
+		sn_context = NULL;
+	}
+#endif /* HAVE_STARTUP_NOTIFICATION */
+	
 	result = gnome_vfs_mime_application_launch_with_env (application, &uris, envp);
+
+#ifdef HAVE_STARTUP_NOTIFICATION
+	if (sn_context != NULL) {
+		if (result != GNOME_VFS_OK)
+			sn_launcher_context_complete (sn_context); /* end sequence */
+		else
+			add_startup_timeout (screen ? screen :
+					     gdk_display_get_default_screen (gdk_display_get_default ()),
+					     sn_context);
+		sn_launcher_context_unref (sn_context);
+	}
+	
+	sn_display_unref (sn_display);
+#endif /* HAVE_STARTUP_NOTIFICATION */
 
 	switch (result) {
 	case GNOME_VFS_OK:
