Menús y barras de herramientas

Menús

Existen dos formas de crear menús: Una sencilla y otra un poco más complicada, ya que utiliza el método manual. La forma sencilla utiliza la factoría de menús (GtkItemFactory), que facilita la creación de menús. El método manual crea todos los menús usando las llamadas directamente. Aunque usando GtkItemFactory facilita la creación de menús, no es posible añadir imágenes o el carácter '/'.

Creación Manual de Menús

Para crear un Menú, se necesitan los siguientes widgets:

  • Item del menú: lo que el usuario selecciona, ej. "Guardar".

  • Menú: actúa como contenedor de los item del menú, ej. "Archivo".

  • Barra de menús: un contenedor para cada menú individual.

Las siguientes funciones se usan para crear menús y barras de menús:

	    GtkWidget *gtk_menu_bar_new (void); // crea una barra de menus.
	    GtkWidget *gtk_menu_new (void);  // devuelve un puntero al nuevo menú. No es necesario
	                                     // mostrarlo (con gtk_widget_show()), ya que es simplemente
	                                     // un contenedor para los items del menú.
	  

Las siguientes funciones crean items de menú que son colocados dentro del menú y de la barra de menús.

	    GtkWidget *gtk_menu_item_new (void); // crea un item de menú sin título
	    GtkWidget *gtk_menu_item_new_with_label (const char *label); // crea un item de menú con título
	    GtkWidget *gtk_menu_item_new_with_mnemnonic (const char *label); // crea un item de menú con carácter mnemónico
	  

Una vez se han creado los items de menú, hay que colocarlos dentro del widget menú usando la función gtk_menu_append. Además, para saber cuándo el item ha sido seleccionado por el usuario es necesario conectar la señal activada.

En el siguiente ejemplo, se muestra el código necesario para crear un menú Archivo estándar, con las opciones Abrir, Guardar y Salir.

	    file_menu = gtk_menu_new ();    /* Crea un menú */

	    /* Crea items de menú */
	    open_item = gtk_menu_item_new_with_label ("Abrir");
	    save_item = gtk_menu_item_new_with_label ("Guardar");
	    quit_item = gtk_menu_item_new_with_label ("Salir");

	    /* Añade los items al menú */
	    gtk_menu_append (GTK_MENU (file_menu), open_item);
	    gtk_menu_append (GTK_MENU (file_menu), save_item);
	    gtk_menu_append (GTK_MENU (file_menu), quit_item);

	    /* Attach the callback functions to the activate signal */
	    g_signal_connect_swapped (G_OBJECT (open_item), "activate",
	                              G_CALLBACK (menuitem_response),
	                              (gpointer) "file.open");
	    g_signal_connect_swapped (G_OBJECT (save_item), "activate",
	                              G_CALLBACK (menuitem_response),
	                              (gpointer) "file.save");

	    /* We can attach the Quit menu item to our exit function */
	    g_signal_connect_swapped (G_OBJECT (quit_item), "activate",
	                              G_CALLBACK (destroy),
	                              (gpointer) "file.quit");

	    /* Es necesario mostrar los items de menú */
	    gtk_widget_show (open_item);
	    gtk_widget_show (save_item);
	    gtk_widget_show (quit_item);
	  

El siguiente paso de este ejemplo es la creación de una barra de menús y un item para la opción Archivo, al que se le añadirá el menú (file_menu).

	    /* Crea una barra de menús */
	    menu_bar = gtk_menu_bar_new ();
	    gtk_container_add (GTK_CONTAINER (window), menu_bar);
	    gtk_widget_show (menu_bar);

	    /* Crea el item "Archivo" */
	    file_item = gtk_menu_item_new_with_label ("Archivo");
	    gtk_widget_show (file_item);
	  

Lo siguiente es asociar el menú (file_menu) con la opción Archivo (file_item), para ello se necesita la función:

	    void gtk_menu_item_set_submenu (GtkMenuItem *menu_item,
	                                    GtkWidget   *submenu);
	  

En el ejemplo:

	    gtk_menu_item_set_submenu (GTK_MENU_ITEM (file_item), file_menu);
	  

Por último, se añade el menú a la barra de menús, usando la función:

	    void gtk_menu_bar_append (GtkMenuBar *menu_bar,
	                              GtkWidget  *menu_item);
	  

En el ejemplo:

	    gtk_menu_bar_append (GTK_MENU_BAR (menu_bar), file_item);
	  

Para alinear el menú a la derecha de la barra de menús, como por ejemplo el menú Ayuda, se usa la función void gtk_menu_item_right_justify.

Para que queden claros todos los conceptos, a continuación se resumen los pasos a seguir para la creación de una barra de menús con menús.

  1. Crear un menú con la función gtk_menu_new

  2. Crear los items que van a estar en el menú con la función gtk_menu_item_new

  3. Colocar los items creados en el menú usando la función gtk_menu_append

  4. Crear un item de menú con la función gtk_menu_item_new_with_label. Este será el menú raíz, es decir, el texto que aparece en la barra de menús.

  5. Asociar el menú al menú raíz usando la función gtk_menu_item_set_submenu.

  6. Crear una barra de menús con gtk_menu_bar_new que contendrá al menú raíz.

  7. Colocar el menú raíz en la barra de menús usando gtk_menu_bar_append.

