The SSH host key has changed on 8 April, 2022 to this one: SHA256:573uTBSeh74kvOo0HJXi5ijdzRm8me27suzNEDlGyrQ
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

7439 lines
288 KiB

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
"http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd">
<book id="treeview-tutorial">
<bookinfo>
<date>September 1st, 2003</date>
<title>GTK+ 2.0 Tree View Tutorial</title>
<authorgroup>
<author>
<firstname>Tim-Philipp</firstname>
<surname>Müller</surname>
</author>
<author>
<firstname>Gergely</firstname>
<surname>Polonkai</surname>
</author>
</authorgroup>
<abstract>
<para>This is a tutorial on how to use the GTK+ GtkTreeView widget
through its C interface.</para>
<para>Please mail all comments and suggestions to <email>tim at
centricular dot net</email></para>
<para>A tarball of the tutorial for off-line reading including the
example source codes is available here: <ulink
url="treeview-tutorial.tar.gz">treeview-tutorial.tar.gz</ulink>.</para>
<para>There is also a <ulink url="treeview-tutorial.pdf">version in PDF
format</ulink> (for easier printing) and the raw <ulink
url="treeview-tutorial-xml.tar.gz">docbook XML source
document</ulink>.</para>
<para>This tutorial is work-in-progress. The latest version can be found
at <ulink url="http://scentric.net/tutorial/">
http://scentric.net/tutorial/</ulink>.</para>
<para>Some sections are a bit outdated (e.g. GtkTreeModelFilter has been
in Gtk since 2.4), just haven't gotten around to rewrite them or update
them. Sorry!</para>
<para>Last updated: September 29th, 2006</para>
</abstract>
</bookinfo>
<toc/>
<chapter id="ch-TreeView">
<title>Lists and Trees: the GtkTreeView Widget</title>
<para><classname>GtkTreeView</classname> is a widget that displays single-
or multi-columned lists and trees. It replaces the old Gtk+-1.2
<classname>GtkCList</classname> and <classname>GtkCTree</classname>
widgets. Even though <classname>GtkTreeView</classname> is slightly harder
to master than its predecessors, it is so much more powerful and flexible
that most application developers will not want to miss it once they have
come to know it.</para>
<para>The purpose of this chapter is not to provide an exhaustive
documentation of <classname>GtkTreeView</classname> – that is what the
<ulink
url="https://developer.gnome.org/gtk3/stable/TreeWidgetObjects.html">API
documentation</ulink> is for, which should be read alongside with this
tutorial. The goal is rather to present an introduction to the most
commonly-used aspects of <classname>GtkTreeView</classname>, and to
demonstrate how the various <classname>GtkTreeView</classname> components and
concepts work together. Furthermore, an attempt has been made to shed some
light on custom tree models and custom cell renderers, which seem to be
often-mentioned, but rarely explained.</para>
<para>Developers looking for a quick and dirty introduction that teaches
them everything they need to know in less than five paragraphs will not
find it here. In the authors’ experience, developers who do not understand
how the tree view and the models work together will run into problems once
they try to modify the given examples, whereas developers who have worked
with other toolkits that employ the Model/View/Controller design will find
that the API reference provides all the information they need to know in
more condensed form anyway. Those who disagree may jump straight to the
<link linkend="sec-treeview-col-example">working example code</link> of
course.</para>
<para>Please note that the code examples in the following sections do not
necessarily demonstrate how <classname>GtkTreeView</classname> is used
best in a particular situation. There are different ways to achieve the
same result, and the examples merely show those different ways, so that
developers are able to decide which one is most suitable for the task at
hand.</para>
<para>You can compile all the examples the standard GTK way on Linux. Just
use the <command>gcc `pkg-config --cflags --libs gtk+-3.0` -o treeviewtest
treeviewtest.c</command> command.</para>
<sect1 id="sec-TreeView-HelloWorld">
<title>Hello World</title>
<para>For the impatient, here is a small
<classname>GtkTreeView</classname> ‘Hello World’ program (which can also
be found in the examples section of the <ulink
url="treeview-tutorial.tar.gz"> treeview-tutorial.tar.gz</ulink>
tarball).</para>
<programlisting role="C">#include &lt;gtk/gtk.h&gt;
enum
{
COL_NAME = 0,
COL_AGE,
NUM_COLS
} ;
static GtkTreeModel *
create_and_fill_model (void)
{
GtkListStore *store;
GtkTreeIter iter;
store = gtk_list_store_new (NUM_COLS, G_TYPE_STRING, G_TYPE_UINT);
/* Append a row and fill in some data */
gtk_list_store_append (store, &amp;iter);
gtk_list_store_set (store, &amp;iter,
COL_NAME, "Heinz El-Mann",
COL_AGE, 51,
-1);
/* append another row and fill in some data */
gtk_list_store_append (store, &amp;iter);
gtk_list_store_set (store, &amp;iter,
COL_NAME, "Jane Doe",
COL_AGE, 23,
-1);
/* ... and a third row */
gtk_list_store_append (store, &amp;iter);
gtk_list_store_set (store, &amp;iter,
COL_NAME, "Joe Bungop",
COL_AGE, 91,
-1);
return GTK_TREE_MODEL (store);
}
static GtkWidget *
create_view_and_model (void)
{
GtkCellRenderer *renderer;
GtkTreeModel *model;
GtkWidget *view;
view = gtk_tree_view_new ();
/* --- Column #1 --- */
renderer = gtk_cell_renderer_text_new ();
gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view),
-1,
"Name",
renderer,
"text", COL_NAME,
NULL);
/* --- Column #2 --- */
renderer = gtk_cell_renderer_text_new ();
gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view),
-1,
"Age",
renderer,
"text", COL_AGE,
NULL);
model = create_and_fill_model ();
gtk_tree_view_set_model (GTK_TREE_VIEW (view), model);
/* The tree view has acquired its own reference to the
* model, so we can drop ours. That way the model will
* be freed automatically when the tree view is destroyed */
g_object_unref (model);
return view;
}
int
main (int argc, char **argv)
{
GtkWidget *window;
GtkWidget *view;
gtk_init (&amp;argc, &amp;argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
g_signal_connect (window, "delete_event", gtk_main_quit, NULL); /* dirty */
view = create_view_and_model ();
gtk_container_add (GTK_CONTAINER (window), view);
gtk_widget_show_all (window);
gtk_main ();
return 0;
}
</programlisting>
</sect1>
</chapter>
<chapter id="sec-treeview-components">
<title>Components: Model, Renderer, Column, View</title>
<para>The most important concept underlying
<classname>GtkTreeView</classname> is that of complete separation between
data and how that data is displayed on the screen. This is commonly known
as Model/View/Controller design (MVC). Data of various type (strings,
numbers, images, etc.) is stored in a 'model'. The 'view' is then told
which data to display, where to display it, and how to display it. One of
the advantages of this approach is that you can have multiple views that
display the same data (a directory tree for example) in different ways, or
in the same way multiple times, with only one copy of the underlying data.
This avoids duplication of data and programming effort if the same data is
re-used in different contexts. Also, when the data in the model is
updated, all views automatically get updated as well.</para>
<para>So, while <classname>GtkTreeModel</classname> is used to store data,
there are other components that determine which data is displayed in the
<classname>GtkTreeView</classname> and how it is displayed. These
components are <classname>GtkTreeViewColumn</classname> and
<classname>GtkCellRenderer</classname>. A <classname>GtkTreeView</classname> is
made up of tree view columns. These are the columns that users perceive as
columns. They have a clickable column header with a column title that can
be hidden, and can be resized and sorted. Tree view columns do not display
any data, they are only used as a device to represent the user-side of the
tree view (sorting etc.) and serve as packing widgets for the components
that do the actual rendering of data onto the screen, namely the
<classname>GtkCellRenderer</classname> family of objects (I call them
'objects' because they are not GtkWidgets). There are a number of
different cell renderers that specialise in rendering certain data like
strings, pixbufs, or toggle buttons. More on this <link
linkend="sec-renderer">later</link>.</para>
<para>Cell renderers are packed into tree view columns to display data. A
tree view column needs to contain at least one cell renderer, but can
contain multiple cell renderers. For example, if one wanted to display a
'Filename' column where each filename has a little icon on the left
indicating the file type, one would pack a
<classname>GtkCellRendererPixbuf</classname> and a
<classname>GtkCellRendererText</classname> into one tree view column. Packing
renderers into a tree view column is similar to packing widgets into a
<classname>GtkHBox</classname>.</para>
</chapter>
<chapter id="sec-treemodels">
<title>GtkTreeModels for Data Storage: GtkListStore and
GtkTreeStore</title>
<para>It is important to realise what <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModel.html"><classname>GtkTreeModel</classname></ulink>
is and what it is not. <classname>GtkTreeModel</classname> is basically just
an 'interface' to the data store, meaning that it is a standardised set of
functions that allows a <classname>GtkTreeView</classname> widget (and the
application programmer) to query certain characteristics of a data store,
for example how many rows there are, which rows have children, and how
many children a particular row has. It also provides functions to retrieve
data from the data store, and tell the tree view what type of data is
stored in the model. Every data store must implement the
<classname>GtkTreeModel</classname> interface and provide these functions,
which you can use by casting a store to a tree model with
<literal>GTK_TREE_MODEL(store)</literal>. <classname>GtkTreeModel</classname>
itself only provides a way to query a data store's characteristics and to
retrieve existing data, it does not provide a way to remove or add rows to
the store or put data into the store. This is done using the specific
store's functions.</para>
<para>Gtk+ comes with two built-in data stores (models): <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkListStore.html"><classname>GtkListStore</classname></ulink>
and <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeStore.html"><classname>GtkTreeStore</classname></ulink>.
As the names imply, <classname>GtkListStore</classname> is used for simple
lists of data items where items have no hierarchical parent-child
relationships, and <classname>GtkTreeStore</classname> is used for tree-like
data structures, where items can have parent-child relationships. A list
of files in a directory would be an example of a simple list structure,
whereas a directory tree is an example for a tree structure. A list is
basically just a special case of a tree with none of the items having any
children, so one could use a tree store to maintain a simple list of items
as well. The only reason <classname>GtkListStore</classname> exists is in
order to provide an easier interface that does not need to cater for
child-parent relationships, and because a simple list model can be
optimised for the special case where no children exist, which makes it
faster and more efficient.</para>
<para><classname>GtkListStore</classname> and <classname>GtkTreeStore</classname>
should cater for most types of data an application developer might want to
display in a <classname>GtkTreeView</classname>. However, it should be noted
that <classname>GtkListStore</classname> and <classname>GtkTreeStore</classname>
have been designed with flexibility in mind. If you plan to store a lot of
data, or have a large number of rows, you should consider implementing
your own custom model that stores and manipulates data your own way and
implements the <classname>GtkTreeModel</classname> interface. This will not
only be more efficient, but probably also lead to saner code in the long
run, and give you more control over your data. See <link
linkend="sec-custom-models">below</link> for more details on how to
implement custom models.</para>
<para>Tree model implementations like <classname>GtkListStore</classname> and
<classname>GtkTreeStore</classname> will take care of the view side for you
once you have configured the <classname>GtkTreeView</classname> to display
what you want. If you change data in the store, the model will notify the
tree view and your data display will be updated. If you add or remove
rows, the model will also notify the store, and your row will appear in or
disappear from the view as well.</para>
<sect1 id="sec-treemodel-data">
<title>How Data is Organised in a Store</title>
<para>A model (data store) has model columns and rows. While a tree view
will display each row in the model as a row in the view, the model's
columns are not to be confused with a view's columns. A model column
represents a certain data field of an item that has a fixed data type.
You need to know what kind of data you want to store when you create a
list store or a tree store, as you can not add new fields later
on.</para>
<para>For example, we might want to display a list of files. We would
create a list store with two fields: a field that stores the filename
(ie. a string) and a field that stores the file size (ie. an unsigned
integer). The filename would be stored in column 0 of the model, and the
file size would be stored in column 1 of the model. For each file we
would add a row to the list store, and set the row's fields to the
filename and the file size.</para>
<para>The <ulink
url="http://developer.gnome.org/doc/API/2.0/gobject/gobject-GType.html">GLib
type system (GType)</ulink> is used to indicate what type of data is
stored in a model column. These are the most commonly used types:</para>
<itemizedlist>
<listitem>
<simpara><literal>G_TYPE_BOOLEAN</literal></simpara>
</listitem>
<listitem>
<simpara><literal>G_TYPE_INT</literal>,
<literal>G_TYPE_UINT</literal></simpara>
</listitem>
<listitem>
<simpara><literal>G_TYPE_LONG</literal>,
<literal>G_TYPE_ULONG</literal>, <literal>G_TYPE_INT64</literal>,
<literal>G_TYPE_UINT64</literal> (these are not supported in early
gtk+-2.0.x versions)</simpara>
</listitem>
<listitem>
<simpara><literal>G_TYPE_FLOAT</literal>,
<literal>G_TYPE_DOUBLE</literal></simpara>
</listitem>
<listitem>
<simpara><literal>G_TYPE_STRING</literal> - stores a string in the
store (makes a copy of the original string)</simpara>
</listitem>
<listitem>
<simpara><literal>G_TYPE_POINTER</literal> - stores a pointer value
(does not copy any data into the store, just stores the pointer
value!)</simpara>
</listitem>
<listitem>
<simpara><literal>GDK_TYPE_PIXBUF</literal> - stores a GdkPixbuf in
the store (increases the pixbuf's refcount, <link
linkend="sec-treemodel-storing-gobjects">see below</link>)</simpara>
</listitem>
</itemizedlist>
<para>You do not need to understand the type system, it will usually
suffice to know the above types, so you can tell a list store or tree
store what kind of data you want to store. Advanced users can derive
their own types from the fundamental GLib types. For simple structures
you could <ulink
url="http://developer.gnome.org/doc/API/2.0/gobject/gobject-Boxed-Types.html#g-boxed-type-register-static">register</ulink>
a new <ulink
url="http://developer.gnome.org/doc/API/2.0/gobject/gobject-Boxed-Types.html">boxed
type</ulink> for example, but that is usually not necessary.
<literal>G_TYPE_POINTER</literal> will often do as well, you will just
need to take care of memory allocation and freeing yourself then.</para>
<para>Storing <classname>GObject</classname>-derived types (most
<literal>GDK_TYPE_FOO</literal> and <literal>GTK_TYPE_FOO</literal>) is
a special case that is dealt with <link
linkend="sec-treemodel-storing-gobjects">further below</link>.</para>
<para>Here is an example of how to create a list store:</para>
<programlisting role="C">
GtkListStore *list_store;
list_store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_UINT);
</programlisting>
<para>This creates a new list store with two columns. Column 0 stores a
string and column 1 stores an unsigned integer for each row. At this
point the model has no rows yet of course. Before we start to add rows,
let's have a look at the different ways used to refer to a particular
row.</para>
</sect1>
<sect1 id="sec-treemodel-rowref">
<title>Refering to Rows: GtkTreeIter, GtkTreePath,
GtkTreeRowReference</title>
<para>There are different ways to refer to a specific row. The two you
will have to deal with are <classname>GtkTreeIter</classname> and
<classname>GtkTreePath</classname>.</para>
<sect2 id="sec-treemodel-rowref-path">
<title>GtkTreePath</title>
<subtitle>Describing a row 'geographically'</subtitle>
<para>A <classname>GtkTreePath</classname> is a comparatively
straight-forward way to describe the logical position of a row in the
model. As a <classname>GtkTreeView</classname> always displays
<emphasis>all</emphasis> rows in a model, a tree path always describes
the same row in both model and view.</para>
<figure id="sec-treemodel-treepath-sshot">
<title>Tree Paths</title>
<screenshot>
<mediaobject>
<imageobject>
<imagedata fileref="images/treepath.png" format="PNG"/>
</imageobject>
<imageobject>
<imagedata fileref="images/treepath.eps" format="EPS"/>
</imageobject>
</mediaobject>
</screenshot>
</figure>
<para>The picture shows the tree path in string form next to the
label. Basically, it just counts the children from the imaginary root
of the tree view. An empty tree path string would specify that
imaginary invisible root. Now 'Songs' is the first child (from the
root) and thus its tree path is just "0". 'Videos' is the second child
from the root, and its tree path is "1". 'oggs' is the second child of
the first item from the root, so its tree path is "0:1". So you just
count your way down from the root to the row in question, and you get
your tree path.</para>
<para>To clarify this, a tree path of "3:9:4:1" would basically mean
<emphasis>in human language</emphasis> (attention - this is not what
it really means!) something along the lines of: go to the 3rd
top-level row. Now go to the 9th child of that row. Proceed to the 4th
child of the previous row. Then continue to the 1st child of that. Now
you are at the row this tree path describes. This is not what it means
for Gtk+ though. While humans start counting at 1, computers usually
start counting at 0. So the real meaning of the tree path "3:9:4:1"
is: Go to the 4th top-level row. Then go to the 10th child of that
row. Pick the 5th child of that row. Then proceed to the 2nd child of
the previous row. Now you are at the row this tree path describes.
:)</para>
<para>The implication of this way of refering to rows is as follows:
if you insert or delete rows in the middle or if the rows are
resorted, a tree path might suddenly refer to a completely different
row than it refered to before the insertion/deletion/resorting. This
is important to keep in mind. (See the <link
linkend="sec-treemodel-rowref-rowref">section on
<classname>GtkTreeRowReference</classname>s below</link> for a tree path
that keeps updating itself to make sure it always refers to the same
row when the model changes).</para>
<para>This effect becomes apparent if you imagine what would happen if
we were to delete the row entitled 'funny clips' from the tree in the
above picture. The row 'movie trailers' would suddenly be the first
and only child of 'clips', and be described by the tree path that
formerly belonged to 'funny clips', ie. "1:0:0".</para>
<para>You can get a new <classname>GtkTreePath</classname> from a path in
string form using <literal>gtk_tree_path_new_from_string</literal>,
and you can convert a given <classname>GtkTreePath</classname> into its
string notation with <literal>gtk_tree_path_to_string</literal>.
Usually you will rarely have to handle the string notation, it is
described here merely to demonstrate the concept of tree paths.</para>
<para>Instead of the string notation, <classname>GtkTreePath</classname>
uses an integer array internally. You can get the depth (ie. the
nesting level) of a tree path with <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModel.html#gtk-tree-path-get-depth">
<literal>gtk_tree_path_get_depth</literal></ulink>. A depth of 0 is
the imaginary invisible root node of the tree view and model. A depth
of 1 means that the tree path describes a top-level row. As lists are
just trees without child nodes, all rows in a list always have tree
paths of depth 1. <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModel.html#gtk-tree-path-get-indices">
<literal>gtk_tree_path_get_indices</literal></ulink> returns the
internal integer array of a tree path. You will rarely need to operate
with those either.</para>
<para>If you operate with tree paths, you are most likely to use a
given tree path, and use functions like <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModel.html#gtk_tree_path_up">
<literal>gtk_tree_path_up</literal></ulink>, <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModel.html#gtk-tree-path-down">
<literal>gtk_tree_path_down</literal></ulink>, <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModel.html#gtk-tree-path-next">
<literal>gtk_tree_path_next</literal></ulink>, <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModel.html#gtk-tree-path-prev">
<literal>gtk_tree_path_prev</literal></ulink>, <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModel.html#gtk-tree-path-is-ancestor">
<literal>gtk_tree_path_is_ancestor</literal></ulink>, or <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModel.html#gtk-tree-path-is-descendant">
<literal>gtk_tree_path_is_descendant</literal></ulink>. Note that this
way you can construct and operate on tree paths that refer to rows
that do not exist in model or view! The only way to check whether a
path is valid for a specific model (ie. the row described by the path
exists) is to convert the path into an iter using <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModel.html#gtk-tree-model-get-iter">
<literal>gtk_tree_model_get_iter</literal></ulink>.</para>
<para><classname>GtkTreePath</classname> is an opaque structure, with its
details hidden from the compiler. If you need to make a copy of a tree
path, use <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModel.html#gtk-tree-path-copy">
<literal>gtk_tree_path_copy</literal></ulink>.</para>
</sect2>
<sect2 id="sec-treemodel-rowref-iter">
<title>GtkTreeIter</title>
<subtitle>Refering to a row in model-speak</subtitle>
<para>Another way to refer to a row in a list or tree is
<classname>GtkTreeIter</classname>. A tree iter is just a structure that
contains a couple of pointers that mean something to the model you are
using. Tree iters are used internally by models, and they often
contain a direct pointer to the internal data of the row in question.
You should never look at the content of a tree iter and you must not
modify it directly either.</para>
<para>All tree models (and therefore also
<classname>GtkListStore</classname> and <classname>GtkTreeStore</classname>)
must support the <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModel.html">
<classname>GtkTreeModel</classname></ulink> functions that operate on tree
iters (e.g. get the tree iter for the first child of the row specified
by a given tree iter, get the first row in the list/tree, get the n-th
child of a given iter etc.). Some of these functions are:</para>
<itemizedlist>
<listitem>
<simpara><ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModel.html#gtk-tree-model-get-iter-first">
<literal>gtk_tree_model_get_iter_first</literal></ulink> - sets
the given iter to the first top-level item in the list or
tree</simpara>
</listitem>
<listitem>
<simpara><ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModel.html#gtk-tree-model-iter-next">
<literal>gtk_tree_model_iter_next</literal></ulink> - sets the
given iter to the next item at the current level in a list or
tree.</simpara>
</listitem>
<listitem>
<simpara><ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModel.html#gtk-tree-model-iter-children">
<literal>gtk_tree_model_iter_children</literal></ulink> - sets the
first given iter to the first child of the row referenced by the
second iter (not very useful for lists, mostly useful for
trees).</simpara>
</listitem>
<listitem>
<simpara><ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModel.html#gtk-tree-model-iter-n-children">
<literal>gtk_tree_model_iter_n_children</literal></ulink> -
returns the number of children the row referenced by the provided
iter has. If you pass <literal>NULL</literal> instead of a pointer
to an iter structure, this function will return the number of
top-level rows. You can also use this function to count the number
of items in a list store.</simpara>
</listitem>
<listitem>
<simpara><ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModel.html#gtk-tree-model-iter-nth-child">
<literal>gtk_tree_model_iter_nth_child</literal></ulink> - sets
the first iter to the n-th child of the row referenced by the
second iter. If you pass <literal>NULL</literal> instead of a
pointer to an iter structure as the second iter, you can get the
first iter set to the n-th row of a list.</simpara>
</listitem>
<listitem>
<simpara><ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModel.html#gtk-tree-model-iter-parent">
<literal>gtk_tree_model_iter_parent</literal></ulink> - sets the
first iter to the parent of the row referenced by the second iter
(does nothing for lists, only useful for trees).</simpara>
</listitem>
</itemizedlist>
<para>Almost all of those functions return <literal>TRUE</literal> if
the requested operation succeeded, and return <literal>FALSE</literal>
otherwise. There are more functions that operate on iters. Check out
the <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModel.html">
<classname>GtkTreeModel</classname> API reference</ulink> for
details.</para>
<para>You might notice that there is no
<literal>gtk_tree_model_iter_prev</literal>. This is unlikely to be
implemented for a variety of reasons. It should be fairly simple to
write a helper function that provides this functionality though once
you have read this section.</para>
<para>Tree iters are used to retrieve data from the store, and to put
data into the store. You also get a tree iter as result if you add a
new row to the store using <literal>gtk_list_store_append</literal> or
<literal>gtk_tree_store_append</literal>.</para>
<para>Tree iters are often only valid for a short time, and might
become invalid if the store changes with some models. It is therefore
usually a bad idea to store tree iters, unless you really know what
you are doing. You can use <literal>gtk_tree_model_get_flags</literal>
to get a model's flags, and check whether the
<literal>GTK_TREE_MODEL_ITERS_PERSIST</literal> flag is set (in which
case a tree iter will be valid as long as a row exists), yet still it
is not advisable to store iter structures unless you really mean to do
that. There is a better way to keep track of a row over time:
<classname>GtkTreeRowReference</classname></para>
</sect2>
<sect2 id="sec-treemodel-rowref-rowref">
<title>GtkTreeRowReference</title>
<subtitle>Keeping track of rows even when the model changes</subtitle>
<para>A <classname>GtkTreeRowReference</classname> is basically an object
that takes a tree path, and watches a model for changes. If anything
changes, like rows getting inserted or removed, or rows getting
re-ordered, the tree row reference object will keep the given tree
path up to date, so that it always points to the same row as before.
In case the given row is removed, the tree row reference will become
invalid.</para>
<para>A new tree row reference can be created with <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModel.html#gtk-tree-row-reference-new">
<literal>gtk_tree_row_reference_new</literal></ulink>, given a model
and a tree path. After that, the tree row reference will keep updating
the path whenever the model changes. The current tree path of the row
originally refered to when the tree row reference was created can be
retrieved with <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModel.html#gtk-tree-row-reference-get-path">
<literal>gtk_tree_row_reference_get_path</literal></ulink>. If the row
has been deleted, <literal>NULL</literal> will be returned instead of
of a tree path. The tree path returned is a <emphasis>copy</emphasis>,
so it will need to be freed with <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModel.html#gtk-tree-path-free">
<literal>gtk_tree_path_free</literal></ulink> when it is no longer
needed.</para>
<para>You can check whether the row referenced still exists with
<ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModel.html#gtk-tree-row-reference-valid">
<literal>gtk_tree_row_reference_valid</literal></ulink>, and free it
with <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModel.html#gtk-tree-row-reference-free">gtk_tree_row_reference_free
<literal/></ulink> when no longer needed.</para>
<para>For the curious: internally, the tree row reference connects to
the tree model's <literal>"row-inserted"</literal>,
<literal>"row-deleted"</literal>, and
<literal>"rows-reordered"</literal> signals and updates its internal
tree path whenever something happened to the model that affects the
position of the referenced row.</para>
<para>Note that using tree row references entails a small overhead.
This is hardly significant for 99.9% of all applications out there,
but when you have multiple thousands of rows and/or row references,
this might be something to keep in mind (because whenever rows are
inserted, removed, or reordered, a signal will be sent out and
processed for each row reference).</para>
<para>If you have read the tutorial only up to here so far, it is hard
to explain really what tree row references are good for. An example
where tree row references come in handy can be found further below in
the <link linkend="sec-treemodel-remove-many-rows">section on removing
multiple rows in one go</link>.</para>
<para>In practice, a programmer can either use tree row references to
keep track of rows over time, or store tree iters directly (if, and
only if, the model has persistent iters). Both
<classname>GtkListStore</classname> and <classname>GtkTreeStore</classname>
have persistent iters, so storing iters is possible. However, using
tree row references is definitively the Right Way(tm) to do things,
even though it comes with some overhead that might impact performance
in case of trees that have a very large number of rows (in that case
it might be preferable to write a custom model anyway though).
Especially beginners might find it easier to handle and store tree row
references than iters, because tree row references are handled by
pointer value, which you can easily add to a <classname>GList</classname>
or pointer array, while it is easy to store tree iters in a wrong
way.</para>
</sect2>
<sect2 id="sec-treemodel-rowref-usage">
<title>Usage</title>
<para>Tree iters can easily be converted into tree paths using <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModel.html#gtk-tree-model-get-path">
<literal>gtk_tree_model_get_path</literal></ulink>, and tree paths can
easily be converted into tree iters using <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModel.html#gtk-tree-model-get-iter">
<literal>gtk_tree_model_get_iter</literal></ulink>. Here is an example
that shows how to get the iter from the tree path that is passed to us
from the tree view in the <literal>"row-activated"</literal> signal
callback. We need the iter here to retrieve data from the store</para>
<programlisting role="C">
/************************************************************
* *
* Converting a GtkTreePath into a GtkTreeIter *
* *
************************************************************/
/************************************************************
*
* onTreeViewRowActivated: a row has been double-clicked
*
************************************************************/
void
onTreeViewRowActivated (GtkTreeView *view, GtkTreePath *path,
GtkTreeViewColumn *col, gpointer userdata)
{
GtkTreeIter iter;
GtkTreeModel *model;
model = gtk_tree_view_get_model(view);
if (gtk_tree_model_get_iter(model, &amp;iter, path))
{
gchar *name;
gtk_tree_model_get(model, &amp;iter, COL_NAME, &amp;name, -1);
g_print ("The row containing the name '%s' has been double-clicked.\n", name);
g_free(name);
}
}
</programlisting>
<para>Tree row references reveal the current path of a row with
<literal>gtk_tree_row_reference_get_path</literal>. There is no direct
way to get a tree iter from a tree row reference, you have to retrieve
the tree row reference's path first and then convert that into a tree
iter.</para>
<para>As tree iters are only valid for a short time, they are usually
allocated on the stack, as in the following example (keep in mind that
<classname>GtkTreeIter</classname> is just a structure that contains data
fields you do not need to know anything about):</para>
<programlisting role="C">
/************************************************************
* *
* Going through every row in a list store *
* *
************************************************************/
void
traverse_list_store (GtkListStore *liststore)
{
GtkTreeIter iter;
gboolean valid;
g_return_if_fail ( liststore != NULL );
/* Get first row in list store */
valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(liststore), &amp;iter);
while (valid)
{
/* ... do something with that row using the iter ... */
/* (Here column 0 of the list store is of type G_TYPE_STRING) */
gtk_list_store_set(liststore, &amp;iter, 0, "Joe", -1);
/* Make iter point to the next row in the list store */
valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(liststore), &amp;iter);
}
}
</programlisting>
<para>The code above asks the model to fill the iter structure to make
it point to the first row in the list store. If there is a first row
and the list store is not empty, the iter will be set, and <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModel.html#gtk-tree-model-get-iter-first">
<literal>gtk_tree_model_get_iter_first</literal></ulink> will return
<literal>TRUE</literal>. If there is no first row, it will just return
<literal>FALSE</literal>. If a first row exists, the while loop will
be entered and we change some of the first row's data. Then we ask the
model to make the given iter point to the next row, until there are no
more rows, which is when <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModel.html#gtk-tree-model-iter-next">
<literal>gtk_tree_model_iter_next</literal></ulink> returns
<literal>FALSE</literal>. Instead of traversing the list store we
could also have used <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModel.html#gtk-tree-model-foreach">
<literal>gtk_tree_model_foreach</literal></ulink></para>
</sect2>
</sect1>
<sect1 id="sec-treemodel-add-rows">
<title>Adding Rows to a Store</title>
<sect2 id="sec-liststore-add-rows">
<title>Adding Rows to a List Store</title>
<para>Rows are added to a list store with <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkListStore.html#gtk-list-store-append">
<literal>gtk_list_store_append</literal></ulink>. This will insert a
new empty row at the end of the list. There are other functions,
documented in the <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkListStore.html">
GtkListStore API reference</ulink>, that give you more control about
where exactly the new row is inserted, but as they work very similar
to <literal>gtk_list_store_append</literal> and are fairly
straight-forward to use, we will not deal with them here.</para>
<para>Here is a simple example of how to create a list store and add
some (empty) rows to it:</para>
<programlisting role="C">
GtkListStore *liststore;
GtkTreeIter iter;
liststore = gtk_list_store_new(1, G_TYPE_STRING);
/* Append an empty row to the list store. Iter will point to the new row */
gtk_list_store_append(liststore, &amp;iter);
/* Append an empty row to the list store. Iter will point to the new row */
gtk_list_store_append(liststore, &amp;iter);
/* Append an empty row to the list store. Iter will point to the new row */
gtk_list_store_append(liststore, &amp;iter);
</programlisting>
<para>This in itself is not very useful yet of course. We will add
data to the rows in the next section.</para>
</sect2>
<sect2 id="sec-treestore-add-rows">
<title>Adding Rows to a Tree Store</title>
<para>Adding rows to a tree store works similar to adding rows to a
list store, only that <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeStore.html#gtk-tree-store-append">
<literal>gtk_tree_store_append</literal></ulink> is the function to
use and one more argument is required, namely the tree iter to the
parent of the row to insert. If you supply NULL instead of providing
the tree iter of another row, a new top-level row will be inserted. If
you do provide a parent tree iter, the new empty row will be inserted
after any already existing children of the parent. Again, there are
other ways to insert a row into the tree store and they are documented
in the <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeStore.html">
GtkTreeStore API reference manual</ulink>. Another short
example:</para>
<programlisting role="C">
GtkTreeStore *treestore;
GtkTreeIter iter, child;
treestore = gtk_tree_store_new(1, G_TYPE_STRING);
/* Append an empty top-level row to the tree store.
* Iter will point to the new row */
gtk_tree_store_append(treestore, &amp;iter, NULL);
/* Append another empty top-level row to the tree store.
* Iter will point to the new row */
gtk_tree_store_append(treestore, &amp;iter, NULL);
/* Append a child to the row we just added.
* Child will point to the new row */
gtk_tree_store_append(treestore, &amp;child, &amp;iter);
/* Get the first row, and add a child to it as well (could have been done
* right away earlier of course, this is just for demonstration purposes) */
if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(treestore), &amp;iter))
{
/* Child will point to new row */
gtk_tree_store_append(treestore, &amp;child, &amp;iter);
}
else
{
g_error("Oops, we should have a first row in the tree store!\n");
}
</programlisting>
</sect2>
<sect2 id="sec-treestore-adding-many-rows">
<title>Speed Issues when Adding a Lot of Rows</title>
<para>A common scenario is that a model needs to be filled with a lot
of rows at some point, either at start-up, or when some file is
opened. An equally common scenario is that this takes an awfully long
time even on powerful machines once the model contains more than a
couple of thousand rows, with an exponentially decreasing rate of
insertion. As already pointed out above, <link
linkend="sec-custom-models">writing a custom model</link> might be the
best thing to do in this case. Nevertheless, there are some things you
can do to work around this problem and speed things up a bit even with
the stock Gtk+ models:</para>
<para>Firstly, you should detach your list store or tree store from
the tree view before doing your mass insertions, then do your
insertions, and only connect your store to the tree view again when
you are done with your insertions. Like this:</para>
<programlisting role="C">
...
model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
g_object_ref(model); /* Make sure the model stays with us after the tree view unrefs it */
gtk_tree_view_set_model(GTK_TREE_VIEW(view), NULL); /* Detach model from view */
... insert a couple of thousand rows ...
gtk_tree_view_set_model(GTK_TREE_VIEW(view), model); /* Re-attach model to view */
g_object_unref(model);
...
</programlisting>
<para>Secondly, you should make sure that sorting is disabled while
you are doing your mass insertions, otherwise your store might be
resorted after each and every single row insertion, which is going to
be everything but fast.</para>
<para>Thirdly, you should not keep around a lot of tree row references
if you have so many rows, because with each insertion (or removal)
every single tree row reference will check whether its path needs to
be updated or not.</para>
</sect2>
</sect1>
<sect1 id="sec-treemodel-data-manipulation">
<title>Manipulating Row Data</title>
<para>Adding empty rows to a data store is not terribly exciting, so
let's see how we can add or change data in the store.</para>
<para><ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkListStore.html#gtk-list-store-set">
<literal>gtk_list_store_set</literal></ulink> and <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeStore.html#gtk-tree-store-set">
<literal>gtk_tree_store_set</literal></ulink> are used to manipulate a
given row's data. There is also
<literal>gtk_list_store_set_value</literal> and
<literal>gtk_tree_store_set_value</literal>, but those should only be
used by people familiar with GLib's GValue system.</para>
<para>Both <literal>gtk_list_store_set</literal> and
<literal>gtk_tree_store_set</literal> take a variable number of
arguments, and must be terminated with a -1 argument. The first two
arguments are a pointer to the model, and the iter pointing to the row
whose data we want to change. They are followed by a variable number of
(column, data) argument pairs, terminated by a -1. The column refers to
the model column number and is usually an enum value (to make the code
more readable and to make changes easier). The data should be of the
same data type as the model column.</para>
<para>Here is an example where we create a store that stores two strings
and one integer for each row:</para>
<programlisting role="C">
enum
{
COL_FIRST_NAME = 0,
COL_LAST_NAME,
COL_YEAR_BORN,
NUM_COLS
};
GtkListStore *liststore;
GtkTreeIter iter;
liststore = gtk_list_store_new(NUM_COLS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_UINT);
/* Append an empty row to the list store. Iter will point to the new row */
gtk_list_store_append(liststore, &amp;iter);
/* Fill fields with some data */
gtk_list_store_set (liststore, &amp;iter,
COL_FIRST_NAME, "Joe",
COL_LAST_NAME, "Average",
COL_YEAR_BORN, (guint) 1970,
-1);
</programlisting>
<para>You do not need to worry about allocating and freeing memory for
the data to store. The model (or more precisely: the GLib/GObject GType
and GValue system) will take care of that for you. If you store a
string, for example, the model will make a copy of the string and store
that. If you then set the field to a new string later on, the model will
automatically free the old string and again make a copy of the new
string and store the copy. This applies to almost all types, be it
<literal>G_TYPE_STRING</literal> or
<literal>GDK_TYPE_PIXBUF</literal>.</para>
<para>The exception to note is <literal>G_TYPE_POINTER</literal>. If you
allocate a chunk of data or a complex structure and store it in a
<literal>G_TYPE_POINTER</literal> field, only the pointer
<emphasis>value</emphasis> is stored. The model does not know anything
about the size or content of the data your pointer refers to, so it
could not even make a copy if it wanted to, so you need to allocate and
free the memory yourself in this case. However, if you do not want to do
that yourself and want the model to take care of your custom data for
you, then you need to register your own type and derive it from one of
the GLib fundamental types (usually <ulink
url="http://developer.gnome.org/doc/API/2.0/gobject/gobject-Boxed-Types.html">G_TYPE_BOXED</ulink>).
See the GObject <ulink
url="http://developer.gnome.org/doc/API/2.0/gobject/gobject-GType.html">GType
reference manual</ulink> for details. Making a copy of data involves
memory allocation and other overhead of course, so one should consider
the performance implications of using a custom GLib type over a
G_TYPE_POINTER carefully before taking that approach. Again, a custom
model might be the better alternative, depending on the overall amount
of data to be stored (and retrieved).</para>
</sect1>
<sect1 id="sec-treemodel-data-retrieval">
<title>Retrieving Row Data</title>
<para>Storing data is not very useful if it cannot be retrieved again.
This is done using <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModel.html#gtk-tree-model-get">
<literal>gtk_tree_model_get</literal></ulink>, which takes similar
arguments as <literal>gtk_list_store_set</literal> or
<literal>gtk_tree_store_set</literal> do, only that it takes (column,
pointer) arguments. The pointer must point to a variable that is of the
same type as the data stored in that particular model column.</para>
<para>Here is the previous example extended to traverse the list store
and print out the data stored. As an extra, we use
<literal>gtk_tree_model_foreach</literal> to traverse the store and
retrieve the row number from the <classname>GtkTreePath</classname> passed
to us in the foreach callback function:</para>
<programlisting role="C">
#include &lt;gtk/gtk.h&gt;
enum
{
COL_FIRST_NAME = 0,
COL_LAST_NAME,
COL_YEAR_BORN,
NUM_COLS
};
gboolean
foreach_func (GtkTreeModel *model,
GtkTreePath *path,
GtkTreeIter *iter,
gpointer user_data)
{
gchar *first_name, *last_name, *tree_path_str;
guint year_of_birth;
/* Note: here we use 'iter' and not '&amp;iter', because we did not allocate
* the iter on the stack and are already getting the pointer to a tree iter */
gtk_tree_model_get (model, iter,
COL_FIRST_NAME, &amp;first_name,
COL_LAST_NAME, &amp;last_name,
COL_YEAR_BORN, &amp;year_of_birth,
-1);
tree_path_str = gtk_tree_path_to_string(path);
g_print ("Row %s: %s %s, born %u\n", tree_path_str,
first_name, last_name, year_of_birth);
g_free(tree_path_str);
g_free(first_name); /* gtk_tree_model_get made copies of */
g_free(last_name); /* the strings for us when retrieving them */
return FALSE; /* do not stop walking the store, call us with next row */
}
void
create_and_fill_and_dump_store (void)
{
GtkListStore *liststore;
GtkTreeIter iter;
liststore = gtk_list_store_new(NUM_COLS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_UINT);
/* Append an empty row to the list store. Iter will point to the new row */
gtk_list_store_append(liststore, &amp;iter);
/* Fill fields with some data */
gtk_list_store_set (liststore, &amp;iter,
COL_FIRST_NAME, "Joe",
COL_LAST_NAME, "Average",
COL_YEAR_BORN, (guint) 1970,
-1);
/* Append another row, and fill in some data */
gtk_list_store_append(liststore, &amp;iter);
gtk_list_store_set (liststore, &amp;iter,
COL_FIRST_NAME, "Jane",
COL_LAST_NAME, "Common",
COL_YEAR_BORN, (guint) 1967,
-1);
/* Append yet another row, and fill it */
gtk_list_store_append(liststore, &amp;iter);
gtk_list_store_set (liststore, &amp;iter,
COL_FIRST_NAME, "Yo",
COL_LAST_NAME, "Da",
COL_YEAR_BORN, (guint) 1873,
-1);
/* Now traverse the list */
gtk_tree_model_foreach(GTK_TREE_MODEL(liststore), foreach_func, NULL);
}
int
main (int argc, char **argv)
{
gtk_init(&amp;argc, &amp;argv);
create_and_fill_and_dump_store();
return 0;
}
</programlisting>
<para>Note that when a new row is created, all fields of a row are set
to a default NIL value appropriate for the data type in question. A
field of type <literal>G_TYPE_INT</literal> will automatically contain
the value 0 until it is set to a different value, and strings and all
kind of pointer types will be <literal>NULL</literal> until set to
something else. Those are valid contents for the model, and if you are
not sure that row contents have been set to something, you need to be
prepared to handle <literal>NULL</literal> pointers and the like in your
code.</para>
<para>Run the above program with an additional empty row and look at the
output to see this in effect.</para>
<sect2 id="sec-treemodel-retrieved-data-disposal">
<title>Freeing Retrieved Row Data</title>
<para>Unless you are dealing with a model column of type
<literal>G_TYPE_POINTER</literal>,
<literal>gtk_tree_model_get</literal> will always make
<emphasis>copies</emphasis> of the data retrieved.</para>
<para>In the case of strings, this means that you need to
<literal>g_free</literal> the string returned when you don't need it
any longer, as in the example above.</para>
<para>If you retrieve a <classname>GObject</classname> such as a
<classname>GdkPixbuf</classname> from the store,
<literal>gtk_tree_model_get</literal> will automatically add a
reference to it, so you need to call <literal>g_object_unref</literal>
on the retrieved object once you are done with it:</para>
<programlisting role="C">
...
GdkPixbuf *pixbuf;
gtk_tree_model_get (model, &amp;iter,
COL_PICTURE, &amp;pixbuf,
NULL);
if (pixbuf != NULL)
{
do_something_with_pixbuf (pixbuf);
g_object_unref (pixbuf);
}
...
</programlisting>
<para>Similarly, <classname>GBoxed</classname>-derived types retrieved
from a model need to be freed with <literal>g_boxed_free</literal>
when done with them (don't worry if you have never heard of
<classname>GBoxed</classname>).</para>
<para>If the model column is of type
<literal>G_TYPE_POINTER</literal>,
<literal>gtk_tree_model_get</literal> will simply copy the pointer
value, but not the data (even if if it wanted to, it couldn't copy the
data, because it would not know how to copy it or what to copy
exactly). If you store pointers to objects or strings in a pointer
column (which you should not do unless you really know what you are
doing and why you are doing it), you do not need to unref or free the
returned values as described above, because
<literal>gtk_tree_model_get</literal> would not know what kind of data
they are and therefore won't ref or copy them on retrieval.</para>
</sect2>
</sect1>
<sect1 id="sec-treemodel-remove-row">
<title>Removing Rows</title>
<para>Rows can easily be removed with <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkListStore.html#gtk-list-store-remove">
<literal>gtk_list_store_remove</literal></ulink> and <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeStore.html#gtk-tree-store-remove">
<literal>gtk_tree_store_remove</literal></ulink>. The removed row will
automatically be removed from the tree view as well, and all data stored
will automatically be freed, with the exception of
<literal>G_TYPE_POINTER</literal> columns (see above).</para>
<para>Removing a single row is fairly straight forward: you need to get
the iter that identifies the row you want to remove, and then use one of
the above functions. Here is a simple example that removes a row when
you double-click on it (bad from a user interface point of view, but
then it is just an example):</para>
<programlisting role="C">
static void
onRowActivated (GtkTreeView *view,
GtkTreePath *path,
GtkTreeViewColumn *col,
gpointer user_data)
{
GtkTreeModel *model;
GtkTreeIter iter;
g_print ("Row has been double-clicked. Removing row.\n");
model = gtk_tree_view_get_model(view);
if (!gtk_tree_model_get_iter(model, &amp;iter, path))
return; /* path describes a non-existing row - should not happen */
gtk_list_store_remove(GTK_LIST_STORE(model), &amp;iter);
}
void
create_treeview (void)
{
...
g_signal_connect(treeview, "row-activated", G_CALLBACK(onRowActivated), NULL);
...
}
</programlisting>
<para><emphasis>Note:</emphasis> <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkListStore.html#gtk-list-store-remove">
<literal>gtk_list_store_remove</literal></ulink> and <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeStore.html#gtk-tree-store-remove">
<literal>gtk_tree_store_remove</literal></ulink> both have slightly
different semantics in Gtk+-2.0 and Gtk+-2.2 and later. In Gtk+-2.0,
both functions do not return a value, while in later Gtk+ versions those
functions return either <literal>TRUE</literal> or
<literal>FALSE</literal> to indicate whether the iter given has been set
to the next valid row (or invalidated if there is no next row). This is
important to keep in mind when writing code that is supposed to work
with all Gtk+-2.x versions. In that case you should just ignore the
value returned (as in the call above) and check the iter with <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkListStore.html#gtk-list-store-iter-is-valid">
<literal>gtk_list_store_iter_is_valid</literal></ulink> if you need
it.</para>
<para>If you want to remove the n-th row from a list (or the n-th child
of a tree node), you have two approaches: either you first create a
<classname>GtkTreePath</classname> that describes that row and then turn it
into an iter and remove it; or you take the iter of the parent node and
use <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModel.html#gtk-tree-model-iter-nth-child">
<literal>gtk_tree_model_iter_nth_child</literal></ulink> (which will
also work for list stores if you use <literal>NULL</literal> as the
parent iter. Of course you could also start with the iter of the first
top-level row, and then step-by-step move it to the row you want,
although that seems a rather awkward way of doing it.</para>
<para>The following code snippet will remove the n-th row of a list if
it exists:</para>
<programlisting role="C">
/******************************************************************
*
* list_store_remove_nth_row
*
* Removes the nth row of a list store if it exists.
*
* Returns TRUE on success or FALSE if the row does not exist.
*
******************************************************************/
gboolean
list_store_remove_nth_row (GtkListStore *store, gint n)
{
GtkTreeIter iter;
g_return_val_if_fail (GTK_IS_LIST_STORE(store), FALSE);
/* NULL means the parent is the virtual root node, so the
* n-th top-level element is returned in iter, which is
* the n-th row in a list store (as a list store only has
* top-level elements, and no children) */
if (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(store), &amp;iter, NULL, n))
{
gtk_list_store_remove(store, &amp;iter);
return TRUE;
}
return FALSE;
}
</programlisting>
</sect1>
<sect1 id="sec-treemodel-remove-many-rows">
<title>Removing Multiple Rows</title>
<para>Removing multiple rows at once can be a bit tricky at times, and
requires some thought on how to do this best. For example, it is not
possible to traverse a store with
<literal>gtk_tree_model_foreach</literal>, check in the callback
function whether the given row should be removed and then just remove it
by calling one of the stores' remove functions. This will not work,
because the model is changed from within the foreach loop, which might
suddenly invalidate formerly valid tree iters in the foreach function,
and thus lead to unpredictable results.</para>
<para>You could traverse the store in a <literal>while</literal> loop of
course, and call <literal>gtk_list_store_remove</literal> or
<literal>gtk_tree_store_remove</literal> whenever you want to remove a
row, and then just continue if the remove functions returns
<literal>TRUE</literal> (meaning that the iter is still valid and now
points to the row after the row that was removed). However, this
approach will only work with Gtk+-2.2 or later and will not work if you
want your programs to compile and work with Gtk+-2.0 as well, for the
reasons outlined above (in Gtk+-2.0 the remove functions did not set the
passed iter to the next valid row). Also, while this approach might be
feasable for a list store, it gets a bit awkward for a tree
store.</para>
<para>Here is an example for an alternative approach to removing
multiple rows in one go (here we want to remove all rows from the store
that contain persons that have been born after 1980, but it could just
as well be all selected rows or some other criterion):</para>
<programlisting role="C">
/******************************************************************
*
* Removing multiple rows in one go
*
******************************************************************/
...
gboolean
foreach_func (GtkTreeModel *model,
GtkTreePath *path,
GtkTreeIter *iter,
GList **rowref_list)
{
guint year_of_birth;
g_assert ( rowref_list != NULL );
gtk_tree_model_get (model, iter, COL_YEAR_BORN, &amp;year_of_birth, -1);
if ( year_of_birth &gt; 1980 )
{
GtkTreeRowReference *rowref;
rowref = gtk_tree_row_reference_new(model, path);
*rowref_list = g_list_append(*rowref_list, rowref);
}
return FALSE; /* do not stop walking the store, call us with next row */
}
void
remove_people_born_after_1980 (void)
{
GList *rr_list = NULL; /* list of GtkTreeRowReferences to remove */
GList *node;
gtk_tree_model_foreach(GTK_TREE_MODEL(store),
(GtkTreeModelForeachFunc) foreach_func,
&amp;rr_list);
for ( node = rr_list; node != NULL; node = node-&gt;next )
{
GtkTreePath *path;
path = gtk_tree_row_reference_get_path((GtkTreeRowReference*)node-&gt;data);
if (path)
{
GtkTreeIter iter;
if (gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &amp;iter, path))
{
gtk_list_store_remove(store, &amp;iter);
}
/* FIXME/CHECK: Do we need to free the path here? */
}
}
g_list_foreach(rr_list, (GFunc) gtk_tree_row_reference_free, NULL);
g_list_free(rr_list);
}
...
</programlisting>
<para><ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkListStore.html#gtk-list-store-clear">
<literal>gtk_list_store_clear</literal></ulink> and <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeStore.html#gtk-tree-store-clear">
<literal>gtk_tree_store_clear</literal></ulink> come in handy if you
want to remove all rows.</para>
</sect1>
<sect1 id="sec-treemodel-storing-gobjects">
<title>Storing GObjects (Pixbufs etc.)</title>
<para>A special case are <classname>GObject</classname> types, like
<literal>GDK_TYPE_PIXBUF</literal>, that get stored in a list or tree
store. The store will not make a copy of the object, rather it will
increase the object's refcount. The store will then unref the object
again if it is no longer needed (ie. a new object is stored in the old
object's place, the current value is replaced by NULL, the row is
removed, or the store is destroyed).</para>
<para>From a developer perspective, this means that you need to
<literal>g_object_unref</literal> an object that you have just added to
the store if you want the store to automatically dispose of it when no
longer needed. This is because on object creation, the object has an
initial refcount of 1, which is "your" refcount, and the object will
only be destroyed when it reaches a refcount of 0. Here is the life
cycle of a pixbuf:</para>
<programlisting role="C">
GtkListStore *list_store;
GtkTreeIter iter;
GdkPixbuf *pixbuf;
GError *error = NULL;
list_store = gtk_list_store_new (2, GDK_TYPE_PIXBUF, G_TYPE_STRING);
pixbuf = gdk_pixbuf_new_from_file("icon.png", &amp;error);
/* pixbuf has a refcount of 1 after creation */
if (error)
{
g_critical ("Could not load pixbuf: %s\n", error-&gt;message);
g_error_free(error);
return;
}
gtk_list_store_append(list_store, &amp;iter);
gtk_list_store_set(list_store, &amp;iter, 0, pixbuf, 1, "foo", -1);
/* pixbuf has a refcount of 2 now, as the list store has added its own reference */
g_object_unref(pixbuf);
/* pixbuf has a refcount of 1 now that we have released our initial reference */
/* we don't want an icon in that row any longer */
gtk_list_store_set(list_store, &amp;iter, 0, NULL, -1);
/* pixbuf has automatically been destroyed after its refcount has reached 0.
* The list store called g_object_unref() on the pixbuf when it replaced
* the object in the store with a new value (NULL). */
</programlisting>
<para>Having learned how to add, manipulate, and retrieve data from a
store, the next step is to get that data displayed in a
<classname>GtkTreeView</classname> widget.</para>
</sect1>
<sect1 id="sec-treemodel-storing-structs">
<title>Storing Data Structures: of Pointers, GBoxed Types, and GObject
(TODO)</title>
<para>Unfinished chapter.</para>
<!--
<para>
<emphasis>
This section is for advanced users. You might want to skip it
for now and come back to it later when you find yourself
needing to store C-type custom data structures of your own
in a tree model.
</emphasis>
</para>
<para>
<emphasis>
This section is also not really very refined and only a rough draft.
Its purpose is just to introduce some approaches that might not be
immediately obvious for someone not familiar with the API.
</emphasis>
</para>
<para>
If the data you want to display becomes more complex (or simply
just a lot), storing it into a list store or tree store using
a trizillion model columns is not always the most convenient
way. It quickly leads to bulky code and unnecessary overhead.
</para>
<para>
Also, you often have an object or item of data where you
only want to display <emphasis>some</emphasis> of the data
in the tree view and have a fair bit of data you need to keep
track of in addition. Storing all the data in the model is
inconvenient, and keeping two copies of the data around and
syncing the data in the model with the data in the structure
is also inconvenient, even more so if the data changes
regularly.
</para>
<para>
Consider a tree view that displays the download status of
various files, e.g. a web browser's download window. You
might want to display the following data for each download:
<itemizedlist>
<listitem><para>
Destination File Name or Path
</para></listitem>
<listitem><para>
Source URL
</para></listitem>
<listitem><para>
Size of Download
</para></listitem>
<listitem><para>
Download Status (Completed, Paused, Queued, Downloading, etc.)
</para></listitem>
<listitem><para>
Download Speed (if applicable)
</para></listitem>
<listitem><para>
Estimated Time Left
</para></listitem>
</itemizedlist>
</para>
<para>
An obvious solution is to keep track of a download in a structure,
something along the lines of
</para>
<programlisting role="C">
typedef struct _MyDownload MyDownload;
struct _MyDownload
{
gchar *dest_fn;
gchar *src_url;
guint status;
guint64 size;
guint64 transfered;
gfloat speed;
guint seconds_left;
... more variables for the download action itself ..
};
</programlisting>
<para>
Then you could have list store with a column of <literal>G_TYPE_POINTER</literal>
and just store a pointer to the structure there for each row. Instead of connecting
model columns to cell renderer properties via attributes (see below), you will have
to use cell data functions with this approach.
</para>
<para>
The main issue with storing struct pointers in a list store is that memory management
becomes a bit awkward. You have to make sure that you remove the pointer data and/or
whole row from the store when you free the structure. Equally, you need to free the
structure 'manually' when you remove the row and don't need it any longer. Nothing
that cannot be solved, yet still not exactly elegant either.
</para>
<para>
The advantage is of course that code that retrieves data from a row (e.g. a selected row)
becomes much cleaner, and that there is less overhead when retrieving data (like string
copies). Compare
</para>
<programlisting role="C">
void
treeview_on_row_activated (GtkTreeView *treeview,
GtkTreePath *path,
GtkTreeViewColumn *col,
gpointer data)
{
GtkTreeModel *model;
GtkTreeIter iter;
model = gtk_tree_view_get_model (treeview);
if (gtk_tree_model_get_iter (model, &amp;iter, path))
{
gchar *fn, *url;
guint status;
gtk_tree_model_get (model, &amp;iter,
COL_FILENAME, &amp;fn,
COL_URL, &amp;url,
COL_STATUS, &amp;status,
-1);
... do something with data on row double-click ....
g_free (fn);
g_free (url);
}
}
</programlisting>
<para>
with
</para>
<programlisting role="C">
void
treeview_on_row_activated (GtkTreeView *treeview,
GtkTreePath *path,
GtkTreeViewColumn *col,
gpointer data)
{
GtkTreeModel *model;
GtkTreeIter iter;
model = gtk_tree_view_get_model (treeview);
if (gtk_tree_model_get_iter (model, &amp;iter, path))
{
MyDownload *dl;
gtk_tree_model_get (model, &amp;iter, 0, &amp;dl, -1);
... do something with data on row double-click ....
}
}
</programlisting>
<para>
Now, It would be nice if one could use memory management
techniques like reference counting with one's custom
structures, and have the tree models make use of them
automatically, just like the tree models take care of
memory management for strings or for GObjects like GdkPixbuf.
</para>
<para>
The obvious way would be to derive MyDownload from GObject,
in which case it will be handled automatically by the models.
This requires a bit of a dive into the GObject system though
and makes things more complicated than they have to be.
</para>
<para>
The alternative is to register your own type as a GBoxed-derived
type with the GType system, and implement some simple refcounting
yourself. Some code snippets:
</para>
<programlisting role="C">
#define MY_TYPE_DOWNLOAD (my_download_get_type())
typedef struct _MyDownload MyDownload;
struct _MyDownload
{
gchar *dest_fn;
gchar *src_url;
guint status;
... more ...
gint refcount;
};
void
my_download_ref (MyDownload *dl)
{
g_return_if_fail (dl->refcount > 0);
dl->refcount += 1;
}
void
my_download_unref (MyDownload *dl)
{
g_return_if_fail (dl->refcount > 0);
dl->refcount -= 1;
if (dl->refcount == 0)
{
/* free structure content */
g_free (dl->dest_fn);
g_free (dl->src_url);
/* poison memory */
memset (dl, 0xFF, sizeof (MyDownload));
/* free structure itself */
g_free (dl);
}
}
static gpointer
my_download_copy_func (gpointer boxed)
{
my_download_ref ((MyDownload*) boxed);
return boxed;
}
static void
my_download_free_func (gpointer boxed)
{
/* for clarity */
my_download_unref ((MyDownload*) boxed);
}
GType
my_download_get_type (void)
{
static GType my_type = 0;
if (my_type == 0)
{
my_type = g_boxed_type_register_static ("MyDownload",
my_download_copy_func,
my_download_free_func);
}
return my_type;
}
MyDownload *
make_new_download (const gchar *from, const gchar *to)
{
MyDownload *dl;
dl = g_new0 (MyDownload, 1);
dl->src_url = g_strdup (from);
dl->dest_fn = g_strdup (to);
dl->status = 0;
dl->refcount = 1;
return dl;
}
</programlisting>
-->
</sect1>
</chapter>
<chapter id="sec-treeview">
<title>Creating a Tree View</title>
<para>In order to display data in a tree view widget, we need to create
one first, and we need to instruct it where to get the data to display
from.</para>
<para>A new tree view is created with:</para>
<programlisting role="C">
GtkWidget *view;
view = gtk_tree_view_new();
</programlisting>
<sect1 id="sec-treeview-connect-model">
<title>Connecting Tree View and Model</title>
<para>Before we proceed to the next section where we display data on the
screen, we need connect our data store to the tree view, so it knows
where to get the data to display from. This is achieved with <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeView.html#gtk-tree-view-set-model">
<literal>gtk_tree_view_set_model</literal></ulink>, which will by itself
do very little. However, it is a prerequisite for what we do in the
following sections. <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeView.html#gtk-tree-view-new-with-model">
<literal>gtk_tree_view_new_with_model</literal></ulink> is a convenience
function for the previous two.</para>
<para><ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeView.html#gtk-tree-view-get-model">
<literal>gtk_tree_view_get_model</literal></ulink> will return the model
that is currently attached to a given tree view, which is particularly
useful in callbacks where you only get passed the tree view widget
(after all, we do not want to go down the road of global variables,
which will inevitably lead to the Dark Side, do we?).</para>
<sect2 id="sec-treeview-connect-model-refcounting">
<title>Reference counting</title>
<para>Tree models like <classname>GtkListStore</classname> and
<classname>GtkTreeStore</classname> are <classname>GObjects</classname> and
have a reference count of 1 after creation. The tree view will add its
own reference to the model when you add the model with
<literal>gtk_tree_view_set_model</literal>, and will unref it again
when you replace the model with another model, unset the model by
passing <literal>NULL</literal> as a model, or when the tree view is
destroyed. <footnote>
<para>'Reference counting' means that an object has a counter that
can be increased or decreased (ref-ed and unref-ed). If the
counter is unref-ed to 0, the object is automatically destroyed.
This is useful, because other objects or application programmers
only have to think about whether <emphasis>they
themselves</emphasis> are still using that object or not, without
knowing anything about others also using it. The object is simply
automatically destroyed when no one is using it any more.</para>
</footnote></para>
<para>This means that you need to take care of "your" reference
yourself, otherwise the model will not be destroyed properly when you
disconnect it from the tree view, and its memory will not be freed
(which does not matter much if the same model is connected to the tree
view from application start to end). If you plan to use the same model
for a tree view for the whole duration of the application, you can get
rid of "your" reference right after you have connected the model to
the view - then the model will be destroyed automatically when the
tree view is destroyed (which will be automatically destroyed when the
window it is in is destroyed):</para>
<programlisting role="C">
GtkListStore *liststore;
GtkWidget *view;
view = gtk_tree_view_new();
liststore = gtk_list_store_new(1, G_TYPE_STRING);
gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(liststore));
g_object_unref(liststore);
/* Now the model will be destroyed when the tree view is destroyed */
</programlisting>
</sect2>
</sect1>
<sect1 id="sec-treeview-look">
<title>Tree View Look and Feel</title>
<para>There are a couple of ways to influence the look and feel of the
tree view. You can hide or show column headers with <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeView.html#gtk-tree-view-set-headers-visible">
<literal>gtk_tree_view_set_headers_visible</literal></ulink>, and set
them clickable or not with <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeView.html#gtk-tree-view-set-headers-clickable">
<literal>gtk_tree_view_set_headers_clickable</literal></ulink> (which
will be done automatically for you if you enable sorting).</para>
<para><ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeView.html#gtk-tree-view-set-rules-hint">
<literal>gtk_tree_view_set_rules_hint</literal></ulink> will enable or
disable rules in the tree view. <footnote>
<para>'Rules' means that every second line of the tree view has a
shaded background, which makes it easier to see which cell belongs
to which row in tree views that have a lot of columns.</para>
</footnote> As the function name implies, this setting is only a hint;
in the end it depends on the active Gtk+ theme engine if the tree view
shows ruled lines or not. Users seem to have strong feelings about rules
in tree views, so it is probably a good idea to provide an option
somewhere to disable rule hinting if you set it on tree views (but then,
people also seem to have strong feelings about options abundance and
'sensible' default options, so whatever you do will probably upset
someone at some point).</para>
<para>The expander column can be set with <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeView.html#gtk-tree-view-set-expander-column">
<literal>gtk_tree_view_set_expander_column</literal></ulink>. This is
the column where child elements are indented with respect to their
parents, and where rows with children have an 'expander' arrow with
which a node's children can be collapsed (hidden) or expanded (shown).
By default, this is the first column.</para>
</sect1>
</chapter>
<chapter id="sec-treeviewcol-renderer">
<title>Mapping Data to the Screen: GtkTreeViewColumn and
GtkCellRenderer</title>
<para>As outlined above, tree view columns represent the visible columns
on the screen that have a column header with a column name and can be
resized or sorted. A tree view is made up of tree view columns, and you
need at least one tree view column in order to display something in the
tree view. Tree view columns, however, do not display anything by
themselves, this is done by specialised <classname>GtkCellRenderer</classname>
objects. Cell renderers are packed into tree view columns much like
widgets are packed into <classname>GtkHBox</classname>es.</para>
<para>Here is a diagram (courtesy of Owen Taylor) that pictures the
relationship between tree view columns and cell renderers:</para>
<figure id="sec-treeviewcol-renderer-diagram">
<title>Cell Renderer Properties</title>
<screenshot>
<mediaobject>
<imageobject>
<imagedata fileref="images/tree-view-column.png" format="PNG"/>
</imageobject>
<imageobject>
<imagedata fileref="images/tree-view-column.eps" format="EPS"/>
</imageobject>
</mediaobject>
</screenshot>
</figure>
<para>In the above diagram, both 'Country' and 'Representative' are tree
view columns, where the 'Country' and 'Representative' labels are the
column headers. The 'Country' column contains two cell renderers, one to
display the flag icons, and one to display the country name. The
'Representative' column only contains one cell renderer to display the
representative's name.</para>
<sect1 id="sec-renderer">
<title>Cell Renderers</title>
<para>Cell renderers are objects that are responsible for the actual
rendering of data within a <classname>GtkTreeViewColumn</classname>. They
are basically just GObjects (ie. not widgets) that have certain
properties, and those properties determine how a single cell is
drawn.</para>
<para>In order to draw cells in different rows with different content, a
cell renderer's properties need to be set accordingly for each single
row/cell to render. This is done either via <link
linkend="sec-treeview-col-attributes">attributes</link> or <link
linkend="sec-treeview-col-celldatafunc">cell data functions</link> (see
below). If you set up attributes, you tell Gtk which model column
contains the data from which a property should be set before rendering a
certain row. Then the properties of a cell renderer are set
automatically according to the data in the model before each row is
rendered. Alternatively, you can set up cell data functions, which are
called for each row to be rendererd, so that you can manually set the
properties of the cell renderer before it is rendered. Both approaches
can be used at the same time as well. Lastly, you can set a cell
renderer property when you create the cell renderer. That way it will be
used for all rows/cells to be rendered (unless it is changed later of
course).</para>
<para>Different cell renderers exist for different purposes:</para>
<itemizedlist>
<listitem>
<simpara><ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkCellRendererText.html">
<classname>GtkCellRendererText</classname></ulink> renders strings or
numbers or boolean values as text ("Joe", "99.32", "true")</simpara>
</listitem>
<listitem>
<simpara><ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkCellRendererPixbuf.html">
<classname>GtkCellRendererPixbuf</classname></ulink> is used to display
images; either user-defined images, or one of the stock icons that
come with Gtk+.</simpara>
</listitem>
<listitem>
<simpara><ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkCellRendererToggle.html">
<classname>GtkCellRendererToggle</classname></ulink> displays a boolean
value in form of a check box or as a radio button.</simpara>
</listitem>
<listitem>
<simpara><ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkCellEditable.html">
<classname>GtkCellEditable</classname></ulink> is a special cell that
implements editable cells (ie. GtkEntry or GtkSpinbutton in a
treeview). This is not a cell renderer! If you want to have editable
text cells, use <classname>GtkCellRendererText</classname> and make sure
the "editable" property is set. <classname>GtkCellEditable</classname>
is only used by implementations of editable cells and widgets that
can be inside of editable cells. You are unlikely to ever need
it.</simpara>
</listitem>
</itemizedlist>
<para>Contrary to what one may think, a cell renderer does not render
just one single cell, but is responsible for rendering part or whole of
a tree view column for each single row. It basically starts in the first
row and renders its part of the column there. Then it proceeds to the
next row and renders its part of the column there again. And so
on.</para>
<para>How does a cell renderer know what to render? A cell renderer
object has certain 'properties' that are documented in the <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/TreeWidgetObjects.html">
API reference</ulink> (just like most other objects, and widgets). These
properties determine what the cell renderer is going to render and how
it is going to be rendered. Whenever the cell renderer is called upon to
render a certain cell, it looks at its properties and renders the cell
accordingly. This means that whenever you set a property or change a
property of the cell renderer, this will affect all rows that are
rendered after the change, until you change the property again.</para>
<para>Here is a diagram (courtesy of Owen Taylor) that tries to show
what is going on when rows are rendered:</para>
<figure id="sec-treeviewcol-diagram">
<title>GtkTreeViewColumns and GtkCellRenderers</title>
<screenshot>
<mediaobject>
<imageobject>
<imagedata fileref="images/cell-renderer-properties.png"
format="PNG"/>
</imageobject>
<imageobject>
<imagedata fileref="images/cell-renderer-properties.eps"
format="EPS"/>
</imageobject>
</mediaobject>
</screenshot>
</figure>
<para>The above diagram shows the process when attributes are used. In
the example, a text cell renderer's <literal>"text"</literal> property
has been linked to the first model column. The <literal>"text"</literal>
property contains the string to be rendered. The
<literal>"foreground"</literal> property, which contains the colour of
the text to be shown, has been linked to the second model column.
Finally, the <literal>"strikethrough"</literal> property, which
determines whether the text should be with a horizontal line that
strikes through the text, has been connected to the third model column
(of type <literal>G_TYPE_BOOLEAN</literal>).</para>
<para>With this setup, the cell renderer's properties are 'loaded' from
the model before each cell is rendered.</para>
<para>Here is a silly and utterly useless little example that
demonstrates this behaviour, and introduces some of the most commonly
used properties of <classname>GtkCellRendererText</classname>:</para>
<programlisting role="C">
#include &lt;gtk/gtk.h&gt;
enum
{
COL_FIRST_NAME = 0,
COL_LAST_NAME,
NUM_COLS
} ;
static GtkTreeModel *
create_and_fill_model (void)
{
GtkTreeStore *treestore;
GtkTreeIter toplevel, child;
treestore = gtk_tree_store_new(NUM_COLS, G_TYPE_STRING, G_TYPE_STRING);
/* Append a top level row and leave it empty */
gtk_tree_store_append(treestore, &amp;toplevel, NULL);
/* Append a second top level row, and fill it with some data */
gtk_tree_store_append(treestore, &amp;toplevel, NULL);
gtk_tree_store_set(treestore, &amp;toplevel,
COL_FIRST_NAME, "Joe",
COL_LAST_NAME, "Average",
-1);
/* Append a child to the second top level row, and fill in some data */
gtk_tree_store_append(treestore, &amp;child, &amp;toplevel);
gtk_tree_store_set(treestore, &amp;child,
COL_FIRST_NAME, "Jane",
COL_LAST_NAME, "Average",
-1);
return GTK_TREE_MODEL(treestore);
}
static GtkWidget *
create_view_and_model (void)
{
GtkTreeViewColumn *col;
GtkCellRenderer *renderer;
GtkWidget *view;
GtkTreeModel *model;
view = gtk_tree_view_new();
/* --- Column #1 --- */
col = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(col, "First Name");
/* pack tree view column into tree view */
gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
renderer = gtk_cell_renderer_text_new();
/* pack cell renderer into tree view column */
gtk_tree_view_column_pack_start(col, renderer, TRUE);
/* set 'text' property of the cell renderer */
g_object_set(renderer, "text", "Boooo!", NULL);
/* --- Column #2 --- */
col = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(col, "Last Name");
/* pack tree view column into tree view */
gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
renderer = gtk_cell_renderer_text_new();
/* pack cell renderer into tree view column */
gtk_tree_view_column_pack_start(col, renderer, TRUE);
/* set 'cell-background' property of the cell renderer */
g_object_set(renderer,
"cell-background", "Orange",
"cell-background-set", TRUE,
NULL);
model = create_and_fill_model();
gtk_tree_view_set_model(GTK_TREE_VIEW(view), model);
g_object_unref(model); /* destroy model automatically with view */
gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(view)),
GTK_SELECTION_NONE);
return view;
}
int
main (int argc, char **argv)
{
GtkWidget *window;
GtkWidget *view;
gtk_init(&amp;argc, &amp;argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
g_signal_connect(window, "delete_event", gtk_main_quit, NULL); /* dirty */
view = create_view_and_model();
gtk_container_add(GTK_CONTAINER(window), view);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
</programlisting>
<para>The above code should produce something looking like this:</para>
<figure id="sec-renderer-sshot">
<title>Persistent Cell Renderer Properties</title>
<screenshot>
<mediaobject>
<imageobject>
<imagedata fileref="images/renderer.png" format="PNG"/>
</imageobject>
<imageobject>
<imagedata fileref="images/renderer.eps" format="EPS"/>
</imageobject>
</mediaobject>
</screenshot>
</figure>
<para>It looks like the tree view display is partly correct and partly
incomplete. On the one hand the tree view renders the correct number of
rows (note how there is no orange on the right after row 3), and it
displays the hierarchy correctly (on the left), but it does not display
any of the data that we have stored in the model. This is because we
have made no connection between what the cell renderers should render
and the data in the model. We have simply set some cell renderer
properties on start-up, and the cell renderers adhere to those set
properties meticulously.</para>
<para>There are two different ways to connect cell renderers to data in
the model: <link linkend="sec-treeview-col-attributes">attributes</link>
and <link linkend="sec-treeview-col-celldatafunc">cell data
functions</link>.</para>
</sect1>
<sect1 id="sec-treeview-col-attributes">
<title>Attributes</title>
<para>An attribute is a connection between a cell renderer property and
a field/column in the model. Whenever a cell is to be rendered, a cell
renderer property will be set to the values of the specified model
column of the row that is to be rendered. It is very important that the
column's data type is the same type that a property takes according to
the API reference manual. Here is some code to look at:</para>
<programlisting role="C">
...
col = gtk_tree_view_column_new();
renderer = gtk_cell_renderer_text_new();
gtk_tree_view_column_pack_start(col, renderer, TRUE);
gtk_tree_view_column_add_attribute(col, renderer, "text", COL_FIRST_NAME);
...
</programlisting>
<para>This means that the text cell renderer property
<literal>"text"</literal> will be set to the string in model column
<literal>COL_FIRST_NAME</literal> of each row to be drawn. It is
important to internalise the difference between <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeViewColumn.html#gtk-tree-view-column-add-attribute">
<literal>gtk_tree_view_column_add_attribute</literal></ulink> and
<literal>g_object_set</literal>: <literal>g_object_set</literal> sets a
property to a certain <emphasis>value</emphasis>, while
<literal>gtk_tree_view_column_add_attribute</literal> sets a property to
whatever is in the specified _model column_ at the time of
rendering.</para>
<para>Again, when setting attributes it is very important that the data
type stored in a model column is the same as the data type that a
property requires as argument. Check the API reference manual to see the
data type that is required for each property. When reading through the
example a bit further above, you might have noticed that we set the
<literal>"cell-background"</literal> property of a <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkCellRendererText.html">
<classname>GtkCellRendererText</classname></ulink>, even though the API
documentation does not list such a property. We can do this, because
<classname>GtkCellRendererText</classname> is derived from <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkCellRenderer.html">
<classname>GtkCellRenderer</classname></ulink>, which does in fact <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkCellRenderer.html#GtkCellRenderer--cell-background">
have</ulink> such a property. Derived classes inherit the properties of
their parents. This is the same as with widgets that you can cast into
one of their ancestor classes. The API reference has an <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/ch01.html">object
hierarchy</ulink> that shows you which classes a widget or some other
object is derived from.</para>
<para>There are two more noteworthy things about
<classname>GtkCellRenderer</classname> properties: one is that sometimes
there are different properties which do the same, but take different
arguments, such as the <literal>"foreground"</literal> and
<literal>"foreground-gdk"</literal> properties of
<classname>GtkCellRendererText</classname> (which specify the text colour).
The <literal>"foreground"</literal> property take a colour in string
form, such as "Orange" or "CornflowerBlue", whereas
<literal>"foreground-gdk"</literal> takes a <ulink
url="http://developer.gnome.org/doc/API/2.0/gdk/gdk-Colormaps-and-Colors.html#GdkColor">GdkColor</ulink>
argument. It is up to you to decide which one to use - the effect will
be the same. The other thing worth mentioning is that most properties
have a <literal>"foo-set"</literal> property taking a boolean value as
argument, such as <literal>"foreground-set"</literal>. This is useful
when you want to have a certain setting have an effect or not. If you
set the <literal>"foreground"</literal> property, but set
<literal>"foreground-set"</literal> to <literal>FALSE</literal>, then
your foreground color setting will be disregarded. This is useful in
cell data functions (see below), or, for example, if you want set the
foreground colour to a certain value at start-up, but only want this to
be in effect in some columns, but not in others (in which case you could
just connect the <literal>"foreground-set"</literal> property to a model
column of type <literal>G_TYPE_BOOLEAN</literal> with <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeViewColumn.html#gtk-tree-view-column-add-attribute">
<literal>gtk_tree_view_column_add_attribute</literal></ulink>.</para>
<para>Setting column attributes is the most straight-forward way to get
your model data to be displayed. This is usually used whenever you want
the data in the model to be displayed exactly as it is in the
model.</para>
<para>Another way to get your model data displayed on the screen is to
set up cell data functions.</para>
</sect1>
<sect1 id="sec-treeview-col-celldatafunc">
<title>Cell Data Functions</title>
<para>A cell data function is a function that is called for a specific
cell renderer for each single row before that row is rendered. It gives
you maximum control over what exactly is going to be rendered, as you
can set the cell renderer's properties just like you want to have them.
Remember not only to <emphasis>set</emphasis> a property if you want it
to be active, but also to <emphasis>unset</emphasis> a property if it
should not be active (and it might have been set in the previous
row).</para>
<para>Cell data functions are often used if you want more fine-grained
control over what is to be displayed, or if the standard way to display
something is not quite like you want it to be. A case in point are
floating point numbers. If you want floating point numbers to be
displayed in a certain way, say with only one digit after the
colon/comma, then you need to use a cell data function. Use <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeViewColumn.html#gtk-tree-view-column-set-cell-data-func">
<literal>gtk_tree_view_column_set_cell_data_func</literal></ulink> to
set up a cell data function for a particular cell renderer. Here is an
example:</para>
<programlisting role="C">
enum
{
COLUMN_NAME = 0,
COLUMN_AGE_FLOAT,
NUM_COLS
};
...
static void
age_cell_data_function (GtkTreeViewColumn *col,
GtkCellRenderer *renderer,
GtkTreeModel *model,
GtkTreeIter *iter,
gpointer user_data)
{
gfloat age;
gchar buf[20];
gtk_tree_model_get(model, iter, COLUMN_AGE_FLOAT, &amp;age, -1);
g_snprintf(buf, sizeof(buf), "%.1f", age);
g_object_set(renderer, "text", buf, NULL);
}
...
liststore = gtk_list_store_new(NUM_COLS, G_TYPE_STRING, G_TYPE_FLOAT);
col = gtk_tree_view_column_new();
cell = gtk_cell_renderer_text_new();
gtk_tree_view_column_pack_start(col, cell, TRUE);
gtk_tree_view_column_set_cell_data_func(col, cell, age_cell_data_func, NULL, NULL);
...
</programlisting>
<para>for each row to be rendered by this particular cell renderer, the
cell data function is going to be called, which then retrieves the float
from the model, and turns it into a string where the float has only one
digit after the colon/comma, and renders that with the text cell
renderer.</para>
<para>This is only a simple example, you can make cell data functions a
lot more complicated if you want to. As always, there is a trade-off to
keep in mind though. Your cell data function is going to be called every
single time a cell in that (renderer) column is going to be rendered. Go
and check how often this function is called in your program if you ever
use one. If you do time-consuming operations within a cell data
function, things are not going to be fast, especially if you have a lot
of rows. The alternative in this case would have been to make an
additional column COLUMN_AGE_FLOAT_STRING of type
<literal>G_TYPE_STRING</literal>, and to set the float in string form
whenever you set the float itself in a row, and then hook up the string
column to a text cell renderer using attributes. This way the float to
string conversion would only need to be done once. This is a cpu cycles
/ memory trade-off, and it depends on your particular case which one is
more suitable. Things you should probably not do is to convert long
strings into UTF8 format in a cell data function, for example.</para>
<para>You might notice that your cell data function is called at times
even for rows that are not visible at the moment. This is because the
tree view needs to know its total height, and in order to calculate this
it needs to know the height of each and every single row, and it can
only know that by having it measured, which is going to be slow when you
have a lot of rows with different heights (if your rows all have the
same height, there should not be any visible delay though).</para>
</sect1>
<sect1 id="sec-treeview-celltext-types">
<title>GtkCellRendererText and Integer, Boolean and Float Types</title>
<para>It has been said before that, when using attributes to connect
data from the model to a cell renderer property, the data in the model
column specified in
<literal>gtk_tree_view_column_add_attribute</literal> must always be of
the same type as the data type that the property requires.</para>
<para>This is usually true, but there is an exception: if you use
<literal>gtk_tree_view_column_add_attribute</literal> to connect a text
cell renderer's <literal>"text"</literal> property to a model column,
the model column does not need to be of
<literal>G_TYPE_STRING</literal>, it can also be one of most other
fundamental GLib types, e.g. <literal>G_TYPE_BOOLEAN</literal>,
<literal>G_TYPE_INT</literal>, <literal>G_TYPE_UINT</literal>,
<literal>G_TYPE_LONG</literal>, <literal>G_TYPE_ULONG</literal>,
<literal>G_TYPE_INT64</literal>, <literal>G_TYPE_UINT64</literal>,
<literal>G_TYPE_FLOAT</literal>, or <literal>G_TYPE_DOUBLE</literal>.
The text cell renderer will automatically display the values of these
types correctly in the tree view. For example:</para>
<programlisting role="C">
enum
{
COL_NAME = 0,
COL_YEAR_BORN,
NUM_COLS
};
liststore = gtk_list_store_new(NUM_COLS, G_TYPE_STRING, G_TYPE_UINT);
...
cell = gtk_cell_renderer_text_new();
col = gtk_tree_view_column_new();
gtk_tree_view_column_add_attribute(col, cell, "text", COL_YEAR_BORN);
...
</programlisting>
<para>Even though the <literal>"text"</literal> property would require a
string value, we use a model column of an integer type when setting
attributes. The integer will then automatically be converted into a
string before the cell renderer property is set <footnote>
<para>For those interested, the conversion actually takes place
within <literal>g_object_set_property</literal>. Before a certain
cell is rendered, the tree view column will call
<literal>gtk_tree_model_get_value</literal> to set the cell renderer
properties according to values stored in the tree model (if any are
mapped via <literal>gtk_tree_view_column_add_attribute</literal> or
one of the convenience functions that do the same thing), and then
pass on the <classname>GValue</classname> retrieved to
<literal>g_object_set_property</literal>.</para>
</footnote>.</para>
<para>If you are using a floating point type, ie.
<literal>G_TYPE_FLOAT</literal> or <literal>G_TYPE_DOUBLE</literal>,
there is no way to tell the text cell renderer how many digits after the
floating point (or comma) should be rendered. If you only want a certain
amount of digits after the point/comma, you will need to use a <link
linkend="sec-treeview-col-celldatafunc">cell data
function</link>.</para>
</sect1>
<sect1 id="sec-treeview-col-utf8-pango">
<title>GtkCellRendererText, UTF8, and pango markup</title>
<para>All text used in Gtk+-2.0 widgets needs to be in UTF8 encoding,
and <literal>GtkCellRendererText</literal> is no exception. Text in
plain ASCII is automatically valid UTF8, but as soon as you have special
characters that do not exist in plain ASCII (usually characters that are
not used in the English language alphabet), they need to be in UTF8
encoding. There are many different character encodings that all specify
different ways to tell the computer which character is meant. Gtk+-2.0
uses UTF8, and whenever you have text that is in a different encoding,
you need to convert it to UTF8 encoding first, using one of the GLib
<ulink
url="http://developer.gnome.org/doc/API/2.0/glib/glib-Character-Set-Conversion.html">
<literal>g_convert</literal></ulink> family of functions. If you only
use text input from other Gtk+ widgets, you are on the safe side, as
they will return all text in UTF8 as well.</para>
<para>However, if you use 'external' sources of text input, then you
must convert that text from the text's encoding (or the user's locale)
to UTF8, or it will not be rendered correctly (either not at all, or it
will be cut off after the first invalid character). Filenames are
especially hard, because there is no indication whatsoever what
character encoding a filename is in (it might have been created when the
user was using a different locale, so filename encoding is basically
unreliable and broken). You may want to convert to UTF8 with fallback
characters in that case. You can check whether a string is valid UTF8
with <ulink
url="http://developer.gnome.org/doc/API/2.0/glib/glib-Unicode-Manipulation.html#g-utf8-validate">
<literal>g_utf8_validate</literal></ulink>. You should, in this author's
opinion at least, put these checks into your code at crucial places
wherever it is not affecting performance, especially if you are an
English-speaking programmer that has little experience with non-English
locales. It will make it easier for others and yourself to spot problems
with non-English locales later on.</para>
<para>In addition to the "text" property, GtkCellRendererText also has a
"markup" property that takes text with <ulink
url="http://developer.gnome.org/doc/API/2.0/pango/PangoMarkupFormat.html">
pango markup</ulink> as input. Pango markup allows you to place special
tags into a text string that affect the style the text is rendered (see
the pango documentation). Basically you can achieve everything you can
achieve with the other properties also with pango markup (only that
using properties is more efficient and less messy). Pango markup has one
distinct advantage though that you cannot achieve with text cell
renderer properties: with pango markup, you can change the text style in
the middle of the text, so you could, for example, render one part of a
text string in bold print, and the rest of the text in normal. Here is
an example of a string with pango markup:</para>
<para><literal> "You can have text in &lt;b&gt;bold&lt;/b&gt; or in a
&lt;span color='Orange'&gt;different color&lt;/span&gt;"
</literal></para>
<para>When using the <literal>"markup"</literal> property, you need to
take into account that the <literal>"markup"</literal> and
<literal>"text"</literal> properties do not seem to be mutually
exclusive (I suppose this could be called a bug). In other words:
whenever you set <literal>"markup"</literal> (and have used the
<literal>"text"</literal> property before), set the
<literal>"text"</literal> property to NULL, and vice versa.
Example:</para>
<programlisting role="C">
...
void
foo_cell_data_function ( ... )
{
...
if (foo-&gt;is_important)
g_object_set(renderer, "markup", "&lt;b&gt;important&lt;/b&gt;", "text", NULL, NULL);
else
g_object_set(renderer, "markup", NULL, "text", "not important", NULL);
...
}
...
</programlisting>
<para>Another thing to keep in mind when using pango markup text is that
you might need to escape text if you construct strings with pango markup
on the fly using random input data. For example:</para>
<programlisting role="C">
...
void
foo_cell_data_function ( ... )
{
gchar *markuptxt;
...
/* This might be problematic if artist_string or title_string
* contain markup characters/entities: */
markuptxt = g_strdup_printf("&lt;b&gt;%s&lt;/b&gt; - &lt;i&gt;%s&lt;/i&gt;",
artist_string, title_string);
...
g_object_set(renderer, "markup", markuptxt, "text", NULL, NULL);
...
g_free(markuptxt);
}
...
</programlisting>
<para>The above example will not work if artist_string is "Simon &amp;
Garfunkel" for example, because the &amp; character is one of the
characters that is special. They need to be escaped, so that pango knows
that they do not refer to any pango markup, but are just characters. In
this case the string would need to be "Simon &amp;amp; Garfunkel" in
order to make sense in between the pango markup in which it is going to
be pasted. You can escape a string with <ulink
url="http://developer.gnome.org/doc/API/2.0/glib/glib-Simple-XML-Subset-Parser.html#g-markup-escape-text">
<literal>g_markup_escape</literal></ulink> (and you will need to free
the resulting newly-allocated string again with
<literal>g_free</literal>).</para>
<para>It is possible to combine both pango markup and text cell renderer
properties. Both will be 'added' together to render the string in
question, only that the text cell renderer properties will be applied to
the whole string. If you set the <literal>"markup"</literal> property to
normal text without any pango markup, it will render as normal text just
as if you had used the <literal>"text"</literal> property. However, as
opposed to the <literal>"text"</literal> property, special characters in
the <literal>"markup"</literal> property text would still need to be
escaped, even if you do not use pango markup in the text.</para>
</sect1>
<sect1 id="sec-treeview-col-example">
<title>A Working Example</title>
<para>Here is our example from the very beginning again (with an
additional column though), only that the contents of the model are
rendered properly on the screen this time. Both attributes and a cell
data function are used for demonstration purposes.</para>
<programlisting role="C">
#include &lt;gtk/gtk.h&gt;
enum
{
COL_FIRST_NAME = 0,
COL_LAST_NAME,
COL_YEAR_BORN,
NUM_COLS
} ;
static GtkTreeModel *
create_and_fill_model (void)
{
GtkTreeStore *treestore;
GtkTreeIter toplevel, child;
treestore = gtk_tree_store_new(NUM_COLS,
G_TYPE_STRING,
G_TYPE_STRING,
G_TYPE_UINT);
/* Append a top level row and leave it empty */
gtk_tree_store_append(treestore, &amp;toplevel, NULL);
gtk_tree_store_set(treestore, &amp;toplevel,
COL_FIRST_NAME, "Maria",
COL_LAST_NAME, "Incognito",
-1);
/* Append a second top level row, and fill it with some data */
gtk_tree_store_append(treestore, &amp;toplevel, NULL);
gtk_tree_store_set(treestore, &amp;toplevel,
COL_FIRST_NAME, "Jane",
COL_LAST_NAME, "Average",
COL_YEAR_BORN, (guint) 1962,
-1);
/* Append a child to the second top level row, and fill in some data */
gtk_tree_store_append(treestore, &amp;child, &amp;toplevel);
gtk_tree_store_set(treestore, &amp;child,
COL_FIRST_NAME, "Janinita",
COL_LAST_NAME, "Average",
COL_YEAR_BORN, (guint) 1985,
-1);
return GTK_TREE_MODEL(treestore);
}
void
age_cell_data_func (GtkTreeViewColumn *col,
GtkCellRenderer *renderer,
GtkTreeModel *model,
GtkTreeIter *iter,
gpointer user_data)
{
guint year_born;
guint year_now = 2003; /* to save code not relevant for the example */
gchar buf[64];
gtk_tree_model_get(model, iter, COL_YEAR_BORN, &amp;year_born, -1);
if (year_born &lt;= year_now &amp;&amp; year_born &gt; 0)
{
guint age = year_now - year_born;
g_snprintf(buf, sizeof(buf), "%u years old", age);
g_object_set(renderer, "foreground-set", FALSE, NULL); /* print this normal */
}
else
{
g_snprintf(buf, sizeof(buf), "age unknown");
/* make red */
g_object_set(renderer, "foreground", "Red", "foreground-set", TRUE, NULL);
}
g_object_set(renderer, "text", buf, NULL);
}
static GtkWidget *
create_view_and_model (void)
{
GtkTreeViewColumn *col;
GtkCellRenderer *renderer;
GtkWidget *view;
GtkTreeModel *model;
view = gtk_tree_view_new();
/* --- Column #1 --- */
col = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(col, "First Name");
/* pack tree view column into tree view */
gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
renderer = gtk_cell_renderer_text_new();
/* pack cell renderer into tree view column */
gtk_tree_view_column_pack_start(col, renderer, TRUE);
/* connect 'text' property of the cell renderer to
* model column that contains the first name */
gtk_tree_view_column_add_attribute(col, renderer, "text", COL_FIRST_NAME);
/* --- Column #2 --- */
col = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(col, "Last Name");
/* pack tree view column into tree view */
gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
renderer = gtk_cell_renderer_text_new();
/* pack cell renderer into tree view column */
gtk_tree_view_column_pack_start(col, renderer, TRUE);
/* connect 'text' property of the cell renderer to
* model column that contains the last name */
gtk_tree_view_column_add_attribute(col, renderer, "text", COL_LAST_NAME);
/* set 'weight' property of the cell renderer to
* bold print (we want all last names in bold) */
g_object_set(renderer,
"weight", PANGO_WEIGHT_BOLD,
"weight-set", TRUE,
NULL);
/* --- Column #3 --- */
col = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(col, "Age");
/* pack tree view column into tree view */
gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
renderer = gtk_cell_renderer_text_new();
/* pack cell renderer into tree view column */
gtk_tree_view_column_pack_start(col, renderer, TRUE);
/* connect a cell data function */
gtk_tree_view_column_set_cell_data_func(col, renderer, age_cell_data_func, NULL, NULL);
model = create_and_fill_model();
gtk_tree_view_set_model(GTK_TREE_VIEW(view), model);
g_object_unref(model); /* destroy model automatically with view */
gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(view)),
GTK_SELECTION_NONE);
return view;
}
int
main (int argc, char **argv)
{
GtkWidget *window;
GtkWidget *view;
gtk_init(&amp;argc, &amp;argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
g_signal_connect(window, "delete_event", gtk_main_quit, NULL); /* dirty */
view = create_view_and_model();
gtk_container_add(GTK_CONTAINER(window), view);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
</programlisting>
</sect1>
<sect1 id="sec-treeview-col-whole-row">
<title>How to Make a Whole Row Bold or Coloured</title>
<para>This seems to be a frequently asked question, so it is worth
mentioning it here. You have the two approaches mentioned above: either
you use cell data functions, and check in each whether a particular row
should be highlighted in a particular way (bold, coloured, whatever),
and then set the renderer properties accordingly (and unset them if you
want that row to look normal), or you use attributes. Cell data
functions are most likely not the right choice in this case
though.</para>
<para>If you only want every second line to have a gray background to
make it easier for the user to see which data belongs to which line in
wide tree views, then you do not have to bother with the stuff mentioned
here. Instead just set the rules hint on the tree view as described in
the <link linkend="sec-treeview-look"> here</link>, and everything will
be done automatically, in colours that conform to the chosen theme even
(unless the theme disables rule hints, that is).</para>
<para>Otherwise, the most suitable approach for most cases is that you
add two columns to your model, one for the property itself (e.g. a
column COL_ROW_COLOR of type <literal>G_TYPE_STRING</literal>), and one
for the boolean flag of the property (e.g. a column COL_ROW_COLOR_SET of
type <literal>G_TYPE_BOOLEAN</literal>). You would then connect these
columns with the <literal>"foreground"</literal> and
<literal>"foreground-set"</literal> properties of each renderer. Now,
whenever you set a row's COL_ROW_COLOR field to a colour, and set that
row's COL_ROW_COLOR_SET field to <literal>TRUE</literal>, then this
column will be rendered in the colour of your choice. If you only want
either the default text colour or one special other colour, you could
even achieve the same thing with just one extra model column: in this
case you could just set all renderer's <literal>"foreground"</literal>
property to whatever special color you want, and only connect the
COL_ROW_COLOR_SET column to all renderer's
<literal>"foreground-set"</literal> property using attributes. This
works similar with any other attribute, only that you need to adjust the
data type for the property of course (e.g. <literal>"weight"</literal>
would take a <literal>G_TYPE_INT</literal>, in form of a
<literal>PANGO_WEIGHT_FOO</literal> define in this case).</para>
<para>As a general rule, you should not change the text colour or the
background colour of a cell unless you have a really good reason for it.
To <ulink url="http://ometer.com/gtk-colors.html">quote</ulink> Havoc
Pennington: <quote>Because colors in GTK+ represent a theme the user has
chosen, you should never set colors purely for aesthetic reasons. If
users don't like GTK+ gray, they can change it themselves to their
favorite shade of orange.</quote></para>
</sect1>
<sect1 id="sec-treeview-col-pixbufs">
<title>How to Pack Icons into the Tree View</title>
<para>So far we have only put text in the tree view. While everything
you need to know to display icons (in the form of
<classname>GdkPixbuf</classname>s) has been introduced in the previous
sections, a short example might help to make things clearer. The
following code will pack an icon and some text into the same tree view
column:</para>
<programlisting role="C">
enum
{
COL_ICON = 0,
COL_TEXT,
NUM_COLS
};
GtkListStore *
create_liststore(void)
{
GtkListStore *store;
GtkTreeIter iter;
GdkPixbuf *icon;
GError *error = NULL;
store = gtk_list_store_new(2, GDK_TYPE_PIXBUF, G_TYPE_STRING);
icon = gdk_pixbuf_new_from_file("icon.png", &amp;error);
if (error)
{
g_warning ("Could not load icon: %s\n", error-&gt;message);
g_error_free(error);
error = NULL;
}
gtk_list_store_append(store, &amp;iter);
gtk_list_store_set(store, &amp;iter,
COL_ICON, icon,
COL_TEXT, "example",
-1);
return store;
}
GtkWidget *
create_treeview(void)
{
GtkTreeModel *model;
GtkTreeViewColumn *col;
GtkCellRenderer *renderer;
GtkWidget *view;
model = GTK_TREE_MODEL(create_liststore());
view = gtk_tree_view_new_with_model(model);
col = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(col, "Title");
renderer = gtk_cell_renderer_pixbuf_new();
gtk_tree_view_column_pack_start(col, renderer, FALSE);
gtk_tree_view_column_set_attributes(col, renderer,
"pixbuf", COL_ICON,
NULL);
renderer = gtk_cell_renderer_text_new();
gtk_tree_view_column_pack_start(col, renderer, TRUE);
gtk_tree_view_column_set_attributes(col, renderer,
"text", COL_TEXT,
NULL);
gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
gtk_widget_show_all(view);
return view;
}
</programlisting>
<para>Note that the tree view will not resize icons for you, but
displays them in their original size. If you want to display <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/gtk-Stock-Items.html">
stock icons</ulink> instead of <classname>GdkPixbuf</classname>s loaded from
file, you should have a look at the <literal>"stock-id"</literal>
property of <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkCellRendererPixbuf.html">
<classname>GtkCellRendererPixbuf</classname></ulink> (and your model column
should be of type <literal>G_TYPE_STRING</literal>, as all stock IDs are
just strings by which to identify the stock icon).</para>
</sect1>
</chapter>
<chapter id="sec-sel-click-menus">
<title>Selections, Double-Clicks and Context Menus</title>
<sect1 id="sec-selections">
<title>Handling Selections</title>
<para>One of the most basic features of a list or tree view is that rows
can be selected or unselected. Selections are handled using the <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeSelection.html">
<classname>GtkTreeSelection</classname></ulink> object of a tree view. Every
tree view automatically has a <classname>GtkTreeSelection</classname>
associated with it, and you can get it using <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeView.html#gtk-tree-view-get-selection">
<literal>gtk_tree_view_get_selection</literal></ulink>. Selections are
handled completely on the tree view side, which means that the model
knows nothing about which rows are selected or not. There is no
particular reason why selection handling could not have been implemented
with functions that access the tree view widget directly, but for
reasons of API cleanliness and code clarity the Gtk+ developers decided
to create this special <classname>GtkTreeSelection</classname> object that
then internally deals with the tree view widget. You will never need to
create a tree selection object, it will be created for you automatically
when you create a new tree view. You only need to use said
<literal>gtk_tree_view_get_selection</literal> function to get a pointer
to the selection object.</para>
<para>There are three ways to deal with tree view selections: either you
get a list of the currently selected rows whenever you need it, for
example within a context menu function, or you keep track of all select
and unselect actions and keep a list of the currently selected rows
around for whenever you need them; as a last resort, you can also
traverse your list or tree and check each single row for whether it is
selected or not (which you need to do if you want all rows that are
<emphasis>not</emphasis> selected for example).</para>
<sect2 id="sec-selections-modes">
<title>Selection Modes</title>
<para>You can use <literal>gtk_tree_selection_set_mode</literal> to
influence the way that selections are handled. There are four
selection modes:</para>
<itemizedlist>
<listitem>
<simpara><literal>GTK_SELECTION_NONE</literal> - no items can be
selected</simpara>
</listitem>
<listitem>
<simpara><literal>GTK_SELECTION_SINGLE</literal> - no more than
one item can be selected</simpara>
</listitem>