forked from AuroraMiddleware/gtk
227d59c190
This makes it possible to create filter subclasses by implementing the vcalls. You can optionally chain up to the parent class' implementation, to make it possible to have "your filter + additional custom filtering". Fixes https://bugzilla.gnome.org/show_bug.cgi?id=604150
3535 lines
114 KiB
C
3535 lines
114 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 "gtkalias.h"
|
|
#include <string.h>
|
|
|
|
/* 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 zero_ref_count;
|
|
gboolean visible;
|
|
};
|
|
|
|
struct _FilterLevel
|
|
{
|
|
GArray *array;
|
|
gint ref_count;
|
|
gint visible_nodes;
|
|
|
|
gint parent_elt_index;
|
|
FilterLevel *parent_level;
|
|
};
|
|
|
|
#define GTK_TREE_MODEL_FILTER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_TREE_MODEL_FILTER, GtkTreeModelFilterPrivate))
|
|
|
|
struct _GtkTreeModelFilterPrivate
|
|
{
|
|
gpointer root;
|
|
gint stamp;
|
|
guint child_flags;
|
|
GtkTreeModel *child_model;
|
|
gint zero_ref_count;
|
|
|
|
GtkTreePath *virtual_root;
|
|
|
|
GtkTreeModelFilterVisibleFunc visible_func;
|
|
gpointer visible_data;
|
|
GDestroyNotify visible_destroy;
|
|
|
|
gint modify_n_columns;
|
|
GType *modify_types;
|
|
GtkTreeModelFilterModifyFunc modify_func;
|
|
gpointer modify_data;
|
|
GDestroyNotify modify_destroy;
|
|
|
|
gint visible_column;
|
|
|
|
gboolean visible_method_set;
|
|
gboolean modify_func_set;
|
|
|
|
gboolean in_row_deleted;
|
|
gboolean virtual_root_deleted;
|
|
|
|
/* signal ids */
|
|
guint changed_id;
|
|
guint inserted_id;
|
|
guint has_child_toggled_id;
|
|
guint deleted_id;
|
|
guint reordered_id;
|
|
};
|
|
|
|
/* properties */
|
|
enum
|
|
{
|
|
PROP_0,
|
|
PROP_CHILD_MODEL,
|
|
PROP_VIRTUAL_ROOT
|
|
};
|
|
|
|
#define GTK_TREE_MODEL_FILTER_CACHE_CHILD_ITERS(filter) \
|
|
(((GtkTreeModelFilter *)filter)->priv->child_flags & GTK_TREE_MODEL_ITERS_PERSIST)
|
|
|
|
#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_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);
|
|
|
|
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_unref_node (GtkTreeModel *model,
|
|
GtkTreeIter *iter,
|
|
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);
|
|
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_fetch_child (GtkTreeModelFilter *filter,
|
|
FilterLevel *level,
|
|
gint offset,
|
|
gint *index);
|
|
static void gtk_tree_model_filter_remove_node (GtkTreeModelFilter *filter,
|
|
GtkTreeIter *iter);
|
|
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);
|
|
|
|
|
|
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 = GTK_TREE_MODEL_FILTER_GET_PRIVATE (filter);
|
|
|
|
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_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);
|
|
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);
|
|
|
|
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;
|
|
gint length = 0;
|
|
gint i;
|
|
|
|
g_assert (filter->priv->child_model != 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);
|
|
}
|
|
|
|
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->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 */
|
|
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 (new_level != filter->priv->root)
|
|
filter->priv->zero_ref_count++;
|
|
|
|
i = 0;
|
|
|
|
first_node = iter;
|
|
|
|
do
|
|
{
|
|
if (gtk_tree_model_filter_visible (filter, &iter))
|
|
{
|
|
GtkTreeIter f_iter;
|
|
FilterElt filter_elt;
|
|
|
|
filter_elt.offset = i;
|
|
filter_elt.zero_ref_count = 0;
|
|
filter_elt.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++;
|
|
|
|
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));
|
|
|
|
if (new_level->parent_level || filter->priv->virtual_root)
|
|
gtk_tree_model_filter_ref_node (GTK_TREE_MODEL (filter), &f_iter);
|
|
|
|
if (emit_inserted)
|
|
{
|
|
GtkTreePath *f_path;
|
|
GtkTreeIter children;
|
|
|
|
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));
|
|
|
|
if (new_level->array->len == 0
|
|
&& (new_level != filter->priv->root || filter->priv->virtual_root))
|
|
{
|
|
/* If none of the nodes are visible, we will just pull in the
|
|
* first node of the level and keep a reference on it. We need this
|
|
* to make sure that we get all signals for this level.
|
|
*/
|
|
FilterElt filter_elt;
|
|
GtkTreeIter f_iter;
|
|
|
|
filter_elt.offset = 0;
|
|
filter_elt.zero_ref_count = 0;
|
|
filter_elt.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);
|
|
|
|
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));
|
|
|
|
gtk_tree_model_filter_ref_node (GTK_TREE_MODEL (filter), &f_iter);
|
|
}
|
|
else if (new_level->array->len == 0)
|
|
gtk_tree_model_filter_free_level (filter, new_level);
|
|
}
|
|
|
|
static void
|
|
gtk_tree_model_filter_free_level (GtkTreeModelFilter *filter,
|
|
FilterLevel *filter_level)
|
|
{
|
|
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));
|
|
|
|
if (filter_level->parent_level || filter->priv->virtual_root)
|
|
{
|
|
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, i));
|
|
|
|
gtk_tree_model_filter_unref_node (GTK_TREE_MODEL (filter), &f_iter);
|
|
}
|
|
}
|
|
|
|
if (filter_level->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)
|
|
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;
|
|
}
|
|
|
|
/* 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 (level->ref_count == 0 && level != filter->priv->root)
|
|
{
|
|
gtk_tree_model_filter_free_level (filter, level);
|
|
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;
|
|
}
|
|
|
|
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_fetch_child (GtkTreeModelFilter *filter,
|
|
FilterLevel *level,
|
|
gint offset,
|
|
gint *index)
|
|
{
|
|
gint i = 0;
|
|
gint start, middle, end;
|
|
gint len;
|
|
GtkTreePath *c_path = NULL;
|
|
GtkTreeIter c_iter;
|
|
GtkTreePath *c_parent_path = NULL;
|
|
GtkTreeIter c_parent_iter;
|
|
FilterElt elt;
|
|
|
|
/* 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;
|
|
|
|
/* add child */
|
|
elt.offset = offset;
|
|
elt.zero_ref_count = 0;
|
|
elt.ref_count = 0;
|
|
elt.children = NULL;
|
|
/* visibility should be FALSE as we don't emit row_inserted */
|
|
elt.visible = FALSE;
|
|
|
|
if (GTK_TREE_MODEL_FILTER_CACHE_CHILD_ITERS (filter))
|
|
elt.iter = c_iter;
|
|
|
|
/* 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;
|
|
}
|
|
|
|
c_iter.stamp = filter->priv->stamp;
|
|
c_iter.user_data = level;
|
|
c_iter.user_data2 = &g_array_index (level->array, FilterElt, *index);
|
|
|
|
if (level->parent_level || filter->priv->virtual_root)
|
|
gtk_tree_model_filter_ref_node (GTK_TREE_MODEL (filter), &c_iter);
|
|
|
|
return &g_array_index (level->array, FilterElt, *index);
|
|
}
|
|
|
|
static void
|
|
gtk_tree_model_filter_remove_node (GtkTreeModelFilter *filter,
|
|
GtkTreeIter *iter)
|
|
{
|
|
FilterElt *elt, *parent;
|
|
FilterLevel *level, *parent_level;
|
|
gint i, length, parent_elt_index;
|
|
|
|
gboolean emit_child_toggled = FALSE;
|
|
|
|
level = FILTER_LEVEL (iter->user_data);
|
|
elt = FILTER_ELT (iter->user_data2);
|
|
|
|
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 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->ref_count > 1: 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)
|
|
{
|
|
GtkTreePath *path;
|
|
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);
|
|
|
|
path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), iter);
|
|
elt->visible = FALSE;
|
|
gtk_tree_model_filter_increment_stamp (filter);
|
|
iter->stamp = filter->priv->stamp;
|
|
gtk_tree_model_row_deleted (GTK_TREE_MODEL (filter), path);
|
|
gtk_tree_path_free (path);
|
|
|
|
while (elt->ref_count > 1)
|
|
gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (filter),
|
|
iter, FALSE);
|
|
|
|
if (parent_level || filter->priv->virtual_root)
|
|
gtk_tree_model_filter_unref_node (GTK_TREE_MODEL (filter), iter);
|
|
else if (elt->ref_count > 0)
|
|
gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (filter),
|
|
iter, 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;
|
|
}
|
|
}
|
|
}
|
|
else if ((length == 1 && parent && parent->ref_count > 1)
|
|
|| (length == 1 && level == filter->priv->root))
|
|
{
|
|
GtkTreePath *path;
|
|
|
|
/* 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);
|
|
elt->children = NULL;
|
|
}
|
|
|
|
path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), iter);
|
|
elt->visible = FALSE;
|
|
gtk_tree_model_filter_increment_stamp (filter);
|
|
gtk_tree_model_row_deleted (GTK_TREE_MODEL (filter), path);
|
|
gtk_tree_path_free (path);
|
|
}
|
|
else
|
|
{
|
|
GtkTreePath *path;
|
|
|
|
/* Blow level away, including any child levels */
|
|
|
|
path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), iter);
|
|
elt->visible = FALSE;
|
|
gtk_tree_model_filter_increment_stamp (filter);
|
|
iter->stamp = filter->priv->stamp;
|
|
gtk_tree_model_row_deleted (GTK_TREE_MODEL (filter), path);
|
|
gtk_tree_path_free (path);
|
|
|
|
while (elt->ref_count > 1)
|
|
gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (filter),
|
|
iter, FALSE);
|
|
|
|
gtk_tree_model_filter_free_level (filter, level);
|
|
}
|
|
|
|
if (emit_child_toggled)
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
|
|
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 (gtk_tree_model_iter_has_child (filter->priv->child_model, &c_iter))
|
|
{
|
|
GtkTreePath *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;
|
|
}
|
|
|
|
/* TreeModel signals */
|
|
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;
|
|
|
|
FilterElt *elt;
|
|
FilterLevel *level;
|
|
|
|
gboolean requested_state;
|
|
gboolean current_state;
|
|
gboolean free_c_path = FALSE;
|
|
gboolean signals_emitted = 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);
|
|
|
|
/* 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)
|
|
{
|
|
/* get rid of this node */
|
|
level = FILTER_LEVEL (iter.user_data);
|
|
level->visible_nodes--;
|
|
|
|
gtk_tree_model_filter_remove_node (filter, &iter);
|
|
|
|
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))
|
|
{
|
|
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);
|
|
}
|
|
|
|
goto done;
|
|
}
|
|
|
|
/* only current == FALSE and requested == TRUE is left,
|
|
* pull in the child
|
|
*/
|
|
g_return_if_fail (current_state == FALSE && requested_state == TRUE);
|
|
|
|
/* make sure the new item has been pulled in */
|
|
if (!filter->priv->root)
|
|
{
|
|
FilterLevel *root;
|
|
|
|
gtk_tree_model_filter_build_level (filter, NULL, -1, TRUE);
|
|
|
|
/* We will only proceed below if the item is found. If the item
|
|
* is found, we can be sure row-inserted has just been emitted
|
|
* for it.
|
|
*/
|
|
signals_emitted = TRUE;
|
|
|
|
root = FILTER_LEVEL (filter->priv->root);
|
|
}
|
|
|
|
gtk_tree_model_filter_increment_stamp (filter);
|
|
|
|
/* We need to allow 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).
|
|
*/
|
|
if (!path)
|
|
path = gtk_real_tree_model_filter_convert_child_path_to_path (filter,
|
|
c_path,
|
|
FALSE,
|
|
TRUE);
|
|
|
|
if (!path)
|
|
/* parent is probably being filtered out */
|
|
goto done;
|
|
|
|
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);
|
|
|
|
/* elt->visible can be TRUE at this point if it was pulled in above */
|
|
if (!elt->visible)
|
|
{
|
|
elt->visible = TRUE;
|
|
level->visible_nodes++;
|
|
}
|
|
|
|
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)
|
|
gtk_tree_model_row_inserted (GTK_TREE_MODEL (filter), path, &iter);
|
|
|
|
if (level->parent_level && 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);
|
|
}
|
|
|
|
done:
|
|
if (path)
|
|
gtk_tree_path_free (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 *path = NULL;
|
|
GtkTreePath *real_path = NULL;
|
|
GtkTreeIter iter;
|
|
|
|
GtkTreeIter real_c_iter;
|
|
|
|
FilterElt *elt;
|
|
FilterLevel *level;
|
|
FilterLevel *parent_level;
|
|
|
|
gint i = 0, offset;
|
|
|
|
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 (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])++;
|
|
}
|
|
}
|
|
|
|
if (!filter->priv->root)
|
|
{
|
|
/* No point in building the level if this node is not visible. */
|
|
if (!filter->priv->virtual_root
|
|
&& !gtk_tree_model_filter_visible (filter, c_iter))
|
|
goto done;
|
|
|
|
/* build level will pull in the new child */
|
|
gtk_tree_model_filter_build_level (filter, NULL, -1, FALSE);
|
|
|
|
if (filter->priv->root
|
|
&& FILTER_LEVEL (filter->priv->root)->visible_nodes)
|
|
goto done_and_emit;
|
|
else
|
|
goto done;
|
|
}
|
|
|
|
parent_level = level = FILTER_LEVEL (filter->priv->root);
|
|
|
|
/* 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 (gtk_tree_path_get_depth (real_path) - 1 >= 1)
|
|
{
|
|
/* find the parent level */
|
|
while (i < gtk_tree_path_get_depth (real_path) - 1)
|
|
{
|
|
gint j;
|
|
|
|
if (!level)
|
|
/* we don't cover this signal */
|
|
goto done;
|
|
|
|
elt = bsearch_elt_with_offset (level->array,
|
|
gtk_tree_path_get_indices (real_path)[i],
|
|
&j);
|
|
|
|
if (!elt)
|
|
/* parent is probably being filtered out */
|
|
goto done;
|
|
|
|
if (!elt->children)
|
|
{
|
|
GtkTreePath *tmppath;
|
|
GtkTreeIter tmpiter;
|
|
|
|
tmpiter.stamp = filter->priv->stamp;
|
|
tmpiter.user_data = level;
|
|
tmpiter.user_data2 = elt;
|
|
|
|
tmppath = gtk_tree_model_get_path (GTK_TREE_MODEL (data),
|
|
&tmpiter);
|
|
|
|
if (tmppath)
|
|
{
|
|
gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (data),
|
|
tmppath, &tmpiter);
|
|
gtk_tree_path_free (tmppath);
|
|
}
|
|
|
|
/* not covering this signal */
|
|
goto done;
|
|
}
|
|
|
|
level = elt->children;
|
|
parent_level = level;
|
|
i++;
|
|
}
|
|
}
|
|
|
|
if (!parent_level)
|
|
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;
|
|
|
|
if (GTK_TREE_MODEL_FILTER_CACHE_CHILD_ITERS (filter))
|
|
felt.iter = real_c_iter;
|
|
|
|
felt.offset = offset;
|
|
felt.zero_ref_count = 0;
|
|
felt.ref_count = 0;
|
|
felt.visible = TRUE;
|
|
felt.children = NULL;
|
|
|
|
for (i = 0; i < level->array->len; i++)
|
|
if (g_array_index (level->array, FilterElt, i).offset > offset)
|
|
break;
|
|
|
|
level->visible_nodes++;
|
|
|
|
g_array_insert_val (level->array, i, felt);
|
|
|
|
if (level->parent_level || filter->priv->virtual_root)
|
|
{
|
|
GtkTreeIter f_iter;
|
|
|
|
f_iter.stamp = filter->priv->stamp;
|
|
f_iter.user_data = level;
|
|
f_iter.user_data2 = &g_array_index (level->array, FilterElt, i);
|
|
|
|
gtk_tree_model_filter_ref_node (GTK_TREE_MODEL (filter), &f_iter);
|
|
}
|
|
}
|
|
|
|
/* another iteration to update the references of children to parents. */
|
|
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;
|
|
}
|
|
|
|
/* don't emit the signal if we aren't visible */
|
|
if (!gtk_tree_model_filter_visible (filter, &real_c_iter))
|
|
goto done;
|
|
|
|
done_and_emit:
|
|
/* NOTE: pass c_path here and NOT real_path. This function does
|
|
* root subtraction itself
|
|
*/
|
|
path = gtk_real_tree_model_filter_convert_child_path_to_path (filter,
|
|
c_path,
|
|
FALSE, TRUE);
|
|
|
|
if (!path)
|
|
goto done;
|
|
|
|
gtk_tree_model_filter_increment_stamp (filter);
|
|
|
|
gtk_tree_model_filter_get_iter_full (GTK_TREE_MODEL (data), &iter, path);
|
|
|
|
/* 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_row_inserted (GTK_TREE_MODEL (data), path, &iter);
|
|
|
|
gtk_tree_path_free (path);
|
|
|
|
done:
|
|
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_node() takes care of emitting row-has-child-toggled
|
|
* when required.
|
|
*/
|
|
level->visible_nodes--;
|
|
|
|
gtk_tree_model_filter_remove_node (filter, &iter);
|
|
|
|
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 && gtk_tree_model_iter_has_child (c_model, c_iter))
|
|
gtk_tree_model_filter_build_level (filter, level,
|
|
FILTER_LEVEL_ELT_INDEX (level, elt),
|
|
TRUE);
|
|
|
|
/* 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_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;
|
|
gint offset;
|
|
gint i;
|
|
gint parent_elt_index = -1;
|
|
|
|
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)))
|
|
{
|
|
gint i;
|
|
GtkTreePath *path;
|
|
FilterLevel *level = FILTER_LEVEL (filter->priv->root);
|
|
|
|
gtk_tree_model_filter_unref_path (filter, filter->priv->virtual_root);
|
|
filter->priv->virtual_root_deleted = TRUE;
|
|
|
|
if (!level)
|
|
return;
|
|
|
|
/* remove everything in the filter model
|
|
*
|
|
* For now, we just iterate over the root level and emit a
|
|
* row_deleted for each FilterElt. Not sure if this is correct.
|
|
*/
|
|
|
|
gtk_tree_model_filter_increment_stamp (filter);
|
|
path = gtk_tree_path_new ();
|
|
gtk_tree_path_append_index (path, 0);
|
|
|
|
for (i = 0; i < level->visible_nodes; i++)
|
|
gtk_tree_model_row_deleted (GTK_TREE_MODEL (data), path);
|
|
|
|
gtk_tree_path_free (path);
|
|
gtk_tree_model_filter_free_level (filter, filter->priv->root);
|
|
|
|
return;
|
|
}
|
|
|
|
/* fixup virtual root */
|
|
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])--;
|
|
}
|
|
}
|
|
|
|
path = gtk_real_tree_model_filter_convert_child_path_to_path (filter,
|
|
c_path,
|
|
FALSE,
|
|
FALSE);
|
|
|
|
if (!path)
|
|
{
|
|
/* 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.
|
|
*/
|
|
GtkTreePath *real_path;
|
|
|
|
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);
|
|
|
|
i = 0;
|
|
if (gtk_tree_path_get_depth (real_path) - 1 >= 1)
|
|
{
|
|
/* find the level where the deletion occurred */
|
|
while (i < gtk_tree_path_get_depth (real_path) - 1)
|
|
{
|
|
gint j;
|
|
|
|
if (!level)
|
|
{
|
|
/* we don't cover this */
|
|
gtk_tree_path_free (real_path);
|
|
return;
|
|
}
|
|
|
|
elt = bsearch_elt_with_offset (level->array,
|
|
gtk_tree_path_get_indices (real_path)[i],
|
|
&j);
|
|
|
|
if (!elt || !elt->children)
|
|
{
|
|
/* parent is filtered out, so no level */
|
|
gtk_tree_path_free (real_path);
|
|
return;
|
|
}
|
|
|
|
level = elt->children;
|
|
i++;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
|
|
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 */
|
|
gtk_tree_model_filter_increment_stamp (filter);
|
|
gtk_tree_model_row_deleted (GTK_TREE_MODEL (data), path);
|
|
iter.stamp = filter->priv->stamp;
|
|
}
|
|
|
|
/* The filter model's reference on the child node is released
|
|
* below.
|
|
*/
|
|
while (elt->ref_count > 1)
|
|
gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (data), &iter,
|
|
FALSE);
|
|
|
|
if (level->array->len == 1)
|
|
{
|
|
/* kill level */
|
|
gtk_tree_model_filter_free_level (filter, level);
|
|
}
|
|
else
|
|
{
|
|
FilterElt *tmp;
|
|
|
|
/* release the filter model's reference on the node */
|
|
if (level->parent_level || filter->priv->virtual_root)
|
|
gtk_tree_model_filter_unref_node (GTK_TREE_MODEL (filter), &iter);
|
|
else if (elt->ref_count > 0)
|
|
gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (data), &iter,
|
|
FALSE);
|
|
|
|
/* 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;
|
|
}
|
|
}
|
|
|
|
if (emit_child_toggled && parent_level)
|
|
{
|
|
GtkTreeIter iter;
|
|
GtkTreePath *path;
|
|
|
|
iter.stamp = filter->priv->stamp;
|
|
iter.user_data = parent_level;
|
|
iter.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;
|
|
path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), &iter);
|
|
gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (filter),
|
|
path, &iter);
|
|
gtk_tree_path_free (path);
|
|
filter->priv->in_row_deleted = FALSE;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
/* 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)
|
|
{
|
|
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)
|
|
{
|
|
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_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)
|
|
{
|
|
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 (level->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);
|
|
}
|
|
|
|
static void
|
|
gtk_tree_model_filter_real_unref_node (GtkTreeModel *model,
|
|
GtkTreeIter *iter,
|
|
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 (level->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);
|
|
|
|
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 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_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: 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: 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: 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: 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: 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;
|
|
|
|
path = gtk_tree_model_filter_elt_get_path (filter_iter->user_data,
|
|
filter_iter->user_data2,
|
|
filter->priv->virtual_root);
|
|
gtk_tree_model_get_iter (filter->priv->child_model, child_iter, path);
|
|
gtk_tree_path_free (path);
|
|
}
|
|
}
|
|
|
|
/* 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));
|
|
}
|
|
|
|
#define __GTK_TREE_MODEL_FILTER_C__
|
|
#include "gtkaliasdef.c"
|