[PATCH] Another tree view patch (very preliminary)



Hi,

I have also been working on a patch for a tree view in nautilus. I took
a different route and based it on the tree sidebar view, and didn't use
FMDirectoryView as a subclass. As a result it is somewhat less
featureful than Juerg Billeter's patch (it really only displays the
files at the moment), but I think it has a cleaner separation of model
and view. It also does not keep non-visible items in memory (after a
timeout), and acts like the tree sidebar with respect to empty/loading
folders.

The attached patch is against CVS HEAD. I couldn't get 'cvs diff' to
include the new files in the patch so I've attached them separately.
They should go in src/file-manager.

Unfortunately, my graphics card has packed up and I am stuck in text
mode so I cannot really do any more development until July. So I am just
sending this code in the hope that someone finds it useful.

Bob

? src/file-manager/fm-directory-tree-model.c
? src/file-manager/fm-directory-tree-model.h
? src/file-manager/fm-directory-tree-view.c
? src/file-manager/fm-directory-tree-view.h
Index: src/nautilus-application.c
===================================================================
RCS file: /cvs/gnome/nautilus/src/nautilus-application.c,v
retrieving revision 1.234
diff -p -u -r1.234 nautilus-application.c
--- src/nautilus-application.c	2 May 2005 13:44:35 -0000	1.234
+++ src/nautilus-application.c	15 Jun 2005 08:45:05 -0000
@@ -34,6 +34,7 @@
 #include "file-manager/fm-icon-view.h"
 #include "file-manager/fm-list-view.h"
 #include "file-manager/fm-tree-view.h"
+#include "file-manager/fm-directory-tree-view.h"
 #include "nautilus-information-panel.h"
 #include "nautilus-history-sidebar.h"
 #include "nautilus-notes-viewer.h"
@@ -176,6 +177,7 @@ nautilus_application_instance_init (Naut
 	fm_icon_view_register ();
 	fm_desktop_icon_view_register ();
 	fm_list_view_register ();
+	fm_directory_tree_view_register ();
 
 	/* register sidebars */
 	nautilus_information_panel_register ();
Index: src/file-manager/Makefile.am
===================================================================
RCS file: /cvs/gnome/nautilus/src/file-manager/Makefile.am,v
retrieving revision 1.80
diff -p -u -r1.80 Makefile.am
--- src/file-manager/Makefile.am	25 Nov 2004 14:59:54 -0000	1.80
+++ src/file-manager/Makefile.am	15 Jun 2005 08:45:05 -0000
@@ -19,6 +19,10 @@ libnautilus_file_manager_la_SOURCES=	\
 	fm-desktop-icon-view.h 		\
 	fm-directory-view.c		\
 	fm-directory-view.h 		\
+        fm-directory-tree-view.c        \
+        fm-directory-tree-view.h        \
+        fm-directory-tree-model.c       \
+        fm-directory-tree-model.h       \
 	fm-ditem-page.c			\
 	fm-ditem-page.h			\
 	fm-error-reporting.c		\
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */

#include "fm-directory-tree-model.h"
#include <gtk/gtktreemodel.h>
#include <libnautilus-private/nautilus-file.h>
#include <libnautilus-private/nautilus-directory.h>
#include <gsequence/gsequence.h>

enum {
	START_LOADING,
	STOP_LOADING,
	LAST_SIGNAL
};

static guint directory_tree_model_signals[LAST_SIGNAL] = { 0, 0 };

typedef struct Node Node;

struct Node {
	int ref_count;

	FMDirectoryTreeModel *model;

	NautilusFile *file;

	GSequencePtr  owner;
	Node         *parent;

	/* Directory stuff */
	NautilusDirectory *directory;
	int all_children_ref_count;
	int dummy_child_ref_count;

	GSequence *children;

	guint done_loading_id;
	guint files_added_id;
	guint files_changed_id;

	guint done_loading : 1;
};

struct FMDirectoryTreeModelDetails {
	int stamp;

	Node *root;

	guint ensure_monitoring_idle_id;
	guint check_for_monitoring_cessation_timeout_id;

	GHashTable *file_to_node;
	int loading_count;

	NautilusFileAttributes attributes;
};

static void tree_model_iface_init (GtkTreeModelIface *iface);
static void destroy_node (FMDirectoryTreeModel *model, Node *node, GtkTreePath *path, gboolean include_dummy);
static void destroy_children (FMDirectoryTreeModel *model, Node *node, GtkTreePath *path, gboolean include_dummy);
static void schedule_check_monitoring_cessation (FMDirectoryTreeModel *model);

G_DEFINE_TYPE_WITH_CODE (FMDirectoryTreeModel, fm_directory_tree_model,
			 G_TYPE_OBJECT,
			 G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL,
						tree_model_iface_init));

static gboolean
node_has_dummy_child (Node *node)
{
	g_assert (node != NULL);
	return (node->directory != NULL
		&& (!node->done_loading
		    || g_sequence_get_length (node->children) == 0));
}

static void
increment_loading_count (FMDirectoryTreeModel *model)
{
	model->details->loading_count++;
	if (model->details->loading_count == 1) {
		g_signal_emit_by_name (G_OBJECT (model), "start_loading");
	}
}

static void
decrement_loading_count (FMDirectoryTreeModel *model)
{
	model->details->loading_count--;
	if (model->details->loading_count == 0) {
		g_signal_emit_by_name (G_OBJECT (model), "stop_loading");
	}
}

static GtkTreeModelFlags
model_get_flags (GtkTreeModel *model)
{
	FMDirectoryTreeModel *tree_model;

	tree_model = FM_DIRECTORY_TREE_MODEL (model);

	return GTK_TREE_MODEL_ITERS_PERSIST | GTK_TREE_MODEL_LIST_ONLY;
}

static gint
model_get_n_columns (GtkTreeModel *model)
{
	FMDirectoryTreeModel *tree_model;

	tree_model = FM_DIRECTORY_TREE_MODEL (model);

	return FM_DIRECTORY_TREE_MODEL_N_COLUMNS;
}

static GType
model_get_column_type (GtkTreeModel *model, gint index)
{
	g_assert (FM_IS_DIRECTORY_TREE_MODEL (model));

	switch (index) {
	case FM_DIRECTORY_TREE_MODEL_FILE_COLUMN:
		return NAUTILUS_TYPE_FILE;
	case FM_DIRECTORY_TREE_MODEL_LOADING_COLUMN:
		return G_TYPE_BOOLEAN;
	default:
		g_assert_not_reached ();
	}
}

static gboolean
model_get_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreePath *path)
{
	gint *indices;
	gint depth, i;
	GtkTreeIter parent;

	depth = gtk_tree_path_get_depth (path);
	indices = gtk_tree_path_get_indices (path);

	if (! gtk_tree_model_iter_nth_child (model, iter, NULL, indices[0])) {
		return FALSE;
	}

	for (i = 1; i < depth; i++) {
		parent = *iter;

		if (! gtk_tree_model_iter_nth_child (model, iter, &parent, indices[i])) {
			return FALSE;
		}
	}

	return TRUE;
}

