Index: src/file-manager/fm-properties-window.c
===================================================================
RCS file: /cvs/gnome/nautilus/src/file-manager/fm-properties-window.c,v
retrieving revision 1.226
diff -u -p -r1.226 fm-properties-window.c
--- src/file-manager/fm-properties-window.c	20 Dec 2005 16:37:33 -0000	1.226
+++ src/file-manager/fm-properties-window.c	26 Dec 2005 12:43:56 -0000
@@ -68,7 +68,9 @@
 #include <libgnomevfs/gnome-vfs.h>
 #include <libnautilus-extension/nautilus-property-page-provider.h>
 #include <libnautilus-private/nautilus-customization-data.h>
+#include <libnautilus-private/nautilus-directory.h>
 #include <libnautilus-private/nautilus-entry.h>
+#include <libnautilus-private/nautilus-file.h>
 #include <libnautilus-private/nautilus-file-attributes.h>
 #include <libnautilus-private/nautilus-desktop-icon-file.h>
 #include <libnautilus-private/nautilus-global-preferences.h>
@@ -119,6 +121,9 @@ struct FMPropertiesWindowDetails {	
 	GList *permission_buttons;
 	GHashTable *initial_permissions;
 
+	GtkWidget *apply_recursively_button;
+	GtkWidget *apply_to_combo;
+
 	GList *value_fields;
 
 	GList *mime_list;
@@ -175,6 +180,19 @@ static const GtkTargetEntry target_table
 	{ "x-special/gnome-reset-background", 0, TARGET_RESET_BACKGROUND }
 };
 
+typedef enum {
+	PERMISSIONS_CHANGE_SCOPE_FOLDERS = 1 << 0,
+	PERMISSIONS_CHANGE_SCOPE_FILES = 1 << 1
+} PermissionsChangeScope;
+
+typedef struct {
+	FMPropertiesWindow *window;
+	GnomeVFSFilePermissions permission_mask;
+	gboolean is_on;
+	PermissionsChangeScope scope;
+	unsigned int ref;
+} PermissionsChangeRequest;
+
 #define DIRECTORY_CONTENTS_UPDATE_INTERVAL	200 /* milliseconds */
 #define FILES_UPDATE_INTERVAL			200 /* milliseconds */
 #define STANDARD_EMBLEM_HEIGHT			52
@@ -185,10 +203,14 @@ static void file_changed_callback       
 						   gpointer            user_data);
 static void permission_button_update              (FMPropertiesWindow *window,
 						   GtkToggleButton    *button);
+static void permission_change_callback            (NautilusFile *file,
+						   GnomeVFSResult result,
+						   PermissionsChangeRequest *request);
 static void value_field_update                    (FMPropertiesWindow *window,
 						   GtkLabel           *field);
 static void properties_window_update              (FMPropertiesWindow *window,
 						   GList              *files);
+static gboolean file_list_at_least_one_directory  (GList              *file_list);
 static void is_directory_ready_callback           (NautilusFile       *file,
 						   gpointer            data);
 static void cancel_group_change_callback          (gpointer            callback_data);
@@ -1174,6 +1196,11 @@ properties_window_update (FMPropertiesWi
 		for (l = window->details->value_fields; l != NULL; l = l->next) {
 			value_field_update (window, GTK_LABEL (l->data));
 		}
+
+		if (window->details->apply_recursively_button != NULL) {
+			gtk_widget_set_sensitive (window->details->apply_recursively_button,
+						  file_list_at_least_one_directory (window->details->target_files));
+		}
 	}
 
 	mime_list = get_mime_list (window);
@@ -1311,6 +1338,20 @@ file_list_all_directories (GList *file_l
 	return TRUE;
 }
 
