diff options
author | Alexander Larsson <alexl@redhat.com> | 2003-06-27 15:14:22 +0000 |
---|---|---|
committer | Alexander Larsson <alexl@src.gnome.org> | 2003-06-27 15:14:22 +0000 |
commit | 0f758600b3f03bd7765d642647da9825c3f4e591 (patch) | |
tree | fe667a2a424b82d90f37f163201d16ac7c69c2ba /components | |
parent | c2083b09fd202ea913ad3cd4a3ee299713767cf2 (diff) | |
download | nautilus-0f758600b3f03bd7765d642647da9825c3f4e591.tar.gz |
This is based on a patch by Jürg Billeter <j@bitron.ch> which was partly
2003-06-27 Alexander Larsson <alexl@redhat.com>
This is based on a patch by Jürg Billeter <j@bitron.ch> which was
partly based on a patch by Wolfgang Pichler <madmin@dialog-telekom.at>.
* components/tree/nautilus-tree-model.[ch]:
Support multiple roots. New row_loaded signal that gets fired when a
directory has been fully loaded. New font-weight column.
* components/tree/nautilus-tree-view.c:
Remove tree expansion.
Populate multiple roots: ~/, / and mounted removable media.
Tree follows view uri.
* libnautilus-private/nautilus-desktop-link-monitor.c:
(create_volume_link), (nautilus_desktop_link_monitor_init),
(desktop_link_monitor_finalize):
Kill black_list, its moved to nautilus-volume-manager.
* libnautilus-private/nautilus-desktop-link.c:
(nautilus_desktop_link_new_from_volume):
Move get_icon_for_volume to nautilus-volume-manager
* libnautilus-private/nautilus-tree-view-drag-dest.c:
(file_for_path), (get_drop_target):
Handle NULL for root_uri, meaning drops on the background
are not allowed.
* libnautilus-private/nautilus-volume-monitor.[ch]:
(nautilus_volume_get_icon),
(nautilus_volume_is_in_removable_blacklist):
Add get_icon and the removable media blacklist.
Diffstat (limited to 'components')
-rw-r--r-- | components/tree/nautilus-tree-model.c | 351 | ||||
-rw-r--r-- | components/tree/nautilus-tree-model.h | 18 | ||||
-rw-r--r-- | components/tree/nautilus-tree-view.c | 375 |
3 files changed, 525 insertions, 219 deletions
diff --git a/components/tree/nautilus-tree-model.c b/components/tree/nautilus-tree-model.c index 6071898dd..2630cc74e 100644 --- a/components/tree/nautilus-tree-model.c +++ b/components/tree/nautilus-tree-model.c @@ -37,6 +37,13 @@ #include <libnautilus-private/nautilus-icon-factory.h> #include <string.h> +enum { + ROW_LOADED, + LAST_SIGNAL +}; + +static guint tree_model_signals[LAST_SIGNAL] = { 0 }; + typedef gboolean (* FilePredicate) (NautilusFile *); /* The user_data of the GtkTreeIter is the TreeNode pointer. @@ -45,6 +52,7 @@ typedef gboolean (* FilePredicate) (NautilusFile *); */ typedef struct TreeNode TreeNode; +typedef struct NautilusTreeModelRoot NautilusTreeModelRoot; struct TreeNode { /* part of this node for the file itself */ @@ -52,9 +60,12 @@ struct TreeNode { NautilusFile *file; char *display_name; + char *icon_name; GdkPixbuf *closed_pixbuf; GdkPixbuf *open_pixbuf; + NautilusTreeModelRoot *root; + TreeNode *parent; TreeNode *next; TreeNode *prev; @@ -78,10 +89,8 @@ struct TreeNode { struct NautilusTreeModelDetails { int stamp; - GHashTable *file_to_node_map; TreeNode *root_node; - gboolean root_node_parented; guint monitoring_update_idle_id; @@ -90,6 +99,17 @@ struct NautilusTreeModelDetails { gboolean show_only_directories; }; +struct NautilusTreeModelRoot { + NautilusTreeModel *model; + + /* separate hash table for each root node needed */ + GHashTable *file_to_node_map; + + TreeNode *root_node; + + gulong changed_handler_id; +}; + typedef struct { NautilusDirectory *directory; NautilusTreeModel *model; @@ -112,18 +132,31 @@ object_unref_if_not_NULL (gpointer object) g_object_unref (object); } +static NautilusTreeModelRoot * +tree_model_root_new (NautilusTreeModel *model) +{ + NautilusTreeModelRoot *root; + + root = g_new0 (NautilusTreeModelRoot, 1); + root->model = model; + root->file_to_node_map = g_hash_table_new (NULL, NULL); + + return root; +} + static TreeNode * -tree_node_new (NautilusFile *file) +tree_node_new (NautilusFile *file, NautilusTreeModelRoot *root) { TreeNode *node; node = g_new0 (TreeNode, 1); node->file = nautilus_file_ref (file); + node->root = root; return node; } static void -tree_node_unparent (TreeNode *node) +tree_node_unparent (NautilusTreeModel *model, TreeNode *node) { TreeNode *parent, *next, *prev; @@ -131,34 +164,35 @@ tree_node_unparent (TreeNode *node) next = node->next; prev = node->prev; - if (parent == NULL) { - g_assert (next == NULL); - g_assert (prev == NULL); - return; + if (parent == NULL && + node == model->details->root_node) { + /* it's the first root node -> if there is a next then let it be the first root node */ + model->details->root_node = next; } if (next != NULL) { next->prev = prev; } - if (prev == NULL) { + if (prev == NULL && parent != NULL) { g_assert (parent->first_child == node); parent->first_child = next; - } else { + } else if (prev != NULL) { prev->next = next; } node->parent = NULL; node->next = NULL; node->prev = NULL; + node->root = NULL; } static void -tree_node_destroy (TreeNode *node) +tree_node_destroy (NautilusTreeModel *model, TreeNode *node) { g_assert (node->first_child == NULL); g_assert (node->ref_count == 0); - tree_node_unparent (node); + tree_node_unparent (model, node); g_object_unref (node->file); g_free (node->display_name); @@ -186,6 +220,7 @@ tree_node_parent (TreeNode *node, TreeNode *parent) first_child = parent->first_child; node->parent = parent; + node->root = parent->root; node->next = first_child; if (first_child != NULL) { @@ -200,6 +235,11 @@ static GdkPixbuf * tree_node_get_pixbuf_from_factory (TreeNode *node, const char *modifier) { + if (node->parent == NULL) { + return nautilus_icon_factory_get_pixbuf_from_name + (node->icon_name, NULL, + NAUTILUS_ICON_SIZE_FOR_MENUS, NULL); + } return nautilus_icon_factory_get_pixbuf_for_file (node->file, modifier, NAUTILUS_ICON_SIZE_FOR_MENUS); } @@ -244,6 +284,10 @@ tree_node_update_display_name (TreeNode *node) if (node->display_name == NULL) { return FALSE; } + /* don't update root node display names */ + if (node->parent == NULL) { + return FALSE; + } display_name = nautilus_file_get_display_name (node->file); if (strcmp (display_name, node->display_name) == 0) { g_free (display_name); @@ -348,31 +392,31 @@ make_iter_for_dummy_row (TreeNode *parent, GtkTreeIter *iter, int stamp) } static TreeNode * -get_node_from_file (NautilusTreeModel *model, NautilusFile *file) +get_node_from_file (NautilusTreeModelRoot *root, NautilusFile *file) { - return g_hash_table_lookup (model->details->file_to_node_map, file); + return g_hash_table_lookup (root->file_to_node_map, file); } static TreeNode * -get_parent_node_from_file (NautilusTreeModel *model, NautilusFile *file) +get_parent_node_from_file (NautilusTreeModelRoot *root, NautilusFile *file) { NautilusFile *parent_file; TreeNode *parent_node; parent_file = nautilus_file_get_parent (file); - parent_node = get_node_from_file (model, parent_file); + parent_node = get_node_from_file (root, parent_file); nautilus_file_unref (parent_file); return parent_node; } static TreeNode * -create_node_for_file (NautilusTreeModel *model, NautilusFile *file) +create_node_for_file (NautilusTreeModelRoot *root, NautilusFile *file) { TreeNode *node; - g_assert (get_node_from_file (model, file) == NULL); - node = tree_node_new (file); - g_hash_table_insert (model->details->file_to_node_map, node->file, node); + g_assert (get_node_from_file (root, file) == NULL); + node = tree_node_new (file, root); + g_hash_table_insert (root->file_to_node_map, node->file, node); return node; } @@ -603,8 +647,8 @@ destroy_node_without_reporting (NautilusTreeModel *model, TreeNode *node) stop_monitoring_directory (model, node); node->inserted = FALSE; destroy_children_without_reporting (model, node); - g_hash_table_remove (model->details->file_to_node_map, node->file); - tree_node_destroy (node); + g_hash_table_remove (node->root->file_to_node_map, node->file); + tree_node_destroy (model, node); } static void @@ -659,7 +703,10 @@ destroy_children_by_function (NautilusTreeModel *model, TreeNode *parent, FilePr static void destroy_by_function (NautilusTreeModel *model, FilePredicate f) { - destroy_children_by_function (model, model->details->root_node, f); + TreeNode *node; + for (node = model->details->root_node; node != NULL; node = node->next) { + destroy_children_by_function (model, node, f); + } } static gboolean @@ -714,7 +761,7 @@ reparent_node (NautilusTreeModel *model, TreeNode *node) GtkTreePath *path; TreeNode *new_parent; - new_parent = get_parent_node_from_file (model, node->file); + new_parent = get_parent_node_from_file (node->root, node->file); if (new_parent == NULL || new_parent->directory == NULL) { destroy_node (model, node); return; @@ -723,7 +770,7 @@ reparent_node (NautilusTreeModel *model, TreeNode *node) path = get_node_path (model, node); abandon_node_ref_count (model, node); - tree_node_unparent (node); + tree_node_unparent (model, node); gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path); gtk_tree_path_free (path); @@ -735,6 +782,7 @@ static gboolean should_show_file (NautilusTreeModel *model, NautilusFile *file) { gboolean should; + TreeNode *node; should = nautilus_file_should_show (file, model->details->show_hidden_files, @@ -750,9 +798,10 @@ should_show_file (NautilusTreeModel *model, NautilusFile *file) should = FALSE; } - if (!should && model->details->root_node != NULL - && file == model->details->root_node->file) { - should = TRUE; + for (node = model->details->root_node; node != NULL; node = node->next) { + if (!should && node != NULL && file == node->file) { + should = TRUE; + } } return should; @@ -801,27 +850,27 @@ update_node (NautilusTreeModel *model, TreeNode *node) } static void -process_file_change (NautilusTreeModel *model, +process_file_change (NautilusTreeModelRoot *root, NautilusFile *file) { TreeNode *node, *parent; - node = get_node_from_file (model, file); + node = get_node_from_file (root, file); if (node != NULL) { - update_node (model, node); + update_node (root->model, node); return; } - if (!should_show_file (model, file)) { + if (!should_show_file (root->model, file)) { return; } - parent = get_parent_node_from_file (model, file); + parent = get_parent_node_from_file (root, file); if (parent == NULL) { return; } - insert_node (model, parent, create_node_for_file (model, file)); + insert_node (root->model, parent, create_node_for_file (root, file)); } static void @@ -829,13 +878,13 @@ files_changed_callback (NautilusDirectory *directory, GList *changed_files, gpointer callback_data) { - NautilusTreeModel *model; + NautilusTreeModelRoot *root; GList *node; - model = NAUTILUS_TREE_MODEL (callback_data); + root = (NautilusTreeModelRoot *) (callback_data); for (node = changed_files; node != NULL; node = node->next) { - process_file_change (model, NAUTILUS_FILE (node->data)); + process_file_change (root, NAUTILUS_FILE (node->data)); } } @@ -869,13 +918,20 @@ set_done_loading (NautilusTreeModel *model, TreeNode *node, gboolean done_loadin static void done_loading_callback (NautilusDirectory *directory, - NautilusTreeModel *model) + NautilusTreeModelRoot *root) { NautilusFile *file; + TreeNode *node; + GtkTreeIter iter; file = nautilus_directory_get_corresponding_file (directory); - set_done_loading (model, get_node_from_file (model, file), TRUE); + node = get_node_from_file (root, file); + g_assert (node != NULL); + set_done_loading (root->model, node, TRUE); nautilus_file_unref (file); + + make_iter_for_node (node, &iter, root->model->details->stamp); + g_signal_emit_by_name (root->model, "row_loaded", &iter); } static NautilusFileAttributes @@ -907,13 +963,13 @@ start_monitoring_directory (NautilusTreeModel *model, TreeNode *node) node->done_loading_id = g_signal_connect (directory, "done_loading", - G_CALLBACK (done_loading_callback), model); + G_CALLBACK (done_loading_callback), node->root); node->files_added_id = g_signal_connect (directory, "files_added", - G_CALLBACK (files_changed_callback), model); + G_CALLBACK (files_changed_callback), node->root); node->files_changed_id = g_signal_connect (directory, "files_changed", - G_CALLBACK (files_changed_callback), model); + G_CALLBACK (files_changed_callback), node->root); set_done_loading (model, node, nautilus_directory_are_all_files_seen (directory)); @@ -921,7 +977,7 @@ start_monitoring_directory (NautilusTreeModel *model, TreeNode *node) nautilus_directory_file_monitor_add (directory, model, model->details->show_hidden_files, model->details->show_backup_files, - attributes, files_changed_callback, model); + attributes, files_changed_callback, node->root); } static int @@ -942,6 +998,8 @@ nautilus_tree_model_get_column_type (GtkTreeModel *model, int index) return GDK_TYPE_PIXBUF; case NAUTILUS_TREE_MODEL_FONT_STYLE_COLUMN: return PANGO_TYPE_STYLE; + case NAUTILUS_TREE_MODEL_FONT_WEIGHT_COLUMN: + return PANGO_TYPE_WEIGHT; default: g_assert_not_reached (); } @@ -1012,9 +1070,10 @@ static GtkTreePath * nautilus_tree_model_get_path (GtkTreeModel *model, GtkTreeIter *iter) { NautilusTreeModel *tree_model; - TreeNode *node, *parent; + TreeNode *node, *parent, *cnode; GtkTreePath *path; GtkTreeIter parent_iter; + int i; g_return_val_if_fail (NAUTILUS_IS_TREE_MODEL (model), NULL); tree_model = NAUTILUS_TREE_MODEL (model); @@ -1029,9 +1088,12 @@ nautilus_tree_model_get_path (GtkTreeModel *model, GtkTreeIter *iter) } else { parent = node->parent; if (parent == NULL) { - g_assert (node == tree_model->details->root_node); + i = 0; + for (cnode = tree_model->details->root_node; cnode != node; cnode = cnode->next) { + i++; + } path = gtk_tree_path_new (); - gtk_tree_path_append_index (path, 0); + gtk_tree_path_append_index (path, i); return path; } } @@ -1085,6 +1147,14 @@ nautilus_tree_model_get_value (GtkTreeModel *model, GtkTreeIter *iter, int colum g_value_set_enum (value, PANGO_STYLE_NORMAL); } break; + case NAUTILUS_TREE_MODEL_FONT_WEIGHT_COLUMN: + g_value_init (value, PANGO_TYPE_STYLE); + if (node != NULL && node->parent == NULL) { + g_value_set_enum (value, PANGO_WEIGHT_BOLD); + } else { + g_value_set_enum (value, PANGO_WEIGHT_NORMAL); + } + break; default: g_assert_not_reached (); } @@ -1131,8 +1201,7 @@ nautilus_tree_model_iter_children (GtkTreeModel *model, GtkTreeIter *iter, GtkTr static gboolean nautilus_tree_model_iter_parent (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *child_iter) -{ - TreeNode *child, *parent; +{ TreeNode *child, *parent; g_return_val_if_fail (NAUTILUS_IS_TREE_MODEL (model), FALSE); g_return_val_if_fail (iter_is_valid (NAUTILUS_TREE_MODEL (model), child_iter), FALSE); @@ -1214,11 +1283,10 @@ nautilus_tree_model_iter_nth_child (GtkTreeModel *model, GtkTreeIter *iter, tree_model = NAUTILUS_TREE_MODEL (model); if (parent_iter == NULL) { - if (n != 0) { - return make_iter_invalid (iter); - } - return make_iter_for_node (tree_model->details->root_node, - iter, tree_model->details->stamp); + node = tree_model->details->root_node; + for (i = 0; i < n && node != NULL; i++, node = node->next); + return make_iter_for_node (node, iter, + tree_model->details->stamp); } parent = parent_iter->user_data; @@ -1259,10 +1327,13 @@ static gboolean update_monitoring_idle_callback (gpointer callback_data) { NautilusTreeModel *model; + TreeNode *node; model = NAUTILUS_TREE_MODEL (callback_data); model->details->monitoring_update_idle_id = 0; - update_monitoring (model, model->details->root_node); + for (node = model->details->root_node; node != NULL; node = node->next) { + update_monitoring (model, node); + } return FALSE; } @@ -1289,7 +1360,11 @@ stop_monitoring_directory_and_children (NautilusTreeModel *model, TreeNode *node static void stop_monitoring (NautilusTreeModel *model) { - stop_monitoring_directory_and_children (model, model->details->root_node); + TreeNode *node; + + for (node = model->details->root_node; node != NULL; node = node->next) { + stop_monitoring_directory_and_children (model, node); + } } static void @@ -1314,9 +1389,7 @@ nautilus_tree_model_ref_node (GtkTreeModel *model, GtkTreeIter *iter) ++node->ref_count; } - if (parent == NULL) { - g_assert (node == NAUTILUS_TREE_MODEL (model)->details->root_node); - } else { + if (parent != NULL) { g_assert (parent->all_children_ref_count >= 0); if (++parent->all_children_ref_count == 1) { if (parent->first_child == NULL) { @@ -1355,9 +1428,7 @@ nautilus_tree_model_unref_node (GtkTreeModel *model, GtkTreeIter *iter) --node->ref_count; } - if (parent == NULL) { - g_assert (node == NAUTILUS_TREE_MODEL (model)->details->root_node); - } else { + if (parent != NULL) { g_assert (parent->all_children_ref_count > 0); #if LOG_REF_COUNTS uri = get_node_uri (iter); @@ -1372,27 +1443,41 @@ nautilus_tree_model_unref_node (GtkTreeModel *model, GtkTreeIter *iter) } static void -root_node_file_changed_callback (NautilusFile *file, NautilusTreeModel *model) +root_node_file_changed_callback (NautilusFile *file, NautilusTreeModelRoot *root) { - update_node (model, model->details->root_node); + if (root->root_node != NULL) { + update_node (root->model, root->root_node); + } } void -nautilus_tree_model_set_root_uri (NautilusTreeModel *model, const char *root_uri) +nautilus_tree_model_add_root_uri (NautilusTreeModel *model, const char *root_uri, const char *display_name, const char *icon_name) { NautilusFile *file; - TreeNode *node; + TreeNode *node, *cnode; NautilusFileAttributes attributes; - - g_return_if_fail (model->details->root_node == NULL); + NautilusTreeModelRoot *newroot; file = nautilus_file_get (root_uri); - node = create_node_for_file (model, file); - model->details->root_node = node; + newroot = tree_model_root_new (model); + node = create_node_for_file (newroot, file); + node->display_name = g_strdup (display_name); + node->icon_name = g_strdup (icon_name); + newroot->root_node = node; + node->parent = NULL; + if (model->details->root_node == NULL) { + model->details->root_node = node; + } else { + /* append it */ + for (cnode = model->details->root_node; cnode->next != NULL; cnode = cnode->next); + cnode->next = node; + node->prev = cnode; + } - g_signal_connect_object (file, "changed", - G_CALLBACK (root_node_file_changed_callback), model, 0); + newroot->changed_handler_id = g_signal_connect (node->file, "changed", + G_CALLBACK (root_node_file_changed_callback), + node->root); attributes = get_tree_monitor_attributes (); nautilus_file_monitor_add (file, model, attributes); @@ -1403,15 +1488,53 @@ nautilus_tree_model_set_root_uri (NautilusTreeModel *model, const char *root_uri report_node_inserted (model, node); } +void +nautilus_tree_model_remove_root_uri (NautilusTreeModel *model, const char *uri) +{ + TreeNode *node; + GtkTreePath *path; + NautilusTreeModelRoot *root; + NautilusFile *file; + + file = nautilus_file_get (uri); + for (node = model->details->root_node; node != NULL; node = node->next) { + if (file == node->file) { + break; + } + } + nautilus_file_unref (file); + + if (node) { + /* remove the node */ + nautilus_file_monitor_remove (node->file, model); + path = get_node_path (model, node); + + if (node->prev) { + node->prev->next = node->next; + } + if (node->next) { + node->next->prev = node->prev; + } + if (node == model->details->root_node) { + model->details->root_node = node->next; + } + + /* destroy the root identifier */ + root = node->root; + destroy_node_without_reporting (model, node); + g_hash_table_destroy (root->file_to_node_map); + g_free (root); + gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path); + gtk_tree_path_free (path); + } +} + NautilusTreeModel * -nautilus_tree_model_new (const char *opt_root_uri) +nautilus_tree_model_new (void) { NautilusTreeModel *model; model = g_object_new (NAUTILUS_TYPE_TREE_MODEL, NULL); - if (opt_root_uri) { - nautilus_tree_model_set_root_uri (model, opt_root_uri); - } return model; } @@ -1420,7 +1543,7 @@ static void set_theme (TreeNode *node, NautilusTreeModel *model) { TreeNode *child; - + tree_node_update_closed_pixbuf (node); tree_node_update_open_pixbuf (node); @@ -1434,9 +1557,15 @@ set_theme (TreeNode *node, NautilusTreeModel *model) void nautilus_tree_model_set_theme (NautilusTreeModel *model) { + TreeNode *node; + g_return_if_fail (NAUTILUS_IS_TREE_MODEL (model)); - set_theme (model->details->root_node, model); + node = model->details->root_node; + while (node != NULL) { + set_theme (node, model); + node = node->next; + } } @@ -1515,30 +1644,70 @@ nautilus_tree_model_iter_get_file (NautilusTreeModel *model, GtkTreeIter *iter) return node == NULL ? NULL : nautilus_file_ref (node->file); } +gboolean +nautilus_tree_model_iter_is_root (NautilusTreeModel *model, GtkTreeIter *iter) +{ + TreeNode *node; + + g_return_val_if_fail (NAUTILUS_IS_TREE_MODEL (model), 0); + g_return_val_if_fail (iter_is_valid (model, iter), 0); + node = iter->user_data; + if (node == NULL) { + return FALSE; + } else { + return (node->parent == NULL); + } +} + +gboolean +nautilus_tree_model_file_get_iter (NautilusTreeModel *model, + GtkTreeIter *iter, + NautilusFile *file, + GtkTreeIter *current_iter) +{ + TreeNode *node, *root_node; + + if (current_iter != NULL && current_iter->user_data != NULL) { + node = get_node_from_file (((TreeNode *) current_iter->user_data)->root, file); + return make_iter_for_node (node, iter, model->details->stamp); + } + + for (root_node = model->details->root_node; root_node != NULL; root_node = root_node->next) { + node = get_node_from_file (root_node->root, file); + if (node != NULL) { + return make_iter_for_node (node, iter, model->details->stamp); + } + } + return FALSE; +} + static void nautilus_tree_model_init (NautilusTreeModel *model) { model->details = g_new0 (NautilusTreeModelDetails, 1); - do + do { model->details->stamp = g_random_int (); - while (model->details->stamp == 0); - - model->details->file_to_node_map = g_hash_table_new (NULL, NULL); + } while (model->details->stamp == 0); } static void nautilus_tree_model_finalize (GObject *object) { NautilusTreeModel *model; - TreeNode *root; + TreeNode *root_node, *next_root; + NautilusTreeModelRoot *root; model = NAUTILUS_TREE_MODEL (object); - root = model->details->root_node; - if (root != NULL) { - nautilus_file_monitor_remove (root->file, model); - destroy_node_without_reporting (model, root); + for (root_node = model->details->root_node; root_node != NULL; root_node = next_root) { + next_root = root_node->next; + root = root_node->root; + g_signal_handler_disconnect (root_node->file, root->changed_handler_id); + nautilus_file_monitor_remove (root_node->file, model); + destroy_node_without_reporting (model, root_node); + g_hash_table_destroy (root->file_to_node_map); + g_free (root); } if (model->details->monitoring_update_idle_id != 0) { @@ -1556,6 +1725,16 @@ nautilus_tree_model_class_init (NautilusTreeModelClass *class) parent_class = g_type_class_peek_parent (class); G_OBJECT_CLASS (class)->finalize = nautilus_tree_model_finalize; + + tree_model_signals[ROW_LOADED] = + g_signal_new ("row_loaded", + NAUTILUS_TYPE_TREE_MODEL, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NautilusTreeModelClass, row_loaded), + NULL, NULL, + g_cclosure_marshal_VOID__BOXED, + G_TYPE_NONE, 1, + GTK_TYPE_TREE_ITER); } static void diff --git a/components/tree/nautilus-tree-model.h b/components/tree/nautilus-tree-model.h index 1a83d4dab..f41a05740 100644 --- a/components/tree/nautilus-tree-model.h +++ b/components/tree/nautilus-tree-model.h @@ -42,6 +42,7 @@ enum { NAUTILUS_TREE_MODEL_CLOSED_PIXBUF_COLUMN, NAUTILUS_TREE_MODEL_OPEN_PIXBUF_COLUMN, NAUTILUS_TREE_MODEL_FONT_STYLE_COLUMN, + NAUTILUS_TREE_MODEL_FONT_WEIGHT_COLUMN, NAUTILUS_TREE_MODEL_NUM_COLUMNS }; @@ -54,10 +55,13 @@ typedef struct { typedef struct { GObjectClass parent_class; + + void (* row_loaded) (NautilusTreeModel *tree_model, + GtkTreeIter *iter); } NautilusTreeModelClass; GType nautilus_tree_model_get_type (void); -NautilusTreeModel *nautilus_tree_model_new (const char *opt_root_uri); +NautilusTreeModel *nautilus_tree_model_new (void); void nautilus_tree_model_set_show_hidden_files (NautilusTreeModel *model, gboolean show_hidden_files); void nautilus_tree_model_set_show_backup_files (NautilusTreeModel *model, @@ -66,8 +70,18 @@ void nautilus_tree_model_set_show_only_directories (NautilusTreeMo gboolean show_only_directories); NautilusFile * nautilus_tree_model_iter_get_file (NautilusTreeModel *model, GtkTreeIter *iter); -void nautilus_tree_model_set_root_uri (NautilusTreeModel *model, +void nautilus_tree_model_add_root_uri (NautilusTreeModel *model, + const char *root_uri, + const char *display_name, + const char *icon_name); +void nautilus_tree_model_remove_root_uri (NautilusTreeModel *model, const char *root_uri); +gboolean nautilus_tree_model_iter_is_root (NautilusTreeModel *model, + GtkTreeIter *iter); +gboolean nautilus_tree_model_file_get_iter (NautilusTreeModel *model, + GtkTreeIter *iter, + NautilusFile *file, + GtkTreeIter *currentIter); void nautilus_tree_model_set_theme (NautilusTreeModel *model); diff --git a/components/tree/nautilus-tree-view.c b/components/tree/nautilus-tree-view.c index a67a21eff..1bd4b5491 100644 --- a/components/tree/nautilus-tree-view.c +++ b/components/tree/nautilus-tree-view.c @@ -42,6 +42,7 @@ #include <gtk/gtktreemodelsort.h> #include <gtk/gtktreeselection.h> #include <gtk/gtktreeview.h> +#include <libgnome/gnome-i18n.h> #include <libgnomevfs/gnome-vfs-utils.h> #include <libnautilus-private/nautilus-file-attributes.h> #include <libnautilus-private/nautilus-file-operations.h> @@ -49,8 +50,7 @@ #include <libnautilus-private/nautilus-program-choosing.h> #include <libnautilus-private/nautilus-tree-view-drag-dest.h> #include <libnautilus-private/nautilus-icon-factory.h> - -#define NAUTILUS_PREFERENCES_TREE_VIEW_EXPANSION_STATE "tree-sidebar-panel/expansion_state" +#include <libnautilus-private/nautilus-volume-monitor.h> struct NautilusTreeViewDetails { GtkWidget *scrolled_window; @@ -59,9 +59,11 @@ struct NautilusTreeViewDetails { NautilusTreeModel *child_model; NautilusFile *activation_file; - GHashTable *expanded_uris; NautilusTreeViewDragDest *drag_dest; + + char *selection_location; + gboolean selecting; }; typedef struct { @@ -72,107 +74,140 @@ typedef struct { BONOBO_CLASS_BOILERPLATE (NautilusTreeView, nautilus_tree_view, NautilusView, NAUTILUS_TYPE_VIEW) -/* - * The expansion state storage is pretty broken - * conceptually we have a gconf key, but we can't - * listen on it, since we don't want to sync all - * tree views. We want to load the stored state per - * new tree view we instantiate, and keep a track of - * what nodes we are expanding. - * - * We then arbitrarily serialize all the tree - * view's expansion state - and the last one to shut - * wins the GConf key value - it sucks, but it's what - * happened in Nautilus 1.0 - * - * - Michael Meeks (23/5/2002) - */ - -static void -populate_expansion_hash (const char *string, - gpointer callback_data) +static gboolean +show_iter_for_file (NautilusTreeView *view, NautilusFile *file, GtkTreeIter *iter) { - char *key = g_strdup (string); + GtkTreeModel *model; + NautilusFile *parent_file; + GtkTreeIter parent_iter; + GtkTreePath *path, *sort_path; + GtkTreeIter cur_iter; - g_hash_table_insert (callback_data, key, key); -} + if (view->details->child_model == NULL) { + return FALSE; + } + model = GTK_TREE_MODEL (view->details->child_model); + + /* check if file is visible in the same root as the currently selected folder is */ + gtk_tree_view_get_cursor (view->details->tree_widget, &path, NULL); + if (path != NULL) { + if (gtk_tree_model_get_iter (model, &cur_iter, path)) { + if (nautilus_tree_model_file_get_iter (view->details->child_model, + iter, file, &cur_iter)) { + return TRUE; + } + } + } + /* check if file is visible at all */ + if (nautilus_tree_model_file_get_iter (view->details->child_model, + iter, file, NULL)) { + return TRUE; + } -static void -load_expansion_state (NautilusTreeView *view) -{ - EelStringList *uris; + parent_file = nautilus_file_get_parent (file); - uris = eel_preferences_get_string_list ( - NAUTILUS_PREFERENCES_TREE_VIEW_EXPANSION_STATE); + if (parent_file == NULL) { + return FALSE; + } + if (!show_iter_for_file (view, parent_file, &parent_iter)) { + nautilus_file_unref (parent_file); + return FALSE; + } + nautilus_file_unref (parent_file); - eel_string_list_for_each (uris, populate_expansion_hash, - view->details->expanded_uris); + if (parent_iter.user_data == NULL || parent_iter.stamp == 0) { + return FALSE; + } + path = gtk_tree_model_get_path (model, &parent_iter); + sort_path = gtk_tree_model_sort_convert_child_path_to_path + (view->details->sort_model, path); + gtk_tree_path_free (path); + gtk_tree_view_expand_row (view->details->tree_widget, sort_path, FALSE); + gtk_tree_path_free (sort_path); - eel_string_list_free (uris); + return FALSE; } -static void -expand_row_if_stored (NautilusTreeView *view, - GtkTreePath *path, - const char *uri) +static gboolean +show_selection_idle_callback (gpointer callback_data) { - g_return_if_fail (NAUTILUS_IS_TREE_VIEW (view)); - g_return_if_fail (view->details != NULL); - - if (g_hash_table_lookup (view->details->expanded_uris, uri)) { - if (!gtk_tree_view_expand_row ( - view->details->tree_widget, path, FALSE)) { - g_warning ("Error expanding row '%s' '%s'", uri, - gtk_tree_path_to_string (path)); + NautilusTreeView *view; + NautilusFile *file, *old_file; + GtkTreeIter iter; + GtkTreePath *path, *sort_path; + + view = NAUTILUS_TREE_VIEW (callback_data); + + file = nautilus_file_get (view->details->selection_location); + if (file == NULL) { + return FALSE; + } + + if (!nautilus_file_is_directory (file)) { + old_file = file; + file = nautilus_file_get_parent (file); + nautilus_file_unref (old_file); + if (file == NULL) { + return FALSE; } - g_hash_table_remove (view->details->expanded_uris, uri); } + + view->details->selecting = TRUE; + if (!show_iter_for_file (view, file, &iter)) { + nautilus_file_unref (file); + return FALSE; + } + view->details->selecting = FALSE; + + path = gtk_tree_model_get_path (GTK_TREE_MODEL (view->details->child_model), &iter); + sort_path = gtk_tree_model_sort_convert_child_path_to_path + (view->details->sort_model, path); + gtk_tree_path_free (path); + gtk_tree_view_set_cursor (view->details->tree_widget, sort_path, NULL, FALSE); + gtk_tree_view_scroll_to_cell (view->details->tree_widget, sort_path, NULL, FALSE, 0, 0); + gtk_tree_path_free (sort_path); + + nautilus_file_unref (file); + + return FALSE; } static void -row_inserted_expand_node_callback (GtkTreeModel *tree_model, - GtkTreePath *path, - GtkTreeIter *iter, - NautilusTreeView *view) +row_loaded_callback (GtkTreeModel *tree_model, + GtkTreeIter *iter, + NautilusTreeView *view) { - char *uri; - GtkTreeIter parent; - GtkTreePath *sort_path, *parent_path; - NautilusFile *file; + NautilusFile *file, *tmp_file, *selection_file; - file = nautilus_tree_model_iter_get_file (view->details->child_model, iter); - - if (file) { - /* - * We can't expand a node as it's created, - * we need to wait for the dummy child to be - * made, so it has children, so 'expand_node' - * doesn't fail. - */ - nautilus_file_unref (file); + if (view->details->selection_location == NULL + || !view->details->selecting + || iter->user_data == NULL || iter->stamp == 0) { return; } - if (!gtk_tree_model_iter_parent (tree_model, &parent, iter)) { - g_warning ("Un-parented tree node"); + file = nautilus_tree_model_iter_get_file (view->details->child_model, iter); + if (file == NULL) { + return; + } + if (!nautilus_file_is_directory (file)) { + nautilus_file_unref(file); return; } - file = nautilus_tree_model_iter_get_file (view->details->child_model, &parent); - - uri = nautilus_file_get_uri (file); - g_return_if_fail (uri != NULL); - - parent_path = gtk_tree_model_get_path (tree_model, &parent); - sort_path = gtk_tree_model_sort_convert_child_path_to_path - (view->details->sort_model, parent_path); - - expand_row_if_stored (view, sort_path, uri); - - gtk_tree_path_free (sort_path); - - g_free (uri); + /* if iter is ancestor of wanted selection_location then update selection */ + selection_file = nautilus_file_get (view->details->selection_location); + while (selection_file != NULL) { + if (file == selection_file) { + nautilus_file_unref (file); + nautilus_file_unref (selection_file); + g_idle_add (show_selection_idle_callback, view); + return; + } + tmp_file = nautilus_file_get_parent (selection_file); + nautilus_file_unref (selection_file); + selection_file = tmp_file; + } nautilus_file_unref (file); } @@ -197,42 +232,6 @@ sort_model_path_to_file (NautilusTreeView *view, GtkTreePath *path) } static void -prepend_one_uri (GtkTreeView *tree_view, - GtkTreePath *path, - gpointer callback_data) -{ - PrependURIParameters *p; - NautilusFile *file; - - p = callback_data; - file = sort_model_path_to_file (p->view, path); - if (file == NULL) { - return; - } - p->uris = g_list_prepend (p->uris, nautilus_file_get_uri (file)); - nautilus_file_unref (file); -} - -static void -save_expansion_state_callback (GtkTreeView *tree_widget, - NautilusTreeView *view) -{ - PrependURIParameters p; - EelStringList *uris; - - g_return_if_fail (NAUTILUS_IS_TREE_VIEW (view)); - - p.uris = NULL; - p.view = view; - gtk_tree_view_map_expanded_rows (tree_widget, prepend_one_uri, &p); - p.uris = g_list_sort (p.uris, eel_strcmp_compare_func); - uris = eel_string_list_new_from_g_list (p.uris, TRUE); - eel_g_list_free_deep (p.uris); - eel_preferences_set_string_list (NAUTILUS_PREFERENCES_TREE_VIEW_EXPANSION_STATE, uris); - eel_string_list_free (uris); -} - -static void got_activation_uri_callback (NautilusFile *file, gpointer callback_data) { char *uri, *file_uri; @@ -276,8 +275,15 @@ got_activation_uri_callback (NautilusFile *file, gpointer callback_data) g_free (file_uri); } - } else if (uri != NULL) { - nautilus_view_open_location_in_this_window (NAUTILUS_VIEW (view), uri); + } else if (uri != NULL) { + if (view->details->selection_location == NULL || + strcmp (uri, view->details->selection_location) != 0) { + if (view->details->selection_location != NULL) { + g_free (view->details->selection_location); + } + view->details->selection_location = g_strdup (uri); + nautilus_view_open_location_in_this_window (NAUTILUS_VIEW (view), uri); + } } g_free (uri); @@ -328,6 +334,19 @@ compare_rows (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer call NautilusFile *file_a, *file_b; int result; + if (a->user_data == NULL) { + return -1; + } + else if (b->user_data == NULL) { + return -1; + } + + /* don't sort root nodes */ + if (nautilus_tree_model_iter_is_root (NAUTILUS_TREE_MODEL (model), a) + || nautilus_tree_model_iter_is_root (NAUTILUS_TREE_MODEL (model), b)) { + return 0; + } + file_a = nautilus_tree_model_iter_get_file (NAUTILUS_TREE_MODEL (model), a); file_b = nautilus_tree_model_iter_get_file (NAUTILUS_TREE_MODEL (model), b); @@ -358,7 +377,8 @@ get_root_uri_callback (NautilusTreeViewDragDest *dest, view = NAUTILUS_TREE_VIEW (user_data); - return g_strdup ("file:///"); + /* Don't allow drops on background */ + return NULL; } static NautilusFile * @@ -405,22 +425,98 @@ theme_changed_callback (GObject *icon_factory, gpointer callback_data) } static void +add_root_for_volume (NautilusTreeView *view, + const NautilusVolume *volume) +{ + char *icon, *mount_uri, *name; + + if (nautilus_volume_is_in_removable_blacklist (volume)) { + return; + } + + if (!nautilus_volume_is_removable (volume)) { + return; + } + + /* Name uniqueness is handled by nautilus-desktop-link-monitor.c... */ + + icon = nautilus_volume_get_icon (volume); + mount_uri = nautilus_volume_get_target_uri (volume); + name = nautilus_volume_get_name (volume); + + nautilus_tree_model_add_root_uri (view->details->child_model, + mount_uri, name, icon); + + g_free (icon); + g_free (name); + g_free (mount_uri); + +} + +static void +volume_mounted_callback (NautilusVolumeMonitor *volume_monitor, + NautilusVolume *volume, + NautilusTreeView *view) +{ + add_root_for_volume (view, volume); +} + +static gboolean +add_one_volume_root (const NautilusVolume *volume, gpointer callback_data) +{ + add_root_for_volume (NAUTILUS_TREE_VIEW (callback_data), volume); +} + +static void +volume_unmounted_callback (NautilusVolumeMonitor *volume_monitor, + NautilusVolume *volume, + NautilusTreeView *view) +{ + char *mount_uri; + + mount_uri = nautilus_volume_get_target_uri (volume); + nautilus_tree_model_remove_root_uri (view->details->child_model, + mount_uri); + g_free (mount_uri); +} + + +static void create_tree (NautilusTreeView *view) { GtkCellRenderer *cell; GtkTreeViewColumn *column; + NautilusVolumeMonitor *volume_monitor; + char *home_uri; - view->details->child_model = nautilus_tree_model_new (NULL); + view->details->child_model = nautilus_tree_model_new (); view->details->sort_model = GTK_TREE_MODEL_SORT (gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (view->details->child_model))); view->details->tree_widget = GTK_TREE_VIEW (gtk_tree_view_new_with_model (GTK_TREE_MODEL (view->details->sort_model))); g_object_unref (view->details->sort_model); g_signal_connect_object - (view->details->child_model, "row_inserted", - G_CALLBACK (row_inserted_expand_node_callback), + (view->details->child_model, "row_loaded", + G_CALLBACK (row_loaded_callback), view, G_CONNECT_AFTER); - nautilus_tree_model_set_root_uri (view->details->child_model, "file:///"); + home_uri = gnome_vfs_get_uri_from_local_path (g_get_home_dir ()); + nautilus_tree_model_add_root_uri (view->details->child_model, home_uri, _("Home Folder"), "gnome-home"); + g_free (home_uri); + nautilus_tree_model_add_root_uri (view->details->child_model, "file:///", _("Filesystem Root"), "gnome-folder"); +#ifdef NOT_YET_USABLE + nautilus_tree_model_add_root_uri (view->details->child_model, "network:///", _("Network Neighbourhood"), "gnome-fs-network"); +#endif + + volume_monitor = nautilus_volume_monitor_get (); + nautilus_volume_monitor_each_mounted_volume (volume_monitor, + add_one_volume_root, + view); + + g_signal_connect_object (volume_monitor, "volume_mounted", + G_CALLBACK (volume_mounted_callback), view, 0); + g_signal_connect_object (volume_monitor, "volume_unmounted", + G_CALLBACK (volume_unmounted_callback), view, 0); + g_object_unref (view->details->child_model); gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (view->details->sort_model), @@ -428,9 +524,6 @@ create_tree (NautilusTreeView *view) gtk_tree_view_set_headers_visible (view->details->tree_widget, FALSE); - g_signal_connect_object (view->details->tree_widget, "destroy", - G_CALLBACK (save_expansion_state_callback), view, 0); - view->details->drag_dest = nautilus_tree_view_drag_dest_new (view->details->tree_widget); g_signal_connect_object (view->details->drag_dest, @@ -462,6 +555,7 @@ create_tree (NautilusTreeView *view) gtk_tree_view_column_set_attributes (column, cell, "text", NAUTILUS_TREE_MODEL_DISPLAY_NAME_COLUMN, "style", NAUTILUS_TREE_MODEL_FONT_STYLE_COLUMN, + "weight", NAUTILUS_TREE_MODEL_FONT_WEIGHT_COLUMN, NULL); gtk_tree_view_append_column (view->details->tree_widget, column); @@ -473,6 +567,8 @@ create_tree (NautilusTreeView *view) g_signal_connect_object (gtk_tree_view_get_selection (GTK_TREE_VIEW (view->details->tree_widget)), "changed", G_CALLBACK (selection_changed_callback), view, 0); + + g_idle_add (show_selection_idle_callback, view); } static void @@ -501,7 +597,6 @@ tree_activate_callback (BonoboControl *control, gboolean activating, gpointer us view = NAUTILUS_TREE_VIEW (user_data); if (activating && view->details->tree_widget == NULL) { - load_expansion_state (view); create_tree (view); update_filtering_from_preferences (view); } @@ -514,6 +609,17 @@ filtering_changed_callback (gpointer callback_data) } static void +load_location_callback (NautilusTreeView *view, char *location) +{ + if (view->details->selection_location != NULL) { + g_free (view->details->selection_location); + } + view->details->selection_location = g_strdup (location); + + g_idle_add (show_selection_idle_callback, view); +} + +static void nautilus_tree_view_instance_init (NautilusTreeView *view) { BonoboControl *control; @@ -521,8 +627,6 @@ nautilus_tree_view_instance_init (NautilusTreeView *view) view->details = g_new0 (NautilusTreeViewDetails, 1); view->details->scrolled_window = gtk_scrolled_window_new (NULL, NULL); - view->details->expanded_uris = g_hash_table_new_full - (g_str_hash, g_str_equal, g_free, NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (view->details->scrolled_window), GTK_POLICY_AUTOMATIC, @@ -536,6 +640,11 @@ nautilus_tree_view_instance_init (NautilusTreeView *view) nautilus_view_construct_from_bonobo_control (NAUTILUS_VIEW (view), control); + view->details->selection_location = NULL; + g_signal_connect_object (view, "load_location", + G_CALLBACK (load_location_callback), view, 0); + view->details->selecting = FALSE; + eel_preferences_add_callback (NAUTILUS_PREFERENCES_SHOW_HIDDEN_FILES, filtering_changed_callback, view); eel_preferences_add_callback (NAUTILUS_PREFERENCES_SHOW_BACKUP_FILES, @@ -545,7 +654,7 @@ nautilus_tree_view_instance_init (NautilusTreeView *view) g_signal_connect_object (nautilus_icon_factory_get(), "icons_changed", G_CALLBACK (theme_changed_callback), view, 0); - + } static void @@ -577,6 +686,10 @@ nautilus_tree_view_finalize (GObject *object) cancel_activation (view); + if (view->details->selection_location != NULL) { + g_free (view->details->selection_location); + } + g_free (view->details); G_OBJECT_CLASS (parent_class)->finalize (object); |