Menú PopUp (colgante)

Los pasos a seguir para crear un menú popup son muy similares a lo comentado en el apartado anterior. La única diferencia es que el menú popup no se encuentra dentro de una barra de menús, sino que aparece, por ejemplo, porque se ha pulsado el botón izquierdo del ratón.

Para saber cuándo desplegar el menú popup, se necesita crear una función de manejo del evento. Esta función necesita tener el siguiente prototipo:

	    static gint handler (GtkWidget *widget, GdkEvent  *event);
	  

El argumento event especificará cuándo desplegar el menú popup.

Si el evento, en un manejador de eventos, es la pulsación de uno de los botones del ratón, se debe usar como se muestra en el código de ejemplo para pasar información a gtk_menu_popup.

Para asociar un manejador de eventos a un widget determinado, se usa la siguiente función:

	    g_signal_connect_swapped (G_OBJECT (widget), "event",
	                              G_CALLBACK (handler),
	                              G_OBJECT (menu));
	  

widget es el widget que se va a asociar al menú, mientras que handler es la función de manejo del evento, y menu es el menú popup creado con la función gtk_menu_new.

Creación de Menús usando GtkItemFactory

Ahora que hemos visto cómo crear menús de forma manual, veamos cómo hacerlo usando un método más simple: usando llamadas a gtk_item_factory.

ItemFactory permite crear un menú a partir de un array de entradas ItemFactory. Esto significa que puedes definir un esquema de tu menú y después crear los widgets menu/menubar con un mínimo de funciones.

Entradas ItemFactory

ItemFactoryEntry es una estructura que permite definir una entrada de menú. Al definir un array de entradas ItemFactoryEntry, habremos creardo un menú completo. La definición de una estructura de elemento ItemFactory tiene el siguiente aspecto:

		struct _GtkItemFactoryEntry
		{
		gchar *path;
		gchar *accelerator;

		GtkItemFactoryCallback callback;
		guint                  callback_action;

		gchar          *item_type;
		};
	

Cada campo define una parte de la entrada del menú. *path es una cadena que define tanto el nombre como la ruta de un elemento de menú, por ejemplo, "/Fichero/Abrir" sería el nombre de una entrada de menú que vendría bajo la entrada ItemFactory con el path "/Fichero". Obsérvese sin embargo que "/Fichero/Abrir" se mostraría en el menú Fichero como "Abrir". Nótese también que dado que la barra / es el caracter usado para definir la ruta de un menú, no puede ser usada como parte del nombre. Una letra precedida por un caracter de subrayado indica que dicha letra servirá como atajo de teclado (shortcut) cuando dicho menú esté desplegado.

*accelerator es una cadena que indica la combinación de teclas que puede usarse como atajo de teclado para acceder a ese elemento del menú. La cadena puede estar construída tanto por un simple caracter como por una combinación de teclas de función con un caracter simple. No se diferencian mayúsculas de minúsculas.

Las teclas de función disponibles son: <ALT> - alt <CTL> ó <CTRL> ó <CONTROL> - control <MOD1> a <MOD5> - modn <SHFT> o <SHIFT> - shift

Ejemplos: '<ConTroL>a' '<SHFT><ALT><CONTROL>X'

callback es la función a la que se llamará cuando el elemento de menú emita la señal "activate".

El valor de callback_action se pasará a la función de callback.

