Jump to content

GTK+ By Example/Tree View/Miscellaneous

From Wikibooks, open books for an open world

Miscellaneous

[edit | edit source]

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 <tim at centricular dot net>.

Getting the Column Number from a Tree View Column Widget

[edit | edit source]

Signal callbacks often only get passed a pointer to a GtkTreeViewColumn when the application programmer really just wants to know which column number 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: [1]. Gtk3 : replace 'col->tree_view' by 'gtk_tree_view_column_get_tree_view(col)'

  /* 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->tree_view != NULL, -1 );

    cols = gtk_tree_view_get_columns(GTK_TREE_VIEW(col->tree_view));

    num = g_list_index(cols, (gpointer) col);

    g_list_free(cols);

    return num;
  }

Alternatively, it is possible to use g_object_set_data and g_object_get_data 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:

  ...

  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));
    ...
  }

"columnnum" 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). Notes [1]

This function has been inspired by this mailing list message (thanks to Ken Rastatter for the link and the topic suggestion).

Column Expander Visibility

[edit | edit source]

Hiding the Column Expander

[edit | edit source]

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).

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 gtk_tree_view_column_set_visible. 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 gtk_tree_view_set_expander_column right after the call to _set_visible, then the expander will move back to the hidden column, and no expander is visible any longer.

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...

Forcing Column Expander Visibility

[edit | edit source]

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.

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 "row-expanded" signal, and fill the contents of the already existing row with the first new row, then append new child rows. See this mailing list thread for more details.

Getting the Cell Renderer a Click Event Happened On

[edit | edit source]

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 gtk_tree_view_get_path_at_pos to get a tree path from the x and y coordinates of the button event that is passed to you in a "button-press-event" signal callback (if you use the "row-activated" 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 gtk_tree_model_get_iter and modify the data in the cell you want to modify with gtk_list_store_set or gtk_tree_store_set.

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):

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 && col == NULL;  node = node->next)
	{
		GtkTreeViewColumn *checkcol = (GtkTreeViewColumn*) node->data;

		if (x >= colx  &&  x < (colx + checkcol->width))
			col = checkcol;
		else
			colx += checkcol->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->next)
	{
		GtkCellRenderer *checkcell = (GtkCellRenderer*) node->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, &width, NULL);

		if (x >= colx && x < (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->x, bevent->y, &renderer))
		g_print ("Renderer found\n");
	else
		g_print ("Renderer not found!\n");
}

Glade and Tree Views

[edit | edit source]

A frequently asked question is how you can add columns to a GtkTreeView in Glade.[1] 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 GtkTreeView 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 GtkTreeViewColumns 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.

An alternative approach is to derive your own special widget from GtkTreeView 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.

  1. Do not 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 this mailing list message for a short discussion about why you should avoid Glade code generation.