Página siguiente Página anterior Índice general

13. El widget árbol

El propósito del widget GtkTree es mostrar datos organizados de forma jerárquica. El widget GtkTree en sí es un contenedor vertical para los widgets del tipo GtkTreeItem. GtkTree en sí mismo no es muy diferente de GtkList - ambos están derivados directamente de GtkContainer, y los métodos GtkContainer funcionan igual en los widgets GtkTree que en los GtkList. La diferencia es que los widgets GtkTree pueden anidarse dentro de otros widgets GtkTree. Vamos a verlo de forma resumida.

El widget GtkTree tiene su propia ventana, y tiene por defecto un fondo de color blanco, como GtkList. La mayoría de los métodos de GtkTree funcionan igual que sus correspondientes de GtkList. Sin embargo, GtkTree no está derivado de GtkList, por lo que no puede intercambiarlos.

13.1 Creando un árbol

Puede crear un GtkTree de la forma usual, utilizando:

GtkWidget* gtk_tree_new( void );

Como el widget GtkList, un GtkTree crecerá cuando le añadan elementos o cuando crezca alguno de sus subárboles. Por esta razón, suelen venir dentro de una GtkScrolledWindow. Puede que quiera utilizar gtk_widget_set_usize() con la ventana para asegurarse de que es lo suficientemente grande como para poder ver todos los elementos del árbol, ya que el valor por defecto de GtkScrolledWindow es bastante pequeño.

Ahora que ya sabemos como crear un árbol, probablemente quiera añadirle algunos elementos. El <em/widget/ elemento de árbol más adelante explica todos los detalles de GtkTreeItem. Por ahora, es suficiente con saber como crear uno, utilizando:

GtkWidget* gtk_tree_item_new_with_label( gchar *label );

Puede añadirlo al árbol utilizando una de las siguientes funciones (ver Funciones y macros más adelante para leer más opciones):

void gtk_tree_append( GtkTree    *tree,
                       GtkWidget *tree_item );

void gtk_tree_prepend( GtkTree   *tree,
                       GtkWidget *tree_item );

Observe que debe añadir elementos a un GtkTree de uno en uno - no hay un equivalente a gtk_list_*_items().

13.2 Añadiendo un Subárbol

Un subárbol se crea como cualquier otro widget GtkTree. Un subárbol se añade a otro árbol bajo un elemento del mismo, utilizando:

void gtk_tree_item_set_subtree( GtkTreeItem *tree_item,
                                GtkWidget   *subtree );

No necesita llamar a gtk_widget_show() en un subárbol ni antes ni después de añadirlo a GtkTreeItem. Sin embargo, deberá haber añadido el GtkTreeItem en cuestión a un árbol padre antes de llamar a gtk_tree_item_set_subtree(). Esto se debe a que, técnicamente, el padre del subárbol no es el GtkTreeItem ``propietario'', sino el GtkTree que contiene al GtkTreeItem.

Cuando le añade un subárbol a un GtkTreeItem, aparece el signo de un más o de un menos a su lado, donde puede pinchar el usuario para ``expandirlo'' u ``contraerlo'', o sea, para mostrar u ocultar su subárbol. Los GtkTreeItems están contraídos por defecto. Observe que cuando contrae un GtkTreeItem, cualquier elemento seleccionado en el subárbol permanece seleccionado, que puede no coincidir con lo que el usuario espera.

13.3 Manejando la lista de selección

Como con GtkList, GtkTree tiene un campo selection, y es posible controlar el comportamiento del árbol (de alguna manera) estableciendo el tipo de selección, utilizando:

void gtk_tree_set_selection_mode( GtkTree          *tree,
                                  GtkSelectionMode  mode );

La semántica asociada con los distintos modos de selección está descrita en la sección del widget GtkList. Como ocurría con el widget GtkList, se enviarán las señales select_child, unselect_child (realmente no - ver Señales más adelante para una explicación), y selection_changed cuando los elementos de la lista sean seleccionados o deseleccionados. Sin embargo, para aprovechar estas señales, necesita conocer por medio de que widget GtkTree serán emitidas, y donde encontrar una lista con los elementos seleccionados.

