3.11 Tree and List Widgets


3.11 Tree and List Widgets

GTK+ uses the notion of MVC for its largest and most powerful widgets. MVC stands for model, view, and controller; it means that the internal representation of the data (the model), the way the application shows the data (the view), and the means of manipulating the view and model (the controller) are separate objects that communicate with each other.

GTK+ does not strictly enforce these divisions. The model and view are certainly separate, but there aren't any special controller classes. You could rightly call the system for tree and list views a Swiss Army widget ” it can display anything from a single-column list to a table in a complete tree structure. The only real disadvantage is that you have to disavow a few MVC purist ideals.

You have probably already seen lists and trees in user interfaces that represent hierarchies of items. Have a look at at Figure 3.18 on page 219 to see if it looks familiar. This book uses the following terminology:

  • A node is a line in the display. Each node has a value.

  • A list is a collection of a node's children.

  • A tree is a list with each child's own children taken into account.

  • A drop arrow expands (shows) and collapses a node's children in the display.

A tree can have several columns; each column corresponds to a value in a node. All values in a column are of the same type, and each node contains as many values as there are columns .

To use trees and lists in a program, you must start with a model that reflects this structure. Any class works, as long as it implements the GtkTreeModel ( GTK_TYPE_TREE_MODEL ) interface. You probably do not want to go so far as to come up with your own model and interface. For the most part, you should find one of GTK+'s built-in model implementations more than adequate. These are GtkListStore ( GTK_TYPE_LIST_STORE ) for lists and GtkTreeStore ( GTK_TYPE_TREE_STORE ) for the fully blown trees.

You can process lists and trees with iterators of type GtkTreeIter . An iterator is essentially a reference to one node in a tree. You manipulate iterators with utility functions.

GtkTreeView ( GTK_TYPE_TREE_VIEW ) sits at the top level of the view component. Every column requires its own GtkTreeViewColumn view object, and to draw each value in the model, you need a renderer of the GtkCellRenderer ( GTK_TYPE_CELL_RENDERER ).

To illustrate how these components fit together, here is an example that creates a two-column list widget. The first column contains numerals, and the second contains the spelled-out word corresponding to those numerals.

The initial declarations set the numeral spellings in an array and define constant identifiers for the columns:

 /* -*-coding: utf-8;-*- */ /* list.c -- demonstrate GtkTreeView */ #include <gtk/gtk.h> const char *numbers[] = { "Zero", "One", "Two", "Three", "Four", "Five" }; enum {   INT_COLUMN,   STRING_COLUMN,   N_COLUMNS };   << standard event handlers >> 

You can see the declarations for the list, view, view columns, and cell renderer in the following code:

 int main(int argc, char **argv) {   GtkWindow *window;   GtkListStore *list;   GtkTreeIter iter;   GtkTreeView *view;   GtkTreeViewColumn *num_column, *word_column;   GtkCellRenderer *text_renderer;   gint i;   /* initialize GTK+, create main window */   gtk_init(&argc, &argv);   window = g_object_new(GTK_TYPE_WINDOW,                         "title", "Two Column List",                         "default-width", 300,                         NULL);   << connect standard handlers to main window >> 

The following code creates the list (model) structure; this is where you specify the number of columns and the type in each column.

 /* create a two-column list */   list = gtk_list_store_new(N_COLUMNS, G_TYPE_INT, G_TYPE_STRING); 

Loading data into the list is relatively easy. You can see the column identifiers from the beginning of the program here. Notice the iterator in the functions; use it first to get a handle to a new node in the list and then to set the values of the node.

 /* put some data into the list */   for (i = 0; i < 5; i++)   {     gtk_list_store_append(list, &iter);     gtk_list_store_set(list, &iter,                        INT_COLUMN, i,                        STRING_COLUMN, numbers[i],                        -1);   } 

Now you're ready to create the overall view for the overall node display. The model property binds the preceding model into the view. There are several other properties; for example, reorderable allows the user to drag a node up and down in the list and drop it at a different location (try it yourself). A full list of properties and their descriptions is in Section 3.11.2.

 /* create tree view for the list */   view = g_object_new(GTK_TYPE_TREE_VIEW,                       "model", list,                       "rules-hint", TRUE,                       "headers-clickable", TRUE,                       "reorderable", TRUE,                       "enable-search", TRUE,                       "search-column", STRING_COLUMN,                       NULL); 

This program's cell renderer doesn't have to be anything fancy. GTK+ comes with a few renderers; the text renderer here knows how to deal with numbers and strings.

 /* create and initialize text renderer for cells */   text_renderer = gtk_cell_renderer_text_new(); 

Now you need to attend to the views for each column. The parameters for the column view generator include the title of the column and the renderer. In addition, you must tell the column what part of a node it should display. This program sets the column view's text property to the appropriate column identifier.

 /* create column views */   num_column = gtk_tree_view_column_new_with_attributes("Numeral",                                                         text_renderer,                                                         "text", INT_COLUMN,                                                         NULL); 

These column view properties are similar to those of the overall view. Setting the reorderable property, in particular, means that you can move an entire column around by dragging the title.

 g_object_set(num_column,                "resizable", TRUE,                "clickable", TRUE,                "reorderable", TRUE,                NULL); 

The column view for words is nearly the same as the preceding:

 word_column = gtk_tree_view_column_new_with_attributes("Word",                                                          text_renderer,                                                          "text", STRING_COLUMN,                                                          NULL);   g_object_set(word_column,                "resizable", TRUE,                "clickable", TRUE,                "reorderable", TRUE,                NULL); 

Finally, there's nothing left to do but place the column views into the overall views, pack all the widgets, and show the window.

 /* insert columns into the view */   gtk_tree_view_append_column(view, num_column);   gtk_tree_view_append_column(view, word_column);   /* pack/show everything; start GTK+ main event loop */   gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view));   gtk_widget_show_all(GTK_WIDGET(window));   gtk_main();   return 0; } 

Figure 3.17 shows the final application. You should definitely try this example to get a feel for how you can move nodes and columns around.


Figure 3.17: Two-column list.

For the specific arguments for the functions in this example, see Sections 3.11.1 and 3.11.2.

In general, if you want to create a list or tree widget, you should follow these steps.

  1. Create a model object of the GtkListStore or GtkTreeStore class with gtk_list_store_new() or gtk_tree_store_new() as a generator function. The generator needs to know the number of columns and the type of each column.

  2. Create a node in the list with gtk_list_store_append() (or any other appropriate function from Section 3.11.1). The second argument is a pointer to a GtkTreeIter structure. This function points the iterator at the new node as a side effect.

