tree_widget.sgml 12.4 KB
Newer Older
1
<refentry id="TreeWidget" revision="20 Mar 2002">
Jonathan Blandford's avatar
Jonathan Blandford committed
2 3 4 5 6 7 8 9
  <refmeta>
    <refentrytitle>Tree and List Widget Overview</refentrytitle>
    <manvolnum>3</manvolnum>
    <refmiscinfo>GTK Library</refmiscinfo>
  </refmeta>

  <refnamediv>
    <refname>Tree and List Widget Overview</refname>
10 11
    <refpurpose>Overview of #GtkTreeModel, #GtkTreeView, and other 
    associated widgets</refpurpose>
Jonathan Blandford's avatar
Jonathan Blandford committed
12 13 14 15 16
  </refnamediv>

  <refsect1>
    <title>Overview</title>
    <para>
17 18
      To create a tree or list in GTK+, use the #GtkTreeModel interface in
      conjunction with the #GtkTreeView widget.  This widget is
Jonathan Blandford's avatar
Jonathan Blandford committed
19 20
      designed around a <firstterm>Model/View/Controller</firstterm>
      design and consists of four major parts:
21
      <simplelist>
Jonathan Blandford's avatar
Jonathan Blandford committed
22 23 24 25
	<member>The tree view widget (<structname>GtkTreeView</structname>)</member>
	<member>The view column (<structname>GtkTreeViewColumn</structname>)</member>
	<member>The cell renderers (<structname>GtkCellRenderer</structname> etc.)</member>
	<member>The model interface (<structname>GtkTreeModel</structname>)</member>
26
      </simplelist>
Jonathan Blandford's avatar
Jonathan Blandford committed
27 28 29 30 31 32 33
      The <emphasis>View</emphasis> is composed of the first three
	objects, while the last is the <emphasis>Model</emphasis>.  One
	of the prime benefits of the MVC design is that multiple views
	can be created of a single model.  For example, a model mapping
	the file system could be created for a file manager.  Many views
	could be created to display various parts of the file system,
	but only one copy need be kept in memory.
Jonathan Blandford's avatar
Jonathan Blandford committed
34
    </para>
35 36 37
    <para>
        The purpose of the cell renderers is to provide extensibility to the
        widget and to allow multiple ways of rendering the same type of data.
Jonathan Blandford's avatar
Jonathan Blandford committed
38
        For example, consider how to render a boolean variable.  Should it
39
        render it as a string of "True" or "False", "On" or "Off", or should
Jonathan Blandford's avatar
Jonathan Blandford committed
40
        it be rendered as a checkbox?
41
    </para>
Jonathan Blandford's avatar
Jonathan Blandford committed
42
  </refsect1>
43 44 45
  <refsect1>
    <title>Creating a model</title>
    <para>
46 47 48 49 50 51
      GTK+ provides two simple models that can be used: the #GtkListStore
      and the #GtkTreeStore.  GtkListStore is used to model list widgets, 
      while the GtkTreeStore models trees.  It is possible to develop a new 
      type of model, but the existing models should be satisfactory for all 
      but the most specialized of situations.  Creating the model is quite 
      simple:
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
    </para>
      <informalexample><programlisting><![CDATA[
GtkListStore *store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN);
]]></programlisting></informalexample>
    <para>
      This creates a list store with two columns: a string column and a boolean
      column.  Typically the 2 is never passed directly like that; usually an
      enum is created wherein the different columns are enumerated, followed by
      a token that represents the total number of columns.  The next example will
      illustrate this, only using a tree store instead of a list store. Creating
      a tree store operates almost exactly the same.
    </para>
    <informalexample><programlisting><![CDATA[
enum
{
   TITLE_COLUMN,
   AUTHOR_COLUMN,
   CHECKED_COLUMN,
   N_COLUMNS
};

GtkTreeStore *store = gtk_tree_store_new (N_COLUMNS,       /* Total number of columns */
                                          G_TYPE_STRING,   /* Book title              */
                                          G_TYPE_STRING,   /* Author                  */
                                          G_TYPE_BOOLEAN); /* Is checked out?         */
Matthias Clasen's avatar
Matthias Clasen committed
77
]]></programlisting></informalexample>
78
    <para>
79 80 81 82
      Adding data to the model is done using gtk_tree_store_set() or 
      gtk_list_store_set(), depending upon which sort of model was
      created.  To do this, a #GtkTreeIter must be acquired.  The iterator 
      points to the location where data will be added.
83 84
    </para>
    <para>
85 86 87
      Once an iterator has been acquired, gtk_tree_store_set() is used to
      apply data to the part of the model that the iterator points to.  
      Consider the following example:
88 89 90 91 92 93 94 95 96
    </para>
    <informalexample><programlisting><![CDATA[
GtkTreeIter   iter;

gtk_tree_store_append (store, &iter, NULL);  /* Acquire an iterator */

gtk_tree_store_set (store, &iter,
                    TITLE_COLUMN, "The Principle of Reason",
                    AUTHOR_COLUMN, "Martin Heidegger",
Matthias Clasen's avatar
Matthias Clasen committed
97
                    CHECKED_COLUMN, FALSE,
Jonathan Blandford's avatar
Jonathan Blandford committed
98
                    -1);
99 100 101
]]></programlisting></informalexample>

  <para>
Jonathan Blandford's avatar
Jonathan Blandford committed
102
    Notice that the last argument is -1.  This is always done because
103 104 105 106 107
    this is a variable-argument function and it needs to know when to stop
    processing arguments.  It can be used to set the data in any or all
    columns in a given row.
  </para>
  <para>
108
    The third argument to gtk_tree_store_append() is the parent iterator.  It
109 110 111 112 113 114 115 116 117 118 119 120 121
    is used to add a row to a GtkTreeStore as a child of an existing row.  This
    means that the new row will only be visible when its parent is visible and
    in its expanded state.  Consider the following example:
  </para>
  <informalexample><programlisting><![CDATA[
GtkTreeIter iter1;  /* Parent iter */
GtkTreeIter iter2;  /* Child iter  */

gtk_tree_store_append (store, &iter1, NULL);  /* Acquire a top-level iterator */
gtk_tree_store_set (store, &iter1,
                    TITLE_COLUMN, "The Art of Computer Programming",
                    AUTHOR_COLUMN, "Donald E. Knuth",
                    CHECKED_COLUMN, FALSE,
Jonathan Blandford's avatar
Jonathan Blandford committed
122
                    -1);
123 124 125 126

gtk_tree_store_append (store, &iter2, &iter1);  /* Acquire a child iterator */
gtk_tree_store_set (store, &iter2,
                    TITLE_COLUMN, "Volume 1: Fundamental Algorithms",
Jonathan Blandford's avatar
Jonathan Blandford committed
127
                    -1);
128 129 130 131

gtk_tree_store_append (store, &iter2, &iter1);
gtk_tree_store_set (store, &iter2,
                    TITLE_COLUMN, "Volume 2: Seminumerical Algorithms",
Jonathan Blandford's avatar
Jonathan Blandford committed
132
                    -1);
133 134 135 136

gtk_tree_store_append (store, &iter2, &iter1);
gtk_tree_store_set (store, &iter2,
                    TITLE_COLUMN, "Volume 3: Sorting and Searching",
Jonathan Blandford's avatar
Jonathan Blandford committed
137
                    -1);
138 139 140 141 142 143
]]></programlisting></informalexample>
  </refsect1>

  <refsect1>
    <title>Creating the view component</title>
    <para>
Jonathan Blandford's avatar
Jonathan Blandford committed
144 145
      While there are several different models to choose from, there is
      only one view widget to deal with.  It works with either the list
146 147 148
      or the tree store.  Setting up a #GtkTreeView is not a difficult
      matter.  It needs a #GtkTreeModel to know where to retrieve its data 
      from.
149 150 151 152 153 154 155 156 157 158
    </para>
    <informalexample><programlisting><![CDATA[
GtkWidget *tree;

tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
]]></programlisting></informalexample>

    <refsect2>
      <title>Columns and cell renderers</title>
      <para>
159 160
	Once the #GtkTreeView widget has a model, it will need to know how 
        to display the model.  It does this with columns and cell renderers.
Jonathan Blandford's avatar
Jonathan Blandford committed
161 162 163
      </para>
      <para>
        Cell renderers are used to draw the data in the tree model in a
Matthias Clasen's avatar
Matthias Clasen committed
164
        way.  There are a number of cell renderers that come with GTK+ 2.x,
165 166
        including the #GtkCellRendererText, #GtkCellRendererPixbuf and
        the #GtkCellRendererToggle.
Jonathan Blandford's avatar
Jonathan Blandford committed
167 168 169
        It is relatively easy to write a custom renderer.
      </para>
      <para>
170 171 172 173
        A #GtkTreeViewColumn is the object that GtkTreeView uses to organize 
        the vertical columns in the tree view.  It needs to know the name of 
        the column to label for the user, what type of cell renderer to use, 
        and which piece of data to retrieve from the model for a given row.