static GtkTreePath *
model_get_path (GtkTreeModel *model, GtkTreeIter *iter)
{
	FMDirectoryTreeModel *tree_model;
	Node *node, *parent;
	GtkTreeIter parent_iter;
	GtkTreePath *path;
	int index;

	tree_model = FM_DIRECTORY_TREE_MODEL (model);

	g_return_val_if_fail (tree_model->details->stamp == iter->stamp, NULL);

	node = iter->user_data;
	if (node == NULL) {
		parent = iter->user_data2;
		g_assert (node_has_dummy_child (parent));
		index = 0;
	} else if (node == tree_model->details->root) {
		/* Internal case: outsiders should never get an iter pointing to the root */
		return gtk_tree_path_new ();
	} else {
		parent = node->parent;
		index = g_sequence_ptr_get_position (node->owner) + (node_has_dummy_child (parent) ? 1 : 0);
	}

	parent_iter.stamp = tree_model->details->stamp;
	parent_iter.user_data = parent;
	parent_iter.user_data2 = NULL;
	parent_iter.user_data3 = NULL;

	path = model_get_path (model, &parent_iter);
	
	gtk_tree_path_append_index (path, index);

	return path;
}

static void
model_get_value (GtkTreeModel *model, GtkTreeIter *iter, gint column, GValue *value)
{
	FMDirectoryTreeModel *tree_model;
	Node *node, *parent;

	tree_model = FM_DIRECTORY_TREE_MODEL (model);

	g_return_if_fail (tree_model->details->stamp == iter->stamp);

	node = iter->user_data;

	switch (column) {
	case FM_DIRECTORY_TREE_MODEL_FILE_COLUMN:
		g_value_init (value, NAUTILUS_TYPE_FILE);
		g_value_set_object (value, node == NULL ? NULL : node->file);
		break;
	case FM_DIRECTORY_TREE_MODEL_LOADING_COLUMN:
		g_value_init (value, G_TYPE_BOOLEAN);
		if (node == NULL) {
			parent = iter->user_data2;
			g_value_set_boolean (value, !parent->done_loading);
		} else {
			g_value_set_boolean (value, TRUE);
		}
		break;
	default:
		g_assert_not_reached ();
	}
}

static gboolean
make_iter_invalid (GtkTreeIter *iter)
{
	iter->stamp = 0;
	iter->user_data = NULL;
	iter->user_data2 = NULL;
	iter->user_data3 = NULL;
	return FALSE;
}

static gboolean
make_iter_for_dummy_child (Node *parent, GtkTreeIter *iter, int stamp)
{
	g_assert (parent != NULL);
	iter->stamp = stamp;
	iter->user_data = NULL;
	iter->user_data2 = parent;
	iter->user_data3 = NULL;
	return TRUE;
}

static gboolean
make_iter_for_node (Node *node, GtkTreeIter *iter, int stamp)
{
	if (node == NULL) {
		return make_iter_invalid (iter);
	}
	iter->stamp = stamp;
	iter->user_data = node;
	iter->user_data2 = NULL;
	iter->user_data3 = NULL;
	return TRUE;
}

static gboolean
model_iter_next (GtkTreeModel *model, GtkTreeIter *iter)
{
	FMDirectoryTreeModel *tree_model;
	Node *node, *parent;
	GSequencePtr ptr;

	tree_model = FM_DIRECTORY_TREE_MODEL (model);

	g_return_val_if_fail (tree_model->details->stamp == iter->stamp, FALSE);

	node = iter->user_data;
	if (node == NULL) {
		parent = iter->user_data2;
		g_assert (node_has_dummy_child (parent));
		ptr = g_sequence_get_begin_ptr (parent->children);
	} else {
		ptr = g_sequence_ptr_next (node->owner);
	}

	if (g_sequence_ptr_is_end (ptr)) {
		return make_iter_invalid (iter);
	}

	return make_iter_for_node (g_sequence_ptr_get_data (ptr),
				   iter, iter->stamp);
}

static gboolean
model_iter_children (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *parent_iter)
{
	FMDirectoryTreeModel *tree_model;
	Node *parent;

	tree_model = FM_DIRECTORY_TREE_MODEL (model);

	g_return_val_if_fail (parent_iter == NULL || tree_model->details->stamp == parent_iter->stamp, FALSE);

	if (parent_iter == NULL) {
		parent = tree_model->details->root;
	} else {
		parent = parent_iter->user_data;
	}

	if (node_has_dummy_child (parent)) {
		return make_iter_for_dummy_child (parent, iter, tree_model->details->stamp);
	}

	if (parent->children != NULL && g_sequence_get_length (parent->children) != 0) {
		return make_iter_for_node (g_sequence_ptr_get_data (g_sequence_get_begin_ptr (parent->children)),
					   iter, tree_model->details->stamp);
	}

	return make_iter_invalid (iter);
}

static gboolean
model_iter_has_child (GtkTreeModel *model, GtkTreeIter *iter)
{
	FMDirectoryTreeModel *tree_model;
	Node *node;

	tree_model = FM_DIRECTORY_TREE_MODEL (model);

	g_return_val_if_fail (tree_model->details->stamp == iter->stamp, FALSE);

	node = iter->user_data;

	return node != NULL && node->directory != NULL;
}

static gint
model_iter_n_children (GtkTreeModel *model, GtkTreeIter *iter)
{
	FMDirectoryTreeModel *tree_model;
	Node *parent;
	
	tree_model = FM_DIRECTORY_TREE_MODEL (model);

	g_return_val_if_fail (iter == NULL || iter->stamp == tree_model->details->stamp, 0);

	if (iter == NULL) {
		parent = tree_model->details->root;
	} else {
		parent = iter->user_data;
	}

	if (parent == NULL || parent->directory == NULL) {
		return 0;
	}

	return g_sequence_get_length (parent->children) + (node_has_dummy_child (parent) ? 1 : 0);
}

static gboolean
model_iter_nth_child (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *parent_iter, gint n)
{
	FMDirectoryTreeModel *tree_model;
	Node *parent;
	GSequencePtr ptr;
	int offset;

	tree_model = FM_DIRECTORY_TREE_MODEL (model);

	g_return_val_if_fail (parent_iter == NULL
			      || tree_model->details->stamp == parent_iter->stamp, FALSE);

	if (parent_iter == NULL) {
		parent = tree_model->details->root;
	} else {
		parent = parent_iter->user_data;
	}

	if (parent == NULL || parent->directory == NULL) {
		return make_iter_invalid (iter);
	}

	if (node_has_dummy_child (parent)) {
		if (n == 0) {
			return make_iter_for_dummy_child (parent, iter, tree_model->details->stamp);
		} else {
			offset = 1;
		}
	} else {
		offset = 0;
	}

	ptr = g_sequence_get_ptr_at_pos (parent->children, n - offset);
	if (ptr == NULL || g_sequence_ptr_is_end (ptr)) {
		return make_iter_invalid (iter);
	}

	return make_iter_for_node (g_sequence_ptr_get_data (ptr), iter, tree_model->details->stamp);
}

static gboolean
model_iter_parent (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *child_iter)
{
	FMDirectoryTreeModel *tree_model;
	Node *node, *parent;

	tree_model = FM_DIRECTORY_TREE_MODEL (model);

	g_return_val_if_fail (tree_model->details->stamp == child_iter->stamp, FALSE);

	node = child_iter->user_data;
	if (node == NULL) {
		parent = child_iter->user_data2;
	} else {
		parent = node->parent;
	}

	if (parent == tree_model->details->root) {
		return make_iter_invalid (iter);
	}

	return make_iter_for_node (parent, iter, tree_model->details->stamp);
}