Warning  

Iterators aren't objects; they're C structures. Most of the functions that take iterators as arguments have the ability to change the iterator. Therefore, if you want to use an iterator, declare one as a fully allocated structure like this:

 GtkTreeIter iterator; 

Do not create a pointer by adding a * ; use &iterator to pass the iterator to functions.

  1. Set the contents of the new node with gtk_list_store_set() (or another function). Notice from the example that the contents are paired with their column identifiers, terminated by -1 (not NULL ; this would evaluate to a column identifier).

  2. Create the overall view, an object of the GtkTreeView class.

  3. Create your cell rendering object(s). If you're just drawing text, gtk_cell_renderer_text_new() works fine.

  4. Create a column view object for each column. Make sure that you include the appropriate cell renderer object and the column identifier. You normally create column views with gtk_tree_view_column_new_with_attributes() .

  5. Add the column views to the overall view with gtk_tree_view_append_column() .

3.11.1 List and Tree API Reference

The GtkTreeModel interface defines a number of methods that work on any model that implements the interface ” they're mostly for reading data from the model. However, GtkListStore and GtkTreeStore have many more functions of their own for writing and removing nodes. Because these classes are very similar, each function in one class usually has a counterpart in the other. The arguments are a little different, though, because you need to deal with the relationship between nodes; in a list, the nodes are all children of a single root.

You can tell whether the following functions implement the GtkTreeModel interface by looking at its prefix; the interface methods begin with gtk_tree_model , and the regular class methods start with gtk_tree_store and gtk_list_store .

  • GtkTreeStore *gtk_tree_store_new(gint num_columns , ... )

    Creates a new GtkTreeStore object with num_columns columns. The remaining arguments are GValue type identifiers (see Section 2.4.2).

  • GtkListStore *gtk_list_store_new(gint num_columns , ... )

    Like the preceding function, but creates a GtkListStore object.

  • void gtk_tree_store_set_value(GtkTreeStore * tree , GtkTreeIter * iterator , gint column , GValue value )

    Stores a GValue value in tree at the node specified by iterator . The column value is an integral column identifier.

  • void gtk_list_store_set_value(GtkListStore * list , GtkTreeIter * iterator , gint column , GValue value )

    Like the preceding function, but for lists.

  • void gtk_tree_model_get_value(GtkTreeModel * model , GtkTreeIter * iterator , gint column , GValue * value )

    Retrieves the value at node iter in model (column column ), putting the result in the GValue pointed to by value . After you finish with value , free it with g_value_unset() .

  • void gtk_tree_model_get(GtkTreeModel * model , GtkTreeIter * iterator , ... )

    Like the preceding function, but retrieves all values from the current node. After iterator , place a list of GValue pointers; terminate the list with -1.

  • void gtk_tree_store_set(GtkTreeStore * tree , GtkTreeIter * iterator , ... )

    Like the preceding function, but sets values in the current node instead of reading them. Note that the arguments are not GValue s; they are pairs of column identifiers and data.

  • void gtk_list_store_set(GtkListStore * list , GtkTreeIter * iterator , ... )

    Like the preceding function, but for lists.

  • void gtk_tree_store_insert(GtkTreeStore * tree , GtkTreeIter * iterator , GtkTreeIter *parent , gint index )

    Inserts a new node into tree with the index index . If parent is not NULL , the new node becomes a child of that node. This function points iterator to the new node.

  • void gtk_list_store_insert(GtkListStore * list , GtkTreeIter * iterator , gint index )

    Like the preceding function, but for lists.

  • void gtk_tree_store_insert_before(GtkTreeStore * tree , GtkTreeIter * iterator , GtkTreeIter * parent , GtkTreeIter * sibling )

    Inserts a new node into the tree as a child of parent in front of sibling ( parent may be NULL ). If sibling is NULL , the new node goes after the children of parent . If parent and sibling are NULL , the node goes into the top-level children. After invocation, iterator points to the new node.

  • void gtk_list_store_insert_before(GtkListStore * list , GtkTreeIter * iterator , GtkTreeIter * sibling )

    Like the preceding function, but for lists.

  • void gtk_tree_store_insert_after(GtkTreeStore * tree , GtkTreeIter * iterator , GtkTreeIter * parent , GtkTreeIter * sibling )

    Similar to gtk_tree_store_insert_before() , but inserts after sibling (or before all children if sibling is NULL ).

  • void gtk_list_store_insert_after(GtkListStore * list , GtkTreeIter * iterator , GtkTreeIter * sibling )

    Like the preceding function, but for lists.

  • void gtk_tree_store_prepend(GtkTreeStore * tree , GtkTreeIter * iterator , GtkTreeIter * parent )

    Inserts a node into a tree as the first child of parent . This function works like gtk_tree_store_insert_before() , but without the sibling argument.

  • void gtk_list_store_prepend(GtkListStore * list , GtkTreeIter * iterator , GtkTreeIter * parent )

    Like the preceding function, but for lists.

  • void gtk_tree_store_append(GtkTreeStore * tree , GtkTreeIter * iterator , GtkTreeIter * parent )

    Like gtk_tree_store_prepend() , but the new node becomes the last child of parent instead of the first.

  • void gtk_list_store_append(GtkListStore * list , GtkTreeIter * iterator )

    Like the preceding function, but for lists.

  • void gtk_tree_store_remove(GtkTreeStore * tree , GtkTreeIter * iterator )

    Removes the tree node at iterator .

  • void gtk_list_store_remove(GtkListStore * list , GtkTreeIter * iterator )

    Removes the list node at iterator .

  • void gtk_tree_store_clear(GtkTreeStore * tree )

    Removes all nodes in tree .

  • void gtk_list_store_clear(GtkListStore * list )

    Removes all nodes in list .

  • gboolean gtk_tree_store_is_ancestor(GtkTreeStore * tree , GtkTreeIter * iterator , GtkTreeIter * child )

    Returns TRUE if child is in the tree rooted at iterator .

  • gint gtk_tree_store_iter_depth(GtkTreeStore * tree , GtkTreeIter * iterator )

    Returns the depth of iterator in tree (0 is the top level, 1 is the next level, and so on).

  • void gtk_tree_model_foreach(GtkTreeModel * model , GtkTreeModelForeachFunc * function , gpointer data )

    Runs function on all nodes of tree . See the following discussion.

