[PATCH] Add tree functionality to list view
- From: Juerg Billeter <j bitron ch>
- To: nautilus-list gnome org
- Subject: [PATCH] Add tree functionality to list view
- Date: Tue, 14 Jun 2005 11:22:54 +0200
Hi
I've created a draft implementation of a tree view[1], as I thought it's
easier to decide whether it's useful if there is a working
implementation to play with.
I've added auto-expand for dnd which works with the sidebar tree view,
too. This patch may solve some of the problems some users have with
spatial nautilus.
There are probably still some rough edges needing cleanup but it doesn't
work too bad so far. Besides that, expansion state saving and loading
have yet to be added for spatial windows.
Any comments?
Regards,
Jürg
[1] http://mail.gnome.org/archives/nautilus-list/2004-April/msg00125.html
--
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 08:53:01 -0000
@@ -54,6 +54,7 @@ struct _NautilusTreeViewDragDestDetails
guint highlight_id;
guint scroll_id;
+ guint expand_id;
};
enum {
@@ -133,6 +134,29 @@ 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);
+ gtk_tree_view_expand_row (tree_view, drop_path, FALSE);
+
+ 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 +393,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 +412,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) {
@@ -427,6 +470,7 @@ drag_leave_callback (GtkWidget *widget,
free_drag_data (dest);
remove_scroll_timeout (dest);
+ remove_expand_timeout (dest);
}
static void
@@ -612,6 +656,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 +671,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 +690,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 08:53:02 -0000
@@ -249,6 +249,8 @@ struct FMDirectoryViewDetails
NautilusFile *file_monitored_for_open_with;
GtkActionGroup *open_with_action_group;
guint open_with_merge_id;
+
+ GList *sub_directory_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,19 @@ 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);
+ gboolean result;
+ GList *node;
+ NautilusDirectory *directory;
+
+ result = nautilus_directory_contains_file (view->details->model, file);
+
+ for (node = view->details->sub_directory_list; !result && node != NULL;
+ node = node->next) {
+ directory = NAUTILUS_DIRECTORY (node->data);
+ result |= nautilus_directory_contains_file (directory, file);
+ }
+
+ return result;
}
static gboolean
@@ -2739,6 +2754,88 @@ fm_directory_view_queue_file_change (FMD
queue_pending_files (view, &singleton_list, &view->details->new_changed_files);
}
+void
+fm_directory_view_add_sub_directory (FMDirectoryView *view,
+ NautilusDirectory*directory)
+{
+ NautilusFileAttributes attributes;
+
+ if (g_list_find (view->details->sub_directory_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->sub_directory_list = g_list_append (
+ view->details->sub_directory_list, directory);
+ }
+}
+
+static void
+real_remove_sub_directory (FMDirectoryView *view,
+ NautilusDirectory*directory)
+{
+ view->details->sub_directory_list = g_list_remove (
+ view->details->sub_directory_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_sub_directory (FMDirectoryView *view,
+ NautilusDirectory*directory)
+{
+ GList *node;
+ NautilusFile *parent, *file1, *file2;
+ NautilusDirectory *subdir;
+
+ parent = nautilus_directory_get_corresponding_file (directory);
+
+ for (node = view->details->sub_directory_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_sub_directory (view, subdir);
+ }
+ if (file1 != NULL) {
+ nautilus_file_unref (file1);
+ }
+ }
+}
+
/**
* fm_directory_view_clear:
*
@@ -6881,19 +6979,21 @@ fm_directory_view_notify_selection_chang
if (eel_g_list_exactly_one_item (selection)) {
file = NAUTILUS_FILE (selection->data);
- if (nautilus_file_needs_slow_mime_type (file)) {
- nautilus_file_call_when_ready
- (file,
- NAUTILUS_FILE_ATTRIBUTE_SLOW_MIME_TYPE,
- NULL,
+ if (file != NULL) {
+ if (nautilus_file_needs_slow_mime_type (file)) {
+ nautilus_file_call_when_ready
+ (file,
+ NAUTILUS_FILE_ATTRIBUTE_SLOW_MIME_TYPE,
+ NULL,
+ NULL);
+ }
+
+ nautilus_file_call_when_ready
+ (file,
+ NAUTILUS_FILE_ATTRIBUTE_ACTIVATION_URI,
+ NULL,
NULL);
}
-
- nautilus_file_call_when_ready
- (file,
- NAUTILUS_FILE_ATTRIBUTE_ACTIVATION_URI,
- NULL,
- NULL);
}
nautilus_file_list_free (selection);
@@ -7369,6 +7469,11 @@ load_directory (FMDirectoryView *view,
* of old selection.
*/
schedule_update_menus (view);
+
+ while (view->details->sub_directory_list != NULL) {
+ real_remove_sub_directory (view,
+ view->details->sub_directory_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 08:53:02 -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_sub_directory (FMDirectoryView *view,
+ NautilusDirectory*directory);
+void fm_directory_view_remove_sub_directory (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 08:53:03 -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);
@@ -59,6 +64,8 @@ struct FMListModelDetails {
int drag_begin_y;
GPtrArray *columns;
+
+ GList *sub_dirs_done;
};
typedef struct {
@@ -67,6 +74,13 @@ typedef struct {
GList *path_list;
} DragDataGetInfo;
+typedef struct _FileEntry {
+ NautilusFile *file;
+ struct _FileEntry *parent;
+ GSequence *files;
+ GSequencePtr ptr;
+} FileEntry;
+
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 +90,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 +148,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 +182,9 @@ fm_list_model_get_path (GtkTreeModel *tr
{
GtkTreePath *path;
FMListModel *model;
+ GSequencePtr ptr;
+ FileEntry *file_entry;
+
model = (FMListModel *)tree_model;
@@ -161,7 +196,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 +214,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 +230,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 +248,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 +290,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 +333,13 @@ 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")) {
+ g_value_set_string (value, _("Loading..."));
+ }
g_free (attribute);
} else {
g_assert_not_reached ();
@@ -311,19 +365,26 @@ 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) {
+ file_entry = 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 +392,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 +427,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 +454,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 +483,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 +494,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 +536,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 +546,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 +564,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 +586,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 +767,89 @@ 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);
+ 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 */
+ iter.stamp = model->details->stamp;
+ iter.user_data = dummy_ptr;
+
+ 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 +864,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 +891,45 @@ 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 parent_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 {
+ g_sequence_remove (child_ptr);
+ }
- g_hash_table_remove (model->details->reverse_map, g_sequence_ptr_get_data (ptr));
+ }
+ }
+
+ g_hash_table_remove (model->details->reverse_map, file_entry->file);
+ file_entry = file_entry->parent;
g_sequence_remove (ptr);
model->details->stamp++;
-
gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path);
+
+ if (file_entry != NULL) {
+ if (g_sequence_get_length (file_entry->files) == 0) {
+ gtk_tree_path_up (path);
+ parent_iter.stamp = model->details->stamp;
+ parent_iter.user_data = file_entry->ptr;
+ gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (model),
+ path, &parent_iter);
+ }
+ }
+
gtk_tree_path_free (path);
}
@@ -723,20 +943,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 +1250,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 +1352,79 @@ fm_list_model_get_type (void)
return object_type;
}
+
+static gboolean
+sub_directory_done_loading_idle_callback (gpointer data)
+{
+ GtkTreeIter iter;
+ GtkTreePath *path;
+ FileEntry *file_entry, *dummy_entry;
+ NautilusFile *parent_file;
+ GSequencePtr parent_ptr, dummy_ptr;
+ GSequence *files;
+ FMListModel *model;
+
+ model = FM_LIST_MODEL (data);
+
+ while (model->details->sub_dirs_done != NULL) {
+ parent_file = nautilus_directory_get_corresponding_file (model->details->sub_dirs_done->data);
+ model->details->sub_dirs_done = g_list_remove (
+ model->details->sub_dirs_done,
+ model->details->sub_dirs_done->data);
+ parent_ptr = g_hash_table_lookup (model->details->reverse_map,
+ parent_file);
+ nautilus_file_unref (parent_file);
+ if (parent_ptr == NULL) {
+ continue;
+ }
+
+ file_entry = g_sequence_ptr_get_data (parent_ptr);
+ files = file_entry->files;
+ if (g_sequence_get_length (files) == 0) {
+ continue;
+ }
+
+ 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 */
+ continue;
+ }
+
+ iter.stamp = model->details->stamp;
+ iter.user_data = dummy_ptr;
+
+ path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
+
+ g_sequence_remove (dummy_ptr);
+
+ model->details->stamp++;
+ gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path);
+
+ if (g_sequence_get_length (files) == 0) {
+ iter.stamp = model->details->stamp;
+ iter.user_data = parent_ptr;
+ gtk_tree_path_up (path);
+ gtk_tree_model_row_has_child_toggled (
+ GTK_TREE_MODEL (model),
+ path, &iter);
+
+ }
+
+ gtk_tree_path_free (path);
+ }
+
+ return FALSE;
+}
+
+void
+fm_list_model_sub_directory_done_loading (FMListModel *model, NautilusDirectory *directory)
+{
+ model->details->sub_dirs_done = g_list_append (model->details->sub_dirs_done, directory);
+
+ /* We want lower priority than the idle that handles displaying pending
+ files to avoid collapsing a tree node just because the files haven't
+ been added yet. */
+ g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 20,
+ sub_directory_done_loading_idle_callback, model, NULL);
+}
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 08:53:03 -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_sub_directory_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 08:53:03 -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
+sub_directory_done_loading_callback (NautilusDirectory *directory, FMListView *view)
+{
+ fm_list_model_sub_directory_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_sub_directory (FM_DIRECTORY_VIEW (view), directory);
+
+ if (nautilus_directory_are_all_files_seen (directory)) {
+ fm_list_model_sub_directory_done_loading (view->details->model,
+ directory);
+ } else {
+ g_signal_connect_object (directory, "done_loading",
+ G_CALLBACK (sub_directory_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_sub_directory (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]