#include "gtktreedatalist.h"
#include "gtktreednd.h"
#include "gtktreemodel.h"
-#include "gtkalias.h"
+
+/*** Structure: how GtkFileSystemModel works
+ *
+ * This is a custom GtkTreeModel used to hold a collection of files for GtkFileChooser. There are two use cases:
+ *
+ * 1. The model populates itself from a folder, using the GIO file enumerator API. This happens if you use
+ * _gtk_file_system_model_new_for_directory(). This is the normal usage for showing the contents of a folder.
+ *
+ * 2. The caller populates the model by hand, with files not necessarily in the same folder. This happens
+ * if you use _gtk_file_system_model_new() and then _gtk_file_system_model_add_and_query_file(). This is
+ * the special kind of usage for "search" and "recent-files", where the file chooser gives the model the
+ * files to be displayed.
+ *
+ * Each file is kept in a FileModelNode structure. Each FileModelNode holds a GFile* and other data. All the
+ * node structures have the same size, determined at runtime, depending on the number of columns that were passed
+ * to _gtk_file_system_model_new() or _gtk_file_system_model_new_for_directory() (that is, the size of a node is
+ * not sizeof (FileModelNode), but rather model->node_size). The last field in the FileModelNode structure,
+ * node->values[], is an array of GValue, used to hold the data for those columns.
+ *
+ * The model stores an array of FileModelNode structures in model->files. This is a GArray where each element is
+ * model->node_size bytes in size (the model computes that node size when initializing itself). There are
+ * convenience macros, get_node() and node_index(), to access that array based on an array index or a pointer to
+ * a node inside the array.
+ *
+ * The model accesses files through two of its fields:
+ *
+ * model->files - GArray of FileModelNode structures.
+ *
+ * model->file_lookup - hash table that maps a GFile* to an index inside the model->files array.
+ *
+ * The model->file_lookup hash table is populated lazily. It is both accessed and populated with the
+ * node_get_for_file() function. The invariant is that the files in model->files[n] for n < g_hash_table_size
+ * (model->file_lookup) are already added to the hash table. The hash table will get cleared when we re-sort the
+ * files, as the array will be in a different order and the indexes need to be rebuilt.
+ *
+ * Each FileModelNode has a node->visible field, which indicates whether the node is visible in the GtkTreeView.
+ * A node may be invisible if, for example, it corresponds to a hidden file and the file chooser is not showing
+ * hidden files.
+ *
+ * Since not all nodes in the model->files array may be visible, we need a way to map visible row indexes from
+ * the treeview to array indexes in our array of files. And thus we introduce a bit of terminology:
+ *
+ * index - An index in the model->files array. All variables/fields that represent indexes are either called
+ * "index" or "i_*", or simply "i" for things like loop counters.
+ *
+ * row - An index in the GtkTreeView, i.e. the index of a row within the outward-facing API of the
+ * GtkFileSystemModel. However, note that our rows are 1-based, not 0-based, for the reason explained in the
+ * following paragraph. Variables/fields that represent visible rows are called "row", or "r_*", or simply
+ * "r".
+ *
+ * Each FileModelNode has a node->row field which is the number of visible rows in the treeview, *before and
+ * including* that node. This means that node->row is 1-based, instead of 0-based --- this makes some code
+ * simpler, believe it or not :) This also means that when the calling GtkTreeView gives us a GtkTreePath, we
+ * turn the 0-based treepath into a 1-based row for our purposes. If a node is not visible, it will have the
+ * same row number as its closest preceding visible node.
+ *
+ * We try to compute the node->row fields lazily. A node is said to be "valid" if its node->row is accurate.
+ * For this, the model keeps a model->n_nodes_valid field which is the count of valid nodes starting from the
+ * beginning of the model->files array. When a node changes its information, or when a node gets deleted, that
+ * node and the following ones get invalidated by simply setting model->n_nodes_valid to the array index of the
+ * node. If the model happens to need a node's row number and that node is in the model->files array after
+ * model->n_nodes_valid, then the nodes get re-validated up to the sought node. See node_validate_rows() for
+ * this logic.
+ *
+ * You never access a node->row directly. Instead, call node_get_tree_row(). That function will validate the nodes
+ * up to the sought one if the node is not valid yet, and it will return a proper 0-based row.
+ */
/*** DEFINES ***/
GFile * file; /* file represented by this node or NULL for editable */
GFileInfo * info; /* info for this file or NULL if unknown */
- guint index; /* if valid, index in path - aka visible nodes before this one */
+ guint row; /* if valid (see model->n_valid_indexes), visible nodes before and including
+ * this one - see the "Structure" comment above.
+ */
guint visible :1; /* if the file is currently visible */
guint frozen_add :1; /* true if the model was frozen and the entry has not been added yet */
GFile * dir; /* directory that's displayed */
guint dir_thaw_source;/* GSource id for unfreezing the model */
- char * attributes; /* attributes the file info must contain */
- GFileMonitor * dir_monitor; /* directory that is monitored */
+ char * attributes; /* attributes the file info must contain, or NULL for all attributes */
+ GFileMonitor * dir_monitor; /* directory that is monitored, or NULL if monitoring was not supported */
GCancellable * cancellable; /* cancellable in use for all operations - cancelled on dispose */
GArray * files; /* array of FileModelNode containing all our files */
- GSize node_size; /* Size of a FileModelNode structure once its ->values field has n_columns */
- guint n_indexes_valid;/* count of valid indexes */
- GHashTable * file_lookup; /* file => array index table */
+ gsize node_size; /* Size of a FileModelNode structure once its ->values field has n_columns */
+ guint n_nodes_valid; /* count of valid nodes (i.e. those whose node->row is accurate) */
+ GHashTable * file_lookup; /* mapping of GFile => array index in model->files
+ * This hash table doesn't always have the same number of entries as the files array;
+ * it can get cleared completely when we resort.
+ * The hash table gets re-populated in node_get_for_file() if this mismatch is
+ * detected.
+ */
guint n_columns; /* number of columns */
GType * column_types; /* types of each column */
guint show_hidden :1; /* whether to show hidden files */
guint show_folders :1;/* whether to show folders */
guint show_files :1; /* whether to show files */
+ guint filter_folders :1;/* whether filter applies to folders */
};
#define GTK_FILE_SYSTEM_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_SYSTEM_MODEL, GtkFileSystemModelClass))
void (*finished_loading) (GtkFileSystemModel *model, GError *error);
};
+static void add_file (GtkFileSystemModel *model,
+ GFile *file,
+ GFileInfo *info);
+static void remove_file (GtkFileSystemModel *model,
+ GFile *file);
+
/* iter setup:
* @user_data: the model
* @user_data2: GUINT_TO_POINTER of array index of current entry
/*** FileModelNode ***/
+/* Get a FileModelNode structure given an index in the model->files array of nodes */
#define get_node(_model, _index) ((FileModelNode *) ((_model)->files->data + (_index) * (_model)->node_size))
+
+/* Get an index within the model->files array of nodes, given a FileModelNode* */
#define node_index(_model, _node) (((gchar *) (_node) - (_model)->files->data) / (_model)->node_size)
+/* @up_to_index: smallest model->files array index that will be valid after this call
+ * @up_to_row: smallest node->row that will be valid after this call
+ *
+ * If you want to validate up to an index or up to a row, specify the index or
+ * the row you want and specify G_MAXUINT for the other argument. Pass
+ * G_MAXUINT for both arguments for "validate everything".
+ */
static void
-node_validate_indexes (GtkFileSystemModel *model, guint min_index, guint min_visible)
+node_validate_rows (GtkFileSystemModel *model, guint up_to_index, guint up_to_row)
{
- guint validate, current;
+ guint i, row;
if (model->files->len == 0)
return;
- min_index = MIN (min_index, model->files->len - 1);
- validate = model->n_indexes_valid;
- if (validate)
- current = get_node (model, validate - 1)->index;
+
+ up_to_index = MIN (up_to_index, model->files->len - 1);
+
+ i = model->n_nodes_valid;
+ if (i != 0)
+ row = get_node (model, i - 1)->row;
else
- current = 0;
- while (validate <= min_index && current <= min_visible)
+ row = 0;
+
+ while (i <= up_to_index && row <= up_to_row)
{
- FileModelNode *node = get_node (model, validate);
+ FileModelNode *node = get_node (model, i);
if (node->visible)
- current++;
- node->index = current;
- validate++;
+ row++;
+ node->row = row;
+ i++;
}
- model->n_indexes_valid = validate;
+ model->n_nodes_valid = i;
}
static guint
-node_get_index (GtkFileSystemModel *model, guint id)
+node_get_tree_row (GtkFileSystemModel *model, guint index)
{
- if (model->n_indexes_valid <= id)
- node_validate_indexes (model, id, G_MAXUINT);
+ if (model->n_nodes_valid <= index)
+ node_validate_rows (model, index, G_MAXUINT);
- return get_node (model, id)->index - 1;
+ return get_node (model, index)->row - 1;
}
static void
node_invalidate_index (GtkFileSystemModel *model, guint id)
{
- model->n_indexes_valid = MIN (model->n_indexes_valid, id);
+ model->n_nodes_valid = MIN (model->n_nodes_valid, id);
}
static GtkTreePath *
gtk_tree_path_new_from_node (GtkFileSystemModel *model, guint id)
{
- guint i = node_get_index (model, id);
+ guint i = node_get_tree_row (model, id);
g_assert (i < model->files->len);
}
static void
-node_set_visible (GtkFileSystemModel *model, guint id, gboolean visible)
+emit_row_inserted_for_node (GtkFileSystemModel *model, guint id)
+{
+ GtkTreePath *path;
+ GtkTreeIter iter;
+
+ path = gtk_tree_path_new_from_node (model, id);
+ ITER_INIT_FROM_INDEX (model, &iter, id);
+ gtk_tree_model_row_inserted (GTK_TREE_MODEL (model), path, &iter);
+ gtk_tree_path_free (path);
+}
+
+static void
+emit_row_changed_for_node (GtkFileSystemModel *model, guint id)
{
- FileModelNode *node = get_node (model, id);
GtkTreePath *path;
GtkTreeIter iter;
+ path = gtk_tree_path_new_from_node (model, id);
+ ITER_INIT_FROM_INDEX (model, &iter, id);
+ gtk_tree_model_row_changed (GTK_TREE_MODEL (model), path, &iter);
+ gtk_tree_path_free (path);
+}
+
+static void
+emit_row_deleted_for_row (GtkFileSystemModel *model, guint row)
+{
+ GtkTreePath *path;
+
+ path = gtk_tree_path_new_from_indices (row, -1);
+ gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path);
+ gtk_tree_path_free (path);
+}
+
+static void
+node_set_visible (GtkFileSystemModel *model, guint id, gboolean visible)
+{
+ FileModelNode *node = get_node (model, id);
+
if (node->visible == visible ||
node->frozen_add)
return;
{
node->visible = TRUE;
node_invalidate_index (model, id);
- path = gtk_tree_path_new_from_node (model, id);
- ITER_INIT_FROM_INDEX (model, &iter, id);
- gtk_tree_model_row_inserted (GTK_TREE_MODEL (model), path, &iter);
- gtk_tree_path_free (path);
+ emit_row_inserted_for_node (model, id);
}
else
{
- path = gtk_tree_path_new_from_node (model, id);
+ guint row;
+
+ row = node_get_tree_row (model, id);
+ g_assert (row < model->files->len);
+
node->visible = FALSE;
node_invalidate_index (model, id);
- gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path);
- gtk_tree_path_free (path);
+ emit_row_deleted_for_row (model, row);
}
}
FileModelNode *node = get_node (model, id);
GtkFileFilterInfo filter_info = { 0, };
GtkFileFilterFlags required;
- gboolean is_folder, result;
+ gboolean result;
char *mime_type = NULL;
char *filename = NULL;
char *uri = NULL;
(g_file_info_get_is_hidden (node->info) || g_file_info_get_is_backup (node->info)))
return FALSE;
- is_folder = _gtk_file_info_consider_as_directory (node->info);
-
- /* wtf? */
- if (model->show_folders != model->show_files &&
- model->show_folders != is_folder)
- return FALSE;
+ if (_gtk_file_info_consider_as_directory (node->info))
+ {
+ if (!model->show_folders)
+ return FALSE;
- if (is_folder)
- return TRUE;
+ if (!model->filter_folders)
+ return TRUE;
+ }
+ else
+ {
+ if (!model->show_files)
+ return FALSE;
+ }
if (model->filter == NULL)
return TRUE;
if (required & GTK_FILE_FILTER_MIME_TYPE)
{
- filter_info.mime_type = g_file_info_get_attribute_string (node->info, "filechooser::mime-type");
- if (filter_info.mime_type != NULL)
- filter_info.contains |= GTK_FILE_FILTER_MIME_TYPE;
- else
- {
- const char *s = g_file_info_get_content_type (node->info);
- if (s)
- {
- mime_type = g_content_type_get_mime_type (s);
- if (mime_type)
- {
- filter_info.mime_type = mime_type;
- filter_info.contains |= GTK_FILE_FILTER_MIME_TYPE;
- }
- }
- }
+ const char *s = g_file_info_get_content_type (node->info);
+ if (s)
+ {
+ mime_type = g_content_type_get_mime_type (s);
+ if (mime_type)
+ {
+ filter_info.mime_type = mime_type;
+ filter_info.contains |= GTK_FILE_FILTER_MIME_TYPE;
+ }
+ }
}
if (required & GTK_FILE_FILTER_FILENAME)
{
filename = g_file_get_path (node->file);
- if (filter_info.filename)
+ if (filename)
{
filter_info.filename = filename;
filter_info.contains |= GTK_FILE_FILTER_FILENAME;
{
const FileModelNode *node = _node;
- return GPOINTER_TO_UINT (key) - node->index;
+ return GPOINTER_TO_UINT (key) - node->row;
}
static gboolean
{
GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
char *node;
- guint id, find;
+ guint id;
+ guint row_to_find;
g_return_val_if_fail (n >= 0, FALSE);
if (parent != NULL)
return FALSE;
- find = n + 1;
+ row_to_find = n + 1; /* plus one as our node->row numbers are 1-based; see the "Structure" comment at the beginning */
- if (model->n_indexes_valid > 0 &&
- get_node (model, model->n_indexes_valid - 1)->index >= find)
+ if (model->n_nodes_valid > 0 &&
+ get_node (model, model->n_nodes_valid - 1)->row >= row_to_find)
{
- /* fast path */
- node = bsearch (GUINT_TO_POINTER (find),
+ /* Fast path - the nodes are valid up to the sought one.
+ *
+ * First, find a node with the sought row number...*/
+
+ node = bsearch (GUINT_TO_POINTER (row_to_find),
model->files->data,
- model->n_indexes_valid,
+ model->n_nodes_valid,
model->node_size,
compare_indices);
if (node == NULL)
return FALSE;
+ /* ... Second, back up until we find the first visible node with that row number */
+
id = node_index (model, node);
while (!get_node (model, id)->visible)
id--;
+
+ g_assert (get_node (model, id)->row == row_to_find);
}
else
{
- node_validate_indexes (model, G_MAXUINT, n);
- id = model->n_indexes_valid - 1;
- if (model->n_indexes_valid == 0 || get_node (model, id)->index != find)
+ /* Slow path - the nodes need to be validated up to the sought one */
+
+ node_validate_rows (model, G_MAXUINT, n); /* note that this is really "n", not row_to_find - see node_validate_rows() */
+ id = model->n_nodes_valid - 1;
+ if (model->n_nodes_valid == 0 || get_node (model, id)->row != row_to_find)
return FALSE;
}
{
g_return_val_if_fail (gtk_tree_path_get_depth (path) > 0, FALSE);
+ if (gtk_tree_path_get_depth (path) > 1)
+ return FALSE;
+
return gtk_file_system_model_iter_nth_child (tree_model,
iter,
NULL,
if (iter)
return 0;
- return node_get_index (model, model->files->len - 1) + 1;
+ return node_get_tree_row (model, model->files->len - 1) + 1;
}
static gboolean
gtk_file_system_model_ref_node (GtkTreeModel *tree_model,
GtkTreeIter *iter)
{
- /* FIXME: implement */
+ /* nothing to do */
}
static void
gtk_file_system_model_unref_node (GtkTreeModel *tree_model,
GtkTreeIter *iter)
{
- /* FIXME: implement */
+ /* nothing to do */
}
static void
if (sort_data_init (&data, model))
{
GtkTreePath *path;
- guint i, j, n_elements;
+ guint i;
+ guint r, n_visible_rows;
- node_validate_indexes (model, G_MAXUINT, G_MAXUINT);
- n_elements = node_get_index (model, model->files->len - 1) + 1;
- model->n_indexes_valid = 0;
+ node_validate_rows (model, G_MAXUINT, G_MAXUINT);
+ n_visible_rows = node_get_tree_row (model, model->files->len - 1) + 1;
+ model->n_nodes_valid = 0;
g_hash_table_remove_all (model->file_lookup);
- g_qsort_with_data (get_node (model, 1),
+ g_qsort_with_data (get_node (model, 1), /* start at index 1; don't sort the editable row */
model->files->len - 1,
model->node_size,
compare_array_element,
&data);
- g_assert (model->n_indexes_valid == 0);
+ g_assert (model->n_nodes_valid == 0);
g_assert (g_hash_table_size (model->file_lookup) == 0);
- if (n_elements)
+ if (n_visible_rows)
{
- int *new_order = g_new (int, n_elements);
+ int *new_order = g_new (int, n_visible_rows);
- j = 0;
+ r = 0;
for (i = 0; i < model->files->len; i++)
{
FileModelNode *node = get_node (model, i);
if (!node->visible)
{
- node->index = j;
+ node->row = r;
continue;
}
- new_order[j] = node->index;
- j++;
- node->index = j;
+ new_order[r] = node->row - 1;
+ r++;
+ node->row = r;
}
- g_assert (j == n_elements);
+ g_assert (r == n_visible_rows);
path = gtk_tree_path_new ();
gtk_tree_model_rows_reordered (GTK_TREE_MODEL (model),
path,
{
GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (object);
+ if (model->dir_thaw_source)
+ {
+ g_source_remove (model->dir_thaw_source);
+ model->dir_thaw_source = 0;
+ }
+
g_cancellable_cancel (model->cancellable);
if (model->dir_monitor)
g_file_monitor_cancel (model->dir_monitor);
for (i = 0; i < model->files->len; i++)
{
+ int v;
+
FileModelNode *node = get_node (model, i);
if (node->file)
g_object_unref (node->file);
if (node->info)
g_object_unref (node->info);
+
+ for (v = 0; v < model->n_columns; v++)
+ if (G_VALUE_TYPE (&node->values[v]) != G_TYPE_INVALID)
+ g_value_unset (&node->values[v]);
}
g_array_free (model->files, TRUE);
if (model->filter)
g_object_unref (model->filter);
+ g_slice_free1 (sizeof (GType) * model->n_columns, model->column_types);
+
_gtk_tree_data_list_header_free (model->sort_list);
if (model->default_sort_destroy)
model->default_sort_destroy (model->default_sort_data);
model->show_files = TRUE;
model->show_folders = TRUE;
model->show_hidden = FALSE;
+ model->filter_folders = FALSE;
model->sort_column_id = GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID;
continue;
}
file = g_file_get_child (model->dir, name);
- _gtk_file_system_model_add_file (model, file, info);
+ add_file (model, file, info);
g_object_unref (file);
g_object_unref (info);
}
g_list_free (files);
- }
- if (files == NULL)
+ 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
{
- g_file_enumerator_close_async (enumerator,
- IO_PRIORITY,
- model->cancellable,
- gtk_file_system_model_closed_enumerator,
- NULL);
- if (model->dir_thaw_source != 0)
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
{
- g_source_remove (model->dir_thaw_source);
- model->dir_thaw_source = 0;
- _gtk_file_system_model_thaw_updates (model);
+ 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;
+ _gtk_file_system_model_thaw_updates (model);
+ }
+
+ g_signal_emit (model, file_system_model_signals[FINISHED_LOADING], 0, error);
}
if (error)
g_error_free (error);
- else
- g_signal_emit (model, file_system_model_signals[FINISHED_LOADING], 0, NULL);
-
- g_object_unref (model);
}
- 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);
gdk_threads_leave ();
}
if (info == NULL)
return;
+ gdk_threads_enter ();
_gtk_file_system_model_update_file (model, file, info, TRUE);
+ gdk_threads_leave ();
}
static void
/* We can treat all of these the same way */
g_file_query_info_async (file,
model->attributes,
- 0,
+ G_FILE_QUERY_INFO_NONE,
IO_PRIORITY,
model->cancellable,
gtk_file_system_model_query_done,
model);
break;
case G_FILE_MONITOR_EVENT_DELETED:
- _gtk_file_system_model_remove_file (model, file);
+ 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? */
enumerator = g_file_enumerate_children_finish (G_FILE (dir), res, &error);
if (enumerator == NULL)
{
- g_signal_emit (model, file_system_model_signals[FINISHED_LOADING], 0, error);
- g_object_unref (model);
- g_error_free (error);
+ 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
{
model);
g_object_unref (enumerator);
model->dir_monitor = g_file_monitor_directory (model->dir,
- 0,
+ G_FILE_MONITOR_NONE,
model->cancellable,
- NULL);
+ 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",
model->sort_list = _gtk_tree_data_list_header_new (n_columns, model->column_types);
- model->files = g_array_new (FALSE, FALSE, model->node_size);
+ 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);
GFile * dir,
const gchar * attributes)
{
- g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model));
- g_return_if_fail (dir == NULL || G_IS_FILE (dir));
+ g_assert (G_IS_FILE (dir));
model->dir = g_object_ref (dir);
model->attributes = g_strdup (attributes);
- g_object_ref (model);
g_file_enumerate_children_async (model->dir,
attributes,
- 0,
+ G_FILE_QUERY_INFO_NONE,
IO_PRIORITY,
model->cancellable,
gtk_file_system_model_got_enumerator,
* @...: @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_file().
+ * 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
**/
/**
* _gtk_file_system_model_new_for_directory:
* @directory: the directory to show.
- * @attributes: attributes to immediately load or %NULL for all
+ * @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.
}
}
+/**
+ * _gtk_file_system_model_set_filter_folders:
+ * @model: a #GtkFileSystemModel
+ * @filter_folders: whether the filter applies to folders
+ *
+ * Sets whether the filter set by _gtk_file_system_model_set_filter()
+ * applies to folders. By default, it does not and folders are always
+ * visible.
+ **/
+void
+_gtk_file_system_model_set_filter_folders (GtkFileSystemModel *model,
+ gboolean filter_folders)
+{
+ g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model));
+
+ filter_folders = filter_folders != FALSE;
+
+ if (filter_folders != model->filter_folders)
+ {
+ model->filter_folders = filter_folders;
+ gtk_file_system_model_refilter_all (model);
+ }
+}
+
/**
* _gtk_file_system_model_get_cancellable:
* @model: the model
}
/**
- * _gtk_file_system_model_get_is_visible:
+ * _gtk_file_system_model_iter_is_visible:
* @model: the model
* @iter: a valid iterator
*
* Returns: %TRUE if the iterator is visible
**/
gboolean
-_gtk_file_system_model_get_is_visible (GtkFileSystemModel *model,
- GtkTreeIter *iter)
+_gtk_file_system_model_iter_is_visible (GtkFileSystemModel *model,
+ GtkTreeIter *iter)
{
FileModelNode *node;
if (i != 0)
return i;
- /* node 0 is the editable row and has no associated file or entry in the table */
+ /* Node 0 is the editable row and has no associated file or entry in the table, so we start counting from 1.
+ *
+ * The invariant here is that the files in model->files[n] for n < g_hash_table_size (model->file_lookup)
+ * are already added to the hash table. The table can get cleared when we re-sort; this loop merely rebuilds
+ * our (file -> index) mapping on demand.
+ *
+ * If we exit the loop, the next pending batch of mappings will be resolved when this function gets called again
+ * with another file that is not yet in the mapping.
+ */
for (i = g_hash_table_size (model->file_lookup) + 1; i < model->files->len; i++)
{
FileModelNode *node = get_node (model, i);
* Initializes @iter to point to the row used for @file, if @file is part
* of the model. Note that upon successful return, @iter may point to an
* invisible row in the @model. Use
- * _gtk_file_system_model_get_is_visible() to make sure it is visible to
+ * _gtk_file_system_model_iter_is_visible() to make sure it is visible to
* the tree view.
*
* Returns: %TRUE if file is part of the model and @iter was initialized
}
/**
- * _gtk_file_system_model_add_file:
+ * add_file:
* @model: the model
* @file: the file to add
* @info: the information to associate with the file
* Adds the given @file with its associated @info to the @model.
* If the model is frozen, the file will only show up after it is thawn.
**/
-void
-_gtk_file_system_model_add_file (GtkFileSystemModel *model,
- GFile *file,
- GFileInfo *info)
+static void
+add_file (GtkFileSystemModel *model,
+ GFile *file,
+ GFileInfo *info)
{
FileModelNode *node;
}
/**
- * _gtk_file_system_model_remove_file:
+ * remove_file:
* @model: the model
* @file: file to remove from the model. The file must have been
* added to the model previously
* Removes the given file from the model. If the file is not part of
* @model, this function does nothing.
**/
-void
-_gtk_file_system_model_remove_file (GtkFileSystemModel *model,
- GFile *file)
+static void
+remove_file (GtkFileSystemModel *model,
+ GFile *file)
{
FileModelNode *node;
guint id;
node = get_node (model, id);
node_set_visible (model, id, FALSE);
+
+ g_hash_table_remove (model->file_lookup, file);
g_object_unref (node->file);
+
if (node->info)
g_object_unref (node->info);
+
g_array_remove_index (model->files, id);
+ g_hash_table_remove_all (model->file_lookup);
+ /* We don't need to resort, as removing a row doesn't change the sorting order */
}
/**
{
FileModelNode *node;
guint i, id;
+ GFileInfo *old_info;
g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model));
g_return_if_fail (G_IS_FILE (file));
id = node_get_for_file (model, file);
if (id == 0)
- _gtk_file_system_model_add_file (model, file, info);
+ {
+ add_file (model, file, info);
+ id = node_get_for_file (model, file);
+ }
node = get_node (model, id);
- if (node->info)
- g_object_unref (node->info);
+
+ old_info = node->info;
node->info = g_object_ref (info);
+ if (old_info)
+ g_object_unref (old_info);
+
for (i = 0; i < model->n_columns; i++)
{
if (G_VALUE_TYPE (&node->values[i]))
}
if (node->visible)
- {
- GtkTreePath *path;
- GtkTreeIter iter;
-
- path = gtk_tree_path_new_from_node (model, id);
- ITER_INIT_FROM_INDEX (model, &iter, id);
- gtk_tree_model_row_changed (GTK_TREE_MODEL (model), path, &iter);
- gtk_tree_path_free (path);
- }
+ emit_row_changed_for_node (model, id);
if (requires_resort)
gtk_file_system_model_sort_node (model, id);
/**
* _gtk_file_system_model_set_filter:
* @mode: a #GtkFileSystemModel
- * @filter: %NULL or filter to use
+ * @filter: (allow-none): %NULL or filter to use
*
* Sets a filter to be used for deciding if a row should be visible or not.
- * Directories are always visible.
+ * Whether this filter applies to directories can be toggled with
+ * _gtk_file_system_model_set_filter_folders().
**/
void
_gtk_file_system_model_set_filter (GtkFileSystemModel *model,
GtkFileFilter * filter)
{
+ GtkFileFilter *old_filter;
+
g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model));
g_return_if_fail (filter == NULL || GTK_IS_FILE_FILTER (filter));
if (filter)
g_object_ref (filter);
- if (model->filter)
- g_object_unref (model->filter);
+
+ old_filter = model->filter;
model->filter = filter;
+ if (old_filter)
+ g_object_unref (old_filter);
+
gtk_file_system_model_refilter_all (model);
}
}
/**
- * _gtk_file_system_model_clear_cached_values:
+ * _gtk_file_system_model_clear_cache:
* @model: a #GtkFileSystemModel
* @column: the column to clear or -1 for all columns
*
g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model));
g_return_if_fail (column >= -1 && (guint) column < model->n_columns);
- if (column)
+ if (column > -1)
{
start = column;
end = column + 1;
}
if (changed && node->visible)
- {
- GtkTreePath *path;
- GtkTreeIter iter;
-
- path = gtk_tree_path_new_from_node (model, i);
- ITER_INIT_FROM_INDEX (model, &iter, i);
- gtk_tree_model_row_changed (GTK_TREE_MODEL (model), path, &iter);
- gtk_tree_path_free (path);
- }
+ emit_row_changed_for_node (model, i);
}
/* FIXME: resort? */
* @attributes: attributes to query before adding the file
*
* This is a conenience function that calls g_file_query_info_async() on
- * the given file, and when successful, adds it to the model with
- * _gtk_file_system_model_add_file(). Upon failure, the @file is discarded.
+ * the given file, and when successful, adds it to the model.
+ * Upon failure, the @file is discarded.
**/
void
_gtk_file_system_model_add_and_query_file (GtkFileSystemModel *model,
g_file_query_info_async (file,
attributes,
- 0,
+ G_FILE_QUERY_INFO_NONE,
IO_PRIORITY,
model->cancellable,
gtk_file_system_model_query_done,