static void
stop_monitoring_directory (FMDirectoryTreeModel *model, Node *node)
{
	g_assert (node->directory != NULL); // we only set callbacks on directories

	if (node->done_loading_id == 0) {
		g_assert (node->files_added_id == 0);
		g_assert (node->files_changed_id == 0);
		return;
	}

	//g_print ("Stopping monitoring : %s\n", nautilus_file_get_uri (node->file));

	g_signal_handler_disconnect (node->directory, node->done_loading_id);
	g_signal_handler_disconnect (node->directory, node->files_added_id);
	g_signal_handler_disconnect (node->directory, node->files_changed_id);

	node->done_loading_id = 0;
	node->files_added_id = 0;
	node->files_changed_id = 0;

	nautilus_directory_file_monitor_remove (node->directory, model);
}

static void
report_dummy_row_contents_changed (FMDirectoryTreeModel *model, Node *parent)
{
	GtkTreePath *path;
	GtkTreeIter iter;

	make_iter_for_dummy_child (parent, &iter, model->details->stamp);
	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);
}

static void
report_dummy_row_inserted (FMDirectoryTreeModel *model, Node *parent)
{
	GtkTreeIter iter;
	GtkTreePath *path;

	make_iter_for_dummy_child (parent, &iter, model->details->stamp);
	path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
	gtk_tree_model_row_inserted (GTK_TREE_MODEL (model), path, &iter);
	gtk_tree_path_free (path);
}

static void
report_dummy_row_deleted (FMDirectoryTreeModel *model, Node *parent, gboolean inserting_first_child)
{
	GtkTreeIter iter;
	GtkTreePath *path;

	g_assert (!node_has_dummy_child (parent));

	make_iter_for_node (parent, &iter, model->details->stamp);
	path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
	gtk_tree_path_append_index (path, inserting_first_child ? 1 : 0);
	gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path);
	gtk_tree_path_free (path);
	parent->all_children_ref_count -= parent->dummy_child_ref_count;
	parent->dummy_child_ref_count = 0;
}

static void
set_done_loading (FMDirectoryTreeModel *model, Node *node, gboolean done_loading)
{
	gboolean had_dummy;

	if (node->done_loading == done_loading) {
		return;
	}

	had_dummy = node_has_dummy_child (node);

	node->done_loading = done_loading;

	if (done_loading) {
		decrement_loading_count (model);
	}

	if (node_has_dummy_child (node)) {
		if (had_dummy) {
			report_dummy_row_contents_changed (model, node);
		} else {
			report_dummy_row_inserted (model, node);
		}
	} else {
		if (had_dummy) {
			report_dummy_row_deleted (model, node, FALSE);
		} else {
			g_assert_not_reached ();
		}
	}
}

static int
node_cmp_func (gconstpointer a,
	       gconstpointer b,
	       gpointer user_data)
{
	Node *nodea, *nodeb;
	/*FMDirectoryTreeModel *model;

	model = (FMDirectoryTreeModel *)model;*/
	nodea = (Node *)a;
	nodeb = (Node *)b;

	return nautilus_file_compare_for_sort (nodea->file, nodeb->file,
					       NAUTILUS_FILE_SORT_BY_DISPLAY_NAME,
					       TRUE, FALSE);
}

static Node *
partially_create_node (FMDirectoryTreeModel *model, NautilusFile *file)
{
	Node *node;

	g_assert (g_hash_table_lookup (model->details->file_to_node, file) == NULL);

	node = g_new0 (Node, 1);

	node->model = model;
	node->file = nautilus_file_ref (file);

	g_hash_table_insert (model->details->file_to_node, file, node);

	return node;
}

static void
update_node (FMDirectoryTreeModel *model, Node *node)
{
	GtkTreePath *path;
	GtkTreeIter iter;

	if (node->directory == NULL && nautilus_file_is_directory (node->file)) {
		node->directory = nautilus_directory_get_for_file (node->file);
		node->children = g_sequence_new (NULL);
	} else if (node->directory != NULL && !nautilus_file_is_directory (node->file)) {
		stop_monitoring_directory (model, node);
		make_iter_for_node (node, &iter, model->details->stamp);
		path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
		destroy_children (model, node, path, TRUE);
		gtk_tree_path_free (path);
		nautilus_directory_unref (node->directory);
		g_sequence_free (node->children);
	}
}

static void
report_node_inserted (FMDirectoryTreeModel *model, Node *node, GtkTreePath *path)
{
	GtkTreeIter iter;
	GSequencePtr ptr;

	//g_print ("Inserting : %s at %s\n", nautilus_file_get_uri (node->file), gtk_tree_path_to_string (path));

	node->ref_count = 0;
	if (node->children) {
		node->all_children_ref_count = 0;
		node->dummy_child_ref_count = 0;
	}

	make_iter_for_node (node, &iter, model->details->stamp);
	gtk_tree_model_row_inserted (GTK_TREE_MODEL (model), path, &iter);

	if (node->children) {
		gtk_tree_path_down (path);

		if (node_has_dummy_child (node)) {
			//g_print ("   with dummy node at %s\n", gtk_tree_path_to_string (path));
			make_iter_for_dummy_child (node, &iter, model->details->stamp);
			gtk_tree_model_row_inserted (GTK_TREE_MODEL (model), path, &iter);
			gtk_tree_path_next (path);
		}

		for (ptr = g_sequence_get_begin_ptr (node->children); !g_sequence_ptr_is_end (ptr); ptr = g_sequence_ptr_next (ptr)) {
			report_node_inserted (model, g_sequence_ptr_get_data (ptr), path);
			gtk_tree_path_next (path);
		}

		gtk_tree_path_up (path);
	}
}

static void
insert_node (FMDirectoryTreeModel *model, Node *parent, Node *node)
{
	GtkTreePath *path;
	GtkTreeIter iter;
	gboolean parent_was_empty;

	g_assert (parent != NULL);
	g_assert (parent->directory != NULL && parent->children != NULL);

	parent_was_empty = (g_sequence_get_length (parent->children) == 0);

	node->parent = parent;
	node->owner = g_sequence_insert_sorted (parent->children, node, node_cmp_func, model);

	update_node (model, node);

	make_iter_for_node (node, &iter, model->details->stamp);
	path = model_get_path (GTK_TREE_MODEL (model), &iter);
	report_node_inserted (model, node, path);
	gtk_tree_path_free (path);

	if (parent_was_empty && !node_has_dummy_child (parent)) {
		report_dummy_row_deleted (model, parent, TRUE);
	}
}

