El modelo GtkListStore

El modelo GtkListStore permite almacenar los datos como un arreglo o tabla plana.

El prototipo de la función que permite crear un ListStore es como sigue:

GtkListStore* gtk_list_store_new(n_columns,  
 ...); 
gint  n_columns;
 ...;

El primer argumento indica el número de columnas y los siguientes, indican el tipo de dato de cada columna. De esta forma es posible indicar algún tipo definido en GType (tales como G_TYPE_BOOLEAN, G_TYPE_INT, G_TYPE_FLOAT, G_TYPE_CHAR, G_TYPE_STRING, etc.) para definir algunos de los tipos de datos comunes, o algun otro, como GDK_TYPE_PIXBUF, para tipos de datos especiales como imágenes.

Ejemplo 2. Creación de un modelo GtkListStore

	GtkListStore *model;

	model = gtk_list_store_new (NUM_COLUMNS, G_TYPE_INT, G_TYPE_STRING,
				GDK_TYPE_PIXBUF, G_TYPE_BOOLEAN);

	/* Algún código para agregarlo a un widget GtkTreeView */

	g_object_unref (G_OBJECT (model))


En Ejemplo 2, “Creación de un modelo GtkListStore” se ha indicado que se desea crear un nuevo GtkListStore, el cual constará de NUM_COLUMNS columnas (de acuerdo a la definición en Ejemplo 1, “Una lista enumerada típica” corresponde a 4). Los siguientes argumentos son los tipos de datos de cada una de las columnas. Así, se pasarán tantos argumentos como sea el valor de NUM_COLUMNS. Para efectos de ejercitación, se han indicado cuatro diferentes tipos de datos, para mostrar distintas formas de manejo de éstos.

Inserción de datos

Con excepción de la función gtk_list_store_new, todas las funciones del tipo gtk_list_store_ reciben como primer argumento el objeto sobre el cual se realizará la operación indicada.

Una vez que se ha creado el modelo, es necesario problarlo con datos, en este caso es necesario realizar dos pasos:

  1. Añadir una fila vacía

  2. Asignar valores a la nueva fila

Añadir una fila vacía

Para el primer paso se debe emplear alguna de las siguientes funciones, dependiendo del lugar donde se deseen añadir los datos:

void gtk_list_store_insert(*list_store,  
 *iter,  
 position); 
GtkListStore  *list_store;
GtkTreeIter  *iter;
gint  position;
void gtk_list_store_insert_before(*list_store,  
 *iter,  
 *sibling); 
GtkListStore  *list_store;
GtkTreeIter  *iter;
GtkTreeIter  *sibling;
void gtk_list_store_insert_after(*list_store,  
 *iter,  
 *sibling); 
GtkListStore  *list_store;
GtkTreeIter  *iter;
GtkTreeIter  *sibling;
void gtk_list_store_append(*list_store,  
 *iter); 
GtkListStore  *list_store;
GtkTreeIter  *iter;
void gtk_list_store_prepend(*list_store,  
 *iter); 
GtkListStore  *list_store;
GtkTreeIter  *iter;

Ejemplo 3. Inserción de una fila en un GtkListStore

	gtk_list_store_append (model, &iter);

Asignar valores a la nueva fila

Se ha añadido una nueva variable, &iter, la cual es una estructura GtkTreeIter.

GtkTreeIter es un puntero a un nodo particular. Si el modelo se analiza desde el punto de vista de una tabla, con filas y columnas, un GtkTreeIter entrega la fila.

Al usar la función gtk_list_store_append se ha añadido una nueva fila al modelo, la cual se encuentra vacía. Con el parámetro &iter se obtiene la posición en donde ha sido añadida.

En la nueva fila (apuntada por &iter) se asignan los valores de cada columna. En este caso, sólo se ha añadido el valor a la columna COLUM_MOVE, que es de tipo GDK_TYPE_PIXBUF. Para ello, existen la funciones gtk_list_store_set_value o gtk_list_store_set. La única diferencia entre ambas, es que la segunda permite asignar los valores de varias columnas a la vez.

El primer argumento es el modelo, el segundo es la fila, el tercero la columna y el cuarto el valor. El tipo de dato del valor debe ser el mismo definido para cada columna en el modelo.

Si se necesitara agregar todos los valores de inmediato, entonces la función gtk_list_store_set se invocaría de la siguiente manera:

	gtk_list_store_set (model, &iter,
			COLUMN_STATE, 15,
			COLUMN_COMMENTS, "Trabajar con ListStore es divertido",
			COLUMN_MOVE, pixbuf,
			COLUMN_EDITABLE, TRUE,
			-1);

La idea es muy similar a gtk_list_store_value, se indica el modelo, la fila, y luego sigue con un par de datos que indican la columna y su respectivo valor. Finalmente, se termina con -1 para indicar a la función el fin de los argumentos.