Todo esto es una potencial fuente de confusión. La mejor manera de entenderlo es imaginarse que aunque todos los widgets GtkTree son creados iguales, algunos son más iguales que otros. Todos los widgets GtkTree tienen su propia ventana X, y por tanto pueden recibir eventos como pulsaciones de ratón (¡si sus hijos o GtkTreeItems no las capturan primero!). Sin embargo, para hacer que GTK_SELECTION_SINGLE y GTK_SELECTION_BROWSE funcionen bien, la lista de elementos seleccionados debe ser específica al widget GtkTree superior de la jerarquia, conocido como el ``árbol raíz''.

Por tanto no es una buena idea acceder al campo selection directamente en un widget GtkTree arbitrario, a menos que sepa que es el árbol raíz. En vez de eso, utilice la macro GTK_TREE_SELECTION (Tree), que da la lista selección del árbol raíz como un puntero GList. Por supuesto, esta lista puede incluir elementos que no estén en el subárbol en cuestión si el tipo de selección es GTK_SELECTION_MULTIPLE.

Para terminar, las señales select_child (y tt/unselect_child/, en teoría) son emitidas por todos los árboles, pero la señal selection_changed es emitida sólo por el árbol raíz. En consecuencia, si quiere manipular la señal select_child de un árbol y todos sus subárboles, tendrá que llamar a gtk_signal_connect() una vez por cada subárbol.

13.4 Estructura interna del widget árbol

La definición de la estructura GtkTree es ls siguiente:

struct _GtkTree
{
  GtkContainer container;

  GList *children;
  
  GtkTree* root_tree; /* propietario de la lista de selección */
  GtkWidget* tree_owner;
  GList *selection;
  guint level;
  guint indent_value;
  guint current_indent;
  guint selection_mode : 2;
  guint view_mode : 1;
  guint view_line : 1;
};

Ya se han mencionado los peligros asociados con el acceso directo al campo selection. Se puede acceder a los otros campos importantes de la estructura mediante macros manipuladoras o funciones de clase. GTK_TREE_IS_ROOT_TREE (Tree) devuelve un valor booleano que indica si un árbol es árbol raíz de una jerarquia GtkTree, mientras que GTK_TREE_ROOT_TREE (Tree) devuelve el árbol raíz, un objeto de tipo GtkTree (recuerde transformarlo utilizando GTK_WIDGET (Tree) si quiere utilizar con él alguna de la funciones gtk_widget_*()).

En lugar de acceder directamente al campo hijo de un widget GtkTree, probablemente sea mejor transformarlo utilizando GTK_CONTAINER (Tree), y pasárselo a la función gtk_container_children(). Con esto crearemos un duplicado de la lista original, por lo que deberá eliminarlo de la memoria utilizando g_list_free() después haber hecho con él lo que tenga que hacer, o bien crear un bucle que lo vaya destruyendo de elemento en elemento, como por ejemplo así:

children = gtk_container_children (GTK_CONTAINER (tree));
while (children) {
  do_something_nice (GTK_TREE_ITEM (children->data));
  children = g_list_remove_link (children, children);
}

El campo tree_owner sólo está definido en subárboles, donde apunta al widget GtkTreeItem que contiene al árbol en cuestión. El campo level indica el nivel de profundidad de un árbol en particular; los árboles raíz tienen un nivel 0, y cada nivel sucesivo de subárboles tiene un nivel superior al del padre. Sólo se puede asegurar que este campo contiene un valor correcto después de que el widget GtkTree se dibuje en la pantalla.

Señales

void selection_changed( GtkTree *tree );

Esta señal se emitirá cuando cambie el campo selection de un GtkTree. Esto ocurre cuando se selecciona o deselecciona un hijo del GtkTree.

void select_child( GtkTree   *tree,
                   GtkWidget *child );

Esta señal se emite cuando se está seleccionando un hijo del GtkTree. Esto ocurre en las llamadas a gtk_tree_select_item(), gtk_tree_select_child(), en todas las pulsaciones de botón y llamadas a gtk_tree_item_toggle() y gtk_item_toggle(). Puede que a veces se invoque indirectamente en otras ocasiones, cuando el hijo se añada o elimine del GtkTree.

void unselect_child (GtkTree   *tree,
                     GtkWidget *child);

Esta señal se emite cuando se deselecciona un hijo del GtkTree. Con GTK+ 1.0.4, esto sólo parece ocurrir en las llamadas a gtk_tree_unselect_item() o a gtk_tree_unselect_child(), y quizás en otras ocasiones, pero no cuando la pulsación de un botón deselecciona un hijo, y tampoco por la emisión de la señal ``toggle'' por gtk_item_toggle().