item_type es una cadena que describe el tipo de widget que será empaquetado en el menú. Puede ser cualquiera de los siguientes: NULL o "" o "<Item>" - crea un item simple "<Title>" - crea un item de tipo título "<CheckItem>" - crea un item de tipo Check "<ToggleItem>" - crea un item conmutador "<RadioItem>" - crea un item de tipo radial (raíz) "Path" - crea un item de tipo radial hermana "<Tearoff>" - crea un tearoff "<Separator>" - crea un separador "<Branch>" - crea un item para contener submenús (opcional) "<LastBranch>" - crea un submenú justificado a la derecha (como los submenús ayuda" "<StockItem>" - crea un item simple con una imagen de fábrica (disponible de serie en GTK) véase gtkstock.h para una lista de elementos stock

Nótese que <LastBranch> sólo es útil para un submenú de una barra de menús.

Descripción de la retrollamada (callback)

La función de retrollamada para una entrada ItemFactory puede tomar dos formas. Si callback_action es cero:

	void callback( void )
	

Si no, será la siguiente:


	void callback( gpointer    callback_data,
		       guint       callback_action,
		       GtkWidget  *widget )

	

callback_data es un puntero a la estructura que queramos, y se define durante la llamada a gtk_item_factory_create_items().

callback_action tiene el mismo valor que callback_action en la entrada ItemFactory.

*widget es un puntero a un widget elemento de menú (descritos en la sección “Entradas ItemFactory”).

Ejemplos de entradas ItemFactory

Cómo crear una entrada simple de menú:

		GtkItemFactoryEntry entry = {"/_Fichero/_Abrir...", "<CTRL>A", print_hello, 0, "<Item>"};
	

Esto definirá una entrada simple de menú "/Fichero/Abrir" (mostrada como "Abrir"), bajo la entrada de menú "/Fichero". Se define además el atajo de teclado control+'A' que al ser pinchado, llama a la función print_hello(). print_hello() tiene la forma void print_hello(void) dado que el campo callback_action field está a cero. Cuando se muestre la 'A' de "Abrir", será subrayada y si el elemento de menú es visible en pantalla, al pulsar la 'A' será activado este item. Obsérverse que también se podría haber usado "Fichero/_Abrir" como la ruta en lugar de "/_Fichero/_Abrir".

Cómo crear una entrada con una callback más compleja:

	GtkItemFactoryEntry entry = {"/_View/Display _FPS", NULL, print_state, 7,"<CheckItem>"};
	

Esto define una nueva entrada de menú mostrada como "Display FPS" bajo la entrada de menú "View". Cuando se pulse, se hará uan llamada a la función print_state(). Dado que callback_action no es cero, la función print_state() tendrá el siguiente prototipo:

	void print_state( gpointer    callback_data,
			  guint       callback_action,
			  GtkWidget  *widget )
	

con el parámetro callback_action igual a 7.

Crear un conjunto radiobutton:

	GtkItemFactoryEntry entry1 = {"/_View/_Low Resolution", NULL, change_resolution,
				1, "<RadioButton>"};
	GtkItemFactoryEntry entry2 = {"/_View/_High Resolution", NULL, change_resolution,
				2, "/View/Low Resolution"};
	

entry1 define un botón radial que al conmutar llamará a la función change_resolution() con el parámetro callback_action igual a 1. change_resolution() es de la forma:

	void change_resolution(gpointer    callback_data,
                       guint       callback_action,
                       GtkWidget  *widget)
	

entry2 define un botón radial que pertenece al mismo grupo que entry1. Al conmutar llamará a la misma función que la anterior entrada, pero con el parámetro callback_action igual a 2. Nótese que el item_type de entry2 es igual a la ruta de entry1 pero sin el acelerador ('_'). Si se necesitara otro botón radial en el mismo grupo entonces sería definido de la misma forma que entry2 con su item_type de nuevo igual a "/View/Low Resolution".

Arrays de ItemFactoryEntry

Un ItemFactoryEntry por sí solo no es algo útil. Es necesario crear una array de estos elementos para definir un menú. A continuación mostramos un ejemplo de cómo podemos declarar un array de este tipo:

	static GtkItemFactoryEntry entries[] = {
	  { "/_File",         NULL,      NULL,         0, "<Branch>" },
	  { "/File/tear1",    NULL,      NULL,         0, "<Tearoff>" },
	  { "/File/_New",     "<CTRL>N", new_file,     1, "<Item>" },
	  { "/File/_Open...", "<CTRL>O", open_file,    1, "<Item>" },
	  { "/File/sep1",     NULL,      NULL,         0, "<Separator>" },
	  { "/File/_Quit",    "<CTRL>Q", quit_program, 0, "<StockItem>", GTK_STOCK_QUIT } };
	  

Cómo crear un ItemFactory

Un array de elementos GtkItemFactoryEntry define un menú. Una vez definido este array podremos crear la factoría ItemFactory. La función que hace esto es:

	GtkItemFactory* gtk_item_factory_new( GtkType        container_type,
					      const gchar   *path,
					      GtkAccelGroup *accel_group );
	

container_type puede ser uno de los siguientes: GTK_TYPE_MENU GTK_TYPE_MENU_BAR GTK_TYPE_OPTION_MENU

container_type define qué tipo de menú queremos, de tal forma que cuando más adelante lo necesitemos, poder saber si estamos hablando de un menú (por ejemplo para los popups), una barra de menús o una opción de un menú en concreto.

path define la ruta a la raíz del menú. Básicamente consiste en un nombre único para la raíz del menú, rodeado de "<>". Esto es imporante para definir luego la nomenclatura de los atajo de teclado y debe ser único, tanto a nivel de menús como a nivel de programa. Por ejemplo, en un programa llamado 'foo', el menú principal debería llamarse "<FooMain>", y un menú pop-up del mismo programa "<FooImagePopUp>", ó similar. Lo que es importante es que sean únicos.

accel_group es un puntero a gtk_accel_group. La factoría de elementos define la tabla de aceleradores al generar los menús. Pueden generarse nuevos grupos de aceleradores usando la función gtk_accel_group_new().

Pero esto es sólo el primer paso. Para convertir la información del array de GtkItemFactoryEntry en widgets necesitaremos hacer uso de la siguiente función:

		void gtk_item_factory_create_items( GtkItemFactory      *ifactory,
                                    guint                n_entries,
                                    GtkItemFactoryEntry *entries,
                                    gpointer             callback_data );
		

*ifactory es un puntero a la factoría de elementos creada anteriormente. n_entries es el número de entradas del array GtkItemFactoryEntry. *entries es un puntero al array GtkItemFactoryEntry. callback_data es lo que se le pasa a todas las funciones callback para todas las entradas con el parámetro callback_actiondistinto de 0.

El grupo de aceleradores ya ha sido formado, por lo tenemos que adjuntarlo a la ventana en la que hemos situado nuestro menú:

		void gtk_window_add_accel_group( GtkWindow     *window,
                                 GtkAccelGroup *accel_group);
		

Cómo usar el menú y los elementos de menú

Lo último que debemos estudiar es saber cómo usar el menú. La siguiente función extraé los widgets más relevantes de la factoría ItemFactory:

	GtkWidget* gtk_item_factory_get_widget( GtkItemFactory *ifactory,
                                        const gchar    *path );
	

Por ejemplo, si una factoría de elementos ItemFactory tiene dos entradas como "/File" y "/File/New", usando en la función anterior un path como "/File", nos devolverá un widget menu de la factoría ItemFactory. Usando un path como "/File/New" nos devolverá un widget elemento de menú. Esto nos permite definir el estado inicial de los elementos de un menú. Por ejemplo, para definir el elemento por defecto de un conjunto de botones radiales a aquel con el path "/Shape/Oval", usaríamos el siguiente trozo de código:

	gtk_check_menu_item_set_active(
	GTK_CHECK_MENU_ITEM (gtk_item_factory_get_item (item_factory, "/Shape/Oval")),
	TRUE);
	

Finalmente, para obtener la raíz del menú, usaremos la función gtk_item_factory_get_item() con el path "<main>" (o cualquier otro path que hubiéramos usado en gtk_item_factory_new()). En caso de que ItemFactory hubiera sido creado con el tipo GTK_TYPE_MENU_BAR este devolvería un widget de tipo barra de menú. Con el tipo GTK_TYPE_MENU, un widget de tipo menú. Y con el tipo GTK_TYPE_OPTION_MENU, devolvería un widget de tipo opción de menú.

Recordemos que para una entrada definida con el path "/_File", el path a utilizar en la función gtk_item_factory_get_widget() es realmente "/File".

Ahora que tenemos una barra de menú o un menú completamente definido, podemos manipularlo de la misma forma que vimos en la sección sobre “Creación Manual de Menús”crear menús de forma manual.

Nota

Podemos ver un completo ejemplo de la creación y uso de menús usando factorías en el programa menuitemfactory.c del directorio de ejemplos.

GtkToolbar

Las barras de herramientas se usan generalmente para agrupar varios widgets con el objetivo de simplificar la personalización de su aspecto y colocación. Típicamente, una barra de herramientas consta de una serie de botones con iconos, etiquetas y tooltips, pero es posible colocar cualquier otro widget dentro de una barra de herramientas. Para finalizar, diremos que los elementos de una barra pueden ser colocados en horizontal o vertical y que los botones de la misma pueden mostrar iconos, etiquetas o ambos elementos. Podemos crear una barra de herramientas (como era de esperar) con la siguiente función: GtkWidget *gtk_toolbar_new( void ); Tras crear una barra de herramientas es posible añadirle por delante, por detrás o insertar en una posición determinada simples cadenas de texto o cualquier otro tipo de widget. Para describir un elemento de la barra necesitamos una etiqueta de texto, un texto para la sugerencia ("tooltip"), un texto para la sugerencia privada, un icono para el botón y una función callback. Por ejemplo, para añadir un elemento por delante o por detrás, podríamos usar cualquiera de las siguientes funciones:

GtkWidget * gtk_toolbar_append_item (toolbar,  
 text,  
 tooltip_text,  
 tooltip_private_text,  
 icon,  
 callback,  
 user_data); 
GtkToolbar * toolbar;
const char * text;
const char *  tooltip_text;
const char *  tooltip_private_text;
GtkWidget *  icon;
GtkSignalFunc callback;
gpointer user_data;

GtkWidget *gtk_toolbar_prepend_item( GtkToolbar *toolbar, const char *text, const char *tooltip_text, const char *tooltip_private_text, GtkWidget *icon, GtkSignalFunc callback, gpointer user_data ); Si el lector desea usar gtk_toolbar_insert_item(), el único parámetro adicional que debería de ser especificado es la posición en la que el elemento debería de ser insertado, así: GtkWidget *gtk_toolbar_insert_item( GtkToolbar *toolbar, const char *text, const char *tooltip_text, const char *tooltip_private_text, GtkWidget *icon, GtkSignalFunc callback, gpointer user_data, gint position ); Para simplificar el proceso de añadir espacios entre los elementos de la barra de herramientas, es posible usar las siguientes funciones: void gtk_toolbar_append_space( GtkToolbar *toolbar ); void gtk_toolbar_prepend_space( GtkToolbar *toolbar ); void gtk_toolbar_insert_space( GtkToolbar *toolbar, gint position ); Si así se requiere, es posible cambiar la orientación y el estilo de una barra de herramientas "al vuelo", usando las siguientes funciones: void gtk_toolbar_set_orientation( GtkToolbar *toolbar, GtkOrientation orientation ); void gtk_toolbar_set_style( GtkToolbar *toolbar, GtkToolbarStyle style ); void gtk_toolbar_set_tooltips( GtkToolbar *toolbar, gint enable ); Donde orientation puede ser uno de los siguientes GTK_ORIENTATION_HORIZONTAL o GTK_ORIENTATION_VERTICAL. El estilo se usa para asignar una apariencia a los elementos de la barra de herramientas, mediante uno de lo siguientes : GTK_TOOLBAR_ICONS, GTK_TOOLBAR_TEXT, ó GTK_TOOLBAR_BOTH.

Para mostrar algunas otras cosas que se pueden hacer con una barra de herramientas, tomemos como ejemplo el siguiente programa (cortaremos el flujo normal del mismo para añadir ciertas explicaciones):

       #include <gtk/gtk.h>

	/* This function is connected to the Close button or
	 * closing the window from the WM */
	static gboolean delete_event( GtkWidget *widget,
				      GdkEvent *event,
				      gpointer data )
	{
	  gtk_main_quit ();
	  return FALSE;
	}
      

El comienzo anterior será familiar ya al lector si ha seguido este libro desde el principio. Hay una nota adicional que hacer, y es que incluiremos un gráfico XPM como icono de todos nuestros botones.

	GtkWidget* close_button; /* This button will emit signal to close
                          * application */
	GtkWidget* tooltips_button; /* to enable/disable tooltips */
	GtkWidget* text_button,
		 * icon_button,
		 * both_button; /* radio buttons for toolbar style */
	GtkWidget* entry; /* a text entry to show packing any widget into
			   * toolbar */

	

De hecho, no se necesitan todos los widgets definidos más arriba, pero los hemos puesto para hacer más didáctico el ejemplo.

	/* that's easy... when one of the buttons is toggled, we just
	 * check which one is active and set the style of the toolbar
	 * accordingly
	 * ATTENTION: our toolbar is passed as data to callback ! */
	static void radio_event( GtkWidget *widget,
				 gpointer data )
	{
	  if (GTK_TOGGLE_BUTTON (text_button)->active)
	    gtk_toolbar_set_style (GTK_TOOLBAR (data), GTK_TOOLBAR_TEXT);
	  else if (GTK_TOGGLE_BUTTON (icon_button)->active)
	    gtk_toolbar_set_style (GTK_TOOLBAR (data), GTK_TOOLBAR_ICONS);
	  else if (GTK_TOGGLE_BUTTON (both_button)->active)
	    gtk_toolbar_set_style (GTK_TOOLBAR (data), GTK_TOOLBAR_BOTH);
	}

	/* even easier, just check given toggle button and enable/disable
	 * tooltips */
	static void toggle_event( GtkWidget *widget,
				  gpointer   data )
	{
	  gtk_toolbar_set_tooltips (GTK_TOOLBAR (data),
				    GTK_TOGGLE_BUTTON (widget)->active );
	}
	

Lo de arriba son sólo dos funciones callback que serán llamadas cuando se pulse uno de los botones de la barra de herramientas. El lector debería de estar familiarizado con cosas como ésta al haber leído sobre el uso de "toogle button" y botones radiales.

	int main (int argc, char *argv[])
	{
	  /* Here is our main window (a dialog) and a handle for the handlebox */
	  GtkWidget* dialog;
	  GtkWidget* handlebox;

	  /* Ok, we need a toolbar, an icon with a mask (one for all of
	     the buttons) and an icon widget to put this icon in (but
	     we'll create a separate widget for each button) */
	  GtkWidget * toolbar;
	  GtkWidget * iconw;

	  /* this is called in all GTK application. */
	  gtk_init (&argc, &argv);

	  /* create a new window with a given title, and nice size */
	  dialog = gtk_dialog_new ();
	  gtk_window_set_title (GTK_WINDOW (dialog), "GTKToolbar Tutorial");
	  gtk_widget_set_size_request (GTK_WIDGET (dialog), 600, 300);
	  GTK_WINDOW (dialog)->allow_shrink = TRUE;

	  /* typically we quit if someone tries to close us */
	  g_signal_connect (G_OBJECT (dialog), "delete_event",
			    G_CALLBACK (delete_event), NULL);

	  /* we need to realize the window because we use pixmaps for
	   * items on the toolbar in the context of it */
	  gtk_widget_realize (dialog);

	  /* to make it nice we'll put the toolbar into the handle box,
	   * so that it can be detached from the main window */
	  handlebox = gtk_handle_box_new ();
	  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
			      handlebox, FALSE, FALSE, 5);
	

Lo anterior debería de ser similar a cualquier otra aplicación GTK. Simplemente inicializar GTK, crear la ventana, etc. Sólo hay una cosa que merece una especial atención: la caja gestora ("handle box"). La caja gestora es otro tipo de caja que puede ser usada para empaquetar widgets. La diferencia entre ésta y otras cajas típicas es que la caja gestora puede ser desenganchada de una ventana padre (o, más exactamente, la caja gestora permanece enganchada al padre, pero éste se reduce a un muy pequeño rectángulo, mientras que todos sus componentes son heredados por una nueva ventana flotante). Generalmente, es algo deseable disponer de una barra de herramientas que permita ser desenganchada, por lo que la aparición conjunta de estos dos widgets es bastante común.

	  /* toolbar will be horizontal, with both icons and text, and
   * with 5pxl spaces between items and finally,
   * we'll also put it into our handlebox */
  toolbar = gtk_toolbar_new ();
  gtk_toolbar_set_orientation (GTK_TOOLBAR (toolbar), GTK_ORIENTATION_HORIZONTAL);
  gtk_toolbar_set_style (GTK_TOOLBAR (toolbar), GTK_TOOLBAR_BOTH);
  gtk_container_set_border_width (GTK_CONTAINER (toolbar), 5);
  gtk_container_add (GTK_CONTAINER (handlebox), toolbar);
	

Bien, lo hecho en el párrafo anterior es simplemente una inicialización del widget barra de herramientas.

	  /* our first item is <close> button */
  iconw = gtk_image_new_from_file ("gtk.xpm"); /* icon widget */
  close_button =
    gtk_toolbar_append_item (GTK_TOOLBAR (toolbar), /* our toolbar */
                             "Close",               /* button label */
                             "Closes this app",     /* this button's tooltip */
                             "Private",             /* tooltip private info */
                             iconw,                 /* icon widget */
                             GTK_SIGNAL_FUNC (delete_event), /* a signal */
                             NULL);
  gtk_toolbar_append_space (GTK_TOOLBAR (toolbar)); /* space after item */
	

En el código anterior podemos ver el caso más sencillo: añador un botón a la barra de herramientas. Justo antes de añadir un nuevo elemento, debemos construir un widget imagen que sirva como icono de este elemento; este paso deberá ser repetido para cada nuevo item. Justo tras colocar el item añadiremos también un espacio, de tal forma que los siguientes items no se toquen entre sí. Como puede observar, gtk_toolbar_append_item() devuelve un puntero al widget botón que acabamos de crear, para poder trabajar con él de la forma habitual.

	  /* now, let's make our radio buttons group... */
	  iconw = gtk_image_new_from_file ("gtk.xpm");
	  icon_button = gtk_toolbar_append_element (
			    GTK_TOOLBAR (toolbar),
			    GTK_TOOLBAR_CHILD_RADIOBUTTON, /* a type of element */
			    NULL,                          /* pointer to widget */
			    "Icon",                        /* label */
			    "Only icons in toolbar",       /* tooltip */
			    "Private",                     /* tooltip private string */
			    iconw,                         /* icon */
			    GTK_SIGNAL_FUNC (radio_event), /* signal */
			    toolbar);                      /* data for signal */
	  gtk_toolbar_append_space (GTK_TOOLBAR (toolbar));
	

En las líneas anteriores comenzamos creando un grupo de botones radiales. Para ello, nos valemos de la función gtk_toolbar_append_element. De hecho, usando esta función, podríamos añadir simples ítems de texto o incluso espacios (usando los tipos GTK_TOOLBAR_CHILD_SPACE ó GTK_TOOLBAR_CHILD_BUTTON).En el ejemplo anterior, hemos creado un grupo de botones radiales. Al crear otros botones radiales en este grupo, es necesario disponer de un puntero al botón anterior del grupo, de tal forma que sea posible crear fácilmente una lista de botones (para más información, consulte la sección “GtkRadioButton”)

	 /* following radio buttons refer to previous ones */
  iconw = gtk_image_new_from_file ("gtk.xpm");
  text_button =
    gtk_toolbar_append_element (GTK_TOOLBAR (toolbar),
                                GTK_TOOLBAR_CHILD_RADIOBUTTON,
                                icon_button,
                                "Text",
                                "Only texts in toolbar",
                                "Private",
                                iconw,
                                GTK_SIGNAL_FUNC (radio_event),
                                toolbar);
  gtk_toolbar_append_space (GTK_TOOLBAR (toolbar));

  iconw = gtk_image_new_from_file ("gtk.xpm");
  both_button =
    gtk_toolbar_append_element (GTK_TOOLBAR (toolbar),
                                GTK_TOOLBAR_CHILD_RADIOBUTTON,
                                text_button,
                                "Both",
                                "Icons and text in toolbar",
                                "Private",
                                iconw,
                                GTK_SIGNAL_FUNC (radio_event),
                                toolbar);
  gtk_toolbar_append_space (GTK_TOOLBAR (toolbar));
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (both_button), TRUE);
	