static void
files_changed_callback (NautilusDirectory *directory,
			GList *changed_files,
			gpointer callback_data)
{
	FMDirectoryTreeModel *model;
	NautilusFile *file, *parent_file;
	Node *parent, *node, *new_parent;
	GtkTreeIter iter;
	GtkTreePath *path;
	GList *elem;
	gboolean parent_had_dummy_child, not_in_this_directory;
	gboolean had_dummy_child, had_directory;
	gboolean has_dummy_child, has_directory;

	parent = (Node *)callback_data;

	g_assert (parent != NULL);

	model = parent->model;

	for (elem = changed_files; elem != NULL; elem = elem->next) {
		file = NAUTILUS_FILE (elem->data);

		//g_print ("(%p(%p)) : File changed: %s : ", model, parent, nautilus_file_get_uri (file));

		node = g_hash_table_lookup (model->details->file_to_node, file);

		if (node != NULL) {
			make_iter_for_node (node, &iter, model->details->stamp);
			path = model_get_path (GTK_TREE_MODEL (model), &iter);

			if (nautilus_file_is_gone (file) || !nautilus_file_should_show (file, FALSE, FALSE)) {
				new_parent = NULL;
				not_in_this_directory = TRUE;
			} else if (!nautilus_directory_contains_file (parent->directory, file)) {
				parent_file = nautilus_file_get_parent (file);
				new_parent = g_hash_table_lookup (model->details->file_to_node, parent_file);
				nautilus_file_unref (parent_file);
				not_in_this_directory = TRUE;
			} else {
				not_in_this_directory = FALSE;
			}

			if (not_in_this_directory) {
				/* This node needs reparenting, or removal */
				parent_had_dummy_child = node_has_dummy_child (parent);

				g_sequence_remove (node->owner);
				parent->all_children_ref_count -= node->ref_count;
				node->ref_count = 0;
				node->parent = NULL;
				if (node_has_dummy_child (parent) && !parent_had_dummy_child) {
					report_dummy_row_inserted (model, parent);
					gtk_tree_path_next (path);
				}

				gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path);

				if (new_parent != NULL && new_parent->all_children_ref_count != 0) {
					insert_node (model, new_parent, node);
				} else {
					destroy_node (model, node, NULL, FALSE);
				}
				schedule_check_monitoring_cessation (model);
			} else {
				had_dummy_child = node_has_dummy_child (node);
				had_directory = node->directory != NULL;

				update_node (model, node);

				has_dummy_child = node_has_dummy_child (node);
				has_directory = node->directory != NULL;

				if (had_dummy_child != has_dummy_child) {
					if (has_dummy_child) {
						report_dummy_row_inserted (model, node);
					} else {
						report_dummy_row_deleted (model, node, FALSE);
					}
				}
				if (had_directory != has_directory) {
					gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (model), path, &iter);
				}

				gtk_tree_model_row_changed (GTK_TREE_MODEL (model), path, &iter);
			}
			gtk_tree_path_free (path);
		} else {
			if (nautilus_file_should_show (file, FALSE, FALSE)) {
				node = partially_create_node (model, file);
				insert_node (model, parent, node);
			}
		}
	}
}

static void
done_loading_callback (NautilusDirectory *directory,
		       gpointer callback_data)
{
	Node *node;

	node = (Node *)callback_data;

	set_done_loading (node->model, node, TRUE);
}

static void
start_monitoring_directory (FMDirectoryTreeModel *model, Node *node)
{
	g_assert (node->directory != NULL); /* we only set callbacks on directories */

	if (node->done_loading_id != 0) {
		g_assert (node->files_added_id != 0);
		g_assert (node->files_changed_id != 0);
		return;
	}

	//g_print ("Start monitoring: %s in %p(%p)\n", nautilus_file_get_uri (node->file), model, node);

	g_assert (node->files_added_id == 0);
	g_assert (node->files_changed_id == 0);

	node->done_loading_id = g_signal_connect
		(node->directory, "done_loading",
		G_CALLBACK (done_loading_callback), node);
	node->files_added_id = g_signal_connect
		(node->directory, "files_added",
		 G_CALLBACK (files_changed_callback), node);
	node->files_changed_id = g_signal_connect
		(node->directory, "files_changed",
		G_CALLBACK (files_changed_callback), node);

	nautilus_directory_file_monitor_add (node->directory, model,
					     FALSE, FALSE,
					     model->details->attributes,
					     files_changed_callback, node);

	increment_loading_count (model);
	set_done_loading (model, node, nautilus_directory_are_all_files_seen (node->directory));
}

static void
check_for_monitoring_cessation_at_node (FMDirectoryTreeModel *model, Node *node, GtkTreePath *path)
{
	GSequencePtr ptr;

	if (node->directory == NULL) {
		return;
	}

	if (node->all_children_ref_count == 0) {
		stop_monitoring_directory (model, node);
		destroy_children (model, node, path, FALSE);
		set_done_loading (model, node, FALSE);
	} else {
		gtk_tree_path_down (path);

		for (ptr = g_sequence_get_begin_ptr (node->children);
		     !g_sequence_ptr_is_end (ptr);
		     ptr = g_sequence_ptr_next (ptr)) {
			check_for_monitoring_cessation_at_node (model, g_sequence_ptr_get_data (ptr), path);
			gtk_tree_path_next (path);
		}

		gtk_tree_path_up (path);
	}
}

static gboolean
check_for_monitoring_cessation_callback (gpointer callback_data)
{
	FMDirectoryTreeModel *model;
	GtkTreePath *path;

	model = FM_DIRECTORY_TREE_MODEL (callback_data);
	model->details->check_for_monitoring_cessation_timeout_id = 0;
	path = gtk_tree_path_new ();
	check_for_monitoring_cessation_at_node (model, model->details->root, path);
	gtk_tree_path_free (path);
	return FALSE;
}

static void
schedule_check_monitoring_cessation (FMDirectoryTreeModel *model)
{
	if (model->details->check_for_monitoring_cessation_timeout_id != 0) {
		g_source_remove (model->details->check_for_monitoring_cessation_timeout_id);
	}

	model->details->check_for_monitoring_cessation_timeout_id = 
		g_timeout_add (5000, check_for_monitoring_cessation_callback, model);
}

static void
ensure_monitoring_at_node (FMDirectoryTreeModel *model, Node *node)
{
	GSequencePtr ptr;

	if (node->directory == NULL || node->all_children_ref_count == 0) {
		return;
	}

	for (ptr = g_sequence_get_begin_ptr (node->children);
	     !g_sequence_ptr_is_end (ptr);
	     ptr = g_sequence_ptr_next (ptr)) {
		ensure_monitoring_at_node (model, g_sequence_ptr_get_data (ptr));
	}

	start_monitoring_directory (model, node);
}

static gboolean
ensure_monitoring_callback (gpointer callback_data)
{
	FMDirectoryTreeModel *model;

	model = FM_DIRECTORY_TREE_MODEL (callback_data);
	model->details->ensure_monitoring_idle_id = 0;
	ensure_monitoring_at_node (model, model->details->root);
	return FALSE;
}

static void
schedule_ensure_monitoring (FMDirectoryTreeModel *model)
{
	if (model->details->ensure_monitoring_idle_id == 0) {
		model->details->ensure_monitoring_idle_id =
			g_idle_add (ensure_monitoring_callback, model);
	}
}

static void
model_ref_node (GtkTreeModel *model, GtkTreeIter *iter)
{
	FMDirectoryTreeModel *tree_model;
	Node *node, *parent;

	tree_model = FM_DIRECTORY_TREE_MODEL (model);

	g_return_if_fail (tree_model->details->stamp == iter->stamp);

	node = iter->user_data;
	if (node == NULL) {
		parent = iter->user_data2;
		parent->dummy_child_ref_count++;
		//g_print ("Reffing (%d) dummy node under: %s\n", parent->dummy_child_ref_count, nautilus_file_get_uri (parent->file));
	} else {
		parent = node->parent;
		node->ref_count++;
		//g_print ("Reffing (%d) node: %s\n", node->ref_count, nautilus_file_get_uri (node->file));
	}

	g_assert (parent->all_children_ref_count >= 0);
	if (++parent->all_children_ref_count == 1) {
		schedule_ensure_monitoring (tree_model);
	}
	//g_print ("All child rc = %d, for: %s\n", parent->all_children_ref_count, nautilus_file_get_uri (parent->file));
}