+static gboolean
+file_list_at_least_one_directory (GList *file_list)
+{
+	GList *l;
+
+	for (l = file_list; l != NULL; l = l->next) {
+		if (nautilus_file_is_directory (NAUTILUS_FILE (l->data))) {
+			return TRUE;
+		}
+	}
+
+	return FALSE;
+}
+
 static void
 value_field_update_internal (GtkLabel *label, 
 			     GList *file_list, 
@@ -2483,24 +2524,125 @@ create_emblems_page (FMPropertiesWindow 
 	gtk_widget_show_all (emblems_table);
 }
 
+static gboolean
+file_is_in_scope (NautilusFile *file,
+		  PermissionsChangeScope scope)
+{
+	if (nautilus_file_is_directory (file)) {
+		return (scope & PERMISSIONS_CHANGE_SCOPE_FOLDERS) == PERMISSIONS_CHANGE_SCOPE_FOLDERS;
+	} else {
+		return (scope & PERMISSIONS_CHANGE_SCOPE_FILES) == PERMISSIONS_CHANGE_SCOPE_FILES;
+	}
+}
+
 static void
-permission_change_callback (NautilusFile *file, GnomeVFSResult result, gpointer callback_data)
+directory_contents_ready (NautilusDirectory *directory,
+			  GList *files,
+			  PermissionsChangeRequest *request)
 {
-	FMPropertiesWindow *window;
-	g_assert (callback_data != NULL);
+	NautilusFile *file;
+	GList *l;
 
-	window = FM_PROPERTIES_WINDOW (callback_data);
-	if (GTK_WIDGET (window)->window != NULL &&
-	    window->details->long_operation_underway == 1) {
+	g_assert (FM_IS_PROPERTIES_WINDOW (request->window));
+
+	for (l = files; l != NULL; l = l->next) {
+		file = NAUTILUS_FILE (l->data);
+
+		if (nautilus_file_can_set_permissions (file) &&
+		    file_is_in_scope (file, request->scope)) {
+			GnomeVFSFilePermissions permissions;
+
+			request->ref++;
+			request->window->details->long_operation_underway++;
+
+			permissions = nautilus_file_get_permissions (file);
+			if (request->is_on) {
+				permissions |= request->permission_mask;
+			} else {
+				permissions &= ~request->permission_mask;
+			}
+
+			nautilus_file_set_permissions
+				(file, permissions,
+				 (NautilusFileOperationCallback) permission_change_callback,
+				 request);
+		}
+	}
+
+	nautilus_directory_file_monitor_remove (directory, request->window);
+	nautilus_directory_unref (directory);
+
+	if (--request->window->details->long_operation_underway == 0 &&
+	    GTK_WIDGET (request->window)->window != NULL) {
+		/* finished !! */
+		gdk_window_set_cursor (GTK_WIDGET (request->window)->window, NULL);
+	}
+
+	if (--request->ref == 0) {
+		g_object_unref (request->window);
+		g_free (request);
+	}
+}
+
+static void
+directory_done_loading (NautilusDirectory *directory,
+			PermissionsChangeRequest *request)
+{
+	g_assert (NAUTILUS_IS_DIRECTORY (directory));
+	g_assert (FM_IS_PROPERTIES_WINDOW (request->window));
+
+	nautilus_directory_call_when_ready (directory,
+					    nautilus_mime_actions_get_minimum_file_attributes (),
+					    TRUE,
+					    (NautilusDirectoryCallback) directory_contents_ready,
+					    request);
+}
+
+static void
+permission_change_callback (NautilusFile *file,
+			    GnomeVFSResult result,
+			    PermissionsChangeRequest *request)
+{
+	NautilusDirectory *directory;
+
+	g_assert (NAUTILUS_IS_FILE (file));
+	g_assert (FM_IS_PROPERTIES_WINDOW (request->window));
+
+	if (result == GNOME_VFS_OK &&
+	    request->scope != 0 &&
+	    nautilus_file_is_directory (file)) {
+		/* will be dec. in directory_contents_ready */
+		request->ref++;
+		request->window->details->long_operation_underway++;
+
+		directory = nautilus_directory_get_for_file (file);
+		if (nautilus_directory_are_all_files_seen (directory)) {
+			directory_done_loading (directory, request);
+		} else {
+			g_signal_connect (directory, "done-loading",
+					  G_CALLBACK (directory_done_loading),
+					  request);
+			nautilus_directory_file_monitor_add (directory, request->window,
+							     TRUE, TRUE,
+							     NAUTILUS_FILE_ATTRIBUTE_CAPABILITIES |
+							     NAUTILUS_FILE_ATTRIBUTE_IS_DIRECTORY,
+							     NULL, NULL);
+		}
+	}
+
+	if (--request->window->details->long_operation_underway == 0 &&
+	    GTK_WIDGET (request->window)->window != NULL) {
 		/* finished !! */
-		gdk_window_set_cursor (GTK_WIDGET (window)->window, NULL);
+		gdk_window_set_cursor (GTK_WIDGET (request->window)->window, NULL);
 	}
-	window->details->long_operation_underway--;
 	
 	/* Report the error if it's an error. */
 	fm_report_error_setting_permissions (file, result, NULL);
 
-	g_object_unref (window);
+	if (--request->ref == 0) {
+		g_object_unref (request->window);
+		g_free (request);
+	}
 }
 
 static void
@@ -2528,6 +2670,31 @@ get_initial_permission_state (FMProperti
 	}
 }
 
+static PermissionsChangeScope
+get_permissions_change_scope (FMPropertiesWindow *window)
+{
+	if (file_list_at_least_one_directory (window->details->target_files) &&
+	    gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (window->details->apply_recursively_button))) {
+		/* cf. append_recursive_buttons */
+		switch (gtk_combo_box_get_active (GTK_COMBO_BOX (window->details->apply_to_combo))) {
+		case 0:
+			return PERMISSIONS_CHANGE_SCOPE_FILES;
+
+		case 1:
+			return PERMISSIONS_CHANGE_SCOPE_FOLDERS;
+
+		case 2:
+			return PERMISSIONS_CHANGE_SCOPE_FILES |
+			       PERMISSIONS_CHANGE_SCOPE_FOLDERS;
+
+		default:
+			g_assert_not_reached ();
+		}
+	}
+
+	return 0;
+}
+
 static void
 permission_button_toggled (GtkToggleButton *button, 
 			   FMPropertiesWindow *window)