This is the GtkTreeModelForeachFunc prototype:

 typedef gboolean (*GtkTreeModelForeachFunc) (GtkTreeModel *model,                                              GtkTreePath *path,                                              GtkTreeIter *iter,                                              gpointer data); 

The model parameter here is the tree or list from the foreach function call, as is the data parameter. The current node is iter .

That leaves path to explain; it's the address of the node inside the tree. A path is essentially a list of numbers; if you follow the numbers in the list, you can reach a node from the parent. In a list, there is only one number in the path; 0 is the first element, and so 42 would represent the 43rd element in the list (remember that it eventually appears as a row in the view). For trees, you can string the numbers together with colons. 4:3 is the third child of the fourth node in the tree; another example is 0:6:2. Most tree paths begin with 0.

You could use a path to create a tree structure in a plain text file containing some data. When your program reads the file, it can read the tree structure directly and send it straight into a tree model object.

GtkTreePath is a data structure that you normally do not access directly. Use one of these access functions instead:

  • GtkTreePath *gtk_tree_model_get_path(GtkTreeModel * model , GtkTreeIter * iter )

    Returns a newly allocated tree path containing iter 's path in model .

  • GtkTreePath *gtk_tree_path_new(void)

    Returns a fresh, empty path.

  • GtkTreePath *gtk_tree_path_new_first(void)

    Returns a new path containing the address 0.

  • GtkTreePath *gtk_tree_path_new_from_string(const gchar * str )

    Converts the colon -delimited address in str to a new path.

  • GtkTreePath *gtk_tree_path_copy(const GtkTreePath * path )

    Returns a new copy of path .

  • void gtk_tree_path_free(GtkTreePath * path )

    Deallocates path 's memory.

  • gchar *gtk_tree_path_to_string(GtkTreePath * path )

    Returns a colon-delimited string corresponding to path . You should deallocate this string with g_free() when you no longer need it.

  • void gtk_tree_path_append_index(GtkTreePath * path , gint index )

    Appends index to the end of path .

  • void gtk_tree_path_prepend_index(GtkTreePath * path , gint index )

    Inserts index at the beginning of path .

  • gint gtk_tree_path_get_depth(GtkTreePath * path )

    Returns the depth of path .

  • gint *gtk_tree_path_get_indices(GtkTreePath * path )

    Returns an array of gint . Each number corresponds to an index in path . This is a pointer to data inside path , so you should not free it when you are finished.

  • gint gtk_tree_path_compare(const GtkTreePath * path1 , const GtkTreePath * path2 )

    Compares two GtkTreePath paths. If they are equal, this returns 0; if path1 is the lesser path, the result is -1; otherwise , the result is 1.

  • void gtk_tree_path_next(GtkTreePath * path )

    Pushes path to the next node ” that is, this function increments the last index by 1.

  • gboolean gtk_tree_path_prev(GtkTreePath * path )

    Moves path to the previous node. In other words, this function decrements the last index by 1 and returns TRUE . If that isn't possible (that is, the last index is 0), this function returns FALSE and does not change path .

  • gboolean gtk_tree_path_up(GtkTreePath * path )

    Changes path to its parent and returns TRUE . This is a matter of removing the last index in path ; if there is only one index, this function returns FALSE .

  • void gtk_tree_path_down(GtkTreePath * path )

    Adds a child to path by appending a 0 index to the end of path .

  • gboolean gtk_tree_path_is_ancestor(GtkTreePath * parent , GtkTreePath * child )

    Returns TRUE if child is somewhere in the subtree of parent .

  • gboolean gtk_tree_path_is_descendant(GtkTreePath * child , GtkTreePath * parent )

    Like the preceding function, but with the arguments reversed .

Keep in mind that a path is only an address; changing a path doesn't actually do anything to a tree. To modify trees, you need to use iterators (that you possibly obtained from a path). The most important iterator functions are

 gtk_tree_model_get_iter(  model  ,  iter  ,  path  ) gtk_tree_model_get_iter_from_string(  model  ,  iter  ,  path_string  ) 

Both functions have gboolean return values, returning TRUE when they successfully point iter to the node in model corresponding to the path. The only difference is the path specification: path is a GtkTreePath structure, whereas path_string is a colon-delimited path string.

Note  

Naturally, gtk_tree_model_get_iter_from_string() is a convenience function, but it can still save you three lines of code: a tree path structure declaration, its initialization, and its return to the free memory pool.

Here are some more GTkTreeIter functions:

  • gboolean gtk_tree_model_get_iter_first(GtkTreeModel * model , GtkTreeIter * iter )

    Sets iter to the first node in model (that would be the one with a path of 0). If the model is empty, this function returns FALSE .

  • gboolean gtk_tree_model_iter_next(GtkTreeModel * model , GtkTreeIter * iter )

    Points iter to its next sibling in model . If this is the last sibling, this function returns FALSE .

  • gboolean gtk_tree_model_iter_children(GtkTreeModel * model , GtkTreeIter * iter , GtkTreeIter * parent )

    Points iter to the first child of parent . If there are no children, this function returns FALSE .

  • gboolean gtk_tree_model_nth_child(GtkTreeModel * model , GtkTreeIter * iter , GtkTreeIter * parent , gint n )

    Points iter to the n th child of parent . If parent is NULL , this function returns the n th child at the top level. If there are no children, this function returns FALSE .

  • gboolean gtk_tree_model_iter_has_child(GtkTreeModel * model , GtkTreeIter * iter )

    Returns TRUE if the node at iter has children.

  • gint gtk_tree_model_iter_n_children(GtkTreeModel * model , GtkTreeIter * iter )

    Returns the number of children of iter . If you specify NULL for the iterator, this function returns the number of nodes at the top level.

  • gboolean gtk_tree_model_iter_parent(GtkTreeModel * model , GtkTreeIter * iter )

    Points iter to its parent. If this is impossible , this function returns FALSE .

Adding, removing, or otherwise modifying nodes in a model invalidates all of the model's iterators (for example, upon a signal emission). If you need a little more stability, there are pointer objects that use reference counts. Their class is GtkTreeRowReference , a type of weak reference that you can use regardless of signal emissions. One situation in which you might use these references is when you need to put marks on nodes that should be valid for as long as the nodes exist.

  • GtkTreeRowReference *gtk_tree_row_reference_new(GtkTreeModel * model , GtkTreePath * path )

    Returns a new reference to the row (node) in model given by path . If there is no such node, this function returns NULL .

  • GtkTreePath *gtk_tree_row_reference_get_path(GtkTreeRowReference * ref )

    Returns a new path corresponding to ref 's node in its tree, or NULL if the reference isn't valid.

  • gboolean gtk_tree_row_reference_valid(GtkTreeRowReference * ref )

    Returns TRUE if ref is valid (that is, non- NULL and to an existing node).

  • void gtk_tree_row_reference_free(GtkTreeRowReference * ref )

    Removes the row reference ref .