static void
model_unref_node (GtkTreeModel *model, GtkTreeIter *iter)
{
	FMDirectoryTreeModel *tree_model;
	Node *node, *parent;

	tree_model = FM_DIRECTORY_TREE_MODEL (model);

	g_return_if_fail (tree_model->details->stamp == iter->stamp);

	node = iter->user_data;
	if (node == NULL) {
		parent = iter->user_data2;
		parent->dummy_child_ref_count--;
		//g_print ("UnReffing (%d) dummy node under: %s\n", parent->dummy_child_ref_count, nautilus_file_get_uri (parent->file));
	} else {
		parent = node->parent;
		node->ref_count--;
		//g_print ("UnReffing (%d) node: %s\n", node->ref_count, nautilus_file_get_uri (node->file));
	}

	g_assert (parent->all_children_ref_count > 0);
	if (--parent->all_children_ref_count == 0) {
		schedule_check_monitoring_cessation (tree_model);
	}
	//g_print ("All child rc = %d, for: %s\n", parent->all_children_ref_count, nautilus_file_get_uri (parent->file));
}

static Node *
make_new_root_node (FMDirectoryTreeModel *model, const char *uri)
{
	Node *node;

	node = g_new0 (Node, 1);

	node->model = model;
	node->owner = NULL;
	node->parent = NULL;
	node->directory = nautilus_directory_get (uri);
	node->file = nautilus_directory_get_corresponding_file (node->directory);
	node->all_children_ref_count = 0;
	node->children = g_sequence_new (NULL);
	node->done_loading = 0;

	return node;
}

static void destroy_children (FMDirectoryTreeModel *model, Node *node, GtkTreePath *path, gboolean include_dummy)
{
	GSequencePtr ptr, next;
	Node *child;

	if (path != NULL) {
		gtk_tree_path_down (path);
	}

	if (node_has_dummy_child (node)) {
		if (include_dummy) {
			node->all_children_ref_count -= node->dummy_child_ref_count;
			node->dummy_child_ref_count = 0;
		}

		if (path != NULL) {
			if (include_dummy) {
				//g_print ("Removing dummy node under: %s\n", nautilus_file_get_uri (node->file));
				gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path);
			} else {
				gtk_tree_path_next (path);
			}
		}
	}

	for (ptr = g_sequence_get_begin_ptr (node->children); !g_sequence_ptr_is_end (ptr); ) {
		child = g_sequence_ptr_get_data (ptr);

		destroy_node (model, child, path, include_dummy);

		next = g_sequence_ptr_next (ptr);
		g_sequence_remove (ptr);
		ptr = next;
	}

	if (path != NULL) {
		gtk_tree_path_up (path);
	}
}

static void
destroy_node (FMDirectoryTreeModel *model, Node *node, GtkTreePath *path, gboolean include_dummy)
{
	if (node->directory != NULL) {
		stop_monitoring_directory (model, node);

		destroy_children (model, node, path, include_dummy);

		nautilus_directory_unref (node->directory);
		g_sequence_free (node->children);
	}

	if (node->parent != NULL) {
		node->parent->all_children_ref_count -= node->ref_count;
	}

	//g_print ("Removing (%d): %s\n", node->ref_count, nautilus_file_get_uri (node->file));

	g_hash_table_remove (node->model->details->file_to_node, node->file);
	nautilus_file_unref (node->file);
	g_free (node);

	if (path != NULL && node != model->details->root) {
		gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path);
	}
}

void
fm_directory_tree_model_set_directory (FMDirectoryTreeModel *model,
				       const char *uri)
{
	GtkTreePath *path;

	g_assert (FM_IS_DIRECTORY_TREE_MODEL (model));

	if (model->details->root != NULL) {
		path = gtk_tree_path_new ();

		destroy_node (model, model->details->root, path, TRUE);

		gtk_tree_path_free (path);
	}

	model->details->root = make_new_root_node (model, uri);

	g_hash_table_insert (model->details->file_to_node,
			     model->details->root->file,
			     model->details->root);

	/* Report insertion of the initial "Loading..." row */
	report_dummy_row_inserted (model, model->details->root);
}

void
fm_directory_tree_model_set_attributes (FMDirectoryTreeModel *model, NautilusFileAttributes attributes)
{
	model->details->attributes = attributes | NAUTILUS_FILE_ATTRIBUTE_IS_DIRECTORY;
}

static void
tree_model_iface_init (GtkTreeModelIface *iface)
{
	iface->get_flags = model_get_flags;
	iface->get_n_columns = model_get_n_columns;
	iface->get_column_type = model_get_column_type;
	iface->get_iter = model_get_iter;
	iface->get_path = model_get_path;
	iface->get_value = model_get_value;
	iface->iter_next = model_iter_next;
	iface->iter_children = model_iter_children;
	iface->iter_has_child = model_iter_has_child;
	iface->iter_n_children = model_iter_n_children;
	iface->iter_nth_child = model_iter_nth_child;
	iface->iter_parent = model_iter_parent;
	iface->ref_node = model_ref_node;
	iface->unref_node = model_unref_node;
}

static void
fm_directory_tree_model_finalize (GObject *object)
{
	FMDirectoryTreeModel *model;

	model = FM_DIRECTORY_TREE_MODEL (object);

	if (model->details->check_for_monitoring_cessation_timeout_id != 0) {
		g_source_remove (model->details->check_for_monitoring_cessation_timeout_id);
	}
	if (model->details->ensure_monitoring_idle_id != 0) {
		g_source_remove (model->details->ensure_monitoring_idle_id);
	}

	destroy_node (model, model->details->root, NULL, TRUE);

	g_hash_table_destroy (model->details->file_to_node);

	g_free (model->details);

	G_OBJECT_CLASS (fm_directory_tree_model_parent_class)->finalize (object);
}

static void
fm_directory_tree_model_class_init (FMDirectoryTreeModelClass *klass)
{
	G_OBJECT_CLASS (klass)->finalize = fm_directory_tree_model_finalize;

	directory_tree_model_signals[START_LOADING] =
		g_signal_new ("start_loading",
			      FM_TYPE_DIRECTORY_TREE_MODEL,
			      G_SIGNAL_RUN_LAST,
			      G_STRUCT_OFFSET (FMDirectoryTreeModelClass, start_loading),
			      NULL, NULL,
			      g_cclosure_marshal_VOID__VOID,
			      G_TYPE_NONE, 0);

	directory_tree_model_signals[STOP_LOADING] =
		g_signal_new ("stop_loading",
			      FM_TYPE_DIRECTORY_TREE_MODEL,
			      G_SIGNAL_RUN_LAST,
			      G_STRUCT_OFFSET (FMDirectoryTreeModelClass, stop_loading),
			      NULL, NULL,
			      g_cclosure_marshal_VOID__VOID,
			      G_TYPE_NONE, 0);
}

static void
fm_directory_tree_model_init (FMDirectoryTreeModel *model)
{
	model->details = g_new0 (FMDirectoryTreeModelDetails, 1);

	model->details->file_to_node = g_hash_table_new (g_direct_hash, g_direct_equal);

	do {
		model->details->stamp = g_random_int ();
	} while (model->details->stamp == 0);

	model->details->attributes = NAUTILUS_FILE_ATTRIBUTE_IS_DIRECTORY;
}
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */

#ifndef FM_DIRECTORY_TREE_MODEL_H
#define FM_DIRECTORY_TREE_MODEL_H