Funciones y macros

guint gtk_tree_get_type( void );

Devuelve el identificador de tipo de `GtkTree'.

GtkWidget* gtk_tree_new( void );

Crea un nuevo objeto GtkTree. El nuevo widget se devuelve como un puntero a un objeto GtkWidget. Se devolverá NULL si se produce algún error.

void gtk_tree_append( GtkTree   *tree,
                      GtkWidget *tree_item );

Añade un árbol a un GtkTree.

void gtk_tree_prepend( GtkTree   *tree,
                       GtkWidget *tree_item );

Preañade un árbol a un GtkTree.

void gtk_tree_insert( GtkTree   *tree,
                      GtkWidget *tree_item,
                      gint       position );

Inserta un árbol en un GtkTree en la posición de la lista especificada por position.

void gtk_tree_remove_items( GtkTree *tree,
                            GList   *items );

Elimina una lista de elementos (en forma de una GList *) de un GtkTree. Eliminar un elemento de un árbol lo dereferencia (y por tanto normalmente) lo destruye (""), a él y a su subárbol, de haberlo, y a todos los subárboles que contenga ese subárbol. Si quiere eliminar sólo un elemento, deberá utilizar gtk_container_remove().

void gtk_tree_clear_items( GtkTree *tree,
                           gint     start,
                           gint     end );

Elimina los elementos de un GtkTree desde la posición start hasta la posición end. De nuevo hay que llevarse cuidado con donde se aplica la dereferencia, ya que gtk_tree_clear_items() simplemente construye una lista y se la pasa a gtk_tree_remove_items().

void gtk_tree_select_item( GtkTree *tree,
                           gint     item );

Emite la señal select_item para el hijo que se encuentra en la posición item, y por tanto selecciona a ese hijo (a menos que lo deseleccione en un manejador de señal...)

void gtk_tree_unselect_item( GtkTree *tree,
                             gint     item );

Emite la señal unselect_item para el hijo en la posición item, y por tanto deselecciona al hijo.

void gtk_tree_select_child( GtkTree   *tree,
                            GtkWidget *tree_item );

Emite la señal select_item para el hijo tree_item, y por tanto lo selecciona.

void gtk_tree_unselect_child( GtkTree   *tree,
                              GtkWidget *tree_item );

Emite la señal unselect_item para el hijo tree_item, y por tanto lo deselecciona.

gint gtk_tree_child_position( GtkTree   *tree,
                              GtkWidget *child );

Devuelve la posición en el árbol de child, a menos que child no esté en el árbol, en cuya caso devuelve -1.

void gtk_tree_set_selection_mode( GtkTree          *tree,
                                  GtkSelectionMode  mode );

Establece el modo de selección, que puede ser uno de los siguientes GTK_SELECTION_SINGLE (por defecto), GTK_SELECTION_BROWSE, GTK_SELECTION_MULTIPLE, o GTK_SELECTION_EXTENDED. Esto sólo está definido para los árboles raíz, que es donde tiene sentido, ya que el árbol raíz es el ``propietario'' de la selección. Establecer este valor en un subárbol no tiene ningún efecto en absoluto; el valor simplemente será ignorado.

void gtk_tree_set_view_mode( GtkTree         *tree,
                             GtkTreeViewMode  mode ); 

Establece el ``modo de visión'', que puede ser o GTK_TREE_VIEW_LINE (por defecto) o GTK_TREE_VIEW_ITEM. El modo de visión se propaga de un árbol a sus subárboles, y no puede establecerse en exclusiva para un subárbol (esto no es exacto del todo - vea los comentarios en el código de ejemplo).

El termino ``modo de visión'' es algo ambiguo - básicamente, controla la forma en que se resalta a uno de los hijos del árbol cuando es seleccionado. Si es GTK_TREE_VIEW_LINE, se resaltará el widget GtkTreeItem completo, mientras que si es GTK_TREE_VIEW_ITEM, sólo se resaltará el widget hijo (es decir, lo que normalmente es la etiqueta).

void gtk_tree_set_view_lines( GtkTree *tree,
                              guint    flag );

Controla si se dibujarán las líneas de conexión entre los elementos del árbol. flag es o TRUE, en cuyo caso se dibujarán, o FALSE, en cuyo caso no se dibujarán.

GtkTree *GTK_TREE (gpointer obj);

Convierte un puntero genérico a `GtkTree *'.