Finalmente, debemos activar el estado de uno de los botones de forma manual (o en caso contrario todos estarían activos al comienzo, impidiéndonos pulsar uno u otro)

	  /* here we have just a simple toggle button */
  iconw = gtk_image_new_from_file ("gtk.xpm");
  tooltips_button =
    gtk_toolbar_append_element (GTK_TOOLBAR (toolbar),
                                GTK_TOOLBAR_CHILD_TOGGLEBUTTON,
                                NULL,
                                "Tooltips",
                                "Toolbar with or without tips",
                                "Private",
                                iconw,
                                GTK_SIGNAL_FUNC (toggle_event),
                                toolbar);
  gtk_toolbar_append_space (GTK_TOOLBAR (toolbar));
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (tooltips_button), TRUE);
	

Podemos crear un botón conmutador de la forma obvia (si uno ya sabe crear botones radiales).

	  /* to pack a widget into toolbar, we only have to
   * create it and append it with an appropriate tooltip */
  entry = gtk_entry_new ();
  gtk_toolbar_append_widget (GTK_TOOLBAR (toolbar),
                             entry,
                             "This is just an entry",
                             "Private");

  /* well, it isn't created within the toolbar, so we must still show it */
  gtk_widget_show (entry);
	

Como podemos ver, añadir cualquier tipo de widget a una barra de heramientas es algo simple. Lo único que hay que recordar es que este widget debe ser mostrado manualmente (al contrario que otros items que mostraremos más adelante conjuntamente con la barra de herramientas)

	  /* that's it ! let's show everything. */
	  gtk_widget_show (toolbar);
	  gtk_widget_show (handlebox);
	  gtk_widget_show (dialog);

	  /* rest in gtk_main and wait for the fun to begin! */
	  gtk_main ();

	  return 0;
	}
	