#include <glib-object.h>
#include <libnautilus-private/nautilus-file-attributes.h>

#define FM_TYPE_DIRECTORY_TREE_MODEL            (fm_directory_tree_model_get_type ())
#define FM_DIRECTORY_TREE_MODEL(obj)            (GTK_CHECK_CAST ((obj), FM_TYPE_DIRECTORY_TREE_MODEL, FMDirectoryTreeModel))
#define FM_DIRECTORY_TREE_MODEL_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), FM_TYPE_DIRECTORY_TREE_MODEL, FMDirectoryTreeModelClass))
#define FM_IS_DIRECTORY_TREE_MODEL(obj)         (GTK_CHECK_TYPE ((obj), FM_TYPE_DIRECTORY_TREE_MODEL))
#define FM_IS_DIRECTORY_TREE_MODEL_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), FM_TYPE_DIRECTORY_TREE_MODEL))

typedef struct FMDirectoryTreeModelDetails FMDirectoryTreeModelDetails;

typedef struct {
	GObject parent;
	FMDirectoryTreeModelDetails *details;
} FMDirectoryTreeModel;

typedef struct {
	GObjectClass parent_class;

	/* Signals */
	void (*start_loading) (void);
	void (*stop_loading) (void);
} FMDirectoryTreeModelClass;

enum {
	FM_DIRECTORY_TREE_MODEL_FILE_COLUMN,
	FM_DIRECTORY_TREE_MODEL_LOADING_COLUMN,
	FM_DIRECTORY_TREE_MODEL_N_COLUMNS
};

GType fm_directory_tree_model_get_type (void);
void  fm_directory_tree_model_set_directory (FMDirectoryTreeModel *model,
					     const char *uri);
void  fm_directory_tree_model_set_attributes (FMDirectoryTreeModel *model,
					      NautilusFileAttributes attributes);

#endif /* FM_DIRECTORY_TREE_MODEL_H */
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */

#include "fm-directory-tree-view.h"
#include <gtk/gtk.h>
#include <eel/eel-cell-renderer-pixbuf-list.h>
#include <libnautilus-private/nautilus-view.h>
#include <libnautilus-private/nautilus-view-factory.h>
#include <libnautilus-private/nautilus-window-info.h>
#include <libnautilus-private/nautilus-tree-view-drag-dest.h>
#include <libnautilus-private/nautilus-icon-factory.h>
#include "fm-directory-tree-model.h"

struct FMDirectoryTreeViewDetails {
	NautilusWindowInfo *window;
	GtkWidget *view;
	FMDirectoryTreeModel *model;
	char *uri;
	NautilusTreeViewDragDest *drag_dest;
	guint initial_load : 1;
};

static void fm_directory_tree_view_iface_init (NautilusViewIface *iface);

G_DEFINE_TYPE_WITH_CODE (FMDirectoryTreeView, fm_directory_tree_view, GTK_TYPE_SCROLLED_WINDOW,
			 G_IMPLEMENT_INTERFACE (NAUTILUS_TYPE_VIEW,
						fm_directory_tree_view_iface_init));

static const char *
fm_directory_tree_view_get_id (NautilusView *view)
{
     return FM_DIRECTORY_TREE_VIEW_ID;
}

static GtkWidget *
fm_directory_tree_view_get_widget (NautilusView *view)
{
     return GTK_WIDGET (view);
}

static void
fm_directory_tree_view_load_location (NautilusView *view,
			    const char *location)
{
     FMDirectoryTreeView *directory_tree_view;

     directory_tree_view = FM_DIRECTORY_TREE_VIEW (view);

     fm_directory_tree_model_set_directory (directory_tree_view->details->model, location);

     if (directory_tree_view->details->uri != NULL) {
	     g_free (directory_tree_view->details->uri);
     }

     directory_tree_view->details->uri = g_strdup (location);
}

static void
fm_directory_tree_view_model_start_loading (FMDirectoryTreeModel *model, gpointer user_data)
{
	FMDirectoryTreeView *directory_tree_view;

	directory_tree_view = FM_DIRECTORY_TREE_VIEW (user_data);

	nautilus_window_info_report_load_underway (directory_tree_view->details->window, NAUTILUS_VIEW (directory_tree_view));
	if (directory_tree_view->details->initial_load) {
		nautilus_window_info_show_window (directory_tree_view->details->window);
		directory_tree_view->details->initial_load = FALSE;
	}
}

static void
fm_directory_tree_view_model_stop_loading (FMDirectoryTreeModel *model, gpointer user_data)
{
	FMDirectoryTreeView *directory_tree_view;

	directory_tree_view = FM_DIRECTORY_TREE_VIEW (user_data);

	nautilus_window_info_report_load_complete (directory_tree_view->details->window, NAUTILUS_VIEW (directory_tree_view));
}

static void
fm_directory_tree_view_stop_loading (NautilusView *view)
{
}

static int
fm_directory_tree_view_get_selection_count (NautilusView *view)
{
     return 0;
}

static GList *
fm_directory_tree_view_get_selection (NautilusView *view)
{
     return NULL;
}

static void
fm_directory_tree_view_set_selection (NautilusView *view, GList *new_selection)
{
}

static char *
fm_directory_tree_view_get_first_visible_file (NautilusView *view)
{
     return g_strdup ("");
}

static void
fm_directory_tree_view_scroll_to_file (NautilusView *view, const char *uri)
{
}

static gboolean
fm_directory_tree_view_supports_zooming (NautilusView *view)
{
     return FALSE;
}

static void
fm_directory_tree_view_bump_zoom_level (NautilusView *view, int increment)
{
}

static void
fm_directory_tree_view_zoom_to_level (NautilusView *view, NautilusZoomLevel level)
{
}

static NautilusZoomLevel
fm_directory_tree_view_get_zoom_level (NautilusView *view)
{
     return NAUTILUS_ZOOM_LEVEL_STANDARD;
}

static void
fm_directory_tree_view_restore_default_zoom_level (NautilusView *view)
{
}

static gboolean
fm_directory_tree_view_can_zoom_in (NautilusView *view)
{
     return FALSE;
}

static gboolean
fm_directory_tree_view_can_zoom_out (NautilusView *view)
{
     return FALSE;
}

static void
fm_directory_tree_view_iface_init (NautilusViewIface *iface)
{
     iface->get_view_id = fm_directory_tree_view_get_id;
     iface->get_widget = fm_directory_tree_view_get_widget;
     iface->load_location = fm_directory_tree_view_load_location;
     iface->stop_loading = fm_directory_tree_view_stop_loading;
     iface->get_selection_count = fm_directory_tree_view_get_selection_count;
     iface->get_selection = fm_directory_tree_view_get_selection;
     iface->set_selection = fm_directory_tree_view_set_selection;
     iface->get_first_visible_file = fm_directory_tree_view_get_first_visible_file;
     iface->scroll_to_file = fm_directory_tree_view_scroll_to_file;
     iface->supports_zooming = fm_directory_tree_view_supports_zooming;
     iface->bump_zoom_level = fm_directory_tree_view_bump_zoom_level;
     iface->zoom_to_level = fm_directory_tree_view_zoom_to_level;
     iface->get_zoom_level = fm_directory_tree_view_get_zoom_level;
     iface->restore_default_zoom_level = fm_directory_tree_view_restore_default_zoom_level;
     iface->can_zoom_in = fm_directory_tree_view_can_zoom_in;
     iface->can_zoom_out = fm_directory_tree_view_can_zoom_out;
}