3.11.2 Tree Views

The GtkTreeView class implements the V part of MVC. You can have as many views for a single model as you like; an actual view display process is detached from the model and works by querying the model as a database.

The column and cell renderers do the actual work of querying and formatting the data. Therefore, the GtkTreeView functions and properties you're about to see pertain primarily to the view's outer appearance and user interface. (There are a few of exceptions, such as the reorderable property.)

  • void gtk_tree_view_scroll_to_cell(GtkTreeView * view , GtkTreePath * path , GtkTreeViewColumn * column , gboolean align , gfloat row_align , gfloat col_align )

    Scroll to the cell in view given by path and/or column . One of these latter two parameters may be NULL if you need only horizontal or vertical scrolling. If you want finer control of where the cell appears in the view, set align to TRUE and row_align and col_align each to a number between 0.0 and 1.0 (as in the alignment settings for other widgets, 0.0 is the left or top, 0.5 is center, and 1.0 is right or bottom).

  • void gtk_tree_view_set_cursor(GtkTreeView * view , GtkTreePath * path , GtkTreeViewColumn * column , gboolean start_editing )

    Moves the keyboard focus to the cell in view given by path (and column if non- NULL ) and selects that cell or row. If you set start_editing to TRUE , GTK+ opens the cell for the user to modify.

  • void gtk_tree_view_get_cursor(GtkTreeView * view , GtkTreePath ** path , GtkTreeViewColumn ** column )

    Retrieves the current view cursor position and sets the pointers behind the path and column addresses to the appropriate row and column. If there is no selected column, this function sets * column to NULL . If there is no current position, both pointers go to NULL .

  • void gtk_tree_view_row_activated(GtkTreeView * view , GtkTreePath * path , GtkTreeViewColumn * column )

    Activates the row or cell in view specified by path and column (a double-click on a row or cell constitutes an activation, for example).

  • void gtk_tree_view_expand_all(GtkTreeView * view )

    Completely expands view ; in other words, this function opens all drop arrows in the display.

  • void gtk_tree_view_collapse_all(GtkTreeView * view )

    Collapses all rows in view so that only the top level is visible.

  • gboolean gtk_tree_view_expand_row(GtkTreeView * view , GtkTreePath * path , gboolean recursive)

    Expands the row in view corresponding to path . If recursive is TRUE , expand the entire subtree at path . This function returns TRUE if it successfully expands the row.

  • gboolean gtk_tree_view_collapse_row(GtkTreeView * view , GtkTreePath * path )

    Collapses the row at path in view and returns TRUE if successful.

  • gboolean gtk_tree_view_row_expanded(GtkTreeView * view , GtkTreePath * path )

    Returns TRUE if path in view is currently in an expanded state.

GtkTreeView has these properties:

  • model ( GtkTreeModel ): The current model inside the view.

  • hadjustment ( GtkAdjustment ): The adjustment object for the horizontal scroll direction (see Section 3.9).

  • vadjustment ( GtkAdjustment ): The adjustment object for the vertical scroll direction.

  • headers-visible ( gboolean ): If TRUE , column titles appear at the top of the columns.

  • headers-clickable ( gboolean ): If TRUE , the user can click column titles (these events may reorder columns or change the sorting order).

  • expander-column ( GtkTreeViewColumn ): The column where the drop arrow appears. The default is the first visible column.

  • reorderable ( gboolean ): If TRUE , the user can reorder rows by dragging them to different places in the view.

    Note  

    This works only with models that implement the GtkDragSourceIface and GtkDragDestIface interfaces, as GtkListStore and GtkTreeStore do. If you activate reorderable , you can arbitrarily manipulate rows. Otherwise, you will have to provide your own drag-and-drop mechanism (a relatively complicated task).

  • rules-hint ( gboolean ): If TRUE , the view takes on a table format and can benefit from predefined visual enhancements.

    Note  

    This property doesn't directly influence the view's appearance; the current GTK+ theme decides what to do. Therefore, don't set this to TRUE if you desire a particular characteristic such as striped lists; the theme is not required to carry out any specific details.

  • enable-search ( gboolean ): If TRUE , the user can perform interactive searches (by pressing CONTROL-F) on one of the columns (see below).

  • search-column ( gint ): The column on which the user can perform an interactive search.

GtkTreeView has an important signal, row-activated , that goes out when the user activates a cell with a double-click or keyboard action. It has this handler prototype, with path and column set to the corresponding row and cell:

 void handler(GtkTreeView *view,              GtkTreePath *path,              GtkTreeViewColumn *column,              gpointer *data); 

Columns

GtkTreeViewColumn objects shuttle data between the model and cell renderer. You may have more than one renderer per column; for example, you can pack a person's name into a cell with a true/false check box next to the name indicating whether the person is left-handed .

You can create most view columns with

 GtkTreeViewColumn *col; col = gtk_tree_view_column_new_with_attributes(  title  ,  renderer  , ...) 

Here, title is a title string for the column header, and renderer draws the cells. A NULL -terminated list of cell renderer attributes follows (see the example in Section 3.11).

A cell renderer attribute usually consists of a rendering type as the key and a column identifier (or number) to render. The example in Section 3.11 maps the attribute " text " to one of the two column identifiers. Cell renderer attributes are not properties, so you can't set them with g_object_set() . Use these functions instead:

  • void gtk_tree_view_column_set_attributes(GtkTreeViewColumn * column , GtkCellRenderer * renderer , ... )

    Completely replaces the attributes of renderer in column with a NULL -terminated list of attribute pairs.

  • void gtk_tree_view_column_add_attribute(GtkTreeViewColumn * column , GtkCellRenderer * renderer , gchar * attribute , gint col_id )

    Adds a new attribute to renderer in column . The attribute string is the name of the attribute, and col_id is the column identifier for the attribute.

  • void gtk_tree_view_column_clear_attributes(GtkTreeViewColumn * column , GtkCellRenderer * renderer )

    Removes all attributes for renderer in column .

  • void gtk_tree_view_column_clear(GtkTreeViewColumn * column )

    Removes all attributes for all renderers in column .

