Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

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) + 10, 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






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik