GTK+ By Example/Tree View/Columns and Renderers
Mapping Data to the Screen: GtkTreeViewColumn and GtkCellRenderer
[edit | edit source]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 GtkCellRenderer objects. Cell renderers are packed into tree view columns much like widgets are packed into GtkHBoxes.
Here is a diagram (courtesy of Owen Taylor) that pictures the relationship between tree view columns and cell renderers:
Figure 5-1. Cell Renderer Properties
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.
Cell Renderers
[edit | edit source]Cell renderers are objects that are responsible for the actual rendering of data within a GtkTreeViewColumn. They are basically just GObjects (i.e. not widgets) that have certain properties, and those properties determine how a single cell is drawn.
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 attributes or cell data functions (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).
Different cell renderers exist for different purposes:
- GtkCellRendererText renders strings or numbers or boolean values as text ("Joe", "99.32", "true")
- GtkCellRendererPixbuf is used to display images; either user-defined images, or one of the stock icons that come with Gtk+.
- GtkCellRendererToggle displays a boolean value in form of a check box or as a radio button.
- GtkCellEditable is a special cell that implements editable cells (i.e. GtkEntry or GtkSpinbutton in a treeview). This is not a cell renderer! If you want to have editable text cells, use GtkCellRendererText and make sure the "editable" property is set. GtkCellEditable is only used by implementations of editable cells and widgets that can be inside of editable cells. You are unlikely to ever need it.
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.
How does a cell renderer know what to render? A cell renderer object has certain 'properties' that are documented in the API reference (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.
Here is a diagram (courtesy of Owen Taylor) that tries to show what is going on when rows are rendered:
Figure 5-2. GtkTreeViewColumns and GtkCellRenderers
The above diagram shows the process when attributes are used. In the example, a text cell renderer's "text" property has been linked to the first model column. The "text" property contains the string to be rendered. The "foreground" property, which contains the colour of the text to be shown, has been linked to the second model column. Finally, the "strikethrough" 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 G_TYPE_BOOLEAN).
With this setup, the cell renderer's properties are 'loaded' from the model before each cell is rendered.
Here is a silly and utterly useless little example that demonstrates this behaviour, and introduces some of the most commonly used properties of GtkCellRendererText:
#include <gtk/gtk.h>
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, &toplevel, NULL);
/* Append a second top level row, and fill it with some data */
gtk_tree_store_append(treestore, &toplevel, NULL);
gtk_tree_store_set(treestore, &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, &child, &toplevel);
gtk_tree_store_set(treestore, &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(&argc, &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;
}
The above code should produce something looking like this:
Figure 5-3. Persistent Cell Renderer Properties
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.
There are two different ways to connect cell renderers to data in the model: attributes and cell data functions.
Attributes
[edit | edit source]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:
...
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);
...
This means that the text cell renderer property "text" will be set to the string in model column COL_FIRST_NAME of each row to be drawn. It is important to internalise the difference between gtk_tree_view_column_add_attribute and g_object_set: g_object_set sets a property to a certain value, while gtk_tree_view_column_add_attribute sets a property to whatever is in the specified _model column_ at the time of rendering.
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 "cell-background" property of a GtkCellRendererText, even though the API documentation does not list such a property. We can do this, because GtkCellRendererText is derived from GtkCellRenderer, which does in fact have 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 object hierarchy that shows you which classes a widget or some other object is derived from.
There are two more noteworthy things about GtkCellRenderer properties: one is that sometimes there are different properties which do the same, but take different arguments, such as the "foreground" and "foreground-gdk" properties of GtkCellRendererText (which specify the text colour). The "foreground" property take a colour in string form, such as "Orange" or "CornflowerBlue", whereas "foreground-gdk" takes a GdkColor 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 "foo-set" property taking a boolean value as argument, such as "foreground-set". This is useful when you want to have a certain setting have an effect or not. If you set the "foreground" property, but set "foreground-set" to FALSE, 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 "foreground-set" property to a model column of type G_TYPE_BOOLEAN with gtk_tree_view_column_add_attribute.
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.
Another way to get your model data displayed on the screen is to set up cell data functions.
Cell Data Functions
[edit | edit source]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 set a property if you want it to be active, but also to unset a property if it should not be active (and it might have been set in the previous row).
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 gtk_tree_view_column_set_cell_data_func to set up a cell data function for a particular cell renderer. Here is an example:
enum
{
COLUMN_NAME = 0,
COLUMN_AGE_FLOAT,
NUM_COLS
};
...
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, &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);
...
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.
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 G_TYPE_STRING, 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.
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).
GtkCellRendererText and Integer, Boolean and Float Types
[edit | edit source]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 gtk_tree_view_column_add_attribute must always be of the same type as the data type that the property requires.
This is usually true, but there is an exception: if you use gtk_tree_view_column_add_attribute to connect a text cell renderer's "text" property to a model column, the model column does not need to be of G_TYPE_STRING, it can also be one of most other fundamental GLib types, e.g. G_TYPE_BOOLEAN, G_TYPE_INT, G_TYPE_UINT, G_TYPE_LONG, G_TYPE_ULONG, G_TYPE_INT64, G_TYPE_UINT64, G_TYPE_FLOAT, or G_TYPE_DOUBLE. The text cell renderer will automatically display the values of these types correctly in the tree view. For example:
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);
...
Even though the "text" 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 [1].
If you are using a floating point type, i.e. G_TYPE_FLOAT or G_TYPE_DOUBLE, 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 cell data function. Notes [1]
For those interested, the conversion actually takes place within g_object_set_property. Before a certain cell is rendered, the tree view column will call gtk_tree_model_get_value to set the cell renderer properties according to values stored in the tree model (if any are mapped via gtk_tree_view_column_add_attribute or one of the convenience functions that do the same thing), and then pass on the GValue retrieved to g_object_set_property.
GtkCellRendererText, UTF8, and pango markup
[edit | edit source]All text used in Gtk+-2.0 widgets needs to be in UTF8 encoding, and GtkCellRendererText 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 g_convert 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.
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 g_utf8_validate. 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.
In addition to the "text" property, GtkCellRendererText also has a "markup" property that takes text with pango markup 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:
"You can have text in bold or in a different color"
When using the "markup" property, you need to take into account that the "markup" and "text" properties do not seem to be mutually exclusive (I suppose this could be called a bug). In other words: whenever you set "markup" (and have used the "text" property before), set the "text" property to NULL, and vice versa. Example:
...
void
foo_cell_data_function ( ... )
{
...
if (foo->is_important)
g_object_set(renderer, "markup", "<b>important</b>", "text", NULL, NULL);
else
g_object_set(renderer, "markup", NULL, "text", "not important", NULL);
...
}
...
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:
...
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("<b>%s</b> - <i>%s</i>",
artist_string, title_string);
...
g_object_set(renderer, "markup", markuptxt, "text", NULL, NULL);
...
g_free(markuptxt);
}
...
The above example will not work if artist_string is "Simon & Garfunkel" for example, because the & 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 & 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 g_markup_escape (and you will need to free the resulting newly-allocated string again with g_free).
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 "markup" property to normal text without any pango markup, it will render as normal text just as if you had used the "text" property. However, as opposed to the "text" property, special characters in the "markup" property text would still need to be escaped, even if you do not use pango markup in the text.
A Working Example
[edit | edit source]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.
#include <gtk/gtk.h>
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, &toplevel, NULL);
gtk_tree_store_set(treestore, &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, &toplevel, NULL);
gtk_tree_store_set(treestore, &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, &child, &toplevel);
gtk_tree_store_set(treestore, &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, &year_born, -1);
if (year_born <= year_now && year_born > 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(&argc, &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;
}
How to Make a Whole Row Bold or Coloured
[edit | edit source]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.
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 here, and everything will be done automatically, in colours that conform to the chosen theme even (unless the theme disables rule hints, that is).
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 G_TYPE_STRING), and one for the boolean flag of the property (e.g. a column COL_ROW_COLOR_SET of type G_TYPE_BOOLEAN). You would then connect these columns with the "foreground" and "foreground-set" 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 TRUE, 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 "foreground" property to whatever special color you want, and only connect the COL_ROW_COLOR_SET column to all renderer's "foreground-set" 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. "weight" would take a G_TYPE_INT, in form of a PANGO_WEIGHT_FOO define in this case).
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 quote Havoc Pennington: "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."
How to Pack Icons into the Tree View
[edit | edit source]So far we have only put text in the tree view. While everything you need to know to display icons (in the form of GdkPixbufs) 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:
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", &error);
if (error)
{
g_warning ("Could not load icon: %s\n", error->message);
g_error_free(error);
error = NULL;
}
gtk_list_store_append(store, &iter);
gtk_list_store_set(store, &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;
}
Note that the tree view will not resize icons for you, but displays them in their original size. If you want to display stock icons instead of GdkPixbufs loaded from file, you should have a look at the "stock-id" property of GtkCellRendererPixbuf (and your model column should be of type G_TYPE_STRING, as all stock IDs are just strings by which to identify the stock icon).