Con esto finalizar el capítulo de la barra de herramientas. Sólo añadir que será necesario hacer uso del siguiente icono XPM para apreciar el ejemplo en todo su esplendor:

	/* XPM */
static char * gtk_xpm[] = {
"32 39 5 1",
".      c none",
"+      c black",
"@      c #3070E0",
"#      c #F05050",
"$      c #35E035",
"................+...............",
"..............+++++.............",
"............+++++@@++...........",
"..........+++++@@@@@@++.........",
"........++++@@@@@@@@@@++........",
"......++++@@++++++++@@@++.......",
".....+++@@@+++++++++++@@@++.....",
"...+++@@@@+++@@@@@@++++@@@@+....",
"..+++@@@@+++@@@@@@@@+++@@@@@++..",
".++@@@@@@+++@@@@@@@@@@@@@@@@@@++",
".+#+@@@@@@++@@@@+++@@@@@@@@@@@@+",
".+##++@@@@+++@@@+++++@@@@@@@@$@.",
".+###++@@@@+++@@@+++@@@@@++$$$@.",
".+####+++@@@+++++++@@@@@+@$$$$@.",
".+#####+++@@@@+++@@@@++@$$$$$$+.",
".+######++++@@@@@@@++@$$$$$$$$+.",
".+#######+##+@@@@+++$$$$$$@@$$+.",
".+###+++##+##+@@++@$$$$$$++$$$+.",
".+###++++##+##+@@$$$$$$$@+@$$@+.",
".+###++++++#+++@$$@+@$$@++$$$@+.",
".+####+++++++#++$$@+@$$++$$$$+..",
".++####++++++#++$$@+@$++@$$$$+..",
".+#####+++++##++$$++@+++$$$$$+..",
".++####+++##+#++$$+++++@$$$$$+..",
".++####+++####++$$++++++@$$$@+..",
".+#####++#####++$$+++@++++@$@+..",
".+#####++#####++$$++@$$@+++$@@..",
".++####++#####++$$++$$$$$+@$@++.",
".++####++#####++$$++$$$$$$$$+++.",
".+++####+#####++$$++$$$$$$$@+++.",
"..+++#########+@$$+@$$$$$$+++...",
"...+++########+@$$$$$$$$@+++....",
".....+++######+@$$$$$$$+++......",
"......+++#####+@$$$$$@++........",
".......+++####+@$$$$+++.........",
".........++###+$$$@++...........",
"..........++##+$@+++............",
"...........+++++++..............",
".............++++..............."};
	