GtkTreeClass *GTK_TREE_CLASS (gpointer class);

Convierte un puntero genérico a `GtkTreeClass *'.

gint GTK_IS_TREE (gpointer obj);

Determina si un puntero genérico se refiere a un objeto `GtkTree'.

gint GTK_IS_ROOT_TREE (gpointer obj)

Determina si un puntero genérico se refiere a un objeto `GtkTree' y es un árbol raíz. Aunque la función acepta cualquier puntero, los resultados de pasarle un puntero que no se refiera a un GtkTree no están definidos y probablemente no tengan ningún sentido.

GtkTree *GTK_TREE_ROOT_TREE (gpointer obj)

Devuelve el árbol raíz de un puntero a un objeto `GtkTree'. Seguimos con el mismo problema que en el caso anterior.

GList *GTK_TREE_SELECTION(gpointer obj)

Devuelve la lista de selección del árbol raíz de un objeto `GtkTree'. Seguimos con el mismo problema que antes.

13.5 El widget elemento de árbol

El widget GtkTreeItem, cómo el GtkListItem, está derivado de GtkItem, que de nuevo, está derivado de GtkBin. Sin embargo, el elemento en sí mismo es un contenedor genérico que contiene un widget hijo, que puede ser de cualquier tipo. El widget GtkTreeItem tiene ciertos campos extra, pero el único que nos interesa ahora es el campo subárbol.

La definición de la estructura GtkTreeItem es así:

struct _GtkTreeItem
{
  GtkItem item;

  GtkWidget *subtree;
  GtkWidget *pixmaps_box;
  GtkWidget *plus_pix_widget, *minus_pix_widget;

  GList *pixmaps    /* nodo pixmap para esta profundidad de color */

  guint expanded : 1;
};

El campo pixmaps_box es un GtkEventBox que caza las pulsaciones en el símbolo más/menos que controla la expansión y contracción. El campo pixmaps apunta a una estructura de datos interna. Ya que siempre puede obtener el subárbol de un GtkTreeItem de una forma (relativamente) segura mediante la macro GTK_TREE_ITEM_SUBTREE (Item), es aconsejable no tocar las tripas de un GtkTreeItem a menos que realmente sepa que es lo que está haciendo.

Ya que está derivado directamente de un GtkItem, puede tratarse como tal utilizando la macro GTK_ITEM (TreeItem). Un GtkTreeItem normalmente tiene una etiqueta, por lo que tenemos a nuestra disposición la función gtk_list_item_new_with_label(). Podemos conseguir el mismo efecto utilizando código como el siguiente, que por ahora es sólo una copia de la función gtk_tree_item_new_with_label():

tree_item = gtk_tree_item_new ();
label_widget = gtk_label_new (label);
gtk_misc_set_alignment (GTK_MISC (label_widget), 0.0, 0.5);

gtk_container_add (GTK_CONTAINER (tree_item), label_widget);
gtk_widget_show (label_widget);

Cómo no es obligatorio añadir una GtkLabel a un GtkTreeItem, puede también añadirle un GtkHBox o una GtkArrow, o hasta un GtkNotebook (aunque en esos casos su aplicación no será muy popular).

Si elimina todos los elementos de un subárbol, será destruido y se eliminará la información sobre su padre, a menos que lo referencie de antemano, además el GtkTreeItem que sea su propietario se colapsará. Por lo tanto, si quiere que se mantenga el subárbol tendrá que hacer algo así:

gtk_widget_ref (tree);
owner = GTK_TREE(tree)->tree_owner;
gtk_container_remove (GTK_CONTAINER(tree), item);
if (tree->parent == NULL){
  gtk_tree_item_expand (GTK_TREE_ITEM(owner));
  gtk_tree_item_set_subtree (GTK_TREE_ITEM(owner), tree);
}
else
  gtk_widget_unref (tree);

Finalmente, hay que mencionar que la opción de drag-n-drop (arrastar y soltar) funciona con los GtkTreeItems. Sólo tiene que asegurarse de que el GtkTreeItem que quiere convertir en un elemento de arrastre o en un lugar en el que, además de haber sido añadido a GtkTree, sino que además cada su widget padre tiene a su vez un padre, y así hasta llegar al nivel más alto o ventana de diálogo, cuando llamamos a gtk_widget_dnd_drag_set() o gtk_widget_dnd_drop_set(). En caso contrario, podrían ocurrir cosas extrañas.

Señales

GtkTreeItem hereda las señales select, deselect, y toggle de GtkItem. Además, añade dos señales propias, expand y collapse.

void select( GtkItem *tree_item );

Esta señal se emite cuando un elemento está siendo seleccionado, o bien después de que el usuario pinche en él, o bien cuando el programa llame a gtk_tree_item_select(), gtk_item_select(), o a gtk_tree_select_child().

void deselect( GtkItem *tree_item );

Esta señal se emite cuando un elemento está siendo deseleccionado, o bien después de que el usuario pinche en él, o bien cuando el programa llame a gtk_tree_item_deselect() o a gtk_item_deselect(). En el caso de GtkTreeItems, también se emitirá por gtk_tree_unselect_child(), y a veces por gtk_tree_select_child().

void toggle( GtkItem *tree_item );

Esta señal se emite cuando el programa llama a gtk_item_toggle(). El efecto que tiene cuando se emite en un GtkTreeItem es llamar a gtk_tree_select_child() (y nunca a gtk_tree_unselect_child()) en el árbol padre del elemento, si el elemento tiene un árbol padre. Si no lo tiene, entonces se cambiará el resaltado del elemento.

void expand( GtkTreeItem *tree_item );

Esta señal se emite cuando se está expandiendo el subárbol del elemento, esto es, cuando el usuario pincha en el signo más que hay al lado del elemento, o cuando el programa llama a gtk_tree_item_expand().

void collapse( GtkTreeItem *tree_item );

Esta señal se emite cuando se está contrayendo el subárbol del elemento, esto es, cuando el usuario pincha en el signo menos que hay al lado del elemento, o cuando el programa llama a gtk_tree_item_collapse().

Funciones y Macros

guint gtk_tree_item_get_type( void );

Devuelve el identificador de tipo de `GtkTreeItem'.