Ejemplo 4. Comparación entre gtk_list_store_set_value y gtk_list_store_set

	gtk_list_store_set (model, &iter,
	              COLUMN_STATE, 15,
	              COLUMN_COMMENTS, "Trabajar con ListStore es divertido",
	              COLUMN_MOVE, pixbuf,
	              COLUMN_EDITABLE, TRUE,
	              -1);

	gtk_list_store_set_value (model, &iter,
	              COLUMN_COMMENTS, "Trabajar con ListStore es divertido");
	gtk_list_store_set_value (model, &iter,
	              COLUMN_MOVE, pixbuf);
	gtk_list_store_set_value (model, &iter,
	              COLUMN_EDITABLE, TRUE);

En Ejemplo 4, “ Comparación entre gtk_list_store_set_value y gtk_list_store_set ” se muestra la misma asignación de valores a model realizada todas de una vez con gtk_list_store_set y columna por columna a través de gtk_list_store_value. El parámetro iter indica la fila en la cual se debe realizar la inserción, y cuyo valor se obtuvo al llamar a alguna de las funciones de inserción de filas.

Inserción de imágenes

Para cargar imágenes, se requiere definir un tipo de dato Pixbuf.

	GdkPixbuf *pixbuf;

Para cargar una imagen definida en una variable, típicamente un XPM,

	pixbuf = gdk_pixbuf_new_from_xpm_data ((const char **) image_xpm);

En este caso, se debe definir a priori la variable image_xpm.

Si la imagen a cargar no se encuentra definida en una variable, sino que se desea cargar desde un archivo, entonces se emplea:

	pixbuf = gdk_pixbuf_new_from_file ("imagen.png", NULL);

	GtkTreeIter iter;
	GdkPixbuf *pixbuf;

	/* ... */

	pixbuf = gdk_pixbuf_new_from_file ("imagen.png", NULL);
	
	gtk_list_store_append (model, &iter);
	gtk_list_store_set_value (model, &iter,
			COLUMN_MOVE, pixbuf);

Modificación de datos

Para modificar un dato de una celda en particular, se requiere conocer la fila y columna en la cual se llevará a cabo la operación. La manera de hacerlo es muy similar a la inserción, empleando las funciones gtk_list_store_set_value o gtk_list_store_set de la misma forma, la excepción es que ya no se requiere agregar una nueva fila. Sin embargo, para poder realizar la modificación es necesario conocer la fila (iter) en la cual se desea modificar los datos.

Eliminación de una fila

Para eliminar una fila, se requiere conocer su posición, la cual se obtiene a través de un GtkTreeIter, y posteriormente llamando a la función gtk_list_store_remove, la cual tiene el siguiente prototipo:

	void gtk_list_store_remove
			(GtkListStore *list_store,
			GtkTreeIter *iter);

Si se quisiera eliminar la fila que se encuentra activa, se obtiene su posición y se remueve de la siguiente forma:

	gtk_tree_model_get_iter (model, &iter, path);
	gtk_list_store_remove (model, &iter);

Como se puede notar, es muy similar a los casos anteriores. A diferencia de la inserción, en la edición y eliminación es necesario indicar en forma correcta el parámetro &iter, ya que en estos casos debe ser válido, de lo contrario provocará una excepción en la última llamada. Es por ello que se utiliza gtk_tree_model_get_iter, de tal manera de obtener un puntero válido a una fila.

También se puede apreciar que para obtener la estructura GtkTreeIter, se requiere de otro parámetro, GtkTreePath path. Un GtkTreePath es una tupla de enteros que representan una posición en el árbol. Por ejemplo, 1:4:41 significa "el hijo 42 del quinto hijo del segundo hijo del nivel raíz" (no hay que olvidar que los índices comienzan en 0). Si se altera el contenido del árbol, un GtkTreePath particular puede quedar apuntando a un nodo diferente o incluso, no ser válido.

A través de un GtkTreeIter se puede obtener un puntero a GtkTreePath, y viceversa. Por ejemplo, si se conoce el parámetro iter, se puede obtener el path de la siguiente manera:

	GtkTreePath *path;

	path = gtk_tree_model_get_path (model, &iter);
	
	/* ... */

	gtk_tree_path_free (path);

Finalmente, cuando ya no se necesite el path, se debe liberar la memoria con gtk_tree_path_free

Para obtener una referencia dura a un nodo, se debe emplear la estructura GtkTreeRowReference y emplear la función gtk_tree_row_reference_new, la cual crea una referencia de un nodo a partir de un GtkTreePath. Esta referencia se actualizará automáticamente cada vez que se modifique el modelo, de esta forma se puede apuntar siempre al mismo nodo, independiente de las alteraciones que sufra el modelo. Por ejemplo:

	GtkTreeRowReference *nodeXY;
	GtkTreePath *path;

	path = gtk_tree_path_new_from_string ("1:2");
	nodoXY = gtk_tree_row_reference_new (model, path);

Sin embargo, si la referencia (path) no existe en el modelo model, nodeXY será NULL.

Mezclando texto e imágenes en una misma columna

Ejemplos para empaquetas diferentes tipos de datos en una misma columna.