GtkCombo

El combo box es otro widget relativamente simple que simplemente consiste en una agrupación de otros widgets. Desde el punto de vista del usuario, el widget consiste en una caja de entrada de texto y un menú desplegable desde el que el usuario puede seleccionar una opción de un conjunto de entradas predefinidas. De manera opcional, el usuario puede teclear una opción diferente directamente en la caja de texto.

El siguiente trozo de código está extraído de la definición de la estructura ComboBox e identifica muchos de sus componentes:

	struct _GtkCombo {
        GtkHBox hbox;
        GtkWidget *entry;
        GtkWidget *button;
        GtkWidget *popup;
        GtkWidget *popwin;
        GtkWidget *list;
	...  };
      

Como puede observar el lector, el ComboBox consta de dos partes principales: un widget de entrada de datos y un widget lista.

Para crear un combo box, deberíamos de usar la siguiente instrucción:

	GtkWidget *gtk_combo_new( void );
	

Si ahora quisiéramos inicializar la cadena de texto en la sección de entrada del combo box, lo podríamos hacer manipulando directamente dicho widget de entrada de datos:

	gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (combo)->entry), "My String.");
	

Para inicializar los valores en la lista desplegable, deberíamos de usar la siguiente función:

	void gtk_combo_set_popdown_strings( GtkCombo *combo,
                                    GList    *strings );
	