If you want to put additional renderers in column , pack them with one of these two functions:

 gtk_tree_view_column_pack_start(  column  ,  renderer  ,  expand  ) gtk_tree_view_column_pack_end(  column  ,  renderer  ,  expand  ) 

Here, expand is a gboolean value; TRUE means that the renderer should attempt to occupy any free space in a cell, much like its analog property for container widgets in Section 3.4.

The example showed

 gtk_tree_view_append_column(  view  ,  column  ) 

for appending column at the end of the view column list. This function returns the number of columns in the view, but programmers frequently ignore the return value.

Here are more functions for manipulating columns in a view:

  • gint gtk_tree_view_insert_column(GtkTreeView * view , GtkTreeViewColumn * column , gint index )

    Inserts column into view in place index . Index 0 is the start of the column list; using -1 for index appends the element to the end of the view. This function returns the new number of columns in view .

  • gint gtk_tree_view_insert_column_with_attributes(GtkTreeView * view , GtkTreeViewColumn * column , gint index , GtkCellRenderer * renderer , ... )

    Like the preceding function, but adds renderer , followed by a NULL -terminated list of cell renderer attributes.

  • GtkTreeViewColumn *gtk_tree_view_get_column(GtkTreeView * view , gint index )

    Returns the column in view corresponding to index . The first column index in a view is 0. This function returns NULL upon failure.

  • GList *gtk_tree_view_get_columns(GtkTreeView * view )

    Returns a freshly allocated GList of the columns in view . You should deallocate this list with g_list_free() when you are finished.

  • void gtk_tree_view_move_column_after(GtkTreeView * view , GtkTreeViewColumn * col1 , GtkTreeViewColumn * col2 )

    Moves col1 after col2 in view . If col2 is NULL , col1 goes to the end of the view.

  • gint gtk_tree_view_remove_column(GtkTreeView * view , GtkTreeViewColumn * column )

    Removes column from view and returns the new number of columns in the view.

There's a handy function for setting all column widths in a view to their optimal width:

 gtk_tree_view_columns_autosize(  view  ) 

This function works only after you pack and realize view .

Data normally flows from the tree model to the renderer with the help of column associations that bind renderer attributes to column identifiers, but you can define your own column assignment functions:

 gtk_tree_view_insert_column_with_data_func(  view  ,  position  ,  title  ,  renderer  ,  func  ,  func_data  ,  destroy_notify  ) 

As with gtk_tree_view_insert_column_with_attributes() , view , position , title , and renderer are the view, column position, title, and cell renderer for the new column, but func is your function for carrying data to the cell renderer; func_data is an untyped pointer that GTK+ passes to func , and destroy_notify is responsible for deallocating data .

The type definition of func is

 typedef void (*GtkTreeCellDataFunc) (GtkTreeViewColumn *tree_column,                                      GtkCellRenderer *cell,                                      GtkTreeModel *tree_model,                                      GtkTreeIter *iter,                                      gpointer data); 

To render a cell, GTK+ invokes this cell data function with the view column tree_column , cell renderer cell , and data model tree_model . The most important argument is perhaps iter , because it points to the target row to render. As usual, data is an optional data pointer.

This function should call the cell renderer cell with the appropriate (and perhaps slightly altered ) data from tree_model . The program in Section 3.11.4 contains an example, room_convert() , for changing -1 into a dash.

If you wish to set a renderer data function after creating a column, call

 gtk_tree_view_column_set_cell_data_func(tree_column,  cell_renderer  ,  func  ,  func_data  ,  destroy_notify  ) 
Note  

To remove a renderer data function, call the preceding function with NULL for func .

GtkTreeViewColumn objects have these properties:

  • visible ( gboolean ): If FALSE , the column does not appear in the view.

  • resizable ( gboolean ): If TRUE , the user may resize the column by dragging the column title's border.

  • widget ( gint , read-only): The current width of the column in pixels.

  • sizing ( GtkTreeViewColumnSizing ): The column's width resize mode with respect to the model. Possible values are as follows:

    • GTK_TREE_VIEW_COLUMN_GROW_ONLY : A change in the model can cause the column to expand, but not to contract.

    • GTK_TREE_VIEW_COLUMN_FIXED : The column remains the same width according to the fixed-width property (described later in this section).

    • GTK_TREE_VIEW_COLUMN_AUTOSIZE : The column may expand or contract to the optimal width as data enters or leaves the model. This can be inefficient with large models.

  • fixed-width ( gint ): If you set sizing to the fixed setting, this property is the width of the column in pixels.

  • min-width ( gint ): The minimum width of the column in pixels.

  • max-width ( gint ): The maximum width of the column in pixels.

  • title ( gchararray ): The column's title.

  • widget ( GtkWidget ): A widget to put in the column heading instead of a text title.

    Note  

    It doesn't make much sense to put arbitrary widgets in the title. Small icons work, but not many other widgets do.

  • alignment ( gfloat ): The horizontal alignment of the column title. As with other widgets, 0.0 left-justifies, 0.5 centers, and 1.0 right-justifies.

  • sort -indicator ( gboolean ): If TRUE , the column contains a sort indicator (normally an arrow in the title).

  • sort-order (enumeration): If the column view sorts its data, this property indicates the sort order; possible values are GTK_SORT_ASCENDING and GTK_SORT_DESCENDING .

  • clickable ( gboolean ): If TRUE , the user may click the column header.

  • reorderable ( gboolean ): If TRUE , the user may move the entire column to a different position in the view by dragging the header.

GtkTreeViewColumn has one signal, clicked , emitted when the user clicks the column header. The handler prototype is

 void handler(GtkTreeViewColumn *column, gpointer data); 

Cell Renderers

The final link between the model and view is the cell renderer (abstract class GtkCellRenderer ). The renderer takes a cell's data as input and draws the cell in the view.

GTK+ has three prepackaged cell renderers, all subclasses of GtkCellRenderer :

  • GtkCellRendererText ( GTK_CELL_RENDERER_TEXT ): Draws various data types as text.

  • GtkCellRendererPixbuf ( GTK_CELL_RENDERER_PIXBUF ): Draws images.

  • GtkCellRendererToggle ( GTK_CELL_RENDERER_TOGGLE ): Draws Boolean values as check boxes.