GtkWidget* gtk_tree_item_new( void );

Crea un nuevo objeto GtkTreeItem. El nuevo widget se devuelve como un puntero a un objeto GtkWidget. Se devolverá NULL si hay algún fallo.

GtkWidget* gtk_tree_item_new_with_label (gchar       *label);

Crea un nuevo objeto GtkTreeItem, teniendo una simple GtkLabel como único hijo. El nuevo widget se devolverá como un puntero a un objeto GtkWidget. Se devolverá NULL en caso de haber algún fallo.

void gtk_tree_item_select( GtkTreeItem *tree_item );

Esta función es básicamente un recubrimiento de una llamada a gtk_item_select (GTK_ITEM (tree_item)) que emitirá la señal select.

void gtk_tree_item_deselect( GtkTreeItem *tree_item );

Esta función es básicamente un recubrimiento de una llamada a gtk_item_deselect (GTK_ITEM (tree_item)) que emitirá la señal deselect.

void gtk_tree_item_set_subtree( GtkTreeItem *tree_item,
                                GtkWidget   *subtree );

Esta función añade subtree a tree_item, mostrándolo si tree_item está expandido, u ocultándolo si tree_item está contraído. De nuevo, recuerde que el tree_item ya debe de haber sido añadido a un árbol para que esto funcione.

void gtk_tree_item_remove_subtree( GtkTreeItem *tree_item );

Esto elimina todos los hijos de los subárboles del tree_item (esto es, dereferencia y destruye a los subárboles hijos, y a los hijos de los hijos y...), entonces elimina el subárbol en si mismo, y oculta el signo más/menos.

void gtk_tree_item_expand( GtkTreeItem *tree_item );

Esto emite la señal ``expand'' para el tree_item, que lo expande.

void gtk_tree_item_collapse( GtkTreeItem *tree_item );

Esto emite la señal ``collapse'' en el tree_item, que lo contrae.

GtkTreeItem *GTK_TREE_ITEM (gpointer obj)

Convierte un puntero genérico en un `GtkTreeItem *'.

GtkTreeItemClass *GTK_TREE_ITEM_CLASS (gpointer obj)

Convierte un puntero genérico en un `GtkTreeItemClass'.

gint GTK_IS_TREE_ITEM (gpointer obj)

Determina si un puntero genérico se refiere a un objeto `GtkTreeItem'.

GtkWidget GTK_TREE_ITEM_SUBTREE (gpointer obj)

Devuelve un subárbol del elemento (obj debería apuntar a un objeto `GtkTreeItem').