static void
fm_directory_tree_view_finalize (GObject *object)
{
     FMDirectoryTreeView *view;

     view = FM_DIRECTORY_TREE_VIEW (object);

     g_object_unref (view->details->model);
     g_free (view->details->uri);
     g_object_unref (view->details->drag_dest);

     g_free (view->details);

     G_OBJECT_CLASS (fm_directory_tree_view_parent_class)->finalize (object);
}

static void
fm_directory_tree_view_destroy (GtkObject *object)
{
	/* unmerge ui stuff here */
	GTK_OBJECT_CLASS (fm_directory_tree_view_parent_class)->destroy (object);
}


static char *
get_root_uri_callback (NautilusTreeViewDragDest *dest,
		       gpointer user_data)
{
	FMDirectoryTreeView *view;

	view = FM_DIRECTORY_TREE_VIEW (user_data);

	return g_strdup (view->details->uri);
}

static NautilusFile *
get_file_for_path_callback (NautilusTreeViewDragDest *dest,
			    GtkTreePath *path,
			    gpointer user_data)
{
	FMDirectoryTreeView *view;
	GtkTreeIter iter;
	NautilusFile *file;

	view = FM_DIRECTORY_TREE_VIEW (user_data);

	file = NULL;

	if (gtk_tree_model_get_iter (GTK_TREE_MODEL (view->details->model),
				     &iter, path)) {
		gtk_tree_model_get (GTK_TREE_MODEL (view->details->model),
				    &iter,
				    FM_DIRECTORY_TREE_MODEL_FILE_COLUMN,
				    &file,
				    -1);
	}

	return file;
}

static void
move_copy_items_callback (NautilusTreeViewDragDest *dest,
			  const GList *item_uris,
			  const char *target_uri,
			  guint action,
			  int x,
			  int y,
			  gpointer user_data)
{
	const GList *ptr;

	g_print ("Copying/Moving uris to %s:\n", target_uri);
	for (ptr = item_uris; ptr != NULL; ptr = ptr->next) {
		g_print ("URI: %s\n", (char*)ptr->data);
	}
}


static void
row_activated (GtkTreeView *tree_view,
	       GtkTreePath *path,
	       GtkTreeViewColumn *column,
	       gpointer user_data)
{
	FMDirectoryTreeView *view;

	view = FM_DIRECTORY_TREE_VIEW (user_data);

	if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (view->details->view), path)) {
		gtk_tree_view_collapse_row (GTK_TREE_VIEW (view->details->view), path);
	} else {
		gtk_tree_view_expand_row (GTK_TREE_VIEW (view->details->view), path, FALSE);
	}

	/*if (gtk_tree_model_get_iter (GTK_TREE_MODEL (view->details->model), &iter, path)) {
		gtk_tree_model_get (GTK_TREE_MODEL (view->details->model), &iter,
				    FM_DIRECTORY_TREE_MODEL_FILE_COLUMN, &file,
				    -1);

		uri = nautilus_file_get_uri (file);

		nautilus_window_info_open_location (view->details->window,
						    uri,
						    NAUTILUS_WINDOW_OPEN_ACCORDING_TO_MODE,
						    0,
						    NULL);

		g_free (uri);
		}*/
}

static gboolean
popup_menu_callback (GtkWidget *widget, gpointer callback_data)
{
	FMDirectoryTreeView *view;

	view = FM_DIRECTORY_TREE_VIEW (callback_data);

	g_print ("popup_menu_callback\n");

	return TRUE;
}

static gboolean
button_release_callback (GtkWidget *widget,
			 GdkEventButton *event,
			 gpointer callback_data)
{
	FMDirectoryTreeView *view;

	view = FM_DIRECTORY_TREE_VIEW (callback_data);


	return FALSE;
}

static gboolean
button_press_callback (GtkWidget *widget, GdkEventButton *event, gpointer callback_data)
{
	FMDirectoryTreeView *view;

	view = FM_DIRECTORY_TREE_VIEW (callback_data);

	/*if (event->window != gtk_tree_view_get_bin_window (GTK_TREE_VIEW (view->details->view))) {
		return FALSE;
		}*/


	return FALSE;
}

static gboolean
motion_notify_callback (GtkWidget *widget, GdkEventMotion *event, gpointer callback_data)
{
	FMDirectoryTreeView *view;

	view = FM_DIRECTORY_TREE_VIEW (callback_data);


	return FALSE;
}

/*
static void
drag_data_get_callback (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data, guint info, guint time)
{
	
}*/

static void
fm_directory_tree_view_class_init (FMDirectoryTreeViewClass *klass)
{
	GtkScrolledWindowClass *scrolled_window_class;

	scrolled_window_class = GTK_SCROLLED_WINDOW_CLASS (klass);

	scrolled_window_class->scrollbar_spacing = 0;

	G_OBJECT_CLASS (klass)->finalize = fm_directory_tree_view_finalize;
	GTK_OBJECT_CLASS (klass)->destroy = fm_directory_tree_view_destroy;
}

static void
name_renderer_data_func (GtkCellLayout *column,
			 GtkCellRenderer *renderer,
			 GtkTreeModel *model,
			 GtkTreeIter *iter,
			 gpointer data)
{
	FMDirectoryTreeView *view;
	NautilusFile *file;
	gboolean loading;
	char *name;

	view = FM_DIRECTORY_TREE_VIEW (data);

	file = NULL;
	gtk_tree_model_get (model, iter,
			    FM_DIRECTORY_TREE_MODEL_FILE_COLUMN, &file,
			    FM_DIRECTORY_TREE_MODEL_LOADING_COLUMN, &loading,
			    -1);
	if (file != NULL) {
		name = nautilus_file_get_display_name (file);
	} else {
		name = g_strdup (loading ? "Loading ..." : "Empty");
	}

	g_object_set (G_OBJECT (renderer),
		      "text", name,
		      "style", file == NULL ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL,
		      "underline", file == NULL ? PANGO_UNDERLINE_NONE : PANGO_UNDERLINE_SINGLE,
		      NULL);

	g_free (name);

	if (file != NULL) {
		nautilus_file_unref (file);
	}
}

static void
icon_renderer_data_func (GtkCellLayout *column,
			 GtkCellRenderer *renderer,
			 GtkTreeModel *model,
			 GtkTreeIter *iter,
			 gpointer data)
{
	FMDirectoryTreeView *view;
	NautilusFile *file;
	GdkPixbuf *opened, *closed;

	view = FM_DIRECTORY_TREE_VIEW (data);

	file = NULL;
	gtk_tree_model_get (model, iter,
			    FM_DIRECTORY_TREE_MODEL_FILE_COLUMN, &file,
			    -1);

	if (file != NULL) {
		closed = nautilus_icon_factory_get_pixbuf_for_file_force_size (file, NULL, NAUTILUS_ICON_SIZE_SMALLEST);
		opened = nautilus_icon_factory_get_pixbuf_for_file_force_size (file, "accept", NAUTILUS_ICON_SIZE_SMALLEST);

		g_object_set (G_OBJECT (renderer),
			      "pixbuf", closed, "pixbuf-expander-closed", closed, "pixbuf-expander-open", opened, NULL);
		
		g_object_unref (closed);
		g_object_unref (opened);

		nautilus_file_unref (file);
	} else {
		g_object_set (G_OBJECT (renderer),
			      "pixbuf", NULL, "pixbuf-expander-closed", NULL, "pixbuf-expander-open", NULL, NULL);
	}
}