All renderers have these GtkCellRenderer properties:

  • mode ( GtkCellRendererMode ): The activation mode for cells. Possible values are as follows:

    • GTK_CELL_RENDERER_MODE_ACTIVATABLE : User may activate (click) cells.

    • GTK_CELL_RENDERER_MODE_EDITABLE : User may edit cells.

    • GTK_CELL_RENDERER_MODE_INTER : User may not activate or edit cells.

  • visible ( gboolean ): If FALSE , the renderer does not draw cells.

  • xalign ( gfloat ): Horizontal alignment for cell content. As usual, 0.0 leftjustifies, 0.5 centers, and 1.0 right-justifies.

  • yalign ( gfloat ): Vertical alignment for cells. As usual, 0.0 places the content at the top, 0.5 centers, and 1.0 places the content at the bottom of the cell.

  • xpad ( guint ): The number of pixels to pad at the left and right of a cell.

  • ypad ( guint ): The number of pixels to pad at the top and bottom of a cell.

  • width ( gint ): Fixed width of cells, in pixels.

  • height ( gint ): Fixed height of cells, in pixels.

GtkCellRendererText has several properties, including the following:

  • text ( gchararray ): The cell text.

  • use-markup ( gchararray , write-only): Pango formats text written into this property.

In addition, the following properties have the same types and purpose as in GtkTextTag: background , foreground , editable , font , family , style , variant , weight , stretch , size , size-points , scale , strikethrough , underline , and rise . For each of these these, there is a gboolean property-set property; set it to FALSE if you don't want property to have any effect on the text.

GtkCellRendererText has one signal, edited , emitted when a cell's content is changed. Its handler prototype is

 void handler(GtkCellRendererText *renderer,              gchar *path,              gchar *new_text,              gpointer data); 

Here, path represents the altered cell's path; new_text is the cell's new content.

GtkCellRendererPixbuf adds one property to its parent class:

  • pixbuf ( GdkPixBuf ): The image to render in the cell.

GtkCellRendererToggle has these properties:

  • activatable ( gboolean ): If TRUE , the user can click the cell to activate the toggle.

  • active ( gboolean ): Represents the current state of the toggle. TRUE means that the cell is currently on.

  • radio ( gboolean ): If TRUE , the cell is a radio button (grouped with the other cells drawn by the renderer object) rather than a toggle.

GtkCellRendererToggle has a toggled signal that goes out when the user clicks a cell. Its handler prototype is like the text renderer handler described earlier, but with no text field:

 void handler(GtkCellRendererToggle *renderer, gchar *path, gpointer data); 

You are responsible for writing any relevant data from a toggle cell back into your model; you would normally do this with a toggled handler. Keep in mind that you may have to change several rows in the model to enable the radio button feature.

3.11.3 Selecting Rows

After your application displays a list or tree in a view, the user can interact with the data. All GtkTreeView objects come with a GtkTreeSelection object ( GTK_TYPE_TREE_SELECTION ) that helps you manage rows that a user selects.

Note  

A selection object belongs to a specific view. If you have several views of the same model, each view has its own selection object and therefore may show a different selection. In addition, you cannot use selections to permanently mark elements of all views.

Because a selection is coupled to a view, you cannot create or destroy selection objects independent of their views. To get a handle on a view's selection object, use

 GtkTreeSelection *  selection  ; selection = gtk_tree_view_get_selection(  view  ); 

You may also get a view from its selection with

 view = gtk_tree_selection_get_view(  selection  ); 

After you obtain a GtkTreeSelection object, you should decide how the selection behaves with

 gtk_tree_selection_set_mode(  selection  ,  mode  ) 

where mode is one of the following:

  • GTK_SELECTION_NONE : The user may not select anything in the view.

  • GTK_SELECTION_SINGLE : The user may select at most one row in the view. This is the default.

  • GTK_SELECTION_BROWSE : Exactly one row in the display shows up as the selection at any time. This is useful for lists of fonts, files, and the like, where there should always be only one selection.

  • GTK_SELECTION_MULTIPLE : The user may select multiple rows.

In the GTK_SELECTION_SINGLE and GTK_SELECTION_BROWSE modes, you can get at the currently selected node with

 gboolean gtk_tree_selection_get_selected(GtkTreeSelection *  selection  ,                                          GtkTreeModel **  model  ,                                               GtkTreeIter *  iter  ) 

If there is a current selection, this function returns TRUE , points iter at the current selection, and, if model is not NULL , sets that pointer to the selection's list or tree model. However, if there is no selection, this function returns NULL and does nothing else.

You can't use the preceding function if the selection mode is GTK_SELECTION_MULTIPLE . There are two ways to handle this; the easier is

 gtk_tree_selection_selected_foreach(  selection  ,  func  ,  data  ) 

to call func on each row in selection , along with the data pointer. When you write func , you must follow this type definition:

 typedef void (* GtkTreeSelectionForeachFunc) (GtkTreeModel *model,                                               GtkTreePath *path,                                               GtkTreeIter *iter,                                               gpointer data); 

Here, iter points to a selection's node in model ; path is the node's address.

If you prefer to get multiple row selections as a GList (if you need to pass the selections to some other function, for example), use

 gtk_tree_selection_get_selected_rows(  selection  ,  model_ptr_addr  ) 

The return value is a GList of GtkTreePath structures. If you provide the address of a model pointer as model_ptr_addr , this function also sets the pointer to the current model. You are responsible for deallocating the return value's memory; you can do this with

 g_list_foreach(  selected_rows  , gtk_tree_path_free, NULL); g_list_free(  selected_rows  ); 

These additional functions query and manipulate selections:

  • gboolean gtk_tree_selection_path_is_selected(GtkTreeSelection * selection , GtkTreePath * path )

    Returns TRUE if path is part of selection .

  • gboolean gtk_tree_selection_iter_is_selected(GtkTreeSelection * selection , GtkTreeIter * iter )

    Returns TRUE if iter points to part of selection .

  • void gtk_tree_selection_select_path(GtkTreeSelection * selection , GtkTreePath * path )

    Switches selection to the node addressed by path .

  • void gtk_tree_selection_select_iter(GtkTreeSelection * selection , GtkTreeIter * iter )

    Switches selection to the node pointed to by iter .

  • void gtk_tree_selection_select_all(GtkTreeSelection * selection )

    Forces selection to select all nodes in its corresponding view.

  • void gtk_tree_selection_unselect_path(GtkTreeSelection * selection , GtkTreePath * path )

    Removes the node addressed by path from selection .

  • void gtk_tree_selection_unselect_iter(GtkTreeSelection * selection , GtkTreeIter * iter )

    Removes the node pointed to by iter from selection .

  • void gtk_tree_selection_unselect_all(GtkTreeSelection * selection )

    Deselects everything in selection .

  • void gtk_tree_selection_select_range(GtkTreeSelection * selection , GtkTreePath * begin , GtkTreePath * end )

    Switches selection to the nodes from begin to end .