13.6 Árbol ejemplo

Este ejemplo es muy parecido al árbol ejemplo que hay en testgtk.c, pero mucho menos completo (aunque mucho mejor comentado). Pone una ventana con un árbol, y conecta todas las señales de los objetos relevantes, con lo que podrá ver cuando se emiten.

/* principio del ejemplo tree tree.c */

#include <gtk/gtk.h>

/* para todas las señales GtkItem:: y GtkTreeItem:: */
static void cb_itemsignal (GtkWidget *item, gchar *signame)
{
  gchar *name;
  GtkLabel *label;

  /* Es un GtkBin, por lo que tiene un hijo, que sabemos que es una
   * etiqueta, por lo que la cogemos */
  label = GTK_LABEL (GTK_BIN (item)->child);
  /* Conseguimos el texto de la etiqueta */
  gtk_label_get (label, &name);
  /* Conseguimos el nivel del árbol en el que se encuentra el elemento */
  g_print ("%s called for item %s->%p, level %d\n", signame, name,
           item, GTK_TREE (item->parent)->level);
}

/* nunca se llamará a esta función */
static void cb_unselect_child (GtkWidget *root_tree, GtkWidget *child,
                               GtkWidget *subtree)
{
  g_print ("unselect_child called for root tree %p, subtree %p, child %p\n",
           root_tree, subtree, child);
}

/* Se llamará a esta función cada vez que el usuario pulse en un
 * elemento, esté o no seleccionado. */
   whether it is already selected or not. */
static void cb_select_child (GtkWidget *root_tree, GtkWidget *child,
                             GtkWidget *subtree)
{
  g_print ("select_child called for root tree %p, subtree %p, child %p\n",
           root_tree, subtree, child);
}

static void cb_selection_changed (GtkWidget *tree)
{
  GList *i;
  
  g_print ("selection_change called for tree %p\n", tree);
  g_print ("selected objects are:\n");

  i = GTK_TREE_SELECTION(tree);
  while (i){
    gchar *name;
    GtkLabel *label;
    GtkWidget *item;

    /* Get a GtkWidget pointer from the list node */
    item = GTK_WIDGET (i->data);
    label = GTK_LABEL (GTK_BIN (item)->child);
    gtk_label_get (label, &name);
    g_print ("\t%s on level %d\n", name, GTK_TREE
             (item->parent)->level);
    i = i->next;
  }
}

