[PATCH] multi-file activation, part 2



Since Alex really did a nice job with cleaning up my last patch, we're
now ready for step 2: Implement actual multi-file activation.

Proposed patch attached.

I've optimized the intersection of the application handler lists for
multiple files, using a Merge Sort-like algorithm. What probably still
leaves much to desire is the performance for many files with the same
MIME type and many handlers, since we don't yet peek what MIME types
were already seen. A simple hash table should help here, I'll provide
another patch for that later.

-- 
Christian Neumair <chris gnome-de org>
Index: libnautilus-private/nautilus-mime-actions.c
===================================================================
RCS file: /cvs/gnome/nautilus/libnautilus-private/nautilus-mime-actions.c,v
retrieving revision 1.119
diff -u -p -r1.119 nautilus-mime-actions.c
--- libnautilus-private/nautilus-mime-actions.c	26 Oct 2005 09:04:40 -0000	1.119
+++ libnautilus-private/nautilus-mime-actions.c	25 Nov 2005 15:29:16 -0000
@@ -233,3 +233,154 @@ nautilus_mime_has_any_applications_for_f
 
 	return result;
 }
+
+GnomeVFSMimeApplication *
+nautilus_mime_get_default_application_for_files (GList *files)
+{
+	GList *l;
+	NautilusFile *file;
+	GnomeVFSMimeApplication *app, *one_app;
+
+	g_assert (files != NULL);
+
+	app = NULL;
+	for (l = files; l != NULL; l = l->next) {
+		file = l->data;
+
+		one_app = nautilus_mime_get_default_application_for_file (file);
+		if (one_app == NULL || (app != NULL && !gnome_vfs_mime_application_equal (app, one_app))) {
+			gnome_vfs_mime_application_free (app);
+			gnome_vfs_mime_application_free (one_app);
+			app = NULL;
+			break;
+		}
+
+		if (app == NULL) {
+			app = one_app;
+		} else {
+			gnome_vfs_mime_application_free (one_app);
+		}
+	}
+
+	return app;
+}
+
+/* returns an intersection of two mime application lists,
+ * and returns a new list, freeing a, b and all applications
+ * that are not in the intersection set.
+ * The lists are assumed to be pre-sorted by their IDs */
+static GList *
+intersect_application_lists (GList *a,
+			     GList *b)
+{
+	GList *l, *m;
+	GList *ret;
+	GnomeVFSMimeApplication *a_app, *b_app;
+	int cmp;
+
+	ret = NULL;
+
+	l = a;
+	m = b;
+
+	while (l != NULL && m != NULL) {
+		a_app = (GnomeVFSMimeApplication *) l->data;
+		b_app = (GnomeVFSMimeApplication *) m->data;
+
+		cmp = strcmp (a_app->id, b_app->id);
+		if (cmp > 0) {
+			gnome_vfs_mime_application_free (b_app);
+			m = m->next;
+		} else if (cmp < 0) {
+			gnome_vfs_mime_application_free (a_app);
+			l = l->next;
+		} else {
+			gnome_vfs_mime_application_free (b_app);
+			ret = g_list_prepend (ret, a_app);
+			l = l->next;
+			m = m->next;
+		}
+	}
+
+	g_list_foreach (l, (GFunc) gnome_vfs_mime_application_free, NULL);
+	g_list_foreach (m, (GFunc) gnome_vfs_mime_application_free, NULL);
+
+	g_list_free (a);
+	g_list_free (b);
+
+	return g_list_reverse (ret);
+}
+
+GList *
+nautilus_mime_get_open_with_applications_for_files (GList *files)
+{
+	GList *l;
+	NautilusFile *file;
+	GList *one_ret, *ret;
+
+	g_assert (files != NULL);
+
+	ret = NULL;
+	for (l = files; l != NULL; l = l->next) {
+		file = l->data;
+
+		one_ret = nautilus_mime_get_open_with_applications_for_file (file);
+		if (ret != NULL) {
+			ret = intersect_application_lists (ret, one_ret);
+		} else {
+			ret = one_ret;
+		}
+
+		if (ret == NULL) {
+			break;
+		}
+	}
+
+	return ret;
+}
+
+GList *
+nautilus_mime_get_applications_for_files (GList *files)
+{
+	GList *l;
+	NautilusFile *file;
+	GList *one_ret, *ret;
+
+	g_assert (files != NULL);
+
+	ret = NULL;
+	for (l = files; l != NULL; l = l->next) {
+		file = l->data;
+
+		one_ret = nautilus_mime_get_applications_for_file (file);
+		if (ret != NULL) {
+			ret = intersect_application_lists (ret, one_ret);
+		} else {
+			ret = one_ret;
+		}
+
+		if (ret == NULL) {
+			break;
+		}
+	}
+
+	return ret;
+}
+
+gboolean
+nautilus_mime_has_any_applications_for_files (GList *files)
+{
+	GList *l;
+	NautilusFile *file;
+
+	g_assert (files != NULL);
+
+	for (l = files; l != NULL; l = l->next) {
+		file = NAUTILUS_FILE (l->data);
+		if (!nautilus_mime_has_any_applications_for_file (file)) {
+			return FALSE;
+		}
+	}
+
+	return TRUE;
+}
Index: libnautilus-private/nautilus-mime-actions.h
===================================================================
RCS file: /cvs/gnome/nautilus/libnautilus-private/nautilus-mime-actions.h,v
retrieving revision 1.33
diff -u -p -r1.33 nautilus-mime-actions.h
--- libnautilus-private/nautilus-mime-actions.h	25 Nov 2004 14:13:37 -0000	1.33
+++ libnautilus-private/nautilus-mime-actions.h	25 Nov 2005 15:29:16 -0000
@@ -36,6 +36,11 @@ GnomeVFSMimeApplication *nautilus_mime_g
 GList *                  nautilus_mime_get_open_with_applications_for_file	(NautilusFile *file);
 GList *			 nautilus_mime_get_applications_for_file		(NautilusFile *file);
 