The preceding functions work on paths and iterators, but you can also exert further control with a custom function. If your program calls

 gtk_tree_selection_set_select_function(  selection  ,  func  ,  data  ,  destroy  ) 

then the all selection functions run your own func on any candidate nodes to see if the nodes are valid. If func returns TRUE , the calling selection function selects or deselects the node as usual, but if the return value is FALSE , the selection function does nothing with the node. The data argument is an optional data pointer; destroy is data 's destroy function. As shown in the following, func uses the GtkTree-SelectionFunc type definition:

 typedef gboolean (* GtkTreeSelectionFunc) (GtkTreeSelection *selection,                                            GtkTreeModel *model,                                            GtkTreePath *path,                                            gboolean path_currently_selected,                                            gpointer data); 

The model and path parameters help you retrieve the model data; path_currently_selected is TRUE if the node is already a part of the current selection.

Your application can use GtkTreeSelectionFunc to determine whether a user can select rows, but you can also use it to select rows based on a search pattern.

Note  

Selection functions are powerful, but you should be careful where you apply them. Users with a list or tree in front of them generally expect that they can select anything in the view. If your application uses a GtkTreeSelectionFunc function to make it impossible to select certain rows, the reason should be obvious. For instance, if you outfit a view with subheadings, users should understand why they cannot click one of these subheadings .

To understand the potential confusion, consider an opinion survey application with an extensive list of survey takers. If one particular survey were for potential voters, it would be logical to exclude anyone under 18 years old from the list of survey takers. However, if rows for people under 18 still appeared in the list, users would be confused if nothing happened upon a mouse click. It would be better to exclude the rows entirely.

3.11.4 An Extended Example

At this point, you have seen quite a bit of detail on tree views and models, but no real code. This section contains a larger example program that displays the personnel organization of Rixico, Inc. (Fine Flippers and Slop for Household and Industry ” Since 1951.)

The first part of the program defines a small data set for the employee data. The structure includes a path component that conforms to GTK+ string path specification. In a real-life data set, this probably wouldn't be the case, so you would have to determine the path in some other way.

 /* -*-coding: utf-8;-*- */ /* tree.c -- Rixico Inc. personnel */ #include <gtk/gtk.h> /* employee data structure */ typedef struct _employee {   gchar *path;   gchar *last_name;   gchar *first_name;   gboolean on_site;   gchar *job_title;   gint room_no; /* -1 if employee is off-site */ } employee; /* employee data */ employee staff[] = {   { "0", "Ricshaw", "George", FALSE, "Majority Shareholder", -1 },   { "1", "Kolakowski", "Edna", FALSE, "Shareholder", -1 },   { "2", "Gainer", "Merideth", TRUE, "General Manager", 100 },   { "2:0", "Zimmerman", "Walter", TRUE, "Administrative Assistant", 101 },   { "2:1", "Fenner", "Harold", TRUE, "Accounting Director", 110 },   { "2:1:0", "Kunkle", "Heather", TRUE, "Accountant", 111 },   { "2:1:1", "Gasteiner", "Tom", TRUE, "Accountant", 111 },   { "2:2", "Ardmore", "Henrietta", TRUE, "Production Director", 200 },   { "2:2:0", "Lampoudi", "Fred", TRUE, "Flipper Lead", 210 },   { "2:2:0:0", "Gray", "Anthony", TRUE, "Flipper Machinist", 211 },   { "2:2:0:1", "de Vries", "Linda", TRUE, "Flipper Machinist", 211 },   { "2:2:0:2", "Hawkins", "Drew", TRUE, "Flipper Machinist", 211 },   { "2:2:0:3", "Wray", "Steven", TRUE, "Flipper Trainer", 212 },   { "2:2:0:4", "Stein", "Martha", FALSE, "Flipper Trainee", 212 },   { "2:2:1", "Sawyer", "Leonard", TRUE, "Slop Lead", 230 },   { "2:2:1:0", "Nestroy", "Joan", TRUE, "Slop Assembler", 231 },   { "2:2:1:1", "Parker", "Robin", FALSE, "Slop Assembler (Temp)", 232 },   { "2:2:2", "Evering", "Tracy", TRUE, "Quality Control", 299 },   { "2:2:3", "Zoidberg", "John", FALSE, "Company Doctor", -1 },   { "2:3", "Stoner", "Martin", TRUE, "IT Director", 120 },   { "2:3:0", "English", "Doug", FALSE, "Web Designer/Slacker", -1 },   { "3", "Ledbetter", "Arthur", FALSE,      "Research Partner, University of Cross Roads", -1},   { NULL, NULL, NULL, FALSE, NULL, -1} }; 

The view columns use the following column identifiers. The path (in the preceding code) is not in the column; the organization will appear under the title column.

 /* column identifiers in tree view */ enum {   LASTNAME_COL,   FIRSTNAME_COL,   ONSITE_COL,   TITLE_COL,   ROOMNO_COL,   NUM_COLS }; 

The following two functions help insert a node into the tree. Node insertion creates any parent and sibling nodes that precede the target path.

Warning  

In this example, undefined parent and previous siblings show up as empty rows in the view; you must take care to define these nodes. In an industrial-strength application, you should avoid this kind of problem.

 /* verify that path is a valid node in tree */ gboolean node_exists(GtkTreeStore *tree, GtkTreePath *path) {   GtkTreeIter iter;   return(gtk_tree_model_get_iter(GTK_TREE_MODEL(tree), &iter, path)); } /* inserts a new node at path in tree */ void node_insert(GtkTreeStore *tree, GtkTreePath *path) {   gint depth;   gint *indices;   gint index;   GtkTreeIter iter;   /* determine depth and last index of path */   depth = gtk_tree_path_get_depth(path);   indices = gtk_tree_path_get_indices(path);   index = indices[depth-1];   if (!node_exists(tree, path))   {     if (depth == 1)     {  /* if this is a child of the root node, use NULL instead of iter */        while (!(gtk_tree_model_iter_n_children(GTK_TREE_MODEL(tree), NULL)                == (index+1)))        {          gtk_tree_store_append(tree, &iter, NULL);        }     } else {        GtkTreePath *parent_path;        GtkTreeIter parent;        /* determine parent node, creating parent if it does not exist */        parent_path = gtk_tree_path_copy(path);        gtk_tree_path_up(parent_path);        if (!node_exists(tree, parent_path))        {          node_insert(tree, parent_path);        }        /* append new nodes up to index-th child of parent */        gtk_tree_model_get_iter(GTK_TREE_MODEL(tree), &parent, parent_path);        while(!(gtk_tree_model_iter_n_children(                 GTK_TREE_MODEL(tree), &parent) == (index+1)))       {         gtk_tree_store_append(tree, &iter, &parent);       }       gtk_tree_path_free(parent_path);     }   } } 