@@ -2536,6 +2703,7 @@ permission_button_toggled (GtkToggleButt
 	GnomeVFSFilePermissions permission_mask;
 	GList *files_on;
 	GList *files_off;
+	PermissionsChangeScope scope;
 	
 	permission_mask = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (button),
 							      "permission"));
@@ -2586,6 +2754,8 @@ permission_button_toggled (GtkToggleButt
 	window->details->long_operation_underway += g_list_length (files_on);
 	window->details->long_operation_underway += g_list_length (files_off);
 
+	scope = get_permissions_change_scope (window);
+
 	for (l = files_on; l != NULL; l = l->next) {
 		NautilusFile *file;
 		
@@ -2593,15 +2763,22 @@ permission_button_toggled (GtkToggleButt
 
 		if (nautilus_file_can_set_permissions (file)) {
 			GnomeVFSFilePermissions permissions;
+			PermissionsChangeRequest *request;
 
 			permissions = nautilus_file_get_permissions (file);
 			permissions |= permission_mask;
-			
-			g_object_ref (window);
+
+			request = g_new (PermissionsChangeRequest, 1);
+			request->window = g_object_ref (window);
+			request->scope = scope;
+			request->is_on = TRUE;
+			request->permission_mask = permission_mask;
+			request->ref = 1;
+
 			nautilus_file_set_permissions
 				(file, permissions,
-				 permission_change_callback,
-				 window);
+				 (NautilusFileOperationCallback) permission_change_callback,
+				 request);
 		}
 		
 	}
@@ -2613,15 +2790,22 @@ permission_button_toggled (GtkToggleButt
 
 		if (nautilus_file_can_set_permissions (file)) {
 			GnomeVFSFilePermissions permissions;
+			PermissionsChangeRequest *request;
 
 			permissions = nautilus_file_get_permissions (file);
 			permissions &= ~permission_mask;
 
-			g_object_ref (window);
+			request = g_new (PermissionsChangeRequest, 1);
+			request->window = g_object_ref (window);
+			request->scope = scope;
+			request->is_on = FALSE;
+			request->permission_mask = permission_mask;
+			request->ref = 1;
+
 			nautilus_file_set_permissions
 				(file, permissions,
-				 permission_change_callback,
-				 window);
+				 (NautilusFileOperationCallback) permission_change_callback,
+				 request);
 		}
 	}	
 
@@ -2826,6 +3010,58 @@ append_special_execution_flags (FMProper
 	
 }
 
+static void
+apply_recursively_button_toggled (GtkToggleButton *button,
+				  GtkWidget       *combo)
+{
+	gboolean sensitive;
+
+	sensitive = GTK_WIDGET_IS_SENSITIVE (GTK_WIDGET (button)) &&
+		    gtk_toggle_button_get_active (button);
+
+	gtk_widget_set_sensitive (combo, sensitive);
+	if (sensitive) {
+		gtk_widget_grab_focus (combo);
+	}
+}
+
+static void
+append_recursive_buttons (FMPropertiesWindow *window,
+			  GtkTable *table)
+{
+	unsigned int last_row;
+	GtkWidget *hbox;
+
+	last_row = append_row (table);
+
+	hbox = gtk_hbox_new (FALSE, 12);
+	gtk_widget_show (hbox);
+	gtk_table_attach (table, hbox,
+			  TITLE_COLUMN, VALUE_COLUMN + 1,
+			  last_row, last_row + 1,
+			  GTK_FILL, 0,
+			  0, 0);
+
+	window->details->apply_recursively_button =
+		gtk_check_button_new_with_mnemonic ("_Apply changes recursively:");
+	gtk_widget_show (window->details->apply_recursively_button);
+	gtk_container_add (GTK_CONTAINER (hbox), window->details->apply_recursively_button);
+
+	window->details->apply_to_combo = gtk_combo_box_new_text ();
+	gtk_combo_box_append_text (GTK_COMBO_BOX (window->details->apply_to_combo), _("Files"));
+	gtk_combo_box_append_text (GTK_COMBO_BOX (window->details->apply_to_combo), _("Folders"));
+	gtk_combo_box_append_text (GTK_COMBO_BOX (window->details->apply_to_combo), _("Whole Contents"));
+	gtk_combo_box_set_active (GTK_COMBO_BOX (window->details->apply_to_combo), 2);
+	gtk_widget_show (window->details->apply_to_combo);
+	gtk_box_pack_start (GTK_BOX (hbox), window->details->apply_to_combo, FALSE, FALSE, 0);
+
+	g_signal_connect (window->details->apply_recursively_button, "toggled",
+			  G_CALLBACK (apply_recursively_button_toggled),
+			  window->details->apply_to_combo);
+
+	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (window->details->apply_recursively_button), FALSE);
+}
+
 static gboolean
 all_can_get_permissions (GList *file_list)
 {
@@ -3060,6 +3296,10 @@ create_permissions_page (FMPropertiesWin
 			(window, page_table, _("Last changed:"), 
 			 "date_permissions", _("--"),
 			 FALSE);
+
+		append_separator (page_table);
+
+		append_recursive_buttons (window, page_table);
 	} else {
 		if (!is_multi_file_window (window)) {
 			file_name = nautilus_file_get_display_name (get_target_file (window));