int main (int argc, char *argv[])
{
  GtkWidget *window, *scrolled_win, *tree;
  static gchar *itemnames[] = {"Foo", "Bar", "Baz", "Quux",
                               "Maurice"};
  gint i;

  gtk_init (&argc, &argv);

  /* una ventana general */
  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_signal_connect (GTK_OBJECT(window), "delete_event",
                      GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
  gtk_container_border_width (GTK_CONTAINER(window), 5);

  /* una ventana con barras de desplazamiento */
  scrolled_win = gtk_scrolled_window_new (NULL, NULL);
  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
                                  GTK_POLICY_AUTOMATIC,
                                  GTK_POLICY_AUTOMATIC);
  gtk_widget_set_usize (scrolled_win, 150, 200);
  gtk_container_add (GTK_CONTAINER(window), scrolled_win);
  gtk_widget_show (scrolled_win);
  
  /* Crear el árbol raíz */
  tree = gtk_tree_new();
  g_print ("root tree is %p\n", tree);
  /* connect all GtkTree:: signals */
  gtk_signal_connect (GTK_OBJECT(tree), "select_child",
                      GTK_SIGNAL_FUNC(cb_select_child), tree);
  gtk_signal_connect (GTK_OBJECT(tree), "unselect_child",
                      GTK_SIGNAL_FUNC(cb_unselect_child), tree);
  gtk_signal_connect (GTK_OBJECT(tree), "selection_changed",
                      GTK_SIGNAL_FUNC(cb_selection_changed), tree);
  /* Añadirlo a la ventana con barras de desplazamiento */
  gtk_container_add (GTK_CONTAINER(scrolled_win), tree);
  /* Poner el modo de selección */
  gtk_tree_set_selection_mode (GTK_TREE(tree),
                               GTK_SELECTION_MULTIPLE);
  /* mostrar el árbol */
  gtk_widget_show (tree);

  for (i = 0; i < 5; i++){
    GtkWidget *subtree, *item;
    gint j;

    /* Crear un elemento del árbol */
    item = gtk_tree_item_new_with_label (itemnames[i]);
    /* Conectar todas las señales GtkItem:: y GtkTreeItem:: */
    gtk_signal_connect (GTK_OBJECT(item), "select",
                        GTK_SIGNAL_FUNC(cb_itemsignal), "select");
    gtk_signal_connect (GTK_OBJECT(item), "deselect",
                        GTK_SIGNAL_FUNC(cb_itemsignal), "deselect");
    gtk_signal_connect (GTK_OBJECT(item), "toggle",
                        GTK_SIGNAL_FUNC(cb_itemsignal), "toggle");
    gtk_signal_connect (GTK_OBJECT(item), "expand",
                        GTK_SIGNAL_FUNC(cb_itemsignal), "expand");
    gtk_signal_connect (GTK_OBJECT(item), "collapse",
                        GTK_SIGNAL_FUNC(cb_itemsignal), "collapse");
    /* Añadirlo al árbol padre */
    gtk_tree_append (GTK_TREE(tree), item);
    /* Mostrarlo - esto se puede hacer en cualquier momento */
    gtk_widget_show (item);
    /* Crear el subárbol de este elemento */
    subtree = gtk_tree_new();
    g_print ("-> item %s->%p, subtree %p\n", itemnames[i], item,
             subtree);

    /* Esto todavía es necesario si quiere que se llamen a están
     * señales en el subárbol hijo.  Note that selection_change will
     * be signalled for the root tree regardless. */
    gtk_signal_connect (GTK_OBJECT(subtree), "select_child",
                        GTK_SIGNAL_FUNC(cb_select_child), subtree);
    gtk_signal_connect (GTK_OBJECT(subtree), "unselect_child",
                        GTK_SIGNAL_FUNC(cb_unselect_child), subtree);
    /* Esto no tiene absolutamente ningún efecto, ya que se ignora
     * completamente en los subárboles */
    gtk_tree_set_selection_mode (GTK_TREE(subtree),
                                 GTK_SELECTION_SINGLE);
    /* Esto tampoco hace nada, pero por una razón diferente - los
     * valores view_mode y view_line de un árbol se propagan a los
     * subárboles cuando son mapeados. Por tanto, establecer los
     * valores después actualmente tendría (algún impredecible) efecto
     */
    gtk_tree_set_view_mode (GTK_TREE(subtree), GTK_TREE_VIEW_ITEM);
    /* Establecer este subárbol del elemento - ¡Recuerde que no puede
     * hacerlo hasta que se haya añadido a su árbol padre! */
    gtk_tree_item_set_subtree (GTK_TREE_ITEM(item), subtree);

    for (j = 0; j < 5; j++){
      GtkWidget *subitem;

      /* Crea un elemento subárbol, más o menos lo mismo de antes */
      subitem = gtk_tree_item_new_with_label (itemnames[j]);
      /* Conectar todas las señales GtkItem:: y GtkTreeItem:: */
      gtk_signal_connect (GTK_OBJECT(subitem), "select",
                          GTK_SIGNAL_FUNC(cb_itemsignal), "select");
      gtk_signal_connect (GTK_OBJECT(subitem), "deselect",
                          GTK_SIGNAL_FUNC(cb_itemsignal), "deselect");
      gtk_signal_connect (GTK_OBJECT(subitem), "toggle",
                          GTK_SIGNAL_FUNC(cb_itemsignal), "toggle");
      gtk_signal_connect (GTK_OBJECT(subitem), "expand",
                          GTK_SIGNAL_FUNC(cb_itemsignal), "expand");
      gtk_signal_connect (GTK_OBJECT(subitem), "collapse",
                          GTK_SIGNAL_FUNC(cb_itemsignal), "collapse");
      g_print ("-> -> item %s->%p\n", itemnames[j], subitem);
      /* Añadirlo a su árbol padre */
      gtk_tree_append (GTK_TREE(subtree), subitem);
      /* Mostrarlo */
      gtk_widget_show (subitem);
    }
  }

  /* Mostrar la ventana y entrar en el bucle final */
  gtk_widget_show (window);
  gtk_main();
  return 0;
}
/* fin del ejemplo */


Página siguiente Página anterior Índice general