+ files = g_file_enumerator_next_files_finish (enumerator, res, &error);
+
+ if (files)
+ {
+ if (model->dir_thaw_source == 0)
+ {
+ freeze_updates (model);
+ model->dir_thaw_source = gdk_threads_add_timeout_full (IO_PRIORITY + 1,
+ 50,
+ thaw_func,
+ model,
+ NULL);
+ }
+
+ for (walk = files; walk; walk = walk->next)
+ {
+ const char *name;
+ GFileInfo *info;
+ GFile *file;
+
+ info = walk->data;
+ name = g_file_info_get_name (info);
+ if (name == NULL)
+ {
+ /* Shouldn't happen, but the APIs allow it */
+ g_object_unref (info);
+ continue;
+ }
+ file = g_file_get_child (model->dir, name);
+ add_file (model, file, info);
+ g_object_unref (file);
+ g_object_unref (info);
+ }
+ g_list_free (files);
+
+ g_file_enumerator_next_files_async (enumerator,
+ g_file_is_native (model->dir) ? 50 * FILES_PER_QUERY : FILES_PER_QUERY,
+ IO_PRIORITY,
+ model->cancellable,
+ gtk_file_system_model_got_files,
+ model);
+ }
+ else
+ {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ {
+ g_file_enumerator_close_async (enumerator,
+ IO_PRIORITY,
+ model->cancellable,
+ gtk_file_system_model_closed_enumerator,
+ NULL);
+ if (model->dir_thaw_source != 0)
+ {
+ g_source_remove (model->dir_thaw_source);
+ model->dir_thaw_source = 0;
+ thaw_updates (model);
+ }
+
+ g_signal_emit (model, file_system_model_signals[FINISHED_LOADING], 0, error);
+ }
+
+ if (error)
+ g_error_free (error);
+ }
+
+ gdk_threads_leave ();
+}
+
+static void
+gtk_file_system_model_query_done (GObject * object,
+ GAsyncResult *res,
+ gpointer data)
+{
+ GtkFileSystemModel *model = data; /* only a valid pointer if not cancelled */
+ GFile *file = G_FILE (object);
+ GFileInfo *info;
+ guint id;
+
+ info = g_file_query_info_finish (file, res, NULL);
+ if (info == NULL)
+ return;
+
+ gdk_threads_enter ();
+
+ _gtk_file_system_model_update_file (model, file, info);
+
+ id = node_get_for_file (model, file);
+ gtk_file_system_model_sort_node (model, id);
+
+ gdk_threads_leave ();
+}
+
+static void
+gtk_file_system_model_monitor_change (GFileMonitor * monitor,
+ GFile * file,
+ GFile * other_file,
+ GFileMonitorEvent type,
+ GtkFileSystemModel *model)
+{
+ switch (type)
+ {
+ case G_FILE_MONITOR_EVENT_CREATED:
+ case G_FILE_MONITOR_EVENT_CHANGED:
+ case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED:
+ /* We can treat all of these the same way */
+ g_file_query_info_async (file,
+ model->attributes,
+ G_FILE_QUERY_INFO_NONE,
+ IO_PRIORITY,
+ model->cancellable,
+ gtk_file_system_model_query_done,
+ model);
+ break;
+ case G_FILE_MONITOR_EVENT_DELETED:
+ gdk_threads_enter ();
+ remove_file (model, file);
+ gdk_threads_leave ();
+ break;
+ case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
+ /* FIXME: use freeze/thaw with this somehow? */
+ case G_FILE_MONITOR_EVENT_PRE_UNMOUNT:
+ case G_FILE_MONITOR_EVENT_UNMOUNTED:
+ default:
+ /* ignore these */
+ break;
+ }
+}
+
+static void
+gtk_file_system_model_got_enumerator (GObject *dir, GAsyncResult *res, gpointer data)
+{
+ GtkFileSystemModel *model = data;
+ GFileEnumerator *enumerator;
+ GError *error = NULL;
+
+ gdk_threads_enter ();
+
+ enumerator = g_file_enumerate_children_finish (G_FILE (dir), res, &error);
+ if (enumerator == NULL)
+ {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ {
+ g_signal_emit (model, file_system_model_signals[FINISHED_LOADING], 0, error);
+ g_error_free (error);
+ }
+ }
+ else
+ {
+ g_file_enumerator_next_files_async (enumerator,
+ g_file_is_native (model->dir) ? 50 * FILES_PER_QUERY : FILES_PER_QUERY,
+ IO_PRIORITY,
+ model->cancellable,
+ gtk_file_system_model_got_files,
+ model);
+ g_object_unref (enumerator);
+ model->dir_monitor = g_file_monitor_directory (model->dir,
+ G_FILE_MONITOR_NONE,
+ model->cancellable,
+ NULL); /* we don't mind if directory monitoring isn't supported, so the GError is NULL here */
+ if (model->dir_monitor)
+ g_signal_connect (model->dir_monitor,
+ "changed",
+ G_CALLBACK (gtk_file_system_model_monitor_change),
+ model);
+ }
+
+ gdk_threads_leave ();
+}
+
+static void
+gtk_file_system_model_set_n_columns (GtkFileSystemModel *model,
+ gint n_columns,
+ va_list args)
+{
+ guint i;
+
+ g_assert (model->files == NULL);
+ g_assert (n_columns > 0);
+
+ model->n_columns = n_columns;
+ model->column_types = g_slice_alloc (sizeof (GType) * n_columns);
+
+ model->node_size = sizeof (FileModelNode) + sizeof (GValue) * (n_columns - 1); /* minus 1 because FileModelNode.values[] has a default size of 1 */
+
+ for (i = 0; i < (guint) n_columns; i++)
+ {
+ GType type = va_arg (args, GType);
+ if (! _gtk_tree_data_list_check_type (type))
+ {
+ g_error ("%s: type %s cannot be a column type for GtkFileSystemModel\n", G_STRLOC, g_type_name (type));
+ return; /* not reached */
+ }
+
+ model->column_types[i] = type;
+ }
+
+ model->sort_list = _gtk_tree_data_list_header_new (n_columns, model->column_types);
+
+ model->files = g_array_sized_new (FALSE, FALSE, model->node_size, FILES_PER_QUERY);
+ /* add editable node at start */
+ g_array_set_size (model->files, 1);
+ memset (get_node (model, 0), 0, model->node_size);
+}
+
+static void
+gtk_file_system_model_set_directory (GtkFileSystemModel *model,
+ GFile * dir,
+ const gchar * attributes)
+{
+ g_assert (G_IS_FILE (dir));
+
+ model->dir = g_object_ref (dir);
+ model->attributes = g_strdup (attributes);
+
+ g_file_enumerate_children_async (model->dir,
+ attributes,
+ G_FILE_QUERY_INFO_NONE,
+ IO_PRIORITY,
+ model->cancellable,
+ gtk_file_system_model_got_enumerator,
+ model);
+
+}
+
+static GtkFileSystemModel *
+_gtk_file_system_model_new_valist (GtkFileSystemModelGetValue get_func,
+ gpointer get_data,
+ guint n_columns,
+ va_list args)
+{
+ GtkFileSystemModel *model;
+
+ model = g_object_new (GTK_TYPE_FILE_SYSTEM_MODEL, NULL);
+ model->get_func = get_func;
+ model->get_data = get_data;
+
+ gtk_file_system_model_set_n_columns (model, n_columns, args);
+
+ return model;
+}
+
+/**
+ * _gtk_file_system_model_new:
+ * @get_func: function to call for getting a value
+ * @get_data: user data argument passed to @get_func
+ * @n_columns: number of columns
+ * @...: @n_columns #GType types for the columns
+ *
+ * Creates a new #GtkFileSystemModel object. You need to add files
+ * to the list using _gtk_file_system_model_add_and_query_file()
+ * or _gtk_file_system_model_update_file().
+ *
+ * Return value: the newly created #GtkFileSystemModel
+ **/
+GtkFileSystemModel *
+_gtk_file_system_model_new (GtkFileSystemModelGetValue get_func,
+ gpointer get_data,
+ guint n_columns,
+ ...)
+{
+ GtkFileSystemModel *model;
+ va_list args;
+
+ g_return_val_if_fail (get_func != NULL, NULL);
+ g_return_val_if_fail (n_columns > 0, NULL);
+
+ va_start (args, n_columns);
+ model = _gtk_file_system_model_new_valist (get_func, get_data, n_columns, args);
+ va_end (args);
+
+ return model;
+}
+
+/**
+ * _gtk_file_system_model_new_for_directory:
+ * @directory: the directory to show.
+ * @attributes: (allow-none): attributes to immediately load or %NULL for all
+ * @get_func: function that the model should call to query data about a file
+ * @get_data: user data to pass to the @get_func
+ * @n_columns: number of columns
+ * @...: @n_columns #GType types for the columns
+ *
+ * Creates a new #GtkFileSystemModel object. The #GtkFileSystemModel
+ * object wraps the given @directory as a #GtkTreeModel.
+ * The model will query the given directory with the given @attributes
+ * and add all files inside the directory automatically. If supported,
+ * it will also monitor the drectory and update the model's
+ * contents to reflect changes, if the @directory supports monitoring.
+ *
+ * Return value: the newly created #GtkFileSystemModel
+ **/
+GtkFileSystemModel *
+_gtk_file_system_model_new_for_directory (GFile * dir,
+ const gchar * attributes,
+ GtkFileSystemModelGetValue get_func,
+ gpointer get_data,
+ guint n_columns,
+ ...)
+{
+ GtkFileSystemModel *model;
+ va_list args;
+
+ g_return_val_if_fail (G_IS_FILE (dir), NULL);
+ g_return_val_if_fail (get_func != NULL, NULL);
+ g_return_val_if_fail (n_columns > 0, NULL);
+
+ va_start (args, n_columns);
+ model = _gtk_file_system_model_new_valist (get_func, get_data, n_columns, args);
+ va_end (args);
+
+ gtk_file_system_model_set_directory (model, dir, attributes);
+
+ return model;
+}
+
+static void
+gtk_file_system_model_refilter_all (GtkFileSystemModel *model)
+{
+ guint i;
+
+ if (model->frozen)
+ {
+ model->filter_on_thaw = TRUE;
+ return;
+ }
+
+ freeze_updates (model);
+
+ /* start at index 1, don't change the editable */
+ for (i = 1; i < model->files->len; i++)
+ node_compute_visibility_and_filters (model, i);
+
+ model->filter_on_thaw = FALSE;
+ thaw_updates (model);
+}
+
+/**
+ * _gtk_file_system_model_set_show_hidden:
+ * @model: a #GtkFileSystemModel
+ * @show_hidden: whether hidden files should be displayed
+ *
+ * Sets whether hidden files should be included in the #GtkTreeModel