Quelle gtktreelistmodel.c
Sprache: C
/*
* Copyright © 2018 Benjamin Otte
*
* This library is free software ; you can redistribute it and / or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation ; either
* version 2 . 1 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
* Lesser General Public License for more details .
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library . If not , see < http : //www.gnu.org/licenses/>.
*
* Authors : Benjamin Otte < otte @ gnome . org >
*/
#include "config.h"
#include "gtktreelistmodel.h"
#include "gtkrbtreeprivate.h"
#include "gtkprivate.h"
/**
* GtkTreeListModel :
*
* A list model that can create child models on demand .
*/
enum {
PROP_0,
PROP_AUTOEXPAND,
PROP_ITEM_TYPE,
PROP_MODEL,
PROP_N_ITEMS,
PROP_PASSTHROUGH,
NUM_PROPERTIES
};
typedef struct _TreeNode TreeNode;
typedef struct _TreeAugment TreeAugment;
struct _TreeNode
{
gpointer item;
GListModel *model;
GtkTreeListRow *row;
GtkRbTree *children;
union {
TreeNode *parent;
GtkTreeListModel *list;
};
guint empty : 1 ;
guint is_root : 1 ;
};
struct _TreeAugment
{
guint n_items;
guint n_local;
};
struct _GtkTreeListModel
{
GObject parent_instance;
TreeNode root_node;
GtkTreeListModelCreateModelFunc create_func;
gpointer user_data;
GDestroyNotify user_destroy;
guint autoexpand : 1 ;
guint passthrough : 1 ;
};
struct _GtkTreeListModelClass
{
GObjectClass parent_class;
};
struct _GtkTreeListRow
{
GObject parent_instance;
TreeNode *node; /* NULL when the row has been destroyed */
gpointer item;
};
struct _GtkTreeListRowClass
{
GObjectClass parent_class;
};
static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
static GtkTreeListModel *
tree_node_get_tree_list_model (TreeNode *node)
{
if (node->is_root)
return node->list;
for (node = node->parent; !node->is_root; node = node->parent)
{
/* This can happen during collapsing of a parent node */
if (node->children == NULL)
return NULL;
}
return node->list;
}
static TreeNode *
tree_node_get_nth_child (TreeNode *node,
guint position)
{
GtkRbTree *tree;
TreeNode *child, *tmp;
TreeAugment *aug;
tree = node->children;
child = gtk_rb_tree_get_root (tree);
while (child)
{
tmp = gtk_rb_tree_node_get_left (child);
if (tmp)
{
aug = gtk_rb_tree_get_augment (tree, tmp);
if (position < aug->n_local)
{
child = tmp;
continue ;
}
position -= aug->n_local;
}
if (position == 0 )
return child;
position--;
child = gtk_rb_tree_node_get_right (child);
}
return NULL;
}
static guint
tree_node_get_n_children (TreeNode *node)
{
TreeAugment *child_aug;
TreeNode *child_node;
if (node->children == NULL)
return 0 ;
child_node = gtk_rb_tree_get_root (node->children);
if (child_node == NULL)
return 0 ;
child_aug = gtk_rb_tree_get_augment (node->children, child_node);
return child_aug->n_items;
}
static guint
tree_node_get_position (TreeNode *node)
{
GtkRbTree *tree;
TreeNode *left, *parent;
TreeAugment *left_aug;
guint n;
for (n = 0 ;
!node->is_root;
node = node->parent, n++)
{
tree = node->parent->children;
left = gtk_rb_tree_node_get_left (node);
if (left)
{
left_aug = gtk_rb_tree_get_augment (tree, left);
n += left_aug->n_items;
}
for (parent = gtk_rb_tree_node_get_parent (node);
parent;
parent = gtk_rb_tree_node_get_parent (node))
{
left = gtk_rb_tree_node_get_left (parent);
if (left == node)
{
/* we are the left node, nothing changes */
}
else
{
/* we are the right node */
n += 1 + tree_node_get_n_children (parent);
if (left)
{
left_aug = gtk_rb_tree_get_augment (tree, left);
n += left_aug->n_items;
}
}
node = parent;
}
}
/* the root isn't visible */
n--;
return n;
}
static void
tree_node_mark_dirty (TreeNode *node)
{
for (;
!node->is_root;
node = node->parent)
{
gtk_rb_tree_node_mark_dirty (node);
}
}
static TreeNode *
gtk_tree_list_model_get_nth (GtkTreeListModel *self,
guint position)
{
GtkRbTree *tree;
TreeNode *node, *tmp;
guint n_children;
n_children = tree_node_get_n_children (&self->root_node);
if (n_children <= position)
return NULL;
tree = self->root_node.children;
node = gtk_rb_tree_get_root (tree);
while (TRUE )
{
tmp = gtk_rb_tree_node_get_left (node);
if (tmp)
{
TreeAugment *aug = gtk_rb_tree_get_augment (tree, tmp);
if (position < aug->n_items)
{
node = tmp;
continue ;
}
position -= aug->n_items;
}
if (position == 0 )
return node;
position--;
n_children = tree_node_get_n_children (node);
if (position < n_children)
{
tree = node->children;
node = gtk_rb_tree_get_root (tree);
continue ;
}
position -= n_children;
node = gtk_rb_tree_node_get_right (node);
}
g_return_val_if_reached (NULL);
}
static GListModel *
tree_node_create_model (GtkTreeListModel *self,
TreeNode *node)
{
GListModel *model;
model = self->create_func (node->item, self->user_data);
if (model == NULL)
node->empty = TRUE ;
return model;
}
static GtkTreeListRow *
tree_node_get_row (TreeNode *node)
{
if (node->row)
{
return g_object_ref (node->row);
}
else
{
node->row = g_object_new (GTK_TYPE_TREE_LIST_ROW, NULL);
node->row->node = node;
node->row->item = g_object_ref (node->item);
return node->row;
}
}
static guint
gtk_tree_list_model_expand_node (GtkTreeListModel *self,
TreeNode *node);
static void
gtk_tree_list_model_items_changed_cb (GListModel *model,
guint position,
guint removed,
guint added,
TreeNode *node)
{
GtkTreeListModel *self;
TreeNode *child;
guint i, tree_position, tree_removed, tree_added, n_local;
self = tree_node_get_tree_list_model (node);
if (self == NULL)
return ;
n_local = g_list_model_get_n_items (model) - added + removed;
if (position < n_local)
{
child = tree_node_get_nth_child (node, position);
tree_position = tree_node_get_position (child);
}
else
{
child = NULL;
tree_position = tree_node_get_position (node) + tree_node_get_n_children (node) + 1 ;
}
if (removed)
{
TreeNode *tmp;
g_assert (child != NULL);
if (position + removed < n_local)
{
TreeNode *end = tree_node_get_nth_child (node, position + removed);
tree_removed = tree_node_get_position (end) - tree_position;
}
else
{
tree_removed = tree_node_get_position (node) + tree_node_get_n_children (node) + 1 - tree_position;
}
for (i = 0 ; i < removed; i++)
{
tmp = child;
child = gtk_rb_tree_node_get_next (child);
gtk_rb_tree_remove (node->children, tmp);
}
}
else
{
tree_removed = 0 ;
}
tree_added = added;
for (i = added; i-- > 0 ;)
{
child = gtk_rb_tree_insert_before (node->children, child);
child->parent = node;
child->item = g_list_model_get_item (model, position + i);
g_assert (child->item);
}
if (self->autoexpand)
{
for (i = 0 ; i < added; i++)
{
tree_added += gtk_tree_list_model_expand_node (self, child);
child = gtk_rb_tree_node_get_next (child);
}
}
tree_node_mark_dirty (node);
g_list_model_items_changed (G_LIST_MODEL (self),
tree_position,
tree_removed,
tree_added);
if (tree_removed != tree_added)
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_N_ITEMS]);
}
static void gtk_tree_list_row_destroy (GtkTreeListRow *row);
static void
gtk_tree_list_model_clear_node_children (TreeNode *node)
{
if (node->model)
{
g_signal_handlers_disconnect_by_func (node->model,
gtk_tree_list_model_items_changed_cb,
node);
g_clear_object (&node->model);
}
g_clear_pointer (&node->children, gtk_rb_tree_unref);
}
static void
gtk_tree_list_model_clear_node (gpointer data)
{
TreeNode *node = data;
if (node->row)
{
g_object_freeze_notify (G_OBJECT (node->row));
gtk_tree_list_row_destroy (node->row);
}
gtk_tree_list_model_clear_node_children (node);
if (node->row)
g_object_thaw_notify (G_OBJECT (node->row));
g_clear_object (&node->item);
}
static void
gtk_tree_list_model_augment (GtkRbTree *tree,
gpointer _aug,
gpointer _node,
gpointer left,
gpointer right)
{
TreeAugment *aug = _aug;
aug->n_items = 1 ;
aug->n_items += tree_node_get_n_children (_node);
aug->n_local = 1 ;
if (left)
{
TreeAugment *left_aug = gtk_rb_tree_get_augment (tree, left);
aug->n_items += left_aug->n_items;
aug->n_local += left_aug->n_local;
}
if (right)
{
TreeAugment *right_aug = gtk_rb_tree_get_augment (tree, right);
aug->n_items += right_aug->n_items;
aug->n_local += right_aug->n_local;
}
}
static void
gtk_tree_list_model_init_node (GtkTreeListModel *list,
TreeNode *self,
GListModel *model)
{
gsize i, n;
TreeNode *node;
self->model = model;
g_signal_connect (model,
"items-changed" ,
G_CALLBACK (gtk_tree_list_model_items_changed_cb),
self);
self->children = gtk_rb_tree_new (TreeNode,
TreeAugment,
gtk_tree_list_model_augment,
gtk_tree_list_model_clear_node,
NULL);
n = g_list_model_get_n_items (model);
node = NULL;
for (i = 0 ; i < n; i++)
{
node = gtk_rb_tree_insert_after (self->children, node);
node->parent = self;
node->item = g_list_model_get_item (model, i);
g_assert (node ->item);
if (list->autoexpand)
gtk_tree_list_model_expand_node (list, node);
}
}
static guint
gtk_tree_list_model_expand_node (GtkTreeListModel *self,
TreeNode *node)
{
GListModel *model;
if (node->empty)
return 0 ;
if (node->model != NULL)
return 0 ;
model = tree_node_create_model (self, node);
if (model == NULL)
return 0 ;
gtk_tree_list_model_init_node (self, node, model);
tree_node_mark_dirty (node);
return tree_node_get_n_children (node);
}
static guint
gtk_tree_list_model_collapse_node (GtkTreeListModel *self,
TreeNode *node)
{
guint n_items;
if (node->model == NULL)
return 0 ;
n_items = tree_node_get_n_children (node);
gtk_tree_list_model_clear_node_children (node);
tree_node_mark_dirty (node);
return n_items;
}
static GType
gtk_tree_list_model_get_item_type (GListModel *list)
{
GtkTreeListModel *self = GTK_TREE_LIST_MODEL (list);
if (self->passthrough)
return G_TYPE_OBJECT;
else
return GTK_TYPE_TREE_LIST_ROW;
}
static guint
gtk_tree_list_model_get_n_items (GListModel *list)
{
GtkTreeListModel *self = GTK_TREE_LIST_MODEL (list);
return tree_node_get_n_children (&self->root_node);
}
static gpointer
gtk_tree_list_model_get_item (GListModel *list,
guint position)
{
GtkTreeListModel *self = GTK_TREE_LIST_MODEL (list);
TreeNode *node;
node = gtk_tree_list_model_get_nth (self, position);
if (node == NULL)
return NULL;
if (self->passthrough)
{
return g_object_ref (node->item);
}
else
{
return tree_node_get_row (node);
}
}
static void
gtk_tree_list_model_model_init (GListModelInterface *iface)
{
iface->get_item_type = gtk_tree_list_model_get_item_type;
iface->get_n_items = gtk_tree_list_model_get_n_items;
iface->get_item = gtk_tree_list_model_get_item;
}
G_DEFINE_TYPE_WITH_CODE (GtkTreeListModel, gtk_tree_list_model, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, gtk_tree_list_model_model_init))
static void
gtk_tree_list_model_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkTreeListModel *self = GTK_TREE_LIST_MODEL (object);
switch (prop_id)
{
case PROP_AUTOEXPAND:
gtk_tree_list_model_set_autoexpand (self, g_value_get_boolean (value));
break ;
case PROP_PASSTHROUGH:
self->passthrough = g_value_get_boolean (value);
break ;
default :
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break ;
}
}
static void
gtk_tree_list_model_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GtkTreeListModel *self = GTK_TREE_LIST_MODEL (object);
switch (prop_id)
{
case PROP_AUTOEXPAND:
g_value_set_boolean (value, self->autoexpand);
break ;
case PROP_ITEM_TYPE:
g_value_set_gtype (value, gtk_tree_list_model_get_item_type (G_LIST_MODEL (self)));
break ;
case PROP_MODEL:
g_value_set_object (value, self->root_node.model);
break ;
case PROP_N_ITEMS:
g_value_set_uint (value, tree_node_get_n_children (&self->root_node));
break ;
case PROP_PASSTHROUGH:
g_value_set_boolean (value, self->passthrough);
break ;
default :
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break ;
}
}
static void
gtk_tree_list_model_finalize (GObject *object)
{
GtkTreeListModel *self = GTK_TREE_LIST_MODEL (object);
gtk_tree_list_model_clear_node (&self->root_node);
if (self->user_destroy)
self->user_destroy (self->user_data);
G_OBJECT_CLASS (gtk_tree_list_model_parent_class)->finalize (object);
}
static void
gtk_tree_list_model_class_init (GtkTreeListModelClass *class )
{
GObjectClass *gobject_class = G_OBJECT_CLASS (class );
gobject_class->set_property = gtk_tree_list_model_set_property;
gobject_class->get_property = gtk_tree_list_model_get_property;
gobject_class->finalize = gtk_tree_list_model_finalize;
/**
* GtkTreeListModel : autoexpand :
*
* If all rows should be expanded by default .
*/
properties[PROP_AUTOEXPAND] =
g_param_spec_boolean ("autoexpand" , NULL, NULL,
FALSE ,
G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkTreeListModel : item - type :
*
* The type of items . See [ method @ Gio . ListModel . get_item_type ] .
*
* Since : 4 . 8
**/
properties[PROP_ITEM_TYPE] =
g_param_spec_gtype ("item-type" , NULL, NULL,
G_TYPE_OBJECT,
G_PARAM_READABLE | G_PARAM_STATIC_NAME);
/**
* GtkTreeListModel : model :
*
* The root model displayed .
*/
properties[PROP_MODEL] =
g_param_spec_object ("model" , NULL, NULL,
G_TYPE_LIST_MODEL,
G_PARAM_READABLE | G_PARAM_STATIC_NAME | G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkTreeListModel : n - items :
*
* The number of items . See [ method @ Gio . ListModel . get_n_items ] .
*
* Since : 4 . 8
**/
properties[PROP_N_ITEMS] =
g_param_spec_uint ("n-items" , NULL, NULL,
0 , G_MAXUINT, 0 ,
G_PARAM_READABLE | G_PARAM_STATIC_NAME);
/**
* GtkTreeListModel : passthrough :
*
* Gets whether the model is in passthrough mode .
*
* If % FALSE , the ` GListModel ` functions for this object return custom
* [ class @ Gtk . TreeListRow ] objects . If % TRUE , the values of the child
* models are pass through unmodified .
*/
properties[PROP_PASSTHROUGH] =
g_param_spec_boolean ("passthrough" , NULL, NULL,
FALSE ,
G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
}
static void
gtk_tree_list_model_init (GtkTreeListModel *self)
{
self->root_node.list = self;
self->root_node.is_root = TRUE ;
}
/**
* gtk_tree_list_model_new :
* @ root : ( transfer full ) : The ` GListModel ` to use as root
* @ passthrough : % TRUE to pass through items from the models
* @ autoexpand : % TRUE to set the autoexpand property and expand the @ root model
* @ create_func : ( scope notified ) ( closure user_data ) ( destroy user_destroy ) : function to
* call to create the ` GListModel ` for the children of an item
* @ user_data : Data to pass to @ create_func
* @ user_destroy : Function to call to free @ user_data
*
* Creates a new empty ` GtkTreeListModel ` displaying @ root
* with all rows collapsed .
*
* Returns : a newly created ` GtkTreeListModel ` .
*/
GtkTreeListModel *
gtk_tree_list_model_new (GListModel *root,
gboolean passthrough,
gboolean autoexpand,
GtkTreeListModelCreateModelFunc create_func,
gpointer user_data,
GDestroyNotify user_destroy)
{
GtkTreeListModel *self;
g_return_val_if_fail (G_IS_LIST_MODEL (root), NULL);
g_return_val_if_fail (create_func != NULL, NULL);
self = g_object_new (GTK_TYPE_TREE_LIST_MODEL,
"autoexpand" , autoexpand,
"passthrough" , passthrough,
NULL);
self->create_func = create_func;
self->user_data = user_data;
self->user_destroy = user_destroy;
gtk_tree_list_model_init_node (self, &self->root_node, root);
return self;
}
/**
* gtk_tree_list_model_get_model :
* @ self : a ` GtkTreeListModel `
*
* Gets the root model that @ self was created with .
*
* Returns : ( transfer none ) : the root model
*/
GListModel *
gtk_tree_list_model_get_model (GtkTreeListModel *self)
{
g_return_val_if_fail (GTK_IS_TREE_LIST_MODEL (self), NULL);
return self->root_node.model;
}
/**
* gtk_tree_list_model_get_passthrough :
* @ self : a ` GtkTreeListModel `
*
* Gets whether the model is passing through original row items .
*
* If this function returns % FALSE , the ` GListModel ` functions for @ self
* return custom ` GtkTreeListRow ` objects . You need to call
* [ method @ Gtk . TreeListRow . get_item ] on these objects to get the original
* item .
*
* If % TRUE , the values of the child models are passed through in their
* original state . You then need to call [ method @ Gtk . TreeListModel . get_row ]
* to get the custom ` GtkTreeListRow ` s .
*
* Returns : % TRUE if the model is passing through original row items
**/
gboolean
gtk_tree_list_model_get_passthrough (GtkTreeListModel *self)
{
g_return_val_if_fail (GTK_IS_TREE_LIST_MODEL (self), FALSE );
return self->passthrough;
}
/**
* gtk_tree_list_model_set_autoexpand :
* @ self : a ` GtkTreeListModel `
* @ autoexpand : % TRUE to make the model autoexpand its rows
*
* Sets whether the model should autoexpand .
*
* If set to % TRUE , the model will recursively expand all rows that
* get added to the model . This can be either rows added by changes
* to the underlying models or via [ method @ Gtk . TreeListRow . set_expanded ] .
*/
void
gtk_tree_list_model_set_autoexpand (GtkTreeListModel *self,
gboolean autoexpand)
{
g_return_if_fail (GTK_IS_TREE_LIST_MODEL (self));
if (self->autoexpand == autoexpand)
return ;
self->autoexpand = autoexpand;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_AUTOEXPAND]);
}
/**
* gtk_tree_list_model_get_autoexpand :
* @ self : a ` GtkTreeListModel `
*
* Gets whether the model is set to automatically expand new rows
* that get added .
*
* This can be either rows added by changes to the underlying
* models or via [ method @ Gtk . TreeListRow . set_expanded ] .
*
* Returns : % TRUE if the model is set to autoexpand
*/
gboolean
gtk_tree_list_model_get_autoexpand (GtkTreeListModel *self)
{
g_return_val_if_fail (GTK_IS_TREE_LIST_MODEL (self), FALSE );
return self->autoexpand;
}
/**
* gtk_tree_list_model_get_row :
* @ self : a ` GtkTreeListModel `
* @ position : the position of the row to fetch
*
* Gets the row object for the given row .
*
* If @ position is greater than the number of items in @ self ,
* % NULL is returned .
*
* The row object can be used to expand and collapse rows as
* well as to inspect its position in the tree . See its
* documentation for details .
*
* This row object is persistent and will refer to the current
* item as long as the row is present in @ self , independent of
* other rows being added or removed .
*
* If @ self is set to not be passthrough , this function is
* equivalent to calling g_list_model_get_item ( ) .
*
* Do not confuse this function with [ method @ Gtk . TreeListModel . get_child_row ] .
*
* Returns : ( nullable ) ( transfer full ) : The row item
*/
GtkTreeListRow *
gtk_tree_list_model_get_row (GtkTreeListModel *self,
guint position)
{
TreeNode *node;
g_return_val_if_fail (GTK_IS_TREE_LIST_MODEL (self), NULL);
node = gtk_tree_list_model_get_nth (self, position);
if (node == NULL)
return NULL;
return tree_node_get_row (node);
}
/**
* gtk_tree_list_model_get_child_row :
* @ self : a ` GtkTreeListModel `
* @ position : position of the child to get
*
* Gets the row item corresponding to the child at index @ position for
* @ self ' s root model .
*
* If @ position is greater than the number of children in the root model ,
* % NULL is returned .
*
* Do not confuse this function with [ method @ Gtk . TreeListModel . get_row ] .
*
* Returns : ( nullable ) ( transfer full ) : the child in @ position
**/
GtkTreeListRow *
gtk_tree_list_model_get_child_row (GtkTreeListModel *self,
guint position)
{
TreeNode *child;
g_return_val_if_fail (GTK_IS_TREE_LIST_MODEL (self), NULL);
child = tree_node_get_nth_child (&self->root_node, position);
if (child == NULL)
return NULL;
return tree_node_get_row (child);
}
/**
* GtkTreeListRow :
*
* The type of item used by ` GtkTreeListModel ` .
*
* It allows navigating the model as a tree and modify the state of rows .
*
* ` GtkTreeListRow ` instances are created by a ` GtkTreeListModel ` only
* when the [ property @ Gtk . TreeListModel : passthrough ] property is not set .
*
* There are various support objects that can make use of ` GtkTreeListRow `
* objects , such as the [ class @ Gtk . TreeExpander ] widget that allows displaying
* an icon to expand or collapse a row or [ class @ Gtk . TreeListRowSorter ] that
* makes it possible to sort trees properly .
*/
enum {
ROW_PROP_0,
ROW_PROP_CHILDREN,
ROW_PROP_DEPTH,
ROW_PROP_EXPANDABLE,
ROW_PROP_EXPANDED,
ROW_PROP_ITEM,
ROW_NUM_PROPERTIES
};
static GParamSpec *row_properties[ROW_NUM_PROPERTIES] = { NULL, };
G_DEFINE_TYPE (GtkTreeListRow, gtk_tree_list_row, G_TYPE_OBJECT)
static void
gtk_tree_list_row_destroy (GtkTreeListRow *self)
{
self->node = NULL;
/* FIXME: We could check some properties to avoid excess notifies */
g_object_notify_by_pspec (G_OBJECT (self), row_properties[ROW_PROP_DEPTH]);
g_object_notify_by_pspec (G_OBJECT (self), row_properties[ROW_PROP_EXPANDABLE]);
g_object_notify_by_pspec (G_OBJECT (self), row_properties[ROW_PROP_EXPANDED]);
}
static void
gtk_tree_list_row_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkTreeListRow *self = GTK_TREE_LIST_ROW (object);
switch (prop_id)
{
case ROW_PROP_EXPANDED:
gtk_tree_list_row_set_expanded (self, g_value_get_boolean (value));
break ;
default :
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break ;
}
}
static void
gtk_tree_list_row_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GtkTreeListRow *self = GTK_TREE_LIST_ROW (object);
switch (prop_id)
{
case ROW_PROP_CHILDREN:
g_value_set_object (value, gtk_tree_list_row_get_children (self));
break ;
case ROW_PROP_DEPTH:
g_value_set_uint (value, gtk_tree_list_row_get_depth (self));
break ;
case ROW_PROP_EXPANDABLE:
g_value_set_boolean (value, gtk_tree_list_row_is_expandable (self));
break ;
case ROW_PROP_EXPANDED:
g_value_set_boolean (value, gtk_tree_list_row_get_expanded (self));
break ;
case ROW_PROP_ITEM:
g_value_take_object (value, gtk_tree_list_row_get_item (self));
break ;
default :
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break ;
}
}
static void
gtk_tree_list_row_dispose (GObject *object)
{
GtkTreeListRow *self = GTK_TREE_LIST_ROW (object);
if (self->node)
self->node->row = NULL;
g_clear_object (&self->item);
G_OBJECT_CLASS (gtk_tree_list_row_parent_class)->dispose (object);
}
static void
gtk_tree_list_row_class_init (GtkTreeListRowClass *class )
{
GObjectClass *gobject_class = G_OBJECT_CLASS (class );
gobject_class->set_property = gtk_tree_list_row_set_property;
gobject_class->get_property = gtk_tree_list_row_get_property;
gobject_class->dispose = gtk_tree_list_row_dispose;
/**
* GtkTreeListRow : children :
*
* The model holding the row ' s children .
*/
row_properties[ROW_PROP_CHILDREN] =
g_param_spec_object ("children" , NULL, NULL,
G_TYPE_LIST_MODEL,
G_PARAM_READABLE | G_PARAM_STATIC_NAME);
/**
* GtkTreeListRow : depth :
*
* The depth in the tree of this row .
*/
row_properties[ROW_PROP_DEPTH] =
g_param_spec_uint ("depth" , NULL, NULL,
0 , G_MAXUINT, 0 ,
G_PARAM_READABLE | G_PARAM_STATIC_NAME);
/**
* GtkTreeListRow : expandable : ( getter is_expandable )
*
* If this row can ever be expanded .
*/
row_properties[ROW_PROP_EXPANDABLE] =
g_param_spec_boolean ("expandable" , NULL, NULL,
FALSE ,
G_PARAM_READABLE | G_PARAM_STATIC_NAME);
/**
* GtkTreeListRow : expanded :
*
* If this row is currently expanded .
*/
row_properties[ROW_PROP_EXPANDED] =
g_param_spec_boolean ("expanded" , NULL, NULL,
FALSE ,
G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkTreeListRow : item :
*
* The item held in this row .
*/
row_properties[ROW_PROP_ITEM] =
g_param_spec_object ("item" , NULL, NULL,
G_TYPE_OBJECT,
G_PARAM_READABLE | G_PARAM_STATIC_NAME);
g_object_class_install_properties (gobject_class, ROW_NUM_PROPERTIES, row_properties);
}
static void
gtk_tree_list_row_init (GtkTreeListRow *self)
{
}
/**
* gtk_tree_list_row_get_position :
* @ self : a ` GtkTreeListRow `
*
* Returns the position in the ` GtkTreeListModel ` that @ self occupies
* at the moment .
*
* Returns : The position in the model
*/
guint
gtk_tree_list_row_get_position (GtkTreeListRow *self)
{
g_return_val_if_fail (GTK_IS_TREE_LIST_ROW (self), 0 );
if (self->node == NULL)
return 0 ;
return tree_node_get_position (self->node);
}
/**
* gtk_tree_list_row_get_depth :
* @ self : a ` GtkTreeListRow `
*
* Gets the depth of this row .
*
* Rows that correspond to items in the root model have a depth
* of zero , rows corresponding to items of models of direct children
* of the root model have a depth of 1 and so on .
*
* The depth of a row never changes until the row is removed from its model
* at which point it will forever return 0 .
*
* Returns : The depth of this row
*/
guint
gtk_tree_list_row_get_depth (GtkTreeListRow *self)
{
TreeNode *node;
guint depth;
g_return_val_if_fail (GTK_IS_TREE_LIST_ROW (self), 0 );
if (self->node == NULL)
return 0 ;
depth = 0 ;
for (node = self->node->parent;
!node->is_root;
node = node->parent)
depth++;
return depth;
}
/**
* gtk_tree_list_row_set_expanded :
* @ self : a ` GtkTreeListRow `
* @ expanded : % TRUE if the row should be expanded
*
* Expands or collapses a row .
*
* If a row is expanded , the model of calling the
* [ callback @ Gtk . TreeListModelCreateModelFunc ] for the row ' s
* item will be inserted after this row . If a row is collapsed ,
* those items will be removed from the model .
*
* If the row is not expandable , this function does nothing .
*/
void
gtk_tree_list_row_set_expanded (GtkTreeListRow *self,
gboolean expanded)
{
GtkTreeListModel *list;
gboolean was_expanded;
guint n_items;
g_return_if_fail (GTK_IS_TREE_LIST_ROW (self));
if (self->node == NULL)
return ;
was_expanded = self->node->children != NULL;
if (was_expanded == expanded)
return ;
list = tree_node_get_tree_list_model (self->node);
if (list == NULL)
return ;
if (expanded)
{
n_items = gtk_tree_list_model_expand_node (list, self->node);
if (n_items > 0 )
{
g_list_model_items_changed (G_LIST_MODEL (list), tree_node_get_position (self->node) + 1 , 0 , n_items);
g_object_notify_by_pspec (G_OBJECT (list), properties[PROP_N_ITEMS]);
}
}
else
{
n_items = gtk_tree_list_model_collapse_node (list, self->node);
if (n_items > 0 )
{
g_list_model_items_changed (G_LIST_MODEL (list), tree_node_get_position (self->node) + 1 , n_items, 0 );
g_object_notify_by_pspec (G_OBJECT (list), properties[PROP_N_ITEMS]);
}
}
g_object_notify_by_pspec (G_OBJECT (self), row_properties[ROW_PROP_EXPANDED]);
g_object_notify_by_pspec (G_OBJECT (self), row_properties[ROW_PROP_CHILDREN]);
}
/**
* gtk_tree_list_row_get_expanded :
* @ self : a ` GtkTreeListRow `
*
* Gets if a row is currently expanded .
*
* Returns : % TRUE if the row is expanded
*/
gboolean
gtk_tree_list_row_get_expanded (GtkTreeListRow *self)
{
g_return_val_if_fail (GTK_IS_TREE_LIST_ROW (self), FALSE );
if (self->node == NULL)
return FALSE ;
return self->node->children != NULL;
}
/**
* gtk_tree_list_row_is_expandable : ( get - property expandable )
* @ self : a ` GtkTreeListRow `
*
* Checks if a row can be expanded .
*
* This does not mean that the row is actually expanded ,
* this can be checked with [ method @ Gtk . TreeListRow . get_expanded ] .
*
* If a row is expandable never changes until the row is removed
* from its model at which point it will forever return % FALSE .
*
* Returns : % TRUE if the row is expandable
*/
gboolean
gtk_tree_list_row_is_expandable (GtkTreeListRow *self)
{
GtkTreeListModel *list;
GListModel *model;
g_return_val_if_fail (GTK_IS_TREE_LIST_ROW (self), FALSE );
if (self->node == NULL)
return FALSE ;
if (self->node->empty)
return FALSE ;
if (self->node->model)
return TRUE ;
list = tree_node_get_tree_list_model (self->node);
if (list == NULL)
return FALSE ;
model = tree_node_create_model (list, self->node);
if (model)
{
g_object_unref (model);
return TRUE ;
}
return FALSE ;
}
/**
* gtk_tree_list_row_get_item :
* @ self : a ` GtkTreeListRow `
*
* Gets the item corresponding to this row ,
*
* Returns : ( nullable ) ( type GObject ) ( transfer full ) : The item
* of this row . This function is only marked as nullable for backwards
* compatibility reasons .
*/
gpointer
gtk_tree_list_row_get_item (GtkTreeListRow *self)
{
g_return_val_if_fail (GTK_IS_TREE_LIST_ROW (self), NULL);
return g_object_ref (self->item);
}
/**
* gtk_tree_list_row_get_children :
* @ self : a ` GtkTreeListRow `
*
* If the row is expanded , gets the model holding the children of @ self .
*
* This model is the model created by the
* [ callback @ Gtk . TreeListModelCreateModelFunc ]
* and contains the original items , no matter what value
* [ property @ Gtk . TreeListModel : passthrough ] is set to .
*
* Returns : ( nullable ) ( transfer none ) : The model containing the children
*/
GListModel *
gtk_tree_list_row_get_children (GtkTreeListRow *self)
{
g_return_val_if_fail (GTK_IS_TREE_LIST_ROW (self), NULL);
if (self->node == NULL)
return NULL;
return self->node->model;
}
/**
* gtk_tree_list_row_get_parent :
* @ self : a ` GtkTreeListRow `
*
* Gets the row representing the parent for @ self .
*
* That is the row that would need to be collapsed
* to make this row disappear .
*
* If @ self is a row corresponding to the root model ,
* % NULL is returned .
*
* The value returned by this function never changes
* until the row is removed from its model at which point
* it will forever return % NULL .
*
* Returns : ( nullable ) ( transfer full ) : The parent of @ self
*/
GtkTreeListRow *
gtk_tree_list_row_get_parent (GtkTreeListRow *self)
{
TreeNode *parent;
g_return_val_if_fail (GTK_IS_TREE_LIST_ROW (self), NULL);
if (self->node == NULL)
return NULL;
parent = self->node->parent;
if (parent->is_root)
return NULL;
return tree_node_get_row (parent);
}
/**
* gtk_tree_list_row_get_child_row :
* @ self : a ` GtkTreeListRow `
* @ position : position of the child to get
*
* If @ self is not expanded or @ position is greater than the
* number of children , % NULL is returned .
*
* Returns : ( nullable ) ( transfer full ) : the child in @ position
*/
GtkTreeListRow *
gtk_tree_list_row_get_child_row (GtkTreeListRow *self,
guint position)
{
TreeNode *child;
g_return_val_if_fail (GTK_IS_TREE_LIST_ROW (self), NULL);
if (self->node == NULL)
return NULL;
if (self->node->children == NULL)
return NULL;
child = tree_node_get_nth_child (self->node, position);
if (child == NULL)
return NULL;
return tree_node_get_row (child);
}
Messung V0.5 in Prozent C=98 H=95 G=96
¤ Dauer der Verarbeitung: 0.27 Sekunden
(vorverarbeitet am 2026-07-02)
¤
*© Formatika GbR, Deutschland
2026-07-09