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.
25 #include "gtkfilesystemmodel.h"
26 #include "gtkfilesystem.h"
28 #include "gtkmarshalers.h"
29 #include "gtktreednd.h"
30 #include "gtktreemodel.h"
32 typedef struct _GtkFileSystemModelClass GtkFileSystemModelClass;
33 typedef struct _FileModelNode FileModelNode;
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);
48 struct _GtkFileSystemModel
50 GObject parent_instance;
52 GtkFileSystem *file_system;
53 GtkFileInfoType types;
55 GtkFileFolder *root_folder;
56 GtkFilePath *root_path;
58 GtkFileSystemModelFilter filter_func;
62 GSource *idle_clear_source;
63 GSource *idle_finished_loading_source;
67 guint show_hidden : 1;
68 guint show_folders : 1;
70 guint folders_only : 1;
71 guint has_editable : 1;
80 GtkFileFolder *folder;
82 FileModelNode *children;
83 FileModelNode *parent;
84 GtkFileSystemModel *model;
87 guint n_referenced_children;
98 static void gtk_file_system_model_class_init (GtkFileSystemModelClass *class);
99 static void gtk_file_system_model_iface_init (GtkTreeModelIface *iface);
100 static void gtk_file_system_model_init (GtkFileSystemModel *model);
101 static void gtk_file_system_model_finalize (GObject *object);
103 static void drag_source_iface_init (GtkTreeDragSourceIface *iface);
105 static GtkTreeModelFlags gtk_file_system_model_get_flags (GtkTreeModel *tree_model);
106 static gint gtk_file_system_model_get_n_columns (GtkTreeModel *tree_model);
107 static GType gtk_file_system_model_get_column_type (GtkTreeModel *tree_model,
109 static gboolean gtk_file_system_model_get_iter (GtkTreeModel *tree_model,
112 static GtkTreePath * gtk_file_system_model_get_path (GtkTreeModel *tree_model,
114 static void gtk_file_system_model_get_value (GtkTreeModel *tree_model,
118 static gboolean gtk_file_system_model_iter_next (GtkTreeModel *tree_model,
120 static gboolean gtk_file_system_model_iter_children (GtkTreeModel *tree_model,
122 GtkTreeIter *parent);
123 static gboolean gtk_file_system_model_iter_has_child (GtkTreeModel *tree_model,
125 static gint gtk_file_system_model_iter_n_children (GtkTreeModel *tree_model,
127 static gboolean gtk_file_system_model_iter_nth_child (GtkTreeModel *tree_model,
131 static gboolean gtk_file_system_model_iter_parent (GtkTreeModel *tree_model,
134 static void gtk_file_system_model_ref_node (GtkTreeModel *tree_model,
136 static void gtk_file_system_model_unref_node (GtkTreeModel *tree_model,
139 static gboolean drag_source_row_draggable (GtkTreeDragSource *drag_source,
141 static gboolean drag_source_drag_data_get (GtkTreeDragSource *drag_source,
143 GtkSelectionData *selection_data);
145 static FileModelNode *file_model_node_new (GtkFileSystemModel *model,
146 const GtkFilePath *path);
147 static void file_model_node_free (FileModelNode *node);
148 static void file_model_node_ref (FileModelNode *node);
149 static void file_model_node_unref (GtkFileSystemModel *model,
150 FileModelNode *node);
152 static void file_model_node_idle_clear (FileModelNode *node);
153 static void file_model_node_idle_clear_cancel (FileModelNode *node);
154 static void file_model_node_child_unref (FileModelNode *parent);
156 static const GtkFileInfo *file_model_node_get_info (GtkFileSystemModel *model,
157 FileModelNode *node);
158 static gboolean file_model_node_is_visible (GtkFileSystemModel *model,
159 FileModelNode *node);
160 static void file_model_node_clear (GtkFileSystemModel *model,
161 FileModelNode *node);
162 static FileModelNode * file_model_node_get_children (GtkFileSystemModel *model,
163 FileModelNode *node);
165 static void deleted_callback (GtkFileFolder *folder,
166 FileModelNode *node);
167 static void files_added_callback (GtkFileFolder *folder,
169 FileModelNode *node);
170 static void files_changed_callback (GtkFileFolder *folder,
172 FileModelNode *node);
173 static void files_removed_callback (GtkFileFolder *folder,
175 FileModelNode *node);
177 static void root_deleted_callback (GtkFileFolder *folder,
178 GtkFileSystemModel *model);
179 static void root_files_added_callback (GtkFileFolder *folder,
181 GtkFileSystemModel *model);
182 static void root_files_changed_callback (GtkFileFolder *folder,
184 GtkFileSystemModel *model);
185 static void root_files_removed_callback (GtkFileFolder *folder,
187 GtkFileSystemModel *model);
189 static GObjectClass *parent_class = NULL;
197 static guint file_system_model_signals[LAST_SIGNAL] = { 0 };
202 _gtk_file_system_model_get_type (void)
204 static GType file_system_model_type = 0;
206 if (!file_system_model_type)
208 static const GTypeInfo file_system_model_info =
210 sizeof (GtkFileSystemModelClass),
211 NULL, /* base_init */
212 NULL, /* base_finalize */
213 (GClassInitFunc) gtk_file_system_model_class_init,
214 NULL, /* class_finalize */
215 NULL, /* class_data */
216 sizeof (GtkFileSystemModel),
218 (GInstanceInitFunc) gtk_file_system_model_init,
221 static const GInterfaceInfo file_system_info =
223 (GInterfaceInitFunc) gtk_file_system_model_iface_init, /* interface_init */
224 NULL, /* interface_finalize */
225 NULL /* interface_data */
228 static const GInterfaceInfo drag_source_info =
230 (GInterfaceInitFunc) drag_source_iface_init, /* interface_init */
231 NULL, /* interface_finalize */
232 NULL /* interface_data */
235 file_system_model_type = g_type_register_static (G_TYPE_OBJECT,
236 "GtkFileSystemModel",
237 &file_system_model_info, 0);
238 g_type_add_interface_static (file_system_model_type,
241 g_type_add_interface_static (file_system_model_type,
242 GTK_TYPE_TREE_DRAG_SOURCE,
246 return file_system_model_type;
250 gtk_file_system_model_class_init (GtkFileSystemModelClass *class)
252 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
254 parent_class = g_type_class_peek_parent (class);
256 gobject_class->finalize = gtk_file_system_model_finalize;
258 file_system_model_signals[FINISHED_LOADING] =
259 g_signal_new ("finished-loading",
260 G_OBJECT_CLASS_TYPE (gobject_class),
262 G_STRUCT_OFFSET (GtkFileSystemModelClass, finished_loading),
264 _gtk_marshal_VOID__VOID,
269 gtk_file_system_model_iface_init (GtkTreeModelIface *iface)
271 iface->get_flags = gtk_file_system_model_get_flags;
272 iface->get_n_columns = gtk_file_system_model_get_n_columns;
273 iface->get_column_type = gtk_file_system_model_get_column_type;
274 iface->get_iter = gtk_file_system_model_get_iter;
275 iface->get_path = gtk_file_system_model_get_path;
276 iface->get_value = gtk_file_system_model_get_value;
277 iface->iter_next = gtk_file_system_model_iter_next;
278 iface->iter_children = gtk_file_system_model_iter_children;
279 iface->iter_has_child = gtk_file_system_model_iter_has_child;
280 iface->iter_n_children = gtk_file_system_model_iter_n_children;
281 iface->iter_nth_child = gtk_file_system_model_iter_nth_child;
282 iface->iter_parent = gtk_file_system_model_iter_parent;
283 iface->ref_node = gtk_file_system_model_ref_node;
284 iface->unref_node = gtk_file_system_model_unref_node;
288 gtk_file_system_model_init (GtkFileSystemModel *model)
290 model->show_files = TRUE;
291 model->show_folders = TRUE;
292 model->show_hidden = FALSE;
296 gtk_file_system_model_finalize (GObject *object)
298 GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (object);
299 FileModelNode *children, *next;
301 if (model->root_folder)
302 g_object_unref (model->root_folder);
304 if (model->root_path)
305 gtk_file_path_free (model->root_path);
307 if (model->file_system)
308 g_object_unref (model->file_system);
310 if (model->idle_finished_loading_source)
311 g_source_destroy (model->idle_finished_loading_source);
313 children = model->roots;
316 next = children->next;
317 file_model_node_free (children);
321 G_OBJECT_CLASS (parent_class)->finalize (object);
325 drag_source_iface_init (GtkTreeDragSourceIface *iface)
327 iface->row_draggable = drag_source_row_draggable;
328 iface->drag_data_get = drag_source_drag_data_get;
329 iface->drag_data_delete = NULL;
333 * ******************** GtkTreeModel methods ********************
336 static GtkTreeModelFlags
337 gtk_file_system_model_get_flags (GtkTreeModel *tree_model)
339 GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
340 GtkTreeModelFlags flags = GTK_TREE_MODEL_ITERS_PERSIST;
342 if (model->max_depth == 0)
343 flags |= GTK_TREE_MODEL_LIST_ONLY;
349 gtk_file_system_model_get_n_columns (GtkTreeModel *tree_model)
351 return GTK_FILE_SYSTEM_MODEL_N_COLUMNS;
355 gtk_file_system_model_get_column_type (GtkTreeModel *tree_model,
360 case GTK_FILE_SYSTEM_MODEL_INFO:
361 return GTK_TYPE_FILE_INFO;
362 case GTK_FILE_SYSTEM_MODEL_DISPLAY_NAME:
363 return G_TYPE_STRING;
365 g_assert_not_reached ();
371 gtk_file_system_model_get_iter (GtkTreeModel *tree_model,
379 indices = gtk_tree_path_get_indices (path);
380 depth = gtk_tree_path_get_depth (path);
382 g_return_val_if_fail (depth > 0, FALSE);
384 if (!gtk_tree_model_iter_nth_child (tree_model, iter, NULL, indices[0]))
387 for (i = 1; i < depth; i++)
390 if (!gtk_tree_model_iter_nth_child (tree_model, iter, &parent, indices[i]))
398 gtk_file_system_model_get_path (GtkTreeModel *tree_model,
401 GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
402 FileModelNode *node = iter->user_data;
404 GtkTreePath *result = gtk_tree_path_new ();
408 FileModelNode *parent = node->parent;
409 FileModelNode *children;
413 children = parent->children;
415 children = model->roots;
417 while (children != node)
419 if (children->is_visible)
421 children = children->next;
424 gtk_tree_path_prepend_index (result, n);
433 gtk_file_system_model_get_value (GtkTreeModel *tree_model,
438 GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
439 FileModelNode *node = iter->user_data;
440 const GtkFileInfo *info;
444 case GTK_FILE_SYSTEM_MODEL_INFO:
445 if (model->has_editable && node == model->roots)
448 info = file_model_node_get_info (model, node);
450 g_value_init (value, GTK_TYPE_FILE_INFO);
451 g_value_set_boxed (value, info);
453 case GTK_FILE_SYSTEM_MODEL_DISPLAY_NAME:
455 g_value_init (value, G_TYPE_STRING);
457 if (model->has_editable && node == model->roots)
458 g_value_set_string (value, "");
461 const GtkFileInfo *info = file_model_node_get_info (model, node);
463 g_value_set_string (value, gtk_file_info_get_display_name (info));
468 g_assert_not_reached ();
473 gtk_file_system_model_iter_next (GtkTreeModel *tree_model,
476 FileModelNode *node = iter->user_data;
479 while (node && !node->is_visible)
482 iter->user_data = node;
488 gtk_file_system_model_iter_children (GtkTreeModel *tree_model,
492 GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
493 FileModelNode *children;
497 FileModelNode *parent_node = parent->user_data;
498 children = file_model_node_get_children (model, parent_node);
502 children = model->roots;
505 while (children && !children->is_visible)
506 children = children->next;
508 iter->user_data = children;
510 return children != NULL;
514 gtk_file_system_model_iter_has_child (GtkTreeModel *tree_model,
517 FileModelNode *node = iter->user_data;
518 GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
520 if (node->depth == model->max_depth)
524 const GtkFileInfo *info = file_model_node_get_info (model, node);
525 return gtk_file_info_get_is_folder (info);
530 gtk_file_system_model_iter_n_children (GtkTreeModel *tree_model,
533 GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
534 FileModelNode *children;
539 FileModelNode *node = iter->user_data;
540 children = file_model_node_get_children (model, node);
544 children = model->roots;
549 if (children->is_visible)
551 children = children->next;
558 gtk_file_system_model_iter_nth_child (GtkTreeModel *tree_model,
563 GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
564 FileModelNode *children;
568 FileModelNode *parent_node = parent->user_data;
569 children = file_model_node_get_children (model, parent_node);
573 children = model->roots;
576 while (children && !children->is_visible)
577 children = children->next;
579 while (n && children)
582 children = children->next;
583 while (children && !children->is_visible)
584 children = children->next;
587 iter->user_data = children;
589 return children != NULL;
593 gtk_file_system_model_iter_parent (GtkTreeModel *tree_model,
597 FileModelNode *node = child->user_data;
600 iter->user_data = node;
606 gtk_file_system_model_ref_node (GtkTreeModel *tree_model,
609 file_model_node_ref (iter->user_data);
613 gtk_file_system_model_unref_node (GtkTreeModel *tree_model,
616 file_model_node_unref (GTK_FILE_SYSTEM_MODEL (tree_model),
621 drag_source_row_draggable (GtkTreeDragSource *drag_source,
624 GtkFileSystemModel *model;
628 model = GTK_FILE_SYSTEM_MODEL (drag_source);
630 if (!gtk_file_system_model_get_iter (GTK_TREE_MODEL (model), &iter, path))
633 if (!model->has_editable)
636 node = iter.user_data;
637 return (node != model->roots);
641 drag_source_drag_data_get (GtkTreeDragSource *drag_source,
643 GtkSelectionData *selection_data)
645 GtkFileSystemModel *model;
647 const GtkFilePath *file_path;
651 model = GTK_FILE_SYSTEM_MODEL (drag_source);
653 if (!gtk_file_system_model_get_iter (GTK_TREE_MODEL (model), &iter, path))
656 file_path = _gtk_file_system_model_get_path (model, &iter);
657 g_assert (file_path != NULL);
659 uri = gtk_file_system_path_to_uri (model->file_system, file_path);
660 uris = g_strconcat (uri, "\r\n", NULL);
662 gtk_selection_data_set (selection_data,
663 gdk_atom_intern ("text/uri-list", FALSE),
674 /* Callback used when the root folder finished loading */
676 root_folder_finished_loading_cb (GtkFileFolder *folder,
677 GtkFileSystemModel *model)
679 g_signal_emit (model, file_system_model_signals[FINISHED_LOADING], 0);
682 /* Emits the "finished-loading" signal as an idle handler; see the comment in
683 * _gtk_file_system_model_new()
686 idle_finished_loading_cb (GtkFileSystemModel *model)
688 g_signal_emit (model, file_system_model_signals[FINISHED_LOADING], 0);
690 g_source_destroy (model->idle_finished_loading_source);
691 model->idle_finished_loading_source = NULL;
696 /* Queues an idle handler to emit the "finished-loading" signal */
698 queue_finished_loading (GtkFileSystemModel *model)
700 model->idle_finished_loading_source = g_idle_source_new ();
701 g_source_set_closure (model->idle_finished_loading_source,
702 g_cclosure_new_object (G_CALLBACK (idle_finished_loading_cb),
704 g_source_attach (model->idle_finished_loading_source, NULL);
708 * _gtk_file_system_model_new:
709 * @file_system: an object implementing #GtkFileSystem
710 * @root_path: the path of root of the file system to display
711 * @max_depth: the maximum depth from the children of @root_path
712 * or the roots of the file system to display in
713 * the file selector). A depth of 0 displays
714 * only the immediate children of @root_path,
715 * or the roots of the filesystem. -1 for no
717 * @types: a bitmask indicating the types of information
718 * that is desired about the files. This will
719 * determine what information is returned by
720 * _gtk_file_system_model_get_info().
721 * @error: location to store error, or %NULL.
723 * Creates a new #GtkFileSystemModel object. The #GtkFileSystemModel
724 * object wraps a #GtkFileSystem interface as a #GtkTreeModel.
725 * Using the @root_path and @max_depth parameters, the tree model
726 * can be restricted to a subportion of the entire file system.
728 * Return value: the newly created #GtkFileSystemModel object, or NULL if there
732 _gtk_file_system_model_new (GtkFileSystem *file_system,
733 const GtkFilePath *root_path,
735 GtkFileInfoType types,
738 GtkFileSystemModel *model;
739 GtkFileFolder *root_folder;
743 g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
744 g_return_val_if_fail (root_path != NULL, NULL);
745 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
747 /* First, try to load the folder */
749 types |= GTK_FILE_INFO_IS_FOLDER | GTK_FILE_INFO_IS_HIDDEN;
751 root_folder = gtk_file_system_get_folder (file_system, root_path, types, error);
756 if (!gtk_file_folder_list_children (root_folder, &roots, error))
758 g_object_unref (root_folder);
762 /* Then, actually create the model and the root nodes */
764 model = g_object_new (GTK_TYPE_FILE_SYSTEM_MODEL, NULL);
765 model->file_system = g_object_ref (file_system);
767 model->max_depth = G_MAXUSHORT;
769 model->max_depth = MIN (max_depth, G_MAXUSHORT);
771 model->types = types;
772 model->root_folder = root_folder;
773 model->root_path = gtk_file_path_copy (root_path);
775 if (gtk_file_folder_is_finished_loading (model->root_folder))
776 queue_finished_loading (model); /* done in an idle because we are being created */
778 g_signal_connect_object (model->root_folder, "finished-loading",
779 G_CALLBACK (root_folder_finished_loading_cb), model, 0);
781 g_signal_connect_object (model->root_folder, "deleted",
782 G_CALLBACK (root_deleted_callback), model, 0);
783 g_signal_connect_object (model->root_folder, "files-added",
784 G_CALLBACK (root_files_added_callback), model, 0);
785 g_signal_connect_object (model->root_folder, "files-changed",
786 G_CALLBACK (root_files_changed_callback), model, 0);
787 g_signal_connect_object (model->root_folder, "files-removed",
788 G_CALLBACK (root_files_removed_callback), model, 0);
790 roots = gtk_file_paths_sort (roots);
792 for (tmp_list = roots; tmp_list; tmp_list = tmp_list->next)
794 FileModelNode *node = file_model_node_new (model, tmp_list->data);
795 gtk_file_path_free (tmp_list->data);
796 node->is_visible = file_model_node_is_visible (model, node);
797 node->next = model->roots;
801 g_slist_free (roots);
803 model->roots = (FileModelNode *) g_slist_reverse ((GSList *)model->roots);
809 model_refilter_recurse (GtkFileSystemModel *model,
810 FileModelNode *parent,
813 GtkTreeModel *tree_model = GTK_TREE_MODEL (model);
815 FileModelNode *nodes;
816 gboolean has_children = FALSE;
818 if (parent && !parent->loaded)
822 nodes = parent->children;
824 nodes = model->roots;
828 FileModelNode *next = nodes->next;
831 gtk_tree_path_append_index (path, i);
833 is_visible = file_model_node_is_visible (model, nodes);
835 if (!is_visible && nodes->is_visible)
837 file_model_node_clear (model, nodes);
838 gtk_tree_model_row_deleted (tree_model, path);
840 nodes->is_visible = FALSE;
842 else if (is_visible && !nodes->is_visible)
846 iter.user_data = nodes;
847 nodes->is_visible = TRUE;
848 gtk_tree_model_row_inserted (tree_model, path, &iter);
851 model_refilter_recurse (model, nodes, path);
859 gtk_tree_path_up (path);
864 if (parent && !has_children)
866 /* Fixme - need to insert dummy node here */
871 model_refilter_all (GtkFileSystemModel *model)
875 path = gtk_tree_path_new ();
876 model_refilter_recurse (model, NULL, path);
877 gtk_tree_path_free (path);
881 * _gtk_file_system_model_set_show_hidden:
882 * @model: a #GtkFileSystemModel
883 * @show_hidden: whether hidden files should be displayed
885 * Sets whether hidden files should be included in the #GtkTreeModel
889 _gtk_file_system_model_set_show_hidden (GtkFileSystemModel *model,
890 gboolean show_hidden)
892 show_hidden = show_hidden != FALSE;
894 if (show_hidden != model->show_hidden)
896 model->show_hidden = show_hidden;
897 model_refilter_all (model);
902 * _gtk_file_system_model_set_show_folders:
903 * @model: a #GtkFileSystemModel
904 * @show_folders: whether folders should be displayed
906 * Sets whether folders should be included in the #GtkTreeModel for
910 _gtk_file_system_model_set_show_folders (GtkFileSystemModel *model,
911 gboolean show_folders)
913 show_folders = show_folders != FALSE;
915 if (show_folders != model->show_folders)
917 model->show_folders = show_folders;
918 model_refilter_all (model);
923 * _gtk_file_system_model_set_show_files:
924 * @model: a #GtkFileSystemModel
925 * @show_files: whether files (as opposed to folders) should
928 * Sets whether files (as opposed to folders) should be included
929 * in the #GtkTreeModel for display.
932 _gtk_file_system_model_set_show_files (GtkFileSystemModel *model,
935 show_files = show_files != FALSE;
937 if (show_files != model->show_files)
939 model->show_files = show_files;
940 model_refilter_all (model);
945 * _gtk_file_system_model_get_info:
946 * @model: a #GtkFileSystemModel
947 * @iter: a #GtkTreeIter pointing to a row of @model
949 * Gets the #GtkFileInfo structure for a particular row
950 * of @model. The information included in this structure
951 * is determined by the @types parameter to
952 * _gtk_file_system_model_new().
954 * Return value: a #GtkFileInfo structure. This structure
955 * is owned by @model and must not be modified or freed.
956 * If you want to save the information for later use,
957 * you must make a copy, since the structure may be
958 * freed on later changes to the file system. If you have
959 * called _gtk_file_system_model_add_editable() and the @iter
960 * corresponds to the row that this function returned, the
961 * return value will be NULL.
964 _gtk_file_system_model_get_info (GtkFileSystemModel *model,
969 node = iter->user_data;
970 if (model->has_editable && node == model->roots)
973 return file_model_node_get_info (model, node);
977 * _gtk_file_system_model_get_path:
978 * @model: a #GtkFileSystemModel
979 * @iter: a #GtkTreeIter pointing to a row of @model
981 * Gets the path for a particular row in @model.
983 * Return value: the path. This string is owned by @model and
984 * or freed. If you want to save the path for later use,
985 * you must make a copy, since the string may be freed
986 * on later changes to the file system.
989 _gtk_file_system_model_get_path (GtkFileSystemModel *model,
992 FileModelNode *node = iter->user_data;
994 if (model->has_editable && node == model->roots)
998 return node->parent->path;
1004 unref_node_and_parents (GtkFileSystemModel *model,
1005 FileModelNode *node)
1007 file_model_node_unref (model, node);
1009 file_model_node_unref (model, node->parent);
1012 static FileModelNode *
1013 find_child_node (GtkFileSystemModel *model,
1014 FileModelNode *parent_node,
1015 const GtkFilePath *path)
1017 FileModelNode *children;
1020 children = file_model_node_get_children (model, parent_node);
1022 children = model->roots;
1026 if (children->is_visible &&
1028 gtk_file_path_compare (children->path, path) == 0)
1031 children = children->next;
1038 static FileModelNode *
1039 find_and_ref_path (GtkFileSystemModel *model,
1040 const GtkFilePath *path,
1043 GtkFilePath *parent_path;
1044 FileModelNode *parent_node;
1045 FileModelNode *child_node;
1046 GtkFileFolder *folder;
1048 if (gtk_file_path_compare (path, model->root_path) == 0
1049 || !gtk_file_system_get_parent (model->file_system, path, &parent_path, NULL))
1054 parent_node = find_and_ref_path (model, parent_path, cleanups);
1055 gtk_file_path_free (parent_path);
1060 child_node = find_child_node (model, parent_node, path);
1063 file_model_node_ref (child_node);
1067 folder = gtk_file_system_get_folder (model->file_system,
1070 NULL); /* NULL-GError */
1073 *cleanups = g_slist_prepend (*cleanups, folder);
1075 child_node = find_child_node (model, parent_node, path);
1078 file_model_node_ref (child_node);
1084 unref_node_and_parents (model, parent_node);
1090 * _gtk_file_system_model_set_filter:
1091 * @mode: a #GtkFileSystemModel
1092 * @filter: function to be called for each file
1093 * @user_data: data to pass to @filter
1095 * Sets a callback called for each file/directory to see whether
1096 * it should be included in model. If this function was made
1097 * public, we'd want to include a GDestroyNotify as well.
1100 _gtk_file_system_model_set_filter (GtkFileSystemModel *model,
1101 GtkFileSystemModelFilter filter,
1104 g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model));
1106 model->filter_func = filter;
1107 model->filter_data = user_data;
1109 model_refilter_all (model);
1113 * _gtk_file_system_model_path_do:
1114 * @model: a #GtkFileSystemModel
1115 * @path: a path pointing to a file in the filesystem
1117 * @func: Function to call with the path and iter corresponding
1119 * @user_data: data to pass to @func
1121 * Locates @path within @model, referencing
1122 * (gtk_tree_model_ref_node ()) all parent nodes,
1123 * calls @func passing in the path and iter for @path,
1124 * then unrefs all the parent nodes.
1126 * The reason for doing this operation as a callback
1127 * is so that if the operation performed with the the
1128 * path and iter results in referencing the the node
1129 * and/or parent nodes, we don't load all the information
1132 * This function is particularly useful for expanding
1133 * a #GtkTreeView to a particular point in the file system.
1135 * Return value: %TRUE if the path was successfully
1136 * found in @model and @func was called.
1139 _gtk_file_system_model_path_do (GtkFileSystemModel *model,
1140 const GtkFilePath *path,
1141 GtkFileSystemModelPathFunc func,
1144 GSList *cleanups = NULL;
1145 FileModelNode *node = find_and_ref_path (model, path, &cleanups);
1152 iter.user_data = node;
1153 path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
1155 (*func) (model, path, &iter, user_data);
1157 gtk_tree_path_free (path);
1158 unref_node_and_parents (model, node);
1161 g_slist_foreach (cleanups, (GFunc)g_object_unref, NULL);
1162 g_slist_free (cleanups);
1164 return node != NULL;
1168 * _gtk_file_system_model_add_editable:
1169 * @model: a #GtkFileSystemModel
1170 * @iter: Location to return the iter corresponding to the editable row
1172 * Adds an "empty" row at the beginning of the model. This does not refer to
1173 * any file, but is a temporary placeholder for a file name that the user will
1174 * type when a corresponding cell is made editable. When your code is done
1175 * using this temporary row, call _gtk_file_system_model_remove_editable().
1178 _gtk_file_system_model_add_editable (GtkFileSystemModel *model, GtkTreeIter *iter)
1180 FileModelNode *node;
1183 g_return_if_fail (!model->has_editable);
1185 model->has_editable = TRUE;
1187 node = file_model_node_new (model, NULL);
1188 node->is_visible = TRUE;
1190 node->next = model->roots;
1191 model->roots = node;
1193 path = gtk_tree_path_new ();
1194 gtk_tree_path_append_index (path, 0);
1195 iter->user_data = node;
1197 gtk_tree_model_row_inserted (GTK_TREE_MODEL (model), path, iter);
1199 gtk_tree_path_free (path);
1203 * _gtk_file_system_model_remove_editable:
1204 * @model: a #GtkFileSystemModel
1206 * Removes the "empty" row at the beginning of the model that was
1207 * created with _gtk_file_system_model_add_editable(). You should call
1208 * this function when your code is finished editing this temporary row.
1211 _gtk_file_system_model_remove_editable (GtkFileSystemModel *model)
1214 FileModelNode *node;
1216 g_return_if_fail (model->has_editable);
1218 model->has_editable = FALSE;
1220 node = model->roots;
1221 model->roots = model->roots->next;
1222 file_model_node_free (node);
1224 path = gtk_tree_path_new ();
1225 gtk_tree_path_append_index (path, 0);
1227 gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path);
1229 gtk_tree_path_free (path);
1232 static FileModelNode *
1233 file_model_node_new (GtkFileSystemModel *model,
1234 const GtkFilePath *path)
1236 FileModelNode *node = g_new0 (FileModelNode, 1);
1238 node->model = model;
1239 node->path = path ? gtk_file_path_copy (path) : NULL;
1245 file_model_node_free (FileModelNode *node)
1247 file_model_node_clear (node->model, node);
1250 gtk_file_path_free (node->path);
1253 gtk_file_info_free (node->info);
1258 static const GtkFileInfo *
1259 file_model_node_get_info (GtkFileSystemModel *model,
1260 FileModelNode *node)
1266 node->info = gtk_file_info_new ();
1267 gtk_file_info_set_display_name (node->info, _("(Empty)"));
1269 else if (node->parent || model->root_folder)
1271 node->info = gtk_file_folder_get_info (node->parent ? node->parent->folder : model->root_folder,
1273 NULL); /* NULL-GError */
1276 g_assert_not_reached ();
1283 file_model_node_is_visible (GtkFileSystemModel *model,
1284 FileModelNode *node)
1286 if (model->show_folders != model->show_files ||
1287 !model->show_hidden ||
1290 const GtkFileInfo *info = file_model_node_get_info (model, node);
1294 /* File probably disappeared underneath us or resides in a
1295 directory where we have only partial access rights. */
1299 if (model->show_folders != model->show_files &&
1300 model->show_folders != gtk_file_info_get_is_folder (info))
1303 if (!model->show_hidden && gtk_file_info_get_is_hidden (info))
1306 if (model->filter_func &&
1307 !model->filter_func (model, node->path, info, model->filter_data))
1315 file_model_node_clear (GtkFileSystemModel *model,
1316 FileModelNode *node)
1318 FileModelNode *children;
1320 file_model_node_idle_clear_cancel (node);
1322 children = node->children;
1323 node->children = NULL;
1324 node->loaded = FALSE;
1328 FileModelNode *next = children->next;
1330 file_model_node_free (children);
1337 /* Unreffing node->folder may cause roots_changed,
1338 * so we need to be careful about ordering.
1340 GtkFileFolder *folder = node->folder;
1341 node->folder = NULL;
1343 g_signal_handlers_disconnect_by_func (folder, G_CALLBACK (deleted_callback), node);
1344 g_signal_handlers_disconnect_by_func (folder, G_CALLBACK (files_added_callback), node);
1345 g_signal_handlers_disconnect_by_func (folder, G_CALLBACK (files_changed_callback), node);
1346 g_signal_handlers_disconnect_by_func (folder, G_CALLBACK (files_removed_callback), node);
1348 g_object_unref (folder);
1353 file_model_node_ref (FileModelNode *node)
1356 if (node->ref_count == 1 && node->parent)
1357 node->parent->n_referenced_children++;
1361 idle_clear_callback (GtkFileSystemModel *model)
1363 while (model->idle_clears)
1365 FileModelNode *node = model->idle_clears->data;
1366 model->idle_clears = g_slist_delete_link (model->idle_clears, model->idle_clears);
1368 node->idle_clear = FALSE;
1369 file_model_node_clear (node->model, node);
1376 file_model_node_idle_clear (FileModelNode *node)
1378 if (!node->idle_clear)
1380 GtkFileSystemModel *model = node->model;
1382 node->idle_clear = TRUE;
1383 if (!model->idle_clears)
1385 model->idle_clear_source = g_idle_source_new ();
1386 g_source_set_priority (model->idle_clear_source, G_PRIORITY_HIGH);
1387 g_source_set_closure (model->idle_clear_source,
1388 g_cclosure_new_object (G_CALLBACK (idle_clear_callback),
1390 g_source_attach (model->idle_clear_source, NULL);
1393 model->idle_clears = g_slist_prepend (model->idle_clears, node);
1394 node->idle_clear = TRUE;
1399 file_model_node_idle_clear_cancel (FileModelNode *node)
1401 if (node->idle_clear)
1403 GtkFileSystemModel *model = node->model;
1405 model->idle_clears = g_slist_remove (model->idle_clears, node);
1406 if (!model->idle_clears)
1408 g_source_destroy (model->idle_clear_source);
1409 model->idle_clear_source = NULL;
1412 node->idle_clear = FALSE;
1417 file_model_node_unref (GtkFileSystemModel *model,
1418 FileModelNode *node)
1421 if (node->ref_count == 0)
1423 file_model_node_clear (model, node);
1425 file_model_node_child_unref (node->parent);
1430 file_model_node_child_unref (FileModelNode *parent)
1432 parent->n_referenced_children--;
1433 if (parent->n_referenced_children == 0)
1434 file_model_node_idle_clear (parent);
1437 static FileModelNode *
1438 file_model_node_get_children (GtkFileSystemModel *model,
1439 FileModelNode *node)
1441 if (node->ref_count == 0)
1446 const GtkFileInfo *info = file_model_node_get_info (model, node);
1447 gboolean has_children = FALSE;
1448 gboolean is_folder = node->depth < model->max_depth && gtk_file_info_get_is_folder (info);
1450 file_model_node_idle_clear_cancel (node);
1453 node->folder = gtk_file_system_get_folder (model->file_system,
1456 NULL); /* NULL-GError */
1460 GSList *child_paths, *tmp_list;
1462 if (gtk_file_folder_list_children (node->folder, &child_paths, NULL)) /* NULL-GError */
1464 child_paths = gtk_file_paths_sort (child_paths);
1466 for (tmp_list = child_paths; tmp_list; tmp_list = tmp_list->next)
1468 FileModelNode *child_node = file_model_node_new (model, tmp_list->data);
1469 gtk_file_path_free (tmp_list->data);
1470 child_node->next = node->children;
1471 child_node->parent = node;
1472 child_node->depth = node->depth + 1;
1473 child_node->is_visible = file_model_node_is_visible (model, child_node);
1474 if (child_node->is_visible)
1475 has_children = TRUE;
1476 node->children = child_node;
1478 g_slist_free (child_paths);
1481 node->children = (FileModelNode *)g_slist_reverse ((GSList *)node->children);
1483 g_signal_connect (node->folder, "deleted",
1484 G_CALLBACK (deleted_callback), node);
1485 g_signal_connect (node->folder, "files-added",
1486 G_CALLBACK (files_added_callback), node);
1487 g_signal_connect (node->folder, "files-changed",
1488 G_CALLBACK (files_changed_callback), node);
1489 g_signal_connect (node->folder, "files-removed",
1490 G_CALLBACK (files_removed_callback), node);
1492 g_object_set_data (G_OBJECT (node->folder), "model-node", node);
1495 if (is_folder && !has_children)
1497 /* The hard case ... we claimed this folder had children, but actually
1498 * it didn't. We have to add a dummy child, possibly to remove later.
1500 FileModelNode *child_node = file_model_node_new (model, NULL);
1501 child_node->is_visible = TRUE;
1502 child_node->parent = node;
1503 child_node->is_dummy = TRUE;
1505 node->children = child_node;
1506 node->has_dummy = TRUE;
1509 node->loaded = TRUE;
1512 return node->children;
1516 do_files_added (GtkFileSystemModel *model,
1517 FileModelNode *parent_node,
1520 GtkTreeModel *tree_model = GTK_TREE_MODEL (model);
1521 FileModelNode *children;
1522 FileModelNode *prev = NULL;
1525 GSList *sorted_paths;
1528 sorted_paths = gtk_file_paths_sort (g_slist_copy (paths));
1532 iter.user_data = parent_node;
1533 path = gtk_tree_model_get_path (tree_model, &iter);
1534 children = parent_node->children;
1538 path = gtk_tree_path_new ();
1539 children = model->roots;
1542 gtk_tree_path_down (path);
1544 if (parent_node && parent_node->has_dummy)
1547 children = children->next;
1548 gtk_tree_path_next (path);
1551 for (tmp_list = sorted_paths; tmp_list; tmp_list = tmp_list->next)
1553 const GtkFilePath *file_path = tmp_list->data;
1556 (!children->path || gtk_file_path_compare (children->path, file_path) < 0))
1559 if (children->is_visible)
1560 gtk_tree_path_next (path);
1562 children = children->next;
1566 children->path && gtk_file_path_compare (children->path, file_path) == 0)
1568 /* Shouldn't happen */
1574 new = file_model_node_new (model, file_path);
1577 new->next = children;
1580 else if (parent_node)
1581 parent_node->children = new;
1589 new->parent = parent_node;
1590 new->depth = parent_node->depth + 1;
1593 new->is_visible = file_model_node_is_visible (model, new);
1595 if (new->is_visible)
1597 iter.user_data = new;
1598 gtk_tree_path_free (path);
1599 path = gtk_tree_model_get_path (tree_model, &iter);
1600 gtk_tree_model_row_inserted (tree_model, path, &iter);
1602 if (gtk_file_system_model_iter_has_child (tree_model, &iter))
1603 gtk_tree_model_row_has_child_toggled (tree_model, path, &iter);
1605 if (parent_node && parent_node->has_dummy)
1607 FileModelNode *dummy = parent_node->children;
1608 GtkTreePath *dummy_path;
1610 parent_node->children = parent_node->children->next;
1611 parent_node->has_dummy = FALSE;
1613 dummy_path = gtk_tree_path_copy (path);
1614 gtk_tree_path_up (dummy_path);
1615 gtk_tree_path_down (dummy_path);
1617 gtk_tree_model_row_deleted (tree_model, dummy_path);
1618 gtk_tree_path_free (dummy_path);
1620 if (dummy->ref_count)
1621 file_model_node_child_unref (parent_node);
1622 file_model_node_free (dummy);
1625 gtk_tree_path_next (path);
1630 gtk_tree_path_free (path);
1631 g_slist_free (sorted_paths);
1635 do_files_changed (GtkFileSystemModel *model,
1636 FileModelNode *parent_node,
1639 GtkTreeModel *tree_model = GTK_TREE_MODEL (model);
1640 FileModelNode *children;
1641 FileModelNode *prev = NULL;
1644 GSList *sorted_paths;
1647 sorted_paths = gtk_file_paths_sort (g_slist_copy (paths));
1651 iter.user_data = parent_node;
1652 path = gtk_tree_model_get_path (tree_model, &iter);
1653 children = parent_node->children;
1657 path = gtk_tree_path_new ();
1658 children = model->roots;
1661 gtk_tree_path_down (path);
1663 if (parent_node && parent_node->has_dummy)
1666 children = children->next;
1667 gtk_tree_path_next (path);
1670 for (tmp_list = sorted_paths; tmp_list; tmp_list = tmp_list->next)
1672 const GtkFilePath *file_path = tmp_list->data;
1675 (!children->path || gtk_file_path_compare (children->path, file_path) < 0))
1678 if (children->is_visible)
1679 gtk_tree_path_next (path);
1681 children = children->next;
1685 children->path && gtk_file_path_compare (children->path, file_path) == 0)
1687 gtk_tree_model_row_changed (tree_model, path, &iter);
1691 /* Shouldn't happen */
1695 gtk_tree_path_free (path);
1696 g_slist_free (sorted_paths);
1700 do_files_removed (GtkFileSystemModel *model,
1701 FileModelNode *parent_node,
1704 GtkTreeModel *tree_model = GTK_TREE_MODEL (model);
1705 FileModelNode *children;
1706 FileModelNode *prev = NULL;
1709 GSList *sorted_paths;
1711 FileModelNode *tmp_child;
1714 sorted_paths = gtk_file_paths_sort (g_slist_copy (paths));
1718 iter.user_data = parent_node;
1719 path = gtk_tree_model_get_path (tree_model, &iter);
1720 children = parent_node->children;
1724 path = gtk_tree_path_new ();
1725 children = model->roots;
1728 /* Count the number of currently visible children, so that
1729 * can catch when we need to insert a dummy node.
1732 for (tmp_child = children; tmp_child; tmp_child = tmp_child->next)
1734 if (tmp_child->is_visible)
1738 gtk_tree_path_down (path);
1740 if (parent_node && parent_node->has_dummy)
1743 children = children->next;
1744 gtk_tree_path_next (path);
1747 for (tmp_list = sorted_paths; tmp_list; tmp_list = tmp_list->next)
1749 const GtkFilePath *file_path = tmp_list->data;
1752 (!children->path || gtk_file_path_compare (children->path, file_path) < 0))
1755 if (children->is_visible)
1756 gtk_tree_path_next (path);
1758 children = children->next;
1762 children->path && gtk_file_path_compare (children->path, file_path) == 0)
1764 FileModelNode *next = children->next;
1766 if (children->is_visible)
1769 if (parent_node && n_visible == 0)
1771 FileModelNode *dummy = file_model_node_new (model, NULL);
1772 dummy->is_visible = TRUE;
1773 dummy->parent = parent_node;
1774 dummy->is_dummy = TRUE;
1776 parent_node->children = dummy;
1777 parent_node->has_dummy = TRUE;
1779 iter.user_data = dummy;
1780 gtk_tree_model_row_inserted (tree_model, path, &iter);
1781 gtk_tree_path_next (path);
1788 else if (parent_node)
1789 parent_node->children = next;
1791 model->roots = next;
1793 if (parent_node && children->ref_count)
1794 file_model_node_child_unref (parent_node);
1796 if (children->is_visible)
1797 gtk_tree_model_row_deleted (tree_model, path);
1799 file_model_node_free (children);
1805 /* Shouldn't happen */
1809 gtk_tree_path_free (path);
1810 g_slist_free (sorted_paths);
1814 deleted_callback (GtkFileFolder *folder,
1815 FileModelNode *node)
1820 files_added_callback (GtkFileFolder *folder,
1822 FileModelNode *node)
1824 do_files_added (node->model, node, paths);
1828 files_changed_callback (GtkFileFolder *folder,
1830 FileModelNode *node)
1832 do_files_changed (node->model, node, paths);
1836 files_removed_callback (GtkFileFolder *folder,
1838 FileModelNode *node)
1840 do_files_removed (node->model, node, paths);
1844 root_deleted_callback (GtkFileFolder *folder,
1845 GtkFileSystemModel *model)
1850 root_files_added_callback (GtkFileFolder *folder,
1852 GtkFileSystemModel *model)
1854 do_files_added (model, NULL, paths);
1858 root_files_changed_callback (GtkFileFolder *folder,
1860 GtkFileSystemModel *model)
1862 do_files_changed (model, NULL, paths);
1866 root_files_removed_callback (GtkFileFolder *folder,
1868 GtkFileSystemModel *model)
1870 do_files_removed (model, NULL, paths);