Antes de poder ejecutar la instrucción anterior, es necesario preparar la lista de strings que queremos visualizar en la lista desplegable dentro de una construcción GList. GList es una implementación de lista ligada que forma parte de GLib, una biblioteca de funciones que da soporte a GTK. Por el momento, la explicación más rápida consiste en decir que es necesario definir un puntero a GList, inicializarlo a NULL y después añadirle strings GList is a linked list implementation that is part of GLib, a library supporting GTK. For the moment, the quick and dirty explanation is that you need to set up a GList pointer, set it equal to NULL, then append strings con la instrucción:

	GList *g_list_append( GList *glist,
                      gpointer data );
	

Es importante inicializar el puntero inicial GList a NULL. El valor devuelto por la función g_list_append() debe ser usado como el nuevo puntero para GList.

Aquí podemos ver un fragmento de código típico para crear un conjunto de opciones:

    GList *glist = NULL;

    glist = g_list_append (glist, "String 1");
    glist = g_list_append (glist, "String 2");
    glist = g_list_append (glist, "String 3");
    glist = g_list_append (glist, "String 4");

    gtk_combo_set_popdown_strings (GTK_COMBO (combo), glist);

    /* ahora es posible liberar glist, pues el combo ya tiene una copia de la lista */
    

El widget combo creará una copia de las cadenas de texto que se le pasen como parámetro en la estructura glist. Como resultado, es necesario asegurarse de liberar la memoria usada por la lista si es necesario para la aplicación que estemos construyendo.