static void
fm_directory_tree_view_init (FMDirectoryTreeView *view)
{
	GtkCellRenderer *renderer;
	GtkTreeViewColumn *column;

	view->details = g_new0 (FMDirectoryTreeViewDetails, 1);

	view->details->initial_load = TRUE;

	/* Model stuff */
	view->details->model = g_object_new (FM_TYPE_DIRECTORY_TREE_MODEL, NULL);

	g_signal_connect_object (view->details->model,
				 "start_loading",
				 G_CALLBACK (fm_directory_tree_view_model_start_loading),
				 view, 0);
	g_signal_connect_object (view->details->model,
				 "stop_loading",
				 G_CALLBACK (fm_directory_tree_view_model_stop_loading),
				 view, 0);

	fm_directory_tree_model_set_attributes (view->details->model,
						nautilus_icon_factory_get_required_file_attributes ()
						| NAUTILUS_FILE_ATTRIBUTE_DISPLAY_NAME);

	/* View stuff */
	view->details->view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (view->details->model));

	gtk_tree_selection_set_mode (gtk_tree_view_get_selection (GTK_TREE_VIEW (view->details->view)), GTK_SELECTION_MULTIPLE);

	column = gtk_tree_view_column_new ();
	gtk_tree_view_column_set_title (column, "Name");

	renderer = gtk_cell_renderer_pixbuf_new ();
	gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (column), renderer, FALSE);
	gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (column), renderer, icon_renderer_data_func, view, NULL);

	renderer = gtk_cell_renderer_text_new ();
	g_object_set (renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
	gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (column), renderer, TRUE);
	gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (column), renderer, name_renderer_data_func, view, NULL);

	gtk_tree_view_append_column (GTK_TREE_VIEW (view->details->view), GTK_TREE_VIEW_COLUMN (column));

	/*g_signal_connect_object (G_OBJECT (view->details->view), "drag_data_get",
	  G_CALLBACK (drag_data_get_callback), view, 0);*/
	g_signal_connect_object (G_OBJECT (view->details->view), "motion_notify_event",
				 G_CALLBACK (motion_notify_callback), view, 0);
	g_signal_connect_object (G_OBJECT (view->details->view), "button_press_event",
				 G_CALLBACK (button_press_callback), view, 0);
	g_signal_connect_object (G_OBJECT (view->details->view), "button_release_event",
				 G_CALLBACK (button_release_callback), view, 0);
	g_signal_connect_object (G_OBJECT (view->details->view), "popup_menu",
				 G_CALLBACK (popup_menu_callback), view, 0);

	g_signal_connect (G_OBJECT (view->details->view), "row-activated",
			  G_CALLBACK (row_activated), view);

	/* Drag and drop stuff */
	view->details->drag_dest =
		nautilus_tree_view_drag_dest_new (GTK_TREE_VIEW (view->details->view));

	g_signal_connect_object (view->details->drag_dest,
				 "get_root_uri",
				 G_CALLBACK (get_root_uri_callback),
				 view, 0);
	g_signal_connect_object (view->details->drag_dest,
				 "get_file_for_path",
				 G_CALLBACK (get_file_for_path_callback),
				 view, 0);
	g_signal_connect_object (view->details->drag_dest,
				 "move_copy_items",
				 G_CALLBACK (move_copy_items_callback),
				 view, 0);
	/*	g_signal_connect_object (view->details->drag_dest, "handle_url",
				 G_CALLBACK (list_view_handle_url), view, 0);
	g_signal_connect_object (view->details->drag_dest, "handle_uri_list",
	G_CALLBACK (list_view_handle_uri_list), view, 0);*/

	/* Scrolled Window Stuff */
	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (view),
					GTK_POLICY_AUTOMATIC,
					GTK_POLICY_AUTOMATIC);
	gtk_scrolled_window_set_hadjustment (GTK_SCROLLED_WINDOW (view), NULL);
	gtk_scrolled_window_set_vadjustment (GTK_SCROLLED_WINDOW (view), NULL);

	gtk_widget_show (view->details->view);
	gtk_container_add (GTK_CONTAINER (view), view->details->view);
}

static NautilusView *
fm_directory_tree_view_create (NautilusWindowInfo *window)
{
     FMDirectoryTreeView *view;

     view = g_object_new (FM_TYPE_DIRECTORY_TREE_VIEW, NULL);
     view->details->window = window;
     g_object_ref (G_OBJECT (view));
     gtk_object_sink (GTK_OBJECT (view));
     return NAUTILUS_VIEW (view);
}

static gboolean
fm_directory_tree_view_supports_uri (const char *uri,
			   GnomeVFSFileType file_type,
			   const char *mime_type)
{
     if (file_type == GNOME_VFS_FILE_TYPE_DIRECTORY) {
	  return TRUE;
     }
     if (g_str_has_prefix (uri, "trash:")) {
	  return TRUE;
     }

     return FALSE;
}

static NautilusViewInfo fm_directory_tree_view = {
     FM_DIRECTORY_TREE_VIEW_ID,
     "Tree View",
     "View as Tree",
     "View as _Tree",
     "The tree view has encountered an error.",
     "The tree view encountered an error while starting up.",
     "Display this location with the tree view.",
     fm_directory_tree_view_create,
     fm_directory_tree_view_supports_uri
};

void
fm_directory_tree_view_register (void)
{
     nautilus_view_factory_register (&fm_directory_tree_view);
}
#ifndef FM_DIRECTORY_TREE_VIEW_H
#define FM_DIRECTORY_TREE_VIEW_H

#include <gtk/gtkscrolledwindow.h>

typedef struct FMDirectoryTreeView FMDirectoryTreeView;
typedef struct FMDirectoryTreeViewClass FMDirectoryTreeViewClass;

#define FM_TYPE_DIRECTORY_TREE_VIEW                (fm_directory_tree_view_get_type ())
#define FM_DIRECTORY_TREE_VIEW(obj)                (GTK_CHECK_CAST ((obj), FM_TYPE_DIRECTORY_TREE_VIEW, FMDirectoryTreeView))
#define FM_DIRECTORY_TREE_VIEW_CLASS(klass)        (GTK_CHECK_CLASS_CAST ((klass), FM_TYPE_DIRECTORY_TREE_VIEW, FMDirectoryTreeViewClass))
#define FM_IS_DIRECTORY_TREE_VIEW(obj)             (GTK_CHECK_TYPE ((obj), FM_TYPE_DIRECTORY_TREE_VIEW))
#define FM_IS_DIRECTORY_TREE_VIEW_CLASS(klass)     (GTK_CHECK_CLASS_TYPE ((klass), FM_TYPE_DIRECTORY_TREE_VIEW))

#define FM_DIRECTORY_TREE_VIEW_ID "OAFID:Nautilus_File_Manager_Directory_Tree_View"

typedef struct FMDirectoryTreeViewDetails FMDirectoryTreeViewDetails;

struct FMDirectoryTreeView {
     GtkScrolledWindow parent;
     FMDirectoryTreeViewDetails *details;
};

struct FMDirectoryTreeViewClass {
     GtkScrolledWindowClass parent_class;
};

GType fm_directory_tree_view_get_type (void);
void  fm_directory_tree_view_register (void);

#endif /* FM_DIRECTORY_TREE_VIEW_H */


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