forked from AuroraMiddleware/gtk
88dd6372b0
- 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.
4074 lines
134 KiB
C
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)
|
|
* {
|
|
* /* Visible if row is non-empty and first column is "HI" */
|
|
* 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));
|
|
}
|