treeview-tutorial-xml/treeview-tutorial.xml

7440 lines
288 KiB
XML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?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>
<listitem>
<simpara><literal>GTK_SELECTION_BROWSE</literal> - exactly one
item is always selected</simpara>
</listitem>
<listitem>
<simpara><literal>GTK_SELECTION_MULTIPLE</literal> - anything
between no item and all items can be selected</simpara>
</listitem>
</itemizedlist>
</sect2>
<sect2 id="sec-selections-current">
<title>Getting the Currently Selected Rows</title>
<para>You can access the currently selected rows either by traversing
all selected rows using <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeSelection.html#gtk-tree-selection-selected-foreach">
<literal>gtk_tree_selection_selected_foreach</literal></ulink> or get
a <classname>GList</classname> of tree paths of the selected rows using
<ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeSelection.html#gtk-tree-selection-get-selected-rows">
<literal>gtk_tree_selection_get_selected_rows</literal></ulink>. Note
that this function is only available in Gtk+-2.2 and newer, which
means that you can't use it or need to reimplement it if you want your
application to work with older installations.</para>
<para>If the selection mode you are using is either
<literal>GTK_SELECTION_SINGLE</literal> or
<literal>GTK_SELECTION_BROWSE</literal>, the most convenient way to
get the selected row is the function
<literal>gtk_tree_selection_get_selected</literal>, which will return
<literal>TRUE</literal> and fill in the specified tree iter with the
selected row (if a row is selected), and return
<literal>FALSE</literal> otherwise. It is used like this:</para>
<programlisting role="C">
...
GtkTreeSelection *selection;
GtkTreeModel *model;
GtkTreeIter iter;
/* This will only work in single or browse selection mode! */
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
if (gtk_tree_selection_get_selected(selection, &amp;model, &amp;iter))
{
gchar *name;
gtk_tree_model_get (model, &amp;iter, COL_NAME, &amp;name, -1);
g_print ("selected row is: %s\n", name);
g_free(name);
}
else
{
g_print ("no row selected.\n");
}
...
</programlisting>
<para>One thing you need to be aware of is that you need to take care
when removing rows from the model in a
<literal>gtk_tree_selection_selected_foreach</literal> callback, or
when looping through the list that
<literal>gtk_tree_selection_get_selected_rows</literal> returns
(because it contains paths, and when you remove rows in the middle,
then the old paths will point to either a non-existing row, or to
another row than the one selected). You have two ways around this
problem: one way is to use the solution to removing multiple rows that
has been <link linkend="sec-treemodel-remove-row">described
above</link>, ie. to get tree row references for all selected rows and
then remove the rows one by one; the other solution is to sort the
list of selected tree paths so that the last rows come first in the
list, so that you remove rows from the end of the list or tree. You
cannot remove rows from within a foreach callback in any case, that is
simply not allowed.</para>
<para>Here is an example of how to use
<literal>gtk_tree_selection_selected_foreach</literal>:</para>
<programlisting role="C">
...
gboolean
view_selected_foreach_func (GtkTreeModel *model,
GtkTreePath *path,
GtkTreeIter *iter,
gpointer userdata)
{
gchar *name;
gtk_tree_model_get (model, iter, COL_NAME, &amp;name, -1);
g_print ("%s is selected\n", name);
}
void
do_something_with_all_selected_rows (GtkWidget *treeview)
{
GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
gtk_tree_selection_selected_foreach(selection, view_selected_foreach_func, NULL);
}
void
create_view (void)
{
GtkWidget *view;
GtkTreeSelection *selection;
...
view = gtk_tree_view_new();
...
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
...
}
...
</programlisting>
</sect2>
<sect2 id="sec-selections-function">
<title>Using Selection Functions</title>
<para>You can set up a custom selection function with <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeSelection.html#gtk-tree-selection-set-select-function">
<literal>gtk_tree_selection_set_select_function</literal></ulink>.
This function will then be called every time a row is going to be
selected or unselected (meaning: it will be called before the
selection status of that row is changed). Selection functions are
commonly used for the following things:</para>
<orderedlist>
<listitem>
<para>... to keep track of the currently selected items (then you
maintain a list of selected items yourself). In this case, note
again that your selection function is called
<emphasis>before</emphasis> the row's selection status is changed.
In other words: if the row is <emphasis>going to be</emphasis>
selected, then the boolean path_currently_selected variable that
is passed to the selection function is still
<literal>FALSE</literal>. Also note that the selection function
might not always be called when a row is removed, so you either
have to unselect a row before you remove it to make sure your
selection function is called and removes the row from your list,
or check the validity of a row when you process the selection list
you keep. You should not store tree paths in your self-maintained
list of of selected rows, because whenever rows are added or
removed or the model is resorted the paths might point to other
rows. Use tree row references or other unique means of identifying
a row instead.</para>
</listitem>
<listitem>
<para>... to tell Gtk+ whether it is allowed to select or unselect
that specific row (you should make sure though that it is
otherwise obvious to a user whether a row can be selected or not,
otherwise the user will be confused if she just cannot select or
unselect a row). This is done by returning TRUE or FALSE in the
selection function.</para>
</listitem>
<listitem>
<para>... to take additional action whenever a row is selected or
unselected.</para>
</listitem>
</orderedlist>
<para>Yet another simple example:</para>
<programlisting role="C">
...
gboolean
view_selection_func (GtkTreeSelection *selection,
GtkTreeModel *model,
GtkTreePath *path,
gboolean path_currently_selected,
gpointer userdata)
{
GtkTreeIter iter;
if (gtk_tree_model_get_iter(model, &amp;iter, path))
{
gchar *name;
gtk_tree_model_get(model, &amp;iter, COL_NAME, &amp;name, -1);
if (!path_currently_selected)
{
g_print ("%s is going to be selected.\n", name);
}
else
{
g_print ("%s is going to be unselected.\n", name);
}
g_free(name);
}
return TRUE; /* allow selection state to change */
}
void
create_view (void)
{
GtkWidget *view;
GtkTreeSelection *selection;
...
view = gtk_tree_view_new();
...
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
gtk_tree_selection_set_select_function(selection, view_selection_func, NULL, NULL);
...
}
...
</programlisting>
</sect2>
<sect2 id="sec-selections-check-row">
<title>Checking Whether a Row is Selected</title>
<para>You can check whether a given row is selected or not using the
functions <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeSelection.html#gtk-tree-selection-iter-is-selected">
<literal>gtk_tree_selection_iter_is_selected</literal></ulink>. or
<ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeSelection.html#gtk-tree-selection-path-is-selected">
<literal>gtk_tree_selection_path_is_selected</literal></ulink>. If you
want to know all rows that are <emphasis>not</emphasis> selected, for
example, you could just traverse the whole list or tree, and use the
above functions to check for each row whether it is selected or
not.</para>
</sect2>
<sect2 id="sec-selections-selecting-rows">
<title>Selecting and Unselecting Rows</title>
<para>You can select or unselect rows manually with <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeSelection.html#gtk-tree-selection-select-iter">
<literal>gtk_tree_selection_select_iter</literal></ulink>, <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeSelection.html#gtk-tree-selection-select-path">
<literal>gtk_tree_selection_select_path</literal></ulink>, <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeSelection.html#gtk-tree-selection-unselect-iter">
<literal>gtk_tree_selection_unselect_iter</literal></ulink>, <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeSelection.html#gtk-tree-selection-unselect-path">
<literal>gtk_tree_selection_unselect_path</literal></ulink>, <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeSelection.html#gtk-tree-selection-select-all">
<literal>gtk_tree_selection_select_all</literal></ulink>, and <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeSelection.html#gtk-tree-selection-unselect-all">
<literal>gtk_tree_selection_unselect_all</literal></ulink> should you
ever need to do that.</para>
</sect2>
<sect2 id="sec-selections-row-count">
<title>Getting the Number of Selected Rows</title>
<para>Sometimes you want to know the number of rows that are currently
selected (for example to set context menu entries active or inactive
before you pop up a context menu). If you are using selection mode
<literal>GTK_SELECTION_SINGLE</literal> or
<literal>GTK_SELECTION_BROWSE</literal>, this is trivial to check with
<literal>gtk_tree_selection_get_selected</literal>, which will return
either <literal>TRUE</literal> or <literal>FALSE</literal> (meaning
one selected row or no selected row).</para>
<para>If you are using <literal>GTK_SELECTION_MULTIPLE</literal> or
want a more general approach that works for all selection modes,
<literal>gtk_tree_selection_count_selected_rows</literal> will return
the information you are looking for. The only caveat with this
function is that it only exists in Gtk+-2.2 and newer, so you will
have to reimplement it if you want users with old installations that
still use Gtk+-2.0 to be able to use your program as well. Here is a
way to reimplement this function:</para>
<programlisting role="C">
static void
count_foreach_helper (GtkTreeModel *model,
GtkTreePath *path,
GtkTreeIter *iter,
gpointer userdata)
{
gint *p_count = (gint*) userdata;
g_assert (p_count != NULL);
*p_count = *p_count + 1;
}
gint
my_tree_selection_count_selected_rows (GtkTreeSelection *selection)
{
gint count = 0;
gtk_tree_selection_selected_foreach(selection, count_foreach_helper, &amp;count);
return count;
}
</programlisting>
</sect2>
</sect1>
<sect1 id="sec-selections-double-click">
<title>Double-Clicks on a Row</title>
<para>Catching double-clicks on a row is quite easy and is done by
connecting to a tree view's <literal>"row-activated"</literal> signal,
like this:</para>
<programlisting role="C">
void
view_onRowActivated (GtkTreeView *treeview,
GtkTreePath *path,
GtkTreeViewColumn *col,
gpointer userdata)
{
GtkTreeModel *model;
GtkTreeIter iter;
g_print ("A row has been double-clicked!\n");
model = gtk_tree_view_get_model(treeview);
if (gtk_tree_model_get_iter(model, &amp;iter, path))
{
gchar *name;
gtk_tree_model_get(model, &amp;iter, COLUMN_NAME, &amp;name, -1);
g_print ("Double-clicked row contains name %s\n", name);
g_free(name);
}
}
void
create_view (void)
{
GtkWidget *view;
view = gtk_tree_view_new();
...
g_signal_connect(view, "row-activated", (GCallback) view_onRowActivated, NULL);
...
}
</programlisting>
</sect1>
<sect1 id="sec-selections-context-menus">
<title>Context Menus on Right Click</title>
<para>Context menus are context-dependent menus that pop up when a user
right-clicks on a list or tree and usually let the user do something
with the selected items or manipulate the list or tree in other
ways.</para>
<para>Right-clicks on a tree view are caught just like mouse button
clicks are caught with any other widgets, namely by connecting to the
tree view's "button_press_event" signal handler (which is a GtkWidget
signal, and as GtkTreeView is derived from GtkWidget it has this signal
as well). Additionally, you should also connect to the "popup-menu"
signal, so users can access your context menu without a mouse. The
"popup-menu" signal is emitted when the user presses Shift-F10. Also,
you should make sure that all functions provided in your context menu
can also be accessed by other means such as the application's main menu.
See the <ulink url="http://developer.gnome.org/projects/gup/hig/">GNOME
Human Interface Guidelines (HIG)</ulink> for more details. Straight from
the a-snippet-of-code-says-more-than-a-thousand-words-department, some
code to look at:</para>
<programlisting role="C">
void
view_popup_menu_onDoSomething (GtkWidget *menuitem, gpointer userdata)
{
/* we passed the view as userdata when we connected the signal */
GtkTreeView *treeview = GTK_TREE_VIEW(userdata);
g_print ("Do something!\n");
}
void
view_popup_menu (GtkWidget *treeview, GdkEventButton *event, gpointer userdata)
{
GtkWidget *menu, *menuitem;
menu = gtk_menu_new();
menuitem = gtk_menu_item_new_with_label("Do something");
g_signal_connect(menuitem, "activate",
(GCallback) view_popup_menu_onDoSomething, treeview);
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
gtk_widget_show_all(menu);
/* Note: event can be NULL here when called from view_onPopupMenu;
* gdk_event_get_time() accepts a NULL argument */
gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
(event != NULL) ? event-&gt;button : 0,
gdk_event_get_time((GdkEvent*)event));
}
gboolean
view_onButtonPressed (GtkWidget *treeview, GdkEventButton *event, gpointer userdata)
{
/* single click with the right mouse button? */
if (event-&gt;type == GDK_BUTTON_PRESS &amp;&amp; event-&gt;button == 3)
{
g_print ("Single right click on the tree view.\n");
/* optional: select row if no row is selected or only
* one other row is selected (will only do something
* if you set a tree selection mode as described later
* in the tutorial) */
if (1)
{
GtkTreeSelection *selection;
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
/* Note: gtk_tree_selection_count_selected_rows() does not
* exist in gtk+-2.0, only in gtk+ &gt;= v2.2 ! */
if (gtk_tree_selection_count_selected_rows(selection) &lt;= 1)
{
GtkTreePath *path;
/* Get tree path for row that was clicked */
if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(treeview),
(gint) event-&gt;x,
(gint) event-&gt;y,
&amp;path, NULL, NULL, NULL))
{
gtk_tree_selection_unselect_all(selection);
gtk_tree_selection_select_path(selection, path);
gtk_tree_path_free(path);
}
}
} /* end of optional bit */
view_popup_menu(treeview, event, userdata);
return TRUE; /* we handled this */
}
return FALSE; /* we did not handle this */
}
gboolean
view_onPopupMenu (GtkWidget *treeview, gpointer userdata)
{
view_popup_menu(treeview, NULL, userdata);
return TRUE; /* we handled this */
}
void
create_view (void)
{
GtkWidget *view;
view = gtk_tree_view_new();
...
g_signal_connect(view, "button-press-event", (GCallback) view_onButtonPressed, NULL);
g_signal_connect(view, "popup-menu", (GCallback) view_onPopupMenu, NULL);
...
}
</programlisting>
</sect1>
</chapter>
<chapter id="sec-sorting">
<title>Sorting</title>
<para>Lists and trees are meant to be sorted. This is done using the
<ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeSortable.html">
<classname>GtkTreeSortable</classname></ulink> interface that can be
implemented by tree models. 'Interface' means that you can just cast a
<classname>GtkTreeModel</classname> into a <classname>GtkTreeSortable</classname>
with <literal>GTK_TREE_SORTABLE(model)</literal> and use the documented
tree sortable functions on it, just like we did before when we cast a list
store to a tree model and used the <literal>gtk_tree_model_foo</literal>
family of functions. Both <classname>GtkListStore</classname> and
<classname>GtkTreeStore</classname> implement the tree sortable
interface.</para>
<para>The most straight forward way to sort a list store or tree store is
to directly use the tree sortable interface on them. This will sort the
store in place, meaning that rows will actually be reordered in the store
if required. This has the advantage that the position of a row in the tree
view will always be the same as the position of a row in the model, in
other words: a tree path refering to a row in the view will always refer
to the same row in the model, so you can get a row's iter easily with
<literal>gtk_tree_model_get_iter</literal> using a tree path supplied by
the tree view. This is not only convenient, but also sufficient for most
scenarios.</para>
<para>However, there are cases when sorting a model in place is not
desirable, for example when several tree views display the same model with
different sortings, or when the unsorted state of the model has some
special meaning and needs to be restored at some point. This is where
<ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModelSort.html">
<classname>GtkTreeModelSort</classname></ulink> comes in, which is a special
model that maps the unsorted rows of a child model (e.g. a list store or
tree store) into a sorted state without changing the child model.</para>
<sect1 id="sec-sorting-tree-sortable">
<title>GtkTreeSortable</title>
<para>The tree sortable interface is fairly simple and should be easy to
use. Basically you define a 'sort column ID' integer for every criterion
you might want to sort by and tell the tree sortable which function
should be called to compare two rows (represented by two tree iters) for
every sort ID with <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeSortable.html#gtk-tree-sortable-set-sort-func">
<literal>gtk_tree_sortable_set_sort_func</literal></ulink>. Then you
sort the model by setting the sort column ID and sort order with <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeSortable.html#gtk-tree-sortable-set-column-id">
<literal>gtk_tree_sortable_set_sort_column_id</literal></ulink>, and the
model will be re-sorted using the compare function you have set up. Your
sort column IDs can correspond to your model columns, but they do not
have to (you might want to sort according to a criterion that is not
directly represented by the data in one single model column, for
example). Some code to illustrate this:</para>
<programlisting role="C">
enum
{
COL_NAME = 0,
COL_YEAR_BORN
};
enum
{
SORTID_NAME = 0,
SORTID_YEAR
};
GtkTreeModel *liststore = NULL;
void
toolbar_onSortByYear (void)
{
GtkTreeSortable *sortable;
GtkSortType order;
gint sortid;
sortable = GTK_TREE_SORTABLE(liststore);
/* If we are already sorting by year, reverse sort order,
* otherwise set it to year in ascending order */
if (gtk_tree_sortable_get_sort_column_id(sortable, &amp;sortid, &amp;order) == TRUE
&amp;&amp; sortid == SORTID_YEAR)
{
GtkSortType neworder;
neworder = (order == GTK_SORT_ASCENDING) ? GTK_SORT_DESCENDING : GTK_SORT_ASCENDING;
gtk_tree_sortable_set_sort_column_id(sortable, SORTID_YEAR, neworder);
}
else
{
gtk_tree_sortable_set_sort_column_id(sortable, SORTID_YEAR, GTK_SORT_ASCENDING);
}
}
/* This is not pretty. Of course you can also use a
* separate compare function for each sort ID value */
gint
sort_iter_compare_func (GtkTreeModel *model,
GtkTreeIter *a,
GtkTreeIter *b,
gpointer userdata)
{
gint sortcol = GPOINTER_TO_INT(userdata);
gint ret = 0;
switch (sortcol)
{
case SORTID_NAME:
{
gchar *name1, *name2;
gtk_tree_model_get(model, a, COL_NAME, &amp;name1, -1);
gtk_tree_model_get(model, b, COL_NAME, &amp;name2, -1);
if (name1 == NULL || name2 == NULL)
{
if (name1 == NULL &amp;&amp; name2 == NULL)
break; /* both equal =&gt; ret = 0 */
ret = (name1 == NULL) ? -1 : 1;
}
else
{
ret = g_utf8_collate(name1,name2);
}
g_free(name1);
g_free(name2);
}
break;
case SORTID_YEAR:
{
guint year1, year2;
gtk_tree_model_get(model, a, COL_YEAR_BORN, &amp;year1, -1);
gtk_tree_model_get(model, b, COL_YEAR_BORN, &amp;year2, -1);
if (year1 != year2)
{
ret = (year1 &gt; year2) ? 1 : -1;
}
/* else both equal =&gt; ret = 0 */
}
break;
default:
g_return_val_if_reached(0);
}
return ret;
}
void
create_list_and_view (void)
{
GtkTreeSortable *sortable;
...
liststore = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_UINT);
sortable = GTK_TREE_SORTABLE(liststore);
gtk_tree_sortable_set_sort_func(sortable, SORTID_NAME, sort_iter_compare_func,
GINT_TO_POINTER(SORTID_NAME), NULL);
gtk_tree_sortable_set_sort_func(sortable, SORTID_YEAR, sort_iter_compare_func,
GINT_TO_POINTER(SORTID_YEAR), NULL);
/* set initial sort order */
gtk_tree_sortable_set_sort_column_id(sortable, SORTID_NAME, GTK_SORT_ASCENDING);
...
view = gtk_tree_view_new_with_model(liststore);
...
}
</programlisting>
<para>Usually things are a bit easier if you make use of the tree view
column headers for sorting, in which case you only need to assign sort
column IDs and your compare functions, but do not need to set the
current sort column ID or order yourself (see <link
linkend="sec-sorting-view-cols">below</link>).</para>
<para>Your tree iter compare function should return a negative value if
the row specified by iter a comes before the row specified by iter b,
and a positive value if row b comes before row a. It should return 0 if
both rows are equal according to your sorting criterion (you might want
to use a second sort criterion though to avoid 'jumping' of equal rows
when the store gets resorted). Your tree iter compare function should
not take the sort order into account, but assume an ascending sort order
(otherwise bad things will happen).</para>
</sect1>
<sect1 id="sec-sorting-model-sort">
<title>GtkTreeModelSort</title>
<para><ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModelSort.html">
<classname>GtkTreeModelSort</classname></ulink> is a wrapper tree model. It
takes another tree model such as a list store or a tree store as child
model, and presents the child model to the 'outside' (ie. a tree view or
whoever else is accessing it via the tree model interface) in a sorted
state. It does that without changing the order of the rows in the child
model. This is useful if you want to display the same model in different
tree views with different sorting criteria for each tree view, for
example, or if you need to restore the original unsorted state of your
store again at some point.</para>
<para><classname>GtkTreeModelSort</classname> implements the
<classname>GtkTreeSortable</classname> interface, so you can treat it just
as if it was your data store for sorting purposes. Here is the basic
setup with a tree view:</para>
<programlisting role="C">
...
void
create_list_and_view (void)
{
...
liststore = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_UINT);
sortmodel = gtk_tree_model_sort_new_with_model(liststore);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(sortmodel), SORTID_NAME,
sort_func, GINT_TO_POINTER(SORTID_NAME), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(sortmodel), SORTID_YEAR,
sort_func, GINT_TO_POINTER(SORTID_YEAR), NULL);
/* set initial sort order */
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(sortmodel),
SORTID_NAME, GTK_SORT_ASCENDING);
...
view = gtk_tree_view_new_with_model(sortmodel);
...
}
...
</programlisting>
<para>However, when using the sort tree model, you need to be careful
when you use iters and paths with the model. This is because a path
pointing to a row in the view (and the sort tree model here) does
probably not point to the same row in the child model which is your
original list store or tree store, because the row order in the child
model is probably different from the sorted order. Similarly, an iter
that is valid for the sort tree model is not valid for the child model,
and vice versa. You can convert paths and iters from and to the child
model using
<literal>gtk_tree_model_sort_convert_child_path_to_path</literal>,
<literal>gtk_tree_model_sort_convert_child_iter_to_iter</literal>,
<literal>gtk_tree_model_sort_convert_path_to_child_path</literal>, and
<literal>gtk_tree_model_sort_convert_iter_to_child_iter</literal>. You
are unlikely to need these functions frequently though, as you can still
directly use <literal>gtk_tree_model_get</literal> on the sort tree
model with a path supplied by the tree view.</para>
<para>For the tree view, the sort tree model is the 'real' model - it
knows nothing about the sort tree model's child model at all, which
means that any path or iter that you get passed from the tree view in a
callback or otherwise will refer to the sort tree model, and that you
need to pass a path or iter refering to the sort tree model as well if
you call tree view functions.</para>
</sect1>
<sect1 id="sec-sorting-view-cols">
<title>Sorting and Tree View Column Headers</title>
<para>Unless you have hidden your tree view column headers or use custom
tree view column header widgets, each tree view column's header can be
made clickable. Clicking on a tree view column's header will then sort
the list according to the data in that column. You need to do two things
to make this happen: firstly, you need to tell your model which sort
function to use for which sort column ID with <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeSortable.html#gtk-tree-sortable-set-sort-func">
<literal>gtk_tree_sortable_set_sort_func</literal></ulink>. Once you
have done this, you tell each tree view column which sort column ID
should be active if this column's header is clicked. This is done with
<ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeViewColumn.html#gtk-tree-view-column-set-sort-column-id">
<literal>gtk_tree_view_column_set_sort_column_id</literal></ulink>.</para>
<para>And that is really all you need to do to get your list or tree
sorted. The tree view columns will automatically set the active sort
column ID and sort order for you if you click on a column header.</para>
</sect1>
<sect1 id="sec-sorting-string-nocase">
<title>Case-insensitive String Comparing</title>
<para>As described above in the <link
linkend="sec-treeview-col-utf8-pango">"GtkCellRendererText, UTF8, and
pango markup" section</link>, all strings that are to be displayed in
the tree view need to be encoded in UTF8 encoding. All ASCII strings are
valid UTF8, but as soon as non-ASCII characters are used, things get a
bit tricky and the character encoding matters.</para>
<para>Comparing two ASCII strings ignoring the case is trivial and can
be done using <literal>g_ascii_strcasecmp</literal>, for example.
<literal>strcasecmp</literal> will usually do the same, only that it is
also locale-aware to some extent. The only problem is that a lot of
users use locale character encodings that are not UTF8, so
<literal>strcasecmp</literal> does not take us very far.</para>
<para><literal>g_utf8_collate</literal> will compare two strings in UTF8
encoding, but it does not ignore the case. In order to achieve at least
half-way correct linguistic case-insensitive sorting, we need to take a
two-step approach. For example, we could use
<literal>g_utf8_casefold</literal> to convert the strings to compare
into a form that is independent of case, and then use
<literal>g_utf8_collate</literal> to compare those two strings (note
that the strings returned by <literal>g_utf8_casefold</literal> will not
resemble the original string in any recognisable way; they will work
fine for comparisons though). Alternatively, one could use
<literal>g_utf8_strdown</literal> on both strings and then compare the
results again with <literal>g_utf8_collate</literal>.</para>
<para>Obviously, all this is not going to be very fast, and adds up if
you have a lot of rows. To speed things up, you can create a 'collation
key' with <literal>g_utf8_collate_key</literal> and store that in your
model as well. A collation key is just a string that does not mean
anything to us, but can be used with <literal>strcmp</literal> for
string comparison purposes (which is a lot faster than
<literal>g_utf8_collate</literal>).</para>
<para>It should be noted that the way <literal>g_utf8_collate</literal>
sorts is dependent on the current locale. Make sure you are not working
in the 'C' locale (=default, none specified) before you are wondering
about weird sorting orders. Check with 'echo $LANG' on a command line
what you current locale is set to.</para>
<para>Check out the <ulink
url="http://developer.gnome.org/doc/API/2.0/glib/glib-Unicode-Manipulation.html#g-utf8-casefold">
"Unicode Manipulation" section</ulink> in the GLib API Reference for
more details.</para>
</sect1>
</chapter>
<chapter id="sec-editable-cells">
<title>Editable Cells</title>
<sect1 id="sec-editable-cells-text">
<title>Editable Text Cells</title>
<para>With <classname>GtkCellRendererText</classname> you can not only
display text, but you can also allow the user to edit a single cell's
text right in the tree view by double-clicking on a cell.</para>
<para>To make this work you need to tell the cell renderer that a cell
is editable, which you can do by setting the
<literal>"editable"</literal> property of the text cell renderer in
question to <literal>TRUE</literal>. You can either do this on a per-row
basis (which allows you to set each single cell either editable or not)
by connecting the <literal>"editable"</literal> property to a boolean
type column in your tree model using attributes; or you can just do a
...</para>
<programlisting role="C">
g_object_set(renderer, "editable", TRUE, NULL);
</programlisting>
<para>... when you create the renderer, which sets all rows in that
particular renderer column to be editable.</para>
<para>Now that our cells are editable, we also want to be notified when
a cell has been edited. This can be achieved by connecting to the cell
renderer's <literal>"edited"</literal> signal:</para>
<programlisting role="C">
g_signal_connect(renderer, "edited", (GCallback) cell_edited_callback, NULL);
</programlisting>
<para>This callback is then called whenever a cell has been edited.
Instead of <literal>NULL</literal> we could have passed a pointer to the
model as user data for convenience, as we probably want to store the new
value in the model.</para>
<para>The callback for the <literal>"edited"</literal> signal looks like
this (the API reference is a bit lacking in this particular
case):</para>
<programlisting role="C">
void cell_edited_callback (GtkCellRendererText *cell,
gchar *path_string,
gchar *new_text,
gpointer user_data);
</programlisting>
<para>The tree path is passed to the <literal>"edited"</literal> signal
callback in string form. You can convert this into a
<literal>GtkTreePath</literal> with <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModel.html#gtk-tree-path-new-from-string">
<literal>gtk_tree_path_new_from_string</literal></ulink>, or convert it
into an iter with <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModel.html#gtk-tree-model-get-iter-from-string">
<literal>gtk_tree_model_get_iter_from_string</literal></ulink>.</para>
<para>Note that the cell renderer will not change the data for you in
the store. After a cell has been edited, you will only receive an
<literal>"edited"</literal> signal. If you do not change the data in the
store, the old text will be rendered again as if nothing had
happened.</para>
<para>If you have multiple (renderer) columns with editable cells, it is
not necessary to have a different callback for each renderer, you can
use the same callback for all renderers, and attach some data to each
renderer, which you can later retrieve again in the callback to know
which renderer/column has been edited. This is done like this, for
example:</para>
<programlisting role="C">
renderer = gtk_cell_renderer_text_new();
...
g_object_set_data(G_OBJECT(renderer), "my_column_num", GUINT_TO_POINTER(COLUMN_NAME));
...
renderer = gtk_cell_renderer_text_new();
...
g_object_set_data(G_OBJECT(renderer), "my_column_num", GUINT_TO_POINTER(COLUMN_YEAR_OF_BIRTH));
...
</programlisting>
<para>where COLUMN_NAME and COLUMN_YEAR_OF_BIRTH are enum values. In
your callback you can then get the column number with</para>
<programlisting role="C">
guint column_number = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(renderer), "my_column_num"));
</programlisting>
<para>You can use this mechanism to attach all kinds of custom data to
any object or widget, with a string identifier to your liking.</para>
<para>A good example for editable cells is in gtk-demo, which is part of
the Gtk+ source code tree (in gtk+-2.x.y/demos/gtk-demo).</para>
<sect2 id="sec-editable-cells-text-set">
<title>Setting the cursor to a specific cell</title>
<para>You can move the cursor to a specific cell in a tree view with
<ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeView.html#gtk-tree-view-set-cursor">
<literal>gtk_tree_view_set_cursor</literal></ulink> (or <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeView.html#gtk-tree-view-set-cursor-on-cell">
<literal>gtk_tree_view_set_cursor_on_cell</literal></ulink> if you
have multiple editable cell renderers packed into one tree view
column), and start editing the cell if you want to. Similarly, you can
get the current row and focus column with <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeView.html#gtk-tree-view-get-cursor">
<literal>gtk_tree_view_get_cursor</literal></ulink>. Use <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkWidget.html#gtk-widget-grab-focus">
<literal>gtk_widget_grab_focus(treeview)</literal></ulink> will make
sure that the tree view has the keyboard focus.</para>
<para>As the API reference points out, the tree view needs to be
realised for cell editing to happen. In other words: If you want to
start editing a specific cell right at program startup, you need to
set up an idle timeout with <ulink
url="http://developer.gnome.org/doc/API/2.0/glib/glib-The-Main-Event-Loop.html#g-idle-add">
<literal>g_idle_add</literal></ulink> that does this for you as soon
as the window and everything else has been realised (return
<literal>FALSE</literal> in the timeout to make it run only once).
Alternatively you could connect to the <literal>"realize"</literal>
signal of the treeview with <literal>g_signal_connect_after</literal>
to achieve the same thing.</para>
<para>Connect to the tree view's <literal>"cursor-changed"</literal>
and/or <literal>"move-cursor"</literal> signals to keep track of the
current position of the cursor.</para>
</sect2>
</sect1>
<sect1 id="sec-editable-cells-toggle">
<title>Editable Toggle and Radio Button Cells</title>
<para>Just like you can set a <classname>GtkCellRendererText</classname>
editable, you can specify whether a
<classname>GtkCellRendererToggle</classname> should change its state when
clicked by setting the <literal>"activatable"</literal> property -
either when you create the renderer (in which case all cells in that
column will be clickable) or by connecting the renderer property to a
model column of boolean type via attributes.</para>
<para>Connect to the <literal>"toggled"</literal> signal of the toggle
cell renderer to be notified when the user clicks on a toggle button (or
radio button). The user click will not change the value in the store, or
the appearance of the value rendered. The toggle button will only change
state when you update the value in the store. Until then it will be in
an "inconsistent" state, which is also why you should read the current
value of that cell from the model, and not from the cell
renderer.</para>
<para>The callback for the <literal>"toggled"</literal> signal looks
like this (the API reference is a bit lacking in this particular
case):</para>
<programlisting role="C">
void cell_toggled_callback (GtkCellRendererToggle *cell,
gchar *path_string,
gpointer user_data);
</programlisting>
<para>Just like with the <literal>"edited"</literal> signal of the text
cell renderer, the tree path is passed to the
<literal>"toggled"</literal> signal callback in string form. You can
convert this into a <classname>GtkTreePath</classname> with <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModel.html#gtk-tree-path-new-from-string">
<literal>gtk_tree_path_new_from_string</literal></ulink>, or convert it
into an iter with <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModel.html#gtk-tree-model-get-iter-from-string">
<literal>gtk_tree_model_get_iter_from_string</literal></ulink>.</para>
</sect1>
<sect1 id="sec-editable-cells-spin">
<title>Editable Spin Button Cells</title>
<para>Even though <classname>GtkSpinButton</classname> implements the
<classname>GtkCellEditable</classname> interface (as does
<classname>GtkEntry</classname>), there is no easy way to get a cell
renderer that uses a spin button instead of a normal entry when in
editing mode.</para>
<para>To get this functionality, you need to either write a new cell
renderer that works very similar to
<classname>GtkCellRendererText</classname>, or you need to write a new cell
renderer class that derives from the text cell renderer and changes the
behaviour in editing mode.</para>
<para>The cleanest solution would probably be to write a
'CellRendererNumeric' that does everything that the text cell renderer
does, only that it has a float type property instead of the
<literal>"text"</literal> property, and an additional digits property.
However, no one seems to have done this yet, so you need to either write
one, or find another solution to get spin buttons in editing
mode.</para>
<para>Among this tutorial's code examples there is a hackish
CellRendererSpin implementation which is based on
<classname>GtkCellRendererText</classname> and shows spin buttons in editing
mode. The implementation is not very refined though, so you need to make
sure it works in your particular context, and modify it as
needed.</para>
</sect1>
</chapter>
<chapter id="sec-misc">
<title>Miscellaneous</title>
<para>This section deals with issues and questions that did not seem to
fit in anywhere else. If you can think of something else that should be
dealt with here, do not hesitate to send a mail to <email>tim at
centricular dot net</email>.</para>
<sect1 id="sec-misc-column-index">
<title>Getting the Column Number from a Tree View Column Widget</title>
<para>Signal callbacks often only get passed a pointer to a
<classname>GtkTreeViewColumn</classname> when the application programmer
really just wants to know which column <emphasis>number</emphasis> was
affected. There are two ways to find out the position of a column within
the tree view. One way is to write a small helper function that looks up
the column number from a given tree view column object, like this for
example: <footnote>
<para>This function has been inspired by <ulink
url="http://mail.gnome.org/archives/gtk-list/2003-July/msg00060.html">
this</ulink> mailing list message (thanks to Ken Rastatter for the
link and the topic suggestion).</para>
</footnote>.</para>
<programlisting role="C">
/* Returns column number or -1 if not found or on error */
gint
get_col_number_from_tree_view_column (GtkTreeViewColumn *col)
{
GList *cols;
gint num;
g_return_val_if_fail ( col != NULL, -1 );
g_return_val_if_fail ( col-&gt;tree_view != NULL, -1 );
cols = gtk_tree_view_get_columns(GTK_TREE_VIEW(col-&gt;tree_view));
num = g_list_index(cols, (gpointer) col);
g_list_free(cols);
return num;
}
</programlisting>
<para>Alternatively, it is possible to use
<literal>g_object_set_data</literal> and
<literal>g_object_get_data</literal> on the tree view column in order to
identify which column it is. This also has the advantage that you can
still keep track of your columns even if the columns get re-ordered
within the tree view (a feature which is usually disabled though). Use
like this:</para>
<programlisting role="C">
...
enum
{
COL_FIRSTNAME,
COL_SURNAME,
};
...
void
some_callback (GtkWidget *treeview, ..., GtkTreeViewColumn *col, ...)
{
guint colnum = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(col), "columnnum"));
...
}
void
create_view(void)
{
...
col = gtk_tree_view_column_new();
g_object_set_data(G_OBJECT(col), "columnnum", GUINT_TO_POINTER(COL_FIRSTNAME));
...
col = gtk_tree_view_column_new();
g_object_set_data(G_OBJECT(col), "columnnum", GUINT_TO_POINTER(COL_SURNAME));
...
}
</programlisting>
<para><literal>"columnnum"</literal> is a random string in the above
example - you can use whatever string you want instead, or store
multiple bits of data (with different string identifiers of course). Of
course you can also combine both approaches, as they do slightly
different things (the first tracks the 'physical' position of a column
within the tree view, the second tracks the 'meaning' of a column to
you, independent of its position within the view).</para>
</sect1>
<sect1 id="sec-misc-expander-visibility">
<title>Column Expander Visibility</title>
<sect2 id="sec-misc-expander-visibility-hide">
<title>Hiding the Column Expander</title>
<para>Is it possible to hide the column expander completely? Yes and
no. What follows, is probably a dirty hack at best and there is no
guarantee that it will work with upcoming Gtk+ versions or even with
all past versions (although the latter is easy enough to test of
course).</para>
<para>What you can do to hide the column expander is to create an
empty tree view column (containing empty strings, for example) and
make this the first column in the tree view. Then you can hide that
column with <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeViewColumn.html#gtk-tree-view-column-set-visible">
<literal>gtk_tree_view_column_set_visible</literal></ulink>. You will
notice that the expander column will now automatically move to the
formerly second, now first, visible column in the tree view. However,
if you call <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> right
after the call to <literal>_set_visible</literal>, then the expander
will move back to the hidden column, and no expander is visible any
longer.</para>
<para>This means of course that you will have to take care of
expanding and collapsing rows yourself and use the appropriate tree
view functions. While it is at last thinkable that one could implement
custom expanders using custom cell rendereres or pixbuf cell
renderers, this is probably a task that will keep you busy for more
than five minutes. Keep those head ache tablets nearby if you attempt
it anyway...</para>
</sect2>
<sect2 id="sec-misc-expander-visibility-force">
<title>Forcing Column Expander Visibility</title>
<para>There are situations where an expander should be visible even if
the row in question does not have any children yet, for instance when
part of a model should only be loaded on request when a node gets
expanded (e.g. to show the contents of a directory). This is not
possible. An expander is only shown if a node has children.</para>
<para>A work-around for this problem exists however: simply attach an
empty child row and set the node to collapsed state. Then listen for
the tree view's <literal>"row-expanded"</literal> signal, and fill the
contents of the already existing row with the first new row, then
append new child rows. See <ulink
url="http://mail.gnome.org/archives/gtk-app-devel-list/2003-May/msg00241.html">
this mailing list thread</ulink> for more details.</para>
</sect2>
</sect1>
<sect1 id="sec-misc-get-renderer-from-click">
<title>Getting the Cell Renderer a Click Event Happened On</title>
<para>It seems that in many cases when people want to know the cell
renderer a click event happened on, they do not really need to know the
cell renderer, but rather want to modify an individual cell in a
particular column. For this you do not need to know the cell renderer.
Use <literal> <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeView.html#gtk-tree-view-get-path-at-pos">
gtk_tree_view_get_path_at_pos</ulink></literal> to get a tree path from
the x and y coordinates of the button event that is passed to you in a
<literal>"button-press-event"</literal> signal callback (if you use the
<literal>"row-activated"</literal> signal to catch double-clicks you get
the tree path passed directly into the callback function). Then convert
that tree path into an iter using <literal><ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModel.html#gtk-tree-model-get-iter">
gtk_tree_model_get_iter</ulink></literal> and modify the data in the
cell you want to modify with <literal>gtk_list_store_set</literal> or
<literal>gtk_tree_store_set</literal>.</para>
<para>If you really do need to know the cell renderer where a button
press event happened, that is a bit more tricky. Here is a suggestion on
how to approach this issue (the function has not been well-tested and
might not work correctly if the content rendered by one renderer in
different columns varies in width; please send suggestions on how to fix
or improve this function to the author):</para>
<programlisting role="C">
static gboolean
tree_view_get_cell_from_pos(GtkTreeView *view, guint x, guint y, GtkCellRenderer **cell)
{
GtkTreeViewColumn *col = NULL;
GList *node, *columns, *cells;
guint colx = 0;
g_return_val_if_fail ( view != NULL, FALSE );
g_return_val_if_fail ( cell != NULL, FALSE );
/* (1) find column and column x relative to tree view coordinates */
columns = gtk_tree_view_get_columns(view);
for (node = columns; node != NULL &amp;&amp; col == NULL; node = node-&gt;next)
{
GtkTreeViewColumn *checkcol = (GtkTreeViewColumn*) node-&gt;data;
if (x &gt;= colx &amp;&amp; x &lt; (colx + checkcol-&gt;width))
col = checkcol;
else
colx += checkcol-&gt;width;
}
g_list_free(columns);
if (col == NULL)
return FALSE; /* not found */
/* (2) find the cell renderer within the column */
cells = gtk_tree_view_column_get_cell_renderers(col);
for (node = cells; node != NULL; node = node-&gt;next)
{
GtkCellRenderer *checkcell = (GtkCellRenderer*) node-&gt;data;
guint width = 0, height = 0;
/* Will this work for all packing modes? doesn't that
* return a random width depending on the last content
* rendered? */
gtk_cell_renderer_get_size(checkcell, GTK_WIDGET(view), NULL, NULL, NULL, &amp;width, NULL);
if (x &gt;= colx &amp;&amp; x &lt; (colx + width))
{
*cell = checkcell;
g_list_free(cells);
return TRUE;
}
colx += width;
}
g_list_free(cells);
return FALSE; /* not found */
}
static gboolean
onButtonPress (GtkWidget *view, GdkEventButton *bevent, gpointer data)
{
GtkCellRenderer *renderer = NULL;
if (tree_view_get_cell_from_pos(GTK_TREE_VIEW(view), bevent-&gt;x, bevent-&gt;y, &amp;renderer))
g_print ("Renderer found\n");
else
g_print ("Renderer not found!\n");
}
</programlisting>
</sect1>
<sect1 id="sec-misc-glade">
<title>Glade and Tree Views</title>
<para>A frequently asked question is how you can add columns to a
<classname>GtkTreeView</classname> in <ulink
url="http://glade.gnome.org">Glade</ulink>. <footnote>
<para>Do <emphasis>not</emphasis> use Glade to generate code for
you. Use Glade to create the interface. It will save the interface
into a .glade file in XML format. You can then use libglade2 to
construct your interface (windows etc.) from that .glade file. See
<ulink
url="http://lists.ximian.com/archives/public/glade-devel/2003-February/000015.html">
this mailing list message for a short discussion about why you
should avoid Glade code generation</ulink>.</para>
</footnote> The answer is basically that you don't, and that you
can't. The only thing glade/libglade can do for you is to create the
<classname>GtkTreeView</classname> for you with nothing in it. You will need
to look up the tree view widget at the start of your application (after
the interface has been created of course), and connect your list store
or tree store to it. Then you will need to add
<classname>GtkTreeViewColumn</classname>s and cell renderers to display the
information from the model as you want it to be displayed. You will need
to do all that from within your application.</para>
<para>An alternative approach is to derive your own special widget from
<classname>GtkTreeView</classname> that sets up everything as you want it
to, and then use the 'custom widget' function in glade. Of course this
still means that you have to write all the code to fill in the columns
and cell renderers and to create the model yourself.</para>
</sect1>
</chapter>
<chapter id="sec-dnd">
<title>Drag'n'Drop (DnD) **** needs revision ***</title>
<para>****** NEEDS REVISION</para>
<para>This section needs revision more than any other section. If you know
anything about tree view drag'n'drop, you probably know more than the
author of this text. Please give some feedback in that case.</para>
<para>If you want to dive into treeview drag'n'drop, you might want to
check out <ulink
url="http://mail.gnome.org/archives/gtk-devel-list/2001-November/msg00018.html">
Owen Taylor's mail on that topic</ulink>. It might not be completely
identical to what has actually been implemented, but it gives a great
overview, and provides more information than the docs do.</para>
<para>In addition to the standard Gtk+ Drag and Drop mechanisms that work
with any widget, there are special Drag and Drop mechanisms just for the
tree view widget. You usually want to use the tree-view specific
Drag-and-Drop framework.</para>
<sect1 id="sec-dnd-selected-item-info">
<title>Drag'n'Dropping Row-Unrelated Data to and from a Tree View from
other Windows or Widgets</title>
<para>Drag'n'Dropping general information from or to a tree view widget
works just like it works with any other widget and involves the standard
<ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/gtk-Drag-and-Drop.html">
Gtk+ Drag and Drop</ulink> mechanisms. If you use this, you can receive
drops to or initiate drags from anywhere in your tree view (including
empty sections). This is not row- or column-specific and <emphasis>is
most likely not want you want</emphasis>. Nevertheless, here is a small
example of a tree view in which you can drag'n'drop URIs from other
applications (browsers, for example), with the dropped URIs just being
appended to the list (note that usually you would probably rather want
to set up your whole window as a target then and not just the tree view
widget):</para>
<programlisting role="C">
#include &lt;gtk/gtk.h&gt;
enum
{
COL_URI = 0,
NUM_COLS
} ;
void
view_onDragDataReceived(GtkWidget *wgt, GdkDragContext *context, int x, int y,
GtkSelectionData *seldata, guint info, guint time,
gpointer userdata)
{
GtkTreeModel *model;
GtkTreeIter iter;
model = GTK_TREE_MODEL(userdata);
gtk_list_store_append(GTK_LIST_STORE(model), &amp;iter);
gtk_list_store_set(GTK_LIST_STORE(model), &amp;iter, COL_URI, (gchar*)seldata-&gt;data, -1);
}
static GtkWidget *
create_view_and_model (void)
{
GtkTreeViewColumn *col;
GtkCellRenderer *renderer;
GtkListStore *liststore;
GtkWidget *view;
liststore = gtk_list_store_new(NUM_COLS, G_TYPE_STRING);
view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(liststore));
g_object_unref(liststore); /* destroy model with view */
col = gtk_tree_view_column_new();
renderer = gtk_cell_renderer_text_new();
gtk_tree_view_column_set_title(col, "URI");
gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
gtk_tree_view_column_pack_start(col, renderer, TRUE);
gtk_tree_view_column_add_attribute(col, renderer, "text", COL_URI);
gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(view)),
GTK_SELECTION_SINGLE);
/* Make tree view a destination for Drag'n'Drop */
if (1)
{
enum
{
TARGET_STRING,
TARGET_URL
};
static GtkTargetEntry targetentries[] =
{
{ "STRING", 0, TARGET_STRING },
{ "text/plain", 0, TARGET_STRING },
{ "text/uri-list", 0, TARGET_URL },
};
gtk_drag_dest_set(view, GTK_DEST_DEFAULT_ALL, targetentries, 3,
GDK_ACTION_COPY|GDK_ACTION_MOVE|GDK_ACTION_LINK);
g_signal_connect(view, "drag_data_received",
G_CALLBACK(view_onDragDataReceived), liststore);
}
return view;
}
int
main (int argc, char **argv)
{
GtkWidget *window, *vbox, *view, *label;
gtk_init(&amp;argc, &amp;argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
g_signal_connect(window, "delete_event", gtk_main_quit, NULL); /* dirty */
gtk_window_set_default_size(GTK_WINDOW(window), 400, 200);
vbox = gtk_vbox_new(FALSE, 0);
gtk_container_add(GTK_CONTAINER(window), vbox);
label = gtk_label_new("\nDrag and drop links from your browser into the tree view.\n");
gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
view = create_view_and_model();
gtk_box_pack_start(GTK_BOX(vbox), view, TRUE, TRUE, 0);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
</programlisting>
<para>If you are receiving drops into a tree view, you can connect to
the view's <literal>"drag-motion"</literal> signal to track the mouse
pointer while it is in a drag and drop operation over the tree view.
This is useful for example if you want to expand a collapsed node in a
tree when the mouse hovers above the node for a certain amount of time
during a drag'n'drop operation. Here is an example of how to achieve
this:</para>
<programlisting role="C">
/***************************************************************************
*
* onDragMotion_expand_timeout
*
* Timeout used to make sure that we expand rows only
* after hovering about them for a certain amount
* of time while doing Drag'n'Drop
*
***************************************************************************/
gboolean
onDragMotion_expand_timeout (GtkTreePath **path)
{
g_return_val_if_fail ( path != NULL, FALSE );
g_return_val_if_fail ( *path != NULL, FALSE );
gtk_tree_view_expand_row(GTK_TREE_VIEW(view), *path, FALSE);
return FALSE; /* only call once */
}
/***************************************************************************
*
* view_onDragMotion: we don't want to expand unexpanded nodes
* immediately when the mouse pointer passes across
* them during DnD. Instead, we only want to expand
* the node if the pointer has been hovering above the
* node for at least 1.5 seconds or so. To achieve this,
* we use a timeout that is removed whenever the row
* in focus changes.
*
***************************************************************************/
static gboolean
view_onDragMotion (GtkWidget *widget, GdkDragContext *context, gint x,
gint y, guint time, gpointer data)
{
static GtkTreePath *lastpath; /* NULL */
GtkTreePath *path = NULL;
if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), x, y, &amp;path, NULL, NULL, NULL))
{
if (!lastpath || ((lastpath) &amp;&amp; gtk_tree_path_compare(lastpath, path) != 0))
{
(void) g_source_remove_by_user_data(&amp;lastpath);
if (!gtk_tree_view_row_expanded(GTK_TREE_VIEW(widget), path))
{
/* 1500 = 1.5 secs */
g_timeout_add(1500, (GSourceFunc) onDragMotion_expand_timeout, &amp;lastpath);
}
}
}
else
{
g_source_remove_by_user_data(&amp;lastpath);
}
if (lastpath)
gtk_tree_path_free(lastpath);
lastpath = path;
return TRUE;
}
</programlisting>
<para>Connect to the view's <literal>"drag-drop"</literal> signal to be
called when the drop happens. You can translate the coordinates provided
into a tree path with <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeView.html#gtk-tree-view-get-path-at-pos">
<literal>gtk_tree_view_get_path_at_pos</literal></ulink>.</para>
</sect1>
<sect1 id="sec-dnd-rows-within-tree">
<title>Dragging Rows Around Within a Tree **** TODO ***</title>
<para>****** TODO</para>
<para>Both <classname>GtkListStore</classname> and
<classname>GtkTreeStore</classname> implement the
<classname>GtkTreeDragDest</classname> and
<classname>GtkTreeDragSource</classname> interfaces, which means that they
have in-built support for row reordering. You need to call
<literal>gtk_tree_view_set_reorderable</literal> to activate this, and
then connect to the tree model's signals to catch the reorderings that
take place.</para>
<para>*** SOMEONE NEEDS TO WRITE THIS SECTION (I have never gotten this
to work in a way that does not suck, ie. where one does not have to
place the row to move exact to the pixel on the target row).</para>
</sect1>
<sect1 id="sec-dnd-rows-between-trees">
<title>Dragging Rows from One Tree to Another **** TODO ***</title>
<para>****** TODO (is this possible at all in Gtk+ &lt;= 2.2?)</para>
</sect1>
</chapter>
<chapter id="sec-custom-models">
<title>Writing Custom Models</title>
<sect1 id="sec-custom-models-when">
<title>When is a Custom Model Useful?</title>
<para>A custom tree model gives you complete control over your data and
how it is represented to the outside (e.g. to the tree view widget). It
has the advantage that you can store, access and modify your data
exactly how you need it, and you can optimise the way your data is
stored and retrieved, as you can write your own functions to access your
data and need not rely solely on the
<literal>gtk_tree_model_get</literal>. A model tailored to your needs
will probably also be a lot faster than the generic list and tree stores
that come with gtk and that have been designed with flexibility in
mind.</para>
<para>Another case where a custom model might come in handy is when you
have all your data already stored in an external tree-like structure
(for example a libxml2 XML tree) and only want to display that
structure. Then you could write a custom model that maps that structure
to a tree model (which is probably not quite as trivial as it sounds
though).</para>
<para>Using a custom model you could also implement a filter model that
only displays certain rows according to some filter criterion instead of
displaying all rows (Gtk+-2.4 has a filter model, <classname> <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModelFilter.html">
GtkTreeModelFilter</ulink></classname>, that does exactly that and much
more, but you might want to implement this yourself anyway. If you need
to use GtkTreeModelFilter in Gtk-2.0 or Gtk-2.2, check out the code
examples of this tutorial - there is GuiTreeModelFilter, which is
basically just the original GtkTreeModelFilter but has been made to work
with earlier Gtk-2.x versions and has a different name space, so that it
does not clash with Gtk-2.4).</para>
<para>However, all this comes at a cost: you are unlikely to write a
useful custom model in less than a thousand lines, unless you strip all
newline characters. Writing a custom model is not as difficult as it
might sound though, and it may well be worth the effort, not least
because it will result in much saner code if you have a lot of data to
keep track of.</para>
</sect1>
<sect1 id="sec-custom-models-what">
<title>What Does Writing a Custom Model Involve?</title>
<para>Basically, all you need to do is to write a new GObject that
implements the <classname>GtkTreeModel</classname> interface,
<classname>GtkTreeModelIface</classname>. Intimate knowledge about the GLib
GObject system is not a requirement - you just need to copy some
boilerplate code and modify it a bit. The core of your custom tree model
is your own implementation of a couple of
<literal>gtk_tree_model_foo</literal> functions that reveal the
structure of your data, ie. how many rows there are, how many children a
row has, how many columns there are and what type of data they contain.
Furthermore, you need to provide functions that convert a tree path to a
tree iter and a tree iter to a tree path. Additionally, you should
provide some functions to add and remove rows to your custom model, but
those are only ever used by yourself anyway, so they do not fall within
the scope of the tree model interface.</para>
<para>The functions you <emphasis>need</emphasis> to implement
are:</para>
<itemizedlist>
<listitem>
<para><literal>get_flags</literal> - tells the outside that your
model has certain special characterstics, like persistent
iters.</para>
</listitem>
<listitem>
<para><literal>get_n_columns</literal> - how many data fields per
row are visible to the outside that uses gtk_tree_model_get, e.g.
cell renderer attributes</para>
</listitem>
<listitem>
<para><literal>get_column_type</literal> - what type of data is
stored in a data field (model column) that is visible to the
outside</para>
</listitem>
<listitem>
<para><literal>get_iter</literal> - take a tree path and fill an
iter structure so that you know which row it refers to</para>
</listitem>
<listitem>
<para><literal>get_path</literal> - take an iter and convert it into
a tree path, ie. the 'physical' position within the model</para>
</listitem>
<listitem>
<para><literal>get_value</literal> - retrieve data from a row</para>
</listitem>
<listitem>
<para><literal>iter_next</literal> - take an iter structure and make
it point to the next row</para>
</listitem>
<listitem>
<para><literal>iter_children</literal> - tell whether the row
represented by a given iter has any children or not</para>
</listitem>
<listitem>
<para><literal>iter_n_children</literal> - tell how many children a
row represented by a given iter has</para>
</listitem>
<listitem>
<para><literal>iter_nth_child</literal> - set a given iter structure
to the n-th child of a given parent iter</para>
</listitem>
<listitem>
<para><literal>iter_parent</literal> - set a given iter structure to
the parent of a given child iter</para>
</listitem>
</itemizedlist>
<para>It is up to you to decide which of your data you make 'visible' to
the outside in form of model columns and which not. You can always
implement functions specific to your custom model that will return any
data in any form you desire. You only <emphasis>need</emphasis> to make
data 'visble' to the outside via the GType and GValue system if you want
the tree view components to access it (e.g. when setting cell renderer
attributes).</para>
</sect1>
<sect1 id="sec-custom-model-list">
<title>Example: A Simple Custom List Model</title>
<para>What follows is the outline for a simple custom list model. You
can find the complete source code for this model <link
linkend="sec-custom-model-code">below</link>. The beginning of the code
might look a bit scary, but you can just skip most of the GObject and
GType stuff and proceed to the heart of the custom list, ie. the
implementation of the tree model functions.</para>
<para>Our list model is represented by a simple list of records, where
each row corresponds to a <classname>CustomRecord</classname> structure
which keeps track of the data we are interested in. For now, we only
want to keep track of persons' names and years of birth (usually this
would not really justify a custom model, but this is still just an
example). It is trivial to extend the model to deal with additional
fields in the <classname>CustomRecord</classname> structure.</para>
<para>Within the model, more precisely: the
<classname>CustomList</classname> structure, the list is stored as a pointer
array, which not only provides fast access to the n-th record in the
list, but also comes in handy later on when we add sorting. Apart from
that, any other kind of list-specific data would go in this structure as
well (the active sort column, for example, or hash tables to speed up
searching for a specific row, etc.).</para>
<para>Each row in our list is represented by a
<classname>CustomRecord</classname> structure. You can store whatever other
data you need in that structure. How you make row data available is up
to you. Either you export it via the tree model interface using the
<ulink
url="http://developer.gnome.org/doc/API/2.0/gobject/gobject-Standard-Parameter-and-Value-Types.html">GValue
system</ulink>, so that you can use
<literal>gtk_tree_model_get</literal> to retrieve your data, or you
provide custom model-specific functions to retrieve data, for example
<literal>custom_list_get_name</literal>, taking a tree iter or a tree
path as argument. Of course you can also do both.</para>
<para>Furthermore, you will need to provide your own functions to add
rows, remove rows, and set or modify row data, and you need to let the
view and others know whenever something changes in your model by
emitting the appropriate signals via the provided tree model
functions.</para>
<para>Some thought should go into how exactly you fill the <ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModel.html#GtkTreeIter">
<classname>GtkTreeIter</classname> fields</ulink> of the tree iters used by
your model. You have three pointer fields at your disposal. These should
be filled so that you can easily identify the row given the iter, and
should also facilitate access to the next row and the parent row (if
any). If your model advertises to have persistent iters, you need to
make sure that the content of your iters is perfectly valid even if the
user stores it somewhere for later use and the model gets changed or
reordered. The 'stamp' field of a tree iter should be filled by a random
model-instance-specific integer that was assigned to the model when it
was created. This way you can catch iters that do not belong to your
model. If your model does not have persistent iters, then you should
change the model's stamp whenever the model changes, so that you can
catch invalid iters that get passed to your functions (note: in the code
below we do not check the stamp of the iters in order to save a couple
of lines of code to print here).</para>
<para>In our specific example, we simply store a pointer to a row's
<classname>CustomRecord</classname> structure in our model's tree iters,
which is valid as long as the row exists. Additionally we store the
position of a row within the list in the <classname>CustomRecord</classname>
as well, which is not only intuitive, but is also useful later on when
we resort the list.</para>
<para>If you want to store an integer value in an iter's fields, you
should use GLib's <literal>GINT_TO_POINTER</literal> and
<literal>GPOINTER_TO_INT</literal> macros for that.</para>
<para>Let's look at the code sections in a bit more detail:</para>
<sect2 id="sec-custom-model-list-header">
<title>custom-list.h</title>
<para>The <link linkend="sec-custom-model-code-header">header
file</link> for our custom list model defines some standard type casts
and type check macros, our <classname>CustomRecord</classname> structure,
our <classname>CustomList</classname> structure, and some enums for the
model columns we are exporting.</para>
<para>The <classname>CustomRecord</classname> structure represents one
row, while the <classname>CustomList</classname> structure contains all
list-specific data. You can add additional fields to both structures
without problems. For example, you might need a function that quickly
looks up rows given the name or year of birth, for which additional
hashtables or so might come in handy (which you would need to keep up
to date as you insert, modify or remove rows of course).</para>
<para>The only function you must export is
<literal>custom_list_get_type</literal>, as it is used by the type
check and type cast macros that are also defined in the header file.
Additionally, we want to export a function to create one instance of
our custom model, and a function that adds some rows. You will
probably add more custom model-specific functions to modify the model
as you extend it to suit your needs.</para>
</sect2>
<sect2 id="sec-custom-model-list-body">
<title>custom-list.c</title>
<para>Firstly, we need some boilerplate code to register our custom
model with the GObject type system. You can skip this section and
proceed to the tree model implementation.</para>
<para>Functions of interested in this section are
<literal>custom_list_init</literal> and
<literal>custom_list_get_type</literal>. In
<literal>custom_list_init</literal> we define what data type our
exported model columns have, and how many columns we export. Towards
the end of <literal>custom_list_get_type</literal> we register the
<classname>GtkTreeModel</classname> interface with our custom model
object. This is where we can also register additional interfaces (e.g.
<classname>GtkTreeSortable</classname> or one of the Drag'n'Drop
interfaces) that we want to implement.</para>
<para>In <literal>custom_list_tree_model_init</literal> we override
those tree model functions that we need to implement with our own
functions. If it is beneficial for your model to know which rows are
currently displayed in the tree view (for example for caching), you
might want to override the <literal>ref_node</literal> and
<literal>unref_node</literal> functions as well.</para>
<para>Let's have a look at the heart of the object type
registration:</para>
<programlisting role="C">
GType
custom_list_get_type (void)
{
static GType custom_list_type = 0;
if (custom_list_type)
return custom_list_type;
/* Some boilerplate type registration stuff */
if (1)
{
static const GTypeInfo custom_list_info =
{
sizeof (CustomListClass),
NULL, /* base_init */
NULL, /* base_finalize */
(GClassInitFunc) custom_list_class_init,
NULL, /* class finalize */
NULL, /* class_data */
sizeof (CustomList),
0, /* n_preallocs */
(GInstanceInitFunc) custom_list_init
};
custom_list_type = g_type_register_static (G_TYPE_OBJECT, "CustomList",
&amp;custom_list_info, (GTypeFlags)0);
}
/* Here we register our GtkTreeModel interface with the type system */
if (1)
{
static const GInterfaceInfo tree_model_info =
{
(GInterfaceInitFunc) custom_list_tree_model_init,
NULL,
NULL
};
g_type_add_interface_static (custom_list_type, GTK_TYPE_TREE_MODEL, &amp;tree_model_info);
}
return custom_list_type;
}
</programlisting>
<para>Here we just return the type assigned to our custom list by the
type system if we have already registered it. If not, we register it
and save the type. Of the three callbacks that we pass to the type
system, only two are of immediate interest to us, namely
<literal>custom_list_tree_model_init</literal> and
<literal>custom_list_init</literal>.</para>
<para>In <literal>custom_list_tree_model_init</literal> we fill the
tree model interface structure with pointers to our own functions (at
least the ones we implement):</para>
<programlisting role="C">
static void
custom_list_tree_model_init (GtkTreeModelIface *iface)
{
/* Here we override the GtkTreeModel
* interface functions that we implement */
iface-&gt;get_flags = custom_list_get_flags;
iface-&gt;get_n_columns = custom_list_get_n_columns;
iface-&gt;get_column_type = custom_list_get_column_type;
iface-&gt;get_iter = custom_list_get_iter;
iface-&gt;get_path = custom_list_get_path;
iface-&gt;get_value = custom_list_get_value;
iface-&gt;iter_next = custom_list_iter_next;
iface-&gt;iter_children = custom_list_iter_children;
iface-&gt;iter_has_child = custom_list_iter_has_child;
iface-&gt;iter_n_children = custom_list_iter_n_children;
iface-&gt;iter_nth_child = custom_list_iter_nth_child;
iface-&gt;iter_parent = custom_list_iter_parent;
}
</programlisting>
<para>In <literal>custom_list_init</literal> we initialised the custom
list structure to sensible default values. This function will be
called whenever a new instance of our custom list is created, which we
do in <literal>custom_list_new</literal>.</para>
<para><literal>custom_list_finalize</literal> is called just before
one of our lists is going to be destroyed. You should free all
resources that you have dynamically allocated in there.</para>
<para>Having taken care of all the type system stuff, we now come to
the heart of our custom model, namely the tree model implementation.
Our tree model functions need to behave exactly as the API reference
requires them to behave, including all special cases, otherwise things
will not work. Here is a list of links to the API reference
descriptions of the functions we are implementing:</para>
<itemizedlist>
<listitem>
<para><ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModel.html#gtk-tree-model-get-flags">gtk_tree_model_get_flags</ulink></para>
</listitem>
<listitem>
<para><ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModel.html#gtk-tree-model-get-n-columns">gtk_tree_model_get_n_columns</ulink></para>
</listitem>
<listitem>
<para><ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModel.html#gtk-tree-model-get-column-type">gtk_tree_model_get_column_type</ulink></para>
</listitem>
<listitem>
<para><ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModel.html#gtk-tree-model-get-iter">gtk_tree_model_get_iter</ulink></para>
</listitem>
<listitem>
<para><ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModel.html#gtk-tree-model-get-path">gtk_tree_model_get_path</ulink></para>
</listitem>
<listitem>
<para><ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModel.html#gtk-tree-model-get-value">gtk_tree_model_get_value</ulink></para>
</listitem>
<listitem>
<para><ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModel.html#gtk-tree-model-iter-next">gtk_tree_model_iter_next</ulink></para>
</listitem>
<listitem>
<para><ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModel.html#gtk-tree-model-iter-children">gtk_tree_model_iter_children</ulink></para>
</listitem>
<listitem>
<para><ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModel.html#gtk-tree-model-iter-has-child">gtk_tree_model_iter_has_child</ulink></para>
</listitem>
<listitem>
<para><ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModel.html#gtk-tree-model-iter-n-children">gtk_tree_model_iter_n_children</ulink></para>
</listitem>
<listitem>
<para><ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModel.html#gtk-tree-model-iter-nth-child">gtk_tree_model_iter_nth_child</ulink></para>
</listitem>
<listitem>
<para><ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModel.html#gtk-tree-model-iter-parent">gtk_tree_model_iter_parent</ulink></para>
</listitem>
</itemizedlist>
<para>Almost all functions are more or less straight-forward and
self-explanatory in connection with the API reference descriptions, so
you should be able to jump right into <link
linkend="sec-custom-model-code-body">the code</link> and see how it
works.</para>
<para>After the tree model implementation we have those functions that
are specific to our custom model. <literal>custom_list_new</literal>
will create a new custom list for us, and
<literal>custom_list_append_record</literal> will append a new record
to the end of the list. Note the call to
<literal>gtk_tree_model_row_inserted</literal> at the end of our
append function, which emits a <literal>"row-inserted"</literal>
signal on the model and informs all interested objects (tree views,
tree row references) that a new row has been inserted, and where it
has been inserted.</para>
<para>You will need to emit tree model signals whenever something
changes, e.g. rows are inserted, removed, or reordered, or when a row
changes from a child-less row to a row which has children, or if a
row's data changes. Here are the functions you need to use in those
cases (we only implement row insertions here - other cases are left as
an exercise for the reader):</para>
<itemizedlist>
<listitem>
<para><ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModel.html#gtk-tree-model-row-inserted">gtk_tree_model_row_inserted</ulink></para>
</listitem>
<listitem>
<para><ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModel.html#gtk-tree-model-row-changed">gtk_tree_model_row_changed</ulink>
(makes tree view redraw that row)</para>
</listitem>
<listitem>
<para><ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModel.html#gtk-tree-model-row-has-child-toggled">gtk_tree_model_row_has_child_toggled</ulink></para>
</listitem>
<listitem>
<para><ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModel.html#gtk-tree-model-row-deleted">gtk_tree_model_row_deleted</ulink></para>
</listitem>
<listitem>
<para><ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeModel.html#gtk-tree-model-rows-reordered">gtk_tree_model_rows_reordered</ulink>
(note <ulink
url="http://bugs.gnome.org/show_bug.cgi?id=124790">bug
124790</ulink>)</para>
</listitem>
</itemizedlist>
<para>And that is all you have to do to write a custom model.</para>
</sect2>
</sect1>
<sect1 id="sec-custom-model-tree">
<title>From a List to a Tree</title>
<para>Writing a custom model for a tree is a bit trickier than a simple
list model, but follows the same pattern. Basically you just need to
extend the above model to cater for the case of children. You could do
this by keeping track of the whole tree hierarchy in the
<classname>CustomList</classname> structure, using GLib N-ary trees for
example, or you could do this by keeping track of each row's children
within the row's <classname>CustomRecord</classname> structure, keeping only
a pointer to the (invisible) root record in the
<classname>CustomList</classname> structure.</para>
<para>TODO: do we need anything else here?</para>
</sect1>
<sect1 id="sec-custom-model-sorting">
<title>Additional interfaces, here: the GtkTreeSortable
interface</title>
<para>A custom model can implement additional interfaces to extend its
functionality. Additional interfaces are:</para>
<itemizedlist>
<listitem>
<para><ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/GtkTreeSortable.html#GtkTreeSortableIface">GtkTreeSortableIface</ulink></para>
</listitem>
<listitem>
<para><ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/gtk-GtkTreeView-drag-and-drop.html#GtkTreeDragDestIface">GtkTreeDragDestIface</ulink></para>
</listitem>
<listitem>
<para><ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/gtk-GtkTreeView-drag-and-drop.html#GtkTreeDragSourceIface">GtkTreeDragSourceIface</ulink></para>
</listitem>
</itemizedlist>
<para>Here, we will show how to implement additional interfaces at the
example of the <classname>GtkTreeSortable</classname> interface, which we
will implement only partially (enough to make it functional and useful
though).</para>
<para>Three things are necessary to add another interface: we will need
to register the interface with our model in
<literal>custom_list_get_type</literal>, provide an interface init
function where we set the interface to our own implementation of the
interface functions, and then provide the implementation of those
functions.</para>
<para>Firstly, we need to provide the function prototypes for our
functions at the beginning of the file:</para>
<programlisting role="C">
/* custom-list.c */
...
/* -- GtkTreeSortable interface functions -- */
static gboolean custom_list_sortable_get_sort_column_id (GtkTreeSortable *sortable,
gint *sort_col_id,
GtkSortType *order);
static void custom_list_sortable_set_sort_column_id (GtkTreeSortable *sortable,
gint sort_col_id,
GtkSortType order);
static void custom_list_sortable_set_sort_func (GtkTreeSortable *sortable,
gint sort_col_id,
GtkTreeIterCompareFunc sort_func,
gpointer user_data,
GtkDestroyNotify destroy_func);
static void custom_list_sortable_set_default_sort_func (GtkTreeSortable *sortable,
GtkTreeIterCompareFunc sort_func,
gpointer user_data,
GtkDestroyNotify destroy_func);
static gboolean custom_list_sortable_has_default_sort_func (GtkTreeSortable *sortable);
static void custom_list_resort (CustomList *custom_list);
...
</programlisting>
<para>Next, let's extend our <classname>CustomList</classname> structure
with a field for the currently active sort column ID and one for the
sort order, and add an enum for the sort column IDs:</para>
<programlisting role="C">
/* custom-list.h */
enum
{
SORT_ID_NONE = 0,
SORT_ID_NAME,
SORT_ID_YEAR_BORN,
};
...
struct _CustomList
{
GObject parent;
guint num_rows; /* number of rows that we have */
CustomRecord **rows; /* a dynamically allocated array of pointers to the
* CustomRecord structure for each row */
gint n_columns;
GType column_types[CUSTOM_LIST_N_COLUMNS];
gint sort_id;
GtkSortType sort_order;
gint stamp; /* Random integer to check whether an iter belongs to our model */
};
...
</programlisting>
<para>Now, we make sure we initialise the new fields in
<literal>custom_list_new</literal>, and add our new interface:</para>
<programlisting role="C">
...
static void custom_list_sortable_init (GtkTreeSortableIface *iface);
...
void
custom_list_init (CustomList *custom_list)
{
...
custom_list-&gt;sort_id = SORT_ID_NONE;
custom_list-&gt;sort_order = GTK_SORT_ASCENDING;
...
}
GType
custom_list_get_type (void)
{
...
/* Add GtkTreeSortable interface */
if (1)
{
static const GInterfaceInfo tree_sortable_info =
{
(GInterfaceInitFunc) custom_list_sortable_init,
NULL,
NULL
};
g_type_add_interface_static (custom_list_type, GTK_TYPE_TREE_SORTABLE, &amp;tree_sortable_info);
}
...
}
static void
custom_list_sortable_init (GtkTreeSortableIface *iface)
{
iface-&gt;get_sort_column_id = custom_list_sortable_get_sort_column_id;
iface-&gt;set_sort_column_id = custom_list_sortable_set_sort_column_id;
iface-&gt;set_sort_func = custom_list_sortable_set_sort_func; /* NOT SUPPORTED */
iface-&gt;set_default_sort_func = custom_list_sortable_set_default_sort_func; /* NOT SUPPORTED */
iface-&gt;has_default_sort_func = custom_list_sortable_has_default_sort_func; /* NOT SUPPORTED */
}
</programlisting>
<para>Now that we have finally taken care of the administrativa, we
implement the tree sortable interface functions:</para>
<programlisting role="C">
static gboolean
custom_list_sortable_get_sort_column_id (GtkTreeSortable *sortable,
gint *sort_col_id,
GtkSortType *order)
{
CustomList *custom_list;
g_return_val_if_fail ( sortable != NULL , FALSE );
g_return_val_if_fail ( CUSTOM_IS_LIST(sortable), FALSE );
custom_list = CUSTOM_LIST(sortable);
if (sort_col_id)
*sort_col_id = custom_list-&gt;sort_id;
if (order)
*order = custom_list-&gt;sort_order;
return TRUE;
}
static void
custom_list_sortable_set_sort_column_id (GtkTreeSortable *sortable,
gint sort_col_id,
GtkSortType order)
{
CustomList *custom_list;
g_return_if_fail ( sortable != NULL );
g_return_if_fail ( CUSTOM_IS_LIST(sortable) );
custom_list = CUSTOM_LIST(sortable);
if (custom_list-&gt;sort_id == sort_col_id &amp;&amp; custom_list-&gt;sort_order == order)
return;
custom_list-&gt;sort_id = sort_col_id;
custom_list-&gt;sort_order = order;
custom_list_resort(custom_list);
/* emit "sort-column-changed" signal to tell any tree views
* that the sort column has changed (so the little arrow
* in the column header of the sort column is drawn
* in the right column) */
gtk_tree_sortable_sort_column_changed(sortable);
}
static void
custom_list_sortable_set_sort_func (GtkTreeSortable *sortable,
gint sort_col_id,
GtkTreeIterCompareFunc sort_func,
gpointer user_data,
GtkDestroyNotify destroy_func)
{
g_warning ("%s is not supported by the CustomList model.\n", __FUNCTION__);
}
static void
custom_list_sortable_set_default_sort_func (GtkTreeSortable *sortable,
GtkTreeIterCompareFunc sort_func,
gpointer user_data,
GtkDestroyNotify destroy_func)
{
g_warning ("%s is not supported by the CustomList model.\n", __FUNCTION__);
}
static gboolean
custom_list_sortable_has_default_sort_func (GtkTreeSortable *sortable)
{
return FALSE;
}
</programlisting>
<para>Now, last but not least, the only thing missing is the function
that does the actual sorting. We do not implement
<literal>set_sort_func</literal>,
<literal>set_default_sort_func</literal> and
<literal>set_has_default_sort_func</literal> because we use our own
internal sort function here.</para>
<para>The actual sorting is done using GLib's
<literal>g_qsort_with_data</literal> function, which sorts an array
using the QuickSort algorithm. Note how we notify the tree view and
other objects of the new row order by emitting the "rows-reordered"
signal on the tree model.</para>
<programlisting role="C">
static gint
custom_list_compare_records (gint sort_id, CustomRecord *a, CustomRecord *b)
{
switch(sort_id)
{
case SORT_ID_NONE:
return 0;
case SORT_ID_NAME:
{
if ((a-&gt;name) &amp;&amp; (b-&gt;name))
return g_utf8_collate(a-&gt;name, b-&gt;name);
if (a-&gt;name == b-&gt;name)
return 0; /* both are NULL */
else
return (a-&gt;name == NULL) ? -1 : 1;
}
case SORT_ID_YEAR_BORN:
{
if (a-&gt;year_born == b-&gt;year_born)
return 0;
return (a-&gt;year_born &gt; b-&gt;year_born) ? 1 : -1;
}
}
g_return_val_if_reached(0);
}
static gint
custom_list_qsort_compare_func (CustomRecord **a, CustomRecord **b, CustomList *custom_list)
{
gint ret;
g_assert ((a) &amp;&amp; (b) &amp;&amp; (custom_list));
ret = custom_list_compare_records(custom_list-&gt;sort_id, *a, *b);
/* Swap -1 and 1 if sort order is reverse */
if (ret != 0 &amp;&amp; custom_list-&gt;sort_order == GTK_SORT_DESCENDING)
ret = (ret &lt; 0) ? 1 : -1;
return ret;
}
static void
custom_list_resort (CustomList *custom_list)
{
GtkTreePath *path;
gint *neworder, i;
g_return_if_fail ( custom_list != NULL );
g_return_if_fail ( CUSTOM_IS_LIST(custom_list) );
if (custom_list-&gt;sort_id == SORT_ID_NONE)
return;
if (custom_list-&gt;num_rows == 0)
return;
/* resort */
g_qsort_with_data(custom_list-&gt;rows,
custom_list-&gt;num_rows,
sizeof(CustomRecord*),
(GCompareDataFunc) custom_list_qsort_compare_func,
custom_list);
/* let other objects know about the new order */
neworder = g_new0(gint, custom_list-&gt;num_rows);
for (i = 0; i &lt; custom_list-&gt;num_rows; ++i)
{
/* Note that the API reference might be wrong about
* this, see bug number 124790 on bugs.gnome.org.
* Both will work, but one will give you 'jumpy'
* selections after row reordering. */
/* neworder[(custom_list-&gt;rows[i])-&gt;pos] = i; */
neworder[i] = (custom_list-&gt;rows[i])-&gt;pos;
(custom_list-&gt;rows[i])-&gt;pos = i;
}
path = gtk_tree_path_new();
gtk_tree_model_rows_reordered(GTK_TREE_MODEL(custom_list), path, NULL, neworder);
gtk_tree_path_free(path);
g_free(neworder);
}
</programlisting>
<para>Finally, we should make sure that the model is resorted after we
have inserted a new row by adding a call to
<literal>custom_list_resort</literal> to the end of
<literal>custom_list_append</literal>:</para>
<programlisting role="C">
...
void
custom_list_append_record (CustomList *custom_list, const gchar *name, guint year_born)
{
...
custom_list_resort(custom_list);
}
</programlisting>
<para>And that is it. Adding two calls to
<literal>gtk_tree_view_column_set_sort_column_id</literal> in main.c is
left as yet another exercise for the reader.</para>
<para>If you are interested in seeing string sorting speed issues in
action, you should modify main.c like this:</para>
<programlisting role="C">
GtkWidget *
create_view_and_model (void)
{
gint i;
...
for (i=0; i &lt; 1000; ++i)
{
fill_model(customlist);
}
...
}
</programlisting>
<para>Most likely, sorting 24000 rows by name will take up to several
seconds now. Now, if you go back to
<literal>custom_list_compare_records</literal> and replace the call to
<literal>g_utf8_collate</literal> with:</para>
<programlisting role="C">
static gint
custom_list_compare_records (gint sort_id, CustomRecord *a, CustomRecord *b)
{
...
if ((a-&gt;name) &amp;&amp; (b-&gt;name))
return strcmp(a-&gt;name_collate_key,b-&gt;name_collate_key);
...
}
</programlisting>
<para>... then you should hopefully register a dramatic speed increase
when sorting by name.</para>
</sect1>
<sect1 id="sec-custom-model-code">
<title>Working Example: Custom List Model Source Code</title>
<para>Here is the complete source code for the custom list model
presented <link linkend="sec-custom-model-list">above</link>.</para>
<itemizedlist>
<listitem>
<para><link
linkend="sec-custom-model-code-header">custom-list.h</link></para>
</listitem>
<listitem>
<para><link
linkend="sec-custom-model-code-body">custom-list.c</link></para>
</listitem>
<listitem>
<para><link
linkend="sec-custom-model-code-example">main.c</link></para>
</listitem>
</itemizedlist>
<sect2 id="sec-custom-model-code-header">
<title>custom-list.h</title>
<programlisting role="C">
#ifndef _custom_list_h_included_
#define _custom_list_h_included_
#include &lt;gtk/gtk.h&gt;
/* Some boilerplate GObject defines. 'klass' is used
* instead of 'class', because 'class' is a C++ keyword */
#define CUSTOM_TYPE_LIST (custom_list_get_type ())
#define CUSTOM_LIST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CUSTOM_TYPE_LIST, CustomList))
#define CUSTOM_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CUSTOM_TYPE_LIST, CustomListClass))
#define CUSTOM_IS_LIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CUSTOM_TYPE_LIST))
#define CUSTOM_IS_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CUSTOM_TYPE_LIST))
#define CUSTOM_LIST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CUSTOM_TYPE_LIST, CustomListClass))
/* The data columns that we export via the tree model interface */
enum
{
CUSTOM_LIST_COL_RECORD = 0,
CUSTOM_LIST_COL_NAME,
CUSTOM_LIST_COL_YEAR_BORN,
CUSTOM_LIST_N_COLUMNS,
} ;
typedef struct _CustomRecord CustomRecord;
typedef struct _CustomList CustomList;
typedef struct _CustomListClass CustomListClass;
/* CustomRecord: this structure represents a row */
struct _CustomRecord
{
/* data - you can extend this */
gchar *name;
gchar *name_collate_key;
guint year_born;
/* admin stuff used by the custom list model */
guint pos; /* pos within the array */
};
/* CustomList: this structure contains everything we need for our
* model implementation. You can add extra fields to
* this structure, e.g. hashtables to quickly lookup
* rows or whatever else you might need, but it is
* crucial that 'parent' is the first member of the
* structure. */
struct _CustomList
{
GObject parent; /* this MUST be the first member */
guint num_rows; /* number of rows that we have */
CustomRecord **rows; /* a dynamically allocated array of pointers to
* the CustomRecord structure for each row */
/* These two fields are not absolutely necessary, but they */
/* speed things up a bit in our get_value implementation */
gint n_columns;
GType column_types[CUSTOM_LIST_N_COLUMNS];
gint stamp; /* Random integer to check whether an iter belongs to our model */
};
/* CustomListClass: more boilerplate GObject stuff */
struct _CustomListClass
{
GObjectClass parent_class;
};
GType custom_list_get_type (void);
CustomList *custom_list_new (void);
void custom_list_append_record (CustomList *custom_list,
const gchar *name,
guint year_born);
#endif /* _custom_list_h_included_ */
</programlisting>
<itemizedlist>
<listitem>
<para><link
linkend="sec-custom-model-code-header">custom-list.h</link></para>
</listitem>
<listitem>
<para><link
linkend="sec-custom-model-code-body">custom-list.c</link></para>
</listitem>
<listitem>
<para><link
linkend="sec-custom-model-code-example">main.c</link></para>
</listitem>
</itemizedlist>
</sect2>
<sect2 id="sec-custom-model-code-body">
<title>custom-list.c</title>
<programlisting role="C">
#include "custom-list.h"
/* boring declarations of local functions */
static void custom_list_init (CustomList *pkg_tree);
static void custom_list_class_init (CustomListClass *klass);
static void custom_list_tree_model_init (GtkTreeModelIface *iface);
static void custom_list_finalize (GObject *object);
static GtkTreeModelFlags custom_list_get_flags (GtkTreeModel *tree_model);
static gint custom_list_get_n_columns (GtkTreeModel *tree_model);
static GType custom_list_get_column_type (GtkTreeModel *tree_model,
gint index);
static gboolean custom_list_get_iter (GtkTreeModel *tree_model,
GtkTreeIter *iter,
GtkTreePath *path);
static GtkTreePath *custom_list_get_path (GtkTreeModel *tree_model,
GtkTreeIter *iter);
static void custom_list_get_value (GtkTreeModel *tree_model,
GtkTreeIter *iter,
gint column,
GValue *value);
static gboolean custom_list_iter_next (GtkTreeModel *tree_model,
GtkTreeIter *iter);
static gboolean custom_list_iter_children (GtkTreeModel *tree_model,
GtkTreeIter *iter,
GtkTreeIter *parent);
static gboolean custom_list_iter_has_child (GtkTreeModel *tree_model,
GtkTreeIter *iter);
static gint custom_list_iter_n_children (GtkTreeModel *tree_model,
GtkTreeIter *iter);
static gboolean custom_list_iter_nth_child (GtkTreeModel *tree_model,
GtkTreeIter *iter,
GtkTreeIter *parent,
gint n);
static gboolean custom_list_iter_parent (GtkTreeModel *tree_model,
GtkTreeIter *iter,
GtkTreeIter *child);
static GObjectClass *parent_class = NULL; /* GObject stuff - nothing to worry about */
/*****************************************************************************
*
* custom_list_get_type: here we register our new type and its interfaces
* with the type system. If you want to implement
* additional interfaces like GtkTreeSortable, you
* will need to do it here.
*
*****************************************************************************/
GType
custom_list_get_type (void)
{
static GType custom_list_type = 0;
if (custom_list_type)
return custom_list_type;
/* Some boilerplate type registration stuff */
if (1)
{
static const GTypeInfo custom_list_info =
{
sizeof (CustomListClass),
NULL, /* base_init */
NULL, /* base_finalize */
(GClassInitFunc) custom_list_class_init,
NULL, /* class finalize */
NULL, /* class_data */
sizeof (CustomList),
0, /* n_preallocs */
(GInstanceInitFunc) custom_list_init
};
custom_list_type = g_type_register_static (G_TYPE_OBJECT, "CustomList",
&amp;custom_list_info, (GTypeFlags)0);
}
/* Here we register our GtkTreeModel interface with the type system */
if (1)
{
static const GInterfaceInfo tree_model_info =
{
(GInterfaceInitFunc) custom_list_tree_model_init,
NULL,
NULL
};
g_type_add_interface_static (custom_list_type, GTK_TYPE_TREE_MODEL, &amp;tree_model_info);
}
return custom_list_type;
}
/*****************************************************************************
*
* custom_list_class_init: more boilerplate GObject/GType stuff.
* Init callback for the type system,
* called once when our new class is created.
*
*****************************************************************************/
static void
custom_list_class_init (CustomListClass *klass)
{
GObjectClass *object_class;
parent_class = (GObjectClass*) g_type_class_peek_parent (klass);
object_class = (GObjectClass*) klass;
object_class-&gt;finalize = custom_list_finalize;
}
/*****************************************************************************
*
* custom_list_tree_model_init: init callback for the interface registration
* in custom_list_get_type. Here we override
* the GtkTreeModel interface functions that
* we implement.
*
*****************************************************************************/
static void
custom_list_tree_model_init (GtkTreeModelIface *iface)
{
iface-&gt;get_flags = custom_list_get_flags;
iface-&gt;get_n_columns = custom_list_get_n_columns;
iface-&gt;get_column_type = custom_list_get_column_type;
iface-&gt;get_iter = custom_list_get_iter;
iface-&gt;get_path = custom_list_get_path;
iface-&gt;get_value = custom_list_get_value;
iface-&gt;iter_next = custom_list_iter_next;
iface-&gt;iter_children = custom_list_iter_children;
iface-&gt;iter_has_child = custom_list_iter_has_child;
iface-&gt;iter_n_children = custom_list_iter_n_children;
iface-&gt;iter_nth_child = custom_list_iter_nth_child;
iface-&gt;iter_parent = custom_list_iter_parent;
}
/*****************************************************************************
*
* custom_list_init: this is called everytime a new custom list object
* instance is created (we do that in custom_list_new).
* Initialise the list structure's fields here.
*
*****************************************************************************/
static void
custom_list_init (CustomList *custom_list)
{
custom_list-&gt;n_columns = CUSTOM_LIST_N_COLUMNS;
custom_list-&gt;column_types[0] = G_TYPE_POINTER; /* CUSTOM_LIST_COL_RECORD */
custom_list-&gt;column_types[1] = G_TYPE_STRING; /* CUSTOM_LIST_COL_NAME */
custom_list-&gt;column_types[2] = G_TYPE_UINT; /* CUSTOM_LIST_COL_YEAR_BORN */
g_assert (CUSTOM_LIST_N_COLUMNS == 3);
custom_list-&gt;num_rows = 0;
custom_list-&gt;rows = NULL;
custom_list-&gt;stamp = g_random_int(); /* Random int to check whether an iter belongs to our model */
}
/*****************************************************************************
*
* custom_list_finalize: this is called just before a custom list is
* destroyed. Free dynamically allocated memory here.
*
*****************************************************************************/
static void
custom_list_finalize (GObject *object)
{
/* CustomList *custom_list = CUSTOM_LIST(object); */
/* free all records and free all memory used by the list */
#warning IMPLEMENT
/* must chain up - finalize parent */
(* parent_class-&gt;finalize) (object);
}
/*****************************************************************************
*
* custom_list_get_flags: tells the rest of the world whether our tree model
* has any special characteristics. In our case,
* we have a list model (instead of a tree), and each
* tree iter is valid as long as the row in question
* exists, as it only contains a pointer to our struct.
*
*****************************************************************************/
static GtkTreeModelFlags
custom_list_get_flags (GtkTreeModel *tree_model)
{
g_return_val_if_fail (CUSTOM_IS_LIST(tree_model), (GtkTreeModelFlags)0);
return (GTK_TREE_MODEL_LIST_ONLY | GTK_TREE_MODEL_ITERS_PERSIST);
}
/*****************************************************************************
*
* custom_list_get_n_columns: tells the rest of the world how many data
* columns we export via the tree model interface
*
*****************************************************************************/
static gint
custom_list_get_n_columns (GtkTreeModel *tree_model)
{
g_return_val_if_fail (CUSTOM_IS_LIST(tree_model), 0);
return CUSTOM_LIST(tree_model)-&gt;n_columns;
}
/*****************************************************************************
*
* custom_list_get_column_type: tells the rest of the world which type of
* data an exported model column contains
*
*****************************************************************************/
static GType
custom_list_get_column_type (GtkTreeModel *tree_model,
gint index)
{
g_return_val_if_fail (CUSTOM_IS_LIST(tree_model), G_TYPE_INVALID);
g_return_val_if_fail (index &lt; CUSTOM_LIST(tree_model)-&gt;n_columns &amp;&amp; index &gt;= 0, G_TYPE_INVALID);
return CUSTOM_LIST(tree_model)-&gt;column_types[index];
}
/*****************************************************************************
*
* custom_list_get_iter: converts a tree path (physical position) into a
* tree iter structure (the content of the iter
* fields will only be used internally by our model).
* We simply store a pointer to our CustomRecord
* structure that represents that row in the tree iter.
*
*****************************************************************************/
static gboolean
custom_list_get_iter (GtkTreeModel *tree_model,
GtkTreeIter *iter,
GtkTreePath *path)
{
CustomList *custom_list;
CustomRecord *record;
gint *indices, n, depth;
g_assert(CUSTOM_IS_LIST(tree_model));
g_assert(path!=NULL);
custom_list = CUSTOM_LIST(tree_model);
indices = gtk_tree_path_get_indices(path);
depth = gtk_tree_path_get_depth(path);
/* we do not allow children */
g_assert(depth == 1); /* depth 1 = top level; a list only has top level nodes and no children */
n = indices[0]; /* the n-th top level row */
if ( n &gt;= custom_list-&gt;num_rows || n &lt; 0 )
return FALSE;
record = custom_list-&gt;rows[n];
g_assert(record != NULL);
g_assert(record-&gt;pos == n);
/* We simply store a pointer to our custom record in the iter */
iter-&gt;stamp = custom_list-&gt;stamp;
iter-&gt;user_data = record;
iter-&gt;user_data2 = NULL; /* unused */
iter-&gt;user_data3 = NULL; /* unused */
return TRUE;
}
/*****************************************************************************
*
* custom_list_get_path: converts a tree iter into a tree path (ie. the
* physical position of that row in the list).
*
*****************************************************************************/
static GtkTreePath *
custom_list_get_path (GtkTreeModel *tree_model,
GtkTreeIter *iter)
{
GtkTreePath *path;
CustomRecord *record;
CustomList *custom_list;
g_return_val_if_fail (CUSTOM_IS_LIST(tree_model), NULL);
g_return_val_if_fail (iter != NULL, NULL);
g_return_val_if_fail (iter-&gt;user_data != NULL, NULL);
custom_list = CUSTOM_LIST(tree_model);
record = (CustomRecord*) iter-&gt;user_data;
path = gtk_tree_path_new();
gtk_tree_path_append_index(path, record-&gt;pos);
return path;
}
/*****************************************************************************
*
* custom_list_get_value: Returns a row's exported data columns
* (_get_value is what gtk_tree_model_get uses)
*
*****************************************************************************/
static void
custom_list_get_value (GtkTreeModel *tree_model,
GtkTreeIter *iter,
gint column,
GValue *value)
{
CustomRecord *record;
CustomList *custom_list;
g_return_if_fail (CUSTOM_IS_LIST (tree_model));
g_return_if_fail (iter != NULL);
g_return_if_fail (column &lt; CUSTOM_LIST(tree_model)-&gt;n_columns);
g_value_init (value, CUSTOM_LIST(tree_model)-&gt;column_types[column]);
custom_list = CUSTOM_LIST(tree_model);
record = (CustomRecord*) iter-&gt;user_data;
g_return_if_fail ( record != NULL );
if(record-&gt;pos &gt;= custom_list-&gt;num_rows)
g_return_if_reached();
switch(column)
{
case CUSTOM_LIST_COL_RECORD:
g_value_set_pointer(value, record);
break;
case CUSTOM_LIST_COL_NAME:
g_value_set_string(value, record-&gt;name);
break;
case CUSTOM_LIST_COL_YEAR_BORN:
g_value_set_uint(value, record-&gt;year_born);
break;
}
}
/*****************************************************************************
*
* custom_list_iter_next: Takes an iter structure and sets it to point
* to the next row.
*
*****************************************************************************/
static gboolean
custom_list_iter_next (GtkTreeModel *tree_model,
GtkTreeIter *iter)
{
CustomRecord *record, *nextrecord;
CustomList *custom_list;
g_return_val_if_fail (CUSTOM_IS_LIST (tree_model), FALSE);
if (iter == NULL || iter-&gt;user_data == NULL)
return FALSE;
custom_list = CUSTOM_LIST(tree_model);
record = (CustomRecord *) iter-&gt;user_data;
/* Is this the last record in the list? */
if ((record-&gt;pos + 1) &gt;= custom_list-&gt;num_rows)
return FALSE;
nextrecord = custom_list-&gt;rows[(record-&gt;pos + 1)];
g_assert ( nextrecord != NULL );
g_assert ( nextrecord-&gt;pos == (record-&gt;pos + 1) );
iter-&gt;stamp = custom_list-&gt;stamp;
iter-&gt;user_data = nextrecord;
return TRUE;
}
/*****************************************************************************
*
* custom_list_iter_children: Returns TRUE or FALSE depending on whether
* the row specified by 'parent' has any children.
* If it has children, then 'iter' is set to
* point to the first child. Special case: if
* 'parent' is NULL, then the first top-level
* row should be returned if it exists.
*
*****************************************************************************/
static gboolean
custom_list_iter_children (GtkTreeModel *tree_model,
GtkTreeIter *iter,
GtkTreeIter *parent)
{
CustomList *custom_list;
g_return_val_if_fail (parent == NULL || parent-&gt;user_data != NULL, FALSE);
/* this is a list, nodes have no children */
if (parent)
return FALSE;
/* parent == NULL is a special case; we need to return the first top-level row */
g_return_val_if_fail (CUSTOM_IS_LIST (tree_model), FALSE);
custom_list = CUSTOM_LIST(tree_model);
/* No rows =&gt; no first row */
if (custom_list-&gt;num_rows == 0)
return FALSE;
/* Set iter to first item in list */
iter-&gt;stamp = custom_list-&gt;stamp;
iter-&gt;user_data = custom_list-&gt;rows[0];
return TRUE;
}
/*****************************************************************************
*
* custom_list_iter_has_child: Returns TRUE or FALSE depending on whether
* the row specified by 'iter' has any children.
* We only have a list and thus no children.
*
*****************************************************************************/
static gboolean
custom_list_iter_has_child (GtkTreeModel *tree_model,
GtkTreeIter *iter)
{
return FALSE;
}
/*****************************************************************************
*
* custom_list_iter_n_children: Returns the number of children the row
* specified by 'iter' has. This is usually 0,
* as we only have a list and thus do not have
* any children to any rows. A special case is
* when 'iter' is NULL, in which case we need
* to return the number of top-level nodes,
* ie. the number of rows in our list.
*
*****************************************************************************/
static gint
custom_list_iter_n_children (GtkTreeModel *tree_model,
GtkTreeIter *iter)
{
CustomList *custom_list;
g_return_val_if_fail (CUSTOM_IS_LIST (tree_model), -1);
g_return_val_if_fail (iter == NULL || iter-&gt;user_data != NULL, FALSE);
custom_list = CUSTOM_LIST(tree_model);
/* special case: if iter == NULL, return number of top-level rows */
if (!iter)
return custom_list-&gt;num_rows;
return 0; /* otherwise, this is easy again for a list */
}
/*****************************************************************************
*
* custom_list_iter_nth_child: If the row specified by 'parent' has any
* children, set 'iter' to the n-th child and
* return TRUE if it exists, otherwise FALSE.
* A special case is when 'parent' is NULL, in
* which case we need to set 'iter' to the n-th
* row if it exists.
*
*****************************************************************************/
static gboolean
custom_list_iter_nth_child (GtkTreeModel *tree_model,
GtkTreeIter *iter,
GtkTreeIter *parent,
gint n)
{
CustomRecord *record;
CustomList *custom_list;
g_return_val_if_fail (CUSTOM_IS_LIST (tree_model), FALSE);
custom_list = CUSTOM_LIST(tree_model);
/* a list has only top-level rows */
if(parent)
return FALSE;
/* special case: if parent == NULL, set iter to n-th top-level row */
if( n &gt;= custom_list-&gt;num_rows )
return FALSE;
record = custom_list-&gt;rows[n];
g_assert( record != NULL );
g_assert( record-&gt;pos == n );
iter-&gt;stamp = custom_list-&gt;stamp;
iter-&gt;user_data = record;
return TRUE;
}
/*****************************************************************************
*
* custom_list_iter_parent: Point 'iter' to the parent node of 'child'. As
* we have a list and thus no children and no
* parents of children, we can just return FALSE.
*
*****************************************************************************/
static gboolean
custom_list_iter_parent (GtkTreeModel *tree_model,
GtkTreeIter *iter,
GtkTreeIter *child)
{
return FALSE;
}
/*****************************************************************************
*
* custom_list_new: This is what you use in your own code to create a
* new custom list tree model for you to use.
*
*****************************************************************************/
CustomList *
custom_list_new (void)
{
CustomList *newcustomlist;
newcustomlist = (CustomList*) g_object_new (CUSTOM_TYPE_LIST, NULL);
g_assert( newcustomlist != NULL );
return newcustomlist;
}
/*****************************************************************************
*
* custom_list_append_record: Empty lists are boring. This function can
* be used in your own code to add rows to the
* list. Note how we emit the "row-inserted"
* signal after we have appended the row
* internally, so the tree view and other
* interested objects know about the new row.
*
*****************************************************************************/
void
custom_list_append_record (CustomList *custom_list,
const gchar *name,
guint year_born)
{
GtkTreeIter iter;
GtkTreePath *path;
CustomRecord *newrecord;
gulong newsize;
guint pos;
g_return_if_fail (CUSTOM_IS_LIST(custom_list));
g_return_if_fail (name != NULL);
pos = custom_list-&gt;num_rows;
custom_list-&gt;num_rows++;
newsize = custom_list-&gt;num_rows * sizeof(CustomRecord*);
custom_list-&gt;rows = g_realloc(custom_list-&gt;rows, newsize);
newrecord = g_new0(CustomRecord, 1);
newrecord-&gt;name = g_strdup(name);
newrecord-&gt;name_collate_key = g_utf8_collate_key(name,-1); /* for fast sorting, used later */
newrecord-&gt;year_born = year_born;
custom_list-&gt;rows[pos] = newrecord;
newrecord-&gt;pos = pos;
/* inform the tree view and other interested objects
* (e.g. tree row references) that we have inserted
* a new row, and where it was inserted */
path = gtk_tree_path_new();
gtk_tree_path_append_index(path, newrecord-&gt;pos);
custom_list_get_iter(GTK_TREE_MODEL(custom_list), &amp;iter, path);
gtk_tree_model_row_inserted(GTK_TREE_MODEL(custom_list), path, &amp;iter);
gtk_tree_path_free(path);
}
</programlisting>
<itemizedlist>
<listitem>
<para><link
linkend="sec-custom-model-code-header">custom-list.h</link></para>
</listitem>
<listitem>
<para><link
linkend="sec-custom-model-code-body">custom-list.c</link></para>
</listitem>
<listitem>
<para><link
linkend="sec-custom-model-code-example">main.c</link></para>
</listitem>
</itemizedlist>
</sect2>
<sect2 id="sec-custom-model-code-example">
<title>main.c</title>
<para>The following couple of lines provide a working test case that
makes use of our custom list. It creates one of our custom lists, adds
some records, and displays it in a tree view.</para>
<programlisting role="C">
#include "custom-list.h"
#include &lt;stdlib.h&gt;
void
fill_model (CustomList *customlist)
{
const gchar *firstnames[] = { "Joe", "Jane", "William", "Hannibal", "Timothy", "Gargamel", NULL } ;
const gchar *surnames[] = { "Grokowich", "Twitch", "Borheimer", "Bork", NULL } ;
const gchar **fname, **sname;
for (sname = surnames; *sname != NULL; sname++)
{
for (fname = firstnames; *fname != NULL; fname++)
{
gchar *name = g_strdup_printf ("%s %s", *fname, *sname);
custom_list_append_record (customlist, name, 1900 + (guint) (103.0*rand()/(RAND_MAX+1900.0)));
g_free(name);
}
}
}
GtkWidget *
create_view_and_model (void)
{
GtkTreeViewColumn *col;
GtkCellRenderer *renderer;
CustomList *customlist;
GtkWidget *view;
customlist = custom_list_new();
fill_model(customlist);
view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(customlist));
g_object_unref(customlist); /* destroy store automatically with view */
renderer = gtk_cell_renderer_text_new();
col = gtk_tree_view_column_new();
gtk_tree_view_column_pack_start (col, renderer, TRUE);
gtk_tree_view_column_add_attribute (col, renderer, "text", CUSTOM_LIST_COL_NAME);
gtk_tree_view_column_set_title (col, "Name");
gtk_tree_view_append_column(GTK_TREE_VIEW(view),col);
renderer = gtk_cell_renderer_text_new();
col = gtk_tree_view_column_new();
gtk_tree_view_column_pack_start (col, renderer, TRUE);
gtk_tree_view_column_add_attribute (col, renderer, "text", CUSTOM_LIST_COL_YEAR_BORN);
gtk_tree_view_column_set_title (col, "Year Born");
gtk_tree_view_append_column(GTK_TREE_VIEW(view),col);
return view;
}
int
main (int argc, char **argv)
{
GtkWidget *window, *view, *scrollwin;
gtk_init(&amp;argc,&amp;argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_default_size (GTK_WINDOW(window), 200, 400);
g_signal_connect(window, "delete_event", gtk_main_quit, NULL);
scrollwin = gtk_scrolled_window_new(NULL,NULL);
view = create_view_and_model();
gtk_container_add(GTK_CONTAINER(scrollwin), view);
gtk_container_add(GTK_CONTAINER(window), scrollwin);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
</programlisting>
</sect2>
</sect1>
</chapter>
<chapter id="sec-custom-cell-renderers">
<title>Writing Custom Cell Renderers</title>
<para>The cell renderers that come with Gtk+ should be sufficient for most
purposes, but there might be occasions where you want to display something
in a tree view that you cannot display with the provided cell renderers,
or where you want to derive from one of the provided cell renderers to
extend its functionality.</para>
<para>You can do this by writing a new object that derives from
<classname>GtkCellRenderer</classname> (or even one of the other cell
renderers if you just want to extend an existing one).</para>
<para>Three things you need to do in the course of that:</para>
<itemizedlist>
<listitem>
<para>Register some new properties that your renderer needs with the
type system and write your own <literal>set_property</literal> and
<literal>get_property</literal> functions to set and get your new
renderer's properties.</para>
</listitem>
<listitem>
<para>Write your own <literal>cell_renderer_get_size</literal>
function and override the parent object's function (usually the parent
is of type <classname>GtkCellRenderer</classname>. Note that you should
honour the standard properties for padding and cell alignment of the
parent object here.</para>
</listitem>
<listitem>
<para>Write your own <literal>cell_renderer_render</literal> function
and override the parent object's function. This function does the
actual rendering.</para>
</listitem>
</itemizedlist>
<para>The GObject type system stuff of writing a new cell renderer is
similar to what we have done above when writing a custom tree model, and
is relatively straight forward in this case. Copy and paste and modify
according to your own needs.</para>
<para>Good examples of cell renderer code to look at or even modify are
<classname>GtkCellRendererPixbuf</classname> and
<classname>GtkCellRendererToggle</classname> in the Gtk+ source code tree.
Both cases are less than five hundred lines of code to look at and thus
should be fairly easy to digest.</para>
<sect1 id="sec-custom-cell-renderer-example">
<title>Working Example: a Progress Bar Cell Renderer</title>
<para>In the following we will write a custom cell renderer to render
progress bars into a tree view (the code was "heavily inspired" by Sean
Egan's progress bar cell renderer implementation in GAIM):</para>
<itemizedlist>
<listitem>
<para><link
linkend="sec-custom-cell-renderer-example-header">custom-cell-renderer-progressbar.h</link></para>
</listitem>
<listitem>
<para><link
linkend="sec-custom-cell-renderer-example-body">custom-cell-renderer-progressbar.c</link></para>
</listitem>
<listitem>
<para><link
linkend="sec-custom-cell-renderer-example-test">main.c</link></para>
</listitem>
</itemizedlist>
<sect2 id="sec-custom-cell-renderer-example-header">
<title>custom-cell-renderer-progressbar.h</title>
<para>The header file consists of the usual GObject type cast and type
check defines and our <classname>CustomCellRendererProgress</classname>
structure. As the type of the parent indicates, we derive from
<classname>GtkCellRenderer</classname>. The parent object must always be
the first item in the structure (note also that it is not a pointer to
an object, but the parent object structure itself embedded in our
structure).</para>
<para>Our <classname>CustomCellRendererProgress</classname> structure is
fairly uneventful and contains only a double precision float variable
in which we store our new <literal>"percentage"</literal> property
(which will determine how long the progressbar is going to be).</para>
<programlisting role="C">
#ifndef _custom_cell_renderer_progressbar_included_
#define _custom_cell_renderer_progressbar_included_
#include &lt;gtk/gtk.h&gt;
/* Some boilerplate GObject type check and type cast macros.
* 'klass' is used here instead of 'class', because 'class'
* is a c++ keyword */
#define CUSTOM_TYPE_CELL_RENDERER_PROGRESS (custom_cell_renderer_progress_get_type())
#define CUSTOM_CELL_RENDERER_PROGRESS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), CUSTOM_TYPE_CELL_RENDERER_PROGRESS, CustomCellRendererProgress))
#define CUSTOM_CELL_RENDERER_PROGRESS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CUSTOM_TYPE_CELL_RENDERER_PROGRESS, CustomCellRendererProgressClass))
#define CUSTOM_IS_CELL_PROGRESS_PROGRESS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CUSTOM_TYPE_CELL_RENDERER_PROGRESS))
#define CUSTOM_IS_CELL_PROGRESS_PROGRESS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CUSTOM_TYPE_CELL_RENDERER_PROGRESS))
#define CUSTOM_CELL_RENDERER_PROGRESS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CUSTOM_TYPE_CELL_RENDERER_PROGRESS, CustomCellRendererProgressClass))
typedef struct _CustomCellRendererProgress CustomCellRendererProgress;
typedef struct _CustomCellRendererProgressClass CustomCellRendererProgressClass;
/* CustomCellRendererProgress: Our custom cell renderer
* structure. Extend according to need */
struct _CustomCellRendererProgress
{
GtkCellRenderer parent;
gdouble progress;
};
struct _CustomCellRendererProgressClass
{
GtkCellRendererClass parent_class;
};
GType custom_cell_renderer_progress_get_type (void);
GtkCellRenderer *custom_cell_renderer_progress_new (void);
#endif /* _custom_cell_renderer_progressbar_included_ */
</programlisting>
</sect2>
<sect2 id="sec-custom-cell-renderer-example-body">
<title>custom-cell-renderer-progressbar.c</title>
<para>The code contains everything as described above, so let's jump
right into it:</para>
<programlisting role="C">
#include "custom-cell-renderer-progressbar.h"
/* This is based mainly on GtkCellRendererProgress
* in GAIM, written and (c) 2002 by Sean Egan
* (Licensed under the GPL), which in turn is
* based on Gtk's GtkCellRenderer[Text|Toggle|Pixbuf]
* implementation by Jonathan Blandford */
/* Some boring function declarations: GObject type system stuff */
static void custom_cell_renderer_progress_init (CustomCellRendererProgress *cellprogress);
static void custom_cell_renderer_progress_class_init (CustomCellRendererProgressClass *klass);
static void custom_cell_renderer_progress_get_property (GObject *object,
guint param_id,
GValue *value,
GParamSpec *pspec);
static void custom_cell_renderer_progress_set_property (GObject *object,
guint param_id,
const GValue *value,
GParamSpec *pspec);
static void custom_cell_renderer_progress_finalize (GObject *gobject);
/* These functions are the heart of our custom cell renderer: */
static void custom_cell_renderer_progress_get_size (GtkCellRenderer *cell,
GtkWidget *widget,
GdkRectangle *cell_area,
gint *x_offset,
gint *y_offset,
gint *width,
gint *height);
static void custom_cell_renderer_progress_render (GtkCellRenderer *cell,
GdkWindow *window,
GtkWidget *widget,
GdkRectangle *background_area,
GdkRectangle *cell_area,
GdkRectangle *expose_area,
guint flags);
enum
{
PROP_PERCENTAGE = 1,
};
static gpointer parent_class;
/***************************************************************************
*
* custom_cell_renderer_progress_get_type: here we register our type with
* the GObject type system if we
* haven't done so yet. Everything
* else is done in the callbacks.
*
***************************************************************************/
GType
custom_cell_renderer_progress_get_type (void)
{
static GType cell_progress_type = 0;
if (cell_progress_type)
return cell_progress_type;
if (1)
{
static const GTypeInfo cell_progress_info =
{
sizeof (CustomCellRendererProgressClass),
NULL, /* base_init */
NULL, /* base_finalize */
(GClassInitFunc) custom_cell_renderer_progress_class_init,
NULL, /* class_finalize */
NULL, /* class_data */
sizeof (CustomCellRendererProgress),
0, /* n_preallocs */
(GInstanceInitFunc) custom_cell_renderer_progress_init,
};
/* Derive from GtkCellRenderer */
cell_progress_type = g_type_register_static (GTK_TYPE_CELL_RENDERER,
"CustomCellRendererProgress",
&amp;cell_progress_info,
0);
}
return cell_progress_type;
}
/***************************************************************************
*
* custom_cell_renderer_progress_init: set some default properties of the
* parent (GtkCellRenderer).
*
***************************************************************************/
static void
custom_cell_renderer_progress_init (CustomCellRendererProgress *cellrendererprogress)
{
GTK_CELL_RENDERER(cellrendererprogress)-&gt;mode = GTK_CELL_RENDERER_MODE_INERT;
GTK_CELL_RENDERER(cellrendererprogress)-&gt;xpad = 2;
GTK_CELL_RENDERER(cellrendererprogress)-&gt;ypad = 2;
}
/***************************************************************************
*
* custom_cell_renderer_progress_class_init:
*
* set up our own get_property and set_property functions, and
* override the parent's functions that we need to implement.
* And make our new "percentage" property known to the type system.
* If you want cells that can be activated on their own (ie. not
* just the whole row selected) or cells that are editable, you
* will need to override 'activate' and 'start_editing' as well.
*
***************************************************************************/
static void
custom_cell_renderer_progress_class_init (CustomCellRendererProgressClass *klass)
{
GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS(klass);
GObjectClass *object_class = G_OBJECT_CLASS(klass);
parent_class = g_type_class_peek_parent (klass);
object_class-&gt;finalize = custom_cell_renderer_progress_finalize;
/* Hook up functions to set and get our
* custom cell renderer properties */
object_class-&gt;get_property = custom_cell_renderer_progress_get_property;
object_class-&gt;set_property = custom_cell_renderer_progress_set_property;
/* Override the two crucial functions that are the heart
* of a cell renderer in the parent class */
cell_class-&gt;get_size = custom_cell_renderer_progress_get_size;
cell_class-&gt;render = custom_cell_renderer_progress_render;
/* Install our very own properties */
g_object_class_install_property (object_class,
PROP_PERCENTAGE,
g_param_spec_double ("percentage",
"Percentage",
"The fractional progress to display",
0, 1, 0,
G_PARAM_READWRITE));
}
/***************************************************************************
*
* custom_cell_renderer_progress_finalize: free any resources here
*
***************************************************************************/
static void
custom_cell_renderer_progress_finalize (GObject *object)
{
/*
CustomCellRendererProgress *cellrendererprogress = CUSTOM_CELL_RENDERER_PROGRESS(object);
*/
/* Free any dynamically allocated resources here */
(* G_OBJECT_CLASS (parent_class)-&gt;finalize) (object);
}
/***************************************************************************
*
* custom_cell_renderer_progress_get_property: as it says
*
***************************************************************************/
static void
custom_cell_renderer_progress_get_property (GObject *object,
guint param_id,
GValue *value,
GParamSpec *psec)
{
CustomCellRendererProgress *cellprogress = CUSTOM_CELL_RENDERER_PROGRESS(object);
switch (param_id)
{
case PROP_PERCENTAGE:
g_value_set_double(value, cellprogress-&gt;progress);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, psec);
break;
}
}
/***************************************************************************
*
* custom_cell_renderer_progress_set_property: as it says
*
***************************************************************************/
static void
custom_cell_renderer_progress_set_property (GObject *object,
guint param_id,
const GValue *value,
GParamSpec *pspec)
{
CustomCellRendererProgress *cellprogress = CUSTOM_CELL_RENDERER_PROGRESS (object);
switch (param_id)
{
case PROP_PERCENTAGE:
cellprogress-&gt;progress = g_value_get_double(value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
break;
}
}
/***************************************************************************
*
* custom_cell_renderer_progress_new: return a new cell renderer instance
*
***************************************************************************/
GtkCellRenderer *
custom_cell_renderer_progress_new (void)
{
return g_object_new(CUSTOM_TYPE_CELL_RENDERER_PROGRESS, NULL);
}
/***************************************************************************
*
* custom_cell_renderer_progress_get_size: crucial - calculate the size
* of our cell, taking into account
* padding and alignment properties
* of parent.
*
***************************************************************************/
#define FIXED_WIDTH 100
#define FIXED_HEIGHT 10
static void
custom_cell_renderer_progress_get_size (GtkCellRenderer *cell,
GtkWidget *widget,
GdkRectangle *cell_area,
gint *x_offset,
gint *y_offset,
gint *width,
gint *height)
{
gint calc_width;
gint calc_height;
calc_width = (gint) cell-&gt;xpad * 2 + FIXED_WIDTH;
calc_height = (gint) cell-&gt;ypad * 2 + FIXED_HEIGHT;
if (width)
*width = calc_width;
if (height)
*height = calc_height;
if (cell_area)
{
if (x_offset)
{
*x_offset = cell-&gt;xalign * (cell_area-&gt;width - calc_width);
*x_offset = MAX (*x_offset, 0);
}
if (y_offset)
{
*y_offset = cell-&gt;yalign * (cell_area-&gt;height - calc_height);
*y_offset = MAX (*y_offset, 0);
}
}
}99
/***************************************************************************
*
* custom_cell_renderer_progress_render: crucial - do the rendering.
*
***************************************************************************/
static void
custom_cell_renderer_progress_render (GtkCellRenderer *cell,
GdkWindow *window,
GtkWidget *widget,
GdkRectangle *background_area,
GdkRectangle *cell_area,
GdkRectangle *expose_area,
guint flags)
{
CustomCellRendererProgress *cellprogress = CUSTOM_CELL_RENDERER_PROGRESS (cell);
GtkStateType state;
gint width, height;
gint x_offset, y_offset;
custom_cell_renderer_progress_get_size (cell, widget, cell_area,
&amp;x_offset, &amp;y_offset,
&amp;width, &amp;height);
if (GTK_WIDGET_HAS_FOCUS (widget))
state = GTK_STATE_ACTIVE;
else
state = GTK_STATE_NORMAL;
width -= cell-&gt;xpad*2;
height -= cell-&gt;ypad*2;
gtk_paint_box (widget-&gt;style,
window,
GTK_STATE_NORMAL, GTK_SHADOW_IN,
NULL, widget, "trough",
cell_area-&gt;x + x_offset + cell-&gt;xpad,
cell_area-&gt;y + y_offset + cell-&gt;ypad,
width - 1, height - 1);
gtk_paint_box (widget-&gt;style,
window,
state, GTK_SHADOW_OUT,
NULL, widget, "bar",
cell_area-&gt;x + x_offset + cell-&gt;xpad,
cell_area-&gt;y + y_offset + cell-&gt;ypad,
width * cellprogress-&gt;progress,
height - 1);
}
</programlisting>
</sect2>
<sect2 id="sec-custom-cell-renderer-example-test">
<title>main.c</title>
<para>And here is a little test that makes use of our new
<classname>CustomCellRendererProgress</classname>:</para>
<programlisting role="C">
#include "custom-cell-renderer-progressbar.h"
static GtkListStore *liststore;
static gboolean increasing = TRUE; /* direction of progress bar change */
enum
{
COL_PERCENTAGE = 0,
COL_TEXT,
NUM_COLS
};
#define STEP 0.01
gboolean
increase_progress_timeout (GtkCellRenderer *renderer)
{
GtkTreeIter iter;
gfloat perc = 0.0;
gchar buf[20];
gtk_tree_model_get_iter_first(GTK_TREE_MODEL(liststore), &amp;iter); /* first and only row */
gtk_tree_model_get (GTK_TREE_MODEL(liststore), &amp;iter, COL_PERCENTAGE, &amp;perc, -1);
if ( perc &gt; (1.0-STEP) || (perc &lt; STEP &amp;&amp; perc &gt; 0.0) )
{
increasing = (!increasing);
}
if (increasing)
perc = perc + STEP;
else
perc = perc - STEP;
g_snprintf(buf, sizeof(buf), "%u %%", (guint)(perc*100));
gtk_list_store_set (liststore, &amp;iter, COL_PERCENTAGE, perc, COL_TEXT, buf, -1);
return TRUE; /* Call again */
}
GtkWidget *
create_view_and_model (void)
{
GtkTreeViewColumn *col;
GtkCellRenderer *renderer;
GtkTreeIter iter;
GtkWidget *view;
liststore = gtk_list_store_new(NUM_COLS, G_TYPE_FLOAT, G_TYPE_STRING);
gtk_list_store_append(liststore, &amp;iter);
gtk_list_store_set (liststore, &amp;iter, COL_PERCENTAGE, 0.5, -1); /* start at 50% */
view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(liststore));
g_object_unref(liststore); /* destroy store automatically with view */
renderer = gtk_cell_renderer_text_new();
col = gtk_tree_view_column_new();
gtk_tree_view_column_pack_start (col, renderer, TRUE);
gtk_tree_view_column_add_attribute (col, renderer, "text", COL_TEXT);
gtk_tree_view_column_set_title (col, "Progress");
gtk_tree_view_append_column(GTK_TREE_VIEW(view),col);
renderer = custom_cell_renderer_progress_new();
col = gtk_tree_view_column_new();
gtk_tree_view_column_pack_start (col, renderer, TRUE);
gtk_tree_view_column_add_attribute (col, renderer, "percentage", COL_PERCENTAGE);
gtk_tree_view_column_set_title (col, "Progress");
gtk_tree_view_append_column(GTK_TREE_VIEW(view),col);
g_timeout_add(50, (GSourceFunc) increase_progress_timeout, NULL);
return view;
}
int
main (int argc, char **argv)
{
GtkWidget *window, *view;
gtk_init(&amp;argc,&amp;argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_default_size (GTK_WINDOW(window), 150, 100);
g_signal_connect(window, "delete_event", gtk_main_quit, NULL);
view = create_view_and_model();
gtk_container_add(GTK_CONTAINER(window), view);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
</programlisting>
</sect2>
</sect1>
<sect1 id="sec-custom-cell-renderers-external">
<title>Cell Renderers Others Have Written</title>
<para>Just in case you are one of those people who do not like to
re-invent the wheel, here is a list of custom cell renderers other
people have written:</para>
<itemizedlist>
<listitem>
<para><ulink
url="http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/gaim/gaim/src/gtkcellrendererprogress.c">Progress
bar cell renderer</ulink> (gaim)</para>
</listitem>
<listitem>
<para><ulink
url="http://cvs.gnome.org/bonsai/cvsblame.cgi?file=mrproject%2Fsrc%2Fcell-renderers/mg-cell-renderer-date.c&amp;rev=&amp;root=/cvs/gnome">Date
cell renderer</ulink> (mrproject) (is this one easy to
re-use?)</para>
</listitem>
<listitem>
<para><ulink
url="http://cvs.gnome.org/bonsai/cvsblame.cgi?file=mrproject%2Fsrc%2Fcell-renderers/mg-cell-renderer-list.c&amp;rev=&amp;root=/cvs/gnome">List/combo
cell renderer</ulink> (mrproject) (is this one easy to
re-use?)</para>
</listitem>
<listitem>
<para><ulink
url="http://cvs.gnome.org/bonsai/cvsblame.cgi?file=mrproject%2Fsrc%2Fcell-renderers/mg-cell-renderer-popup.c&amp;rev=&amp;root=/cvs/gnome">Pop-up
cell renderer</ulink> (mrproject) (what does this do?)</para>
</listitem>
<listitem>
<para>Your custom cell renderer here?!</para>
</listitem>
</itemizedlist>
</sect1>
</chapter>
<chapter id="sec-other-info">
<title>Other Resources</title>
<para>A short tutorial like this cannot possibly cover everything.
Luckily, there is a lot more information out there. Here is a list of
links that you might find useful (if you have any links that should appear
here as well, please send them to <literal>tim at centricular dot
net</literal>).</para>
<itemizedlist>
<listitem>
<para><ulink
url="http://developer.gnome.org/doc/API/2.0/gtk/index.html">Gtk+ API
Reference Manual</ulink></para>
</listitem>
<listitem>
<para><ulink
url="http://developer.gnome.org/doc/API/2.0/gdk/index.html">Gdk API
Reference Manual</ulink></para>
</listitem>
<listitem>
<para><ulink
url="http://developer.gnome.org/doc/API/2.0/pango/index.html">Pango
API Reference Manual</ulink></para>
</listitem>
<listitem>
<para><ulink
url="http://developer.gnome.org/doc/API/2.0/glib/index.html">GLib API
Reference Manual</ulink></para>
</listitem>
<listitem>
<para><ulink
url="http://mail.gnome.org/archives/gtk-app-devel-list/index.html">gtk-app-devel
mailing list archives</ulink> - search them!</para>
</listitem>
<listitem>
<para><ulink
url="http://cvs.gnome.org/bonsai/rview.cgi?cvsroot=/cvs/gnome&amp;dir=gtk%2b/demos/gtk-demo">gtk-demo</ulink>
- part of the Gtk+ source code (look in gtk+-2.x.y/demos/gtk-demo),
especially <ulink
url="http://cvs.gnome.org/bonsai/cvsblame.cgi?file=gtk%2B%2Fdemos%2Fgtk-demo/list_store.c&amp;rev=&amp;root=/cvs/gnome">list_store.c</ulink>,
<ulink
url="http://cvs.gnome.org/bonsai/cvsblame.cgi?file=gtk%2B%2Fdemos%2Fgtk-demo/tree_store.c&amp;rev=&amp;root=/cvs/gnome">tree_store.c</ulink>,
and <ulink
url="http://cvs.gnome.org/bonsai/cvsblame.cgi?file=gtk%2B%2Fdemos%2Fgtk-demo/stock_browser.c&amp;rev=&amp;root=/cvs/gnome">stock_browser.c</ulink></para>
</listitem>
<listitem>
<para><ulink
url="http://www.gtkmm.org/gtkmm2/docs/tutorial/html/ch08.html">TreeView
tutorial using Gtk's C++ interface (gtkmm)</ulink></para>
</listitem>
<listitem>
<para><ulink
url="http://liw.iki.fi/liw/texts/gtktreeview-tutorial.html">TreeView
tutorial using Gtk's python interface</ulink></para>
</listitem>
<listitem>
<para>Some slides from Owen Taylor's GUADEC 2003 <ulink
url="http://people.redhat.com/otaylor/tutorial/guadec2003/">tutorial</ulink>
(<ulink
url="http://people.redhat.com/otaylor/tutorial/guadec2003/gtk-tut.ps">postscript</ulink>,
<ulink
url="http://people.redhat.com/otaylor/tutorial/guadec2003/gtk-tut.pdf">pdf</ulink>,
see pages 13-15)</para>
</listitem>
<listitem>
<para><ulink url="http://www.gnome.org/softwaremap/">Existing
applications</ulink> - yes, they exist, and <emphasis>you</emphasis>
can look at their source code. <ulink
url="http://sourceforge.net/">SourceForge's</ulink> WebCVS browse
feature is quite useful, and the same goes for <ulink
url="http://cvs.gnome.org/bonsai/rview.cgi?cvsroot=/cvs/gnome">GNOME</ulink>
as well.</para>
</listitem>
<listitem>
<para>If your intention is to display external data (from a database,
or in XML form) as a list or tree or table, you might also be
interested <ulink url="http://www.gnome-db.org/">GnomeDB</ulink>,
especially libgda and libgnomedb (e.g. the GnomeDBGrid widget). See
also <ulink url="ftp://kalamazoolinux.org/pub/pdf/dbaccess.pdf">this
PDF presentation</ulink> (page 24ff).</para>
</listitem>
<listitem>
<para>your link here!</para>
</listitem>
</itemizedlist>
</chapter>
<chapter id="sec-treeviewtut-license">
<title>Copyright, License, Credits, and Revision History</title>
<sect1 id="sec-treeviewtut-copyright-license">
<title>Copyright and License</title>
<para>Copyright (c) 2003-2004 Tim-Philipp Müller <email>tim at
centricular dot net</email></para>
<para>This tutorial may be redistributed and modified freely in any
form, as long as all authors are given due credit for their work and all
non-trivial changes by third parties are clearly marked as such either
within the document (e.g. in a revision history), or at an external and
publicly accessible place that is refered to in the document (e.g. a CVS
repository).</para>
</sect1>
<sect1 id="sec-treeviewtut-credits">
<title>Credits</title>
<para>Thanks to Axel C. for proof-reading the first drafts, for many
suggestions, and for introducing me to the tree view widget in the first
place (back then when I was still convinced that porting to Gtk+-2.x was
unnecessary, Gtk+-1.2 applications looked nice, and Aristotle had
already said everything about politics that needs to be said).</para>
<para>Harring Figueiredo <ulink
url="http://mail.gnome.org/archives/gtk-app-devel-list/2003-September/msg00240.html">shed</ulink>
some light on how GtkListStore and GtkTreeStore deal with
pixbufs.</para>
<para>Ken Rastatter <ulink
url="http://mail.gnome.org/archives/gtk-app-devel-list/2003-September/msg00250.html">suggested</ulink>
some additional topics (with complete references even).</para>
<para>Both Andrej Prsa and Alan B. Canon sent me a couple of
suggestions, and 'taf2', Massimo Mangoni and others spotted some
typos.</para>
<para>Many thanks to all of them, and of course also to kris and
everyone else in #gtk+.</para>
</sect1>
<sect1 id="sec-treeviewtut-history">
<title>Revision History</title>
<itemizedlist>
<title>5th June 2005</title>
<listitem>
<para>Remove unnecessary col = gtk_tree_view_column_new() im hello
world code (leftover from migration to convenience
functions).</para>
</listitem>
</itemizedlist>
<itemizedlist>
<title>3rd February 2005</title>
<listitem>
<para>Point out that GObjects such as GdkPixbufs retrieved with
gtk_tree_model_get() need to be g_object_unref()'ed after use, as
gtk_tree_model_get() adds a reference.</para>
</listitem>
<listitem>
<para>Added explicit (gint) event-&gt;x double to int conversion to
code snippet using gtk_tree_view_get_path_at_pos() to avoid compiler
warnings.</para>
</listitem>
</itemizedlist>
<itemizedlist>
<title>9th September 2004</title>
<listitem>
<para>Fixed another mistake in tree path explanation: text did not
correspond picture (s/movie clips/movie trailers/); (thanks to
Benjamin Brandt for spotting it).</para>
</listitem>
</itemizedlist>
<itemizedlist>
<title>6th August 2004</title>
<listitem>
<para>Fixed mistake in tree path explanation (s/4th/5th/) (thanks to
both Andrew Kirillov and Benjamin Brandt for spotting it).</para>
</listitem>
</itemizedlist>
<itemizedlist>
<title>30th April 2004</title>
<listitem>
<para>Added Hello World</para>
</listitem>
</itemizedlist>
<itemizedlist>
<title>31st March 2004</title>
<listitem>
<para>Fixed fatal typo in custom list code: g_assert() in
custom_list_init() should be ==, not != (spotted by mmc).</para>
</listitem>
<listitem>
<para>Added link to Owen Taylor's mail on the GtkTreeView
Drag'n'Drop API.</para>
</listitem>
</itemizedlist>
<itemizedlist>
<title>24th January 2004</title>
<listitem>
<para>Fixed typo in code example (remove n-th row example) (Thanks
to roel for spotting it).</para>
</listitem>
<listitem>
<para>Changed 'Context menus' section title</para>
</listitem>
</itemizedlist>
<itemizedlist>
<title>19th January 2004</title>
<listitem>
<para>Expanded section on GtkTreeRowReferences, and on removing
multiple rows.</para>
</listitem>
</itemizedlist>
<itemizedlist>
<title>8th January 2004</title>
<listitem>
<para>Added tiny section on Glade and treeviews</para>
</listitem>
<listitem>
<para>Added more detail to the section describing GtkTreePath,
GtkTreeIter et.al.</para>
</listitem>
<listitem>
<para>Reformatted document structure: instead of one single chapter
with lots of sections, have multiple chapters (this tutorial is way
to big to become part of the Gtk+ tutorial anyway); enumerate
chapters and sections.</para>
</listitem>
<listitem>
<para>Expanded the section on tree view columns and cell renderers,
with help of two diagrams by Owen Taylor (from the GUADEC 2003 Gtk+
tutorial slides).</para>
</listitem>
</itemizedlist>
<itemizedlist>
<title>10th December 2003</title>
<listitem>
<para>Added more information about how to remove a single row, or
more specifically, the n-th row of a list store</para>
</listitem>
<listitem>
<para>Added a short example about how to pack icons into the tree
view.</para>
</listitem>
</itemizedlist>
<itemizedlist>
<title>28th October 2003</title>
<listitem>
<para>Editable cells will work fine even if selection is set to
GTK_SELECTION_NONE. Removed sentences that say otherwise.</para>
</listitem>
</itemizedlist>
<itemizedlist>
<title>23rd October 2003</title>
<listitem>
<para>fix 'jumpy' selections in custom model GtkTreeSortable
interface implementation. gtk_tree_model_rows_reordered() does not
seem to work like the API reference implies (see <ulink
url="http://bugs.gnome.org/show_bug.cgi?id=124790">bug
#124790</ulink>)</para>
</listitem>
<listitem>
<para>added section about how to get the cell renderer a button
click happened on</para>
</listitem>
<listitem>
<para>added section about editable cells with spin buttons (and a
CellRendererSpin implementation to the examples)</para>
</listitem>
</itemizedlist>
<itemizedlist>
<title>10th October 2003</title>
<listitem>
<para>make custom model GtkTreeSortable implementation emit
"sort-column-changed" signal when sortid is changed</para>
</listitem>
<listitem>
<para>fixed code typo in selection function section; added a
paragraph about rule hint to 'make whole row coloured or bold'
section</para>
</listitem>
</itemizedlist>
<itemizedlist>
<title>7th October 2003</title>
<listitem>
<para>Reformatted source code to make it fit on pages when
generating ps/pdf output</para>
</listitem>
<listitem>
<para>Added link to PDF and docbook XML versions.</para>
</listitem>
</itemizedlist>
</sect1>
</chapter>
</book>