174 175 176 177 178
      </para>
      <informalexample><programlisting><![CDATA[
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;

179
renderer = gtk_cell_renderer_text_new (<!-- -->);
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
column = gtk_tree_view_column_new_with_attributes ("Author",
                                                   renderer,
                                                   "text", AUTHOR_COLUMN,
                                                   NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
]]></programlisting></informalexample>
      <para>
       At this point, all the steps in creating a displayable tree have been
       covered.  The model is created, data is stored in it, a tree view is
       created and columns are added to it.
      </para>
    </refsect2>

    <refsect2>
      <title>Selection handling</title>
      <para>
196 197 198
        Most applications will need to not only deal with displaying data, but 
        also receiving input events from users.  To do this, simply get a 
        reference to a selection object and connect to the "changed" signal.
199 200 201 202 203 204 205 206 207
      </para>
      <informalexample><programlisting><![CDATA[
/* Prototype for selection handler callback */
static void tree_selection_changed_cb (GtkTreeSelection *selection, gpointer data);

/* Setup the selection handler */
GtkTreeSelection *select;

select = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree));
Soren Sandmann's avatar
Soren Sandmann committed
208
gtk_tree_selection_set_mode (select, GTK_SELECTION_SINGLE);
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236
g_signal_connect (G_OBJECT (select), "changed",
                  G_CALLBACK (tree_selection_changed_cb),
                  NULL);
]]></programlisting></informalexample>
        <para>
          Then to retrieve data for the row selected:
        </para>
        <informalexample><programlisting><![CDATA[
static void
tree_selection_changed_cb (GtkTreeSelection *selection, gpointer data)
{
        GtkTreeIter iter;
        GtkTreeModel *model;
        gchar *author;

        if (gtk_tree_selection_get_selected (selection, &model, &iter))
        {
                gtk_tree_model_get (model, &iter, AUTHOR_COLUMN, &author, -1);

                g_print ("You selected a book by %s\n", author);

                g_free (author);
        }
}
]]></programlisting></informalexample>
    </refsect2>
  </refsect1>

Jonathan Blandford's avatar
Jonathan Blandford committed
237 238 239
  <refsect1>
    <title>Simple Example</title>
    <para>
240 241 242 243 244
      Here is a simple example of using a #GtkTreeView widget in context 
      of the other widgets.  It simply creates a simple model and view, 
      and puts them together.  Note that the model is never populated 
      with data &mdash; that is left as an exercise for the reader.  
      More information can be found on this in the #GtkTreeModel section.
Matthias Clasen's avatar
Matthias Clasen committed
245
      <informalexample><programlisting><![CDATA[
Jonathan Blandford's avatar
docs  
Jonathan Blandford committed
246 247
enum
{
248 249 250 251
   TITLE_COLUMN,
   AUTHOR_COLUMN,
   CHECKED_COLUMN,
   N_COLUMNS
Jonathan Blandford's avatar
docs  
Jonathan Blandford committed
252 253
};

254 255
void
setup_tree (void)
Jonathan Blandford's avatar
Jonathan Blandford committed
256
{
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272
   GtkTreeStore *store;
   GtkWidget *tree;
   GtkTreeViewColumn *column;
   GtkCellRenderer *renderer;

   /* Create a model.  We are using the store model for now, though we
    * could use any other GtkTreeModel */
   store = gtk_tree_store_new (N_COLUMNS,
                               G_TYPE_STRING,
                               G_TYPE_STRING,
                               G_TYPE_BOOLEAN);

   /* custom function to fill the model with data */
   populate_tree_model (store);

   /* Create a view */
273
   tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
274 275 276 277 278 279 280

   /* The view now holds a reference.  We can get rid of our own
    * reference */
   g_object_unref (G_OBJECT (store));

   /* Create a cell render and arbitrarily make it red for demonstration
    * purposes */
281
   renderer = gtk_cell_renderer_text_new (<!-- -->);
Jonathan Blandford's avatar
Jonathan Blandford committed
282 283 284
   g_object_set (G_OBJECT (renderer),
                 "foreground", "red",
                 NULL);
285 286 287

   /* Create a column, associating the "text" attribute of the
    * cell_renderer to the first column of the model */
Jonathan Blandford's avatar
Jonathan Blandford committed
288
   column = gtk_tree_view_column_new_with_attributes ("Author", renderer,
289 290 291 292 293 294 295
                                                      "text", AUTHOR_COLUMN,
                                                      NULL);

   /* Add the column to the view. */
   gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);

   /* Second column.. title of the book. */
296
   renderer = gtk_cell_renderer_text_new (<!-- -->);
297 298 299 300 301 302 303
   column = gtk_tree_view_column_new_with_attributes ("Title",
                                                      renderer,
                                                      "text", TITLE_COLUMN,
                                                      NULL);
   gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);

   /* Last column.. whether a book is checked out. */
304
   renderer = gtk_cell_renderer_toggle_new (<!-- -->);
305 306 307 308 309 310 311 312
   column = gtk_tree_view_column_new_with_attributes ("Checked out",
                                                      renderer,
                                                      "active", CHECKED_COLUMN,
                                                      NULL);
   gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);

   /* Now we can manipulate the view just like any other GTK widget */
   ...
Jonathan Blandford's avatar
Jonathan Blandford committed
313 314
}
]]>
Matthias Clasen's avatar
Matthias Clasen committed
315
      </programlisting></informalexample>
Jonathan Blandford's avatar
Jonathan Blandford committed
316 317 318
    </para>
  </refsect1>
</refentry>