1 /* GTK - The GIMP Toolkit
2 * gtkfilesystemmodel.c: GtkTreeModel wrapping a GtkFileSystem
3 * Copyright (C) 2003, Red Hat, Inc.
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
24 #include "gtkfilechooserprivate.h"
25 #include "gtkfilesystemmodel.h"
26 #include "gtkfilesystem.h"
28 #include "gtkmarshalers.h"
29 #include "gtktreednd.h"
30 #include "gtktreemodel.h"
33 typedef struct _GtkFileSystemModelClass GtkFileSystemModelClass;
35 #define GTK_FILE_SYSTEM_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_SYSTEM_MODEL, GtkFileSystemModelClass))
36 #define GTK_IS_FILE_SYSTEM_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_SYSTEM_MODEL))
37 #define GTK_FILE_SYSTEM_MODEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_SYSTEM_MODEL, GtkFileSystemModelClass))
39 struct _GtkFileSystemModelClass
41 GObjectClass parent_class;
45 void (*finished_loading) (GtkFileSystemModel *model);
49 static void gtk_file_system_model_iface_init (GtkTreeModelIface *iface);
50 static void gtk_file_system_model_finalize (GObject *object);
51 static void gtk_file_system_model_dispose (GObject *object);
53 static void drag_source_iface_init (GtkTreeDragSourceIface *iface);
55 static GtkTreeModelFlags gtk_file_system_model_get_flags (GtkTreeModel *tree_model);
56 static gint gtk_file_system_model_get_n_columns (GtkTreeModel *tree_model);
57 static GType gtk_file_system_model_get_column_type (GtkTreeModel *tree_model,
59 static gboolean gtk_file_system_model_get_iter (GtkTreeModel *tree_model,
62 static GtkTreePath * gtk_file_system_model_get_path (GtkTreeModel *tree_model,
64 static void gtk_file_system_model_get_value (GtkTreeModel *tree_model,
68 static gboolean gtk_file_system_model_iter_next (GtkTreeModel *tree_model,
70 static gboolean gtk_file_system_model_iter_children (GtkTreeModel *tree_model,
73 static gboolean gtk_file_system_model_iter_has_child (GtkTreeModel *tree_model,
75 static gint gtk_file_system_model_iter_n_children (GtkTreeModel *tree_model,
77 static gboolean gtk_file_system_model_iter_nth_child (GtkTreeModel *tree_model,
81 static gboolean gtk_file_system_model_iter_parent (GtkTreeModel *tree_model,
84 static void gtk_file_system_model_ref_node (GtkTreeModel *tree_model,
86 static void gtk_file_system_model_unref_node (GtkTreeModel *tree_model,
89 static gboolean drag_source_row_draggable (GtkTreeDragSource *drag_source,
91 static gboolean drag_source_drag_data_get (GtkTreeDragSource *drag_source,
93 GtkSelectionData *selection_data);
95 static FileModelNode *file_model_node_new (GtkFileSystemModel *model,
96 const GtkFilePath *path);
97 static void file_model_node_free (FileModelNode *node);
98 static void file_model_node_ref (FileModelNode *node);
99 static void file_model_node_unref (GtkFileSystemModel *model,
100 FileModelNode *node);
102 static void file_model_node_idle_clear (FileModelNode *node);
103 static void file_model_node_idle_clear_cancel (FileModelNode *node);
104 static void file_model_node_child_unref (FileModelNode *parent);
106 static const GtkFileInfo *file_model_node_get_info (GtkFileSystemModel *model,
107 FileModelNode *node);
108 static gboolean file_model_node_is_visible (GtkFileSystemModel *model,
109 FileModelNode *node);
110 static void file_model_node_clear (GtkFileSystemModel *model,
111 FileModelNode *node);
112 static FileModelNode * file_model_node_get_children (GtkFileSystemModel *model,
113 FileModelNode *node);
115 static void deleted_callback (GtkFileFolder *folder,
116 FileModelNode *node);
117 static void files_added_callback (GtkFileFolder *folder,
119 FileModelNode *node);
120 static void files_changed_callback (GtkFileFolder *folder,
122 FileModelNode *node);
123 static void files_removed_callback (GtkFileFolder *folder,
125 FileModelNode *node);
127 static void root_deleted_callback (GtkFileFolder *folder,
128 GtkFileSystemModel *model);
129 static void root_files_added_callback (GtkFileFolder *folder,
131 GtkFileSystemModel *model);
132 static void root_files_changed_callback (GtkFileFolder *folder,
134 GtkFileSystemModel *model);
135 static void root_files_removed_callback (GtkFileFolder *folder,
137 GtkFileSystemModel *model);
145 static guint file_system_model_signals[LAST_SIGNAL] = { 0 };
149 G_DEFINE_TYPE_WITH_CODE (GtkFileSystemModel, _gtk_file_system_model, G_TYPE_OBJECT,
150 G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL,
151 gtk_file_system_model_iface_init)
152 G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE,
153 drag_source_iface_init))
156 _gtk_file_system_model_class_init (GtkFileSystemModelClass *class)
158 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
160 gobject_class->finalize = gtk_file_system_model_finalize;
161 gobject_class->dispose = gtk_file_system_model_dispose;
163 file_system_model_signals[FINISHED_LOADING] =
164 g_signal_new (I_("finished-loading"),
165 G_OBJECT_CLASS_TYPE (gobject_class),
167 G_STRUCT_OFFSET (GtkFileSystemModelClass, finished_loading),
169 _gtk_marshal_VOID__VOID,
174 gtk_file_system_model_iface_init (GtkTreeModelIface *iface)
176 iface->get_flags = gtk_file_system_model_get_flags;
177 iface->get_n_columns = gtk_file_system_model_get_n_columns;
178 iface->get_column_type = gtk_file_system_model_get_column_type;
179 iface->get_iter = gtk_file_system_model_get_iter;
180 iface->get_path = gtk_file_system_model_get_path;
181 iface->get_value = gtk_file_system_model_get_value;
182 iface->iter_next = gtk_file_system_model_iter_next;
183 iface->iter_children = gtk_file_system_model_iter_children;
184 iface->iter_has_child = gtk_file_system_model_iter_has_child;
185 iface->iter_n_children = gtk_file_system_model_iter_n_children;
186 iface->iter_nth_child = gtk_file_system_model_iter_nth_child;
187 iface->iter_parent = gtk_file_system_model_iter_parent;
188 iface->ref_node = gtk_file_system_model_ref_node;
189 iface->unref_node = gtk_file_system_model_unref_node;
193 _gtk_file_system_model_init (GtkFileSystemModel *model)
195 model->show_files = TRUE;
196 model->show_folders = TRUE;
197 model->show_hidden = FALSE;
201 gtk_file_system_model_finalize (GObject *object)
203 GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (object);
204 FileModelNode *children, *next;
206 if (model->root_folder)
207 g_object_unref (model->root_folder);
209 if (model->root_path)
210 gtk_file_path_free (model->root_path);
212 if (model->file_system)
213 g_object_unref (model->file_system);
215 children = model->roots;
218 next = children->next;
219 file_model_node_free (children);
223 G_OBJECT_CLASS (_gtk_file_system_model_parent_class)->finalize (object);
228 gtk_file_system_model_dispose (GObject *object)
230 GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (object);
232 if (model->pending_handles)
236 for (l = model->pending_handles; l; l = l->next)
237 gtk_file_system_cancel_operation (l->data);
238 g_slist_free (model->pending_handles);
239 model->pending_handles = NULL;
242 G_OBJECT_CLASS (_gtk_file_system_model_parent_class)->dispose (object);
246 drag_source_iface_init (GtkTreeDragSourceIface *iface)
248 iface->row_draggable = drag_source_row_draggable;
249 iface->drag_data_get = drag_source_drag_data_get;
250 iface->drag_data_delete = NULL;
254 * ******************** GtkTreeModel methods ********************
257 static GtkTreeModelFlags
258 gtk_file_system_model_get_flags (GtkTreeModel *tree_model)
260 GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
261 GtkTreeModelFlags flags = GTK_TREE_MODEL_ITERS_PERSIST;
263 if (model->max_depth == 0)
264 flags |= GTK_TREE_MODEL_LIST_ONLY;
270 gtk_file_system_model_get_n_columns (GtkTreeModel *tree_model)
272 return GTK_FILE_SYSTEM_MODEL_N_COLUMNS;
276 gtk_file_system_model_get_column_type (GtkTreeModel *tree_model,
281 case GTK_FILE_SYSTEM_MODEL_INFO:
282 return GTK_TYPE_FILE_INFO;
283 case GTK_FILE_SYSTEM_MODEL_DISPLAY_NAME:
284 return G_TYPE_STRING;
286 g_assert_not_reached ();
292 gtk_file_system_model_get_iter (GtkTreeModel *tree_model,
300 indices = gtk_tree_path_get_indices (path);
301 depth = gtk_tree_path_get_depth (path);
303 g_return_val_if_fail (depth > 0, FALSE);
305 if (!gtk_tree_model_iter_nth_child (tree_model, iter, NULL, indices[0]))
308 for (i = 1; i < depth; i++)
311 if (!gtk_tree_model_iter_nth_child (tree_model, iter, &parent, indices[i]))
319 gtk_file_system_model_get_path (GtkTreeModel *tree_model,
322 GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
323 FileModelNode *node = iter->user_data;
325 GtkTreePath *result = gtk_tree_path_new ();
329 FileModelNode *parent = node->parent;
330 FileModelNode *children;
334 children = parent->children;
336 children = model->roots;
338 while (children != node)
340 if (children->is_visible)
342 children = children->next;
345 gtk_tree_path_prepend_index (result, n);
354 gtk_file_system_model_get_value (GtkTreeModel *tree_model,
359 GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
360 FileModelNode *node = iter->user_data;
361 const GtkFileInfo *info;
365 case GTK_FILE_SYSTEM_MODEL_INFO:
366 if (model->has_editable && node == model->roots)
369 info = file_model_node_get_info (model, node);
371 g_value_init (value, GTK_TYPE_FILE_INFO);
372 g_value_set_boxed (value, info);
374 case GTK_FILE_SYSTEM_MODEL_DISPLAY_NAME:
376 g_value_init (value, G_TYPE_STRING);
378 if (model->has_editable && node == model->roots)
379 g_value_set_string (value, "");
382 const GtkFileInfo *info = file_model_node_get_info (model, node);
384 g_value_set_string (value, gtk_file_info_get_display_name (info));
389 g_assert_not_reached ();
394 gtk_file_system_model_iter_next (GtkTreeModel *tree_model,
397 FileModelNode *node = iter->user_data;
400 while (node && !node->is_visible)
403 iter->user_data = node;
409 gtk_file_system_model_iter_children (GtkTreeModel *tree_model,
413 GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
414 FileModelNode *children;
418 FileModelNode *parent_node = parent->user_data;
419 children = file_model_node_get_children (model, parent_node);
423 children = model->roots;
426 while (children && !children->is_visible)
427 children = children->next;
429 iter->user_data = children;
431 return children != NULL;
435 gtk_file_system_model_iter_has_child (GtkTreeModel *tree_model,
438 FileModelNode *node = iter->user_data;
439 GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
441 if (node->depth == model->max_depth)
445 const GtkFileInfo *info = file_model_node_get_info (model, node);
446 return gtk_file_info_get_is_folder (info);
451 gtk_file_system_model_iter_n_children (GtkTreeModel *tree_model,
454 GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
455 FileModelNode *children;
460 FileModelNode *node = iter->user_data;
461 children = file_model_node_get_children (model, node);
465 children = model->roots;
470 if (children->is_visible)
472 children = children->next;
479 gtk_file_system_model_iter_nth_child (GtkTreeModel *tree_model,
484 GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
485 FileModelNode *children;
489 FileModelNode *parent_node = parent->user_data;
490 children = file_model_node_get_children (model, parent_node);
494 children = model->roots;
497 while (children && !children->is_visible)
498 children = children->next;
500 while (n && children)
503 children = children->next;
504 while (children && !children->is_visible)
505 children = children->next;
508 iter->user_data = children;
510 return children != NULL;
514 gtk_file_system_model_iter_parent (GtkTreeModel *tree_model,
518 FileModelNode *node = child->user_data;
521 iter->user_data = node;
527 gtk_file_system_model_ref_node (GtkTreeModel *tree_model,
530 file_model_node_ref (iter->user_data);
534 gtk_file_system_model_unref_node (GtkTreeModel *tree_model,
537 file_model_node_unref (GTK_FILE_SYSTEM_MODEL (tree_model),
542 drag_source_row_draggable (GtkTreeDragSource *drag_source,
545 GtkFileSystemModel *model;
549 model = GTK_FILE_SYSTEM_MODEL (drag_source);
551 if (!gtk_file_system_model_get_iter (GTK_TREE_MODEL (model), &iter, path))
554 if (!model->has_editable)
557 node = iter.user_data;
558 return (node != model->roots);
562 drag_source_drag_data_get (GtkTreeDragSource *drag_source,
564 GtkSelectionData *selection_data)
566 GtkFileSystemModel *model;
568 const GtkFilePath *file_path;
572 model = GTK_FILE_SYSTEM_MODEL (drag_source);
574 if (!gtk_file_system_model_get_iter (GTK_TREE_MODEL (model), &iter, path))
577 file_path = _gtk_file_system_model_get_path (model, &iter);
578 g_assert (file_path != NULL);
580 uri = gtk_file_system_path_to_uri (model->file_system, file_path);
581 uris = g_strconcat (uri, "\r\n", NULL);
583 gtk_selection_data_set (selection_data,
584 gdk_atom_intern_static_string ("text/uri-list"),
595 /* Callback used when the root folder finished loading */
597 root_folder_finished_loading_cb (GtkFileFolder *folder,
598 GtkFileSystemModel *model)
600 g_signal_emit (model, file_system_model_signals[FINISHED_LOADING], 0);
604 got_root_folder_cb (GtkFileSystemHandle *handle,
605 GtkFileFolder *folder,
609 GSList *roots = NULL;
611 gboolean cancelled = handle->cancelled;
612 GtkFileSystemModel *model = data;
614 tmp_list = g_slist_find (model->pending_handles, handle);
618 model->pending_handles = g_slist_remove_link (model->pending_handles,
621 if (cancelled || !folder)
624 model->root_folder = folder;
626 if (gtk_file_folder_is_finished_loading (model->root_folder))
627 g_signal_emit (model, file_system_model_signals[FINISHED_LOADING], 0);
629 g_signal_connect_object (model->root_folder, "finished-loading",
630 G_CALLBACK (root_folder_finished_loading_cb), model, 0);
632 gtk_file_folder_list_children (model->root_folder, &roots, NULL);
634 g_signal_connect_object (model->root_folder, "deleted",
635 G_CALLBACK (root_deleted_callback), model, 0);
636 g_signal_connect_object (model->root_folder, "files-added",
637 G_CALLBACK (root_files_added_callback), model, 0);
638 g_signal_connect_object (model->root_folder, "files-changed",
639 G_CALLBACK (root_files_changed_callback), model, 0);
640 g_signal_connect_object (model->root_folder, "files-removed",
641 G_CALLBACK (root_files_removed_callback), model, 0);
643 roots = gtk_file_paths_sort (roots);
645 for (tmp_list = roots; tmp_list; tmp_list = tmp_list->next)
647 FileModelNode *node = file_model_node_new (model, tmp_list->data);
648 gtk_file_path_free (tmp_list->data);
649 node->is_visible = file_model_node_is_visible (model, node);
650 node->next = model->roots;
654 if (node->is_visible)
659 iter.user_data = node;
660 path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
661 gtk_tree_model_row_inserted (GTK_TREE_MODEL (model), path, &iter);
662 gtk_tree_path_free (path);
665 g_slist_free (roots);
667 model->roots = (FileModelNode *) g_slist_reverse ((GSList *)model->roots);
670 g_object_unref (model);
671 g_object_unref (handle);
675 * _gtk_file_system_model_new:
676 * @file_system: an object implementing #GtkFileSystem
677 * @root_path: the path of root of the file system to display
678 * @max_depth: the maximum depth from the children of @root_path
679 * or the roots of the file system to display in
680 * the file selector). A depth of 0 displays
681 * only the immediate children of @root_path,
682 * or the roots of the filesystem. -1 for no
684 * @types: a bitmask indicating the types of information
685 * that is desired about the files. This will
686 * determine what information is returned by
687 * _gtk_file_system_model_get_info().
688 * @error: location to store error, or %NULL.
690 * Creates a new #GtkFileSystemModel object. The #GtkFileSystemModel
691 * object wraps a #GtkFileSystem interface as a #GtkTreeModel.
692 * Using the @root_path and @max_depth parameters, the tree model
693 * can be restricted to a subportion of the entire file system.
695 * Return value: the newly created #GtkFileSystemModel object, or NULL if there
699 _gtk_file_system_model_new (GtkFileSystem *file_system,
700 const GtkFilePath *root_path,
702 GtkFileInfoType types,
705 GtkFileSystemModel *model;
706 GtkFileSystemHandle *handle;
708 g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
709 g_return_val_if_fail (root_path != NULL, NULL);
710 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
712 /* First, start loading the root folder */
714 types |= GTK_FILE_INFO_IS_FOLDER | GTK_FILE_INFO_IS_HIDDEN;
716 /* Then, actually create the model and the root nodes */
718 model = g_object_new (GTK_TYPE_FILE_SYSTEM_MODEL, NULL);
719 model->file_system = g_object_ref (file_system);
721 model->max_depth = G_MAXUSHORT;
723 model->max_depth = MIN (max_depth, G_MAXUSHORT);
725 model->types = types;
726 model->root_folder = NULL;
727 model->root_path = gtk_file_path_copy (root_path);
731 handle = gtk_file_system_get_folder (file_system, root_path, types,
733 g_object_ref (model));
736 /* In this case got_root_folder_cb() will never be called, so we
737 * need to unref model twice.
739 g_object_unref (model);
740 g_object_unref (model);
743 GTK_FILE_CHOOSER_ERROR,
744 GTK_FILE_CHOOSER_ERROR_NONEXISTENT,
745 _("Could not obtain root folder"));
750 model->pending_handles = g_slist_append (model->pending_handles, handle);
756 model_refilter_recurse (GtkFileSystemModel *model,
757 FileModelNode *parent,
760 GtkTreeModel *tree_model = GTK_TREE_MODEL (model);
762 FileModelNode *nodes;
763 gboolean has_children = FALSE;
765 if (parent && !parent->loaded)
769 nodes = parent->children;
771 nodes = model->roots;
775 FileModelNode *next = nodes->next;
778 gtk_tree_path_append_index (path, i);
780 is_visible = file_model_node_is_visible (model, nodes);
782 if (!is_visible && nodes->is_visible)
784 file_model_node_clear (model, nodes);
785 gtk_tree_model_row_deleted (tree_model, path);
787 nodes->is_visible = FALSE;
789 else if (is_visible && !nodes->is_visible)
793 iter.user_data = nodes;
794 nodes->is_visible = TRUE;
795 gtk_tree_model_row_inserted (tree_model, path, &iter);
798 model_refilter_recurse (model, nodes, path);
806 gtk_tree_path_up (path);
811 if (parent && !has_children)
813 /* Fixme - need to insert dummy node here */
818 model_refilter_all (GtkFileSystemModel *model)
822 path = gtk_tree_path_new ();
823 model_refilter_recurse (model, NULL, path);
824 gtk_tree_path_free (path);
828 * _gtk_file_system_model_set_show_hidden:
829 * @model: a #GtkFileSystemModel
830 * @show_hidden: whether hidden files should be displayed
832 * Sets whether hidden files should be included in the #GtkTreeModel
836 _gtk_file_system_model_set_show_hidden (GtkFileSystemModel *model,
837 gboolean show_hidden)
839 show_hidden = show_hidden != FALSE;
841 if (show_hidden != model->show_hidden)
843 model->show_hidden = show_hidden;
844 model_refilter_all (model);
849 * _gtk_file_system_model_set_show_folders:
850 * @model: a #GtkFileSystemModel
851 * @show_folders: whether folders should be displayed
853 * Sets whether folders should be included in the #GtkTreeModel for
857 _gtk_file_system_model_set_show_folders (GtkFileSystemModel *model,
858 gboolean show_folders)
860 show_folders = show_folders != FALSE;
862 if (show_folders != model->show_folders)
864 model->show_folders = show_folders;
865 model_refilter_all (model);
870 * _gtk_file_system_model_set_show_files:
871 * @model: a #GtkFileSystemModel
872 * @show_files: whether files (as opposed to folders) should
875 * Sets whether files (as opposed to folders) should be included
876 * in the #GtkTreeModel for display.
879 _gtk_file_system_model_set_show_files (GtkFileSystemModel *model,
882 show_files = show_files != FALSE;
884 if (show_files != model->show_files)
886 model->show_files = show_files;
887 model_refilter_all (model);
892 * _gtk_file_system_model_get_info:
893 * @model: a #GtkFileSystemModel
894 * @iter: a #GtkTreeIter pointing to a row of @model
896 * Gets the #GtkFileInfo structure for a particular row
897 * of @model. The information included in this structure
898 * is determined by the @types parameter to
899 * _gtk_file_system_model_new().
901 * Return value: a #GtkFileInfo structure. This structure
902 * is owned by @model and must not be modified or freed.
903 * If you want to save the information for later use,
904 * you must make a copy, since the structure may be
905 * freed on later changes to the file system. If you have
906 * called _gtk_file_system_model_add_editable() and the @iter
907 * corresponds to the row that this function returned, the
908 * return value will be NULL.
911 _gtk_file_system_model_get_info (GtkFileSystemModel *model,
916 node = iter->user_data;
917 if (model->has_editable && node == model->roots)
920 return file_model_node_get_info (model, node);
924 * _gtk_file_system_model_get_path:
925 * @model: a #GtkFileSystemModel
926 * @iter: a #GtkTreeIter pointing to a row of @model
928 * Gets the path for a particular row in @model.
930 * Return value: the path. This string is owned by @model and
931 * or freed. If you want to save the path for later use,
932 * you must make a copy, since the string may be freed
933 * on later changes to the file system.
936 _gtk_file_system_model_get_path (GtkFileSystemModel *model,
939 FileModelNode *node = iter->user_data;
941 if (model->has_editable && node == model->roots)
945 return node->parent->path;
951 unref_node_and_parents (GtkFileSystemModel *model,
954 file_model_node_unref (model, node);
956 file_model_node_unref (model, node->parent);
959 static FileModelNode *
960 find_child_node (GtkFileSystemModel *model,
961 FileModelNode *parent_node,
962 const GtkFilePath *path)
964 FileModelNode *children;
967 children = file_model_node_get_children (model, parent_node);
969 children = model->roots;
973 if (children->is_visible &&
975 gtk_file_path_compare (children->path, path) == 0)
978 children = children->next;
985 * _gtk_file_system_model_set_filter:
986 * @mode: a #GtkFileSystemModel
987 * @filter: function to be called for each file
988 * @user_data: data to pass to @filter
990 * Sets a callback called for each file/directory to see whether
991 * it should be included in model. If this function was made
992 * public, we'd want to include a GDestroyNotify as well.
995 _gtk_file_system_model_set_filter (GtkFileSystemModel *model,
996 GtkFileSystemModelFilter filter,
999 g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model));
1001 model->filter_func = filter;
1002 model->filter_data = user_data;
1004 model_refilter_all (model);
1010 GtkFileSystemModel *model;
1011 FileModelNode *node;
1012 FileModelNode *parent_node;
1015 GtkFileSystemModelPathFunc func;
1019 /* FIXME: maybe we have to wait on finished-loading? */
1021 ref_path_cb (GtkFileSystemHandle *handle,
1022 GtkFileFolder *folder,
1023 const GError *error,
1026 struct RefPathData *info = data;
1027 gboolean cancelled = handle->cancelled;
1029 if (!g_slist_find (info->model->pending_handles, handle))
1032 info->model->pending_handles = g_slist_remove (info->model->pending_handles, handle);
1034 /* Note that !folder means that the child node was already
1035 * found, without using get_folder.
1037 if (cancelled || error)
1041 info->cleanups = g_slist_prepend (info->cleanups, folder);
1042 else if (g_slist_length (info->paths) == 1
1043 && gtk_file_path_compare (info->node->path, info->paths->data) == 0)
1045 /* Done, now call the function */
1051 iter.user_data = info->node;
1052 path = gtk_tree_model_get_path (GTK_TREE_MODEL (info->model), &iter);
1054 (* info->func) (info->model, path, &iter, info->user_data);
1056 gtk_tree_path_free (path);
1062 info->node = find_child_node (info->model, info->parent_node, info->paths->data);
1064 file_model_node_ref (info->node);
1070 gtk_file_path_free (info->paths->data);
1071 info->paths = g_slist_remove (info->paths, info->paths->data);
1073 if (g_slist_length (info->paths) < 1)
1075 /* Done, now call the function */
1081 iter.user_data = info->node;
1082 path = gtk_tree_model_get_path (GTK_TREE_MODEL (info->model), &iter);
1084 (* info->func) (info->model, path, &iter, info->user_data);
1086 gtk_tree_path_free (path);
1093 info->parent_node = info->node;
1095 if (info->parent_node->loaded)
1097 info->node = find_child_node (info->model, info->parent_node, info->paths->data);
1098 ref_path_cb (NULL, NULL, NULL, info);
1102 GtkFileSystemHandle *handle;
1104 handle = gtk_file_system_get_folder (info->model->file_system,
1108 info->model->pending_handles =
1109 g_slist_append (info->model->pending_handles, handle);
1117 unref_node_and_parents (info->model, info->node);
1118 gtk_file_paths_free (info->paths);
1119 g_slist_foreach (info->cleanups, (GFunc)g_object_unref, NULL);
1120 g_slist_free (info->cleanups);
1121 g_object_unref (info->model);
1124 g_object_unref (handle);
1128 * _gtk_file_system_model_path_do:
1129 * @model: a #GtkFileSystemModel
1130 * @path: a path pointing to a file in the filesystem
1132 * @func: Function to call with the path and iter corresponding
1134 * @user_data: data to pass to @func
1136 * Locates @path within @model, referencing
1137 * (gtk_tree_model_ref_node ()) all parent nodes,
1138 * calls @func passing in the path and iter for @path,
1139 * then unrefs all the parent nodes.
1141 * The reason for doing this operation as a callback
1142 * is so that if the operation performed with the
1143 * path and iter results in referencing the node
1144 * and/or parent nodes, we don't load all the information
1147 * This function is particularly useful for expanding
1148 * a #GtkTreeView to a particular point in the file system.
1150 * Return value: %TRUE if the path was successfully
1151 * found in @model and @func was called.
1154 _gtk_file_system_model_path_do (GtkFileSystemModel *model,
1155 const GtkFilePath *path,
1156 GtkFileSystemModelPathFunc func,
1159 GtkFilePath *parent_path;
1160 GSList *paths = NULL;
1161 FileModelNode *node;
1162 struct RefPathData *info;
1164 if (gtk_file_path_compare (path, model->root_path) == 0
1165 || !gtk_file_system_get_parent (model->file_system, path, &parent_path, NULL))
1168 paths = g_slist_prepend (paths, gtk_file_path_copy (path));
1169 while (gtk_file_path_compare (parent_path, model->root_path) != 0)
1171 paths = g_slist_prepend (paths, parent_path);
1172 if (!gtk_file_system_get_parent (model->file_system, parent_path, &parent_path, NULL))
1174 gtk_file_paths_free (paths);
1179 if (g_slist_length (paths) < 1)
1182 /* Now we have all paths, except the root path */
1183 node = find_child_node (model, NULL, paths->data);
1186 gtk_file_paths_free (paths);
1190 file_model_node_ref (node);
1192 gtk_file_path_free (paths->data);
1193 paths = g_slist_remove (paths, paths->data);
1195 if (g_slist_length (paths) < 1)
1197 /* Done, now call the function */
1203 iter.user_data = node;
1204 path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
1206 (* func) (model, path, &iter, user_data);
1208 gtk_tree_path_free (path);
1209 unref_node_and_parents (model, node);
1214 info = g_new0 (struct RefPathData, 1);
1215 info->paths = paths;
1216 info->model = g_object_ref (model);
1218 info->user_data = user_data;
1221 if (info->node->loaded)
1223 info->parent_node = info->node;
1224 info->node = find_child_node (model, info->parent_node, info->paths->data);
1225 ref_path_cb (NULL, NULL, NULL, info);
1229 GtkFileSystemHandle *handle;
1231 handle = gtk_file_system_get_folder (model->file_system,
1232 paths->data, model->types,
1234 model->pending_handles = g_slist_append (model->pending_handles, handle);
1240 * _gtk_file_system_model_add_editable:
1241 * @model: a #GtkFileSystemModel
1242 * @iter: Location to return the iter corresponding to the editable row
1244 * Adds an "empty" row at the beginning of the model. This does not refer to
1245 * any file, but is a temporary placeholder for a file name that the user will
1246 * type when a corresponding cell is made editable. When your code is done
1247 * using this temporary row, call _gtk_file_system_model_remove_editable().
1250 _gtk_file_system_model_add_editable (GtkFileSystemModel *model, GtkTreeIter *iter)
1252 FileModelNode *node;
1255 g_return_if_fail (!model->has_editable);
1257 model->has_editable = TRUE;
1259 node = file_model_node_new (model, NULL);
1260 node->is_visible = TRUE;
1262 node->next = model->roots;
1263 model->roots = node;
1265 path = gtk_tree_path_new ();
1266 gtk_tree_path_append_index (path, 0);
1267 iter->user_data = node;
1269 gtk_tree_model_row_inserted (GTK_TREE_MODEL (model), path, iter);
1271 gtk_tree_path_free (path);
1275 * _gtk_file_system_model_remove_editable:
1276 * @model: a #GtkFileSystemModel
1278 * Removes the "empty" row at the beginning of the model that was
1279 * created with _gtk_file_system_model_add_editable(). You should call
1280 * this function when your code is finished editing this temporary row.
1283 _gtk_file_system_model_remove_editable (GtkFileSystemModel *model)
1286 FileModelNode *node;
1288 g_return_if_fail (model->has_editable);
1290 model->has_editable = FALSE;
1292 node = model->roots;
1293 model->roots = model->roots->next;
1294 file_model_node_free (node);
1296 path = gtk_tree_path_new ();
1297 gtk_tree_path_append_index (path, 0);
1299 gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path);
1301 gtk_tree_path_free (path);
1304 static FileModelNode *
1305 file_model_node_new (GtkFileSystemModel *model,
1306 const GtkFilePath *path)
1308 FileModelNode *node = g_new0 (FileModelNode, 1);
1310 node->model = model;
1311 node->path = path ? gtk_file_path_copy (path) : NULL;
1317 file_model_node_free (FileModelNode *node)
1319 file_model_node_clear (node->model, node);
1322 gtk_file_path_free (node->path);
1325 gtk_file_info_free (node->info);
1330 static const GtkFileInfo *
1331 file_model_node_get_info (GtkFileSystemModel *model,
1332 FileModelNode *node)
1338 node->info = gtk_file_info_new ();
1339 gtk_file_info_set_display_name (node->info, _("(Empty)"));
1341 else if (node->parent || model->root_folder)
1343 node->info = gtk_file_folder_get_info (node->parent ? node->parent->folder : model->root_folder,
1345 NULL); /* NULL-GError */
1348 g_assert_not_reached ();
1355 file_model_node_is_visible (GtkFileSystemModel *model,
1356 FileModelNode *node)
1358 if (model->show_folders != model->show_files ||
1359 !model->show_hidden ||
1362 const GtkFileInfo *info = file_model_node_get_info (model, node);
1366 /* File probably disappeared underneath us or resides in a
1367 directory where we have only partial access rights. */
1371 if (model->show_folders != model->show_files &&
1372 model->show_folders != gtk_file_info_get_is_folder (info))
1375 if (!model->show_hidden && gtk_file_info_get_is_hidden (info))
1378 if (model->filter_func &&
1379 !model->filter_func (model, node->path, info, model->filter_data))
1387 file_model_node_clear (GtkFileSystemModel *model,
1388 FileModelNode *node)
1390 FileModelNode *children;
1392 file_model_node_idle_clear_cancel (node);
1394 children = node->children;
1395 node->children = NULL;
1396 node->loaded = FALSE;
1400 FileModelNode *next = children->next;
1402 file_model_node_free (children);
1409 /* Unreffing node->folder may cause roots_changed,
1410 * so we need to be careful about ordering.
1412 GtkFileFolder *folder = node->folder;
1413 node->folder = NULL;
1415 g_signal_handlers_disconnect_by_func (folder, G_CALLBACK (deleted_callback), node);
1416 g_signal_handlers_disconnect_by_func (folder, G_CALLBACK (files_added_callback), node);
1417 g_signal_handlers_disconnect_by_func (folder, G_CALLBACK (files_changed_callback), node);
1418 g_signal_handlers_disconnect_by_func (folder, G_CALLBACK (files_removed_callback), node);
1420 g_object_unref (folder);
1425 file_model_node_ref (FileModelNode *node)
1428 if (node->ref_count == 1 && node->parent)
1429 node->parent->n_referenced_children++;
1433 idle_clear_callback (GtkFileSystemModel *model)
1435 while (model->idle_clears)
1437 FileModelNode *node = model->idle_clears->data;
1438 model->idle_clears = g_slist_delete_link (model->idle_clears, model->idle_clears);
1440 node->idle_clear = FALSE;
1441 file_model_node_clear (node->model, node);
1448 file_model_node_idle_clear (FileModelNode *node)
1450 if (!node->idle_clear)
1452 GtkFileSystemModel *model = node->model;
1454 node->idle_clear = TRUE;
1455 if (!model->idle_clears)
1457 model->idle_clear_source = g_idle_source_new ();
1458 g_source_set_priority (model->idle_clear_source, G_PRIORITY_HIGH);
1459 g_source_set_closure (model->idle_clear_source,
1460 g_cclosure_new_object (G_CALLBACK (idle_clear_callback),
1462 g_source_attach (model->idle_clear_source, NULL);
1465 model->idle_clears = g_slist_prepend (model->idle_clears, node);
1466 node->idle_clear = TRUE;
1471 file_model_node_idle_clear_cancel (FileModelNode *node)
1473 if (node->idle_clear)
1475 GtkFileSystemModel *model = node->model;
1477 model->idle_clears = g_slist_remove (model->idle_clears, node);
1478 if (!model->idle_clears)
1480 g_source_destroy (model->idle_clear_source);
1481 model->idle_clear_source = NULL;
1484 node->idle_clear = FALSE;
1489 file_model_node_unref (GtkFileSystemModel *model,
1490 FileModelNode *node)
1493 if (node->ref_count == 0)
1495 file_model_node_clear (model, node);
1497 file_model_node_child_unref (node->parent);
1502 file_model_node_child_unref (FileModelNode *parent)
1504 parent->n_referenced_children--;
1505 if (parent->n_referenced_children == 0)
1506 file_model_node_idle_clear (parent);
1509 struct GetChildrenData
1511 GtkFileSystemModel *model;
1512 FileModelNode *node;
1516 get_children_get_folder_cb (GtkFileSystemHandle *handle,
1517 GtkFileFolder *folder,
1518 const GError *error,
1519 gpointer callback_data)
1521 GSList *child_paths, *tmp_list;
1522 gboolean has_children = FALSE;
1523 gboolean cancelled = handle->cancelled;
1524 struct GetChildrenData *data = callback_data;
1526 tmp_list = g_slist_find (data->model->pending_handles, handle);
1531 data->model->pending_handles = g_slist_remove_link (data->model->pending_handles, tmp_list);
1533 if (cancelled || !folder)
1535 /* error, no folder, remove dummy child */
1536 if (data->node->parent && data->node->parent->has_dummy)
1538 data->node->parent->children = NULL;
1539 data->node->parent->has_dummy = FALSE;
1542 file_model_node_free (data->node);
1547 data->node->folder = folder;
1548 data->node->load_pending = FALSE;
1550 if (gtk_file_folder_list_children (folder, &child_paths, NULL)) /* NULL-GError */
1552 child_paths = gtk_file_paths_sort (child_paths);
1554 for (tmp_list = child_paths; tmp_list; tmp_list = tmp_list->next)
1556 FileModelNode *child_node = file_model_node_new (data->model, tmp_list->data);
1557 gtk_file_path_free (tmp_list->data);
1558 child_node->next = data->node->children;
1559 child_node->parent = data->node;
1560 child_node->depth = data->node->depth + 1;
1561 child_node->is_visible = file_model_node_is_visible (data->model, child_node);
1563 if (child_node->is_visible)
1568 has_children = TRUE;
1570 iter.user_data = child_node;
1571 path = gtk_tree_model_get_path (GTK_TREE_MODEL (data->model), &iter);
1572 gtk_tree_model_row_inserted (GTK_TREE_MODEL (data->model), path, &iter);
1573 gtk_tree_path_free (path);
1576 data->node->children = child_node;
1578 g_slist_free (child_paths);
1581 data->node->children = (FileModelNode *)g_slist_reverse ((GSList *)data->node->children);
1583 g_signal_connect (data->node->folder, "deleted",
1584 G_CALLBACK (deleted_callback), data->node);
1585 g_signal_connect (data->node->folder, "files-added",
1586 G_CALLBACK (files_added_callback), data->node);
1587 g_signal_connect (data->node->folder, "files-changed",
1588 G_CALLBACK (files_changed_callback), data->node);
1589 g_signal_connect (data->node->folder, "files-removed",
1590 G_CALLBACK (files_removed_callback), data->node);
1592 data->node->loaded = TRUE;
1596 /* The hard case ... we claimed this folder had children, but actually
1597 * it didn't. We have to add a dummy child, possibly to remove later.
1599 FileModelNode *child_node = file_model_node_new (data->model, NULL);
1600 child_node->is_visible = TRUE;
1601 child_node->parent = data->node;
1602 child_node->is_dummy = TRUE;
1604 data->node->children = child_node;
1605 data->node->has_dummy = TRUE;
1608 g_object_set_data (G_OBJECT (data->node->folder), I_("model-node"), data->node);
1611 g_object_unref (data->model);
1614 g_object_unref (handle);
1617 static FileModelNode *
1618 file_model_node_get_children (GtkFileSystemModel *model,
1619 FileModelNode *node)
1621 if (node->ref_count == 0)
1624 if (!node->loaded && !node->load_pending)
1626 const GtkFileInfo *info = file_model_node_get_info (model, node);
1627 gboolean has_children = FALSE;
1628 gboolean is_folder = node->depth < model->max_depth && gtk_file_info_get_is_folder (info);
1630 file_model_node_idle_clear_cancel (node);
1634 struct GetChildrenData *data;
1635 GtkFileSystemHandle *handle;
1637 data = g_new (struct GetChildrenData, 1);
1638 data->model = g_object_ref (model);
1642 gtk_file_system_get_folder (model->file_system,
1645 get_children_get_folder_cb,
1648 model->pending_handles = g_slist_append (model->pending_handles, handle);
1649 node->load_pending = TRUE;
1652 if (is_folder && !has_children)
1654 /* The hard case ... we claimed this folder had children, but actually
1655 * it didn't. We have to add a dummy child, possibly to remove later.
1657 FileModelNode *child_node = file_model_node_new (model, NULL);
1658 child_node->is_visible = TRUE;
1659 child_node->parent = node;
1660 child_node->is_dummy = TRUE;
1662 node->children = child_node;
1663 node->has_dummy = TRUE;
1667 return node->children;
1671 do_files_added (GtkFileSystemModel *model,
1672 FileModelNode *parent_node,
1675 GtkTreeModel *tree_model = GTK_TREE_MODEL (model);
1676 FileModelNode *children;
1677 FileModelNode *prev = NULL;
1680 GSList *sorted_paths;
1683 sorted_paths = gtk_file_paths_sort (g_slist_copy (paths));
1687 iter.user_data = parent_node;
1688 path = gtk_tree_model_get_path (tree_model, &iter);
1689 children = parent_node->children;
1693 path = gtk_tree_path_new ();
1694 children = model->roots;
1697 gtk_tree_path_down (path);
1699 if (parent_node && parent_node->has_dummy)
1702 children = children->next;
1703 gtk_tree_path_next (path);
1706 for (tmp_list = sorted_paths; tmp_list; tmp_list = tmp_list->next)
1708 const GtkFilePath *file_path = tmp_list->data;
1711 (!children->path || gtk_file_path_compare (children->path, file_path) < 0))
1714 if (children->is_visible)
1715 gtk_tree_path_next (path);
1717 children = children->next;
1721 children->path && gtk_file_path_compare (children->path, file_path) == 0)
1723 /* Shouldn't happen */
1729 new = file_model_node_new (model, file_path);
1732 new->next = children;
1735 else if (parent_node)
1736 parent_node->children = new;
1744 new->parent = parent_node;
1745 new->depth = parent_node->depth + 1;
1748 new->is_visible = file_model_node_is_visible (model, new);
1750 if (new->is_visible)
1752 iter.user_data = new;
1753 gtk_tree_path_free (path);
1754 path = gtk_tree_model_get_path (tree_model, &iter);
1755 gtk_tree_model_row_inserted (tree_model, path, &iter);
1757 if (gtk_file_system_model_iter_has_child (tree_model, &iter))
1758 gtk_tree_model_row_has_child_toggled (tree_model, path, &iter);
1760 if (parent_node && parent_node->has_dummy)
1762 FileModelNode *dummy = parent_node->children;
1763 GtkTreePath *dummy_path;
1765 parent_node->children = parent_node->children->next;
1766 parent_node->has_dummy = FALSE;
1768 dummy_path = gtk_tree_path_copy (path);
1769 gtk_tree_path_up (dummy_path);
1770 gtk_tree_path_down (dummy_path);
1772 gtk_tree_model_row_deleted (tree_model, dummy_path);
1773 gtk_tree_path_free (dummy_path);
1775 if (dummy->ref_count)
1776 file_model_node_child_unref (parent_node);
1777 file_model_node_free (dummy);
1780 gtk_tree_path_next (path);
1785 gtk_tree_path_free (path);
1786 g_slist_free (sorted_paths);
1790 do_files_changed (GtkFileSystemModel *model,
1791 FileModelNode *parent_node,
1794 GtkTreeModel *tree_model = GTK_TREE_MODEL (model);
1795 FileModelNode *children;
1798 GSList *sorted_paths;
1801 sorted_paths = gtk_file_paths_sort (g_slist_copy (paths));
1805 iter.user_data = parent_node;
1806 path = gtk_tree_model_get_path (tree_model, &iter);
1807 children = parent_node->children;
1811 path = gtk_tree_path_new ();
1812 children = model->roots;
1815 gtk_tree_path_down (path);
1817 if (parent_node && parent_node->has_dummy)
1819 children = children->next;
1820 gtk_tree_path_next (path);
1823 for (tmp_list = sorted_paths; tmp_list; tmp_list = tmp_list->next)
1825 const GtkFilePath *file_path = tmp_list->data;
1828 (!children->path || gtk_file_path_compare (children->path, file_path) < 0))
1830 if (children->is_visible)
1831 gtk_tree_path_next (path);
1833 children = children->next;
1837 children->path && gtk_file_path_compare (children->path, file_path) == 0)
1839 gtk_tree_model_row_changed (tree_model, path, &iter);
1843 /* Shouldn't happen */
1847 gtk_tree_path_free (path);
1848 g_slist_free (sorted_paths);
1852 do_files_removed (GtkFileSystemModel *model,
1853 FileModelNode *parent_node,
1856 GtkTreeModel *tree_model = GTK_TREE_MODEL (model);
1857 FileModelNode *children;
1858 FileModelNode *prev = NULL;
1861 GSList *sorted_paths;
1863 FileModelNode *tmp_child;
1866 sorted_paths = gtk_file_paths_sort (g_slist_copy (paths));
1870 iter.user_data = parent_node;
1871 path = gtk_tree_model_get_path (tree_model, &iter);
1872 children = parent_node->children;
1876 path = gtk_tree_path_new ();
1877 children = model->roots;
1880 /* Count the number of currently visible children, so that
1881 * can catch when we need to insert a dummy node.
1884 for (tmp_child = children; tmp_child; tmp_child = tmp_child->next)
1886 if (tmp_child->is_visible)
1890 gtk_tree_path_down (path);
1892 if (parent_node && parent_node->has_dummy)
1895 children = children->next;
1896 gtk_tree_path_next (path);
1899 for (tmp_list = sorted_paths; tmp_list; tmp_list = tmp_list->next)
1901 const GtkFilePath *file_path = tmp_list->data;
1904 (!children->path || gtk_file_path_compare (children->path, file_path) < 0))
1907 if (children->is_visible)
1908 gtk_tree_path_next (path);
1910 children = children->next;
1914 children->path && gtk_file_path_compare (children->path, file_path) == 0)
1916 FileModelNode *next = children->next;
1918 if (children->is_visible)
1921 if (parent_node && n_visible == 0)
1923 FileModelNode *dummy = file_model_node_new (model, NULL);
1924 dummy->is_visible = TRUE;
1925 dummy->parent = parent_node;
1926 dummy->is_dummy = TRUE;
1928 parent_node->children = dummy;
1929 parent_node->has_dummy = TRUE;
1931 iter.user_data = dummy;
1932 gtk_tree_model_row_inserted (tree_model, path, &iter);
1933 gtk_tree_path_next (path);
1940 else if (parent_node)
1941 parent_node->children = next;
1943 model->roots = next;
1945 if (parent_node && children->ref_count)
1946 file_model_node_child_unref (parent_node);
1948 if (children->is_visible)
1949 gtk_tree_model_row_deleted (tree_model, path);
1951 file_model_node_free (children);
1957 /* Shouldn't happen */
1961 gtk_tree_path_free (path);
1962 g_slist_free (sorted_paths);
1966 deleted_callback (GtkFileFolder *folder,
1967 FileModelNode *node)
1972 files_added_callback (GtkFileFolder *folder,
1974 FileModelNode *node)
1976 do_files_added (node->model, node, paths);
1980 files_changed_callback (GtkFileFolder *folder,
1982 FileModelNode *node)
1984 do_files_changed (node->model, node, paths);
1988 files_removed_callback (GtkFileFolder *folder,
1990 FileModelNode *node)
1992 do_files_removed (node->model, node, paths);
1996 root_deleted_callback (GtkFileFolder *folder,
1997 GtkFileSystemModel *model)
2002 root_files_added_callback (GtkFileFolder *folder,
2004 GtkFileSystemModel *model)
2006 do_files_added (model, NULL, paths);
2010 root_files_changed_callback (GtkFileFolder *folder,
2012 GtkFileSystemModel *model)
2014 do_files_changed (model, NULL, paths);
2018 root_files_removed_callback (GtkFileFolder *folder,
2020 GtkFileSystemModel *model)
2022 do_files_removed (model, NULL, paths);