Re: [PATCH] Add tree functionality to list view



On Die, 2005-06-14 at 18:38 +0200, Jürg Billeter wrote:
> On Die, 2005-06-14 at 18:04 +0200, Alexander Larsson wrote:
> > Here are some more detailed review comments:
> > [...]

The attached patch should fix all mentioned issues except multiple
gtk_tree_model_rows_reordered calls and remove subdirectories on
row_collapsed as commented on in my previous reply.

Regards,

Jürg
-- 
Juerg Billeter <j bitron ch>
Index: libnautilus-private/nautilus-tree-view-drag-dest.c
===================================================================
RCS file: /cvs/gnome/nautilus/libnautilus-private/nautilus-tree-view-drag-dest.c,v
retrieving revision 1.7
diff -p -u -r1.7 nautilus-tree-view-drag-dest.c
--- libnautilus-private/nautilus-tree-view-drag-dest.c	2 Jun 2005 16:16:55 -0000	1.7
+++ libnautilus-private/nautilus-tree-view-drag-dest.c	14 Jun 2005 19:43:53 -0000
@@ -54,6 +54,7 @@ struct _NautilusTreeViewDragDestDetails 
 
 	guint highlight_id;
 	guint scroll_id;
+	guint expand_id;
 };
 
 enum {
@@ -133,6 +134,33 @@ remove_scroll_timeout (NautilusTreeViewD
 	}
 }
 
