forked from AuroraMiddleware/gtk
Extend public and internal documentation about GtkTreeModelFilter
This commit is contained in:
parent
52faf1f984
commit
aa9151a6ee
@ -53,36 +53,191 @@
|
||||
* a #GtkTreePath indicating the root node for the filter at construction time.
|
||||
* </para></listitem>
|
||||
* </itemizedlist>
|
||||
*/
|
||||
|
||||
|
||||
/* ITER FORMAT:
|
||||
*
|
||||
* iter->stamp = filter->priv->stamp
|
||||
* iter->user_data = FilterLevel
|
||||
* iter->user_data2 = FilterElt
|
||||
* The basic API is similar to #GtkTreeModelSort. For an example on its usage,
|
||||
* see the section on #GtkTreeModelSort.
|
||||
*
|
||||
* When using #GtkTreeModelFilter, it is important to realize that
|
||||
* #GtkTreeModelFilter maintains an internal cache of all nodes which are
|
||||
* visible in its clients. The cache is likely to be a subtree of the tree
|
||||
* exposed by the child model. #GtkTreeModelFilter will not cache the entire
|
||||
* child model when unnecessary to not compromise the caching mechanism
|
||||
* that is exposed by the reference counting scheme. If the child model
|
||||
* implements reference counting, unnecessary signals may not be emitted
|
||||
* because of reference counting rule 3, see the #GtkTreeModel
|
||||
* documentation. (Note that e.g. #GtkTreeStore does not implement
|
||||
* reference counting and will always emit all signals, even when
|
||||
* the receiving node is not visible).
|
||||
*
|
||||
* Because of this, limitations for possible visible functions do apply.
|
||||
* In general, visible functions should only use data or properties from
|
||||
* the node for which the visibility state must be determined, its siblings
|
||||
* or its parents. Usually, having a dependency on the state of any child
|
||||
* node is not possible, unless references are taken on these explicitly.
|
||||
* When no such reference exists, no signals may be received for these child
|
||||
* nodes (see reference couting rule number 3 in the #GtkTreeModel section).
|
||||
*
|
||||
* Determining the visibility state of a given node based on the state
|
||||
* of its child nodes is a frequently occurring use case. Therefore,
|
||||
* #GtkTreeModelFilter explicitly supports this. For example, when a node
|
||||
* does not have any children, you might not want the node to be visible.
|
||||
* As soon as the first row is added to the node's child level (or the
|
||||
* last row removed), the node's visibility should be updated.
|
||||
*
|
||||
* This introduces a dependency from the node on its child nodes. In order
|
||||
* to accommodate this, #GtkTreeModelFilter must make sure the necesary
|
||||
* signals are received from the child model. This is achieved by building,
|
||||
* for all nodes which are exposed as visible nodes to #GtkTreeModelFilter's
|
||||
* clients, the child level (if any) and take a reference on the first node
|
||||
* in this level. Furthermore, for every row-inserted, row-changed or
|
||||
* row-deleted signal (also these which were not handled because the node
|
||||
* was not cached), #GtkTreeModelFilter will check if the visibility state
|
||||
* of any parent node has changed.
|
||||
*
|
||||
* Beware, however, that this explicit support is limited to these two
|
||||
* cases. For example, if you want a node to be visible only if two nodes
|
||||
* in a child's child level (2 levels deeper) are visible, you are on your
|
||||
* own. In this case, either rely on #GtkTreeStore to emit all signals
|
||||
* because it does not implement reference counting, or for models that
|
||||
* do implement reference counting, obtain references on these child levels
|
||||
* yourself.
|
||||
*/
|
||||
|
||||
/* all paths, iters, etc prefixed with c_ are paths, iters, etc relative to the
|
||||
* child model.
|
||||
/* Notes on this implementation of GtkTreeModelFilter
|
||||
* ==================================================
|
||||
*
|
||||
* Warnings
|
||||
* --------
|
||||
*
|
||||
* In this code there is a potential for confusion as to whether an iter,
|
||||
* path or value refers to the GtkTreeModelFilter model, or to the child
|
||||
* model that has been set. As a convention, variables referencing the
|
||||
* child model will have a c_ prefix before them (ie. c_iter, c_value,
|
||||
* c_path). In case the c_ prefixed names are already in use, an f_
|
||||
* prefix is used. Conversion of iterators and paths between
|
||||
* GtkTreeModelFilter and the child model is done through the various
|
||||
* gtk_tree_model_filter_convert_* functions.
|
||||
*
|
||||
* Even though the GtkTreeModelSort and GtkTreeModelFilter have very
|
||||
* similar data structures, many assumptions made in the GtkTreeModelSort
|
||||
* code do *not* apply in the GtkTreeModelFilter case. Reference counting
|
||||
* in particular is more complicated in GtkTreeModelFilter, because
|
||||
* we explicitly support reliance on the state of a node's children as
|
||||
* outlined in the public API documentation. Because of these differences,
|
||||
* you are strongly recommended to first read through these notes before
|
||||
* making any modification to the code.
|
||||
*
|
||||
* Iterator format
|
||||
* ---------------
|
||||
*
|
||||
* The iterator format of iterators handed out by GtkTreeModelFilter is
|
||||
* as follows:
|
||||
*
|
||||
* iter->stamp = filter->priv->stamp
|
||||
* iter->user_data = FilterLevel
|
||||
* iter->user_data2 = FilterElt
|
||||
*
|
||||
* Internal data structure
|
||||
* -----------------------
|
||||
*
|
||||
* Using FilterLevel and FilterElt, GtkTreeModelFilter maintains a "cache"
|
||||
* of the mapping from GtkTreeModelFilter nodes to nodes in the child model.
|
||||
* This is to avoid re-creating a level each time (which involves computing
|
||||
* visibility for each node in that level) an operation is requested on
|
||||
* GtkTreeModelFilter, such as get iter, get path and get value.
|
||||
*
|
||||
* A FilterElt corresponds to a single node. The FilterElt can either be
|
||||
* visible or invisible in the model that is exposed to the clients of this
|
||||
* GtkTreeModelFilter. The visibility state is stored in the "visible_siter"
|
||||
* field, which is NULL when the node is not visible. The FilterLevel keeps
|
||||
* a reference to the parent FilterElt and its FilterLevel (if any). The
|
||||
* FilterElt can have a "children" pointer set, which points at a child
|
||||
* level (a sub level).
|
||||
*
|
||||
* In a FilterLevel, two separate GSequences are maintained. One contains
|
||||
* all nodes of this FilterLevel, regardless of the visibility state of
|
||||
* the node. Another contains only visible nodes. A visible FilterElt
|
||||
* is thus present in both the full and the visible GSequence. The
|
||||
* GSequence allows for fast access, addition and removal of nodes.
|
||||
*
|
||||
* It is important to recognize the two different mappings that play
|
||||
* a part in this code:
|
||||
* I. The mapping from the client to this model. The order in which
|
||||
* nodes are stored in the *visible* GSequence is the order in
|
||||
* which the nodes are exposed to clients of the GtkTreeModelFilter.
|
||||
* II. The mapping from this model to its child model. Each FilterElt
|
||||
* contains an "offset" field which is the offset of the
|
||||
* corresponding node in the child model.
|
||||
*
|
||||
* Throughout the code, two kinds of paths relative to the GtkTreeModelFilter
|
||||
* (those generated from the sequence positions) are used. There are paths
|
||||
* which take non-visible nodes into account (generated from the full
|
||||
* sequences) and paths which don't (generated from the visible sequences).
|
||||
* Paths which have been generated from the full sequences should only be
|
||||
* used internally and NEVER be passed along with a signal emisson.
|
||||
*
|
||||
* Reference counting
|
||||
* ------------------
|
||||
*
|
||||
* GtkTreeModelFilter forwards all reference and unreference operations
|
||||
* to the corresponding node in the child model. In addition,
|
||||
* GtkTreeModelFilter will also add references of its own. The full reference
|
||||
* count of each node (i.e. all forwarded references and these by the
|
||||
* filter model) is maintained internally in the "ref_count" fields in
|
||||
* FilterElt and FilterLevel. Because there is a need to determine whether
|
||||
* a node should be visible for the client, the reference count of only
|
||||
* the forwarded references is maintained as well, in the "ext_ref_count"
|
||||
* fields.
|
||||
*
|
||||
* In a few cases, GtkTreeModelFilter takes additional references on
|
||||
* nodes. The first case is that a reference is taken on the parent
|
||||
* (if any) of each level. This happens in gtk_tree_model_filter_build_level()
|
||||
* and the reference is released again in gtk_tree_model_filter_free_level().
|
||||
* This ensures that for all references which are taken by the filter
|
||||
* model, all parent nodes are referenced according to reference counting
|
||||
* rule 1 in the GtkTreeModel documentation.
|
||||
*
|
||||
* A second case is required to support visible functions which depend on
|
||||
* the state of a node's children (see the public API documentation for
|
||||
* GtkTreeModelFilter above). We build the child level of each node that
|
||||
* could be visible in the client (i.e. the level has an ext_ref_count > 0;
|
||||
* not the elt, because the elt might be invisible and thus unreferenced
|
||||
* by the client). For each node that becomes visible, due to insertion or
|
||||
* changes in visibility state, it is checked whether node has children, if
|
||||
* so the child level is built.
|
||||
*
|
||||
* A reference is taken on the first node of each level so that the child
|
||||
* model will emit all signals for this level, due to reference counting
|
||||
* rule 3 in the GtkTreeModel documentation. If due to changes in the level,
|
||||
* another node becomes the first node (e.g. due to insertion or reordering),
|
||||
* this reference is transferred from the old to the new first node.
|
||||
*
|
||||
* When a level has an *external* reference count of zero (which means that
|
||||
* none of the nodes in the level is referenced by the clients), the level
|
||||
* has a "zero ref count" on all its parents. As soon as the level reaches
|
||||
* an *external* reference count of zero, the zero ref count value is
|
||||
* incremented by one for all parents of this level. Due to the additional
|
||||
* references taken by the filter model, it is important to base the
|
||||
* zero ref count on the external reference count instead of on the full
|
||||
* reference count of the node.
|
||||
*
|
||||
* The zero ref count value aids in determining which portions of the
|
||||
* cache are possibly unused and could be removed. If a FilterElt has
|
||||
* a zero ref count of one, then its child level is unused. However, the
|
||||
* child level can only be removed from the cache if the FilterElt's
|
||||
* parent has an external ref count of zero. Otherwise, monitoring
|
||||
* this level is necessary to possibly update the visibility state
|
||||
* of the parent. This is an important difference from GtkTreeModelSort!
|
||||
*
|
||||
* Signals are only required for levels with an external ref count > 0.
|
||||
* This due to reference counting rule 3, see the GtkTreeModel
|
||||
* documentation. In the GtkTreeModelFilter we try hard to stick to this
|
||||
* rule and not emit redundant signals (though redundant emissions of
|
||||
* row-has-child-toggled could appear frequently; it does happen that
|
||||
* we simply forward the signal emitted by e.g. GtkTreeStore but also
|
||||
* emit our own copy).
|
||||
*/
|
||||
|
||||
/* 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 position
|
||||
* of FilterElt in the sequence.
|
||||
*
|
||||
* Note that there are two kinds of paths relative to the filter model
|
||||
* (those generated from the sequence positions): 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;
|
||||
@ -1385,7 +1540,7 @@ gtk_tree_model_filter_remove_elt_from_level (GtkTreeModelFilter *filter,
|
||||
* - else, remove level.
|
||||
*
|
||||
* if level != root level and the number of visible nodes is 0 (ie. this
|
||||
* is the last * node to be removed from the level), emit
|
||||
* is the last node to be removed from the level), emit
|
||||
* row-has-child-toggled.
|
||||
*/
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user