Thư viện tri thức trực tuyến
Kho tài liệu với 50,000+ tài liệu học thuật
© 2023 Siêu thị PDF - Kho tài liệu học thuật hàng đầu Việt Nam

apress foundations_of gtk plus development 2007 phần 6 doc
Nội dung xem thử
Mô tả chi tiết
CHAPTER 8 ■ THE TREE VIEW WIDGET 271
Renderers and Columns
After creating the GtkTreeView, you need to add one or more columns to the view for it to be of
any use. Each GtkTreeViewColumn is composed of a header, which displays a short description
of its content, and at least one cell renderer. Tree view columns do not actually render any
content. Tree view columns hold one or more cell renderers that are used to draw the data on
the screen.
All cell renderers are derived from the GtkCellRenderer class and are referred to as
objects in this chapter, because GtkCellRenderer is derived directly from GtkObject, not from
GtkWidget. Each cell renderer contains a number of properties that determine how the data
will be drawn within a cell.
The GtkCellRenderer class provides common properties to all derivative renderers
including background color, size parameters, alignments, visibility, sensitivity, and padding.
A full list of GtkCellRenderer properties can be found in Appendix A. It also provides the
editing-canceled and editing-started signals, which allow you to implement editing in
custom cell renderers.
In Listing 8-1, you were introduced to GtkCellRendererText, which is capable of rendering
strings, numbers, and gboolean values as text. Textual cell renderers are initialized with
gtk_cell_renderer_text_new().
GtkCellRendererText provides a number of additional properties that dictate how each
cell will be rendered. You should always set the text property, which is the string that will be
displayed in the cell. The rest of the properties are similar to those used with text tags.
GtkCellRendererText contains a large number of properties that dictate how every row will
be rendered. g_object_set() was used in the following example to set the foreground color of
every piece of text in the renderer to orange. Some properties have a corresponding set property as well, which must be set to TRUE if you want the value to be used. For example, you should
set foreground-set to TRUE for the changes will take effect.
g_object_set (G_OBJECT (renderer), "foreground", "Orange",
"foreground-set", TRUE, NULL);
After you create a cell renderer, it needs to be added to a GtkTreeViewColumn. Tree view columns can be created with gtk_tree_view_column_new_with_attributes() if you only want the
column to display one cell renderer. In the following code, a tree view column is created with
the title “Buy” and a renderer with one attribute. This attribute will be referred to as BUY_IT
when the GtkListStore is populated.
column = gtk_tree_view_column_new_with_attributes ("Buy", renderer,
"text", BUY_IT, NULL);
The preceding function accepts a string to display in the column header, a cell renderer,
and a NULL-terminated list of attributes. Each attribute contains a string that refers to the renderer property and the tree view column number. The important thing to realize is that the
column number provided to gtk_tree_view_column_new_with_attributes() refers to the tree
model column, which may not be the same as the number of tree model columns or cell
renderers used by the tree view.
7931.book Page 271 Thursday, February 22, 2007 9:09 PM
272 CHAPTER 8 ■ THE TREE VIEW WIDGET
The following four lines of code implement the same functionality that is provided
by gtk_tree_view_column_new_with_attributes(). An empty column is created with
gtk_tree_view_column_new(), and the column title is set to “Buy”.
column = gtk_tree_view_column_new ();
gtk_tree_view_column_set_title (column, "Buy");
gtk_tree_view_column_pack_start (column, renderer, FALSE);
gtk_tree_view_column_set_attributes (column, renderer, "text", BUY_IT, NULL);
Next, a cell renderer is added to the column. gtk_tree_view_column_pack_start() accepts
a third Boolean parameter, which instructs the column to expand horizontally to fill extra
space if set to TRUE. The last function, gtk_tree_view_column_set_attributes() adds the
NULL-terminated list of attributes that will be customized for every row you add to the tree view.
These attributes are applied to the specified renderer.
Calling gtk_tree_view_column_pack_start() will remove all attributes previously associated with the specified cell renderer. To circumvent this, you can use gtk_tree_view_column_
add_attribute() to add attributes to a column for a specific cell renderer one at a time. Both
of these functions are useful when a GtkTreeViewColumn will contain more than one cell
renderer.
void gtk_tree_view_column_add_attribute (GtkTreeViewColumn *column,
GtkCellRenderer *renderer,
const gchar *attribute,
gint column);
If you want to add multiple renderers to the tree view column, you will need to pack each
renderer and set its attributes separately. For example, in a file manager, you might want to
include a text and an image renderer in the same column. However, if every column only needs
one cell renderer, it is easiest to use gtk_tree_view_column_new_with_attributes().
■Note If you want a property, such as the foreground color, set to the same value for every row in the
column, you should apply that property directly to the cell renderer with g_object_set(). However, if the
property will vary depending on the row, you should add it as an attribute of the column for the given renderer.
After you have finished setting up a tree view column, it needs to be added to the tree
view with gtk_tree_view_append_column(). Columns may also be added into an arbitrary
position of the tree view with gtk_tree_view_insert_column() or removed from the view with
gtk_tree_view_remove_column().
Creating the GtkListStore
The tree view columns are now set up with the desired cell renderers, so it is time to create the
tree model that will interface between the renderers and the tree view. For the example found
in Listing 8-1, we used GtkListStore so that the items would be shown as a list of elements.
7931.book Page 272 Thursday, February 22, 2007 9:09 PM
CHAPTER 8 ■ THE TREE VIEW WIDGET 273
New list stores are created with gtk_list_store_new(). This function accepts the number
of columns and the type of the data each column will hold. In Listing 8-1, the list store has three
columns that store gboolean, integer, and string data types.
GtkListStore* gtk_list_store_new (gint n_columns,
/* List of column types */);
After creating the list store, you need to add rows with gtk_list_store_append() for it to be
of any use. This function will append a new row to the list store, and the iterator will be set to
point to the new row. You will learn more about tree iterators in a later section of this chapter.
For now, it is adequate for you to know that it points to the new tree view row.
void gtk_list_store_append (GtkListStore *store,
GtkTreeIter *iter);
There are multiple other functions for adding rows to a list store including gtk_list_
store_prepend() and gtk_list_store_insert(). A full list of available functions can be found
in the GtkListStore API documentation.
In addition to adding rows, you can also remove them with gtk_list_store_remove(). This
function will remove the row that GtkTreeIter refers to. After the row is removed, the iterator
will point to the next row in the list store, and the function will return TRUE. If the last row was
just removed, the iterator will become invalid, and the function will return FALSE.
gboolean gtk_list_store_remove (GtkListStore *store,
GtkTreeIter *iter);
In addition, gtk_list_store_clear() is provided, which can be used to remove all rows
from a list store. You will be left with a GtkListStore that contains no data. If the object will not
be used beyond this point, it should then be unreferenced.
Now that you have a row, you need to add data to it with gtk_list_store_set(). The
gtk_list_store_set() function receives a list of pairs of column numbers and value parameters. For example, the first column in the following function call, referenced with BUY_IT,
accepts a Boolean value that defines whether the product should be purchased. These values
correspond to those set by gtk_list_store_new().
gtk_list_store_set (store, &iter, BUY_IT, list[i].buy,
QUANTITY, list[i].quantity, PRODUCT, list[i].product, -1);
The last element of gtk_list_store_set() must be set to -1 so that GTK+ knows that there
are no more parameters. Otherwise, your users will be presented with an endless list of warnings and errors in the terminal output.
■Note GtkCellRendererText automatically converts Boolean values and numbers into text strings that
can be rendered on the screen. Therefore, the type of data applied to a text attribute column does not have
to be text itself, but just has to be consistent with the list store column type that was defined during initialization of the GtkListStore.
7931.book Page 273 Thursday, February 22, 2007 9:09 PM
274 CHAPTER 8 ■ THE TREE VIEW WIDGET
After the list store is created, you need to call gtk_tree_view_set_model() to add it to the
tree view. By calling this function, the reference count of the tree model will be incremented by
one. Therefore, if you want the tree model to be destroyed when the tree view is destroyed, you
will need to call g_object_unref() on the list store.
Using GtkTreeStore
There is one other type of built-in tree model called GtkTreeStore, which organizes rows into a
multilevel tree structure. It is possible to implement a list with a GtkTreeStore tree model as
well, but this is not recommended because some overhead is added when the object assumes
that the row may have one or more children.
Figure 8-5 shows an example tree store, which contains two root elements, each with children of its own. By clicking the expander to the left of a row with children, you can show or hide
its children. This is similar to the functionality provided by the GtkExpander widget.
Figure 8-5. A tree view widget using a GtkTreeStore tree model
The only difference between a GtkTreeView implemented with a GtkTreeStore instead of a
GtkListStore is in the creation of the store. Adding columns and renderers is performed in the
same manner with both models, because columns are a part of the view not the model, so
Listing 8-2 excludes the implementation of setup_tree_view().
Listing 8-2 revises the original Grocery List application, splitting the products into categories. This list includes two categories: Cleaning Supplies and Food, which both have children of
their own. The quantity of each category is set initially to zero, because this is calculated during
runtime.
7931.book Page 274 Thursday, February 22, 2007 9:09 PM
CHAPTER 8 ■ THE TREE VIEW WIDGET 275
Listing 8-2. Creating a GtkTreeStore (treestore.c)
#include <gtk/gtk.h>
enum
{
BUY_IT = 0,
QUANTITY,
PRODUCT,
COLUMNS
};
enum
{
PRODUCT_CATEGORY,
PRODUCT_CHILD
};
typedef struct
{
gint product_type;
gboolean buy;
gint quantity;
gchar *product;
} GroceryItem;
GroceryItem list[] =
{
{ PRODUCT_CATEGORY, TRUE, 0, "Cleaning Supplies" },
{ PRODUCT_CHILD, TRUE, 1, "Paper Towels" },
{ PRODUCT_CHILD, TRUE, 3, "Toilet Paper" },
{ PRODUCT_CATEGORY, TRUE, 0, "Food" },
{ PRODUCT_CHILD, TRUE, 2, "Bread" },
{ PRODUCT_CHILD, FALSE, 1, "Butter" },
{ PRODUCT_CHILD, TRUE, 1, "Milk" },
{ PRODUCT_CHILD, FALSE, 3, "Chips" },
{ PRODUCT_CHILD, TRUE, 4, "Soda" },
{ PRODUCT_CATEGORY, FALSE, 0, NULL }
};
/* The implementation of this function is the same as in Listing 8-1. */
static void setup_tree_view (GtkWidget*);
7931.book Page 275 Thursday, February 22, 2007 9:09 PM
276 CHAPTER 8 ■ THE TREE VIEW WIDGET
int main (int argc,
char *argv[])
{
GtkWidget *window, *treeview, *scrolled_win;
GtkTreeStore *store;
GtkTreeIter iter, child;
guint i = 0, j;
gtk_init (&argc, &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (window), "Grocery List");
gtk_container_set_border_width (GTK_CONTAINER (window), 10);
gtk_widget_set_size_request (window, 275, 300);
treeview = gtk_tree_view_new ();
setup_tree_view (treeview);
store = gtk_tree_store_new (COLUMNS, G_TYPE_BOOLEAN, G_TYPE_INT, G_TYPE_STRING);
while (list[i].product != NULL)
{
/* If the product type is a category, count the quantity of all of the products
* in the category that are going to be bought. */
if (list[i].product_type == PRODUCT_CATEGORY)
{
j = i + 1;
/* Calculate how many products will be bought in the category. */
while (list[j].product != NULL && list[j].product_type != PRODUCT_CATEGORY)
{
if (list[j].buy)
list[i].quantity += list[j].quantity;
j++;
}
/* Add the category as a new root element. */
gtk_tree_store_append (store, &iter, NULL);
gtk_tree_store_set (store, &iter, BUY_IT, list[i].buy,
QUANTITY, list[i].quantity, PRODUCT, list[i].product, -1);
}
7931.book Page 276 Thursday, February 22, 2007 9:09 PM