+static int
+expand_timeout (gpointer data)
+{
+	GtkTreeView *tree_view;
+	GtkTreePath *drop_path;
+	
+	tree_view = GTK_TREE_VIEW (data);
+	
+	gtk_tree_view_get_drag_dest_row (tree_view, &drop_path, NULL);
+	
+	if (drop_path) {
+		gtk_tree_view_expand_row (tree_view, drop_path, FALSE);
+		gtk_tree_path_free (drop_path);
+	}
+
+	return FALSE;
+}
+
+static void
+remove_expand_timeout (NautilusTreeViewDragDest *dest)
+{
+	if (dest->details->expand_id) {
+		g_source_remove (dest->details->expand_id);
+		dest->details->expand_id = 0;
+	}
+}
+
 static gboolean
 highlight_expose (GtkWidget *widget,
 		  GdkEventExpose *event,
@@ -369,7 +397,9 @@ drag_motion_callback (GtkWidget *widget,
 {
 	NautilusTreeViewDragDest *dest;
 	GtkTreePath *path;
-	GtkTreePath *drop_path;
+	GtkTreePath *drop_path, *old_drop_path;
+	GtkTreeModel *model;
+	GtkTreeIter drop_iter;
 	GtkTreeViewDropPosition pos;
 	guint action;
 
@@ -386,10 +416,27 @@ drag_motion_callback (GtkWidget *widget,
 	
 	action = get_drop_action (dest, context, drop_path);
 	
+	gtk_tree_view_get_drag_dest_row (GTK_TREE_VIEW (widget), &old_drop_path,
+					 NULL);
+	
 	if (action) {
 		set_drag_dest_row (dest, drop_path);
+		model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
+		if (drop_path == NULL || (old_drop_path != NULL &&
+		    gtk_tree_path_compare (old_drop_path, drop_path) != 0)) {
+			remove_expand_timeout (dest);
+		}
+		if (dest->details->expand_id == 0 && drop_path != NULL) {
+			gtk_tree_model_get_iter (model, &drop_iter, drop_path);
+			if (gtk_tree_model_iter_has_child (model, &drop_iter)) {
+				dest->details->expand_id = g_timeout_add (500,
+						expand_timeout,
+						dest->details->tree_view);
+			}
+		}
 	} else {
 		clear_drag_dest_row (dest);
+		remove_expand_timeout (dest);
 	}
 	
 	if (path) {
@@ -400,6 +447,10 @@ drag_motion_callback (GtkWidget *widget,
 		gtk_tree_path_free (drop_path);
 	}
 	
+	if (old_drop_path) {
+		gtk_tree_path_free (old_drop_path);
+	}
+	
 	if (dest->details->scroll_id == 0) {
 		dest->details->scroll_id = 
 			g_timeout_add (150, 
@@ -427,6 +478,7 @@ drag_leave_callback (GtkWidget *widget,
 	free_drag_data (dest);
 
 	remove_scroll_timeout (dest);
+	remove_expand_timeout (dest);
 }
 
 static void
@@ -612,6 +664,7 @@ drag_drop_callback (GtkWidget *widget,
 
 	get_drag_data (dest, context, time);
 	remove_scroll_timeout (dest);
+	remove_expand_timeout (dest);
 	clear_drag_dest_row (dest);
 	
 	return TRUE;
@@ -626,6 +679,7 @@ tree_view_weak_notify (gpointer user_dat
 	dest = NAUTILUS_TREE_VIEW_DRAG_DEST (user_data);
 	
 	remove_scroll_timeout (dest);
+	remove_expand_timeout (dest);
 
 	dest->details->tree_view = NULL;
 }
@@ -644,6 +698,7 @@ nautilus_tree_view_drag_dest_dispose (GO
 	}
 	
 	remove_scroll_timeout (dest);
+	remove_expand_timeout (dest);
 
 	EEL_CALL_PARENT (G_OBJECT_CLASS, dispose, (object));
 }
Index: src/file-manager/fm-directory-view.c
===================================================================
RCS file: /cvs/gnome/nautilus/src/file-manager/fm-directory-view.c,v
retrieving revision 1.688
diff -p -u -r1.688 fm-directory-view.c
--- src/file-manager/fm-directory-view.c	11 Jun 2005 18:27:17 -0000	1.688
+++ src/file-manager/fm-directory-view.c	14 Jun 2005 19:43:58 -0000
@@ -249,6 +249,8 @@ struct FMDirectoryViewDetails
 	NautilusFile *file_monitored_for_open_with;
 	GtkActionGroup *open_with_action_group;
 	guint open_with_merge_id;
+
+	GList *subdirectory_list;
 };
 
 typedef enum {
@@ -2080,6 +2082,7 @@ done_loading (FMDirectoryView *view)
 	view->details->loading = FALSE;
 }
 
+
 typedef struct {
 	GHashTable *debuting_uris;
 	GList	   *added_files;
@@ -2276,7 +2279,20 @@ copy_move_done_callback (GHashTable *deb
 static gboolean
 real_file_still_belongs (FMDirectoryView *view, NautilusFile *file)
 {
-	return nautilus_directory_contains_file (view->details->model, file);
+	GList *node;
+	
+	if (nautilus_directory_contains_file (view->details->model, file)) {
+		return TRUE;
+	}
+
+	for (node = view->details->subdirectory_list; node != NULL; node = node->next) {
+		if (nautilus_directory_contains_file (NAUTILUS_DIRECTORY (node->data),
+						      file)) {
+			return TRUE;
+		}
+	}
+	
+	return FALSE;
 }
 
 static gboolean
@@ -2739,6 +2755,88 @@ fm_directory_view_queue_file_change (FMD
 	queue_pending_files (view, &singleton_list, &view->details->new_changed_files);
 }
 
+void
+fm_directory_view_add_subdirectory (FMDirectoryView  *view,
+				     NautilusDirectory*directory)
+{
+	NautilusFileAttributes attributes;
+
+	if (g_list_find (view->details->subdirectory_list, directory) == NULL) {
+		nautilus_directory_ref (directory);
+
+		attributes = nautilus_icon_factory_get_required_file_attributes ();
+		attributes |= NAUTILUS_FILE_ATTRIBUTE_DIRECTORY_ITEM_COUNT |
+			NAUTILUS_FILE_ATTRIBUTE_METADATA |
+			NAUTILUS_FILE_ATTRIBUTE_MIME_TYPE |
+			NAUTILUS_FILE_ATTRIBUTE_DISPLAY_NAME |
+			NAUTILUS_FILE_ATTRIBUTE_EXTENSION_INFO;
+
+		nautilus_directory_file_monitor_add (directory,
+						     &view->details->model,
+						     view->details->show_hidden_files,
+						     view->details->show_backup_files,
+						     attributes,
+						     files_added_callback, view);
+
+	    	g_signal_connect
+			(directory, "files_added",
+			 G_CALLBACK (files_added_callback), view);
+		g_signal_connect
+			(directory, "files_changed",
+			 G_CALLBACK (files_changed_callback), view);
+ 
+		view->details->subdirectory_list = g_list_prepend (
+				view->details->subdirectory_list, directory);
+	}
+}
+
+static void
+real_remove_subdirectory (FMDirectoryView  *view,
+			   NautilusDirectory*directory)
+{
+	view->details->subdirectory_list = g_list_remove (
+				view->details->subdirectory_list, directory);
+
+	g_signal_handlers_disconnect_by_func (directory,
+					      G_CALLBACK (files_added_callback),
+					      view);
+	g_signal_handlers_disconnect_by_func (directory,
+					      G_CALLBACK (files_changed_callback),
+					      view);
+
+	nautilus_directory_file_monitor_remove (directory, &view->details->model);
+
+	nautilus_directory_unref (directory);
+}
+
+void
+fm_directory_view_remove_subdirectory (FMDirectoryView  *view,
+					NautilusDirectory*directory)
+{
+	GList *node;
+	NautilusFile *parent, *file1, *file2;
+	NautilusDirectory *subdir;
+	
+	parent = nautilus_directory_get_corresponding_file (directory);
+	
+	for (node = view->details->subdirectory_list; node != NULL;) {
+		file1 = nautilus_directory_get_corresponding_file (node->data);
+		while (file1 != parent && file1 != NULL) {
+			file2 = nautilus_file_get_parent (file1);
+			nautilus_file_unref (file1);
+			file1 = file2;
+		}
+		subdir = NAUTILUS_DIRECTORY (node->data);
+		node = node->next;
+		if (file1 == parent) {
+			real_remove_subdirectory (view, subdir);
+		}
+		if (file1 != NULL) {
+			nautilus_file_unref (file1);
+		}
+	}
+}
+
 /**
  * fm_directory_view_clear:
  *
@@ -7369,6 +7468,11 @@ load_directory (FMDirectoryView *view,
 	 * of old selection.
 	 */
 	schedule_update_menus (view);
+	
+	while (view->details->subdirectory_list != NULL) {
+		real_remove_subdirectory (view,
+				view->details->subdirectory_list->data);
+	}
 
 	disconnect_model_handlers (view);
 
Index: src/file-manager/fm-directory-view.h
===================================================================
RCS file: /cvs/gnome/nautilus/src/file-manager/fm-directory-view.h,v
retrieving revision 1.138
diff -p -u -r1.138 fm-directory-view.h
--- src/file-manager/fm-directory-view.h	2 Jun 2005 10:48:55 -0000	1.138
+++ src/file-manager/fm-directory-view.h	14 Jun 2005 19:43:58 -0000
@@ -392,5 +392,9 @@ void                fm_directory_view_ha
 									int               y);
 void                fm_directory_view_freeze_updates                   (FMDirectoryView  *view);
 void                fm_directory_view_unfreeze_updates                 (FMDirectoryView  *view);
+void                fm_directory_view_add_subdirectory                (FMDirectoryView  *view,
+									NautilusDirectory*directory);
+void                fm_directory_view_remove_subdirectory             (FMDirectoryView  *view,
+									NautilusDirectory*directory);
 
 #endif /* FM_DIRECTORY_VIEW_H */
Index: src/file-manager/fm-list-model.c
===================================================================
RCS file: /cvs/gnome/nautilus/src/file-manager/fm-list-model.c,v
retrieving revision 1.35
diff -p -u -r1.35 fm-list-model.c
--- src/file-manager/fm-list-model.c	2 Jun 2005 16:16:55 -0000	1.35
+++ src/file-manager/fm-list-model.c	14 Jun 2005 19:43:59 -0000
@@ -33,10 +33,15 @@
 #include <eel/eel-glib-extensions.h>
 #include <gtk/gtktreednd.h>
 #include <gtk/gtktreesortable.h>
+#include <libgnome/gnome-i18n.h>
 #include <libnautilus-private/nautilus-icon-factory.h>
 #include <libnautilus-private/nautilus-dnd.h>
 #include <gsequence/gsequence.h>
 
+static int fm_list_model_file_entry_compare_func (gconstpointer a,
+				       gconstpointer b,
+				       gpointer      user_data);
+
 static int fm_list_model_compare_func (gconstpointer a,
 				       gconstpointer b,
 				       gpointer      user_data);
@@ -67,6 +72,16 @@ typedef struct {
 	GList *path_list;
 } DragDataGetInfo;
 
+typedef struct FileEntry FileEntry;
+
+struct FileEntry {
+	NautilusFile *file;
+	FileEntry *parent;
+	GSequence *files;
+	GSequencePtr ptr;
+	guint loaded : 1;
+};
+
 static const GtkTargetEntry drag_types [] = {
 	{ NAUTILUS_ICON_DND_GNOME_ICON_LIST_TYPE, 0, NAUTILUS_ICON_DND_GNOME_ICON_LIST },
 	{ NAUTILUS_ICON_DND_URI_LIST_TYPE, 0, NAUTILUS_ICON_DND_URI_LIST },
@@ -76,10 +91,20 @@ static const GtkTargetEntry drag_types [
 
 static GtkTargetList *drag_target_list = NULL;
 
+static void
+file_entry_free (FileEntry *file_entry)
+{
+	nautilus_file_unref (file_entry->file);
+	if (file_entry->files != NULL) {
+		g_sequence_free (file_entry->files);
+	}
+	g_free (file_entry);
+}
+
 static guint
 fm_list_model_get_flags (GtkTreeModel *tree_model)
 {
-	return GTK_TREE_MODEL_ITERS_PERSIST | GTK_TREE_MODEL_LIST_ONLY;
+	return GTK_TREE_MODEL_ITERS_PERSIST;
 }
 
 static int
@@ -124,19 +149,27 @@ static gboolean
 fm_list_model_get_iter (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreePath *path)
 {
 	FMListModel *model;
+	GSequence *files;
 	GSequencePtr ptr;
-	int i;
+	FileEntry *file_entry;
+	int i, d;
 	
 	model = (FMListModel *)tree_model;
+	ptr = NULL;
+	
+	files = model->details->files;
+	for (d = 0; d < gtk_tree_path_get_depth (path); d++) {
+		i = gtk_tree_path_get_indices (path)[d];
 
-	i = gtk_tree_path_get_indices (path)[0];
+		if (files == NULL || i >= g_sequence_get_length (files)) {
+			return FALSE;
+		}
 
-	if (i >= g_sequence_get_length (model->details->files)) {
-		return FALSE;
+		ptr = g_sequence_get_ptr_at_pos (files, i);
+		file_entry = g_sequence_ptr_get_data (ptr);
+		files = file_entry->files;
 	}
 
-	ptr = g_sequence_get_ptr_at_pos (model->details->files, i);
-
 	iter->stamp = model->details->stamp;
 	iter->user_data = ptr;
 
@@ -150,6 +183,9 @@ fm_list_model_get_path (GtkTreeModel *tr
 {
 	GtkTreePath *path;
 	FMListModel *model;
+	GSequencePtr ptr;
+	FileEntry *file_entry;
+
 
 	model = (FMListModel *)tree_model;
 	
@@ -161,7 +197,16 @@ fm_list_model_get_path (GtkTreeModel *tr
 	}
 	
 	path = gtk_tree_path_new ();
-	gtk_tree_path_append_index (path, g_sequence_ptr_get_position (iter->user_data));
+	ptr = iter->user_data;
+	while (ptr != NULL) {
+		gtk_tree_path_prepend_index (path, g_sequence_ptr_get_position (ptr));
+		file_entry = g_sequence_ptr_get_data (ptr);
+		if (file_entry->parent != NULL) {
+			ptr = file_entry->parent->ptr;
+		} else {
+			ptr = NULL;
+		}
+	}
 
 	return path;
 }
@@ -170,6 +215,7 @@ static void
 fm_list_model_get_value (GtkTreeModel *tree_model, GtkTreeIter *iter, int column, GValue *value)
 {
 	FMListModel *model;
+	FileEntry *file_entry;
 	NautilusFile *file;
 	char *str;
 	GdkPixbuf *icon;
@@ -185,7 +231,8 @@ fm_list_model_get_value (GtkTreeModel *t
 	g_return_if_fail (model->details->stamp == iter->stamp);
 	g_return_if_fail (!g_sequence_ptr_is_end (iter->user_data));
 
-	file = g_sequence_ptr_get_data (iter->user_data);
+	file_entry = g_sequence_ptr_get_data (iter->user_data);
+	file = file_entry->file;
 	
 	switch (column) {
 	case FM_LIST_MODEL_FILE_COLUMN:
@@ -202,36 +249,38 @@ fm_list_model_get_value (GtkTreeModel *t
 	case FM_LIST_MODEL_LARGEST_ICON_COLUMN:
 		g_value_init (value, GDK_TYPE_PIXBUF);
 
-		zoom_level = fm_list_model_get_zoom_level_from_column_id (column);
-		icon_size = nautilus_get_icon_size_for_zoom_level (zoom_level);
-
-		modifier = NULL;
-		if (model->details->drag_view != NULL) {
-			GtkTreePath *path_a, *path_b;
-			
-			gtk_tree_view_get_drag_dest_row (model->details->drag_view,
-							 &path_a,
-							 NULL);
-			if (path_a != NULL) {
-				path_b = gtk_tree_model_get_path (tree_model, iter);
+		if (file != NULL) {
+			zoom_level = fm_list_model_get_zoom_level_from_column_id (column);
+			icon_size = nautilus_get_icon_size_for_zoom_level (zoom_level);
 
-				if (gtk_tree_path_compare (path_a, path_b) == 0) {
-					modifier = "accept";
+			modifier = NULL;
+			if (model->details->drag_view != NULL) {
+				GtkTreePath *path_a, *path_b;
+				
+				gtk_tree_view_get_drag_dest_row (model->details->drag_view,
+								 &path_a,
+								 NULL);
+				if (path_a != NULL) {
+					path_b = gtk_tree_model_get_path (tree_model, iter);
+
+					if (gtk_tree_path_compare (path_a, path_b) == 0) {
+						modifier = "accept";
+					}
+						
+					gtk_tree_path_free (path_a);
+					gtk_tree_path_free (path_b);
 				}
-					
-				gtk_tree_path_free (path_a);
-				gtk_tree_path_free (path_b);
 			}
-		}
-		
-		if (nautilus_file_has_open_window (file)) {
-			modifier = "visiting";
-		}
-		
-		icon = nautilus_icon_factory_get_pixbuf_for_file_force_size (file, modifier, icon_size);
+			
+			if (nautilus_file_has_open_window (file)) {
+				modifier = "visiting";
+			}
+			
+			icon = nautilus_icon_factory_get_pixbuf_for_file_force_size (file, modifier, icon_size);
 
-		g_value_set_object (value, icon);
-		g_object_unref (icon);
+			g_value_set_object (value, icon);
+			g_object_unref (icon);
+		}
 		break;
 	case FM_LIST_MODEL_SMALLEST_EMBLEM_COLUMN:
 	case FM_LIST_MODEL_SMALLER_EMBLEM_COLUMN:
@@ -242,39 +291,41 @@ fm_list_model_get_value (GtkTreeModel *t
 	case FM_LIST_MODEL_LARGEST_EMBLEM_COLUMN:
 		g_value_init (value, GDK_TYPE_PIXBUF);
 
-		parent_file = nautilus_file_get_parent (file);
-		emblems_to_ignore = eel_string_list_new_from_string (NAUTILUS_FILE_EMBLEM_NAME_TRASH, TRUE);
-		if (parent_file) {
-			if (!nautilus_file_can_write (parent_file)) {
-				eel_string_list_prepend (emblems_to_ignore, NAUTILUS_FILE_EMBLEM_NAME_CANT_WRITE);
+		if (file != NULL) {
+			parent_file = nautilus_file_get_parent (file);
+			emblems_to_ignore = eel_string_list_new_from_string (NAUTILUS_FILE_EMBLEM_NAME_TRASH, TRUE);
+			if (parent_file) {
+				if (!nautilus_file_can_write (parent_file)) {
+					eel_string_list_prepend (emblems_to_ignore, NAUTILUS_FILE_EMBLEM_NAME_CANT_WRITE);
+				}
+				nautilus_file_unref (parent_file);
 			}
-			nautilus_file_unref (parent_file);
-		}
-		emblem_icons = nautilus_icon_factory_get_emblem_icons_for_file (file, emblems_to_ignore);
-		eel_string_list_free (emblems_to_ignore);
+			emblem_icons = nautilus_icon_factory_get_emblem_icons_for_file (file, emblems_to_ignore);
+			eel_string_list_free (emblems_to_ignore);
 
-		if (emblem_icons != NULL) {
-			zoom_level = fm_list_model_get_zoom_level_from_emblem_column_id (column);
-			icon_size = nautilus_get_icon_size_for_zoom_level (zoom_level);
-			icon = nautilus_icon_factory_get_pixbuf_for_icon_force_size (
-				emblem_icons->data, NULL, icon_size,
-				NULL, NULL, FALSE, NULL);
-			eel_g_list_free_deep (emblem_icons);
-	
-			g_value_set_object (value, icon);
+			if (emblem_icons != NULL) {
+				zoom_level = fm_list_model_get_zoom_level_from_emblem_column_id (column);
+				icon_size = nautilus_get_icon_size_for_zoom_level (zoom_level);
+				icon = nautilus_icon_factory_get_pixbuf_for_icon_force_size (
+					emblem_icons->data, NULL, icon_size,
+					NULL, NULL, FALSE, NULL);
+				eel_g_list_free_deep (emblem_icons);
+		
+				g_value_set_object (value, icon);
 
-			if (icon != NULL) { 
-				g_object_unref (icon);
+				if (icon != NULL) { 
+					g_object_unref (icon);
+				}
 			}
 		}
 		break;
 	case FM_LIST_MODEL_FILE_NAME_IS_EDITABLE_COLUMN:
 		g_value_init (value, G_TYPE_BOOLEAN);
 		
-                g_value_set_boolean (value, nautilus_file_can_rename (file));
+                g_value_set_boolean (value, file != NULL && nautilus_file_can_rename (file));
                 break;
  	default:
-		if (column >= FM_LIST_MODEL_NUM_COLUMNS || column < FM_LIST_MODEL_NUM_COLUMNS + model->details->columns->len) {
+ 		if (column >= FM_LIST_MODEL_NUM_COLUMNS || column < FM_LIST_MODEL_NUM_COLUMNS + model->details->columns->len) {
 			NautilusColumn *nautilus_column;
 			char *attribute;
 			nautilus_column = model->details->columns->pdata[column - FM_LIST_MODEL_NUM_COLUMNS];
@@ -283,9 +334,17 @@ fm_list_model_get_value (GtkTreeModel *t
 			g_object_get (nautilus_column, 
 				      "attribute", &attribute, 
 				      NULL);
-			str = nautilus_file_get_string_attribute_with_default (file, 
+			if (file != NULL) {
+				str = nautilus_file_get_string_attribute_with_default (file, 
 									       attribute);
-			g_value_set_string_take_ownership (value, str);
+				g_value_set_string_take_ownership (value, str);
+			} else if (!strcmp (attribute, "name")) {
+				if (file_entry->loaded) {
+					g_value_set_string (value, _("(Empty)"));
+				} else {
+					g_value_set_string (value, _("Loading..."));
+				}
+			}
 			g_free (attribute);
 		} else {
 			g_assert_not_reached ();
@@ -311,19 +370,24 @@ static gboolean
 fm_list_model_iter_children (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *parent)
 {
 	FMListModel *model;
+	GSequence *files;
+	FileEntry *file_entry;
 
 	model = (FMListModel *)tree_model;
 	
-	if (parent != NULL) {
-		return FALSE;
+	if (parent == NULL) {
+		files = model->details->files;
+	} else {
+		file_entry = g_sequence_ptr_get_data (parent->user_data);
+		files = file_entry->files;
 	}
 
-	if (g_sequence_get_length (model->details->files) == 0) {
+	if (files == NULL || g_sequence_get_length (files) == 0) {
 		return FALSE;
 	}
-
+	
 	iter->stamp = model->details->stamp;
-	iter->user_data = g_sequence_get_begin_ptr (model->details->files);
+	iter->user_data = g_sequence_get_begin_ptr (files);
 
 	return TRUE;
 }
@@ -331,23 +395,34 @@ fm_list_model_iter_children (GtkTreeMode
 static gboolean
 fm_list_model_iter_has_child (GtkTreeModel *tree_model, GtkTreeIter *iter)
 {
-	return FALSE;
+	FileEntry *file_entry;
+	
+	if (iter == NULL) {
+		return !fm_list_model_is_empty (FM_LIST_MODEL (tree_model));
+	}
+
+	file_entry = g_sequence_ptr_get_data (iter->user_data);
+
+	return (file_entry->files != NULL && g_sequence_get_length (file_entry->files) > 0);
 }
 
 static int
 fm_list_model_iter_n_children (GtkTreeModel *tree_model, GtkTreeIter *iter)
 {
 	FMListModel *model;
+	GSequence *files;
+	FileEntry *file_entry;
 
 	model = (FMListModel *)tree_model;
 
 	if (iter == NULL) {
-		return g_sequence_get_length (model->details->files);
+		files = model->details->files;
+	} else {
+		file_entry = g_sequence_ptr_get_data (iter->user_data);
+		files = file_entry->files;
 	}
 
-	g_return_val_if_fail (model->details->stamp == iter->stamp, -1);
-
-	return 0;
+	return g_sequence_get_length (files);
 }
 
 static gboolean
@@ -355,14 +430,19 @@ fm_list_model_iter_nth_child (GtkTreeMod
 {
 	FMListModel *model;
 	GSequencePtr child;
+	GSequence *files;
+	FileEntry *file_entry;
 
 	model = (FMListModel *)tree_model;
 	
 	if (parent != NULL) {
-		return FALSE;
+		file_entry = g_sequence_ptr_get_data (parent->user_data);
+		files = file_entry->files;
+	} else {
+		files = model->details->files;
 	}
 
-	child = g_sequence_get_ptr_at_pos (model->details->files, n);
+	child = g_sequence_get_ptr_at_pos (files, n);
 
 	if (g_sequence_ptr_is_end (child)) {
 		return FALSE;
@@ -377,7 +457,21 @@ fm_list_model_iter_nth_child (GtkTreeMod
 static gboolean
 fm_list_model_iter_parent (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *child)
 {
-	return FALSE;
+	FMListModel *model;
+	FileEntry *file_entry;
+	
+	model = (FMListModel *)tree_model;
+	
+	file_entry = g_sequence_ptr_get_data (child->user_data);
+	
+	if (file_entry->parent == NULL) {
+		return FALSE;
+	}
+
+	iter->stamp = model->details->stamp;
+	iter->user_data = file_entry->parent->ptr;
+	
+	return TRUE;
 }
 
 gboolean
@@ -392,7 +486,7 @@ fm_list_model_get_tree_iter_from_file (F
 	}
 
 	g_assert (!g_sequence_ptr_is_end (ptr));
-	g_assert (g_sequence_ptr_get_data (ptr) == file);
+	g_assert (((FileEntry *)g_sequence_ptr_get_data (ptr))->file == file);
 	
 	if (iter != NULL) {
 		iter->stamp = model->details->stamp;
@@ -403,6 +497,35 @@ fm_list_model_get_tree_iter_from_file (F
 }
 
 static int
+fm_list_model_file_entry_compare_func (gconstpointer a,
+			    gconstpointer b,
+			    gpointer      user_data)
+{
+	FileEntry *file_entry1;
+	FileEntry *file_entry2;
+	FMListModel *model;
+	int result;
+
+	model = (FMListModel *)user_data;
+
+	file_entry1 = (FileEntry *)a;
+	file_entry2 = (FileEntry *)b;
+	
+	if (file_entry1->file != NULL && file_entry2->file != NULL) {
+		result = nautilus_file_compare_for_sort_by_attribute (file_entry1->file, file_entry2->file,
+							      model->details->sort_attribute,
+							      model->details->sort_directories_first,
+							      (model->details->order == GTK_SORT_DESCENDING));
+	} else if (file_entry1->file == NULL) {
+		return -1;
+	} else {
+		return 1;
+	}
+
+	return result;
+}
+
+static int
 fm_list_model_compare_func (gconstpointer a,
 			    gconstpointer b,
 			    gpointer      user_data)
@@ -416,7 +539,7 @@ fm_list_model_compare_func (gconstpointe
 
 	file1 = (NautilusFile *)a;
 	file2 = (NautilusFile *)b;
-
+	
 	result = nautilus_file_compare_for_sort_by_attribute (file1, file2,
 							      model->details->sort_attribute,
 							      model->details->sort_directories_first,
@@ -426,16 +549,14 @@ fm_list_model_compare_func (gconstpointe
 }
 
 static void
-fm_list_model_sort (FMListModel *model)
+fm_list_model_sort_file_entries (FMListModel *model, GSequence *files, GtkTreePath *path)
 {
-	GSequence *files;
 	GSequencePtr *old_order;
-	GtkTreePath *path;
 	int *new_order;
 	int length;
 	int i;
+	FileEntry *file_entry;
 
-	files = model->details->files;
 	length = g_sequence_get_length (files);
 
 	if (length <= 1) {
@@ -446,12 +567,19 @@ fm_list_model_sort (FMListModel *model)
 	old_order = g_new (GSequencePtr, length);
 	for (i = 0; i < length; ++i) {
 		GSequencePtr ptr = g_sequence_get_ptr_at_pos (files, i);
+		
+		file_entry = g_sequence_ptr_get_data (ptr);
+		if (file_entry->files != NULL) {
+			gtk_tree_path_append_index (path, i);
+			fm_list_model_sort_file_entries (model, file_entry->files, path);
+			gtk_tree_path_up (path);
+		}
 
 		old_order[i] = ptr;
 	}
 
 	/* sort */
-	g_sequence_sort (files, fm_list_model_compare_func, model);
+	g_sequence_sort (files, fm_list_model_file_entry_compare_func, model);
 
 	/* generate new order */
 	new_order = g_new (int, length);
@@ -461,17 +589,27 @@ fm_list_model_sort (FMListModel *model)
 	}
 
 	/* Let the world know about our new order */
-	path = gtk_tree_path_new ();
 
 	g_assert (new_order != NULL);
 	gtk_tree_model_rows_reordered (GTK_TREE_MODEL (model),
 				       path, NULL, new_order);
 
-	gtk_tree_path_free (path);
 	g_free (old_order);
 	g_free (new_order);
 }
 
+static void
+fm_list_model_sort (FMListModel *model)
+{
+	GtkTreePath *path;
+
+	path = gtk_tree_path_new ();
+
+	fm_list_model_sort_file_entries (model, model->details->files, path);
+
+	gtk_tree_path_free (path);
+}
+
 void
 fm_list_model_sort_files (FMListModel *model, GList **files)
 {
@@ -632,31 +770,87 @@ fm_list_model_multi_drag_data_delete (Eg
 	return TRUE;
 }
 
-void
+gboolean
 fm_list_model_add_file (FMListModel *model, NautilusFile *file)
 {
-	GtkTreeIter iter;
-	GtkTreePath *path;
-	GSequencePtr new_ptr;
+	GtkTreeIter iter, child_iter;
+	GtkTreePath *path, *child_path;
+	FileEntry *file_entry, *child_file_entry;
+	NautilusFile *parent_file;
+	GSequencePtr parent_ptr;
+	GSequence *files;
+	gboolean replace_dummy;
 	
 	/* We may only add each file once. */
 	if (fm_list_model_get_tree_iter_from_file (model, file, NULL)) {
-		return;
+		return FALSE;
 	}
 
 	nautilus_file_ref (file);
 	
-	new_ptr = g_sequence_insert_sorted (model->details->files, file,
-					    fm_list_model_compare_func, model);
+	file_entry = g_new0 (FileEntry, 1);
+	file_entry->file = file;
+	file_entry->parent = NULL;
+	file_entry->files = g_sequence_new ((GDestroyNotify)file_entry_free);
+	
+	files = model->details->files;
+	
+	replace_dummy = FALSE;
 
-	g_hash_table_insert (model->details->reverse_map, file, new_ptr);
+	parent_file = nautilus_file_get_parent (file);
+	if (parent_file != NULL) {
+		parent_ptr = g_hash_table_lookup (model->details->reverse_map,
+						  parent_file);
+		nautilus_file_unref (parent_file);
+		if (parent_ptr != NULL) {
+			file_entry->parent = g_sequence_ptr_get_data (parent_ptr);
+			files = file_entry->parent->files;
+			if (g_sequence_get_length (files) == 1) {
+				GSequencePtr dummy_ptr = g_sequence_get_ptr_at_pos (files, 0);
+				FileEntry *dummy_entry = g_sequence_ptr_get_data (dummy_ptr);
+				if (dummy_entry->file == NULL) {
+					/* replace the dummy loading entry */
+					g_sequence_remove (dummy_ptr);
+					
+					replace_dummy = TRUE;
+				}
+			}
+		}
+	}
+	
+	file_entry->ptr = g_sequence_insert_sorted (files, file_entry,
+					    fm_list_model_file_entry_compare_func, model);
+
+	g_hash_table_insert (model->details->reverse_map, file, file_entry->ptr);
 	
 	iter.stamp = model->details->stamp;
-	iter.user_data = new_ptr;
+	iter.user_data = file_entry->ptr;
 
 	path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
-	gtk_tree_model_row_inserted (GTK_TREE_MODEL (model), path, &iter);
+	if (replace_dummy) {
+		gtk_tree_model_row_changed (GTK_TREE_MODEL (model), path, &iter);
+	} else {
+		gtk_tree_model_row_inserted (GTK_TREE_MODEL (model), path, &iter);
+	}
+	if (nautilus_file_is_directory (file)) {
+	
+		child_file_entry = g_new0 (FileEntry, 1);
+		child_file_entry->parent = file_entry;
+		child_file_entry->ptr = g_sequence_insert_sorted (file_entry->files, child_file_entry,
+					    fm_list_model_file_entry_compare_func, model);
+		child_iter.stamp = model->details->stamp;
+		child_iter.user_data = child_file_entry->ptr;
+
+		child_path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &child_iter);
+		gtk_tree_model_row_inserted (GTK_TREE_MODEL (model), child_path, &child_iter);
+		gtk_tree_path_free (child_path);
+
+		gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (model),
+						      path, &iter);
+	}
 	gtk_tree_path_free (path);
+	
+	return TRUE;
 }
 
 void
@@ -671,7 +865,7 @@ fm_list_model_file_changed (FMListModel 
 		return;
 	}
 
-	g_sequence_ptr_sort_changed (ptr, fm_list_model_compare_func, model);
+	g_sequence_ptr_sort_changed (ptr, fm_list_model_file_entry_compare_func, model);
 
 	if (!fm_list_model_get_tree_iter_from_file (model, file, &iter)) {
 		return;
@@ -698,18 +892,52 @@ fm_list_model_get_length (FMListModel *m
 static void
 fm_list_model_remove (FMListModel *model, GtkTreeIter *iter)
 {
-	GSequencePtr ptr;
+	GSequencePtr ptr, child_ptr;
+	FileEntry *file_entry, *child_file_entry;
 	GtkTreePath *path;
+	GtkTreeIter dummy_iter;
 
 	path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), iter);
 	ptr = iter->user_data;
+	file_entry = g_sequence_ptr_get_data (ptr);
+	
+	if (file_entry->files != NULL) {
+		while (g_sequence_get_length (file_entry->files) > 0) {
+			child_ptr = g_sequence_get_begin_ptr (file_entry->files);
+			child_file_entry = g_sequence_ptr_get_data (child_ptr);
+			if (child_file_entry->file != NULL) {
+				fm_list_model_remove_file (model,
+							child_file_entry->file);
+			} else {
+				gtk_tree_path_append_index (path, 0);
+				g_sequence_remove (child_ptr);
+				gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path);
+				gtk_tree_path_up (path);
+			}
 
-	g_hash_table_remove (model->details->reverse_map, g_sequence_ptr_get_data (ptr));
-	g_sequence_remove (ptr);
+		}
+	}
+
+	g_hash_table_remove (model->details->reverse_map, file_entry->file);
+
+	if (file_entry->parent && g_sequence_get_length (file_entry->parent->files) == 1) {
+		/* this is the last child, change it to a dummy node */
+		dummy_iter.stamp = model->details->stamp++;
+		dummy_iter.user_data = ptr;
+		
+		nautilus_file_unref (file_entry->file);
+		file_entry->file = NULL;
+		file_entry->loaded = TRUE;
+		
+		gtk_tree_model_row_changed (GTK_TREE_MODEL (model), path,
+					    &dummy_iter);
+	} else {
+		g_sequence_remove (ptr);
 
-	model->details->stamp++;
+		model->details->stamp++;
+		gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path);
+	}
 	
-	gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path);
 	gtk_tree_path_free (path);
 }
 
@@ -723,20 +951,33 @@ fm_list_model_remove_file (FMListModel *
 	}
 }
 
-void
-fm_list_model_clear (FMListModel *model)
+static void
+fm_list_model_clear_directory (FMListModel *model, GSequence *files)
 {
 	GtkTreeIter iter;
+	FileEntry *file_entry;
+	
+	while (g_sequence_get_length (files) > 0) {
+		iter.user_data = g_sequence_get_begin_ptr (files);
 
-	g_return_if_fail (model != NULL);
-
-	while (g_sequence_get_length (model->details->files) > 0) {
+		file_entry = g_sequence_ptr_get_data (iter.user_data);
+		if (file_entry->files != NULL) {
+			fm_list_model_clear_directory (model, file_entry->files);
+		}
+		
 		iter.stamp = model->details->stamp;
-		iter.user_data = g_sequence_get_begin_ptr (model->details->files);
 		fm_list_model_remove (model, &iter);
 	}
 }
 
+void
+fm_list_model_clear (FMListModel *model)
+{
+	g_return_if_fail (model != NULL);
+
+	fm_list_model_clear_directory (model, model->details->files);
+}
+
 NautilusFile *
 fm_list_model_file_for_path (FMListModel *model, GtkTreePath *path)
 {
@@ -1017,7 +1258,7 @@ static void
 fm_list_model_init (FMListModel *model)
 {
 	model->details = g_new0 (FMListModelDetails, 1);
-	model->details->files = g_sequence_new ((GDestroyNotify)nautilus_file_unref);
+	model->details->files = g_sequence_new ((GDestroyNotify)file_entry_free);
 	model->details->reverse_map = g_hash_table_new (g_direct_hash, g_direct_equal);
 	model->details->stamp = g_random_int ();
 	model->details->sort_attribute = NULL;
@@ -1119,3 +1360,46 @@ fm_list_model_get_type (void)
 	
 	return object_type;
 }
+
+void
+fm_list_model_subdirectory_done_loading (FMListModel *model, NautilusDirectory *directory)
+{
+	GtkTreeIter iter;
+	GtkTreePath *path;
+	FileEntry *file_entry, *dummy_entry;
+	NautilusFile *parent_file;
+	GSequencePtr parent_ptr, dummy_ptr;
+	GSequence *files;
+
+	parent_file = nautilus_directory_get_corresponding_file (directory);
+	parent_ptr = g_hash_table_lookup (model->details->reverse_map,
+					  parent_file);
+	nautilus_file_unref (parent_file);
+	if (parent_ptr == NULL) {
+		return;
+	}
+	
+	file_entry = g_sequence_ptr_get_data (parent_ptr);
+	files = file_entry->files;
+	if (g_sequence_get_length (files) == 0) {
+		return;
+	}
+
+	dummy_ptr = g_sequence_get_ptr_at_pos (files, 0);
+	dummy_entry = g_sequence_ptr_get_data (dummy_ptr);
+	if (dummy_entry->file != NULL) {
+		/* real file */
+		return;
+	}
+	
+	dummy_entry->loaded = 1;
+	
+	iter.stamp = model->details->stamp;
+	iter.user_data = dummy_ptr;
+
+	path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
+	
+	gtk_tree_model_row_changed (GTK_TREE_MODEL (model), path, &iter);
+
+	gtk_tree_path_free (path);
+}
Index: src/file-manager/fm-list-model.h
===================================================================
RCS file: /cvs/gnome/nautilus/src/file-manager/fm-list-model.h,v
retrieving revision 1.13
diff -p -u -r1.13 fm-list-model.h
--- src/file-manager/fm-list-model.h	2 Jun 2005 16:16:55 -0000	1.13
+++ src/file-manager/fm-list-model.h	14 Jun 2005 19:43:59 -0000
@@ -26,6 +26,7 @@
 #include <gtk/gtktreeview.h>
 #include <gdk/gdkdnd.h>
 #include <libnautilus-private/nautilus-file.h>
+#include <libnautilus-private/nautilus-directory.h>
 #include <libnautilus-private/nautilus-icon-factory.h>
 #include <libnautilus-extension/nautilus-column.h>
 
@@ -70,7 +71,7 @@ typedef struct {
 } FMListModelClass;
 
 GType    fm_list_model_get_type                          (void);
-void     fm_list_model_add_file                          (FMListModel          *model,
+gboolean fm_list_model_add_file                          (FMListModel          *model,
 							  NautilusFile         *file);
 void     fm_list_model_file_changed                      (FMListModel          *model,
 							  NautilusFile         *file);
@@ -115,4 +116,8 @@ int               fm_list_model_add_colu
 					    NautilusColumn *column);
 int               fm_list_model_get_column_number (FMListModel *model,
 						   const char *column_name);
+						   
+void              fm_list_model_subdirectory_done_loading (FMListModel       *model,
+							    NautilusDirectory *directory);
+						   
 #endif /* FM_LIST_MODEL_H */
Index: src/file-manager/fm-list-view.c
===================================================================
RCS file: /cvs/gnome/nautilus/src/file-manager/fm-list-view.c,v
retrieving revision 1.245
diff -p -u -r1.245 fm-list-view.c
--- src/file-manager/fm-list-view.c	2 Jun 2005 16:16:55 -0000	1.245
+++ src/file-manager/fm-list-view.c	14 Jun 2005 19:44:01 -0000
@@ -430,8 +430,10 @@ motion_notify_callback (GtkWidget *widge
 				gtk_drag_set_icon_default (context);
 			}
 		}		      
+		return TRUE;
 	}
-	return TRUE;
+	
+	return FALSE;
 }
 
 static void
@@ -458,6 +460,8 @@ button_press_callback (GtkWidget *widget
 	static gint64 last_click_time = 0;
 	static int click_count = 0;
 	int double_click_time;
+	int expander_size, horizontal_separator;
+	NautilusFile *file;
 
 	view = FM_LIST_VIEW (callback_data);
 	tree_view = GTK_TREE_VIEW (widget);
@@ -543,12 +547,25 @@ button_press_callback (GtkWidget *widget
 				call_parent = FALSE;
 			} 
 			
+			file = fm_list_model_file_for_path (view->details->model, path);
+			if (file == NULL) {
+				/* this is the dummy loading node */
+				call_parent = FALSE;
+			} else {
+				nautilus_file_unref (file);
+			}
+
 			if ((event->button == 1 || event->button == 2) &&
 			    ((event->state & GDK_CONTROL_MASK) != 0 ||
 			     (event->state & GDK_SHIFT_MASK) == 0)) {
 				view->details->row_selected_on_button_down = gtk_tree_selection_path_is_selected (selection, path);
 				if (view->details->row_selected_on_button_down) {
-					call_parent = FALSE;
+					gtk_widget_style_get (widget,
+						"expander-size", &expander_size,
+						"horizontal-separator", &horizontal_separator,
+						NULL);
+					call_parent = (event->x <= horizontal_separator / 2 +
+						gtk_tree_path_get_depth (path) * expander_size);
 				} else if  ((event->state & GDK_CONTROL_MASK) != 0) {
 					call_parent = FALSE;
 					gtk_tree_selection_select_path (selection, path);
@@ -611,7 +628,7 @@ button_release_callback (GtkWidget *widg
 		stop_drag_check (view);
 		if (!view->details->drag_started) {
 			fm_list_view_did_not_drag (view, event);
-			return TRUE;
+			//return TRUE;
 		}
 	}
 	return FALSE;
@@ -739,6 +756,38 @@ popup_menu_callback (GtkWidget *widget, 
 	return TRUE;
 }
 
+static void
+subdirectory_done_loading_callback (NautilusDirectory *directory, FMListView *view)
+{
+	fm_list_model_subdirectory_done_loading (view->details->model, directory);
+}
+
+static void
+row_expanded_callback (GtkTreeView *treeview, GtkTreeIter *iter, GtkTreePath *path, gpointer callback_data)
+{
+ 	FMListView *view;
+ 	NautilusFile *file;
+ 	NautilusDirectory *directory;
+
+	view = FM_LIST_VIEW (callback_data);
+
+	file = fm_list_model_file_for_path (view->details->model, path);
+	directory = nautilus_directory_get_for_file (file);
+
+	fm_directory_view_add_subdirectory (FM_DIRECTORY_VIEW (view), directory);
+	
+	if (nautilus_directory_are_all_files_seen (directory)) {
+		fm_list_model_subdirectory_done_loading (view->details->model,
+							  directory);
+	} else {
+		g_signal_connect_object (directory, "done_loading",
+			G_CALLBACK (subdirectory_done_loading_callback),
+			view, 0);
+	}
+	
+	nautilus_directory_unref (directory);
+}
+
 static gboolean
 key_press_callback (GtkWidget *widget, GdkEventKey *event, gpointer callback_data)
 {
@@ -1069,6 +1118,8 @@ create_and_set_up_tree_view (FMListView 
 				 G_CALLBACK (key_press_callback), view, 0);
 	g_signal_connect_object (view->details->tree_view, "popup_menu",
                                  G_CALLBACK (popup_menu_callback), view, 0);
+	g_signal_connect_object (view->details->tree_view, "row_expanded",
+                                 G_CALLBACK (row_expanded_callback), view, 0);
 	
 	view->details->model = g_object_new (FM_TYPE_LIST_MODEL, NULL);
 	gtk_tree_view_set_model (view->details->tree_view, GTK_TREE_MODEL (view->details->model));
@@ -1180,7 +1231,15 @@ create_and_set_up_tree_view (FMListView 
 static void
 fm_list_view_add_file (FMDirectoryView *view, NautilusFile *file)
 {
-	fm_list_model_add_file (FM_LIST_VIEW (view)->details->model, file);
+	FMListModel *model;
+	
+	model = FM_LIST_VIEW (view)->details->model;
+	if (!fm_list_model_add_file (model, file)) {
+		fm_directory_view_remove_subdirectory (view,
+					nautilus_directory_get_for_file (file));
+		fm_list_model_remove_file (model, file);
+		fm_list_model_add_file (model, file);
+	}
 }
 
 static GList *
@@ -1374,8 +1433,10 @@ fm_list_view_get_selection_foreach_func 
 			    FM_LIST_MODEL_FILE_COLUMN, &file,
 			    -1);
 
-	nautilus_file_ref (file);
-	(* list) = g_list_prepend ((* list), file);
+	if (file != NULL) {
+		nautilus_file_ref (file);
+		(* list) = g_list_prepend ((* list), file);
+	}
 }
 
 static GList *


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