En este punto ya disponemos de una configuración funcional de un combo box. Existen algunos aspectos del comportamiento de un combo que podemos cambiar por programación, usando las siguientes funciones:

		void gtk_combo_set_use_arrows( GtkCombo *combo,
                               gboolean  val );

		void gtk_combo_set_use_arrows_always( GtkCombo *combo,
                                      gboolean  val );

		void gtk_combo_set_case_sensitive( GtkCombo *combo,
                                   gboolean  val );
	

gtk_combo_set_use_arrows() permite al usuario cambiar el valor de la entrada usando las teclas del cursor arriba/abajo. Esto no muestra la lista desplegable, sino que reemplaza el texto actual de la entrada de texto del combo con el siguiente valor de la lista (arriba o abajo, en función de la pulsación de tecla). Esto se consigue buscando en la lista el valor actual de la entrada del combo y seleccionando el siguiente o el anterior elemento, en función de lo pulsado por el usuario. Normalmente, en una entrada de texto, las teclas de movimiento arriba/abajo se usan para cambiar el foco (es posible hacerlo igualmente mediante el TABulador). Como nota adicional, hacer notar que cuando el elemento seleccionado es el último de la lista y el usuario pulsa flecha abajo, éste cambiará el foco (lo mismo ocurre cuando está seleccionado el primer elemento y el usuario pulsa flecha arriba).

Si el valor actual de la entrada no está en la lista, la función gtk_combo_set_use_arrows() se deshabilita.

De forma similar, gtk_combo_set_use_arrows_always() permite el uso de las flechas arriba/abajo para moverse por las opciones de la lista desplegable. Esta función es idéntica a la anterior, pero permite usar las flechas arriba y abajo para cambiar el elemento de la lista desplegable aunque lo que se haya tecleado en la entrada de texto no coincida con ninguno de los elementos.

La función gtk_combo_set_case_sensitive() permite conmutar entre la posibilidad o imposibilidad de que GTK busque las entradas del combo teniendo en cuenta diferencias entre mayúsculas y minúsculas. Se usa cuando el widget combo necesita buscar un valor de la lista usando el contenido actual de la entrada de texto. El completamiento puede ser realizado, dependiendo del valor de esta función, teniendo en cuenta mayúsculas/minúsculas o no teniéndolas en cuenta. El widget Combo puede también simplemente completar el valor actual de la entrada de texto si el usuario pulsa la combinación de teclas MOD-1 y "Tab". MOD-1 está normalmente mapeado por la utilidad xmodmap a la tecla "Alt". Obsérvese, sin embargo, que algunos gestores de ventanas también podrían usar esta combinación de teclas para otra función, que tendrá preferencias sobre su uso en GTK.

Ahora que disponemos de un combo box, personalizado para funcionar a nuestro gusto, todo lo que queda es saber cómo acceder a los datos que muestra este combo. Esto es relativamente sencillo. La mayor parte de las veces, todo lo que necesitamos hacer para obtener datos del combo es saber acceder a la entrada de texto del mismo. Esta entrada puede ser accedida simplemente a través de GTK_ENTRY (GTK_COMBO (combo)->entry). Las dos cosas principales que vamos a necesitar hacer es conectar esta entrada a la señal "activate", que indica cuándo el usuario ha pulsado la tecla Return o Enter, y leer el contenido de dicha entrada de texto. Lo primero lo podemos conseguir usando algo como:

		    g_signal_connect (G_OBJECT (GTK_COMBO (combo)->entry), "activate",
                      G_CALLBACK (my_callback_function), (gpointer) my_data);
		

Para acceder al texto de la entrada en cualquier momento, lo podemos hacer simplemente usando la siguiente función:

	gchar *gtk_entry_get_text( GtkEntry *entry );
	

Como aquí:

		    gchar *string;

		    string = gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (combo)->entry));
		

Y esto es todo sobre el widget GtkCombo. Existe también una función

		void gtk_combo_disable_activate( GtkCombo *combo );
	

que deshabilitará la señal activate en el widget de entrada del combo. Personalmente, no encuentro ninguna situación en la que pueda ser interesante usarla, pero existe.

Nota

En el fichero combo.c del directorio de ejemplos GTK se puede estudiar el código completo de una aplicación que hace uso del widget GtkCombo.