Some of the employees do not have offices because they are off-site; the room number -1 denotes this situation. The table would look awkward if it had room numbers of -1, so this function converts a room number into a form appropriate for a text renderer.

 /* read the room number column, and if the number is -1, send a dash (-)    to the renderer instead */ void room_convert(GtkTreeViewColumn *column,                   GtkCellRenderer *renderer,                   GtkTreeModel *model,                   GtkTreeIter *iter,                   gpointer data) {   gint num;   gchar *str;   gtk_tree_model_get(model, iter, ROOMNO_COL, &num, -1);   if (num == -1)   {     str = g_strdup(" - ");   } else {     str = g_strdup_printf("%d", num);   }   g_object_set(renderer, "text", str, NULL);   g_free(str); } 

The main program's variable declarations are as you would expect for a view with several columns and renderers:

 << standard event handlers >> int main(int argc, char **argv) {   GtkWindow *window;   GtkTreeStore *company;   employee *person;   GtkTreePath *path;   GtkTreeView *view;   GtkScrolledWindow *scroller;   GtkCellRenderer *room_no_renderer, *text_renderer,                   *bold_renderer, *check_renderer;   GtkTreeViewColumn *room_no_col, *title_col, *last_name_col,                     *first_name_col, *on_site_col;   /* initialize GTK+, create main window */   gtk_init(&argc, &argv);   window = g_object_new(GTK_TYPE_WINDOW,                         "title", "Rixico Inc.",                         "default-width", 600,                         "default-height", 400,                         NULL);   << attach standard handlers for main window >> 

The process for creating and populating the tree model is very similar to that in the previous example in Section 3.11, except that this has a tree structure. The node_insert() function creates the node. You can also see how this program converts the string path to an iterator using the functions in Section 3.11.1.

 /* create tree model */   company = gtk_tree_store_new(NUM_COLS,                                G_TYPE_STRING,                                G_TYPE_STRING,                                G_TYPE_BOOLEAN,                                G_TYPE_STRING,                                G_TYPE_INT);   /* point at start of personnel data */   person = staff;   /* fill model with personnel data */   while (person->path != NULL)   {     GtkTreeIter iter;     path = gtk_tree_path_new_from_string(person->path);     node_insert(company, path);     gtk_tree_model_get_iter(GTK_TREE_MODEL(company), &iter, path);     gtk_tree_path_free(path);     gtk_tree_store_set(company, &iter,                        LASTNAME_COL, person->last_name,                        FIRSTNAME_COL, person->first_name,                        ONSITE_COL, person->on_site,                        TITLE_COL, person->job_title,                        ROOMNO_COL, person->room_no,                        -1);     person++;   } 

Here are a few renderer variations.

 /* create a right-justified renderer for room numbers */   room_no_renderer = gtk_cell_renderer_text_new();   g_object_set(room_no_renderer, "xalign", 1.0, NULL);   /* a normal text renderer */   text_renderer = gtk_cell_renderer_text_new();   /* a renderer for text in boldface (for last name column) */   bold_renderer = gtk_cell_renderer_text_new();   g_object_set(bold_renderer, "weight", 500, NULL);   /* a check box renderer */   check_renderer = gtk_cell_renderer_toggle_new(); 

Now it's time to create the view columns and put the renderers into the columns. The room number renderer gets a the special room_convert() data function from earlier:

 /* create view columns */   room_no_col = gtk_tree_view_column_new_with_attributes(                    "Room", room_no_renderer,                    NULL);   gtk_tree_view_column_set_cell_data_func(room_no_col, room_no_renderer,                                           room_convert,                                           NULL, NULL); 

The title is the only column that the user can move. The rest of the columns are relatively mundane:

 title_col = gtk_tree_view_column_new_with_attributes("Title", text_renderer,                                                        "text", TITLE_COL,                                                        NULL);   /* allow user to move this column around */   g_object_set(title_col, "reorderable", TRUE, NULL);   last_name_col = gtk_tree_view_column_new_with_attributes(                      "Last name", bold_renderer,                      "text", LASTNAME_COL,                      NULL);   first_name_col = gtk_tree_view_column_new_with_attributes(                       "First name", text_renderer,                       "text", FIRSTNAME_COL,                       NULL);   on_site_col = gtk_tree_view_column_new_with_attributes(                    "On site?", check_renderer,                    "active", ONSITE_COL,                    NULL); 

Creating the top-level tree view is nearly identical to creating a list view, as is packing the columns into the view.

 /* create overall view */   view = g_object_new(GTK_TYPE_TREE_VIEW,                       "model", company,                       "rules-hint", TRUE,                       "enable-search", TRUE,                       "search-column", LASTNAME_COL,                       NULL);   /* put all columns into the view */   gtk_tree_view_append_column(view, room_no_col);   gtk_tree_view_append_column(view, last_name_col);   gtk_tree_view_append_column(view, first_name_col);   gtk_tree_view_append_column(view, on_site_col);   gtk_tree_view_append_column(view, title_col); 

This little bit puts the drop arrow in the title column instead of in the first column (the room number). It makes much more sense to view and interact with the hierarchy based on job title.

 /* set drop arrow at front of the title column */   g_object_set(view, "expander-column", title_col, NULL); 

Because the model can get relatively large, it should go into a scrolled container before going into the main window:

 /* put everything into a scrolled window */   scroller = g_object_new(GTK_TYPE_SCROLLED_WINDOW, NULL);   gtk_container_add(GTK_CONTAINER(scroller), GTK_WIDGET(view));   gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(scroller));   /* show everything and start GTK+ main loop */   gtk_widget_show_all(GTK_WIDGET(window));   gtk_main();   return 0; } 

Figure 3.18 shows the completed application.

click to expand
Figure 3.18: Tree demonstration.

This concludes the discussion of the tree widget system. If you didn't quite understand everything, remember that you already have working code fragments . It is not difficult to adapt these small programs (or other code that you may find on the Net) to your own needs, learning new pieces of the system as you go.




The Official GNOME 2 Developers Guide
The Official GNOME 2 Developers Guide
ISBN: 1593270305
EAN: 2147483647
Year: 2004
Pages: 108

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net