+GnomeVFSMimeApplication *nautilus_mime_get_default_application_for_files	(GList        *files);
+GList *                  nautilus_mime_get_open_with_applications_for_files	(GList        *files);
+GList *			 nautilus_mime_get_applications_for_files		(GList        *file);
+
 gboolean                 nautilus_mime_has_any_applications_for_file		(NautilusFile *file);
+gboolean                 nautilus_mime_has_any_applications_for_files		(GList        *files);
 
 #endif /* NAUTILUS_MIME_ACTIONS_H */
Index: libnautilus-private/nautilus-program-choosing.c
===================================================================
RCS file: /cvs/gnome/nautilus/libnautilus-private/nautilus-program-choosing.c,v
retrieving revision 1.80
diff -u -p -r1.80 nautilus-program-choosing.c
--- libnautilus-private/nautilus-program-choosing.c	20 Oct 2005 15:30:42 -0000	1.80
+++ libnautilus-private/nautilus-program-choosing.c	25 Nov 2005 15:29:16 -0000
@@ -665,18 +665,19 @@ void nautilus_launch_show_file (Nautilus
  * parameter. Provide a parent window for error dialogs. 
  * 
  * @application: The application to be launched.
- * @file: The file whose location should be passed as a parameter to the application
+ * @files: The files whose locations should be passed as a parameter to the application.
  * @parent_window: A window to use as the parent for any error dialogs.
  */
 void
 nautilus_launch_application (GnomeVFSMimeApplication *application, 
-			     NautilusFile *file,
+			     GList *files,
 			     GtkWindow *parent_window)
 {
 	GdkScreen       *screen;
 	char		*uri;
 	char            *uri_scheme;
-	GList            uris;
+	GList           *uris, *l;
+	NautilusFile    *file;
 	char           **envp;
 	GnomeVFSResult   result;
 #ifdef HAVE_STARTUP_NOTIFICATION
@@ -684,19 +685,26 @@ nautilus_launch_application (GnomeVFSMim
 	SnDisplay *sn_display;
 #endif
 
-	uri = NULL;
-	if (nautilus_file_is_nautilus_link (file)) {
-		uri = nautilus_file_get_activation_uri (file);
-	}
-	
-	if (uri == NULL) {
-		uri = nautilus_file_get_uri (file);
+	g_assert (files != NULL);
+
+	uris = NULL;
+	for (l = files; l != NULL; l = l->next) {
+		file = NAUTILUS_FILE (l->data);
+
+		uri = NULL;
+
+		if (nautilus_file_is_nautilus_link (file)) {
+			uri = nautilus_file_get_activation_uri (file);
+		}
+		
+		if (uri == NULL) {
+			uri = nautilus_file_get_uri (file);
+		}
+
+		uris = g_list_prepend (uris, uri);
 	}
+	uris = g_list_reverse (uris);
 
-	uris.next = NULL;
-	uris.prev = NULL;
-	uris.data = uri;
-	
 	screen = gtk_window_get_screen (parent_window);
 	envp = my_gdk_spawn_make_environment_for_screen (screen, NULL);
 	
@@ -710,27 +718,44 @@ nautilus_launch_application (GnomeVFSMim
 	if (gnome_vfs_mime_application_supports_startup_notification (application))
 	{ 
 		char *name;
+		char *description;
 		char *icon;
+		int   files_count;
+
+		file = NAUTILUS_FILE (files->data);
 
 		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);
-			
+
+		files_count = g_list_length (files);
+		if (files_count == 1) {
+			name = nautilus_file_get_display_name (file);
 			description = g_strdup_printf (_("Opening %s"), name);
-			
-			sn_launcher_context_set_description (sn_context, description);
+		} else {
+			name = NULL;
+			description = g_strdup_printf (ngettext ("Opening %d Item",
+								 "Opening %d Items",
+								 files_count),
+						       files_count);
+		}
 
+		if (name != NULL) {
+			sn_launcher_context_set_name (sn_context, name);
 			g_free (name);
+		}
+
+		if (description != NULL) {
+			sn_launcher_context_set_description (sn_context, description);
 			g_free (description);
 		}
 
 		icon = nautilus_icon_factory_get_icon_for_file (file, FALSE);
+
+		if (icon == NULL) {
+			icon = g_strdup (gnome_vfs_mime_application_get_icon (application));
+		}
+
 		if (icon != NULL) {
 			sn_launcher_context_set_icon_name (sn_context, icon);
 			g_free (icon);
@@ -762,7 +787,7 @@ nautilus_launch_application (GnomeVFSMim
 	}
 #endif /* HAVE_STARTUP_NOTIFICATION */
 	
-	result = gnome_vfs_mime_application_launch_with_env (application, &uris, envp);
+	result = gnome_vfs_mime_application_launch_with_env (application, uris, envp);
 
 #ifdef HAVE_STARTUP_NOTIFICATION
 	if (sn_context != NULL) {
@@ -784,7 +809,7 @@ nautilus_launch_application (GnomeVFSMim
 		break;
 
 	case GNOME_VFS_ERROR_NOT_SUPPORTED:
-		uri_scheme = nautilus_file_get_uri_scheme (file);
+		uri_scheme = nautilus_file_get_uri_scheme (NAUTILUS_FILE (files->data));
 		application_cannot_open_location (application,
 						  file,
 						  uri_scheme,
@@ -801,8 +826,8 @@ nautilus_launch_application (GnomeVFSMim
 #endif
 		break;
 	}
-	
-	g_free (uri);
+
+	eel_g_list_free_deep (uris);
 	g_strfreev (envp);
 }
 
Index: libnautilus-private/nautilus-program-choosing.h
===================================================================
RCS file: /cvs/gnome/nautilus/libnautilus-private/nautilus-program-choosing.h,v
retrieving revision 1.19
diff -u -p -r1.19 nautilus-program-choosing.h
--- libnautilus-private/nautilus-program-choosing.h	22 Nov 2004 15:24:36 -0000	1.19
+++ libnautilus-private/nautilus-program-choosing.h	25 Nov 2005 15:29:17 -0000
@@ -37,7 +37,7 @@ typedef void (*NautilusApplicationChoice
 						   gpointer			  callback_data);
 
 void nautilus_launch_application                 (GnomeVFSMimeApplication           *application,
-						  NautilusFile                      *file,
+						  GList                             *files,
 						  GtkWindow                         *parent_window);
 void nautilus_launch_application_from_command    (GdkScreen                         *screen,
 						  const char                        *name,
Index: src/nautilus-information-panel.c
===================================================================
RCS file: /cvs/gnome/nautilus/src/nautilus-information-panel.c,v
retrieving revision 1.231
diff -u -p -r1.231 nautilus-information-panel.c
--- src/nautilus-information-panel.c	15 Jul 2005 13:47:53 -0000	1.231
+++ src/nautilus-information-panel.c	25 Nov 2005 15:29:18 -0000
@@ -855,13 +855,17 @@ command_button_callback (GtkWidget *butt
 {
 	NautilusInformationPanel *information_panel;
 	GnomeVFSMimeApplication *application;
+	GList files;
 	
 	information_panel = NAUTILUS_INFORMATION_PANEL (g_object_get_data (G_OBJECT (button), "user_data"));
 
 	application = gnome_vfs_mime_application_new_from_desktop_id (id_str);
 
 	if (application != NULL) {
-		nautilus_launch_application (application, information_panel->details->file,
+		files.next = NULL;
+		files.prev = NULL;
+		files.data = information_panel->details->file;
+		nautilus_launch_application (application, &files,
 					     nautilus_information_panel_get_window (information_panel));	
 
 		gnome_vfs_mime_application_free (application);
Index: src/file-manager/fm-directory-view.c
===================================================================
RCS file: /cvs/gnome/nautilus/src/file-manager/fm-directory-view.c,v
retrieving revision 1.722
diff -u -p -r1.722 fm-directory-view.c
--- src/file-manager/fm-directory-view.c	24 Nov 2005 20:47:59 -0000	1.722
+++ src/file-manager/fm-directory-view.c	25 Nov 2005 15:29:25 -0000
@@ -420,7 +420,7 @@ EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_d
 
 typedef struct {
 	GnomeVFSMimeApplication *application;
-	NautilusFile *file;
+	GList *files;
 	FMDirectoryView *directory_view;
 } ApplicationLaunchParameters;
 
@@ -437,17 +437,19 @@ typedef struct {
 
 static ApplicationLaunchParameters *
 application_launch_parameters_new (GnomeVFSMimeApplication *application,
-			      	   NautilusFile *file,
+			      	   GList *files,
 			           FMDirectoryView *directory_view)
 {
 	ApplicationLaunchParameters *result;
 
 	result = g_new0 (ApplicationLaunchParameters, 1);
 	result->application = gnome_vfs_mime_application_copy (application);
-	g_object_ref (directory_view);
-	result->directory_view = directory_view;
-	nautilus_file_ref (file);
-	result->file = file;
+	result->files = nautilus_file_list_copy (files);
+
+	if (directory_view != NULL) {
+		g_object_ref (directory_view);
+		result->directory_view = directory_view;
+	}
 
 	return result;
 }
@@ -456,8 +458,12 @@ static void
 application_launch_parameters_free (ApplicationLaunchParameters *parameters)
 {
 	gnome_vfs_mime_application_free (parameters->application);
-	g_object_unref (parameters->directory_view);
-	nautilus_file_unref (parameters->file);
+	nautilus_file_list_free (parameters->files);
+
+	if (parameters->directory_view != NULL) {
+		g_object_unref (parameters->directory_view);
+	}
+
 	g_free (parameters);
 }			      
 
@@ -655,31 +661,37 @@ action_open_alternate_callback (GtkActio
 
 static void
 fm_directory_view_launch_application (GnomeVFSMimeApplication *application,
-				      NautilusFile *file,
+				      GList *files,
 				      FMDirectoryView *directory_view)
 {
 	char *uri;
 	GnomeVFSURI *vfs_uri;
+	NautilusFile *file;
+	GList *l;
 
 	g_assert (application != NULL);
-	g_assert (NAUTILUS_IS_FILE (file));
+	g_assert (NAUTILUS_IS_FILE (files->data));
 	g_assert (FM_IS_DIRECTORY_VIEW (directory_view));
 
 	nautilus_launch_application
-			(application, file, 
+			(application, files, 
 			 fm_directory_view_get_containing_window (directory_view));
 
-	uri = nautilus_file_get_uri (file);
+	for (l = files; l != NULL; l = l->next) {
+		file = NAUTILUS_FILE (l->data);
 
-	/* Only add real gnome-vfs uris to recent. Not things like
-	   trash:// and x-nautilus-desktop:// */
-	vfs_uri = gnome_vfs_uri_new (uri);
-	if (vfs_uri != NULL) {
-		egg_recent_model_add (nautilus_recent_get_model (), uri);
-		gnome_vfs_uri_unref (vfs_uri);
-	}
+		uri = nautilus_file_get_uri (file);
 
-	g_free (uri);
+		/* Only add real gnome-vfs uris to recent. Not things like
+		   trash:// and x-nautilus-desktop:// */
+		vfs_uri = gnome_vfs_uri_new (uri);
+		if (vfs_uri != NULL) {
+			egg_recent_model_add (nautilus_recent_get_model (), uri);
+			gnome_vfs_uri_unref (vfs_uri);
+		}
+
+		g_free (uri);
+	}
 }				      
 
 #if NEW_MIME_COMPLETE
@@ -697,7 +709,7 @@ fm_directory_view_chose_application_call
 	if (application != NULL) {
 		fm_directory_view_launch_application 
 			(application, /* NOT the (empty) application in launch_parameters */
-			 launch_parameters->file,
+			 launch_parameters->files,
 			 launch_parameters->directory_view);
 	}
 
@@ -738,12 +750,16 @@ application_selected_cb (EelOpenWithDial
 {
 	FMDirectoryView *view;
 	NautilusFile *file;
+	GList uri;
 
 	view = FM_DIRECTORY_VIEW (user_data);
 	
 	file = g_object_get_data (G_OBJECT (dialog), "directory-view:file");
 
-	fm_directory_view_launch_application (app, file, view);
+	uri.next = NULL;
+	uri.prev = NULL;
+	uri.data = file;
+	fm_directory_view_launch_application (app, &uri, view);
 }
 
 static void
@@ -3877,7 +3893,7 @@ open_with_launch_application_callback (G
 	launch_parameters = (ApplicationLaunchParameters *) callback_data;
 	fm_directory_view_launch_application 
 		(launch_parameters->application,
-		 launch_parameters->file,
+		 launch_parameters->files,
 		 launch_parameters->directory_view);
 }
 
@@ -4000,7 +4016,7 @@ add_submenu (GtkUIManager *ui_manager,
 static void
 add_application_to_open_with_menu (FMDirectoryView *view,
 				   GnomeVFSMimeApplication *application, 
-				   NautilusFile *file,
+				   GList *files,
 				   int index,
 				   const char *menu_placeholder,
 				   const char *popup_placeholder)
@@ -4013,10 +4029,13 @@ add_application_to_open_with_menu (FMDir
 	GtkAction *action;
 
 	launch_parameters = application_launch_parameters_new 
-		(application, file, view);
+		(application, files, view);
 	escaped_app = eel_str_double_underscores (application->name);
 	label = g_strdup_printf (_("Open with \"%s\""), escaped_app);
-	tip = g_strdup_printf (_("Use \"%s\" to open the selected item"), escaped_app);
+	tip = g_strdup_printf (ngettext ("Use \"%s\" to open the selected item",
+					 "Use \"%s\" to open the selected items",
+					 g_list_length (files)),
+			       escaped_app);
 	g_free (escaped_app);
 
 	action_name = g_strdup_printf ("open_with_%d", index);
@@ -4196,14 +4215,15 @@ reset_open_with_menu (FMDirectoryView *v
 {
 	GList *applications, *node;
 	NautilusFile *file;
-	gboolean submenu_visible;
-	char *uri;
+	gboolean submenu_visible, filter_default;
 	int num_applications;
 	int index;
 	gboolean other_applications_visible;
 	GtkUIManager *ui_manager;
 	GtkAction *action;
-	
+	GnomeVFSMimeApplication *default_app;
+	ActivationAction activation_action;
+
 	/* Clear any previous inserted items in the applications and viewers placeholders */
 
 	ui_manager = nautilus_window_info_get_ui_manager (view->details->window);
@@ -4218,79 +4238,80 @@ reset_open_with_menu (FMDirectoryView *v
 	
 	num_applications = 0;
 
-	/* This menu is only displayed when there's one selected item. */
-	if (!eel_g_list_exactly_one_item (selection)) {
-		submenu_visible = FALSE;
-		other_applications_visible = FALSE;
-	} else {		
-		GnomeVFSMimeApplication *default_app;
-		ActivationAction action;
-		
-		file = NAUTILUS_FILE (selection->data);
-		
-		uri = nautilus_file_get_uri (file);
+	other_applications_visible = (selection != NULL);
+	filter_default = (selection != NULL);
 
-		other_applications_visible =
-			!can_use_component_for_file (file) ||
-			nautilus_file_is_directory (file);
+	for (node = selection; node != NULL; node = node->next) {
+		file = NAUTILUS_FILE (node->data);
+
+		other_applications_visible &=
+			(!can_use_component_for_file (file) ||
+			 nautilus_file_is_directory (file));
+
+		activation_action = get_activation_action (file);
 
-		action = get_activation_action (file);
 		/* Only use the default app for open if there is not
 		   a mime mismatch, otherwise we can't use it in the
 		   open with menu */
-		if (action == ACTIVATION_ACTION_OPEN_IN_APPLICATION &&
-		    can_show_default_app (view, file)) {
-			default_app = nautilus_mime_get_default_application_for_file (file);
-		} else {
-			default_app = NULL;
+		if (activation_action == ACTIVATION_ACTION_OPEN_IN_APPLICATION &&
+		    !can_show_default_app (view, file)) {
+			filter_default = TRUE;
 		}
-		
-		applications = NULL;
-		if (other_applications_visible) {
-			applications = nautilus_mime_get_open_with_applications_for_file (NAUTILUS_FILE (selection->data));
+
+		if (filter_default && !other_applications_visible) {
+			break;
 		}
+	}
 
-		num_applications = g_list_length (applications);
-		
-		for (node = applications, index = 0; node != NULL; node = node->next, index++) {
-			GnomeVFSMimeApplication *application;
-			char *menu_path;
-			char *popup_path;
-			
-			application = node->data;
+	default_app = NULL;
+	if (filter_default) {
+		default_app = nautilus_mime_get_default_application_for_files (selection);
+	}
 
-			if (default_app && gnome_vfs_mime_application_equal (default_app, application)) {
-				continue;
-			}
+	applications = NULL;
+	if (other_applications_visible) {
+		applications = nautilus_mime_get_open_with_applications_for_files (selection);
+	}
 
-			if (num_applications > 3) {
-				menu_path = FM_DIRECTORY_VIEW_MENU_PATH_APPLICATIONS_SUBMENU_PLACEHOLDER;
-				popup_path = FM_DIRECTORY_VIEW_POPUP_PATH_APPLICATIONS_SUBMENU_PLACEHOLDER;
-			} else {
-				menu_path = FM_DIRECTORY_VIEW_MENU_PATH_APPLICATIONS_PLACEHOLDER;
-				popup_path = FM_DIRECTORY_VIEW_POPUP_PATH_APPLICATIONS_PLACEHOLDER;
-			}
+	num_applications = g_list_length (applications);
+	
+	for (node = applications, index = 0; node != NULL; node = node->next, index++) {
+		GnomeVFSMimeApplication *application;
+		char *menu_path;
+		char *popup_path;
+		
+		application = node->data;
 
-			gtk_ui_manager_add_ui (nautilus_window_info_get_ui_manager (view->details->window),
-					       view->details->open_with_merge_id,
-					       menu_path,
-					       "separator",
-					       NULL,
-					       GTK_UI_MANAGER_SEPARATOR,
-					       FALSE);
-					       
-			add_application_to_open_with_menu (view, 
-							   node->data, 
-							   file, 
-							   index, 
-							   menu_path, popup_path);
+		if (default_app != NULL && gnome_vfs_mime_application_equal (default_app, application)) {
+			continue;
 		}
-		gnome_vfs_mime_application_list_free (applications);
-		gnome_vfs_mime_application_free (default_app);
-		g_free (uri);
 
-		submenu_visible = (num_applications > 3);
+		if (num_applications > 3) {
+			menu_path = FM_DIRECTORY_VIEW_MENU_PATH_APPLICATIONS_SUBMENU_PLACEHOLDER;
+			popup_path = FM_DIRECTORY_VIEW_POPUP_PATH_APPLICATIONS_SUBMENU_PLACEHOLDER;
+		} else {
+			menu_path = FM_DIRECTORY_VIEW_MENU_PATH_APPLICATIONS_PLACEHOLDER;
+			popup_path = FM_DIRECTORY_VIEW_POPUP_PATH_APPLICATIONS_PLACEHOLDER;
+		}
+
+		gtk_ui_manager_add_ui (nautilus_window_info_get_ui_manager (view->details->window),
+				       view->details->open_with_merge_id,
+				       menu_path,
+				       "separator",
+				       NULL,
+				       GTK_UI_MANAGER_SEPARATOR,
+				       FALSE);
+				       
+		add_application_to_open_with_menu (view, 
+						   node->data, 
+						   selection, 
+						   index, 
+						   menu_path, popup_path);
 	}
+	gnome_vfs_mime_application_list_free (applications);
+	gnome_vfs_mime_application_free (default_app);
+
+	submenu_visible = (num_applications > 3);
 
 	if (submenu_visible) {
 		action = gtk_action_group_get_action (view->details->dir_action_group,
@@ -6833,7 +6854,7 @@ clipboard_changed_callback (NautilusClip
 static void
 real_update_menus (FMDirectoryView *view)
 {
-	GList *selection;
+	GList *selection, *l;
 	gint selection_count;
 	const char *tip, *label;
 	char *label_with_underscore;
@@ -6848,9 +6869,10 @@ real_update_menus (FMDirectoryView *view
 	gboolean show_separate_delete_command;
 	gboolean vfolder_directory;
 	gboolean show_open_alternate;
-	gboolean can_open;
+	gboolean can_open, show_app;
 	ActivationAction activation_action;
 	GtkAction *action;
+	GnomeVFSMimeApplication *app;
 
 	selection = fm_directory_view_get_selection (view);
 	selection_count = g_list_length (selection);
@@ -6884,13 +6906,13 @@ real_update_menus (FMDirectoryView *view
 
 	action = gtk_action_group_get_action (view->details->dir_action_group,
 					      FM_ACTION_OPEN);
-	gtk_action_set_sensitive (action,  selection_count != 0);
+	gtk_action_set_sensitive (action, selection_count != 0);
 	
-	label_with_underscore = NULL;
-	can_open = TRUE;
-	if (selection_count == 1) {
+	can_open = show_app = selection_count != 0;
+
+	for (l = selection; l != NULL; l = l->next) {
 		NautilusFile *file;
-		
+
 		file = NAUTILUS_FILE (selection->data);
 		
 		activation_action = get_activation_action (file);
@@ -6899,23 +6921,35 @@ real_update_menus (FMDirectoryView *view
 		   a mime mismatch, otherwise we can't use it in the
 		   open with menu */
 		if (activation_action == ACTIVATION_ACTION_OPEN_IN_APPLICATION &&
-		    can_show_default_app (view, file)) {
-			GnomeVFSMimeApplication *app;
+		    !can_show_default_app (view, file)) {
+			can_open = FALSE;
+		}
 
-			app = nautilus_mime_get_default_application_for_file (file);
-			if (app) {
-				char *escaped_app;
-				escaped_app = eel_str_double_underscores (app->name);
-				label_with_underscore = g_strdup_printf (_("_Open with \"%s\""),
-									 escaped_app);
-				g_free (escaped_app);
-				gnome_vfs_mime_application_free (app);
-			} else {
-				can_open = FALSE;
-			}
+		if (activation_action != ACTIVATION_ACTION_OPEN_IN_APPLICATION) {
+			show_app = FALSE;
+		}
+
+		if (!can_open && !show_app) {
+			break;
 		}
 	} 
 
+	label_with_underscore = NULL;
+
+	app = NULL;
+	if (can_open && show_app) {
+		app = nautilus_mime_get_default_application_for_files (selection);
+	}
+
+	if (app != NULL) {
+		char *escaped_app;
+		escaped_app = eel_str_double_underscores (app->name);
+		label_with_underscore = g_strdup_printf (_("_Open with \"%s\""),
+							 escaped_app);
+		g_free (escaped_app);
+		gnome_vfs_mime_application_free (app);
+	}
+
 	g_object_set (action, "label", 
 		      label_with_underscore ? label_with_underscore : _("_Open"),
 		      NULL);
@@ -7386,6 +7420,93 @@ stop_activate (ActivateParameters *param
 }
 
 static void
+list_to_parameters_foreach (GnomeVFSMimeApplication *application,
+			    GList *files,
+			    GList **ret)
+{
+	ApplicationLaunchParameters *parameters;
+
+	files = g_list_reverse (files);
+
+	parameters = application_launch_parameters_new
+		(application, files, NULL);
+	*ret = g_list_prepend (*ret, parameters);
+}
+
+static unsigned int
+mime_application_hash (GnomeVFSMimeApplication *app)
+{
+	return g_str_hash (app->id);
+}
+
+/**
+ * fm_directory_view_make_activation_parameters
+ *
+ * Construct a list of ApplicationLaunchParameters from a list of NautilusFiles,
+ * where files that have the same default application are put into the same
+ * launch parameter, and others are put into the unhandled_files list.
+ *
+ * @files: Files to use for construction.
+ * @unhandled_files: Files without any default application will be put here.
+ * 
+ * Return value: Newly allocated list of ApplicationLaunchParameters.
+ **/
+static GList *
+fm_directory_view_make_activation_parameters (GList *files,
+					      GList **unhandled_files)
+{
+	GList *ret, *l, *app_files;
+	NautilusFile *file;
+	GnomeVFSMimeApplication *app, *old_app;
+	GHashTable *app_table;
+
+	ret = NULL;
+	*unhandled_files = NULL;
+
+	app_table = g_hash_table_new_full
+		((GHashFunc) mime_application_hash,
+		 (GEqualFunc) gnome_vfs_mime_application_equal,
+		 (GDestroyNotify) gnome_vfs_mime_application_free,
+		 (GDestroyNotify) g_list_free);
+
+	for (l = files; l != NULL; l = l->next) {
+		file = NAUTILUS_FILE (l->data);
+
+		app = nautilus_mime_get_default_application_for_file (file);
+		if (app != NULL) {
+			app_files = NULL;
+
+			if (g_hash_table_lookup_extended (app_table, app,
+							  (gpointer *) &old_app,
+							  (gpointer *) &app_files)) {
+				g_assert (g_hash_table_steal (app_table, old_app));
+
+				app_files = g_list_prepend (app_files, file);
+
+				gnome_vfs_mime_application_free (app);
+				app = old_app;
+			} else {
+				app_files = g_list_prepend (NULL, file);
+			}
+
+			g_hash_table_insert (app_table, app, app_files);
+		} else {
+			*unhandled_files = g_list_prepend (*unhandled_files, file);
+		}
+	}
+
+	g_hash_table_foreach (app_table,
+			      (GHFunc) list_to_parameters_foreach,
+			      &ret);
+
+	g_hash_table_destroy (app_table);
+
+	*unhandled_files = g_list_reverse (*unhandled_files);
+
+	return g_list_reverse (ret);
+}
+
+static void
 activate_callback (GList *files, gpointer callback_data)
 {
 	ActivateParameters *parameters;
@@ -7396,6 +7517,9 @@ activate_callback (GList *files, gpointe
 	GList *launch_files;
 	GList *launch_in_terminal_files;
 	GList *open_in_app_files;
+	GList *open_in_app_parameters;
+	GList *unhandled_open_in_app_files;
+	ApplicationLaunchParameters *one_parameters;
 	GList *open_in_view_files;
 	GList *l;
 	int count;
@@ -7548,19 +7672,40 @@ activate_callback (GList *files, gpointe
 		}
 	}
 
-	open_in_app_files = g_list_reverse (open_in_app_files);
-	for (l = open_in_app_files; l != NULL; l = l->next) {
+	open_in_app_parameters = NULL;
+	unhandled_open_in_app_files = NULL;
+
+	if (open_in_app_files != NULL) {
+		open_in_app_files = g_list_reverse (open_in_app_files);
+
+		open_in_app_parameters = fm_directory_view_make_activation_parameters
+			(open_in_app_files, &unhandled_open_in_app_files);
+	}
+
+	if (open_in_app_parameters != NULL ||
+	    unhandled_open_in_app_files != NULL) {
+		if ((parameters->flags & NAUTILUS_WINDOW_OPEN_FLAG_CLOSE_BEHIND) != 0 &&
+		     nautilus_window_info_get_window_type (view->details->window) == NAUTILUS_WINDOW_SPATIAL) {
+			nautilus_window_info_close (view->details->window);
+		}
+	}
+
+	for (l = open_in_app_parameters; l != NULL; l = l->next) {
+		one_parameters = l->data;
+
+		fm_directory_view_launch_application (
+			one_parameters->application,
+			one_parameters->files,
+			view);
+		application_launch_parameters_free (one_parameters);
+	}
+
+	for (l = unhandled_open_in_app_files; l != NULL; l = l->next) {
 		file = NAUTILUS_FILE (l->data);
 
 		nautilus_launch_show_file
 			(file, fm_directory_view_get_containing_window (view));
 
-		if ((parameters->flags & NAUTILUS_WINDOW_OPEN_FLAG_CLOSE_BEHIND) != 0) {
-			if (nautilus_window_info_get_window_type (view->details->window) == NAUTILUS_WINDOW_SPATIAL) {
-				nautilus_window_info_close (view->details->window);
-			}
-		}
-		
 		/* We should not add trash and directory uris.*/
 		if ((!nautilus_file_is_in_trash (file)) && 
 		    (!nautilus_file_is_directory (file))) {
@@ -7576,6 +7721,8 @@ activate_callback (GList *files, gpointe
 	g_list_free (launch_in_terminal_files);
 	g_list_free (open_in_view_files);
 	g_list_free (open_in_app_files);
+	g_list_free (open_in_app_parameters);
+	g_list_free (unhandled_open_in_app_files);
 
 	nautilus_file_list_free (parameters->files);
 	g_free (parameters);

Attachment: signature.asc
Description: This is a digitally signed message part



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