gtk2/gtk/gtktreemodelfilter.c
Kristian Rietveld 88dd6372b0 treemodelfilter: rework reference counting
- Before we kept a reference on all nodes in non-root levels.  This has
   been changed, now we keep a reference on the first node of each level.
   If, due to changes in the model, another node becomes the first node in
   the level, the reference is transferred to this new first node.
 - All non-root levels keep a reference on their parent.
 - By making use of the external ref count, the filter model now emits less
   unnecessary signals.
 - GtkTreeModelFilter does support filter functions which decide visibility
   of a given node based on the number of or visibility of children.
   To accomplish this, a child level of a node is cached when its
   parent has an external ref count > 0, because changes to the node might
   affect this parent.
 - An optimization for not building the root level in case the inserted
   node is not visible in gtk_tree_model_filter_row_inserted() has been
   removed.  In this case, we still need to build the root level and
   possibly a child level to monitor for signals which might make
   this row visible.
2011-08-22 21:30:32 +02:00

4074 lines
134 KiB
C

/* gtktreemodelfilter.c
* Copyright (C) 2000,2001 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
* Copyright (C) 2001-2003 Kristian Rietveld <kris@gtk.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include "config.h"
#include "gtktreemodelfilter.h"
#include "gtkintl.h"
#include "gtktreednd.h"
#include "gtkprivate.h"
#include <string.h>
/**
* SECTION:gtktreemodelfilter
* @Short_description: A GtkTreeModel which hides parts of an underlying tree model
* @Title: GtkTreeModelFilter
* @See_also:#GtkTreeModelSort
*
* A #GtkTreeModelFilter is a tree model which wraps another tree model,
* and can do the following things:
* <itemizedlist>
* <listitem><para>
* Filter specific rows, based on data from a "visible column", a column
* storing booleans indicating whether the row should be filtered or not,
* or based on the return value of a "visible function", which gets a
* model, iter and user_data and returns a boolean indicating whether the
* row should be filtered or not.
* </para></listitem>
* <listitem><para>
* Modify the "appearance" of the model, using a modify function.
* This is extremely powerful and allows for just changing
* some values and also for creating a completely different model based on
* the given child model.
* </para></listitem>
* <listitem><para>
* Set a different root node, also known as a "virtual root". You can pass in
* a #GtkTreePath indicating the root node for the filter at construction time.
* </para></listitem>
* </itemizedlist>
*/
/* ITER FORMAT:
*
* iter->stamp = filter->priv->stamp
* iter->user_data = FilterLevel
* iter->user_data2 = FilterElt
*/
/* all paths, iters, etc prefixed with c_ are paths, iters, etc relative to the
* child model.
*/
/* A few notes:
* There are three model/views involved, so there are two mappings:
* * this model -> child model: mapped via offset in FilterElt.
* * this model -> parent model (or view): mapped via the array index
* of FilterElt.
*
* Note that there are two kinds of paths relative to the filter model
* (those generated from the array indices): paths taking non-visible
* nodes into account, and paths which don't. Paths which take
* non-visible nodes into account should only be used internally and
* NEVER be passed along with a signal emission.
*
* The filter model has a reference on every node that is not in the root
* level and has a parent with ref_count > 1. Exception is a virtual root
* level; all nodes in the virtual root level are referenced too.
*/
typedef struct _FilterElt FilterElt;
typedef struct _FilterLevel FilterLevel;
struct _FilterElt
{
GtkTreeIter iter;
FilterLevel *children;
gint offset;
gint ref_count;
gint ext_ref_count;
gint zero_ref_count;
gboolean visible;
};
struct _FilterLevel
{
GArray *array;
gint ref_count;
gint ext_ref_count;
gint visible_nodes;
gint parent_elt_index;
FilterLevel *parent_level;
};
struct _GtkTreeModelFilterPrivate
{
GtkTreeModel *child_model;
gpointer root;
GtkTreePath *virtual_root;
gint stamp;
guint child_flags;
gint zero_ref_count;
gint visible_column;
GtkTreeModelFilterVisibleFunc visible_func;
gpointer visible_data;
GDestroyNotify visible_destroy;
GType *modify_types;
GtkTreeModelFilterModifyFunc modify_func;
gpointer modify_data;
GDestroyNotify modify_destroy;
gint modify_n_columns;
guint visible_method_set : 1;
guint modify_func_set : 1;
guint in_row_deleted : 1;
guint virtual_root_deleted : 1;
/* signal ids */
gulong changed_id;
gulong inserted_id;
gulong has_child_toggled_id;
gulong deleted_id;
gulong reordered_id;
};
/* properties */
enum
{
PROP_0,
PROP_CHILD_MODEL,
PROP_VIRTUAL_ROOT
};
/* Set this to 0 to disable caching of child iterators. This
* allows for more stringent testing. It is recommended to set this
* to one when refactoring this code and running the unit tests to
* catch more errors.
*/
#if 1
# define GTK_TREE_MODEL_FILTER_CACHE_CHILD_ITERS(filter) \
(((GtkTreeModelFilter *)filter)->priv->child_flags & GTK_TREE_MODEL_ITERS_PERSIST)
#else
# define GTK_TREE_MODEL_FILTER_CACHE_CHILD_ITERS(filter) (FALSE)
#endif
#define FILTER_ELT(filter_elt) ((FilterElt *)filter_elt)
#define FILTER_LEVEL(filter_level) ((FilterLevel *)filter_level)
#define FILTER_LEVEL_PARENT_ELT(level) (&g_array_index (FILTER_LEVEL ((level))->parent_level->array, FilterElt, FILTER_LEVEL ((level))->parent_elt_index))
#define FILTER_LEVEL_ELT_INDEX(level, elt) (FILTER_ELT ((elt)) - FILTER_ELT (FILTER_LEVEL ((level))->array->data))
/* general code (object/interface init, properties, etc) */
static void gtk_tree_model_filter_tree_model_init (GtkTreeModelIface *iface);
static void gtk_tree_model_filter_drag_source_init (GtkTreeDragSourceIface *iface);
static void gtk_tree_model_filter_finalize (GObject *object);
static void gtk_tree_model_filter_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec);
static void gtk_tree_model_filter_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec);
/* signal handlers */
static void gtk_tree_model_filter_row_changed (GtkTreeModel *c_model,
GtkTreePath *c_path,
GtkTreeIter *c_iter,
gpointer data);
static void gtk_tree_model_filter_row_inserted (GtkTreeModel *c_model,
GtkTreePath *c_path,
GtkTreeIter *c_iter,
gpointer data);
static void gtk_tree_model_filter_row_has_child_toggled (GtkTreeModel *c_model,
GtkTreePath *c_path,
GtkTreeIter *c_iter,
gpointer data);
static void gtk_tree_model_filter_row_deleted (GtkTreeModel *c_model,
GtkTreePath *c_path,
gpointer data);
static void gtk_tree_model_filter_rows_reordered (GtkTreeModel *c_model,
GtkTreePath *c_path,
GtkTreeIter *c_iter,
gint *new_order,
gpointer data);
/* GtkTreeModel interface */
static GtkTreeModelFlags gtk_tree_model_filter_get_flags (GtkTreeModel *model);
static gint gtk_tree_model_filter_get_n_columns (GtkTreeModel *model);
static GType gtk_tree_model_filter_get_column_type (GtkTreeModel *model,
gint index);
static gboolean gtk_tree_model_filter_get_iter_full (GtkTreeModel *model,
GtkTreeIter *iter,
GtkTreePath *path);
static gboolean gtk_tree_model_filter_get_iter (GtkTreeModel *model,
GtkTreeIter *iter,
GtkTreePath *path);
static GtkTreePath *gtk_tree_model_filter_get_path (GtkTreeModel *model,
GtkTreeIter *iter);
static void gtk_tree_model_filter_get_value (GtkTreeModel *model,
GtkTreeIter *iter,
gint column,
GValue *value);
static gboolean gtk_tree_model_filter_iter_next (GtkTreeModel *model,
GtkTreeIter *iter);
static gboolean gtk_tree_model_filter_iter_previous (GtkTreeModel *model,
GtkTreeIter *iter);
static gboolean gtk_tree_model_filter_iter_children (GtkTreeModel *model,
GtkTreeIter *iter,
GtkTreeIter *parent);
static gboolean gtk_tree_model_filter_iter_has_child (GtkTreeModel *model,
GtkTreeIter *iter);
static gint gtk_tree_model_filter_iter_n_children (GtkTreeModel *model,
GtkTreeIter *iter);
static gboolean gtk_tree_model_filter_iter_nth_child (GtkTreeModel *model,
GtkTreeIter *iter,
GtkTreeIter *parent,
gint n);
static gboolean gtk_tree_model_filter_iter_parent (GtkTreeModel *model,
GtkTreeIter *iter,
GtkTreeIter *child);
static void gtk_tree_model_filter_ref_node (GtkTreeModel *model,
GtkTreeIter *iter);
static void gtk_tree_model_filter_unref_node (GtkTreeModel *model,
GtkTreeIter *iter);
/* TreeDragSource interface */
static gboolean gtk_tree_model_filter_row_draggable (GtkTreeDragSource *drag_source,
GtkTreePath *path);
static gboolean gtk_tree_model_filter_drag_data_get (GtkTreeDragSource *drag_source,
GtkTreePath *path,
GtkSelectionData *selection_data);
static gboolean gtk_tree_model_filter_drag_data_delete (GtkTreeDragSource *drag_source,
GtkTreePath *path);
/* private functions */
static void gtk_tree_model_filter_build_level (GtkTreeModelFilter *filter,
FilterLevel *parent_level,
gint parent_elt_index,
gboolean emit_inserted);
static void gtk_tree_model_filter_free_level (GtkTreeModelFilter *filter,
FilterLevel *filter_level,
gboolean unref);
static GtkTreePath *gtk_tree_model_filter_elt_get_path (FilterLevel *level,
FilterElt *elt,
GtkTreePath *root);
static GtkTreePath *gtk_tree_model_filter_add_root (GtkTreePath *src,
GtkTreePath *root);
static GtkTreePath *gtk_tree_model_filter_remove_root (GtkTreePath *src,
GtkTreePath *root);
static void gtk_tree_model_filter_increment_stamp (GtkTreeModelFilter *filter);
static void gtk_tree_model_filter_real_modify (GtkTreeModelFilter *self,
GtkTreeModel *child_model,
GtkTreeIter *iter,
GValue *value,
gint column);
static gboolean gtk_tree_model_filter_real_visible (GtkTreeModelFilter *filter,
GtkTreeModel *child_model,
GtkTreeIter *child_iter);
static gboolean gtk_tree_model_filter_visible (GtkTreeModelFilter *filter,
GtkTreeIter *child_iter);
static void gtk_tree_model_filter_clear_cache_helper (GtkTreeModelFilter *filter,
FilterLevel *level);
static void gtk_tree_model_filter_real_ref_node (GtkTreeModel *model,
GtkTreeIter *iter,
gboolean external);
static void gtk_tree_model_filter_real_unref_node (GtkTreeModel *model,
GtkTreeIter *iter,
gboolean external,
gboolean propagate_unref);
static void gtk_tree_model_filter_set_model (GtkTreeModelFilter *filter,
GtkTreeModel *child_model);
static void gtk_tree_model_filter_ref_path (GtkTreeModelFilter *filter,
GtkTreePath *path);
static void gtk_tree_model_filter_unref_path (GtkTreeModelFilter *filter,
GtkTreePath *path,
int depth);
static void gtk_tree_model_filter_set_root (GtkTreeModelFilter *filter,
GtkTreePath *root);
static GtkTreePath *gtk_real_tree_model_filter_convert_child_path_to_path (GtkTreeModelFilter *filter,
GtkTreePath *child_path,
gboolean build_levels,
gboolean fetch_children);
static FilterElt *gtk_tree_model_filter_get_nth (GtkTreeModelFilter *filter,
FilterLevel *level,
int n);
static gboolean gtk_tree_model_filter_elt_is_visible_in_target (FilterLevel *level,
FilterElt *elt);
static FilterElt *gtk_tree_model_filter_get_nth_visible (GtkTreeModelFilter *filter,
FilterLevel *level,
int n);
static FilterElt *gtk_tree_model_filter_insert_elt_in_level (GtkTreeModelFilter *filter,
GtkTreeIter *c_iter,
FilterLevel *level,
gint offset,
gint *index);
static FilterElt *gtk_tree_model_filter_fetch_child (GtkTreeModelFilter *filter,
FilterLevel *level,
gint offset,
gint *index);
static void gtk_tree_model_filter_remove_elt_from_level (GtkTreeModelFilter *filter,
FilterLevel *level,
FilterElt *elt);
static void gtk_tree_model_filter_update_children (GtkTreeModelFilter *filter,
FilterLevel *level,
FilterElt *elt);
static FilterElt *bsearch_elt_with_offset (GArray *array,
gint offset,
gint *index);
static void gtk_tree_model_filter_emit_row_inserted_for_path (GtkTreeModelFilter *filter,
GtkTreeModel *c_model,
GtkTreePath *c_path,
GtkTreeIter *c_iter);
G_DEFINE_TYPE_WITH_CODE (GtkTreeModelFilter, gtk_tree_model_filter, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL,
gtk_tree_model_filter_tree_model_init)
G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE,
gtk_tree_model_filter_drag_source_init))
static void
gtk_tree_model_filter_init (GtkTreeModelFilter *filter)
{
filter->priv = G_TYPE_INSTANCE_GET_PRIVATE (filter,
GTK_TYPE_TREE_MODEL_FILTER,
GtkTreeModelFilterPrivate);
filter->priv->visible_column = -1;
filter->priv->zero_ref_count = 0;
filter->priv->visible_method_set = FALSE;
filter->priv->modify_func_set = FALSE;
filter->priv->in_row_deleted = FALSE;
filter->priv->virtual_root_deleted = FALSE;
}
static void
gtk_tree_model_filter_class_init (GtkTreeModelFilterClass *filter_class)
{
GObjectClass *object_class;
object_class = (GObjectClass *) filter_class;
object_class->set_property = gtk_tree_model_filter_set_property;
object_class->get_property = gtk_tree_model_filter_get_property;
object_class->finalize = gtk_tree_model_filter_finalize;
filter_class->visible = gtk_tree_model_filter_real_visible;
filter_class->modify = gtk_tree_model_filter_real_modify;
/* Properties -- FIXME: disabled translations for now, until I can come up with a
* better description
*/
g_object_class_install_property (object_class,
PROP_CHILD_MODEL,
g_param_spec_object ("child-model",
("The child model"),
("The model for the filtermodel to filter"),
GTK_TYPE_TREE_MODEL,
GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property (object_class,
PROP_VIRTUAL_ROOT,
g_param_spec_boxed ("virtual-root",
("The virtual root"),
("The virtual root (relative to the child model) for this filtermodel"),
GTK_TYPE_TREE_PATH,
GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
g_type_class_add_private (object_class, sizeof (GtkTreeModelFilterPrivate));
}
static void
gtk_tree_model_filter_tree_model_init (GtkTreeModelIface *iface)
{
iface->get_flags = gtk_tree_model_filter_get_flags;
iface->get_n_columns = gtk_tree_model_filter_get_n_columns;
iface->get_column_type = gtk_tree_model_filter_get_column_type;
iface->get_iter = gtk_tree_model_filter_get_iter;
iface->get_path = gtk_tree_model_filter_get_path;
iface->get_value = gtk_tree_model_filter_get_value;
iface->iter_next = gtk_tree_model_filter_iter_next;
iface->iter_previous = gtk_tree_model_filter_iter_previous;
iface->iter_children = gtk_tree_model_filter_iter_children;
iface->iter_has_child = gtk_tree_model_filter_iter_has_child;
iface->iter_n_children = gtk_tree_model_filter_iter_n_children;
iface->iter_nth_child = gtk_tree_model_filter_iter_nth_child;
iface->iter_parent = gtk_tree_model_filter_iter_parent;
iface->ref_node = gtk_tree_model_filter_ref_node;
iface->unref_node = gtk_tree_model_filter_unref_node;
}
static void
gtk_tree_model_filter_drag_source_init (GtkTreeDragSourceIface *iface)
{
iface->row_draggable = gtk_tree_model_filter_row_draggable;
iface->drag_data_delete = gtk_tree_model_filter_drag_data_delete;
iface->drag_data_get = gtk_tree_model_filter_drag_data_get;
}
static void
gtk_tree_model_filter_finalize (GObject *object)
{
GtkTreeModelFilter *filter = (GtkTreeModelFilter *) object;
if (filter->priv->virtual_root && !filter->priv->virtual_root_deleted)
{
gtk_tree_model_filter_unref_path (filter, filter->priv->virtual_root,
-1);
filter->priv->virtual_root_deleted = TRUE;
}
gtk_tree_model_filter_set_model (filter, NULL);
if (filter->priv->virtual_root)
gtk_tree_path_free (filter->priv->virtual_root);
if (filter->priv->root)
gtk_tree_model_filter_free_level (filter, filter->priv->root, TRUE);
g_free (filter->priv->modify_types);
if (filter->priv->modify_destroy)
filter->priv->modify_destroy (filter->priv->modify_data);
if (filter->priv->visible_destroy)
filter->priv->visible_destroy (filter->priv->visible_data);
/* must chain up */
G_OBJECT_CLASS (gtk_tree_model_filter_parent_class)->finalize (object);
}
static void
gtk_tree_model_filter_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkTreeModelFilter *filter = GTK_TREE_MODEL_FILTER (object);
switch (prop_id)
{
case PROP_CHILD_MODEL:
gtk_tree_model_filter_set_model (filter, g_value_get_object (value));
break;
case PROP_VIRTUAL_ROOT:
gtk_tree_model_filter_set_root (filter, g_value_get_boxed (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gtk_tree_model_filter_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GtkTreeModelFilter *filter = GTK_TREE_MODEL_FILTER (object);
switch (prop_id)
{
case PROP_CHILD_MODEL:
g_value_set_object (value, filter->priv->child_model);
break;
case PROP_VIRTUAL_ROOT:
g_value_set_boxed (value, filter->priv->virtual_root);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
/* helpers */
static void
gtk_tree_model_filter_build_level (GtkTreeModelFilter *filter,
FilterLevel *parent_level,
gint parent_elt_index,
gboolean emit_inserted)
{
GtkTreeIter iter;
GtkTreeIter first_node;
GtkTreeIter root;
FilterElt *parent_elt = NULL;
FilterLevel *new_level;
FilterLevel *tmp_level;
GtkTreeIter f_iter;
gint length = 0;
gint i;
gint tmp_elt_index;
g_assert (filter->priv->child_model != NULL);
/* Avoid building a level that already exists */
if (parent_level)
g_assert (g_array_index (parent_level->array, FilterElt, parent_elt_index).children == NULL);
else
g_assert (filter->priv->root == NULL);
if (filter->priv->in_row_deleted)
return;
if (!parent_level)
{
if (filter->priv->virtual_root)
{
if (gtk_tree_model_get_iter (filter->priv->child_model, &root, filter->priv->virtual_root) == FALSE)
return;
length = gtk_tree_model_iter_n_children (filter->priv->child_model, &root);
if (gtk_tree_model_iter_children (filter->priv->child_model, &iter, &root) == FALSE)
return;
}
else
{
if (!gtk_tree_model_get_iter_first (filter->priv->child_model, &iter))
return;
length = gtk_tree_model_iter_n_children (filter->priv->child_model, NULL);
}
}
else
{
GtkTreeIter parent_iter;
GtkTreeIter child_parent_iter;
parent_elt = &g_array_index (parent_level->array, FilterElt, parent_elt_index);
parent_iter.stamp = filter->priv->stamp;
parent_iter.user_data = parent_level;
parent_iter.user_data2 = parent_elt;
gtk_tree_model_filter_convert_iter_to_child_iter (filter,
&child_parent_iter,
&parent_iter);
if (gtk_tree_model_iter_children (filter->priv->child_model, &iter, &child_parent_iter) == FALSE)
return;
/* stamp may have changed */
gtk_tree_model_filter_convert_iter_to_child_iter (filter,
&child_parent_iter,
&parent_iter);
length = gtk_tree_model_iter_n_children (filter->priv->child_model, &child_parent_iter);
/* Take a reference on the parent */
gtk_tree_model_filter_real_ref_node (GTK_TREE_MODEL (filter),
&parent_iter, FALSE);
}
g_return_if_fail (length > 0);
new_level = g_new (FilterLevel, 1);
new_level->array = g_array_sized_new (FALSE, FALSE,
sizeof (FilterElt),
length);
new_level->ref_count = 0;
new_level->ext_ref_count = 0;
new_level->visible_nodes = 0;
new_level->parent_elt_index = parent_elt_index;
new_level->parent_level = parent_level;
if (parent_elt_index >= 0)
parent_elt->children = new_level;
else
filter->priv->root = new_level;
/* increase the count of zero ref_counts */
tmp_level = parent_level;
tmp_elt_index = parent_elt_index;
while (tmp_level)
{
g_array_index (tmp_level->array, FilterElt, tmp_elt_index).zero_ref_count++;
tmp_elt_index = tmp_level->parent_elt_index;
tmp_level = tmp_level->parent_level;
}
if (new_level != filter->priv->root)
filter->priv->zero_ref_count++;
i = 0;
first_node = iter;
do
{
if (gtk_tree_model_filter_visible (filter, &iter))
{
FilterElt filter_elt;
filter_elt.offset = i;
filter_elt.zero_ref_count = 0;
filter_elt.ref_count = 0;
filter_elt.ext_ref_count = 0;
filter_elt.children = NULL;
filter_elt.visible = TRUE;
if (GTK_TREE_MODEL_FILTER_CACHE_CHILD_ITERS (filter))
filter_elt.iter = iter;
g_array_append_val (new_level->array, filter_elt);
new_level->visible_nodes++;
if (emit_inserted)
{
GtkTreePath *f_path;
GtkTreeIter f_iter;
GtkTreeIter children;
f_iter.stamp = filter->priv->stamp;
f_iter.user_data = new_level;
f_iter.user_data2 = &(g_array_index (new_level->array, FilterElt, new_level->array->len - 1));
f_path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter),
&f_iter);
gtk_tree_model_row_inserted (GTK_TREE_MODEL (filter),
f_path, &f_iter);
gtk_tree_path_free (f_path);
if (gtk_tree_model_iter_children (filter->priv->child_model,
&children, &iter))
gtk_tree_model_filter_update_children (filter,
new_level,
FILTER_ELT (f_iter.user_data2));
}
}
i++;
}
while (gtk_tree_model_iter_next (filter->priv->child_model, &iter));
/* The level does not contain any visible nodes. However, changes in
* this level might affect the parent node, which can either be visible
* or invisible. Therefore, this level can only be removed again,
* if the parent of the parent node is not visible. In that case,
* possible changes in state of the parent are not requested.
*/
if (new_level->array->len == 0 &&
(parent_level && parent_level->parent_level &&
FILTER_LEVEL_PARENT_ELT (parent_level)->ext_ref_count == 0))
{
gtk_tree_model_filter_free_level (filter, new_level, FALSE);
return;
}
/* If none of the nodes are visible, we will just pull in the
* first node of the level.
*/
if (new_level->array->len == 0)
{
FilterElt filter_elt;
filter_elt.offset = 0;
filter_elt.zero_ref_count = 0;
filter_elt.ref_count = 0;
filter_elt.ext_ref_count = 0;
filter_elt.children = NULL;
filter_elt.visible = FALSE;
if (GTK_TREE_MODEL_FILTER_CACHE_CHILD_ITERS (filter))
filter_elt.iter = first_node;
g_array_append_val (new_level->array, filter_elt);
}
/* Keep a reference on the first node of this level. We need this
* to make sure that we get all signals for this level.
*/
f_iter.stamp = filter->priv->stamp;
f_iter.user_data = new_level;
f_iter.user_data2 = &(g_array_index (new_level->array, FilterElt, 0));
gtk_tree_model_filter_real_ref_node (GTK_TREE_MODEL (filter), &f_iter,
FALSE);
}
static void
gtk_tree_model_filter_free_level (GtkTreeModelFilter *filter,
FilterLevel *filter_level,
gboolean unref)
{
gint i;
g_assert (filter_level);
for (i = 0; i < filter_level->array->len; i++)
{
if (g_array_index (filter_level->array, FilterElt, i).children)
gtk_tree_model_filter_free_level (filter,
FILTER_LEVEL (g_array_index (filter_level->array, FilterElt, i).children),
unref);
}
/* Release the reference on the first item.
*/
if (unref)
{
GtkTreeIter f_iter;
f_iter.stamp = filter->priv->stamp;
f_iter.user_data = filter_level;
f_iter.user_data2 = &(g_array_index (filter_level->array, FilterElt, 0));
gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (filter),
&f_iter, FALSE, TRUE);
}
if (filter_level->ext_ref_count == 0)
{
FilterLevel *parent_level = filter_level->parent_level;
gint parent_elt_index = filter_level->parent_elt_index;
while (parent_level)
{
g_array_index (parent_level->array, FilterElt, parent_elt_index).zero_ref_count--;
parent_elt_index = parent_level->parent_elt_index;
parent_level = parent_level->parent_level;
}
if (filter_level != filter->priv->root)
filter->priv->zero_ref_count--;
}
if (filter_level->parent_elt_index >= 0)
{
/* Release reference on parent */
if (unref)
{
GtkTreeIter parent_iter;
parent_iter.stamp = filter->priv->stamp;
parent_iter.user_data = filter_level->parent_level;
parent_iter.user_data2 = FILTER_LEVEL_PARENT_ELT (filter_level);
gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (filter),
&parent_iter, FALSE, TRUE);
}
FILTER_LEVEL_PARENT_ELT (filter_level)->children = NULL;
}
else
filter->priv->root = NULL;
g_array_free (filter_level->array, TRUE);
filter_level->array = NULL;
g_free (filter_level);
filter_level = NULL;
}
static void
gtk_tree_model_filter_level_transfer_first_ref (GtkTreeModelFilter *filter,
FilterLevel *level,
gint from_index,
gint to_index)
{
GtkTreeIter f_iter;
f_iter.stamp = filter->priv->stamp;
f_iter.user_data = level;
f_iter.user_data2 = &(g_array_index (level->array, FilterElt, to_index));
gtk_tree_model_filter_real_ref_node (GTK_TREE_MODEL (filter),
&f_iter, FALSE);
f_iter.stamp = filter->priv->stamp;
f_iter.user_data = level;
f_iter.user_data2 = &(g_array_index (level->array, FilterElt, from_index));
gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (filter),
&f_iter, FALSE, TRUE);
}
/* Creates paths suitable for accessing the child model. */
static GtkTreePath *
gtk_tree_model_filter_elt_get_path (FilterLevel *level,
FilterElt *elt,
GtkTreePath *root)
{
FilterLevel *walker = level;
FilterElt *walker2 = elt;
GtkTreePath *path;
GtkTreePath *real_path;
g_return_val_if_fail (level != NULL, NULL);
g_return_val_if_fail (elt != NULL, NULL);
path = gtk_tree_path_new ();
while (walker)
{
gtk_tree_path_prepend_index (path, walker2->offset);
if (!walker->parent_level)
break;
walker2 = FILTER_LEVEL_PARENT_ELT (walker);
walker = walker->parent_level;
}
if (root)
{
real_path = gtk_tree_model_filter_add_root (path, root);
gtk_tree_path_free (path);
return real_path;
}
return path;
}
static GtkTreePath *
gtk_tree_model_filter_add_root (GtkTreePath *src,
GtkTreePath *root)
{
GtkTreePath *retval;
gint i;
retval = gtk_tree_path_copy (root);
for (i = 0; i < gtk_tree_path_get_depth (src); i++)
gtk_tree_path_append_index (retval, gtk_tree_path_get_indices (src)[i]);
return retval;
}
static GtkTreePath *
gtk_tree_model_filter_remove_root (GtkTreePath *src,
GtkTreePath *root)
{
GtkTreePath *retval;
gint i;
gint depth;
gint *indices;
if (gtk_tree_path_get_depth (src) <= gtk_tree_path_get_depth (root))
return NULL;
depth = gtk_tree_path_get_depth (src);
indices = gtk_tree_path_get_indices (src);
for (i = 0; i < gtk_tree_path_get_depth (root); i++)
if (indices[i] != gtk_tree_path_get_indices (root)[i])
return NULL;
retval = gtk_tree_path_new ();
for (; i < depth; i++)
gtk_tree_path_append_index (retval, indices[i]);
return retval;
}
static void
gtk_tree_model_filter_increment_stamp (GtkTreeModelFilter *filter)
{
do
{
filter->priv->stamp++;
}
while (filter->priv->stamp == 0);
gtk_tree_model_filter_clear_cache (filter);
}
static gboolean
gtk_tree_model_filter_real_visible (GtkTreeModelFilter *filter,
GtkTreeModel *child_model,
GtkTreeIter *child_iter)
{
if (filter->priv->visible_func)
{
return filter->priv->visible_func (child_model,
child_iter,
filter->priv->visible_data)
? TRUE : FALSE;
}
else if (filter->priv->visible_column >= 0)
{
GValue val = {0, };
gtk_tree_model_get_value (child_model, child_iter,
filter->priv->visible_column, &val);
if (g_value_get_boolean (&val))
{
g_value_unset (&val);
return TRUE;
}
g_value_unset (&val);
return FALSE;
}
/* no visible function set, so always visible */
return TRUE;
}
static gboolean
gtk_tree_model_filter_visible (GtkTreeModelFilter *self,
GtkTreeIter *child_iter)
{
return GTK_TREE_MODEL_FILTER_GET_CLASS (self)->visible (self,
self->priv->child_model, child_iter);
}
static void
gtk_tree_model_filter_clear_cache_helper (GtkTreeModelFilter *filter,
FilterLevel *level)
{
gint i;
g_assert (level);
for (i = 0; i < level->array->len; i++)
{
if (g_array_index (level->array, FilterElt, i).zero_ref_count > 0)
gtk_tree_model_filter_clear_cache_helper (filter, g_array_index (level->array, FilterElt, i).children);
}
/* If the level's ext_ref_count is zero, it means the level is not visible
* and can be removed. But, since we support monitoring a child level
* of a parent for changes (these might affect the parent), we will only
* free the level if the parent's parent also has an external ref
* count of zero. In that case, changes concerning our parent are
* not requested.
*/
if (level->ext_ref_count == 0 && level != filter->priv->root &&
level->parent_level && FILTER_LEVEL_PARENT_ELT (level) &&
level->parent_level->parent_level &&
FILTER_LEVEL_PARENT_ELT (level->parent_level)->ext_ref_count == 0)
{
gtk_tree_model_filter_free_level (filter, level, TRUE);
return;
}
}
static FilterElt *
gtk_tree_model_filter_get_nth (GtkTreeModelFilter *filter,
FilterLevel *level,
int n)
{
if (level->array->len <= n)
return NULL;
return &g_array_index (level->array, FilterElt, n);
}
static gboolean
gtk_tree_model_filter_elt_is_visible_in_target (FilterLevel *level,
FilterElt *elt)
{
gint elt_index;
if (!elt->visible)
return FALSE;
if (level->parent_elt_index == -1)
return TRUE;
do
{
elt_index = level->parent_elt_index;
level = level->parent_level;
if (elt_index >= 0
&& !g_array_index (level->array, FilterElt, elt_index).visible)
return FALSE;
}
while (level);
return TRUE;
}
/* If a change has occurred in path (inserted, changed or deleted),
* then this function is used to check all its ancestors. An ancestor
* could have changed state as a result and this needs to be propagated
* to the objects monitoring the filter model.
*/
static void
gtk_tree_model_filter_check_ancestors (GtkTreeModelFilter *filter,
GtkTreePath *path)
{
int i = 0;
int *indices = gtk_tree_path_get_indices (path);
FilterElt *elt;
FilterLevel *level;
GtkTreeIter c_iter, tmp_iter;
level = FILTER_LEVEL (filter->priv->root);
if (!level)
return;
if (filter->priv->virtual_root)
gtk_tree_model_get_iter (filter->priv->child_model, &c_iter,
filter->priv->virtual_root);
tmp_iter = c_iter;
gtk_tree_model_iter_nth_child (filter->priv->child_model, &c_iter,
filter->priv->virtual_root ? &tmp_iter : NULL,
indices[i]);
while (i < gtk_tree_path_get_depth (path) - 1)
{
int j;
gboolean requested_state;
elt = bsearch_elt_with_offset (level->array,
gtk_tree_path_get_indices (path)[i],
&j);
requested_state = gtk_tree_model_filter_visible (filter, &c_iter);
if (!elt)
{
int index;
GtkTreePath *c_path;
if (requested_state == FALSE)
return;
/* The elt does not exist in this level (so it is not
* visible), but should now be visible. We emit the
* row-inserted and row-has-child-toggled signals.
*/
elt = gtk_tree_model_filter_insert_elt_in_level (filter,
&c_iter,
level,
indices[i],
&index);
/* insert_elt_in_level defaults to FALSE */
elt->visible = TRUE;
level->visible_nodes++;
c_path = gtk_tree_model_get_path (filter->priv->child_model,
&c_iter);
gtk_tree_model_filter_emit_row_inserted_for_path (filter,
filter->priv->child_model,
c_path,
&c_iter);
gtk_tree_path_free (c_path);
/* We can immediately return, because this node was not visible
* before and its children will be checked for in response to
* the emitted row-has-child-toggled signal.
*/
return;
}
else if (elt->visible)
{
if (!requested_state)
{
/* A node has turned invisible. Remove it from the level
* and emit row-deleted. Since this node is being
* deleted. it makes no sense to look further up the
* chain.
*/
gtk_tree_model_filter_remove_elt_from_level (filter,
level, elt);
return;
}
/* Otherwise continue up the chain */
}
else if (!elt->visible)
{
if (requested_state)
{
/* A node is already in the cache, but invisible. This
* is usually a node on which a reference is kept by
* the filter model, or a node fetched on the filter's
* request, and thus not shown. Therefore, we will
* not emit row-inserted for this node. Instead,
* we signal to its parent that a change has occurred.
*
* Exception: root level, in this case, we must emit
* row-inserted.
*/
if (level->parent_level)
{
GtkTreeIter f_iter;
GtkTreePath *f_path;
elt->visible = TRUE;
level->visible_nodes++;
f_iter.stamp = filter->priv->stamp;
f_iter.user_data = level->parent_level;
f_iter.user_data2 = FILTER_LEVEL_PARENT_ELT(level);
f_path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter),
&f_iter);
gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (filter),
f_path, &f_iter);
gtk_tree_path_free (f_path);
}
else
{
GtkTreePath *c_path;
elt->visible = TRUE;
level->visible_nodes++;
c_path = gtk_tree_model_get_path (filter->priv->child_model,
&c_iter);
gtk_tree_model_filter_emit_row_inserted_for_path (filter,
filter->priv->child_model,
c_path,
&c_iter);
gtk_tree_path_free (c_path);
}
/* We can immediately return, because this node was not visible
* before and the parent will check its children, including
* this node, in response to the emitted row-has-child-toggled
* signal.
*/
return;
}
/* Not visible, so no need to continue. */
return;
}
if (!elt->children)
{
/* If an elt does not have children, these are not visible.
* Therefore, any signals emitted for these children will
* be ignored, so we do not have to emit them.
*/
return;
}
level = elt->children;
i++;
tmp_iter = c_iter;
gtk_tree_model_iter_nth_child (filter->priv->child_model, &c_iter,
&tmp_iter, indices[i]);
}
}
static FilterElt *
gtk_tree_model_filter_get_nth_visible (GtkTreeModelFilter *filter,
FilterLevel *level,
int n)
{
int i = 0;
FilterElt *elt;
if (level->visible_nodes <= n)
return NULL;
elt = FILTER_ELT (level->array->data);
while (!elt->visible)
elt++;
while (i < n)
{
if (elt->visible)
i++;
elt++;
}
while (!elt->visible)
elt++;
return elt;
}
static FilterElt *
gtk_tree_model_filter_insert_elt_in_level (GtkTreeModelFilter *filter,
GtkTreeIter *c_iter,
FilterLevel *level,
gint offset,
gint *index)
{
gint i = 0;
gint start, middle, end;
FilterElt elt;
if (GTK_TREE_MODEL_FILTER_CACHE_CHILD_ITERS (filter))
elt.iter = *c_iter;
elt.offset = offset;
elt.zero_ref_count = 0;
elt.ref_count = 0;
elt.ext_ref_count = 0;
elt.children = NULL;
/* visibility should be FALSE as we don't emit row_inserted */
elt.visible = FALSE;
/* find index (binary search on offset) */
start = 0;
end = level->array->len;
if (start != end)
{
while (start != end)
{
middle = (start + end) / 2;
if (g_array_index (level->array, FilterElt, middle).offset <= offset)
start = middle + 1;
else
end = middle;
}
if (g_array_index (level->array, FilterElt, middle).offset <= offset)
i = middle + 1;
else
i = middle;
}
else
i = 0;
g_array_insert_val (level->array, i, elt);
*index = i;
for (i = 0; i < level->array->len; i++)
{
FilterElt *e = &(g_array_index (level->array, FilterElt, i));
if (e->children)
e->children->parent_elt_index = i;
}
/* If the insert location is zero, we need to move our reference
* on the old first node to the new first node.
*/
if (*index == 0)
gtk_tree_model_filter_level_transfer_first_ref (filter, level,
1, 0);
return &g_array_index (level->array, FilterElt, *index);
}
static FilterElt *
gtk_tree_model_filter_fetch_child (GtkTreeModelFilter *filter,
FilterLevel *level,
gint offset,
gint *index)
{
gint len;
GtkTreePath *c_path = NULL;
GtkTreeIter c_iter;
GtkTreePath *c_parent_path = NULL;
GtkTreeIter c_parent_iter;
/* check if child exists and is visible */
if (level->parent_elt_index >= 0)
{
c_parent_path =
gtk_tree_model_filter_elt_get_path (level->parent_level,
FILTER_LEVEL_PARENT_ELT (level),
filter->priv->virtual_root);
if (!c_parent_path)
return NULL;
}
else
{
if (filter->priv->virtual_root)
c_parent_path = gtk_tree_path_copy (filter->priv->virtual_root);
else
c_parent_path = NULL;
}
if (c_parent_path)
{
gtk_tree_model_get_iter (filter->priv->child_model,
&c_parent_iter,
c_parent_path);
len = gtk_tree_model_iter_n_children (filter->priv->child_model,
&c_parent_iter);
c_path = gtk_tree_path_copy (c_parent_path);
gtk_tree_path_free (c_parent_path);
}
else
{
len = gtk_tree_model_iter_n_children (filter->priv->child_model, NULL);
c_path = gtk_tree_path_new ();
}
gtk_tree_path_append_index (c_path, offset);
gtk_tree_model_get_iter (filter->priv->child_model, &c_iter, c_path);
gtk_tree_path_free (c_path);
if (offset >= len || !gtk_tree_model_filter_visible (filter, &c_iter))
return NULL;
return gtk_tree_model_filter_insert_elt_in_level (filter, &c_iter,
level, offset,
index);
}
/* Note that this function is never called from the row-deleted handler.
* This means that this function is only used for removing elements
* which are still present in the child model. As a result, we must
* take care to properly release the references the filter model has
* on the child model nodes.
*/
static void
gtk_tree_model_filter_remove_elt_from_level (GtkTreeModelFilter *filter,
FilterLevel *level,
FilterElt *elt)
{
FilterElt *parent;
FilterLevel *parent_level;
gint i, length, parent_elt_index, orig_level_ext_ref_count;
GtkTreeIter iter;
GtkTreePath *path = NULL;
gboolean emit_child_toggled = FALSE;
iter.stamp = filter->priv->stamp;
iter.user_data = level;
iter.user_data2 = elt;
path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), &iter);
parent_elt_index = level->parent_elt_index;
if (parent_elt_index >= 0)
parent = FILTER_LEVEL_PARENT_ELT (level);
else
parent = NULL;
parent_level = level->parent_level;
length = level->array->len;
/* We need to know about the level's ext ref count before removal
* of this node.
*/
orig_level_ext_ref_count = level->ext_ref_count;
/* first register the node to be invisible */
level->visible_nodes--;
elt->visible = FALSE;
/* we distinguish a couple of cases:
* - root level, length > 1: emit row-deleted and remove.
* - root level, length == 1: emit row-deleted and keep in cache.
* - level, length == 1: parent->ext_ref_count > 0: emit row-deleted
* and keep.
* - level, length > 1: emit row-deleted and remove.
* - else, remove level.
*
* if level != root level and visible nodes == 0, emit row-has-child-toggled.
*/
if (level != filter->priv->root
&& level->visible_nodes == 0
&& parent
&& parent->visible)
emit_child_toggled = TRUE;
if (length > 1)
{
FilterElt *tmp;
/* We emit row-deleted, and remove the node from the cache.
* If it has any children, these will be removed here as well.
*/
if (elt->children)
gtk_tree_model_filter_free_level (filter, elt->children, TRUE);
/* If the first node is being removed, transfer, the reference */
if (elt == &g_array_index (level->array, FilterElt, 0))
{
gtk_tree_model_filter_level_transfer_first_ref (filter, level,
0, 1);
}
while (elt->ext_ref_count > 0)
gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (filter),
&iter, TRUE, FALSE);
while (elt->ref_count > 0)
gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (filter),
&iter, FALSE, FALSE);
/* remove the node */
tmp = bsearch_elt_with_offset (level->array, elt->offset, &i);
if (tmp)
{
g_array_remove_index (level->array, i);
i--;
for (i = MAX (i, 0); i < level->array->len; i++)
{
/* NOTE: here we do *not* decrease offsets, because the node was
* not removed from the child model
*/
elt = &g_array_index (level->array, FilterElt, i);
if (elt->children)
elt->children->parent_elt_index = i;
}
}
gtk_tree_model_filter_increment_stamp (filter);
/* Only if the node is in the root level (parent == NULL) or
* the level is visible, a row-deleted signal is necessary.
*/
if (!parent || orig_level_ext_ref_count > 0)
gtk_tree_model_row_deleted (GTK_TREE_MODEL (filter), path);
}
else if ((length == 1 && parent && parent->ext_ref_count > 0)
|| (length == 1 && level == filter->priv->root))
{
/* We emit row-deleted, but keep the node in the cache and
* referenced. Its children will be removed.
*/
if (elt->children)
{
gtk_tree_model_filter_free_level (filter, elt->children, TRUE);
elt->children = NULL;
}
gtk_tree_model_filter_increment_stamp (filter);
/* Only if the node is in the root level (parent == NULL) or
* the level is visible, a row-deleted signal is necessary.
*/
if (!parent || orig_level_ext_ref_count > 0)
gtk_tree_model_row_deleted (GTK_TREE_MODEL (filter), path);
}
else
{
while (elt->ext_ref_count > 0)
gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (filter),
&iter, TRUE, FALSE);
while (elt->ref_count > 0)
gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (filter),
&iter, FALSE, FALSE);
/* Blow level away, including any child levels */
gtk_tree_model_filter_free_level (filter, level, TRUE);
gtk_tree_model_filter_increment_stamp (filter);
if (!parent || orig_level_ext_ref_count > 0)
gtk_tree_model_row_deleted (GTK_TREE_MODEL (filter), path);
}
gtk_tree_path_free (path);
if (emit_child_toggled && parent->ext_ref_count > 0)
{
GtkTreeIter piter;
GtkTreePath *ppath;
piter.stamp = filter->priv->stamp;
piter.user_data = parent_level;
piter.user_data2 = parent;
ppath = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), &piter);
gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (filter),
ppath, &piter);
gtk_tree_path_free (ppath);
}
}
/* This function is called after the given node has become visible.
* When the node has children, we should build the level and
* take a reference on the first child.
*/
static void
gtk_tree_model_filter_update_children (GtkTreeModelFilter *filter,
FilterLevel *level,
FilterElt *elt)
{
GtkTreeIter c_iter;
GtkTreeIter iter;
if (!elt->visible)
return;
iter.stamp = filter->priv->stamp;
iter.user_data = level;
iter.user_data2 = elt;
gtk_tree_model_filter_convert_iter_to_child_iter (filter, &c_iter, &iter);
if ((!level->parent_level || FILTER_LEVEL_PARENT_ELT (level)->ext_ref_count > 0) &&
gtk_tree_model_iter_has_child (filter->priv->child_model, &c_iter))
{
if (!elt->children)
gtk_tree_model_filter_build_level (filter, level,
FILTER_LEVEL_ELT_INDEX (level, elt),
FALSE);
if (elt->ext_ref_count > 0 && elt->children && elt->children->array->len)
{
GtkTreePath *path;
path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), &iter);
gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (filter),
path,
&iter);
if (path)
gtk_tree_path_free (path);
}
}
}
static FilterElt *
bsearch_elt_with_offset (GArray *array,
gint offset,
gint *index)
{
gint start, middle, end;
FilterElt *elt;
start = 0;
end = array->len;
if (array->len < 1)
return NULL;
if (start == end)
{
elt = &g_array_index (array, FilterElt, 0);
if (elt->offset == offset)
{
*index = 0;
return elt;
}
else
return NULL;
}
do
{
middle = (start + end) / 2;
elt = &g_array_index (array, FilterElt, middle);
if (elt->offset < offset)
start = middle + 1;
else if (elt->offset > offset)
end = middle;
else
break;
}
while (start != end);
if (elt->offset == offset)
{
*index = middle;
return elt;
}
return NULL;
}
/* Path is relative to the child model (this is on search on elt offset)
* but with the virtual root already removed if necesssary.
*/
static gboolean
find_elt_with_offset (GtkTreeModelFilter *filter,
GtkTreePath *path,
FilterLevel **level_,
FilterElt **elt_)
{
int i = 0;
FilterLevel *level;
FilterLevel *parent_level = NULL;
FilterElt *elt = NULL;
level = FILTER_LEVEL (filter->priv->root);
while (i < gtk_tree_path_get_depth (path))
{
int j;
if (!level)
return FALSE;
elt = bsearch_elt_with_offset (level->array,
gtk_tree_path_get_indices (path)[i],
&j);
if (!elt)
return FALSE;
parent_level = level;
level = elt->children;
i++;
}
if (level_)
*level_ = parent_level;
if (elt_)
*elt_ = elt;
return TRUE;
}
/* TreeModel signals */
static void
gtk_tree_model_filter_emit_row_inserted_for_path (GtkTreeModelFilter *filter,
GtkTreeModel *c_model,
GtkTreePath *c_path,
GtkTreeIter *c_iter)
{
FilterLevel *level;
FilterElt *elt;
GtkTreePath *path;
GtkTreeIter iter, children;
gboolean signals_emitted = FALSE;
if (!filter->priv->root)
{
/* The root level has not been exposed to the view yet, so we
* need to emit signals for any node that is being inserted.
*/
gtk_tree_model_filter_build_level (filter, NULL, -1, TRUE);
/* Check if the root level was built. Then child levels
* that matter have also been built (due to update_children,
* which triggers iter_n_children).
*/
if (filter->priv->root &&
FILTER_LEVEL (filter->priv->root)->visible_nodes)
signals_emitted = TRUE;
}
gtk_tree_model_filter_increment_stamp (filter);
/* We need to disallow to build new levels, because we are then pulling
* in a child in an invisible level. We only want to find path if it
* is in a visible level (and thus has a parent that is visible).
*/
path = gtk_real_tree_model_filter_convert_child_path_to_path (filter,
c_path,
FALSE,
TRUE);
if (!path)
/* parent is probably being filtered out */
return;
gtk_tree_model_filter_get_iter_full (GTK_TREE_MODEL (filter), &iter, path);
level = FILTER_LEVEL (iter.user_data);
elt = FILTER_ELT (iter.user_data2);
/* Make sure elt is visible. elt can already be visible in case
* it was pulled in above, so avoid increasing level->visible_nodes twice.
*/
if (!elt->visible)
{
elt->visible = TRUE;
level->visible_nodes++;
}
/* Check whether the node and all of its parents are visible */
if (gtk_tree_model_filter_elt_is_visible_in_target (level, elt))
{
/* visibility changed -- reget path */
gtk_tree_path_free (path);
path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), &iter);
if (!signals_emitted &&
(!level->parent_level || level->ext_ref_count > 0))
gtk_tree_model_row_inserted (GTK_TREE_MODEL (filter), path, &iter);
if (level->parent_level && FILTER_LEVEL_PARENT_ELT (level)->ext_ref_count > 0 &&
level->visible_nodes == 1)
{
/* We know that this is the first visible node in this level, so
* we need to emit row-has-child-toggled on the parent. This
* does not apply to the root level.
*/
gtk_tree_path_up (path);
gtk_tree_model_get_iter (GTK_TREE_MODEL (filter), &iter, path);
gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (filter),
path,
&iter);
}
if (!signals_emitted
&& gtk_tree_model_iter_children (c_model, &children, c_iter))
gtk_tree_model_filter_update_children (filter, level, elt);
}
gtk_tree_path_free (path);
}
static void
gtk_tree_model_filter_row_changed (GtkTreeModel *c_model,
GtkTreePath *c_path,
GtkTreeIter *c_iter,
gpointer data)
{
GtkTreeModelFilter *filter = GTK_TREE_MODEL_FILTER (data);
GtkTreeIter iter;
GtkTreeIter children;
GtkTreeIter real_c_iter;
GtkTreePath *path = NULL;
GtkTreePath *real_path = NULL;
FilterElt *elt;
FilterLevel *level;
gboolean requested_state;
gboolean current_state;
gboolean free_c_path = FALSE;
g_return_if_fail (c_path != NULL || c_iter != NULL);
if (!c_path)
{
c_path = gtk_tree_model_get_path (c_model, c_iter);
free_c_path = TRUE;
}
if (filter->priv->virtual_root)
real_path = gtk_tree_model_filter_remove_root (c_path,
filter->priv->virtual_root);
else
real_path = gtk_tree_path_copy (c_path);
if (c_iter)
real_c_iter = *c_iter;
else
gtk_tree_model_get_iter (c_model, &real_c_iter, c_path);
/* is this node above the virtual root? */
if (filter->priv->virtual_root &&
(gtk_tree_path_get_depth (filter->priv->virtual_root)
>= gtk_tree_path_get_depth (c_path)))
goto done;
/* what's the requested state? */
requested_state = gtk_tree_model_filter_visible (filter, &real_c_iter);
/* now, let's see whether the item is there */
path = gtk_real_tree_model_filter_convert_child_path_to_path (filter,
c_path,
FALSE,
FALSE);
if (path)
{
gtk_tree_model_filter_get_iter_full (GTK_TREE_MODEL (filter),
&iter, path);
current_state = FILTER_ELT (iter.user_data2)->visible;
}
else
current_state = FALSE;
if (current_state == FALSE && requested_state == FALSE)
/* no changes required */
goto done;
if (current_state == TRUE && requested_state == FALSE)
{
gtk_tree_model_filter_remove_elt_from_level (filter,
FILTER_LEVEL (iter.user_data),
FILTER_ELT (iter.user_data2));
if (real_path)
gtk_tree_model_filter_check_ancestors (filter, real_path);
goto done;
}
if (current_state == TRUE && requested_state == TRUE)
{
/* propagate the signal; also get a path taking only visible
* nodes into account.
*/
gtk_tree_path_free (path);
path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), &iter);
level = FILTER_LEVEL (iter.user_data);
elt = FILTER_ELT (iter.user_data2);
if (gtk_tree_model_filter_elt_is_visible_in_target (level, elt))
{
if (level->ext_ref_count > 0)
gtk_tree_model_row_changed (GTK_TREE_MODEL (filter), path, &iter);
/* and update the children */
if (gtk_tree_model_iter_children (c_model, &children, &real_c_iter))
gtk_tree_model_filter_update_children (filter, level, elt);
}
if (real_path)
gtk_tree_model_filter_check_ancestors (filter, real_path);
goto done;
}
/* only current == FALSE and requested == TRUE is left,
* pull in the child
*/
g_return_if_fail (current_state == FALSE && requested_state == TRUE);
if (real_path)
gtk_tree_model_filter_check_ancestors (filter, real_path);
gtk_tree_model_filter_emit_row_inserted_for_path (filter, c_model,
c_path, c_iter);
done:
if (path)
gtk_tree_path_free (path);
if (real_path)
gtk_tree_path_free (real_path);
if (free_c_path)
gtk_tree_path_free (c_path);
}
static void
gtk_tree_model_filter_row_inserted (GtkTreeModel *c_model,
GtkTreePath *c_path,
GtkTreeIter *c_iter,
gpointer data)
{
GtkTreeModelFilter *filter = GTK_TREE_MODEL_FILTER (data);
GtkTreePath *real_path = NULL;
GtkTreeIter real_c_iter;
FilterElt *elt = NULL;
FilterLevel *level = NULL;
FilterLevel *parent_level = NULL;
gint i = 0, offset;
gboolean free_c_path = FALSE;
gboolean emit_row_inserted = FALSE;
g_return_if_fail (c_path != NULL || c_iter != NULL);
if (!c_path)
{
c_path = gtk_tree_model_get_path (c_model, c_iter);
free_c_path = TRUE;
}
if (c_iter)
real_c_iter = *c_iter;
else
gtk_tree_model_get_iter (c_model, &real_c_iter, c_path);
/* the row has already been inserted. so we need to fixup the
* virtual root here first
*/
if (filter->priv->virtual_root)
{
if (gtk_tree_path_get_depth (filter->priv->virtual_root) >=
gtk_tree_path_get_depth (c_path))
{
gint level;
gint *v_indices, *c_indices;
gboolean common_prefix = TRUE;
level = gtk_tree_path_get_depth (c_path) - 1;
v_indices = gtk_tree_path_get_indices (filter->priv->virtual_root);
c_indices = gtk_tree_path_get_indices (c_path);
for (i = 0; i < level; i++)
if (v_indices[i] != c_indices[i])
{
common_prefix = FALSE;
break;
}
if (common_prefix && v_indices[level] >= c_indices[level])
(v_indices[level])++;
}
}
/* subtract virtual root if necessary */
if (filter->priv->virtual_root)
{
real_path = gtk_tree_model_filter_remove_root (c_path,
filter->priv->virtual_root);
/* not our child */
if (!real_path)
goto done;
}
else
real_path = gtk_tree_path_copy (c_path);
if (!filter->priv->root)
{
/* The root level has not been exposed to the view yet, so we
* need to emit signals for any node that is being inserted.
*/
gtk_tree_model_filter_build_level (filter, NULL, -1, TRUE);
/* Check if the root level was built. Then child levels
* that matter have also been built (due to update_children,
* which triggers iter_n_children).
*/
if (filter->priv->root)
{
emit_row_inserted = FALSE;
goto done;
}
}
if (gtk_tree_path_get_depth (real_path) - 1 >= 1)
{
gboolean found = FALSE;
GtkTreePath *parent = gtk_tree_path_copy (real_path);
gtk_tree_path_up (parent);
found = find_elt_with_offset (filter, parent, &parent_level, &elt);
gtk_tree_path_free (parent);
if (!found)
/* Parent is not in the cache and probably being filtered out */
goto done;
level = elt->children;
}
else
level = FILTER_LEVEL (filter->priv->root);
if (!level)
{
if (elt && elt->visible)
{
/* The level in which the new node should be inserted does not
* exist, but the parent, elt, does. If elt is visible, emit
* row-has-child-toggled.
*/
GtkTreePath *tmppath;
GtkTreeIter tmpiter;
tmpiter.stamp = filter->priv->stamp;
tmpiter.user_data = parent_level;
tmpiter.user_data2 = elt;
tmppath = gtk_tree_model_get_path (GTK_TREE_MODEL (filter),
&tmpiter);
if (tmppath)
{
gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (filter),
tmppath, &tmpiter);
gtk_tree_path_free (tmppath);
}
}
goto done;
}
/* let's try to insert the value */
offset = gtk_tree_path_get_indices (real_path)[gtk_tree_path_get_depth (real_path) - 1];
/* update the offsets, yes if we didn't insert the node above, there will
* be a gap here. This will be filled with the node (via fetch_child) when
* it becomes visible
*/
for (i = 0; i < level->array->len; i++)
{
FilterElt *e = &g_array_index (level->array, FilterElt, i);
if ((e->offset >= offset))
e->offset++;
}
/* only insert when visible */
if (gtk_tree_model_filter_visible (filter, &real_c_iter))
{
FilterElt *felt;
felt = gtk_tree_model_filter_insert_elt_in_level (filter,
&real_c_iter,
level, offset,
&i);
/* insert_elt_in_level defaults to FALSE */
felt->visible = TRUE;
level->visible_nodes++;
emit_row_inserted = TRUE;
}
done:
gtk_tree_model_filter_check_ancestors (filter, real_path);
if (emit_row_inserted)
gtk_tree_model_filter_emit_row_inserted_for_path (filter, c_model,
c_path, c_iter);
if (real_path)
gtk_tree_path_free (real_path);
if (free_c_path)
gtk_tree_path_free (c_path);
}
static void
gtk_tree_model_filter_row_has_child_toggled (GtkTreeModel *c_model,
GtkTreePath *c_path,
GtkTreeIter *c_iter,
gpointer data)
{
GtkTreeModelFilter *filter = GTK_TREE_MODEL_FILTER (data);
GtkTreePath *path;
GtkTreeIter iter;
FilterLevel *level;
FilterElt *elt;
gboolean requested_state;
g_return_if_fail (c_path != NULL && c_iter != NULL);
/* If we get row-has-child-toggled on the virtual root, and there is
* no root level; try to build it now.
*/
if (filter->priv->virtual_root && !filter->priv->root
&& !gtk_tree_path_compare (c_path, filter->priv->virtual_root))
{
gtk_tree_model_filter_build_level (filter, NULL, -1, TRUE);
return;
}
/* For all other levels, there is a chance that the visibility state
* of the parent has changed now.
*/
path = gtk_real_tree_model_filter_convert_child_path_to_path (filter,
c_path,
FALSE,
TRUE);
if (!path)
return;
gtk_tree_model_filter_get_iter_full (GTK_TREE_MODEL (data), &iter, path);
level = FILTER_LEVEL (iter.user_data);
elt = FILTER_ELT (iter.user_data2);
gtk_tree_path_free (path);
requested_state = gtk_tree_model_filter_visible (filter, c_iter);
if (!elt->visible && !requested_state)
{
/* The parent node currently is not visible and will not become
* visible, so we will not pass on the row-has-child-toggled event.
*/
return;
}
else if (elt->visible && !requested_state)
{
/* The node is no longer visible, so it has to be removed.
* _remove_elt_from_level() takes care of emitting row-has-child-toggled
* when required.
*/
gtk_tree_model_filter_remove_elt_from_level (filter, level, elt);
return;
}
else if (!elt->visible && requested_state)
{
elt->visible = TRUE;
level->visible_nodes++;
/* Only insert if the parent is visible in the target */
if (gtk_tree_model_filter_elt_is_visible_in_target (level, elt))
{
path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), &iter);
gtk_tree_model_row_inserted (GTK_TREE_MODEL (filter), path, &iter);
gtk_tree_path_free (path);
/* We do not update children now, because that will happen
* below.
*/
}
}
/* For the remaining possibility, elt->visible && requested_state
* no action is required.
*/
/* If this node is referenced and has children, build the level so we
* can monitor it for changes.
*/
if (elt->ref_count > 1 && !elt->children &&
gtk_tree_model_iter_has_child (c_model, c_iter))
gtk_tree_model_filter_build_level (filter, level,
FILTER_LEVEL_ELT_INDEX (level, elt),
FALSE);
/* get a path taking only visible nodes into account */
path = gtk_tree_model_get_path (GTK_TREE_MODEL (data), &iter);
gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (data), path, &iter);
gtk_tree_path_free (path);
}
static void
gtk_tree_model_filter_virtual_root_deleted (GtkTreeModelFilter *filter,
GtkTreePath *c_path)
{
gint i, nodes;
GtkTreePath *path;
FilterLevel *level = FILTER_LEVEL (filter->priv->root);
/* The virtual root (or one of its ancestors) has been deleted. This
* means that all content for our model is now gone. We deal with
* this by removing everything in the filter model: we just iterate
* over the root level and emit a row-deleted for each FilterElt.
* (FIXME: Should we emit row-deleted for child nodes as well? This
* has never been fully clear in TreeModel).
*/
/* We unref the path of the virtual root, up to and not including the
* deleted node which can no longer be unreffed.
*/
gtk_tree_model_filter_unref_path (filter, filter->priv->virtual_root,
gtk_tree_path_get_depth (c_path) - 1);
filter->priv->virtual_root_deleted = TRUE;
if (!level)
return;
nodes = level->visible_nodes;
/* We should not propagate the unref here. An unref for any of these
* nodes will fail, since the respective nodes in the child model are
* no longer there.
*/
gtk_tree_model_filter_free_level (filter, filter->priv->root, FALSE);
gtk_tree_model_filter_increment_stamp (filter);
path = gtk_tree_path_new ();
gtk_tree_path_append_index (path, 0);
for (i = 0; i < nodes; i++)
gtk_tree_model_row_deleted (GTK_TREE_MODEL (filter), path);
gtk_tree_path_free (path);
}
static void
gtk_tree_model_filter_adjust_virtual_root (GtkTreeModelFilter *filter,
GtkTreePath *c_path)
{
gint i;
gint level;
gint *v_indices, *c_indices;
gboolean common_prefix = TRUE;
level = gtk_tree_path_get_depth (c_path) - 1;
v_indices = gtk_tree_path_get_indices (filter->priv->virtual_root);
c_indices = gtk_tree_path_get_indices (c_path);
for (i = 0; i < level; i++)
if (v_indices[i] != c_indices[i])
{
common_prefix = FALSE;
break;
}
if (common_prefix && v_indices[level] > c_indices[level])
(v_indices[level])--;
}
static void
gtk_tree_model_filter_row_deleted_invisible_node (GtkTreeModelFilter *filter,
GtkTreePath *c_path)
{
int i;
int offset;
GtkTreePath *real_path;
FilterLevel *level;
FilterElt *elt;
/* The node deleted in the child model is not visible in the
* filter model. We will not emit a signal, just fixup the offsets
* of the other nodes.
*/
if (!filter->priv->root)
return;
level = FILTER_LEVEL (filter->priv->root);
/* subtract vroot if necessary */
if (filter->priv->virtual_root)
{
real_path = gtk_tree_model_filter_remove_root (c_path,
filter->priv->virtual_root);
/* we don't handle this */
if (!real_path)
return;
}
else
real_path = gtk_tree_path_copy (c_path);
if (gtk_tree_path_get_depth (real_path) - 1 >= 1)
{
gboolean found = FALSE;
GtkTreePath *parent = gtk_tree_path_copy (real_path);
gtk_tree_path_up (parent);
found = find_elt_with_offset (filter, parent, &level, &elt);
gtk_tree_path_free (parent);
if (!found)
{
/* parent is filtered out, so no level */
gtk_tree_path_free (real_path);
return;
}
level = elt->children;
}
offset = gtk_tree_path_get_indices (real_path)[gtk_tree_path_get_depth (real_path) - 1];
gtk_tree_path_free (real_path);
if (!level)
return;
/* decrease offset of all nodes following the deleted node */
for (i = 0; i < level->array->len; i++)
{
elt = &g_array_index (level->array, FilterElt, i);
if (elt->offset > offset)
elt->offset--;
if (elt->children)
elt->children->parent_elt_index = i;
}
}
static void
gtk_tree_model_filter_row_deleted (GtkTreeModel *c_model,
GtkTreePath *c_path,
gpointer data)
{
GtkTreeModelFilter *filter = GTK_TREE_MODEL_FILTER (data);
GtkTreePath *path;
GtkTreeIter iter;
FilterElt *elt;
FilterLevel *level, *parent_level = NULL;
gboolean emit_child_toggled = FALSE;
gboolean emit_row_deleted = FALSE;
gint offset;
gint i;
gint parent_elt_index = -1;
gint orig_level_ext_ref_count;
g_return_if_fail (c_path != NULL);
/* special case the deletion of an ancestor of the virtual root */
if (filter->priv->virtual_root &&
(gtk_tree_path_is_ancestor (c_path, filter->priv->virtual_root) ||
!gtk_tree_path_compare (c_path, filter->priv->virtual_root)))
{
gtk_tree_model_filter_virtual_root_deleted (filter, c_path);
return;
}
/* adjust the virtual root for the deleted row */
if (filter->priv->virtual_root &&
gtk_tree_path_get_depth (filter->priv->virtual_root) >=
gtk_tree_path_get_depth (c_path))
gtk_tree_model_filter_adjust_virtual_root (filter, c_path);
path = gtk_real_tree_model_filter_convert_child_path_to_path (filter,
c_path,
FALSE,
FALSE);
if (!path)
{
gtk_tree_model_filter_row_deleted_invisible_node (filter, c_path);
return;
}
/* a node was deleted, which was in our cache */
gtk_tree_model_filter_get_iter_full (GTK_TREE_MODEL (data), &iter, path);
level = FILTER_LEVEL (iter.user_data);
elt = FILTER_ELT (iter.user_data2);
offset = elt->offset;
orig_level_ext_ref_count = level->ext_ref_count;
if (elt->visible)
{
/* get a path taking only visible nodes into account */
gtk_tree_path_free (path);
path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), &iter);
level->visible_nodes--;
if (level->visible_nodes == 0)
{
emit_child_toggled = TRUE;
parent_level = level->parent_level;
parent_elt_index = level->parent_elt_index;
}
emit_row_deleted = TRUE;
}
/* Release the references on this node, without propagation because
* the node does not exist anymore in the child model. The filter
* model's references on the node in case of level->parent or use
* of a virtual root are automatically destroyed by the child model.
*/
while (elt->ext_ref_count > 0)
gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (data), &iter,
TRUE, FALSE);
while (elt->ref_count > 0)
gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (data), &iter,
FALSE, FALSE);
if (level->array->len == 1)
{
/* kill level */
gtk_tree_model_filter_free_level (filter, level, FALSE);
}
else
{
FilterElt *tmp;
gboolean is_first;
is_first = elt == &g_array_index (level->array, FilterElt, 0);
/* remove the row */
tmp = bsearch_elt_with_offset (level->array, elt->offset, &i);
offset = tmp->offset;
g_array_remove_index (level->array, i);
i--;
for (i = MAX (i, 0); i < level->array->len; i++)
{
elt = &g_array_index (level->array, FilterElt, i);
if (elt->offset > offset)
elt->offset--;
if (elt->children)
elt->children->parent_elt_index = i;
}
/* Take a reference on the new first node. The first node previously
* keeping this reference has been removed above.
*/
if (is_first)
{
GtkTreeIter f_iter;
f_iter.stamp = filter->priv->stamp;
f_iter.user_data = level;
f_iter.user_data2 = &(g_array_index (level->array, FilterElt, 0));
gtk_tree_model_filter_real_ref_node (GTK_TREE_MODEL (filter),
&f_iter, FALSE);
}
}
if (emit_row_deleted)
{
/* emit row_deleted */
gtk_tree_model_filter_increment_stamp (filter);
if (parent_elt_index == -1 || orig_level_ext_ref_count > 0)
gtk_tree_model_row_deleted (GTK_TREE_MODEL (data), path);
}
if (emit_child_toggled && parent_level)
{
GtkTreeIter iter2;
GtkTreePath *path2;
iter2.stamp = filter->priv->stamp;
iter2.user_data = parent_level;
iter2.user_data2 = &g_array_index (parent_level->array, FilterElt, parent_elt_index);
/* We set in_row_deleted to TRUE to avoid a level build triggered
* by row-has-child-toggled (parent model could call iter_has_child
* for example).
*/
filter->priv->in_row_deleted = TRUE;
path2 = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), &iter2);
gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (filter),
path2, &iter2);
gtk_tree_path_free (path2);
filter->priv->in_row_deleted = FALSE;
}
if (filter->priv->virtual_root)
{
GtkTreePath *real_path;
real_path = gtk_tree_model_filter_remove_root (c_path,
filter->priv->root);
if (real_path)
{
gtk_tree_model_filter_check_ancestors (filter, real_path);
gtk_tree_path_free (real_path);
}
}
else
gtk_tree_model_filter_check_ancestors (filter, c_path);
gtk_tree_path_free (path);
}
static void
gtk_tree_model_filter_rows_reordered (GtkTreeModel *c_model,
GtkTreePath *c_path,
GtkTreeIter *c_iter,
gint *new_order,
gpointer data)
{
FilterElt *elt;
FilterLevel *level;
GtkTreeModelFilter *filter = GTK_TREE_MODEL_FILTER (data);
GtkTreePath *path;
GtkTreeIter iter;
gint *tmp_array;
gint i, j, elt_count;
gint length;
GArray *new_array;
g_return_if_fail (new_order != NULL);
if (c_path == NULL || gtk_tree_path_get_depth (c_path) == 0)
{
length = gtk_tree_model_iter_n_children (c_model, NULL);
if (filter->priv->virtual_root)
{
gint new_pos = -1;
/* reorder root level of path */
for (i = 0; i < length; i++)
if (new_order[i] == gtk_tree_path_get_indices (filter->priv->virtual_root)[0])
new_pos = i;
if (new_pos < 0)
return;
gtk_tree_path_get_indices (filter->priv->virtual_root)[0] = new_pos;
return;
}
path = gtk_tree_path_new ();
level = FILTER_LEVEL (filter->priv->root);
}
else
{
GtkTreeIter child_iter;
/* virtual root anchor reordering */
if (filter->priv->virtual_root &&
gtk_tree_path_is_ancestor (c_path, filter->priv->virtual_root))
{
gint new_pos = -1;
gint length;
gint level;
GtkTreeIter real_c_iter;
level = gtk_tree_path_get_depth (c_path);
if (c_iter)
real_c_iter = *c_iter;
else
gtk_tree_model_get_iter (c_model, &real_c_iter, c_path);
length = gtk_tree_model_iter_n_children (c_model, &real_c_iter);
for (i = 0; i < length; i++)
if (new_order[i] == gtk_tree_path_get_indices (filter->priv->virtual_root)[level])
new_pos = i;
if (new_pos < 0)
return;
gtk_tree_path_get_indices (filter->priv->virtual_root)[level] = new_pos;
return;
}
path = gtk_real_tree_model_filter_convert_child_path_to_path (filter,
c_path,
FALSE,
FALSE);
if (!path && filter->priv->virtual_root &&
gtk_tree_path_compare (c_path, filter->priv->virtual_root))
return;
if (!path && !filter->priv->virtual_root)
return;
if (!path)
{
/* root level mode */
if (!c_iter)
gtk_tree_model_get_iter (c_model, c_iter, c_path);
length = gtk_tree_model_iter_n_children (c_model, c_iter);
path = gtk_tree_path_new ();
level = FILTER_LEVEL (filter->priv->root);
}
else
{
gtk_tree_model_filter_get_iter_full (GTK_TREE_MODEL (data),
&iter, path);
level = FILTER_LEVEL (iter.user_data);
elt = FILTER_ELT (iter.user_data2);
if (!elt->children)
{
gtk_tree_path_free (path);
return;
}
level = elt->children;
gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (filter), &child_iter, &iter);
length = gtk_tree_model_iter_n_children (c_model, &child_iter);
}
}
if (!level || level->array->len < 1)
{
gtk_tree_path_free (path);
return;
}
/* NOTE: we do not bail out here if level->array->len < 2 like
* GtkTreeModelSort does. This because we do some special tricky
* reordering.
*/
/* construct a new array */
new_array = g_array_sized_new (FALSE, FALSE, sizeof (FilterElt),
level->array->len);
tmp_array = g_new (gint, level->array->len);
for (i = 0, elt_count = 0; i < length; i++)
{
FilterElt *e = NULL;
gint old_offset = -1;
for (j = 0; j < level->array->len; j++)
if (g_array_index (level->array, FilterElt, j).offset == new_order[i])
{
e = &g_array_index (level->array, FilterElt, j);
old_offset = j;
break;
}
if (!e)
continue;
tmp_array[elt_count] = old_offset;
g_array_append_val (new_array, *e);
g_array_index (new_array, FilterElt, elt_count).offset = i;
elt_count++;
}
g_array_free (level->array, TRUE);
level->array = new_array;
/* fix up stuff */
for (i = 0; i < level->array->len; i++)
{
FilterElt *e = &g_array_index (level->array, FilterElt, i);
if (e->children)
e->children->parent_elt_index = i;
}
/* Transfer the reference from the old item at position 0 to the
* new item at position 0.
*/
if (tmp_array[0] != 0)
gtk_tree_model_filter_level_transfer_first_ref (filter,
level,
tmp_array[0], 0);
/* emit rows_reordered */
if (!gtk_tree_path_get_indices (path))
gtk_tree_model_rows_reordered (GTK_TREE_MODEL (data), path, NULL,
tmp_array);
else
{
/* get a path taking only visible nodes into account */
gtk_tree_path_free (path);
path = gtk_tree_model_get_path (GTK_TREE_MODEL (data), &iter);
gtk_tree_model_rows_reordered (GTK_TREE_MODEL (data), path, &iter,
tmp_array);
}
/* done */
g_free (tmp_array);
gtk_tree_path_free (path);
}
/* TreeModelIface implementation */
static GtkTreeModelFlags
gtk_tree_model_filter_get_flags (GtkTreeModel *model)
{
GtkTreeModelFlags flags;
g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), 0);
g_return_val_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->child_model != NULL, 0);
flags = gtk_tree_model_get_flags (GTK_TREE_MODEL_FILTER (model)->priv->child_model);
if ((flags & GTK_TREE_MODEL_LIST_ONLY) == GTK_TREE_MODEL_LIST_ONLY)
return GTK_TREE_MODEL_LIST_ONLY;
return 0;
}
static gint
gtk_tree_model_filter_get_n_columns (GtkTreeModel *model)
{
GtkTreeModelFilter *filter = (GtkTreeModelFilter *)model;
g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), 0);
g_return_val_if_fail (filter->priv->child_model != NULL, 0);
if (filter->priv->child_model == NULL)
return 0;
/* so we can't modify the modify func after this ... */
filter->priv->modify_func_set = TRUE;
if (filter->priv->modify_n_columns > 0)
return filter->priv->modify_n_columns;
return gtk_tree_model_get_n_columns (filter->priv->child_model);
}
static GType
gtk_tree_model_filter_get_column_type (GtkTreeModel *model,
gint index)
{
GtkTreeModelFilter *filter = (GtkTreeModelFilter *)model;
g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), G_TYPE_INVALID);
g_return_val_if_fail (filter->priv->child_model != NULL, G_TYPE_INVALID);
/* so we can't modify the modify func after this ... */
filter->priv->modify_func_set = TRUE;
if (filter->priv->modify_types)
{
g_return_val_if_fail (index < filter->priv->modify_n_columns, G_TYPE_INVALID);
return filter->priv->modify_types[index];
}
return gtk_tree_model_get_column_type (filter->priv->child_model, index);
}
/* A special case of _get_iter; this function can also get iters which
* are not visible. These iters should ONLY be passed internally, never
* pass those along with a signal emission.
*/
static gboolean
gtk_tree_model_filter_get_iter_full (GtkTreeModel *model,
GtkTreeIter *iter,
GtkTreePath *path)
{
GtkTreeModelFilter *filter = (GtkTreeModelFilter *)model;
gint *indices;
FilterLevel *level;
FilterElt *elt;
gint depth, i;
g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), FALSE);
g_return_val_if_fail (filter->priv->child_model != NULL, FALSE);
indices = gtk_tree_path_get_indices (path);
if (filter->priv->root == NULL)
gtk_tree_model_filter_build_level (filter, NULL, -1, FALSE);
level = FILTER_LEVEL (filter->priv->root);
depth = gtk_tree_path_get_depth (path);
if (!depth)
{
iter->stamp = 0;
return FALSE;
}
for (i = 0; i < depth - 1; i++)
{
if (!level || indices[i] >= level->array->len)
{
iter->stamp = 0;
return FALSE;
}
elt = gtk_tree_model_filter_get_nth (filter, level, indices[i]);
if (!elt->children)
gtk_tree_model_filter_build_level (filter, level,
FILTER_LEVEL_ELT_INDEX (level, elt),
FALSE);
level = elt->children;
}
if (!level || indices[i] >= level->array->len)
{
iter->stamp = 0;
return FALSE;
}
iter->stamp = filter->priv->stamp;
iter->user_data = level;
elt = gtk_tree_model_filter_get_nth (filter, level, indices[depth - 1]);
iter->user_data2 = elt;
return TRUE;
}
static gboolean
gtk_tree_model_filter_get_iter (GtkTreeModel *model,
GtkTreeIter *iter,
GtkTreePath *path)
{
GtkTreeModelFilter *filter = (GtkTreeModelFilter *)model;
gint *indices;
FilterLevel *level;
FilterElt *elt;
gint depth, i;
g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), FALSE);
g_return_val_if_fail (filter->priv->child_model != NULL, FALSE);
indices = gtk_tree_path_get_indices (path);
if (filter->priv->root == NULL)
gtk_tree_model_filter_build_level (filter, NULL, -1, FALSE);
level = FILTER_LEVEL (filter->priv->root);
depth = gtk_tree_path_get_depth (path);
if (!depth)
{
iter->stamp = 0;
return FALSE;
}
for (i = 0; i < depth - 1; i++)
{
if (!level || indices[i] >= level->visible_nodes)
{
iter->stamp = 0;
return FALSE;
}
elt = gtk_tree_model_filter_get_nth_visible (filter, level, indices[i]);
if (!elt->children)
gtk_tree_model_filter_build_level (filter, level,
FILTER_LEVEL_ELT_INDEX (level, elt),
FALSE);
level = elt->children;
}
if (!level || indices[i] >= level->visible_nodes)
{
iter->stamp = 0;
return FALSE;
}
iter->stamp = filter->priv->stamp;
iter->user_data = level;
elt = gtk_tree_model_filter_get_nth_visible (filter, level,
indices[depth - 1]);
iter->user_data2 = elt;
return TRUE;
}
static GtkTreePath *
gtk_tree_model_filter_get_path (GtkTreeModel *model,
GtkTreeIter *iter)
{
GtkTreePath *retval;
FilterLevel *level;
FilterElt *elt;
gint elt_index;
g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), NULL);
g_return_val_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->child_model != NULL, NULL);
g_return_val_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->stamp == iter->stamp, NULL);
level = iter->user_data;
elt = iter->user_data2;
elt_index = FILTER_LEVEL_ELT_INDEX (level, elt);
if (!elt->visible)
return NULL;
retval = gtk_tree_path_new ();
while (level)
{
int i = 0, index = 0;
while (i < elt_index)
{
if (g_array_index (level->array, FilterElt, i).visible)
index++;
i++;
g_assert (i < level->array->len);
}
gtk_tree_path_prepend_index (retval, index);
elt_index = level->parent_elt_index;
level = level->parent_level;
}
return retval;
}
static void
gtk_tree_model_filter_real_modify (GtkTreeModelFilter *self,
GtkTreeModel *child_model,
GtkTreeIter *iter,
GValue *value,
gint column)
{
if (self->priv->modify_func)
{
g_return_if_fail (column < self->priv->modify_n_columns);
g_value_init (value, self->priv->modify_types[column]);
self->priv->modify_func (GTK_TREE_MODEL (self),
iter,
value,
column,
self->priv->modify_data);
}
else
{
GtkTreeIter child_iter;
gtk_tree_model_filter_convert_iter_to_child_iter (
GTK_TREE_MODEL_FILTER (self), &child_iter, iter);
gtk_tree_model_get_value (child_model,
&child_iter, column, value);
}
}
static void
gtk_tree_model_filter_get_value (GtkTreeModel *model,
GtkTreeIter *iter,
gint column,
GValue *value)
{
GtkTreeModelFilter *filter = GTK_TREE_MODEL_FILTER (model);
g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (model));
g_return_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->child_model != NULL);
g_return_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->stamp == iter->stamp);
GTK_TREE_MODEL_FILTER_GET_CLASS (model)->modify (filter,
filter->priv->child_model, iter, value, column);
}
static gboolean
gtk_tree_model_filter_iter_next (GtkTreeModel *model,
GtkTreeIter *iter)
{
int i;
FilterLevel *level;
FilterElt *elt;
g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), FALSE);
g_return_val_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->child_model != NULL, FALSE);
g_return_val_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->stamp == iter->stamp, FALSE);
level = iter->user_data;
elt = iter->user_data2;
i = elt - FILTER_ELT (level->array->data);
while (i < level->array->len - 1)
{
i++;
elt++;
if (elt->visible)
{
iter->user_data2 = elt;
return TRUE;
}
}
/* no next visible iter */
iter->stamp = 0;
return FALSE;
}
static gboolean
gtk_tree_model_filter_iter_previous (GtkTreeModel *model,
GtkTreeIter *iter)
{
int i;
FilterLevel *level;
FilterElt *elt;
g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), FALSE);
g_return_val_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->child_model != NULL, FALSE);
g_return_val_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->stamp == iter->stamp, FALSE);
level = iter->user_data;
elt = iter->user_data2;
i = elt - FILTER_ELT (level->array->data);
while (i > 0)
{
i--;
elt--;
if (elt->visible)
{
iter->user_data2 = elt;
return TRUE;
}
}
/* no previous visible iter */
iter->stamp = 0;
return FALSE;
}
static gboolean
gtk_tree_model_filter_iter_children (GtkTreeModel *model,
GtkTreeIter *iter,
GtkTreeIter *parent)
{
GtkTreeModelFilter *filter = (GtkTreeModelFilter *)model;
FilterLevel *level;
iter->stamp = 0;
g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), FALSE);
g_return_val_if_fail (filter->priv->child_model != NULL, FALSE);
if (parent)
g_return_val_if_fail (filter->priv->stamp == parent->stamp, FALSE);
if (!parent)
{
int i = 0;
if (!filter->priv->root)
gtk_tree_model_filter_build_level (filter, NULL, -1, FALSE);
if (!filter->priv->root)
return FALSE;
level = filter->priv->root;
if (!level->visible_nodes)
return FALSE;
iter->stamp = filter->priv->stamp;
iter->user_data = level;
while (i < level->array->len)
{
if (!g_array_index (level->array, FilterElt, i).visible)
{
i++;
continue;
}
iter->user_data2 = &g_array_index (level->array, FilterElt, i);
return TRUE;
}
iter->stamp = 0;
return FALSE;
}
else
{
int i = 0;
FilterElt *elt;
elt = FILTER_ELT (parent->user_data2);
if (elt->children == NULL)
gtk_tree_model_filter_build_level (filter,
FILTER_LEVEL (parent->user_data),
FILTER_LEVEL_ELT_INDEX (parent->user_data, elt),
FALSE);
if (elt->children == NULL)
return FALSE;
if (elt->children->visible_nodes <= 0)
return FALSE;
iter->stamp = filter->priv->stamp;
iter->user_data = elt->children;
level = FILTER_LEVEL (iter->user_data);
while (i < level->array->len)
{
if (!g_array_index (level->array, FilterElt, i).visible)
{
i++;
continue;
}
iter->user_data2 = &g_array_index (level->array, FilterElt, i);
return TRUE;
}
iter->stamp = 0;
return FALSE;
}
iter->stamp = 0;
return FALSE;
}
static gboolean
gtk_tree_model_filter_iter_has_child (GtkTreeModel *model,
GtkTreeIter *iter)
{
GtkTreeIter child_iter;
GtkTreeModelFilter *filter = (GtkTreeModelFilter *)model;
FilterElt *elt;
g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), FALSE);
g_return_val_if_fail (filter->priv->child_model != NULL, FALSE);
g_return_val_if_fail (filter->priv->stamp == iter->stamp, FALSE);
filter = GTK_TREE_MODEL_FILTER (model);
gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_iter, iter);
elt = FILTER_ELT (iter->user_data2);
if (!elt->visible)
return FALSE;
/* we need to build the level to check if not all children are filtered
* out
*/
if (!elt->children
&& gtk_tree_model_iter_has_child (filter->priv->child_model, &child_iter))
gtk_tree_model_filter_build_level (filter, FILTER_LEVEL (iter->user_data),
FILTER_LEVEL_ELT_INDEX (iter->user_data, elt),
FALSE);
if (elt->children && elt->children->visible_nodes > 0)
return TRUE;
return FALSE;
}
static gint
gtk_tree_model_filter_iter_n_children (GtkTreeModel *model,
GtkTreeIter *iter)
{
GtkTreeIter child_iter;
GtkTreeModelFilter *filter = (GtkTreeModelFilter *)model;
FilterElt *elt;
g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), 0);
g_return_val_if_fail (filter->priv->child_model != NULL, 0);
if (iter)
g_return_val_if_fail (filter->priv->stamp == iter->stamp, 0);
if (!iter)
{
if (!filter->priv->root)
gtk_tree_model_filter_build_level (filter, NULL, -1, FALSE);
if (filter->priv->root)
return FILTER_LEVEL (filter->priv->root)->visible_nodes;
return 0;
}
elt = FILTER_ELT (iter->user_data2);
if (!elt->visible)
return 0;
gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_iter, iter);
if (!elt->children &&
gtk_tree_model_iter_has_child (filter->priv->child_model, &child_iter))
gtk_tree_model_filter_build_level (filter,
FILTER_LEVEL (iter->user_data),
FILTER_LEVEL_ELT_INDEX (iter->user_data, elt),
FALSE);
if (elt->children)
return elt->children->visible_nodes;
return 0;
}
static gboolean
gtk_tree_model_filter_iter_nth_child (GtkTreeModel *model,
GtkTreeIter *iter,
GtkTreeIter *parent,
gint n)
{
FilterElt *elt;
FilterLevel *level;
GtkTreeIter children;
g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), FALSE);
if (parent)
g_return_val_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->stamp == parent->stamp, FALSE);
/* use this instead of has_child to force us to build the level, if needed */
if (gtk_tree_model_filter_iter_children (model, &children, parent) == FALSE)
{
iter->stamp = 0;
return FALSE;
}
level = children.user_data;
elt = FILTER_ELT (level->array->data);
if (n >= level->visible_nodes)
{
iter->stamp = 0;
return FALSE;
}
elt = gtk_tree_model_filter_get_nth_visible (GTK_TREE_MODEL_FILTER (model),
level, n);
iter->stamp = GTK_TREE_MODEL_FILTER (model)->priv->stamp;
iter->user_data = level;
iter->user_data2 = elt;
return TRUE;
}
static gboolean
gtk_tree_model_filter_iter_parent (GtkTreeModel *model,
GtkTreeIter *iter,
GtkTreeIter *child)
{
FilterLevel *level;
iter->stamp = 0;
g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), FALSE);
g_return_val_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->child_model != NULL, FALSE);
g_return_val_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->stamp == child->stamp, FALSE);
level = child->user_data;
if (level->parent_level)
{
iter->stamp = GTK_TREE_MODEL_FILTER (model)->priv->stamp;
iter->user_data = level->parent_level;
iter->user_data2 = FILTER_LEVEL_PARENT_ELT (level);
return TRUE;
}
return FALSE;
}
static void
gtk_tree_model_filter_ref_node (GtkTreeModel *model,
GtkTreeIter *iter)
{
gtk_tree_model_filter_real_ref_node (model, iter, TRUE);
}
static void
gtk_tree_model_filter_real_ref_node (GtkTreeModel *model,
GtkTreeIter *iter,
gboolean external)
{
GtkTreeModelFilter *filter = (GtkTreeModelFilter *)model;
GtkTreeIter child_iter;
FilterLevel *level;
FilterElt *elt;
g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (model));
g_return_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->child_model != NULL);
g_return_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->stamp == iter->stamp);
gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_iter, iter);
gtk_tree_model_ref_node (filter->priv->child_model, &child_iter);
level = iter->user_data;
elt = iter->user_data2;
elt->ref_count++;
level->ref_count++;
if (external)
{
elt->ext_ref_count++;
level->ext_ref_count++;
if (level->ext_ref_count == 1)
{
FilterLevel *parent_level = level->parent_level;
gint parent_elt_index = level->parent_elt_index;
/* we were at zero -- time to decrease the zero_ref_count val */
while (parent_level)
{
g_array_index (parent_level->array, FilterElt, parent_elt_index).zero_ref_count--;
parent_elt_index = parent_level->parent_elt_index;
parent_level = parent_level->parent_level;
}
if (filter->priv->root != level)
filter->priv->zero_ref_count--;
}
}
}
static void
gtk_tree_model_filter_unref_node (GtkTreeModel *model,
GtkTreeIter *iter)
{
gtk_tree_model_filter_real_unref_node (model, iter, TRUE, TRUE);
}
static void
gtk_tree_model_filter_real_unref_node (GtkTreeModel *model,
GtkTreeIter *iter,
gboolean external,
gboolean propagate_unref)
{
GtkTreeModelFilter *filter = (GtkTreeModelFilter *)model;
FilterLevel *level;
FilterElt *elt;
g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (model));
g_return_if_fail (filter->priv->child_model != NULL);
g_return_if_fail (filter->priv->stamp == iter->stamp);
if (propagate_unref)
{
GtkTreeIter child_iter;
gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_iter, iter);
gtk_tree_model_unref_node (filter->priv->child_model, &child_iter);
}
level = iter->user_data;
elt = iter->user_data2;
g_return_if_fail (elt->ref_count > 0);
elt->ref_count--;
level->ref_count--;
if (external)
{
elt->ext_ref_count--;
level->ext_ref_count--;
if (level->ext_ref_count == 0)
{
FilterLevel *parent_level = level->parent_level;
gint parent_elt_index = level->parent_elt_index;
/* we are at zero -- time to increase the zero_ref_count val */
while (parent_level)
{
g_array_index (parent_level->array, FilterElt, parent_elt_index).zero_ref_count++;
parent_elt_index = parent_level->parent_elt_index;
parent_level = parent_level->parent_level;
}
if (filter->priv->root != level)
filter->priv->zero_ref_count++;
}
}
}
/* TreeDragSource interface implementation */
static gboolean
gtk_tree_model_filter_row_draggable (GtkTreeDragSource *drag_source,
GtkTreePath *path)
{
GtkTreeModelFilter *tree_model_filter = (GtkTreeModelFilter *)drag_source;
GtkTreePath *child_path;
gboolean draggable;
g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (drag_source), FALSE);
g_return_val_if_fail (path != NULL, FALSE);
child_path = gtk_tree_model_filter_convert_path_to_child_path (tree_model_filter, path);
draggable = gtk_tree_drag_source_row_draggable (GTK_TREE_DRAG_SOURCE (tree_model_filter->priv->child_model), child_path);
gtk_tree_path_free (child_path);
return draggable;
}
static gboolean
gtk_tree_model_filter_drag_data_get (GtkTreeDragSource *drag_source,
GtkTreePath *path,
GtkSelectionData *selection_data)
{
GtkTreeModelFilter *tree_model_filter = (GtkTreeModelFilter *)drag_source;
GtkTreePath *child_path;
gboolean gotten;
g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (drag_source), FALSE);
g_return_val_if_fail (path != NULL, FALSE);
child_path = gtk_tree_model_filter_convert_path_to_child_path (tree_model_filter, path);
gotten = gtk_tree_drag_source_drag_data_get (GTK_TREE_DRAG_SOURCE (tree_model_filter->priv->child_model), child_path, selection_data);
gtk_tree_path_free (child_path);
return gotten;
}
static gboolean
gtk_tree_model_filter_drag_data_delete (GtkTreeDragSource *drag_source,
GtkTreePath *path)
{
GtkTreeModelFilter *tree_model_filter = (GtkTreeModelFilter *)drag_source;
GtkTreePath *child_path;
gboolean deleted;
g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (drag_source), FALSE);
g_return_val_if_fail (path != NULL, FALSE);
child_path = gtk_tree_model_filter_convert_path_to_child_path (tree_model_filter, path);
deleted = gtk_tree_drag_source_drag_data_delete (GTK_TREE_DRAG_SOURCE (tree_model_filter->priv->child_model), child_path);
gtk_tree_path_free (child_path);
return deleted;
}
/* bits and pieces */
static void
gtk_tree_model_filter_set_model (GtkTreeModelFilter *filter,
GtkTreeModel *child_model)
{
g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (filter));
if (filter->priv->child_model)
{
g_signal_handler_disconnect (filter->priv->child_model,
filter->priv->changed_id);
g_signal_handler_disconnect (filter->priv->child_model,
filter->priv->inserted_id);
g_signal_handler_disconnect (filter->priv->child_model,
filter->priv->has_child_toggled_id);
g_signal_handler_disconnect (filter->priv->child_model,
filter->priv->deleted_id);
g_signal_handler_disconnect (filter->priv->child_model,
filter->priv->reordered_id);
/* reset our state */
if (filter->priv->root)
gtk_tree_model_filter_free_level (filter, filter->priv->root, TRUE);
filter->priv->root = NULL;
g_object_unref (filter->priv->child_model);
filter->priv->visible_column = -1;
/* FIXME: do we need to destroy more here? */
}
filter->priv->child_model = child_model;
if (child_model)
{
g_object_ref (filter->priv->child_model);
filter->priv->changed_id =
g_signal_connect (child_model, "row-changed",
G_CALLBACK (gtk_tree_model_filter_row_changed),
filter);
filter->priv->inserted_id =
g_signal_connect (child_model, "row-inserted",
G_CALLBACK (gtk_tree_model_filter_row_inserted),
filter);
filter->priv->has_child_toggled_id =
g_signal_connect (child_model, "row-has-child-toggled",
G_CALLBACK (gtk_tree_model_filter_row_has_child_toggled),
filter);
filter->priv->deleted_id =
g_signal_connect (child_model, "row-deleted",
G_CALLBACK (gtk_tree_model_filter_row_deleted),
filter);
filter->priv->reordered_id =
g_signal_connect (child_model, "rows-reordered",
G_CALLBACK (gtk_tree_model_filter_rows_reordered),
filter);
filter->priv->child_flags = gtk_tree_model_get_flags (child_model);
filter->priv->stamp = g_random_int ();
}
}
static void
gtk_tree_model_filter_ref_path (GtkTreeModelFilter *filter,
GtkTreePath *path)
{
int len;
GtkTreePath *p;
len = gtk_tree_path_get_depth (path);
p = gtk_tree_path_copy (path);
while (len--)
{
GtkTreeIter iter;
gtk_tree_model_get_iter (GTK_TREE_MODEL (filter->priv->child_model), &iter, p);
gtk_tree_model_ref_node (GTK_TREE_MODEL (filter->priv->child_model), &iter);
gtk_tree_path_up (p);
}
gtk_tree_path_free (p);
}
static void
gtk_tree_model_filter_unref_path (GtkTreeModelFilter *filter,
GtkTreePath *path,
int depth)
{
int len;
GtkTreePath *p;
if (depth != -1)
len = depth;
else
len = gtk_tree_path_get_depth (path);
p = gtk_tree_path_copy (path);
while (len--)
{
GtkTreeIter iter;
gtk_tree_model_get_iter (GTK_TREE_MODEL (filter->priv->child_model), &iter, p);
gtk_tree_model_unref_node (GTK_TREE_MODEL (filter->priv->child_model), &iter);
gtk_tree_path_up (p);
}
gtk_tree_path_free (p);
}
static void
gtk_tree_model_filter_set_root (GtkTreeModelFilter *filter,
GtkTreePath *root)
{
g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (filter));
if (!root)
filter->priv->virtual_root = NULL;
else
filter->priv->virtual_root = gtk_tree_path_copy (root);
}
/* public API */
/**
* gtk_tree_model_filter_new:
* @child_model: A #GtkTreeModel.
* @root: (allow-none): A #GtkTreePath or %NULL.
*
* Creates a new #GtkTreeModel, with @child_model as the child_model
* and @root as the virtual root.
*
* Return value: (transfer full): A new #GtkTreeModel.
*
* Since: 2.4
*/
GtkTreeModel *
gtk_tree_model_filter_new (GtkTreeModel *child_model,
GtkTreePath *root)
{
GtkTreeModel *retval;
GtkTreeModelFilter *filter;
g_return_val_if_fail (GTK_IS_TREE_MODEL (child_model), NULL);
retval = g_object_new (GTK_TYPE_TREE_MODEL_FILTER,
"child-model", child_model,
"virtual-root", root,
NULL);
filter = GTK_TREE_MODEL_FILTER (retval);
if (filter->priv->virtual_root)
{
gtk_tree_model_filter_ref_path (filter, filter->priv->virtual_root);
filter->priv->virtual_root_deleted = FALSE;
}
return retval;
}
/**
* gtk_tree_model_filter_get_model:
* @filter: A #GtkTreeModelFilter.
*
* Returns a pointer to the child model of @filter.
*
* Return value: (transfer none): A pointer to a #GtkTreeModel.
*
* Since: 2.4
*/
GtkTreeModel *
gtk_tree_model_filter_get_model (GtkTreeModelFilter *filter)
{
g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (filter), NULL);
return filter->priv->child_model;
}
/**
* gtk_tree_model_filter_set_visible_func:
* @filter: A #GtkTreeModelFilter.
* @func: A #GtkTreeModelFilterVisibleFunc, the visible function.
* @data: (allow-none): User data to pass to the visible function, or %NULL.
* @destroy: (allow-none): Destroy notifier of @data, or %NULL.
*
* Sets the visible function used when filtering the @filter to be @func. The
* function should return %TRUE if the given row should be visible and
* %FALSE otherwise.
*
* If the condition calculated by the function changes over time (e.g. because
* it depends on some global parameters), you must call
* gtk_tree_model_filter_refilter() to keep the visibility information of
* the model uptodate.
*
* Note that @func is called whenever a row is inserted, when it may still be
* empty. The visible function should therefore take special care of empty
* rows, like in the example below.
*
* <informalexample><programlisting>
* static gboolean
* visible_func (GtkTreeModel *model,
* GtkTreeIter *iter,
* gpointer data)
* {
* /&ast; Visible if row is non-empty and first column is "HI" &ast;/
* gchar *str;
* gboolean visible = FALSE;
*
* gtk_tree_model_get (model, iter, 0, &str, -1);
* if (str && strcmp (str, "HI") == 0)
* visible = TRUE;
* g_free (str);
*
* return visible;
* }
* </programlisting></informalexample>
*
* Since: 2.4
*/
void
gtk_tree_model_filter_set_visible_func (GtkTreeModelFilter *filter,
GtkTreeModelFilterVisibleFunc func,
gpointer data,
GDestroyNotify destroy)
{
g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (filter));
g_return_if_fail (func != NULL);
g_return_if_fail (filter->priv->visible_method_set == FALSE);
filter->priv->visible_func = func;
filter->priv->visible_data = data;
filter->priv->visible_destroy = destroy;
filter->priv->visible_method_set = TRUE;
}
/**
* gtk_tree_model_filter_set_modify_func:
* @filter: A #GtkTreeModelFilter.
* @n_columns: The number of columns in the filter model.
* @types: (array length=n_columns): The #GType<!-- -->s of the columns.
* @func: A #GtkTreeModelFilterModifyFunc
* @data: (allow-none): User data to pass to the modify function, or %NULL.
* @destroy: (allow-none): Destroy notifier of @data, or %NULL.
*
* With the @n_columns and @types parameters, you give an array of column
* types for this model (which will be exposed to the parent model/view).
* The @func, @data and @destroy parameters are for specifying the modify
* function. The modify function will get called for <emphasis>each</emphasis>
* data access, the goal of the modify function is to return the data which
* should be displayed at the location specified using the parameters of the
* modify function.
*
* Since: 2.4
*/
void
gtk_tree_model_filter_set_modify_func (GtkTreeModelFilter *filter,
gint n_columns,
GType *types,
GtkTreeModelFilterModifyFunc func,
gpointer data,
GDestroyNotify destroy)
{
g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (filter));
g_return_if_fail (func != NULL);
g_return_if_fail (filter->priv->modify_func_set == FALSE);
if (filter->priv->modify_destroy)
{
GDestroyNotify d = filter->priv->modify_destroy;
filter->priv->modify_destroy = NULL;
d (filter->priv->modify_data);
}
filter->priv->modify_n_columns = n_columns;
filter->priv->modify_types = g_new0 (GType, n_columns);
memcpy (filter->priv->modify_types, types, sizeof (GType) * n_columns);
filter->priv->modify_func = func;
filter->priv->modify_data = data;
filter->priv->modify_destroy = destroy;
filter->priv->modify_func_set = TRUE;
}
/**
* gtk_tree_model_filter_set_visible_column:
* @filter: A #GtkTreeModelFilter.
* @column: A #gint which is the column containing the visible information.
*
* Sets @column of the child_model to be the column where @filter should
* look for visibility information. @columns should be a column of type
* %G_TYPE_BOOLEAN, where %TRUE means that a row is visible, and %FALSE
* if not.
*
* Since: 2.4
*/
void
gtk_tree_model_filter_set_visible_column (GtkTreeModelFilter *filter,
gint column)
{
g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (filter));
g_return_if_fail (column >= 0);
g_return_if_fail (filter->priv->visible_method_set == FALSE);
filter->priv->visible_column = column;
filter->priv->visible_method_set = TRUE;
}
/* conversion */
/**
* gtk_tree_model_filter_convert_child_iter_to_iter:
* @filter: A #GtkTreeModelFilter.
* @filter_iter: (out): An uninitialized #GtkTreeIter.
* @child_iter: A valid #GtkTreeIter pointing to a row on the child model.
*
* Sets @filter_iter to point to the row in @filter that corresponds to the
* row pointed at by @child_iter. If @filter_iter was not set, %FALSE is
* returned.
*
* Return value: %TRUE, if @filter_iter was set, i.e. if @child_iter is a
* valid iterator pointing to a visible row in child model.
*
* Since: 2.4
*/
gboolean
gtk_tree_model_filter_convert_child_iter_to_iter (GtkTreeModelFilter *filter,
GtkTreeIter *filter_iter,
GtkTreeIter *child_iter)
{
gboolean ret;
GtkTreePath *child_path, *path;
g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (filter), FALSE);
g_return_val_if_fail (filter->priv->child_model != NULL, FALSE);
g_return_val_if_fail (filter_iter != NULL, FALSE);
g_return_val_if_fail (child_iter != NULL, FALSE);
g_return_val_if_fail (filter_iter != child_iter, FALSE);
filter_iter->stamp = 0;
child_path = gtk_tree_model_get_path (filter->priv->child_model, child_iter);
g_return_val_if_fail (child_path != NULL, FALSE);
path = gtk_tree_model_filter_convert_child_path_to_path (filter,
child_path);
gtk_tree_path_free (child_path);
if (!path)
return FALSE;
ret = gtk_tree_model_get_iter (GTK_TREE_MODEL (filter), filter_iter, path);
gtk_tree_path_free (path);
return ret;
}
/**
* gtk_tree_model_filter_convert_iter_to_child_iter:
* @filter: A #GtkTreeModelFilter.
* @child_iter: (out): An uninitialized #GtkTreeIter.
* @filter_iter: A valid #GtkTreeIter pointing to a row on @filter.
*
* Sets @child_iter to point to the row pointed to by @filter_iter.
*
* Since: 2.4
*/
void
gtk_tree_model_filter_convert_iter_to_child_iter (GtkTreeModelFilter *filter,
GtkTreeIter *child_iter,
GtkTreeIter *filter_iter)
{
g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (filter));
g_return_if_fail (filter->priv->child_model != NULL);
g_return_if_fail (child_iter != NULL);
g_return_if_fail (filter_iter != NULL);
g_return_if_fail (filter_iter->stamp == filter->priv->stamp);
g_return_if_fail (filter_iter != child_iter);
if (GTK_TREE_MODEL_FILTER_CACHE_CHILD_ITERS (filter))
{
*child_iter = FILTER_ELT (filter_iter->user_data2)->iter;
}
else
{
GtkTreePath *path;
gboolean valid = FALSE;
path = gtk_tree_model_filter_elt_get_path (filter_iter->user_data,
filter_iter->user_data2,
filter->priv->virtual_root);
valid = gtk_tree_model_get_iter (filter->priv->child_model, child_iter,
path);
gtk_tree_path_free (path);
g_return_if_fail (valid == TRUE);
}
}
/* The path returned can only be used internally in the filter model. */
static GtkTreePath *
gtk_real_tree_model_filter_convert_child_path_to_path (GtkTreeModelFilter *filter,
GtkTreePath *child_path,
gboolean build_levels,
gboolean fetch_children)
{
gint *child_indices;
GtkTreePath *retval;
GtkTreePath *real_path;
FilterLevel *level;
FilterElt *tmp;
gint i;
g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (filter), NULL);
g_return_val_if_fail (filter->priv->child_model != NULL, NULL);
g_return_val_if_fail (child_path != NULL, NULL);
if (!filter->priv->virtual_root)
real_path = gtk_tree_path_copy (child_path);
else
real_path = gtk_tree_model_filter_remove_root (child_path,
filter->priv->virtual_root);
if (!real_path)
return NULL;
retval = gtk_tree_path_new ();
child_indices = gtk_tree_path_get_indices (real_path);
if (filter->priv->root == NULL && build_levels)
gtk_tree_model_filter_build_level (filter, NULL, -1, FALSE);
level = FILTER_LEVEL (filter->priv->root);
for (i = 0; i < gtk_tree_path_get_depth (real_path); i++)
{
gint j;
gboolean found_child = FALSE;
if (!level)
{
gtk_tree_path_free (real_path);
gtk_tree_path_free (retval);
return NULL;
}
tmp = bsearch_elt_with_offset (level->array, child_indices[i], &j);
if (tmp)
{
gtk_tree_path_append_index (retval, j);
if (!tmp->children && build_levels)
gtk_tree_model_filter_build_level (filter, level,
FILTER_LEVEL_ELT_INDEX (level, tmp),
FALSE);
level = tmp->children;
found_child = TRUE;
}
if (!found_child && fetch_children)
{
tmp = gtk_tree_model_filter_fetch_child (filter, level,
child_indices[i],
&j);
/* didn't find the child, let's try to bring it back */
if (!tmp || tmp->offset != child_indices[i])
{
/* not there */
gtk_tree_path_free (real_path);
gtk_tree_path_free (retval);
return NULL;
}
gtk_tree_path_append_index (retval, j);
if (!tmp->children && build_levels)
gtk_tree_model_filter_build_level (filter, level,
FILTER_LEVEL_ELT_INDEX (level, tmp),
FALSE);
level = tmp->children;
found_child = TRUE;
}
else if (!found_child && !fetch_children)
{
/* no path */
gtk_tree_path_free (real_path);
gtk_tree_path_free (retval);
return NULL;
}
}
gtk_tree_path_free (real_path);
return retval;
}
/**
* gtk_tree_model_filter_convert_child_path_to_path:
* @filter: A #GtkTreeModelFilter.
* @child_path: A #GtkTreePath to convert.
*
* Converts @child_path to a path relative to @filter. That is, @child_path
* points to a path in the child model. The rerturned path will point to the
* same row in the filtered model. If @child_path isn't a valid path on the
* child model or points to a row which is not visible in @filter, then %NULL
* is returned.
*
* Return value: A newly allocated #GtkTreePath, or %NULL.
*
* Since: 2.4
*/
GtkTreePath *
gtk_tree_model_filter_convert_child_path_to_path (GtkTreeModelFilter *filter,
GtkTreePath *child_path)
{
GtkTreeIter iter;
GtkTreePath *path;
/* this function does the sanity checks */
path = gtk_real_tree_model_filter_convert_child_path_to_path (filter,
child_path,
TRUE,
TRUE);
if (!path)
return NULL;
/* get a new path which only takes visible nodes into account.
* -- if this gives any performance issues, we can write a special
* version of convert_child_path_to_path immediately returning
* a visible-nodes-only path.
*/
gtk_tree_model_filter_get_iter_full (GTK_TREE_MODEL (filter), &iter, path);
gtk_tree_path_free (path);
path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), &iter);
return path;
}
/**
* gtk_tree_model_filter_convert_path_to_child_path:
* @filter: A #GtkTreeModelFilter.
* @filter_path: A #GtkTreePath to convert.
*
* Converts @filter_path to a path on the child model of @filter. That is,
* @filter_path points to a location in @filter. The returned path will
* point to the same location in the model not being filtered. If @filter_path
* does not point to a location in the child model, %NULL is returned.
*
* Return value: A newly allocated #GtkTreePath, or %NULL.
*
* Since: 2.4
*/
GtkTreePath *
gtk_tree_model_filter_convert_path_to_child_path (GtkTreeModelFilter *filter,
GtkTreePath *filter_path)
{
gint *filter_indices;
GtkTreePath *retval;
FilterLevel *level;
gint i;
g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (filter), NULL);
g_return_val_if_fail (filter->priv->child_model != NULL, NULL);
g_return_val_if_fail (filter_path != NULL, NULL);
/* convert path */
retval = gtk_tree_path_new ();
filter_indices = gtk_tree_path_get_indices (filter_path);
if (!filter->priv->root)
gtk_tree_model_filter_build_level (filter, NULL, -1, FALSE);
level = FILTER_LEVEL (filter->priv->root);
for (i = 0; i < gtk_tree_path_get_depth (filter_path); i++)
{
FilterElt *elt;
if (!level || level->visible_nodes <= filter_indices[i])
{
gtk_tree_path_free (retval);
return NULL;
}
elt = gtk_tree_model_filter_get_nth_visible (filter, level,
filter_indices[i]);
if (elt->children == NULL)
gtk_tree_model_filter_build_level (filter, level,
FILTER_LEVEL_ELT_INDEX (level, elt),
FALSE);
if (!level || level->visible_nodes <= filter_indices[i])
{
gtk_tree_path_free (retval);
return NULL;
}
gtk_tree_path_append_index (retval, elt->offset);
level = elt->children;
}
/* apply vroot */
if (filter->priv->virtual_root)
{
GtkTreePath *real_retval;
real_retval = gtk_tree_model_filter_add_root (retval,
filter->priv->virtual_root);
gtk_tree_path_free (retval);
return real_retval;
}
return retval;
}
static gboolean
gtk_tree_model_filter_refilter_helper (GtkTreeModel *model,
GtkTreePath *path,
GtkTreeIter *iter,
gpointer data)
{
/* evil, don't try this at home, but certainly speeds things up */
gtk_tree_model_filter_row_changed (model, path, iter, data);
return FALSE;
}
/**
* gtk_tree_model_filter_refilter:
* @filter: A #GtkTreeModelFilter.
*
* Emits ::row_changed for each row in the child model, which causes
* the filter to re-evaluate whether a row is visible or not.
*
* Since: 2.4
*/
void
gtk_tree_model_filter_refilter (GtkTreeModelFilter *filter)
{
g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (filter));
/* S L O W */
gtk_tree_model_foreach (filter->priv->child_model,
gtk_tree_model_filter_refilter_helper,
filter);
}
/**
* gtk_tree_model_filter_clear_cache:
* @filter: A #GtkTreeModelFilter.
*
* This function should almost never be called. It clears the @filter
* of any cached iterators that haven't been reffed with
* gtk_tree_model_ref_node(). This might be useful if the child model
* being filtered is static (and doesn't change often) and there has been
* a lot of unreffed access to nodes. As a side effect of this function,
* all unreffed iters will be invalid.
*
* Since: 2.4
*/
void
gtk_tree_model_filter_clear_cache (GtkTreeModelFilter *filter)
{
g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (filter));
if (filter->priv->zero_ref_count > 0)
gtk_tree_model_filter_clear_cache_helper (filter,
FILTER_LEVEL (filter->priv->root));
}