summaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
authorManuel de Vega Barreiro <barreiro@src.gnome.org>2000-04-23 09:17:36 +0000
committerManuel de Vega Barreiro <barreiro@src.gnome.org>2000-04-23 09:17:36 +0000
commitaee1e501661fd57dbac0128f92a7e9a430058d32 (patch)
treef6ad9a51b58e68193b7f9b46a194aa2c991b94d2 /docs
parentff8a24c85ade4777f4934369132298c165cdbcf5 (diff)
downloadgdk-pixbuf-aee1e501661fd57dbac0128f92a7e9a430058d32.tar.gz
Spanish translation update
Diffstat (limited to 'docs')
-rwxr-xr-xdocs/gtk_tut_12.es.sgml17638
-rwxr-xr-xdocs/tutorial/gtk_tut_12.es.sgml17638
2 files changed, 35276 insertions, 0 deletions
diff --git a/docs/gtk_tut_12.es.sgml b/docs/gtk_tut_12.es.sgml
new file mode 100755
index 000000000..38449c8ed
--- /dev/null
+++ b/docs/gtk_tut_12.es.sgml
@@ -0,0 +1,17638 @@
+<!doctype linuxdoc system>
+
+<!--
+ Traducción realizada por:
+ Joaquín Cuenca Abela (e98cuenc@criens.u-psud.fr)
+ Eduardo Anglada Varela (eduardo.anglada@adi.uam.es)
+
+ Versión beta 2 de la traducción del GTK+
+ Tutorial 1.2. Cualquier sugerencia será bienvenida.
+ Los cambios más significativos de esta versión son la traducción
+ de las variables de los programas y XXX.
+
+ Si quiere obtener este documento puede encontrarlo en Linux Landia:
+ http://www.croftj.net/~barreiro/spanish/gnome-es/
+-->
+
+<article>
+<title>GTK Tutorial v1.2
+<author>Tony Gale <tt><htmlurl url="mailto:gale@gtk.org"
+ name="&lt;gale@gtk.org&gt;"></tt>,
+Ian Main <tt><htmlurl url="mailto:imain@gtk.org"
+ name="&lt;imain@gtk.org&gt;"></tt>
+<date>21 de Febrero de 1999
+<abstract>
+Este documento es un tutorial sobre como utilizar GTK (el GIMP
+Toolkit) en C
+</abstract>
+
+<toc>
+
+<!-- ***************************************************************** -->
+<sect>Introducción
+<!-- ***************************************************************** -->
+<p>
+GTK (GIMP Toolkit) es una biblioteca para crear interfaces gráficas
+de usuario. Su licencia es la LGPL, así que mediante GTK podrá
+desarrollar programas con licencias abiertas, gratuitas, libres, y
+hasta licencias comerciales no libres sin mayores problemas.
+
+Se llama el GIMP toolkit porque fue escrito para el desarrollo del
+General Image Manipulation Program (GIMP), pero ahora GTK se utiliza
+en un gran número de proyectos de programación, incluyendo el
+proyecto GNU Network Object Model Environment (GNOME). GTK está
+construido encima de GDK (GIMP Drawing Kit) que básicamente es un
+recubrimiento de las funciones de bajo nivel que deben haber para
+acceder al sistema de ventanas sobre el que se programe (Xlib en el
+caso de X windows). Los principales autores de GTK son:
+
+<itemize>
+<item> Peter Mattis <tt><htmlurl url="mailto:petm@xcf.berkeley.edu"
+ name="petm@xcf.berkeley.edu"></tt>
+<item> Spencer Kimball <tt><htmlurl url="mailto:spencer@xcf.berkeley.edu"
+ name="spencer@xcf.berkeley.edu"></tt>
+<item> Josh MacDonald <tt><htmlurl url="mailto:jmacd@xcf.berkeley.edu"
+ name="jmacd@xcf.berkeley.edu"></tt>
+</itemize>
+
+GTK es esencialmente una interfaz para la programación de
+aplicaciones orientadas al objeto (API). Aunque está completamente
+escrito en C, esta implementado haciendo uso de la idea de clases y de
+funciones respuesta o de <em/callback/ (punteros o funciones).
+
+Tenemos un tercer componente llamado glib, que contiene unas cuantas
+funciones para reemplazar algunas llamadas estándar, así como
+funciones adicionales para manejar listas enlazadas, etc... Se
+reemplazan algunas funciones para aumentar la portabilidad de GTK, ya
+que algunas de las funciones implementadas no están disponibles o
+no son estándar en otros unixs, como por ejemplo
+g_strerror(). Algunas otras contienen mejoras a la versión de libc,
+como g_malloc que mejora las posibilidades de encontrar errores.
+
+Este tutorial describe la interfaz C de GTK. Hay recubrimientos GTK
+para muchos otros lenguajes, incluyendo C++, Guile, Perl, Python, TOM,
+Ada95, Objective C, Free Pascal, y Eiffel. Si va a utilizar el
+recubrimiento para alguno de estos lenguajes, mire primero su
+documentación. En algunos casos la documentación puede describir
+algún convenio importante (que debería conocer de antemano) y
+después puede volver a este tutorial. También hay algún API
+multiplataforma (como wxWindows y V) que utilizan GTK como una de sus
+plataformas destino; de nuevo, consulte primero la documentación
+que viene con estos paquetes.
+
+Si está desarrollando su aplicación GTK en C++, hay algunas
+cosas que debería saber. Hay un recubriento a GTK para C++ llamado
+GTK--, que proporciona una interfaz C++ a GTK; probablemente
+debería empezar mirando ahí. Si no le gusta esa aproximación
+al problema, por los motivos que sean, tiene dos
+alternativas. Primero, puede ceñirse al subconjunto C de C++ cuando
+realice alguna llamada a GTK a través de su interfaz en C. Segundo,
+puede utilizar GTK y C++ al mismo tiempo declarando todas las
+funciones respuesta como funciones estáticas en clases C++, y de
+nuevo, llamar a GTK utilizando su interfaz C. Si elige esta última
+forma de actuar, puede incluir como dato de la función respuesta un
+puntero al objeto a manipular (el también llamado valor
+«this»). La elección de una u otra opción es cuestión de
+gustos personales, ya que de las tres maneras conseguirá utilizar
+GTK en C++. Ninguna de estas aproximaciones requiere el uso de un
+preprocesador especializado, por lo que sin importar la opción que
+escoja podrá utilizar C++ estándar en C++.
+
+Este tutorial es un intento de documentar GTK tanto como sea posible,
+pero no está completo. Este tutorial asume un buen conocimiento de
+C y de como crear programas bajo este lenguaje. Se verá beneficiado
+si tiene un conocimiento previo de la programación en X, pero no
+debería ser necesario. Si está aprendiendo GTK y es el primer
+conjunto de <em/widgets/ que utiliza, por favor envíenos sus
+comentarios sobre este tutorial y los problemas que ha
+encontrado.
+
+Este documento es un `trabajo pendiente de finalizar'. Para encontrar
+actualizaciones mire en http://www.gtk.org/ <htmlurl
+url="http://www.gtk.org/" name="http://www.gtk.org/">.
+
+Me gustaría escuchar cualquier problema que le surja mientras
+aprende GTK siguiendo este documento, y apreciaré cualquier
+información sobre como mejorarlo. Por favor, vea la sección <ref
+id="sec_Contributing" name="Contribuyendo"> para encontrar más
+información.
+
+<!-- ***************************************************************** -->
+<sect>Comenzando
+<!-- ***************************************************************** -->
+
+<p>
+Por supuesto lo primero que hay que hacer es descargar las fuentes de
+GTK e instalarlas. La última versión siempre se puede obtener de
+ftp.gtk.org (en el directorio /pub/gtk). En <htmlurl
+url="http://www.gtk.org/" name="http://www.gtk.org/"> hay más
+información sobre GTK. Para configurar GTK hay que usar GNU
+autoconf. Una vez descomprimido se pueden obtener las opciones usando
+<tt>./configure --help</tt>.
+
+El código de GTK además contiene las fuentes completas de todos
+los ejemplos usados en este manual, así como los makefiles para
+compilarlos.
+
+Para comenzar nuestra introducción a GTK vamos a empezar con el
+programa más sencillo posible. Con él vamos a crear una ventana de
+200x200 <em/pixels/ que sólo se puede destruir desde el shell.
+
+<tscreen><verb>
+/* principio del ejemplo base base.c */
+
+#include <gtk/gtk.h>
+
+int main (int argc, char *argv[])
+{
+ GtkWidget *ventana;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_widget_show (ventana);
+
+ gtk_main ();
+
+ return 0;
+}
+/* final del ejemplo */
+</verb></tscreen>
+
+Puede compilar el programa anterior con gcc tecleando:
+<tscreen><verb>
+gcc base.c -o base `gtk-config --cflags --libs`
+</verb></tscreen>
+
+El significado de la extraña opción de compilación se explica
+más adelante.
+
+Todo programa que use GTK debe llamar a <tt>gtk/gtk.h</tt> donde se
+declaran todas las variables, funciones, estructuras, etc. que serán
+usadas en el programa.
+
+La siguiente línea:
+
+<tscreen><verb>
+gtk_init (&amp;argc, &amp;argv);
+</verb></tscreen>
+
+Llama a la función gtk_init (gint *argc, gchar *** argv) responsable
+de `arrancar' la biblioteca y de establecer algunos parámetros (como son
+los colores y los visuales por defecto), llama a gdk_init (gint *argc,
+gchar *** argv) que inicializa la biblioteca para que pueda
+utilizarse, establece los controladores de las señales y comprueba los
+argumentos pasados a la aplicación desde la línea de comandos,
+buscando alguno de los siguientes:
+
+<itemize>
+<item> <tt/--gtk-module/
+<item> <tt/--g-fatal-warnings/
+<item> <tt/--gtk-debug/
+<item> <tt/--gtk-no-debug/
+<item> <tt/--gdk-debug/
+<item> <tt/--gdk-no-debug/
+<item> <tt/--display/
+<item> <tt/--sync/
+<item> <tt/--no-xshm/
+<item> <tt/--name/
+<item> <tt/--class/
+</itemize>
+
+En el caso de que encuentre alguno lo quita de la lista, dejando todo
+aquello que no reconozca para que el programa lo utilice o lo
+ignore. Así se consigue crear un conjunto de argumentos que son
+comunes a todas las aplicaciones basadas en GTK.
+
+Las dos líneas de código siguientes crean y muestran una ventana.
+
+<tscreen><verb>
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_widget_show (ventana);
+</verb></tscreen>
+
+El argumento GTK_WINDOW_TOPLEVEL especifica que queremos que el gestor
+de ventanas decore y sitúe la ventana. En lugar de crear una ventana
+de tamaño 0 x 0 toda ventana sin hijos por defecto es de 200 x 200, con
+lo que se consigue que pueda ser manipulada.
+
+La función gtk_widget_show() le comunica a GTK que hemos acabado de
+especificar los atributos del <em/widget/, y que por tanto puede
+mostrarlo.
+
+La última línea comienza el proceso del bucle principal de GTK.
+
+<tscreen><verb>
+gtk_main ();
+</verb></tscreen>
+
+Otra llamada que siempre está presente en cualquier aplicación es
+gtk_main(). Cuando el control llega a ella, GTK se queda dormido
+esperando a que suceda algún tipo de evento de las X (como puede ser
+pulsar un botón), que pase el tiempo necesario para que el usuario
+haga algo, o que se produzcan notificaciones de IO de archivos. En
+nuestro caso concreto todos los eventos serán ignorados.
+
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Programa «Hola Mundo» en GTK
+<p>
+El siguiente ejemplo es un programa con un <em/widget/ (un
+botón). Simplemente es la versión de GTK del clásico «hola mundo».
+
+<tscreen><verb>
+/* comienzo del ejemplo holamundo */
+#include <gtk/gtk.h>
+
+/* Ésta es una función respuesta (callback). Sus argumentos
+ son ignorados por en este ejemplo */
+void hello (GtkWidget *widget, gpointer data)
+{
+ g_print ("Hola mundo\n");
+}
+
+gint delete_event(GtkWidget *widget, GdkEvent *event, gpointer data)
+{
+ /* si se devuelve FALSE al administrador de llamadas
+ * "delete_event", GTK emitirá la señal de destrucción
+ * "destroy". Esto es útil para diálogos emergentes del
+ * tipo: ¿Seguro que desea salir?
+
+ g_print ("Ha ocurrido un evento delete\n");
+
+ /* Cambiando TRUE por FALSE la ventana se destruirá con
+ * "delete_event"*/
+
+ return (TRUE);
+}
+
+/* otra respuesta */
+void destroy (GtkWidget *widget, gpointer data)
+{
+ gtk_main_quit ();
+}
+
+int main (int argc, char *argv[])
+{
+
+ /* GtkWidget es el tipo de almacenamiento usado para los
+ * widgets */
+ GtkWidget *ventana;
+ GtkWidget *boton;
+
+ /* En cualquier aplicación hay que realizar la siguiente
+ * llamada. Los argumentos son tomados de la línea de comandos
+ * y devueltos a la aplicación. */
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ /* creamos una ventana nueva */
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ /* Cuando la ventana recibe la señal "delete_event" (emitida
+ * por el gestor de ventanas, normalmente mediante la opción
+ * 'close', o en la barra del título) hacemos que llame a la
+ * función delete_event() tal y como ya hemos visto. Los datos
+ * pasados a la función de respuesta son NULL, y serán ignorados. */
+ gtk_signal_connect (GTK_OBJECT (ventana), "delete_event",
+ GTK_SIGNAL_FUNC (delete_event), NULL);
+
+ /* Aquí conectamos el evento "destroy" con el administrador de
+ * señales. El evento se produce cuando llamamos a
+ * gtk_widget_destroy() desde la ventana o si devolvemos 'FALSE'
+ * en la respuesta "delete_event". */
+ gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
+ GTK_SIGNAL_FUNC (destroy), NULL);
+
+ /* establecemos el ancho del borde de la ventana. */
+ gtk_container_border_width (GTK_CONTAINER (ventana), 10);
+
+ /* creamos un botón nuevo con la etiqueta "Hola mundo" */
+ boton = gtk_button_new_with_label ("Hola mundo");
+
+ /* Cuando el botón recibe la señal "clicked" llama a la
+ * función hello() pasándole NULL como argumento. (La
+ * función ya ha sido definida arriba). */
+ gtk_signal_connect (GTK_OBJECT (boton), "clicked",
+ GTK_SIGNAL_FUNC (hello), NULL);
+
+ /* Esto hará que la ventana sea destruida llamando a
+ * gtk_widget_destroy(ventana) cuando se produzca "clicked". Una
+ * vez mas la señal de destrucción puede provenir del gestor
+ * de ventanas o de aquí. */
+ gtk_signal_connect_object (GTK_OBJECT (boton), "clicked",
+ GTK_SIGNAL_FUNC (gtk_widget_destroy),
+ GTK_OBJECT (ventana));
+
+ /* Ahora empaquetamos el botón en la ventana (usamos un gtk
+ * container ). */
+ gtk_container_add (GTK_CONTAINER (ventana), boton);
+
+ /* El último paso es representar el nuevo widget... */
+ gtk_widget_show (boton);
+
+ /* y la ventana */
+ gtk_widget_show (ventana);
+
+ /* Todas las aplicaciones basadas en GTK deben tener una llamada
+ * gtk_main() ya que el control termina justo aquí y debe
+ * esperar a que suceda algún evento */
+
+ gtk_main ();
+
+ return 0;
+}
+/* final del ejemplo*/
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Compilando Hello World
+<p>
+Para compilar el ejemplo hay que usar:
+
+<tscreen><verb>
+gcc -Wall -g helloworld.c -o hello_world `gtk-config --cflags` \
+ `gtk-config --libs`
+</verb></tscreen>
+
+Usamos el programa <tt>gtk-config</>, que ya viene (y se instala) con
+la biblioteca. Es muy útil porque `conoce' que opciones son
+necesarias para compilar programas que usen gtk. <tt>gtk-config
+--cflags</tt> dará una lista con los directorios donde el
+compilador debe buscar ficheros «include». A su vez <tt>gtk-config
+--libs</tt> nos permite saber las bibliotecas que el compilador
+intentará enlazar y dónde buscarlas.
+
+Hay que destacar que las comillas simples en la orden de
+compilación son absolutamente necesarias.
+
+Las bibliotecas que se enlazan normalmente son:
+
+<itemize>
+
+<item>La biblioteca GTK (-lgtk), la biblioteca de <em/widgets/ que se
+encuentra encima de GDK.
+
+<item>La biblioteca GDK (-lgdk), el wrapper de Xlib.
+
+<item>La biblioteca glib (-lglib), que contiene diversas funciones. En
+nuestro ejemplo sólo hemos usado g_print(). GTK está construida
+encima de glib por lo que simpre se usará. Vea la sección <ref
+id="sec_glib" name="glib"> para más detalles.
+
+<item>La biblioteca Xlib (-lX11) que es usada por GDK.
+
+<item> La biblioteca Xext (-lXext) contiene código para
+<em/pixmaps/ de memoria compartida y otras extensiones.
+
+<item>La biblioteca matemática (-lm). Es usada por GTK para
+diferentes cosas.
+
+</itemize>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Teoría de señales y respuestas
+<p>
+Antes de profundizar en <tt/holamundo/ vamos a discutir las
+señales y las respuestas. GTK es un toolkit (conjunto de
+herramientas) gestionadas mediante eventos. Esto quiere decir que GTK
+«duerme» en gtk_main hasta que se recibe un evento, momento en el
+cual se transfiere el control a la función adecuada.
+
+El control se transfiere mediante «señales». (Conviene destacar
+que las señales de GTK no son iguales que las de los sistemas
+UNIX, aunque la terminología es la misma.) Cuando sucede un evento,
+como por ejemplo la pulsación de un botón, se «emitirá» la
+señal apropiada por el <em/widget/ pulsado. Así es como GTK
+proporciona la mayor parte de su utilidad. Hay un conjunto de
+señales que todos los <em/widgets/ heredan, como por ejemplo
+«destroy» y hay señales que son específicas de cada
+<em/widget/, como por ejemplo la señal «toggled» de un botón
+de selección (botón <em/toggle/).
+
+Para que un botón haga algo crearemos un controlador que se encarga de
+recoger las señales y llamar a la función apropiada. Esto se hace
+usando una función como:
+
+<tscreen><verb>
+gint gtk_signal_connect( GtkObject *objeto,
+ gchar *nombre,
+ GtkSignalFunc func,
+ gpointer datos_func );
+</verb></tscreen>
+
+Donde el primer argumento es el <em/widget/ que emite la señal, el
+segundo el nombre de la señal que queremos `cazar', el tercero es
+la función a la que queremos que se llame cuando se `cace' la
+señal y el cuarto los datos que queremos pasarle a esta función.
+
+La función especificada en el tercer argumento se denomina «función
+de respuesta» y debe tener la forma siguiente:
+
+<tscreen><verb>
+void callback_func( GtkWidget *widget,
+ gpointer datos_respuesta );
+</verb></tscreen>
+
+Donde el primer argumento será un puntero al <em/widget/ que emitió la
+señal, y el segundo un puntero a los datos pasados a la función tal y
+como hemos visto en el último argumento a gtk_signal_connect().
+
+Conviene destacar que la declaración de la función de respuesta debe
+servir sólo como guía general, ya que algunas señales específicas
+pueden generar diferentes parámetros de llamada. Por ejemplo, la señal
+de GtkCList «select_row» proporciona los parámetros fila y columna.
+
+Otra llamada usada en el ejemplo del hola mundo es:
+
+<tscreen><verb>
+gint gtk_signal_connect_object( GtkObject *objeto,
+ gchar *nombre,
+ GtkSignalFunc func,
+ GtkObject *slot_object );
+</verb></tscreen>
+
+gtk_signal_connect_object() es idéntica a gtk_signal_connect() excepto
+en que la función de llamada sólo usa un argumento, un puntero a un
+objeto GTK. Por tanto cuando usemos esta función para conectar
+señales, la función de respuesta debe ser de la forma:
+
+<tscreen><verb>
+void callback_func( GtkObject *object );
+</verb></tscreen>
+
+Donde, por regla general, el objeto es un <em/widget/. Sin embargo no
+es normal establecer una respuesta para gtk_signal_connect_object. En
+lugar de ello llamamos a una función de GTK que acepte un <em/widget/
+o un objeto como un argumento, tal y como se vio en el ejemplo hola
+mundo.
+
+¿Para qué sirve tener dos funciones para conectar señales? Simplemente
+para permitir que las funciones de respuesta puedan tener un número
+diferente de argumentos. Muchas funciones de GTK sólo aceptan un
+puntero a un GtkWidget como argumento, por lo que tendrá que usar
+gtk_signal_connect_object() con estas funciones, mientras que
+probablemente tenga que suministrarle información adicional a sus
+funciones.
+
+<!-- XXX Completamente revisado hasta aquí -------------------------- -->
+<sect1>Eventos
+<p>
+Además del mecanismo de señales descrito arriba existe otro conjunto
+de <em>eventos</em> que reflejan como las X manejan los eventos. Se
+pueden asignar funciones de respuesta a estos eventos. Los eventos
+son:
+
+<itemize>
+<item> event
+<item> button_press_event
+<item> button_release_event
+<item> motion_notify_event
+<item> delete_event
+<item> destroy_event
+<item> expose_event
+<item> key_press_event
+<item> key_release_event
+<item> enter_notify_event
+<item> leave_notify_event
+<item> configure_event
+<item> focus_in_event
+<item> focus_out_event
+<item> map_event
+<item> unmap_event
+<item> property_notify_event
+<item> selection_clear_event
+<item> selection_request_event
+<item> selection_notify_event
+<item> proximity_in_event
+<item> proximity_out_event
+<item> drag_begin_event
+<item> drag_request_event
+<item> drag_end_event
+<item> drop_enter_event
+<item> drop_leave_event
+<item> drop_data_available_event
+<item> other_event
+</itemize>
+
+Para conectar una función de respuesta a alguno de los eventos
+anteriores debe usar la función gtk_signal_connect, tal y como se
+descrivió anteriormente, utilizando en el parámetro <tt/name/ uno de
+los nombres de los eventos que se acaban de mencionar. La función de
+respuesta para los eventos tiene un forma ligeramente diferente de la
+que tiene para las señales:
+
+<tscreen><verb>
+void callback_func( GtkWidget *widget,
+ GdkEvent *event,
+ gpointer callback_data );
+</verb></tscreen>
+
+GdkEvent es una estructura <tt/union/ cuyo tipo depende de cual de los
+eventos anteriores haya ocurrido. Para que podamos decir que evento se
+ha lanzado cada una de las posibles alternativas posee un parámetro
+<tt/type/ que refleja cual es el evento en cuestión. Los otros
+componentes de la estructura dependerán del tipo de evento. Algunos
+valores posibles son:
+
+<tscreen><verb>
+ GDK_NOTHING
+ GDK_DELETE
+ GDK_DESTROY
+ GDK_EXPOSE
+ GDK_MOTION_NOTIFY
+ GDK_BUTTON_PRESS
+ GDK_2BUTTON_PRESS
+ GDK_3BUTTON_PRESS
+ GDK_BUTTON_RELEASE
+ GDK_KEY_PRESS
+ GDK_KEY_RELEASE
+ GDK_ENTER_NOTIFY
+ GDK_LEAVE_NOTIFY
+ GDK_FOCUS_CHANGE
+ GDK_CONFIGURE
+ GDK_MAP
+ GDK_UNMAP
+ GDK_PROPERTY_NOTIFY
+ GDK_SELECTION_CLEAR
+ GDK_SELECTION_REQUEST
+ GDK_SELECTION_NOTIFY
+ GDK_PROXIMITY_IN
+ GDK_PROXIMITY_OUT
+ GDK_DRAG_BEGIN
+ GDK_DRAG_REQUEST
+ GDK_DROP_ENTER
+ GDK_DROP_LEAVE
+ GDK_DROP_DATA_AVAIL
+ GDK_CLIENT_EVENT
+ GDK_VISIBILITY_NOTIFY
+ GDK_NO_EXPOSE
+ GDK_OTHER_EVENT /* En desuso, usar filtros en lugar de ella */
+</verb></tscreen>
+
+Por lo tanto para conectar una función de respuesta a uno de estos
+eventos debemos usar algo como:
+
+<tscreen><verb>
+gtk_signal_connect( GTK_OBJECT(boton), "button_press_event",
+ GTK_SIGNAL_FUNC(button_press_callback),
+ NULL);
+</verb></tscreen>
+
+Por supuesto se asume que <tt/boton/ es un <em/widget/
+GtkButton. Cada vez que el puntero del ratón se encuentre sobre el
+botón y éste sea presionado, se llamará a la función
+<tt/button_press_callback/. Esta función puede declararse así:
+
+<tscreen><verb>
+static gint button_press_event (GtkWidget *widget,
+ GdkEventButton *event,
+ gpointer data);
+</verb></tscreen>
+
+Conviene destacar que se puede declarar el segundo argumento como
+<tt/GdkEventButton/ porque sabemos que este tipo de evento ocurrirá
+cuando se llame a la función.
+
+El valor devuelto por esta función es usado para saber si el evento
+debe ser propagado a un nivel más profundo dentro del mecanismo de
+GTK para gestionar los eventos. Si devuelve TRUE el evento ya ha sido
+gestionado y por tanto no tiene que ser tratado por el mecanismo de
+gestión. Por contra si devuelve FALSE se continua con la gestión
+normal del evento. Para más detalles se recomienda leer la sección
+donde se aclara como se produce el proceso de propagación.
+
+Para más detalles acerca de los tipos de información GdkEvent
+consultar el apéndice <ref id="sec_GDK_Event_Types" name="Tipos de
+eventos GDK">.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Aclaración de Hello World
+<p>
+Ahora que conocemos la teoría vamos a aclarar las ideas estudiando
+en detalle el programa <tt/helloworld/.
+
+Ésta es la función respuesta a la que se llamará cuando se
+pulse el botón. En el ejemplo ignoramos tanto el <em/widget/ como
+la información, pero no es difícil usarlos. El siguiente ejemplo
+usará la información que recibe como argumento para decirnos que
+botón fue presionado.
+
+<tscreen><verb>
+void hello (GtkWidget *widget, gpointer data)
+{
+ g_print ("Hello World\n");
+}
+</verb></tscreen>
+
+La siguiente respuesta es un poco especial, el «delete_event» ocurre
+cuando el gestor de ventanas envía este evento a la aplicación. Aquí
+podemos decidir que hacemos con estos eventos. Los podemos ignorar,
+dar algún tipo de respuesta, o simplemente terminar la aplicación.
+
+El valor devuelto en esta respuesta le permite a GTK saber que tiene
+que hacer. Si devolvemos TRUE, estamos diciendo que no queremos que se
+emita la señal «destroy» y por lo tanto queremos que nuestra
+aplicación siga ejecutándose. Si devolvemos FALSE, decimos que
+se emita «destroy», lo que hará que se ejecute nuestro manejador
+de señal de «destroy».
+
+<tscreen><verb>
+gint delete_event(GtkWidget *widget, GdkEvent *event, gpointer data)
+{
+ g_print ("delete event occured\n");
+
+ return (TRUE);
+}
+</verb></tscreen>
+
+Con el siguiente ejemplo presentamos otra función de respuesta que hace
+que el programa salga llamando a gtk_main_quit(). Con esta función le
+decimos a GTK que salga de la rutina gtk_main() cuando vuelva a estar
+en ella.
+
+<tscreen><verb>
+void destroy (GtkWidget *widget, gpointer data)
+{
+ gtk_main_quit ();
+}
+</verb></tscreen>
+
+Como el lector probablemente ya sabe toda aplicación debe tener una
+función main(), y una aplicación GTK no va a ser menos. Todas las
+aplicaciones GTK también tienen una función de este tipo.
+
+<tscreen><verb>
+int main (int argc, char *argv[])
+</verb></tscreen>
+
+Las líneas siguientes declaran un puntero a una estructura del tipo
+GtkWidget, que se utilizarán más adelante para crear una ventana y un
+botón.
+
+<tscreen><verb>
+ GtkWidget *ventana;
+ GtkWidget *boton;
+</verb></tscreen>
+
+Aquí tenemos otra vez a gtk_init. Como antes arranca el conjunto de
+herramientas y filtra las opciones introducidas en la línea de
+órdenes. Cualquier argumento que sea reconocido será borrado de la
+lista de argumentos, de modo que la aplicación recibirá el resto.
+
+<tscreen><verb>
+ gtk_init (&amp;argc, &amp;argv);
+</verb></tscreen>
+
+Ahora vamos a crear una ventana. Simplemente reservamos memoria para
+la estructura GtkWindow *ventana, con lo que ya tenemos una nueva
+ventana, ventana que no se mostrará hasta que llamemos a
+gtk_widget_show (ventana) hacia el final del programa.
+
+<tscreen><verb>
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+</verb></tscreen>
+
+Aquí tenemos un ejemplo de como conectar un manejador de señal a un
+objeto, en este caso, la ventana. La señal a cazar será
+«destroy». Esta señal se emite cuando utilizamos el administrador de
+ventanas para matar la ventana (y devolvemos TRUE en el manejador
+«delete_event»), o cuando usamos llamamos a gtk_widget_destroy()
+pasándole el <em/widget/ que representa la ventana como argumento.
+Así conseguimos manejar los dos casos con una simple llamada a la
+función destroy () (definida arriba) pasándole NULL como argumento y
+ella acabará con la aplicación por nosotros.
+
+GTK_OBJECT y GTK_SIGNAL_FUNC son macros que realizan la comprobación y
+transformación de tipos por nosotros. También aumentan la legibilidad
+del código.
+
+<tscreen><verb>
+ gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
+ GTK_SIGNAL_FUNC (destroy), NULL);
+</verb></tscreen>
+
+La siguiente función establece un atributo a un objeto contenedor
+(discutidos luego). En este caso le pone a la ventana un área
+negra de 10 <em/pixels/ de ancho donde no habrán <em/widgets/. Hay
+funciones similares que serán tratadas con más detalle en la sección
+<ref id="sec_setting_widget_attributes" name="Estableciendo los
+atributos de los <em/widgets/">
+
+De nuevo, GTK_CONTAINER es una macro que se encarga de la conversión
+entre tipos
+
+<tscreen><verb>
+ gtk_container_border_width (GTK_CONTAINER (ventana), 10);
+</verb></tscreen>
+
+La siguiente llamada crea un nuevo botón. Reserva espacio en la
+memoria para una nueva estructura del tipo GtkWidget, la inicializa
+y hace que el puntero <tt/boton/ apunte a esta estructura. Su etiqueta
+será: "Hola mundo".
+
+<tscreen><verb>
+ boton = gtk_button_new_with_label ("Hola mundo");
+</verb></tscreen>
+
+Ahora hacemos que el botón sea útil, para ello enlazamos el botón con
+el manejador de señales para que cuando emita la señal «clicked», se
+llame a nuestra función hola(). Los datos adicionales serán
+ignorados, por lo que simplemente le pasaremos NULL a la función
+respuesta. Obviamente se emitirá la señal «clicked» cuando pulsemos
+en el botón con el ratón.
+
+<tscreen><verb>
+ gtk_signal_connect (GTK_OBJECT (boton), "clicked",
+ GTK_SIGNAL_FUNC (hola), NULL);
+</verb></tscreen>
+XXX
+Ahora vamos a usar el botón para terminar nuestro programa. Así
+aclararemos cómo es posible que la señal «destroy» sea emitida tanto
+por el gestor de ventanas como por nuestro programa. Cuando el botón
+es pulsado, al igual que arriba, se llama a la primera función
+respuesta hello() y después se llamará a esta función. Las funciones
+respuesta serán ejecutadas en el orden en que sean conectadas. Como la
+función gtk_widget_destroy() sólo acepta un GtkWidget como argumento,
+utilizaremos gtk_signal_connect_object() en lugar de
+gtk_signal_connect().
+
+<tscreen><verb>
+gtk_signal_connect_object (GTK_OBJECT (boton), "clicked",
+ GTK_SIGNAL_FUNC (gtk_widget_destroy),
+ GTK_OBJECT (ventana));
+</verb></tscreen>
+
+La siguiente llamada sirve para empaquetar (más detalles luego). Se
+usa para decirle a GTK que el botón debe estar en la ventana dónde
+será mostrado. Conviene destacar que un contenedor GTK sólo puede
+contener un <em/widget/. Existen otros <em/widgets/ (descritos
+después) que sirven para contener y establecer la disposición de
+varios <em/widgets/ de diferentes formas.
+
+<tscreen><verb>
+ gtk_container_add (GTK_CONTAINER (ventana), boton);
+</verb></tscreen>
+
+Ahora ya tenemos todo bien organizado. Como todos los controladores de
+las señales ya están en su sitio, y el botón está situado en la
+ventana donde queremos que esté, sólo nos queda pedirle a GTK que
+muestre todos los <em/widgets/ en pantalla. El <em/widget/ ventana será
+el último en mostrarse queremos que aparezca todo de golpe, en vez de
+ver aparecer la ventana, y después ver aparecer el botón. De todas
+formas con un ejemplo tan simple nunca se notaría cual es el orden de
+aparición.
+
+<tscreen><verb>
+ gtk_widget_show (boton);
+
+ gtk_widget_show (ventana);
+</verb></tscreen>
+
+Llamamos a gtk_main() que espera hasta que el servidor X le comunique
+que se ha producido algún evento para emitir las señales apropiadas.
+
+<tscreen><verb>
+ gtk_main ();
+</verb></tscreen>
+
+Por último el `return' final que devuelve el control cuando gtk_quit()
+sea invocada.
+
+<tscreen><verb>
+ return 0;
+</verb></tscreen>
+
+Cuando pulsemos el botón del ratón el <em/widget/ emite la señal
+correspondiente «clicked». Para que podamos usar la información el
+programa activa el gestor de eventos que al recibir la señal llama a
+la función que hemos elegido. En nuestro ejemplo cuando pulsamos el
+botón se llama a la función hello() con NULL como argumento y además
+se invoca al siguiente manipulador de señal. Así conseguimos que se
+llame a la función gtk_widget_destroy() con el <em/widget/ asociado a
+la ventana como argumento, lo que destruye al <em/widget/. Esto hace
+que la ventana emita la señal «destroy», que es cazada, y que llama
+a nuestra función respuesta destroy(), que simplemente sale de GTK.
+
+Otra posibilidad es usar el gestor de ventanas para acabar con la
+aplicación. Esto emitirá «delete_event» que hará que se
+llame a nuestra función manejadora correspondiente. Si en la
+función manejadora «delete_event» devolvemos TRUE la ventana se
+quedará como si nada hubiese ocurrido, pero si devolvemos FALSE GTK
+emitirá la señal «destroy» que, por supuesto, llamará a la
+función respuesta «destroy», que saldrá de GTK.
+
+<!-- ***************************************************************** -->
+<sect>Avanzando
+<!-- ***************************************************************** -->
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Tipos de datos
+<p>
+Existen algunos detalles de los ejemplos anteriores que hay que aclarar.
+Los tipos gint, gchar, etc. que puede ver por ahí son typedefs a int y
+a char respectivamente. Sirven para que no haya que tener en cuenta el
+tamaño de cada uno de ellos a la hora de hacer cálculos.
+
+Un buen ejemplo es <tt/gint32/ que es un entero de 32 bits independientemente
+de la plataforma, bien sea un Alpha de 64 bits o un i386 de 32. Todas las
+definiciones son muy intuitivas y se encuentran definidas en glib/glib.h
+(que se incluye desde gtk.h).
+
+Probablemente el lector se haya dado cuenta de que se puede usar GtkWidget
+cuando la función llama a un GtkObject. Esto es debido a que GTK
+está orienta a objetos y un <em/widget/ es un GtkObject.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Más sobre el manejo de señales
+<p>
+Si estudiamos en mayor profundidad la declaración de
+gtk_signal_connect:
+
+<tscreen><verb>
+gint gtk_signal_connect( GtkObject *object,
+ gchar *name,
+ GtkSignalFunc func,
+ gpointer func_data );
+</verb></tscreen>
+
+Podemos darnos cuenta de que el valor devuelto es del tipo gint. Este
+valor es una etiqueta que identifica a la función de respuesta. Tal
+y como ya vimos podemos tener tantas funciones de respuesta por
+seÑal y objeto como sean necesarias, y cada una de ellas se
+ejecutará en el mismo orden en el que fueron enlazadas.
+
+Esta etiqueta nos permite eliminar la función respuesta de la lista
+usando:
+
+<tscreen><verb>
+void gtk_signal_disconnect( GtkObject *object,
+ gint id );
+</verb></tscreen>
+
+Por lo tanto podemos desconectar un manejador de señal pasándole
+a la función anterior el <em/widget/ del que queremos desconectar y
+la etiqueta o id devuelta por una de las funciones signal_connect.
+
+Otra función que se usa para quitar desconectar todos los
+controladores de un objeto es:
+
+<tscreen><verb>
+void gtk_signal_handlers_destroy( GtkObject *object );
+</verb></tscreen>
+
+Esta llamada es bastante auto explicativa. Simplemente quitamos todos los
+controladores de señales del objeto que pasamos como primer argumento.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Un Hello World mejorado.
+<p>
+Vamos a mejorar el ejemplo para obtener una visión más amplia
+sobre el manejo de señales y respuestas. También introduciremos
+los <em/widgets/ usados para empaquetar.
+
+<tscreen><verb>
+/* principio del ejemplo helloworld2 */
+
+#include <gtk/gtk.h>
+
+/* Nuestra respuesta mejorada. Los argumentos de la función se
+ * imprimen en el stdout.*/
+void callback (GtkWidget *widget, gpointer data)
+{
+ g_print ("Hello again - %s was pressed\n", (char *) data);
+}
+
+/* otra respuesta*/
+void delete_event (GtkWidget *widget, GdkEvent *event, gpointer data)
+{
+ gtk_main_quit ();
+}
+
+int main (int argc, char *argv[])
+{
+ /* GtkWidget es el tipo de almacenamiento usado para los wigtes*/
+ GtkWidget *ventana;
+ GtkWidget *boton;
+ GtkWidget *caja1;
+
+ /* Esta llamada está presente en todas las aplicaciones basadas
+ * en GTK. Los argumentos introducidos a la aplicación*/
+ gtk_init (&amp;argc, &amp;argv);
+
+ /* creamos una nueva ventana*/
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ /* Esta función es nueva, pone como título de la ventana
+ * "¡Hola botones!"*/
+
+ gtk_window_set_title (GTK_WINDOW (ventana), "¡Hola botones!");
+
+ /* Establecemos el controlador para la llamada delete_event que
+ * termina la aplicación inmediatamente. */
+
+ gtk_signal_connect (GTK_OBJECT (ventana), "delete_event",
+ GTK_SIGNAL_FUNC (delete_event), NULL);
+
+
+ /* Establecemos el ancho del borde de la ventana.*/
+ gtk_container_border_width (GTK_CONTAINER (ventana), 10);
+
+ /* Creamos una caja donde empaquetaremos los widgets. El
+ * procedimiento de empaquetamiento se describe en detalle en la
+ * sección correspondiente. La caja no se ve realmente, sólo
+ * sirve para introducir los widgets. */
+ caja1 = gtk_hbox_new(FALSE, 0);
+
+ /* ponemos la caja en la ventana principal */
+ gtk_container_add (GTK_CONTAINER (ventana), caja1);
+
+ /* Creamos un nuevo botón con la etiqueta "Botón 1". */
+ boton = gtk_button_new_with_label ("Botón 1");
+
+ /* Cada vez que el botón sea pulsado llamamos a la función
+ * "callback" con un puntero a "botón 1" como argumento. */
+ gtk_signal_connect (GTK_OBJECT (boton), "clicked",
+ GTK_SIGNAL_FUNC (callback), (gpointer) "botón 1");
+
+ /* En lugar de gtk_container_add empaquetamos el botón en la
+ * caja invisible, que a su vez ha sido empaquetado en la
+ * ventana. */
+ gtk_box_pack_start(GTK_BOX(caja1), boton, TRUE, TRUE, 0);
+
+ /* Siempre se debe realizar este paso. Sirve para decirle a GTK
+ * que los preparativos del botón ya se han finalizado y que
+ * por tanto puede ser mostrado. */
+ gtk_widget_show(boton);
+
+ /* hacemos lo mismo para crear un segundo botón. */
+ boton = gtk_button_new_with_label ("Botón 2");
+
+ /* Llamamos a la misma función de respuesta pero con diferente
+ * argumento: un puntero a "botón 2". */
+ gtk_signal_connect (GTK_OBJECT (boton), "clicked",
+ GTK_SIGNAL_FUNC (callback), (gpointer) "botón 2");
+
+ gtk_box_pack_start(GTK_BOX(caja1), boton, TRUE, TRUE, 0);
+
+ /* El orden en que mostramos los botones no es realmente
+ * importante, pero se recomienda mostrar la ventana la última
+ * para que todo aparezca de golpe. */
+ gtk_widget_show(boton);
+
+ gtk_widget_show(caja1);
+
+ gtk_widget_show (ventana);
+
+ /* Esperamos en gtk_main a que comience el espectáculo.*/
+ gtk_main ();
+
+ return 0;
+}
+/* final del ejemplo*/
+</verb></tscreen>
+
+Compile el programa usando los mismos argumentos que en el ejemplo
+anterior. Probablemente ya se habrá dado cuenta de que no hay una
+forma sencilla para terminar el programa, se debe usar el gestor de
+ventanas o la línea de comandos para ello. Un buen ejercicio para
+el lector es introducir un tercer botón que termine el
+programa. También puede resultar interesante probar las diferentes
+opciones de gtk_box_pack_start() mientras lee la siguiente
+sección. Intente cambiar el tamaño de la ventana y observe el
+comportamiento.
+
+Como última nota, existe otra definición bastante útil:
+gtk_widow_new() - GTK_WINDOW_DIALOG. Su comportamiento es un poco
+diferente y debe ser usado para ventanas intermedias (cuadros de
+diálogo).
+
+<!-- ***************************************************************** -->
+<sect><em/Widgets/ usados para empaquetar
+<!-- ***************************************************************** -->
+<p>
+Al crear una aplicación normalmente se quiere que haya más de un
+<em/widget/ por ventana. Nuestro primer ejemplo sólo usaba un
+<em/widget/ por lo que usábamos la función gtk_container_add
+para «empaquetar» el <em/widget/ en la ventana. Pero cuando cuando
+se quiere poner más de un <em/widget/ en una ventana, ¿Cómo
+podemos controlar donde aparecerá el <em/widget/?. Aquí es donde
+entra el empaquetamiento.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Empaquetamiento usando cajas
+<p>
+Normalmente para empaquetar se usan cajas, tal y como ya hemos
+visto. Éstas son <em/widgets/ invisibles que pueden contener
+nuestros <em/widgets/ de dos formas diferentes, horizontal o
+verticalmente. Al hacerlo de la primera forma los objetos son
+insertados de izquierda a derecha o al revés (dependiendo de que
+llamada se use). Lo mismo ocurre en los verticales (de arriba a bajo o
+al revés). Se pueden usar tantas cajas como se quieran para
+conseguir cualquier tipo de efecto.
+
+Para crear una caja horizontal llamamos a gtk_hbox_new() y para las
+verticales gtk_vbox_new(). Las funciones usadas para introducir
+objetos dentro son gtk_box_pack_start() y gtk_box_pack_end(). La
+primera llenará de arriba a abajo o de izquierda a derecha. La
+segunda lo hará al revés.
+Usando estas funciones podemos ir metiendo <em/widgets/ con una
+justificación a la izquierda o a la derecha y además podemos
+mezclarlas de cualquier manera para conseguir el efecto
+deseado. Nosotros usaremos gtk_box_pack_start() en la mayoria de
+nuestros ejemplos. Un objeto puede ser otro contenedor o un
+<em/widget/. De hecho, muchos <em/widgets/ son contenedores,
+incluyendo el <em/widget/ botón (button) (aunque normalmente lo
+único que meteremos dentro será una etiqueta de texto).
+
+Mediante el uso de estas funciones le decimos a GTK dónde queremos
+situar nuestros widgets, y GTK podrá, por ejemplo, cambiarles el
+tamaño de forma automática y hacer otras cosas de
+utilidad. También hay unas cuantas opciones que tienen que ver con
+la forma en la que los <em/widgets/ serán empaquetados. Como puede
+imaginarse, este método nos da una gran flexibilidad a la hora de
+colocar y crear <em/widgets/.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Detalles de la cajas.
+<p>
+Debido a esta flexibilidad el empaquetamiento puede ser confuso al
+principio. Hay muchas opciones y no es obvio como encajan unas con
+otras. Pero en la práctica sólo hay cinco estilos diferentes.
+
+<? <CENTER> >
+<?
+<IMG SRC="gtk_tut_packbox1.gif" VSPACE="15" HSPACE="10" WIDTH="528"
+HEIGHT="235" ALT="Imagen de ejemplo sobre el empaquetado con cajas">
+>
+<? </CENTER> >
+
+Cada línea contiene una caja horizontal (hbox) con diferentes
+botones. La llamada a gtk_box_pack es una manera de conseguir
+empaquetar cada uno de los botones dentro de la caja. Eso sí, cada uno
+de ellos se empaqueta de la misma forma que el resto (se llama con los
+mismos argumentos a gtk_box_pack_start()).
+
+Esta es la declaración de la función gtk_box_pack_start:
+
+<tscreen><verb>
+void gtk_box_pack_start( GtkBox *box,
+ GtkWidget *hijo,
+ gint expand,
+ gint fill,
+ gint padding );
+</verb></tscreen>
+
+El primer argumento es la caja dónde se empaqueta, el segundo el
+objeto. Por ahora el objeto será un botón, ya que estamos
+empaquetando botones dentro de las cajas.
+
+El argumento <tt/expand/ de gtk_box_pack_start() y de
+gtk_box_pack_end() controla si los <em/widgets/ son expandidos en la
+caja para rellenar todo el espacio de la misma (TRUE) o si por el
+contrario no se usa el espacio extra dentro de la caja
+(FALSE). Poniendo FALSE en <em/expand/ podremos hacer que nuestros
+<em/widgets/ tengan una justaficación a la derecha o a la
+izquierda. En caso contrario, los <em/widgets/ se expandirán para
+llenar toda la caja, y podemos conseguir el mismo efecto utilizando
+sólo una de las funciones gtk_box_pack_start o pack_end.
+
+El argumento <tt/fill/ de gtk_box controla si el espacio extra se mete
+dentro de los objetos (TRUE) o como relleno extra (FALSE). Sólo
+tiene efecto si el argumento de expansión también es TRUE.
+
+Al crear una nueva ventana la función debe ser parecida a esta:
+
+<tscreen><verb>
+GtkWidget *gtk_hbox_new (gint homogeneous,
+ gint spacing);
+</verb></tscreen>
+
+El argumento <tt/homogeneous/ (tanto para gtk_hbox_new como para
+gtk_vbox_new) controla si cada objeto en la caja tiene el mismo
+tamaño (anchura en una hbox o altura en una vbox). Si se activa, el
+argumento <tt/expand/ de las rutinas gtk_box_pack siempre estará
+activado.
+
+Puede que el lector se esté haciendo la siguiente pregunta:
+¿Cúal es la diferencia entre espaciar (establecido cuando se
+crea la caja) y rellenar (determinado cuando se empaquetan los
+elementos)? El espaciado se añade entre objetos, y el rellenado se
+hace en cada parte de cada objeto. La siguiente figura debe aclarar la
+cuestión.
+
+<? <CENTER> >
+<?
+<IMG ALIGN="center" SRC="gtk_tut_packbox2.gif" WIDTH="509" HEIGHT="213"
+VSPACE="15" HSPACE="10" ALT="Imagen de ejemplo sobre el empaquetado con cajas">
+>
+<? </CENTER> >
+
+Estudiemos el código usado para crear las imágenes
+anteriores. Con los comentarios no debería de haber ningún
+problema para entenderlo.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Programa demostración de empaquetamiento
+<p>
+<tscreen><verb>
+/* principio del ejemplo packbox packbox.c */
+
+#include <stdio.h>
+#include "gtk/gtk.h"
+
+void
+delete_event (GtkWidget *widget, GdkEvent *event, gpointer data)
+{
+ gtk_main_quit ();
+}
+
+/* Hacemos una hbox llena de etiquetas de botón. Los argumentos
+ * para las variables que estamos interesados son pasados a esta
+ * función. No mostramos la caja, pero hacemos todo lo que
+ * queremos. */
+GtkWidget *make_box (gint homogeneous, gint spacing,
+ gint expand, gint fill, gint padding)
+{
+ GtkWidget *box;
+ GtkWidget *boton;
+ char padstr[80];
+
+ /* creamos una nueva caja con los argumentos homogeneous y
+ * spacing */
+ box = gtk_hbox_new (homogeneous, spacing);
+
+ /* crear una serie de botones */
+ boton = gtk_button_new_with_label ("gtk_box_pack");
+ gtk_box_pack_start (GTK_BOX (box), boton, expand, fill, padding);
+ gtk_widget_show (boton);
+
+ boton = gtk_button_new_with_label ("(box,");
+ gtk_box_pack_start (GTK_BOX (box), boton, expand, fill, padding);
+ gtk_widget_show (boton);
+
+ boton = gtk_button_new_with_label ("boton,");
+ gtk_box_pack_start (GTK_BOX (box), boton, expand, fill, padding);
+ gtk_widget_show (boton);
+
+ /* Este botón llevará por etiqueta el valor de expand */
+ if (expand == TRUE)
+ boton = gtk_button_new_with_label ("TRUE,");
+ else
+ boton = gtk_button_new_with_label ("FALSE,");
+
+ gtk_box_pack_start (GTK_BOX (box), boton, expand, fill, padding);
+ gtk_widget_show (boton);
+
+ /* Este es el mismo caso que el de arriba, pero más compacto */
+ boton = gtk_button_new_with_label (fill ? "TRUE," : "FALSE,");
+ gtk_box_pack_start (GTK_BOX (box), boton, expand, fill, padding);
+ gtk_widget_show (boton);
+
+ sprintf (padstr, "%d);", padding);
+
+ boton = gtk_button_new_with_label (padstr);
+ gtk_box_pack_start (GTK_BOX (box), boton, expand, fill, padding);
+ gtk_widget_show (boton);
+
+ return box;
+}
+
+int
+main (int argc, char *argv[])
+{
+ GtkWidget *ventana;
+ GtkWidget *boton;
+ GtkWidget *caja1;
+ GtkWidget *caja2;
+ GtkWidget *separator;
+ GtkWidget *etiqueta;
+ GtkWidget *quitbox;
+ int which;
+
+ /* ¡No olvidar la siguiente llamada! */
+ gtk_init (&amp;argc, &amp;argv);
+
+ if (argc != 2) {
+ fprintf (stderr, "usage: packbox num, where num is 1, 2, or 3.\n");
+ /* hacemos limpieza en GTK y devolvemos el valor de 1 */
+ gtk_exit (1);
+ }
+
+ which = atoi (argv[1]);
+
+ /* Creamos la ventana */
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ /* Siempre hay que conectar la señal de destrucción con la
+ * ventana principal. Esto es muy importante para que el
+ * comportamiento de la ventana sea intuitivo. */
+ gtk_signal_connect (GTK_OBJECT (ventana), "delete_event",
+ GTK_SIGNAL_FUNC (delete_event), NULL);
+ gtk_container_border_width (GTK_CONTAINER (ventana), 10);
+
+ /* Creamos una caja vertical donde empaquetaremos las cajas
+ * horizontales. Así podemos apilar las cajas horizontales
+ * llenas con botones una encima de las otras. */
+ caja1 = gtk_vbox_new (FALSE, 0);
+
+ /* Aclaramos cúal es el ejemplo a mostrar. Se corresponde con
+ * las imágenes anteriores. */
+ switch (which) {
+ case 1:
+ /* creamos una nueva etiqueta. */
+ etiqueta = gtk_label_new ("gtk_hbox_new (FALSE, 0);");
+
+ /* Alineamos la etiqueta a la izquierda. Está función
+ * será discutida en detalle en la sección de los
+ * atributos de los widgets. */
+ gtk_misc_set_alignment (GTK_MISC (etiqueta), 0, 0);
+
+ /* Empaquetamos la etiqueta en la caja vertical (vbox
+ * caja1). Siempre hay que recordar que los widgets añadidos a
+ * una vbox serán empaquetados uno encimo de otro. */
+ gtk_box_pack_start (GTK_BOX (caja1), etiqueta, FALSE, FALSE, 0);
+
+ /* mostramos la etiqueta. */
+ gtk_widget_show (etiqueta);
+
+ /* llamada a la función que hace las cajas. Los argumentos
+ * son homogenous = FALSE, expand = FALSE, fill = FALSE,
+ * padding = 0 */
+ caja2 = make_box (FALSE, 0, FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (caja1), caja2, FALSE, FALSE, 0);
+ gtk_widget_show (caja2);
+
+ /* Llamad a la función para hacer cajas -
+ * homogeneous = FALSE, spacing = 0, expand = FALSE,
+ * fill = FALSE, padding = 0 */
+ caja2 = make_box (FALSE, 0, TRUE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (caja1), caja2, FALSE, FALSE, 0);
+ gtk_widget_show (caja2);
+
+ /* Los argumentos son: homogeneous, spacing, expand, fill,
+ * padding */
+ caja2 = make_box (FALSE, 0, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (caja1), caja2, FALSE, FALSE, 0);
+ gtk_widget_show (caja2);
+
+
+ /* creamos un separador. Más tarde aprenderemos más cosas
+ * sobre ellos, pero son bastante sencillos. */
+ separator = gtk_hseparator_new ();
+
+ /* empaquetamos el separador el la vbox. Los widgets serán
+ * apilados verticalmente. */
+ gtk_box_pack_start (GTK_BOX (caja1), separator, FALSE, TRUE, 5);
+ gtk_widget_show (separator);
+
+ /* creamos una nueva etiqueta y la mostramos */
+ etiqueta = gtk_label_new ("gtk_hbox_new (TRUE, 0);");
+ gtk_misc_set_alignment (GTK_MISC (etiqueta), 0, 0);
+ gtk_box_pack_start (GTK_BOX (caja1), etiqueta, FALSE, FALSE, 0);
+ gtk_widget_show (etiqueta);
+
+ /* Los argumentos son: homogeneous, spacing, expand, fill,
+ * padding */
+ caja2 = make_box (TRUE, 0, TRUE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (caja1), caja2, FALSE, FALSE, 0);
+ gtk_widget_show (caja2);
+
+ /* Los argumentos son: homogeneous, spacing, expand, fill,
+ * padding */
+ caja2 = make_box (TRUE, 0, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (caja1), caja2, FALSE, FALSE, 0);
+ gtk_widget_show (caja2);
+
+ /* un nuevo separador */
+ separator = gtk_hseparator_new ();
+ /* Los tres últimos argumentos son: expand, fill, padding. */
+ gtk_box_pack_start (GTK_BOX (caja1), separator, FALSE, TRUE, 5);
+ gtk_widget_show (separator);
+
+ break;
+
+ case 2:
+
+ /* Nueva etiqueta */
+ etiqueta = gtk_label_new ("gtk_hbox_new (FALSE, 10);");
+ gtk_misc_set_alignment (GTK_MISC (etiqueta), 0, 0);
+ gtk_box_pack_start (GTK_BOX (caja1), etiqueta, FALSE, FALSE, 0);
+ gtk_widget_show (etiqueta);
+
+ /* Los argumentos son: homogeneous, spacing, expand, fill,
+ * padding */
+ caja2 = make_box (FALSE, 10, TRUE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (caja1), caja2, FALSE, FALSE, 0);
+ gtk_widget_show (caja2);
+
+ /* Los argumentos son: homogeneous, spacing, expand, fill,
+ * padding */
+ caja2 = make_box (FALSE, 10, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (caja1), caja2, FALSE, FALSE, 0);
+ gtk_widget_show (caja2);
+
+ separator = gtk_hseparator_new ();
+ /* Los argumentos son: expand, fill, padding. */
+ gtk_box_pack_start (GTK_BOX (caja1), separator, FALSE, TRUE, 5);
+ gtk_widget_show (separator);
+
+ etiqueta = gtk_label_new ("gtk_hbox_new (FALSE, 0);");
+ gtk_misc_set_alignment (GTK_MISC (etiqueta), 0, 0);
+ gtk_box_pack_start (GTK_BOX (caja1), etiqueta, FALSE, FALSE, 0);
+ gtk_widget_show (etiqueta);
+
+ /* Los argumentos son: homogeneous, spacing, expand, fill,
+ * padding */
+ caja2 = make_box (FALSE, 0, TRUE, FALSE, 10);
+ gtk_box_pack_start (GTK_BOX (caja1), caja2, FALSE, FALSE, 0);
+ gtk_widget_show (caja2);
+
+ /* Los argumentos son: homogeneous, spacing, expand, fill,
+ * padding */
+ caja2 = make_box (FALSE, 0, TRUE, TRUE, 10);
+ gtk_box_pack_start (GTK_BOX (caja1), caja2, FALSE, FALSE, 0);
+ gtk_widget_show (caja2);
+
+ separator = gtk_hseparator_new ();
+ /* Los argumentos son: expand, fill, padding. */
+ gtk_box_pack_start (GTK_BOX (caja1), separator, FALSE, TRUE, 5);
+ gtk_widget_show (separator);
+ break;
+
+ case 3:
+
+ /* Con esto demostramos como hay que usar gtk_box_pack_end ()
+ * para conseguir que los
+ widgets esten alineados a la izquierda. */
+ caja2 = make_box (FALSE, 0, FALSE, FALSE, 0);
+
+ /* la última etiqueta*/
+ etiqueta = gtk_label_new ("end");
+
+ /* la empaquetamos usando gtk_box_pack_end(), por lo que se
+ * sitúa en el lado derecho de la hbox.*/
+ gtk_box_pack_end (GTK_BOX (caja2), etiqueta, FALSE, FALSE, 0);
+
+ /* mostrar la etiqueta */
+ gtk_widget_show (etiqueta);
+
+
+ /* empaquetamos caja2 en caja1 */
+ gtk_box_pack_start (GTK_BOX (caja1), caja2, FALSE, FALSE, 0);
+ gtk_widget_show (caja2);
+
+
+ /* el separador para la parte de abajo. */
+ separator = gtk_hseparator_new ();
+
+ /* Así se determina el tamaño del separador a 400 pixels
+ * de largo por 5 de alto. La hbox también tendrá 400
+ * pixels de largo y la etiqueta "end" estará separada de
+ * las demás etiquetas en la hbox. Si no establecemos estos
+ * parámetros todos los widgets en la hbox serán
+ * empaquetados tan juntos como se pueda.*/
+ gtk_widget_set_usize (separator, 400, 5);
+
+ /* Empaquetamos el separador creado al principio de main() en
+ * la vbox (caja1). */
+ gtk_box_pack_start (GTK_BOX (caja1), separator, FALSE, TRUE, 5);
+ gtk_widget_show (separator);
+ }
+
+ /* Creamos otra hbox... recordar que podemos crear tantas como
+ * queramos. */
+ quitbox = gtk_hbox_new (FALSE, 0);
+
+ /* El botón de salida. */
+ boton = gtk_button_new_with_label ("Quit");
+
+ /* Establecemos la señal de destrucción de la ventana.
+ * Recuerde que emitirá la señal de "destroy" que a su vez
+ * será procesada por el controlador de señales, tal y como
+ * ya hemos visto. */
+
+ gtk_signal_connect_object (GTK_OBJECT (boton), "clicked",
+ GTK_SIGNAL_FUNC (gtk_main_quit),
+ GTK_OBJECT (ventana));
+ /* Empaquetamos el botón en la caja de salida (quitbox).
+ * los tres últimos argumentos de gtk_box_pack_start
+ * son:expand, fill, padding. */
+ gtk_box_pack_start (GTK_BOX (caja1), quitbox, FALSE, FALSE, 0);
+
+ /* empaquetamos la vbox (caja1) que ya contiene todos los widgets
+ * en la ventana principal. */
+ gtk_container_add (GTK_CONTAINER (ventana), caja1);
+
+ /* mostramos todo aquello que faltaba por mostrar */
+ gtk_widget_show (boton);
+ gtk_widget_show (quitbox);
+
+ gtk_widget_show (caja1);
+
+ /* Si mostramos la ventana lo último todo aparece de golpe. */
+ gtk_widget_show (ventana);
+
+ /* por supuesto tenemos una función main. */
+ gtk_main ();
+
+ /* El programa llega aquí cuando se llama a gtk_main_quit(),
+ * pero no cuando se llama a gtk_exit(). */
+ return 0;
+}
+/* final del ejemplo*/
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Empaquetamiento usando tablas
+<p>
+Existe otra forma de empaquetar: usando tablas. Estas pueden llegar a
+ser extremadamente útiles.
+
+Usando tablas creamos una cuadrícula donde podemos poner los
+widgets. Estos pueden ocupar tanto espacio como queramos.
+
+La primera función que conviene estudiar es gtk_table_new:
+
+<tscreen><verb>
+GtkWidget *gtk_table_new( gint rows,
+ gint columns,
+ gint homogeneous );
+</verb></tscreen>
+
+Como es lógico el primer argumento es el número de filas y el
+segundo el de columnas.
+
+El tercero establece el tamaño de las celdas de la tabla. Si es TRUE
+se fuerza a que el tamaño de las celdas sea igual al de la celda
+mayor. Con FALSE se establece el ancho de toda una columna igual al de
+la celda más ancha de esa columna, y la altura de una fila será
+la de la celda más alta de esa fila.
+
+El número de filas y columnas varía entre 0 y n, donde n es el
+número especificado en la llamada a gtk_table_new. Así si se
+especifica columnas = 2 y filas = 2 la apariencia será parecida a:
+
+<tscreen><verb>
+ 0 1 2
+0+----------+----------+
+ | | |
+1+----------+----------+
+ | | |
+2+----------+----------+
+</verb></tscreen>
+
+Conviene destacar que el origen de coordenadas se sitúa en la esquina superior izquierda. Para
+situar un widget en una ventana se usa la siguiente función:
+
+<tscreen><verb>
+void gtk_table_attach( GtkTable *table,
+ GtkWidget *hijo,
+ gint left_attach,
+ gint right_attach,
+ gint top_attach,
+ gint bottom_attach,
+ gint xoptions,
+ gint yoptions,
+ gint xpadding,
+ gint ypadding );
+</verb></tscreen>
+
+El primer argumento (<tt/table/) es el nombre de la tabla y el segundo
+(<tt/hijo/) el <em/widget/ que quiere poner en la tabla.
+
+Los argumentos <tt/left_attach/, <tt/right_attach/ especifican donde
+se pone el widget y cuantas cajas se usan. Por ejemplo, supongamos que
+queremos poner un botón que sólo ocupe la esquina inferior
+izquierda en nuestra tabla 2x2. Los valores serán left_attach = 1,
+right_attach = 2, top_attach = 2, top_attach = 1, bottom_attach = 2.
+
+Supongamos que queremos ocupar toda la fila de nuestra tabla 2x2,
+usaríamos left_attach = 0, right_attach = 2, top_attach = 0,
+bottom_attach = 1.
+
+Las opciones <tt/xoptions/ e <tt/yoptions/ son usadas para especificar
+como queremos el empaquetamiento y podemos utilizar multiples
+opciones simultaneamente con OR.
+
+Las opciones son:
+
+<itemize>
+<item>GTK_FILL - Si el relleno es más grande que el widget, y se
+especifica GTK_FILL, el <em/widget/ se expandirá ocupando todo el
+espacio disponible.
+
+<item>GTK_SHRINK - En el caso de que hayamos dejado espacio sin usar
+cuando el usuario reajuste el tamaño de la ventana los <em/widgets/
+normalmente serán empujados al fondo de la ventana y
+desaparecerán. Si especifica GTK_SHRINK los widgets se reducirán
+con la tabla.
+
+<item>GTK_EXPAND - Mediante esta opción la tabla se expande usando
+todo el espacio libre de la ventana.
+</itemize>
+
+El relleno es igual que con las cajas. Simplemente se crea una zona
+vacía alrededor del widget (el tamaño se especifica en pixels).
+
+gtk_table_attach() tiene MUCHAS opciones. Asi que hay un atajo:
+
+<tscreen><verb>
+void gtk_table_attach_defaults( GtkTable *table,
+ GtkWidget *widget,
+ gint left_attach,
+ gint right_attach,
+ gint top_attach,
+ gint bottom_attach );
+</verb></tscreen>
+
+Las opciones X e Y se ponen por defecto a GTK_FILL | GTK_EXPAND, y el
+relleno X e Y se pone a 0. El resto de los argumentos son identicos a
+la función anterior.
+
+Existen otras funciones como gtk_table_set_row_spacing() y
+gtk_table_set_col_spacing(), que sirven para especificar el espaciado
+entre las columnas/filas en la columna/fila que queramos.
+
+<tscreen><verb>
+void gtk_table_set_row_spacing( GtkTable *table,
+ gint row,
+ gint spacing );
+</verb></tscreen>
+
+y
+
+<tscreen><verb>
+void gtk_table_set_col_spacing ( GtkTable *table,
+ gint column,
+ gint spacing );
+</verb></tscreen>
+
+Conviene destacar que el espaciado se sitúa a la derecha de la
+columna y debajo de la fila.
+
+Tambien se puede forzar que el espaciado sea el mismo para las filas
+y/o las columnas:
+
+<tscreen><verb>
+void gtk_table_set_row_spacings( GtkTable *table,
+ gint spacing );
+</verb></tscreen>
+
+y
+
+<tscreen><verb>
+void gtk_table_set_col_spacings( GtkTable *table,
+ gint spacing );
+</verb></tscreen>
+
+Usando estas funciones las últimas fila y columna no estarán
+espaciadas.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Ejemplo de empaquetamiento mediante tablas.
+<p>
+Haremos una ventana con tres botones en una tabla 2x2. Los dos
+primeros botones ocuparán la fila de arriba, mientras que el
+tercero (de salida) ocupará toda la fila de abajo. El resultado es
+el siguiente:
+
+<? <CENTER> >
+<?
+<IMG SRC="gtk_tut_table.gif" VSPACE="15" HSPACE="10"
+ALT="Imagen de ejemplo sobre el empaquetado mediante tablas" WIDTH="180" HEIGHT="120">
+>
+<? </CENTER> >
+
+Este es el código:
+<tscreen><verb>
+/* principio del ejemplo table table.c */
+
+#include <gtk/gtk.h>
+
+/* La respuesta, que además se imprime en stdout. */
+void callback (GtkWidget *widget, gpointer data)
+{
+ g_print ("Hello again - %s was pressed\n", (char *) data);
+}
+
+/* Con esta otra respuesta terminamos el programa. */
+void delete_event (GtkWidget *widget, GdkEvent *event, gpointer data)
+{
+ gtk_main_quit ();
+}
+
+int main (int argc, char *argv[])
+{
+ GtkWidget *ventana;
+ GtkWidget *boton;
+ GtkWidget *table;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ gtk_window_set_title (GTK_WINDOW (ventana), "Table");
+
+ gtk_signal_connect (GTK_OBJECT (ventana), "delete_event",
+ GTK_SIGNAL_FUNC (delete_event), NULL);
+
+ gtk_container_border_width (GTK_CONTAINER (ventana), 20);
+
+ table = gtk_table_new (2, 2, TRUE);
+
+ gtk_container_add (GTK_CONTAINER (ventana), table);
+
+ boton = gtk_button_new_with_label ("botón 1");
+
+ gtk_signal_connect (GTK_OBJECT (boton), "clicked",
+ GTK_SIGNAL_FUNC (callback), (gpointer) "botón 1");
+
+ gtk_table_attach_defaults (GTK_TABLE(table), boton, 0, 1, 0, 1);
+
+ gtk_widget_show (boton);
+
+ boton = gtk_button_new_with_label ("botón 2");
+
+ gtk_signal_connect (GTK_OBJECT (boton), "clicked",
+ GTK_SIGNAL_FUNC (callback), (gpointer) "botón 2");
+ gtk_table_attach_defaults (GTK_TABLE(table), boton, 1, 2, 0, 1);
+
+ gtk_widget_show (boton);
+
+ boton = gtk_button_new_with_label ("Quit");
+
+ gtk_signal_connect (GTK_OBJECT (boton), "clicked",
+ GTK_SIGNAL_FUNC (delete_event), NULL);
+ gtk_table_attach_defaults (GTK_TABLE(table), boton, 0, 2, 1, 2);
+
+ gtk_widget_show (boton);
+
+ gtk_widget_show (table);
+ gtk_widget_show (ventana);
+
+ gtk_main ();
+
+ return 0;
+}
+/* final del ejemplo */
+</verb></tscreen>
+
+<!-- ***************************************************************** -->
+<sect>Estudio general de los <em/widgets/
+<!-- ***************************************************************** -->
+<p>
+Los pasos generales a la hora de crear un <em/widget/ son:
+
+<enum>
+
+<item> Usar gtk_*_new - Una de las diferentes formas de crear un
+<em/widget/. (Todas serán explicadas en esta sección).
+
+<item> Connectar todas las señales y los eventos a los
+controladores apropiados.
+
+<item> Establecer los atributos del <em/widget/.
+
+<item> Empaquetar el <em/widget/ en un contenedor usando las llamadas
+apropiadas, como gtk_container_add() o gtk_box_pack_start().
+
+<item> Mostrar el <em/widget/ usando gtk_widget_show().
+
+</enum>
+
+Mediante esta última llamada GTK `sabe' que hemos acabado de
+establecer los atributos del <em/widget/, y que por lo tanto puede
+mostrarse. Se puede usar gtk_widget_hide para hacer que desaparezca.
+El orden en el que se muestran los <em/widgets/ no es importante, pero
+se recomienda mostrar al final la ventana para que todo aparezca de
+golpe. El hijo de un <em/widget/ no se muestra hasta que lo hace la
+propia ventana (que en este caso es un <em/widget/ padre) mediante
+gtk_widget_show().
+
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Conversión de tipos
+<p>
+GTK usa un sistema de conversión de tipos mediante macros que
+comprueban si se puede realizar la conversión y en caso
+afirmativo la hacen. Las más comunes son:
+
+<itemize>
+<item> GTK_WIDGET(widget)
+<item> GTK_OBJECT(object)
+<item> GTK_SIGNAL_FUNC(function)
+<item> GTK_CONTAINER(container)
+<item> GTK_WINDOW(ventana)
+<item> GTK_BOX(box)
+</itemize>
+
+Todas son usadas para cambiar de tipo los argumentos de una función.
+Aparecerán mucho en los ejemplos, para usarlas sólo hay que mirar la
+declaración de la función.
+
+Tal y como se puede ver en el árbol de clases (situado un poco
+más adelante) todos los <em/widgets/ derivan de la clase base
+GtkObject. Esto significa que siempre se puede usar un <em/widget/
+como argumento de una función (que acepte un objeto, claro)
+realizando la conversión de tipo GTK_OBJECT().
+
+Por ejemplo:
+
+<tscreen><verb>
+gtk_signal_connect( GTK_OBJECT(boton), "clicked",
+ GTK_SIGNAL_FUNC(callback_function), callback_data);
+</verb></tscreen>
+
+Hemos hecho que el botón pase a ser un objeto y que se cambie el
+puntero a la función a una función respuesta.
+
+Muchos <em/widgets/ son contenedores, por lo que unos pueden derivar
+de otros (la mayoría lo hace de GtkContainer). Cualquiera puede ser
+usado junto con la macro GTK_CONTAINER como argumento a funciones en
+forma de puntero.
+
+Desgraciadamente estas macros no son descritas en detalle en el
+tutorial, por lo que se recomienda echar un vistazo a los archivos de
+cabecera de GTK. En la práctica es posible aprender a manejar un
+<em/widget/ leyendo las declaraciones de las funciones.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Árbol formado por los <em/widgets/
+<p>
+A continuación se detallan todas las ramas del árbol que forman
+los <em/widgets/.
+
+<tscreen><verb>
+ GtkObject
+ +GtkWidget
+ | +GtkMisc
+ | | +GtkLabel
+ | | | +GtkAccelLabel
+ | | | `GtkTipsQuery
+ | | +GtkArrow
+ | | +GtkImage
+ | | `GtkPixmap
+ | +GtkContainer
+ | | +GtkBin
+ | | | +GtkAlignment
+ | | | +GtkFrame
+ | | | | `GtkAspectFrame
+ | | | +GtkButton
+ | | | | +GtkToggleButton
+ | | | | | `GtkCheckButton
+ | | | | | `GtkRadioButton
+ | | | | `GtkOptionMenu
+ | | | +GtkItem
+ | | | | +GtkMenuItem
+ | | | | | +GtkCheckMenuItem
+ | | | | | | `GtkRadioMenuItem
+ | | | | | `GtkTearoffMenuItem
+ | | | | +GtkListItem
+ | | | | `GtkTreeItem
+ | | | +GtkWindow
+ | | | | +GtkColorSelectionDialog
+ | | | | +GtkDialog
+ | | | | | `GtkInputDialog
+ | | | | +GtkDrawWindow
+ | | | | +GtkFileSelection
+ | | | | +GtkFontSelectionDialog
+ | | | | `GtkPlug
+ | | | +GtkEventBox
+ | | | +GtkHandleBox
+ | | | +GtkScrolledWindow
+ | | | `GtkViewport
+ | | +GtkBox
+ | | | +GtkButtonBox
+ | | | | +GtkHButtonBox
+ | | | | `GtkVButtonBox
+ | | | +GtkVBox
+ | | | | +GtkColorSelection
+ | | | | `GtkGammaCurve
+ | | | `GtkHBox
+ | | | +GtkCombo
+ | | | `GtkStatusbar
+ | | +GtkCList
+ | | | `GtkCTree
+ | | +GtkFixed
+ | | +GtkNotebook
+ | | | `GtkFontSelection
+ | | +GtkPaned
+ | | | +GtkHPaned
+ | | | `GtkVPaned
+ | | +GtkLayout
+ | | +GtkList
+ | | +GtkMenuShell
+ | | | +GtkMenuBar
+ | | | `GtkMenu
+ | | +GtkPacker
+ | | +GtkSocket
+ | | +GtkTable
+ | | +GtkToolbar
+ | | `GtkTree
+ | +GtkCalendar
+ | +GtkDrawingArea
+ | | `GtkCurve
+ | +GtkEditable
+ | | +GtkEntry
+ | | | `GtkSpinButton
+ | | `GtkText
+ | +GtkRuler
+ | | +GtkHRuler
+ | | `GtkVRuler
+ | +GtkRange
+ | | +GtkScale
+ | | | +GtkHScale
+ | | | `GtkVScale
+ | | `GtkScrollbar
+ | | +GtkHScrollbar
+ | | `GtkVScrollbar
+ | +GtkSeparator
+ | | +GtkHSeparator
+ | | `GtkVSeparator
+ | +GtkPreview
+ | `GtkProgress
+ | `GtkProgressBar
+ +GtkData
+ | +GtkAdjustment
+ | `GtkTooltips
+ `GtkItemFactory
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1><em/Widgets/ sin ventanas
+<p>
+Los siguientes <em/widgets/ no tienen ventanas asociadas. Si se
+quieren capturar eventos se tendrá que utilizar GtkEventBox. En la
+sección <ref id="sec_The_EventBox_Widget" name="El widget
+EventBox"> se pueden encontrar más detalles sobre su uso.
+
+<tscreen><verb>
+GtkAlignment
+GtkArrow
+GtkBin
+GtkBox
+GtkImage
+GtkItem
+GtkLabel
+GtkPixmap
+GtkScrolledWindow
+GtkSeparator
+GtkTable
+GtkAspectFrame
+GtkFrame
+GtkVBox
+GtkHBox
+GtkVSeparator
+GtkHSeparator
+</verb></tscreen>
+
+Vamos a continuar la explicación describiendo cada uno de los
+<em/widgets/ mediante ejemplos. También se puede consultar el
+programa testgtk.c (Se encuentra en gtk/testgtk.c).
+
+<!-- ***************************************************************** -->
+<sect>El <em/widget/ Botón
+<!-- ***************************************************************** -->
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Botones normales <label id="sec_Radio_Buttons">
+<p>
+Ya hemos visto prácticamente todo lo que hay que saber a cerca de
+este <em/widget/. Existen dos formas diferentes de crear un
+botón. Se puede usar gtk_button_new_with_label() para conseguir un
+botón con etiqueta o simplemente gtk_button_new(). Si se quiere se
+puede añadir una etiqueta a este último empaquetándola,
+primero se crea una nueva caja y luego se empaquetan los objetos que
+se quieran mediante gtk_box_pack_start. Una vez finalizado esto se
+relaciona la caja con el botón mediante gtk_container_add.
+
+Estudiemos un ejemplo de gtk_button_new para crear un botón con una
+imagen y una etiqueta. El código está dividido en dos para que
+pueda ser reusado.
+
+<tscreen><verb>
+/* principio del ejemplo buttons buttons.c */
+
+#include <gtk/gtk.h>
+
+/* Creamos la caja con una imagen y una etiqueta empaquetadas. Se
+ * devuelve la caja. */
+GtkWidget *xpm_label_box (GtkWidget *parent, gchar *xpm_filename, gchar *label_text)
+{
+ GtkWidget *caja1;
+ GtkWidget *etiqueta;
+ GtkWidget *pixmapwid;
+ GdkPixmap *pixmap;
+ GdkBitmap *mask;
+ GtkStyle *style;
+
+ /* create box for xpm and etiqueta */
+ caja1 = gtk_hbox_new (FALSE, 0);
+ gtk_container_border_width (GTK_CONTAINER (caja1), 2);
+
+ /* obtenemos el estilo del botón (probablemente para el color
+ * de fondo, pero no estoy seguro) */
+ style = gtk_widget_get_style(parent);
+
+ /* cargamos el pixmap. Hay una sección que describe el proceso
+ * en detalle */
+ pixmap = gdk_pixmap_create_from_xpm (parent->window, &amp;mask,
+ &amp;style->bg[GTK_STATE_NORMAL],
+ xpm_filename);
+ pixmapwid = gtk_pixmap_new (pixmap, mask);
+
+ etiqueta = gtk_label_new (label_text);
+
+ gtk_box_pack_start (GTK_BOX (caja1),
+ pixmapwid, FALSE, FALSE, 3);
+
+ gtk_box_pack_start (GTK_BOX (caja1), etiqueta, FALSE, FALSE, 3);
+
+ gtk_widget_show(pixmapwid);
+ gtk_widget_show(etiqueta);
+
+ return (caja1);
+}
+
+/* respuesta */
+void callback (GtkWidget *widget, gpointer data)
+{
+ g_print ("Hola de nuevo. Se ha pulsado %s\n", (char *) data);
+}
+
+
+int main (int argc, char *argv[])
+{
+
+ GtkWidget *ventana;
+ GtkWidget *boton;
+ GtkWidget *caja1;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ gtk_window_set_title (GTK_WINDOW (ventana), "Botones con dibujos");
+
+ /* It's a good idea to do this for all windows. */
+ gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
+ GTK_SIGNAL_FUNC (gtk_exit), NULL);
+
+ gtk_signal_connect (GTK_OBJECT (ventana), "delete_event",
+ GTK_SIGNAL_FUNC (gtk_exit), NULL);
+
+ gtk_container_border_width (GTK_CONTAINER (ventana), 10);
+ gtk_widget_realize(ventana);
+
+ boton = gtk_button_new ();
+
+ gtk_signal_connect (GTK_OBJECT (boton), "clicked",
+ GTK_SIGNAL_FUNC (callback),
+ (gpointer) "botón divertido");
+
+ caja1 = xpm_label_box(ventana, "info.xpm", "botón divertido");
+
+ gtk_widget_show(caja1);
+
+ gtk_container_add (GTK_CONTAINER (boton), caja1);
+
+ gtk_widget_show(boton);
+
+ gtk_container_add (GTK_CONTAINER (ventana), boton);
+
+ gtk_widget_show (ventana);
+
+ gtk_main ();
+
+ return 0;
+}
+/* final del ejemplo */
+</verb></tscreen>
+
+La función xpm_label_box puede ser usada para empaquetar xpm y
+etiquetas en cualquier widget que pueda ser un contenedor.
+
+El botón puede responder a las siguientes señales:
+
+<itemize>
+<item> pressed
+<item> released
+<item> clicked
+<item> enter
+<item> leave
+</itemize>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Botones de selección
+<p>
+Estos botones son muy similares a los normales. La única diferencia
+es que sólo pueden estar en dos posiciones diferentes alternadas
+mediante pulsaciones del ratón.
+
+Los botones de selección son la base de otros tipos: los de
+comprobación y los circulares. Por lo tanto muchas de sus llamadas
+seran heredadas por estos.
+
+Creamos un nuevo botón de selección:
+
+<tscreen><verb>
+GtkWidget *gtk_toggle_button_new( void );
+
+GtkWidget *gtk_toggle_button_new_with_label( gchar *etiqueta );
+</verb></tscreen>
+
+Como se ha podido imaginar estas funciones son iguales a las de un
+botón normal. La primera crea un botón, mientras que la segunda
+crea un botón con una etiqueta.
+
+Para saber cual es el estado de un botón de selección,
+comprobación o circular se usa una de las macros del ejemplo
+siguiente. En éstas se comprueba el estado del botón mediante
+una respuesta. La señal que queremos recibir es
+«toggled». Generalmente para comprobar el estado de una señal se
+establece un controlador de señales y luego se usa la siguiente
+macro. La función de respuesta debe ser de la forma:
+
+<tscreen><verb>
+void toggle_button_callback (GtkWidget *widget, gpointer data)
+{
+ if (GTK_TOGGLE_BUTTON (widget)->active)
+ {
+ /* Si el control llega aquí el botón está pulsado */
+
+ } else {
+
+ /* El botón no está pulsado (sobresale) */
+ }
+}
+</verb></tscreen>
+
+<tscreen><verb>
+void gtk_toggle_button_set_state( GtkToggleButton *toggle_button,
+ gint state );
+</verb></tscreen>
+
+La llamada de arriba puede ser usada para establecer el estado de un
+botón de selección (o de cualquiera de sus hijos: el circular o
+el de comprobación). El primer argumento es el botón, el segundo
+TRUE cuando queremos que el botón no esté pulsado o FALSE para
+cuando lo esté. Por defecto se establece FALSE.
+
+Hay que destacar que cuando se usa gtk_toggle_button_set_state() y se
+cambia el estado del botón este emite la señal «clicked».
+
+<tscreen><verb>
+void gtk_toggle_button_toggled (GtkToggleButton *toggle_button);
+</verb></tscreen>
+
+Cambia el estado del botón emitiendo la señal «toggled».
+<!-- ----------------------------------------------------------------- -->
+<sect1> Botones de comprobación
+<p>
+Los botones de comprobación son un poco diferentes a los anteriores, aunque
+sus propiedades y funciones son bastante similares. En lugar de ser botones
+con texto en su interior son pequeños cuadrados con texto a su derecha.
+Normalmente son usados para (des)seleccionar opciones.
+
+Las dos funciones que los crean son muy similares a las de los botones
+normales.
+
+<tscreen><verb>
+GtkWidget *gtk_check_button_new( void );
+
+GtkWidget *gtk_check_button_new_with_label ( gchar *etiqueta );
+</verb></tscreen>
+
+La función new_with_label crea un botón de comprobación con
+una etiqueta dentro.
+
+El proceso para comprobar el estado de un botón de este tipo es
+igual al de los de comprobación.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Botones circulares
+<p>
+Estos botones son similares a los de selección con la salvedad de
+que están agrupados, de modo que sólo uno puede estar
+seleccionado. Por tanto son usados para permitir al usuario
+seleccionar algo de una lista de opciones mutuamente excluyentes.
+
+Las llamadas para crear un botón circular son:
+<tscreen><verb>
+GtkWidget *gtk_radio_button_new( GSList *group );
+
+GtkWidget *gtk_radio_button_new_with_label( GSList *group,
+ gchar *etiqueta );
+</verb></tscreen>
+
+El nuevo argumento sirve para especificar el grupo al que
+pertenecen. La primera llamada debe pasar NULL como primer
+argumento. A continuación de ésta se puede crear el grupo
+usando:
+
+<tscreen><verb>
+GSList *gtk_radio_button_group( GtkRadioButton *radio_button );
+</verb></tscreen>
+
+Para añadir un nuevo botón a un grupo hay que usar
+gtk_radio_button_group con el anterior botón como argumento. El
+resultado se le pasa a gtk_radio_button_new o a
+gtk_radio_button_new_with_label. Así se consigue enlazar una cadena
+de botones. (El ejemplo siguiente sirve para aclarar el proceso)
+
+También se puede establecer cúal es el botón pulsado por
+defecto:
+
+<tscreen><verb>
+void gtk_toggle_button_set_state( GtkToggleButton *toggle_button,
+ gint state );
+</verb></tscreen>
+El siguiente ejemplo crea un grupo de tres botones:
+
+<tscreen><verb>
+/* Principio del ejemplo radiobuttons.c */
+
+#include <gtk/gtk.h>
+#include <glib.h>
+
+void close_application( GtkWidget *widget, GdkEvent *event, gpointer data ) {
+ gtk_main_quit();
+}
+
+main(int argc,char *argv[])
+{
+ static GtkWidget *ventana = NULL;
+ GtkWidget *caja1;
+ GtkWidget *caja2;
+ GtkWidget *boton;
+ GtkWidget *separator;
+ GSList *group;
+
+ gtk_init(&amp;argc,&amp;argv);
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ gtk_signal_connect (GTK_OBJECT (ventana), "delete_event",
+ GTK_SIGNAL_FUNC(close_application),
+ NULL);
+
+ gtk_window_set_title (GTK_WINDOW (ventana), "radio buttons");
+ gtk_container_border_width (GTK_CONTAINER (ventana), 0);
+
+ caja1 = gtk_vbox_new (FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (ventana), caja1);
+ gtk_widget_show (caja1);
+
+ caja2 = gtk_vbox_new (FALSE, 10);
+ gtk_container_border_width (GTK_CONTAINER (caja2), 10);
+ gtk_box_pack_start (GTK_BOX (caja1), caja2, TRUE, TRUE, 0);
+ gtk_widget_show (caja2);
+
+ boton = gtk_radio_button_new_with_label (NULL, "botón1");
+ gtk_box_pack_start (GTK_BOX (caja2), boton, TRUE, TRUE, 0);
+ gtk_widget_show (boton);
+
+ group = gtk_radio_button_group (GTK_RADIO_BUTTON (boton));
+ boton = gtk_radio_button_new_with_label(group, "botón2");
+ gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (boton), TRUE);
+ gtk_box_pack_start (GTK_BOX (caja2), boton, TRUE, TRUE, 0);
+ gtk_widget_show (boton);
+
+ group = gtk_radio_button_group (GTK_RADIO_BUTTON (boton));
+ boton = gtk_radio_button_new_with_label(group, "botón3");
+ gtk_box_pack_start (GTK_BOX (caja2), boton, TRUE, TRUE, 0);
+ gtk_widget_show (boton);
+
+ separator = gtk_hseparator_new ();
+ gtk_box_pack_start (GTK_BOX (caja1), separator, FALSE, TRUE, 0);
+ gtk_widget_show (separator);
+
+ caja2 = gtk_vbox_new (FALSE, 10);
+ gtk_container_border_width (GTK_CONTAINER (caja2), 10);
+ gtk_box_pack_start (GTK_BOX (caja1), caja2, FALSE, TRUE, 0);
+ gtk_widget_show (caja2);
+
+ boton = gtk_button_new_with_label ("close");
+ gtk_signal_connect_object (GTK_OBJECT (boton), "clicked",
+ GTK_SIGNAL_FUNC(close_application),
+ GTK_OBJECT (ventana));
+ gtk_box_pack_start (GTK_BOX (caja2), boton, TRUE, TRUE, 0);
+ GTK_WIDGET_SET_FLAGS (boton, GTK_CAN_DEFAULT);
+ gtk_widget_grab_default (boton);
+ gtk_widget_show (boton);
+ gtk_widget_show (ventana);
+
+ gtk_main();
+ return(0);
+}
+/* final del ejemplo */
+</verb></tscreen>
+
+<!-- TODO: checout out gtk_radio_button_new_from_widget function - TRG -->
+
+<!-- ***************************************************************** -->
+<sect>Ajustes (<em/Adjustment/) <label id="sec_Adjustment">
+<!-- ***************************************************************** -->
+<p>
+Existen diferentes <em/widgets/ en GTK+ que pueden ser ajustados
+visualmente por el usuario mediante el ratón o el teclado. Un
+ejemplo son los <em/widgets/ de selección descritos en la
+sección <ref id="sec_Range_Widgets" name="Widgets de selección
+de rango">. También hay otros widgets que pueden ser ajustados
+parcialmente, por ejemplo el <em/widget/ de texto o el <em/viewport/.
+
+Como es lógico el programa tiene que poder reaccionar a los
+cambios que el usuario realiza en los <em/widgets/ de selección de
+rango. Una forma de hacer que el programa reaccione sería tener
+cada <em/widget/ emitiendo su propio tipo de señal cuando cambie el
+ajuste, y bien pasar el nuevo valor al manejador de señal o bien
+obligarle a que mire dentro de la estructura de datos del <em/widget/
+para conocer este valor. Pero también puede ser que quiera conectar
+los ajustes de varios <em/widgets/, para que así cuando se ajuste
+uno, los demás se ajusten automáticamente. El ejemplo más
+obvio es conectar una barra de desplazamiento a una región con
+texto. Si cada <em/widget/ posee su propia forma de establecer u
+obtener sus valores de ajuste el programador puede que tenga que
+escribir sus propios controladores de señales para traducir el
+resultado de la señal producida por un <em/widget/ como el
+argumento de una función usada para determinar valores en otro
+<em/widget/.
+
+Para resolver este problema GTK+ usa objetos del tipo GtkAdjustment.
+Con ellos se consigue almacenar y traspasar información de una forma
+abstracta y flexible. El uso más obvio es el de almacenes de
+párametros para <em/widgets/ de escala (barras deslizantes y
+escalas). Como los GtkAdjustment derivan de GtkObject poseen
+cualidades intrínsecas que les permiten ser algo más que simples
+estructuras de datos. Lo más importante es que pueden emitir
+señales que a su vez pueden ser usadas tanto para reaccionar frente
+al cambio de datos introducidos por el usuario como para transferir
+los nuevos valores de forma transparente entre <em/widgets/ ajustables.
+
+<sect1>Creando un ajuste
+<p>
+Los ajustes se pueden crear usando:
+
+<tscreen><verb>
+GtkObject *gtk_adjustment_new( gfloat value,
+ gfloat lower,
+ gfloat upper,
+ gfloat step_increment,
+ gfloat page_increment,
+ gfloat page_size );
+</verb></tscreen>
+
+El argumento <tt/value/ es el valor inicial que le queremos dar
+al ajuste. Normalmente se corresponde con las posiciones situadas
+más arriba y a la izquierda de un <em/widget/ ajustable. El argumento
+<tt/lower/ especifica los valores más pequeños que el ajuste
+puede contener. A su vez con <tt/step_increment/ se especifica el
+valor más pequeño en el que se puede variar la magnitud en
+cuestión (valor de paso asociado), mientras que <tt/page_increment/
+es el mayor. Con <tt/page_size/ se determina el valor visible de un
+<em/widget/.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Forma sencilla de usar los ajustes
+<p>
+Los <em/widgets/ ajustábles se pueden dividir en dos categorias
+diferentes, aquellos que necesitan saber las unidades de la cantidad
+almacenada y los que no. Este último grupo incluye los <em/widgets/
+de tamaño (barras deslizantes, escalas, barras de estado, o botones
+giratorios). Normalmente estos <em/widgets/ son ajustados
+«directamente» por el usuario. Los argumentos <tt/lower/ y
+<tt/upper/ serán los limites dentro de los cuales el usuario puede
+manipular los ajustes. Por defecto sólo se modificará el
+<tt/value/ (valor) de un ajuste.
+
+El otro grupo incluye los <em/widgets/ de texto, la lista compuesta o
+la ventana con barra deslizante. Estos <em/widgets/ usan valores en
+pixels para sus ajustes, y normalmente son ajustados
+«indirectamente» mediante barras deslizantes. Aunque todos los
+<em/widgets/ pueden crear sus propios ajustes o usar otros creados por
+el programador con el segundo grupo suele ser conveniente dejarles que
+creen sus propios ajustes. Normalmente no tendrán en cuenta ninguno
+de los valores de un ajuste proporcionado por el programador, excepto
+<tt/value/, pero los resultados son, en general, indefinidos
+(entiendase que tendrá que leer el código fuente para saber que
+pasa con cada widget).
+
+Probablemente ya se habrá dado cuenta de que como los <em/widgets/
+de texto (y todos los <em/widgets/ del segundo grupo), insisten en
+establecer todos los valores excepto <tt/value/, mientras que las
+barras deslizantes sólo modifican <tt/value/, si se comparte un
+objeto de ajuste entre una barra deslizante y un <em/widget/ de texto
+al manipular la barra se modificará el <em/widget/ de texto. Ahora
+queda completamente demostrada la utilidad de los ajustes. Veamos un
+ejemplo:
+
+<tscreen><verb>
+ /* creamos un ajuste */
+ text = gtk_text_new (NULL, NULL);
+ /* lo usamos con la barra deslizante */
+ vscrollbar = gtk_vscrollbar_new (GTK_TEXT(text)->vadj);
+</verb></tscreen>
+
+</sect1>
+<!-- ----------------------------------------------------------------- -->
+<sect1> Descripción detallada de los ajustes
+<p>
+Puede que se esté preguntando cómo es posible crear sus propios
+controladores para responder a las modificaciones producidas por
+el usuario y cómo obtener el valor del ajuste hecho por este.
+Para aclarar esto y otras cosas vamos a estudiar la estructura
+del ajuste
+
+<tscreen><verb>
+struct _GtkAdjustment
+{
+ GtkData data;
+
+ gfloat lower;
+ gfloat upper;
+ gfloat value;
+ gfloat step_increment;
+ gfloat page_increment;
+ gfloat page_size;
+};
+</verb></tscreen>
+
+Lo primero que hay que aclarar es que no hay ninguna macro o función
+de acceso que permita obtener el <tt/value/ de un GtkAdjustment, por
+lo que tendrá que hacerlo usted mismo. Tampoco se preocupe mucho
+porque la macro <tt>GTK_ADJUSTMENT (Object)</tt> comprueba los tipos
+durante el proceso de ejecución (como hacen todas las macros de GTK+
+que sirven para comprobar los tipos).
+
+Cuando se establece el <tt/value/ de un ajuste normalmente se quiere
+que cualquier <em/widget/ se entere del cambio producido. Para ello
+GTK+ posee una función especial:
+
+<tscreen><verb>
+void gtk_adjustment_set_value( GtkAdjustment *adjustment,
+ gfloat value );
+</verb></tscreen>
+
+Tal y como se mencionó antes GtkAdjustment es una subclase de GtkObject
+y por tanto puede emitir señales. Así se consigue que se actualicen
+los valores de los ajustes cuando se comparten entre varios <em/widgets/.
+Por tanto todos los <em/widgets/ ajustables deben conectar controladores
+de señales a sus señales del tipo <tt/value_changed/. Esta es la
+definición de la señal como viene en <tt/struct _GtkAdjustmentClass/
+
+<tscreen><verb>
+ void (* value_changed) (GtkAdjustment *adjustment);
+</verb></tscreen>
+
+Todos los <em/widgets/ que usan GtkAdjustment deben emitir esta
+señal cuando cambie el valor de algún ajuste. Esto sucede cuando
+el usuario cambia algo o el programa modifica los ajustes
+mediante. Por ejemplo si queremos que rote una figura cuando
+modificamos un <em/widget/ de escala habría que usar una respuesta
+como esta:
+
+<tscreen><verb>
+void cb_rotate_picture (GtkAdjustment *adj, GtkWidget *picture)
+{
+ set_picture_rotation (picture, adj->value);
+...
+</verb></tscreen>
+
+y conectarla con el ajuste del <em/widget/ de escala mediante:
+
+<tscreen><verb>
+gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
+ GTK_SIGNAL_FUNC (cb_rotate_picture), picture);
+</verb></tscreen>
+¿Qué pasa cuando un <em/widget/ reconfigura los valores
+<tt/upper/ o <tt/lower/ (por ejemplo cuando se añade más texto)?
+Simplemente que se emite la señal <tt/changed/, que debe ser
+parecida a:
+
+<tscreen><verb>
+ void (* changed) (GtkAdjustment *adjustment);
+</verb></tscreen>
+
+Los <em/widgets/ de tamaño normalmente conectan un controlador a
+esta señal, que cambia el aspecto de éste para reflejar el
+cambio. Por ejemplo el tamaño de la guía en una barra deslizante
+que se alarga o encoge según la inversa de la diferencia de los
+valores <tt/lower/ y <tt/upper/.
+
+Probablemente nunca tenga que conectar un controlador a esta señal
+a no ser que esté escribiendo un nuevo tipo de <em/widget/. Pero si
+cambia directamente alguno de los valores de GtkAdjustment debe hacer
+que se emita la siguiente señal para reconfigurar todos aquellos
+<em/widgets/ que usen ese ajuste:
+
+<tscreen><verb>
+gtk_signal_emit_by_name (GTK_OBJECT (adjustment), "changed");
+</verb></tscreen>
+
+</sect1>
+</sect>
+<!-- ***************************************************************** -->
+<sect>Los <em/widgets/ de selección de rango <label id="sec_Range_Widgets">
+<!-- ***************************************************************** -->
+<p>
+Este tipo de <em/widgets/ incluye a las barras de desplazamiento
+(<em>scroollbar</em>) y la menos conocida escala
+(<em/scale</em>). Ambos pueden ser usados para muchas cosas, pero como
+sus funciones y su implementación son muy parecidas los describimos
+al mismo tiempo. Principalmente se utilizan para permitirle al usuario
+escoger un valor dentro de un rango ya prefijado.
+
+Todos los <em/widgets/ de selección comparten elementos
+gráficos, cada uno de los cuales tiene su propia ventana X window y
+recibe eventos. Todos contienen una guía y un rectángulo para
+determinar la posición dentro de la guía (en una procesador de
+textos con entorno gráfico se encuentra situado a la derecha del
+texto y sirve para situarnos en las diferentes partes del texto). Con
+el ratón podemos subir o bajar el rectángulo, mientras que si
+hacemos `click' dentro de la guía, pero no sobre el rectángulo,
+este se mueve hacia donde hemos hecho el click. Dependiendo del
+botón pulsado el rectángulo se moverá hasta la posición
+del click o una cantidad prefijada de ante mano.
+
+Tal y como se mencionó en <ref id="sec_Adjustment" name="Ajustes">
+todos los <em/widgets/ usados para seleccionar un rango estan
+asociados con un objeto de ajuste, a partir del cual calculan la
+longitud de la barra y su posición. Cuando el usuario manipula la
+barra de desplazamiento el widget cambiará el valor del ajuste.
+
+<sect1>El <em/widget/ barra de desplazamiento
+<p>
+El <em/widget/ barra de desplazamiento solamente debe utilizarse para
+hacer <em/scroll/ sobre otro <em/widget/, como una lista, una caja de
+texto, o un puerto de visión (y en muchos es más fácil utilizar
+el <em/widget/ scrolled window). Para el resto de los casos, debería
+utilizar los <em/widgets/ de escala, ya son más sencillos de usar y
+más potentes.
+
+Hay dos tipos separados de barras de desplazamiento, según sea
+horizontal o vertical. Realmente no hay mucho que añadir. Puede
+crear estos <em/widgets/ utilizar las funciones siguientes, definidas
+en <tt>&lt;gtk/gtkhscrollbar.h&gt;</tt> y
+<tt>&lt;gtk/gtkvscrollbar.h&gt;</tt>:
+
+<tscreen><verb>
+GtkWidget* gtk_hscrollbar_new( GtkAdjustment *adjustment );
+
+GtkWidget* gtk_vscrollbar_new( GtkAdjustment *adjustment );
+</verb></tscreen>
+
+y esto es todo lo que hay (si no me cree, ¡mire los ficheros de
+cabecera!). El argumento <tt/adjustment/ puede ser un puntero a un
+ajuste ya existente, o puede ser NULL, en cuyo caso se creará
+uno. Es útil especificar NULL si quiere pasar el ajuste recién
+creado a la función constructora de algún otro <em/widget/ (como
+por ejemplo el <em/widget/ texto) que se ocupará de configurarlo
+correctamente por usted.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1><em/Widgets/ de escala
+<p>
+Los <em/widgets/ de escala se usan para determinar el valor de una
+cantidad que se puede interpretar visualmente. El usuario
+probablemente fijará el valor a ojo. Por ejemplo el <em/widget/
+GtkColorSelection contiene <em/widgets/ de escala que controlan las
+componentes del color a seleccionar. Normalmente el valor preciso es
+menos importante que el efecto visual, por lo que el color se
+selecciona con el ratón y no mediante un número concreto.
+
+<!-- ----------------------------------------------------------------- -->
+<sect2>Creación de un <em/widget/ de escala
+<p>
+Existen dos tipos de <em/widgets/ de escala: GtkHScale (que es
+horizontal) y GtkVscale (vertical). Como funcionan de la misma manera
+los vamos a describir a la vez. Las funciones definidas en
+<tt>&lt;gtk/gtkvscale.h&gt;</tt> y <tt>&lt;gtk/gtkhscale.h&gt;</tt>,
+crean <em/widgets/ de escala verticales y horizontales
+respectivamente.
+
+<tscreen><verb>
+GtkWidget* gtk_vscale_new( GtkAdjustment *adjustment );
+
+GtkWidget* gtk_hscale_new( GtkAdjustment *adjustment );
+</verb></tscreen>
+
+El <tt/ajuste/ (adjustment) puede ser tanto un ajuste creado
+mediante <tt/gtk_adjustment_new()/ como <tt/NULL/. En este
+último caso se crea un GtkAdjustment anónimo con todos sus
+valores iguales a <tt/0.0/. Si no ha quedado claro el uso de esta
+función consulte la sección <ref id="sec_Adjustment"
+name="Ajustes"> para una discusión más detallada.
+
+<!-- ----------------------------------------------------------------- -->
+<sect2> Funciones y señales
+<p>
+Los <em/widgets/ de escala pueden indicar su valor actual como un
+número. Su comportamiento por defecto es mostrar este valor, pero
+se puede modificar usando:
+
+<tscreen><verb>
+void gtk_scale_set_draw_value( GtkScale *scale,
+ gint draw_value );
+</verb></tscreen>
+
+Los valores posibles de <tt/draw_value son/ son <tt/TRUE/ o <tt/FALSE/.
+Con el primero se muestra el valor y con el segundo no.
+
+El valor mostrado por un <em/widget/ de escala por defecto se redondea
+a un valor decimal (igual que con <tt/value/ en un GtkAdjustment). Se
+puede cambiar con:
+
+<tscreen>
+<verb>
+void gtk_scale_set_digits( GtkScale *scale,
+ gint digits );
+</verb>
+</tscreen>
+
+donde <tt/digits/ es el número de posiciones decimales que se
+quiera. En la práctica sólo se mostrarán 13 como máximo.
+
+Por último, el valor se puede dibujar en diferentes posiciones con
+respecto a la posición del rectangulo que hay dentro de la guía:
+
+<tscreen>
+<verb>
+void gtk_scale_set_value_pos( GtkScale *scale,
+ GtkPositionType pos );
+</verb>
+</tscreen>
+
+Si ha leido la sección acerca del <em/widget/ libro de notas
+entonces ya conoce cuales son los valores posibles de <tt/pos/. Estan
+definidos en <tt>&lt;gtk/gtkscale.h&gt;</tt> como <tt/enum GtkPositionType/
+y son auto explicatorios. Si se escoge un lateral de la guía,
+entonces seguirá al rectángulo a lo largo de la guía.
+
+Todas las funcioenes precedentes se encuentran definidas en:
+<tt>&lt;gtk/gtkscale.h&gt;</tt>.
+</sect2>
+</sect1>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Funciones comunes <label id="sec_funciones_range">
+<p>
+La descripción interna de la clase GtkRange es bastante complicada,
+pero al igual que con el resto de las «clases base» sólo es
+interesante si se quiere «hackear». Casi todas las señales y
+funciones sólo son útiles para desarrollar derivados. Para un
+usuario normal las funciones interesantes son aquellas definidas en:
+<tt>&lt;gtk/gtkrange.h&gt;</tt> y funcionan igual en todos los
+<em/widgets/ de rango.
+
+<!-- ----------------------------------------------------------------- -->
+<sect2> Estableciendo cada cúanto se actualizan
+<p>
+La política de actualización de un <em/widget/ define en que
+puntos de la interacción con el usuario debe cambiar el valor
+<tt/value/ en su GtkAdjustment y emitir la señal
+«value_changed». Las actualizaciones definidas en
+<tt>&lt;gtk/gtkenums.h&gt;</tt> como <tt>enum GtkUpdateType</tt>, son:
+
+<itemize>
+<item>GTK_UPDATE_POLICY_CONTINUOUS - Este es el valor por defecto.La
+señal «value_changed» se emite continuamente, por ejemplo cuando
+la barra deslizante se mueve incluso aunque sea un poquito.
+</item>
+<item>GTK_UPDATE_POLICY_DISCONTINUOUS - La señal «value_changed»
+sólo se emite cuando se ha parado de mover la barra y el usuario ha
+soltado el botón del ratón.
+</item>
+<item>GTK_UPDATE_POLICY_DELAYED - La señal sólo se emite cuando
+el usuario suelta el botón del ratón o si la barra no se mueve
+durante un periodo largo de tiempo.
+</item>
+</itemize>
+
+Para establecer la política de actualización se usa la
+conversión definida en la macro
+
+<tscreen><verb>
+void gtk_range_set_update_policy( GtkRange *range,
+ GtkUpdateType policy) ;
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect2>Obteniendo y estableciendo Ajustes
+<p>
+Para obtener o establecer el ajuste de un <em/widget/ de rango se usa:
+
+<tscreen><verb>
+GtkAdjustment* gtk_range_get_adjustment( GtkRange *range );
+
+void gtk_range_set_adjustment( GtkRange *range,
+ GtkAdjustment *adjustment );
+</verb></tscreen>
+
+La función <tt/gtk_range_get_adjustment()/ devuelve un puntero al
+ajuste al que <tt/range/ esté conectado.
+
+La función <tt/gtk_range_set_adjustment()/ no hace nada si se le
+pasa como argumento el valor <tt/range/ del ajuste que esta siendo
+usado (aunque se haya modificado algún valor). En el caso de que
+sea un ajuste nuevo (GtkAdjustment) dejará de usar el antiguo
+(probablemente lo destruirá) y conectará las señales
+apropiadas al nuevo. A continuación llamará a la función
+<tt/gtk_range_adjustment_changed()/ que en teoría recalculará el
+tamaño y/o la posición de la barra, redibujándola en caso de
+que sea necesario. Tal y como se mencionó en la sección de los
+ajustes si se quiere reusar el mismo GtkAdjustment cuando se modifican
+sus valores se debe emitir la señal «changed». Por ejemplo:
+
+<tscreen><verb>
+gtk_signal_emit_by_name (GTK_OBJECT (adjustment), "changed");
+</verb></tscreen>
+</sect2>
+</sect1>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Enlaces con el teclado y el ratón
+<p>
+Todos los <em/widgets/ de rango reaccionan más o menos de la misma
+manera a las pulsaciones del ratón. Al pulsar el botón 1 sobre
+el rectángulo de la barra el <tt/value/ del ajuste aumentará o
+disminuirá según <tt/page_increment/. Con el botón 2 la barra
+se desplazará al punto en el que el botón fue pulsado. Con cada
+pulsación de cualquier botón sobre las flechas el valor del
+ajuste se modifica una cantidad igual a <tt/step_increment/.
+
+
+Acostumbrarse a que tanto las barras deslizantes como los <em/widgets/ de
+escala puedan tomar la atención del teclado puede ser un proceso largo.
+Si que se cree que los usuarios no lo van a entender se puede anular
+mediante la función GTK_WIDGET_UNSET_FLAGS y con GTK_CAN_FOCUS como
+argumento:
+
+<tscreen><verb>
+GTK_WIDGET_UNSET_FLAGS (scrollbar, GTK_CAN_FOCUS);
+</verb></tscreen>
+
+Los enlaces entre teclas (que sólo estan activos cuando el
+<em/widget/ tiene la atención (focus)) se comportan de manera
+diferente para los <em/widgets/ de rango horizontales que para los
+verticales. También son diferentes para los <em/widgets/ de escala
+y para las barras deslizantes. (Simplemente para evitar confusiones
+entre las teclas de las barras deslizantes horizontales y verticales,
+ya que ambas actúan sobre la misma área)
+
+<sect2><em/Widgets/ de rango vertical
+<p>
+Todos los <em/widgets/ de rango pueden ser manipulados con las teclas
+arriba, abajo, <tt/Re Pág/, <tt/ Av Pág/. Las flechas mueven las
+barras la cantidad fijada mediante <tt/step_increment/, mientras que
+<tt/Re Pág/ y <tt/Av Pag/ lo hacen según <tt/page_increment/.
+
+El usuario también puede mover la barra de un extremo al otro de la
+guía mediante el teclado. Con el <em/widget/ GtkVScale podemos ir a
+los extremos utilizando las teclas <tt/Inicio/ y <tt/Final/ mientras
+que con el <em/widget/ GtkVScrollbar habrá que utilizar
+<tt/Control-Re Pág/ y <tt/Control-Av Pág/.
+
+<!-- ----------------------------------------------------------------- -->
+<sect2><em/Widgets/ de rango horizontal
+<p>
+Las teclas izquierda y derecha funcionan tal y como espera que
+funcionen en estos <em/widgets/: mueven la barra una cantidad dada por
+<tt/step_increment/. A su vez <tt/Inicio/ y <tt/Final/ sirven para
+pasar de un extremo al otro de la guía. Para el <em/widget/
+GtkHScale el mover la barra una cantidad dada por <tt/page_increment/
+se consigue mediante <tt>Control-Izquierda</tt> y
+<tt>Control-derecha</tt>, mientras que para el <em/widget/
+GtkHScrollbar se consigue con <tt/Control-Inicio/ y
+<tt/Control-Final/.
+</sect2>
+</sect1>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Ejemplo <label id="sec_Ejemplo_Rango">
+<p>
+Este ejemplo es una versión modificada del test «range controls»
+que a su vez forma parte de <tt/testgtk.c/. Simplemente dibuja una
+ventana con tres <em/widgets/ de rango conectados al mismo ajuste, y
+un conjunto de controles para ajustar algunos de los parámetros
+ya mencionados. Así se consigue ver como funcionan estos
+<em/widgets/ al ser manipulados por el usuario.
+
+<tscreen><verb>
+/* principio del ejemplo widgets de selección de rango rangewidgets.c */
+
+#include <gtk/gtk.h>
+
+GtkWidget *hscale, *vscale;
+
+void cb_pos_menu_select( GtkWidget *item,
+ GtkPositionType pos )
+{
+ /* Establece el valor position en los widgets de escala */
+ gtk_scale_set_value_pos (GTK_SCALE (hscale), pos);
+ gtk_scale_set_value_pos (GTK_SCALE (vscale), pos);
+}
+
+void cb_update_menu_select( GtkWidget *item,
+ GtkUpdateType policy )
+{
+ /* Establece la política de actualización para los widgets
+ * de escala */
+ gtk_range_set_update_policy (GTK_RANGE (hscale), policy);
+ gtk_range_set_update_policy (GTK_RANGE (vscale), policy);
+}
+
+void cb_digits_scale( GtkAdjustment *adj )
+{
+ /* Establece el número de cifras decimales a las que se
+ * redondeará adj->value */
+ gtk_scale_set_digits (GTK_SCALE (hscale), (gint) adj->value);
+ gtk_scale_set_digits (GTK_SCALE (vscale), (gint) adj->value);
+}
+
+void cb_page_size( GtkAdjustment *get,
+ GtkAdjustment *set )
+{
+ /* Establece el tamaño de la página y el incremento del
+ * ajuste al valor especificado en la escala "Page Size" */
+ set->page_size = get->value;
+ set->page_increment = get->value;
+ /* Ahora emite la señal "changed" para reconfigurar todos los
+ * widgets que están enlazados a este ajuste */
+ gtk_signal_emit_by_name (GTK_OBJECT (set), "changed");
+}
+
+void cb_draw_value( GtkToggleButton *boton )
+{
+ /* Activa o desactiva el valor display en los widgets de escala
+ * dependiendo del estado del botón de comprobación */
+ gtk_scale_set_draw_value (GTK_SCALE (hscale), boton->active);
+ gtk_scale_set_draw_value (GTK_SCALE (vscale), boton->active);
+}
+
+/* Funciones varias */
+
+GtkWidget *make_menu_item( gchar *name,
+ GtkSignalFunc callback,
+ gpointer data )
+{
+ GtkWidget *item;
+
+ item = gtk_menu_item_new_with_label (name);
+ gtk_signal_connect (GTK_OBJECT (item), "activate",
+ callback, data);
+ gtk_widget_show (item);
+
+ return(item);
+}
+
+void scale_set_default_values( GtkScale *scale )
+{
+ gtk_range_set_update_policy (GTK_RANGE (scale),
+ GTK_UPDATE_CONTINUOUS);
+ gtk_scale_set_digits (scale, 1);
+ gtk_scale_set_value_pos (scale, GTK_POS_TOP);
+ gtk_scale_set_draw_value (scale, TRUE);
+}
+
+/* crea la ventana principal */
+
+void create_range_controls( void )
+{
+ GtkWidget *ventana;
+ GtkWidget *caja1, *caja2, *caja3;
+ GtkWidget *boton;
+ GtkWidget *scrollbar;
+ GtkWidget *separator;
+ GtkWidget *opt, *menu, *item;
+ GtkWidget *etiqueta;
+ GtkWidget *scale;
+ GtkObject *adj1, *adj2;
+
+ /* creación estándar de una ventana */
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
+ GTK_SIGNAL_FUNC(gtk_main_quit),
+ NULL);
+ gtk_window_set_title (GTK_WINDOW (ventana), "range controls");
+
+ caja1 = gtk_vbox_new (FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (ventana), caja1);
+ gtk_widget_show (caja1);
+
+ caja2 = gtk_hbox_new (FALSE, 10);
+ gtk_container_border_width (GTK_CONTAINER (caja2), 10);
+ gtk_box_pack_start (GTK_BOX (caja1), caja2, TRUE, TRUE, 0);
+ gtk_widget_show (caja2);
+
+ /* value, lower, upper, step_increment, page_increment, page_size */
+ /* Observe que el valor de page_size solo sirve para los widgets
+ * barras de desplazamiento (scrollbar), y que el valor más
+ * alto que obtendrá será (upper - page_size). */
+ adj1 = gtk_adjustment_new (0.0, 0.0, 101.0, 0.1, 1.0, 1.0);
+
+ vscale = gtk_vscale_new (GTK_ADJUSTMENT (adj1));
+ scale_set_default_values (GTK_SCALE (vscale));
+ gtk_box_pack_start (GTK_BOX (caja2), vscale, TRUE, TRUE, 0);
+ gtk_widget_show (vscale);
+
+ caja3 = gtk_vbox_new (FALSE, 10);
+ gtk_box_pack_start (GTK_BOX (caja2), caja3, TRUE, TRUE, 0);
+ gtk_widget_show (caja3);
+
+ /* Reutilizamos el mismo ajuste */
+ hscale = gtk_hscale_new (GTK_ADJUSTMENT (adj1));
+ gtk_widget_set_usize (GTK_WIDGET (hscale), 200, 30);
+ scale_set_default_values (GTK_SCALE (hscale));
+ gtk_box_pack_start (GTK_BOX (caja3), hscale, TRUE, TRUE, 0);
+ gtk_widget_show (hscale);
+
+ /* Reutilizamos de nuevo el mismo ajuste */
+ scrollbar = gtk_hscrollbar_new (GTK_ADJUSTMENT (adj1));
+ /* Observe que con esto conseguimos que la escala siempre se
+ * actualice de una forma continua cuando se mueva la barra de
+ * desplazamiento */
+ gtk_range_set_update_policy (GTK_RANGE (scrollbar),
+ GTK_UPDATE_CONTINUOUS);
+ gtk_box_pack_start (GTK_BOX (caja3), scrollbar, TRUE, TRUE, 0);
+ gtk_widget_show (scrollbar);
+
+ caja2 = gtk_hbox_new (FALSE, 10);
+ gtk_container_border_width (GTK_CONTAINER (caja2), 10);
+ gtk_box_pack_start (GTK_BOX (caja1), caja2, TRUE, TRUE, 0);
+ gtk_widget_show (caja2);
+
+ /* Un botón para comprobar si el valor se muestra o no*/
+ boton = gtk_check_button_new_with_label("Display value on scale widgets");
+ gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (boton), TRUE);
+ gtk_signal_connect (GTK_OBJECT (boton), "toggled",
+ GTK_SIGNAL_FUNC(cb_draw_value), NULL);
+ gtk_box_pack_start (GTK_BOX (caja2), boton, TRUE, TRUE, 0);
+ gtk_widget_show (boton);
+
+ caja2 = gtk_hbox_new (FALSE, 10);
+ gtk_container_border_width (GTK_CONTAINER (caja2), 10);
+
+ /* Una opción en el menú para cambiar la posición del
+ * valor */
+ etiqueta = gtk_label_new ("Scale Value Position:");
+ gtk_box_pack_start (GTK_BOX (caja2), etiqueta, FALSE, FALSE, 0);
+ gtk_widget_show (etiqueta);
+
+ opt = gtk_option_menu_new();
+ menu = gtk_menu_new();
+
+ item = make_menu_item ("Top",
+ GTK_SIGNAL_FUNC(cb_pos_menu_select),
+ GINT_TO_POINTER (GTK_POS_TOP));
+ gtk_menu_append (GTK_MENU (menu), item);
+
+ item = make_menu_item ("Bottom", GTK_SIGNAL_FUNC (cb_pos_menu_select),
+ GINT_TO_POINTER (GTK_POS_BOTTOM));
+ gtk_menu_append (GTK_MENU (menu), item);
+
+ item = make_menu_item ("Left", GTK_SIGNAL_FUNC (cb_pos_menu_select),
+ GINT_TO_POINTER (GTK_POS_LEFT));
+ gtk_menu_append (GTK_MENU (menu), item);
+
+ item = make_menu_item ("Right", GTK_SIGNAL_FUNC (cb_pos_menu_select),
+ GINT_TO_POINTER (GTK_POS_RIGHT));
+ gtk_menu_append (GTK_MENU (menu), item);
+
+ gtk_option_menu_set_menu (GTK_OPTION_MENU (opt), menu);
+ gtk_box_pack_start (GTK_BOX (caja2), opt, TRUE, TRUE, 0);
+ gtk_widget_show (opt);
+
+ gtk_box_pack_start (GTK_BOX (caja1), caja2, TRUE, TRUE, 0);
+ gtk_widget_show (caja2);
+
+ caja2 = gtk_hbox_new (FALSE, 10);
+ gtk_container_border_width (GTK_CONTAINER (caja2), 10);
+
+ /* Sí, otra opción de menú, esta vez para la política
+ * de actualización de los widgets */
+ etiqueta = gtk_label_new ("Scale Update Policy:");
+ gtk_box_pack_start (GTK_BOX (caja2), etiqueta, FALSE, FALSE, 0);
+ gtk_widget_show (etiqueta);
+
+ opt = gtk_option_menu_new();
+ menu = gtk_menu_new();
+
+ item = make_menu_item ("Continuous",
+ GTK_SIGNAL_FUNC (cb_update_menu_select),
+ GINT_TO_POINTER (GTK_UPDATE_CONTINUOUS));
+ gtk_menu_append (GTK_MENU (menu), item);
+
+ item = make_menu_item ("Discontinuous",
+ GTK_SIGNAL_FUNC (cb_update_menu_select),
+ GINT_TO_POINTER (GTK_UPDATE_DISCONTINUOUS));
+ gtk_menu_append (GTK_MENU (menu), item);
+
+ item = make_menu_item ("Delayed",
+ GTK_SIGNAL_FUNC (cb_update_menu_select),
+ GINT_TO_POINTER (GTK_UPDATE_DELAYED));
+ gtk_menu_append (GTK_MENU (menu), item);
+
+ gtk_option_menu_set_menu (GTK_OPTION_MENU (opt), menu);
+ gtk_box_pack_start (GTK_BOX (caja2), opt, TRUE, TRUE, 0);
+ gtk_widget_show (opt);
+
+ gtk_box_pack_start (GTK_BOX (caja1), caja2, TRUE, TRUE, 0);
+ gtk_widget_show (caja2);
+
+ caja2 = gtk_hbox_new (FALSE, 10);
+ gtk_container_border_width (GTK_CONTAINER (caja2), 10);
+
+ /* Un widget GtkHScale para ajustar el número de dígitos en
+ * la escala. */
+ etiqueta = gtk_label_new ("Scale Digits:");
+ gtk_box_pack_start (GTK_BOX (caja2), etiqueta, FALSE, FALSE, 0);
+ gtk_widget_show (etiqueta);
+
+ adj2 = gtk_adjustment_new (1.0, 0.0, 5.0, 1.0, 1.0, 0.0);
+ gtk_signal_connect (GTK_OBJECT (adj2), "value_changed",
+ GTK_SIGNAL_FUNC (cb_digits_scale), NULL);
+ scale = gtk_hscale_new (GTK_ADJUSTMENT (adj2));
+ gtk_scale_set_digits (GTK_SCALE (scale), 0);
+ gtk_box_pack_start (GTK_BOX (caja2), scale, TRUE, TRUE, 0);
+ gtk_widget_show (scale);
+
+ gtk_box_pack_start (GTK_BOX (caja1), caja2, TRUE, TRUE, 0);
+ gtk_widget_show (caja2);
+
+ caja2 = gtk_hbox_new (FALSE, 10);
+ gtk_container_border_width (GTK_CONTAINER (caja2), 10);
+
+ /* Y un último widget GtkHScale para ajustar el tamaño de la
+ * página de la barra de desplazamiento. */
+ etiqueta = gtk_label_new ("Scrollbar Page Size:");
+ gtk_box_pack_start (GTK_BOX (caja2), etiqueta, FALSE, FALSE, 0);
+ gtk_widget_show (etiqueta);
+
+ adj2 = gtk_adjustment_new (1.0, 1.0, 101.0, 1.0, 1.0, 0.0);
+ gtk_signal_connect (GTK_OBJECT (adj2), "value_changed",
+ GTK_SIGNAL_FUNC (cb_page_size), adj1);
+ scale = gtk_hscale_new (GTK_ADJUSTMENT (adj2));
+ gtk_scale_set_digits (GTK_SCALE (scale), 0);
+ gtk_box_pack_start (GTK_BOX (caja2), scale, TRUE, TRUE, 0);
+ gtk_widget_show (scale);
+
+ gtk_box_pack_start (GTK_BOX (caja1), caja2, TRUE, TRUE, 0);
+ gtk_widget_show (caja2);
+
+ separator = gtk_hseparator_new ();
+ gtk_box_pack_start (GTK_BOX (caja1), separator, FALSE, TRUE, 0);
+ gtk_widget_show (separator);
+
+ caja2 = gtk_vbox_new (FALSE, 10);
+ gtk_container_border_width (GTK_CONTAINER (caja2), 10);
+ gtk_box_pack_start (GTK_BOX (caja1), caja2, FALSE, TRUE, 0);
+ gtk_widget_show (caja2);
+
+ boton = gtk_button_new_with_label ("Quit");
+ gtk_signal_connect_object (GTK_OBJECT (boton), "clicked",
+ GTK_SIGNAL_FUNC(gtk_main_quit),
+ NULL);
+ gtk_box_pack_start (GTK_BOX (caja2), boton, TRUE, TRUE, 0);
+ GTK_WIDGET_SET_FLAGS (boton, GTK_CAN_DEFAULT);
+ gtk_widget_grab_default (boton);
+ gtk_widget_show (boton);
+
+ gtk_widget_show (ventana);
+}
+
+int main( int argc,
+ char *argv[] )
+{
+ gtk_init(&amp;argc, &amp;argv);
+
+ create_range_controls();
+
+ gtk_main();
+
+ return(0);
+}
+
+/* fin del ejemplo */
+</verb></tscreen>
+
+Observe que el programa no llama a <tt/gtk_signal_connect/ para
+conectar el «delete_event», y que sólo conecta la señal
+«destroy». Con esto seguimos realizando la función deseada, ya que
+un «delete_event» no manejado desenboca en una señal «destroy»
+para la ventana.
+</sect1>
+</sect>
+
+<!-- ***************************************************************** -->
+<sect><em/Widgets/ varios
+<!-- ***************************************************************** -->
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Etiquetas
+<p>
+Las etiquetas se usan mucho en GTK y son bastante simples de manejar.
+No pueden emitir señales ya que no tienen ventanas X window
+asociadas. Si se desea capturar señales se debe usar el <em/widget/
+EventBox o un <em/widget/ botón.
+
+Para crear una nueva etiqueta se usa:
+
+<tscreen><verb>
+GtkWidget *gtk_label_new( char *str );
+</verb></tscreen>
+
+El único argumento es la cadena de texto que se quiere mostrar.
+
+Para cambiarla después de que haya sido creada se usa:
+
+<tscreen><verb>
+void gtk_label_set( GtkLabel *etiqueta,
+ char *str );
+</verb></tscreen>
+
+En este caso el primer argumento es la etiqueta ya creada (cambiado su
+tipo mediante la macro <tt/GTK_LABEL()/) y el segundo es la nueva cadena.
+El espacio que necesite la nueva etiqueta se ajustará
+automáticamente, si es necesario.
+
+Para obtener el estado de la cadena en un momento dado existe la
+función:
+
+<tscreen><verb>
+void gtk_label_get( GtkLabel *etiqueta,
+ char **str );
+</verb></tscreen>
+El primer argumento es la etiqueta, mientras que el segundo es el
+valor devuelto para la cadena. No libere la memoria de la cadena
+devuelta, ya que se utiliza internamente por GTK.
+
+El texto de la etiqueta se puede justificar utilizando:
+
+<tscreen><verb>
+void gtk_label_set_justify( GtkLabel *etiqueta,
+ GtkJustification jtype );
+</verb></tscreen>
+
+Los valores posibles para <tt/jtype/ son:
+<itemize>
+<item> GTK_JUSTIFY_LEFT
+<item> GTK_JUSTIFY_RIGHT
+<item> GTK_JUSTIFY_CENTER (the default)
+<item> GTK_JUSTIFY_FILL
+</itemize>
+
+El <em/widget/ etiqueta también es capaz de separar el texto de forma
+automática cuando se llega al final de una linea. Esto se puede
+conseguir utilizando:
+
+<tscreen><verb>
+void gtk_label_set_line_wrap (GtkLabel *etiqueta,
+ gboolean wrap);
+</verb></tscreen>
+
+El argumento <tt/wrap/ toma el valor TRUE o FALSE.
+
+Si quiere que su etiqueta salga subrayada, puede especificar un motivo
+para el subrayado con:
+
+<tscreen><verb>
+void gtk_label_set_pattern (GtkLabel *etiqueta,
+ const gchar *pattern);
+</verb></tscreen>
+
+El argumento <tt/pattern/ indica cual debe ser el aspecto del
+subrayado. Consiste en una cadena de espacios en blanco y carácteres
+de subrayado. Por ejemplo, la cadena <tt/"__ __"/ debe hacer que
+se subrayen los dos primeros y el octavo y el noveno carácter.
+
+A continuación tenemos un pequeño ejemplo que ilustra el uso de estas
+funciones. Este ejemplo utiliza el <em/widget/ marco (<em/frame/) para
+hacer una mejor demostración de los estilos de la etiqueta. Por ahora
+puede ignorarlo, ya que el <em/widget/ <ref id="sec_Frames"
+name="Frame"> se explicará más tarde.
+
+<tscreen><verb>
+/* principio del ejemplo label label.c */
+
+#include <gtk/gtk.h>
+
+int main( int argc,
+ char *argv[] )
+{
+ static GtkWidget *ventana = NULL;
+ GtkWidget *hbox;
+ GtkWidget *vbox;
+ GtkWidget *frame;
+ GtkWidget *etiqueta;
+
+ /* Inicializa GTK */
+ gtk_init(&amp;argc, &amp;argv);
+
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
+ GTK_SIGNAL_FUNC(gtk_main_quit),
+ NULL);
+
+ gtk_window_set_title (GTK_WINDOW (ventana), "Etiqueta");
+ vbox = gtk_vbox_new (FALSE, 5);
+ hbox = gtk_hbox_new (FALSE, 5);
+ gtk_container_add (GTK_CONTAINER (ventana), hbox);
+ gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (ventana), 5);
+
+ frame = gtk_frame_new ("Normal Label");
+ etiqueta = gtk_label_new ("This is a Normal label");
+ gtk_container_add (GTK_CONTAINER (frame), etiqueta);
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+
+ frame = gtk_frame_new ("Multi-line Label");
+ etiqueta = gtk_label_new ("This is a Multi-line label.\nSecond line\n" \
+ "Third line");
+ gtk_container_add (GTK_CONTAINER (frame), etiqueta);
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+
+ frame = gtk_frame_new ("Left Justified Label");
+ etiqueta = gtk_label_new ("This is a Left-Justified\n" \
+ "Multi-line label.\nThird line");
+ gtk_label_set_justify (GTK_LABEL (etiqueta), GTK_JUSTIFY_LEFT);
+ gtk_container_add (GTK_CONTAINER (frame), etiqueta);
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+
+ frame = gtk_frame_new ("Right Justified Label");
+ etiqueta = gtk_label_new ("This is a Right-Justified\nMulti-line label.\n" \
+ "Fourth line, (j/k)");
+ gtk_label_set_justify (GTK_LABEL (etiqueta), GTK_JUSTIFY_RIGHT);
+ gtk_container_add (GTK_CONTAINER (frame), etiqueta);
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+
+ vbox = gtk_vbox_new (FALSE, 5);
+ gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
+ frame = gtk_frame_new ("Line wrapped label");
+ etiqueta = gtk_label_new ("This is an example of a line-wrapped label. It " \
+ "should not be taking up the entire " /* big space to test spacing */\
+ "width allocated to it, but automatically " \
+ "wraps the words to fit. " \
+ "The time has come, for all good men, to come to " \
+ "the aid of their party. " \
+ "The sixth sheik's six sheep's sick.\n" \
+ " It supports multiple paragraphs correctly, " \
+ "and correctly adds "\
+ "many extra spaces. ");
+ gtk_label_set_line_wrap (GTK_LABEL (etiqueta), TRUE);
+ gtk_container_add (GTK_CONTAINER (frame), etiqueta);
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+
+ frame = gtk_frame_new ("Filled, wrapped label");
+ etiqueta = gtk_label_new ("This is an example of a line-wrapped, filled label. " \
+ "It should be taking "\
+ "up the entire width allocated to it. " \
+ "Here is a seneance to prove "\
+ "my point. Here is another sentence. "\
+ "Here comes the sun, do de do de do.\n"\
+ " This is a new paragraph.\n"\
+ " This is another newer, longer, better " \
+ "paragraph. It is coming to an end, "\
+ "unfortunately.");
+ gtk_label_set_justify (GTK_LABEL (etiqueta), GTK_JUSTIFY_FILL);
+ gtk_label_set_line_wrap (GTK_LABEL (etiqueta), TRUE);
+ gtk_container_add (GTK_CONTAINER (frame), etiqueta);
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+
+ frame = gtk_frame_new ("Underlined label");
+ etiqueta = gtk_label_new ("This label is underlined!\n"
+ "This one is underlined in quite a funky fashion");
+ gtk_label_set_justify (GTK_LABEL (etiqueta), GTK_JUSTIFY_LEFT);
+ gtk_label_set_pattern (GTK_LABEL (etiqueta),
+ "_________________________ _ _________ _ ______ __ _______ ___");
+ gtk_container_add (GTK_CONTAINER (frame), etiqueta);
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+
+ gtk_widget_show_all (ventana);
+
+ gtk_main ();
+
+ return(0);
+}
+/* fin del ejemplo */
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Flechas
+<p>
+En <em/widget/ flecha (<em/arrow/) dibuja la punta de una flecha,
+con un estilo y hacia una dirección a escoger. Puede ser muy útil
+en muchas aplicaciones cuando se coloca en un botón.
+
+Sólo hay dos funciones para manipular el <em/widget/ flecha:
+
+<tscreen><verb>
+GtkWidget *gtk_arrow_new( GtkArrowType arrow_type,
+ GtkShadowType shadow_type );
+
+void gtk_arrow_set( GtkArrow *arrow,
+ GtkArrowType arrow_type,
+ GtkShadowType shadow_type );
+</verb></tscreen>
+
+La primera crea un nuevo <em/widget/ flecha del tipo y apariencia
+indicados. La segunda permite alterar posteriormente estos valores. El
+argumento <tt/arrow_type/ puede tomar uno de los valores siguientes:
+
+<itemize>
+<item> GTK_ARROW_UP
+<item> GTK_ARROW_DOWN
+<item> GTK_ARROW_LEFT
+<item> GTK_ARROW_RIGHT
+</itemize>
+
+Naturalmente, estos valores indican la dirección a la que debe apuntar
+la flecha. El argumento <tt/shadow_type/ puede tomar uno de los
+valores siguientes:
+
+<itemize>
+<item> GTK_SHADOW_IN
+<item> GTK_SHADOW_OUT (por defecto)
+<item> GTK_SHADOW_ETCHED_IN
+<item> GTK_SHADOW_ETCHED_OUT
+</itemize>
+
+Aquí tenemos un pequeño ejemplo para ilustrar la utilización de la
+flecha.
+
+<tscreen><verb>
+/* principio del ejemplo arrow arrow.c */
+
+#include <gtk/gtk.h>
+
+/* Crea un widget flecha con los parámetros especificados
+ * y lo empaqueta en un botón */
+GtkWidget *create_arrow_button( GtkArrowType arrow_type,
+ GtkShadowType shadow_type )
+{
+ GtkWidget *boton;
+ GtkWidget *arrow;
+
+ boton = gtk_button_new();
+ arrow = gtk_arrow_new (arrow_type, shadow_type);
+
+ gtk_container_add (GTK_CONTAINER (boton), arrow);
+
+ gtk_widget_show(boton);
+ gtk_widget_show(arrow);
+
+ return(boton);
+}
+
+int main( int argc,
+ char *argv[] )
+{
+ /* GtkWidget es el tipo utilizado para los widgets */
+ GtkWidget *ventana;
+ GtkWidget *boton;
+ GtkWidget *box;
+
+ /* Inicializa el toolkit */
+ gtk_init (&amp;argc, &amp;argv);
+
+ /* Crea una nueva ventana */
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ gtk_window_set_title (GTK_WINDOW (ventana), "Arrow Buttons");
+
+ /* Es una buena idea hacer esto con todas las ventanas. */
+ gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
+ GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
+
+ /* Establece el ancho del borde de la ventana. */
+ gtk_container_set_border_width (GTK_CONTAINER (ventana), 10);
+
+ /* Crea una caja para almacenar las flechas/botones */
+ box = gtk_hbox_new (FALSE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (box), 2);
+ gtk_container_add (GTK_CONTAINER (ventana), box);
+
+ /* Empaqueta y muestra todos nuestros widgets */
+ gtk_widget_show(box);
+
+ boton = create_arrow_button(GTK_ARROW_UP, GTK_SHADOW_IN);
+ gtk_box_pack_start (GTK_BOX (box), boton, FALSE, FALSE, 3);
+
+ boton = create_arrow_button(GTK_ARROW_DOWN, GTK_SHADOW_OUT);
+ gtk_box_pack_start (GTK_BOX (box), boton, FALSE, FALSE, 3);
+
+ boton = create_arrow_button(GTK_ARROW_LEFT, GTK_SHADOW_ETCHED_IN);
+ gtk_box_pack_start (GTK_BOX (box), boton, FALSE, FALSE, 3);
+
+ boton = create_arrow_button(GTK_ARROW_RIGHT, GTK_SHADOW_ETCHED_OUT);
+ gtk_box_pack_start (GTK_BOX (box), boton, FALSE, FALSE, 3);
+
+ gtk_widget_show (ventana);
+
+ /* Nos quedamos en gtk_main y ¡esperamos que empiece la diversión! */
+ gtk_main ();
+
+ return(0);
+}
+/* fin del ejemplo */
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>El <em/widget/ de información rápida (<em/tooltip/)
+<p>
+Estos <em/widgets/ son las pequeñas etiquetas que texto que
+aparecen cuando se sitúa el puntero del ratón sobre un botón
+u otro <em/widget/ durante algunos segundos. Son bastante fáciles
+de usar, así que no se dará ningún ejemplo. Si quiere ver
+algún ejemplo se recomienda leer el programa testgtk.c que
+acompaña a GTK.
+
+Algunos <em/widgets/ (como la etiqueta) no pueden llevar asociado un
+<em/tooltip/.
+
+Para cada función sólo hay que hacer una llamada para conseguir
+un <em/tooltip/. El objeto <tt/GtkTooltip/ que devuelve la siguiente
+función puede ser usado para crear múltiples <em/widgets/.
+
+<tscreen><verb>
+GtkTooltips *gtk_tooltips_new( void );
+</verb></tscreen>
+
+Una vez que el <em/tooltip/ ha sido creado (y el <em/widget/ sobre el
+que se quiere usar) simplemente hay que usar la siguiente llamada para
+pegarlo:
+
+<tscreen><verb>
+void gtk_tooltips_set_tip( GtkTooltips *tooltips,
+ GtkWidget *widget,
+ const gchar *tip_text,
+ const gchar *tip_private );
+</verb></tscreen>
+
+El primer argumento es el <em/tooltip/ que ya ha creado, seguido del
+<em/widget/ al que se desea asociar el <em/tooltip/, el tercero es el
+texto que se quiere que aparezca y el último es una cadena de texto
+que puede ser usada como un identificador cuando se usa GtkTipsQuery
+para desarollar ayuda sensible al contexto. Por ahora conviene dejarlo
+como NULL.
+
+<!-- TODO: sort out what how to do the context sensitive help -->
+
+Veamos un ejemplo:
+
+<tscreen><verb>
+GtkTooltips *tooltips;
+GtkWidget *boton;
+...
+tooltips = gtk_tooltips_new ();
+boton = gtk_button_new_with_label ("botón 1");
+...
+gtk_tooltips_set_tip (tooltips, boton, "Este es el botón 1", NULL);
+</verb></tscreen>
+
+Existen otras funciones que pueden ser usadas con los <em/tooltips/.
+Solamente vamos a enumerlarlas añadiendo una pequeña descripción
+de que hace cada una.
+
+<tscreen><verb>
+void gtk_tooltips_enable( GtkTooltips *tooltips );
+</verb></tscreen>
+
+Permite que funcionen un conjunto de <em/tooltips/
+
+<tscreen><verb>
+void gtk_tooltips_disable( GtkTooltips *tooltips );
+</verb></tscreen>
+
+Oculta un conjunto de <em/tooltips/ para que no pueda ser mostrado.
+
+<tscreen><verb>
+void gtk_tooltips_set_delay( GtkTooltips *tooltips,
+ gint delay );
+
+</verb></tscreen>
+
+Establece cuantos milisegundos tiene que estar el puntero sobre el
+<em/widget/ para que aparezca el <em/tooltip/. Por defecto se usan 1000
+milisegundos (1 segundo).
+
+<tscreen><verb>
+void gtk_tooltips_set_colors( GtkTooltips *tooltips,
+ GdkColor *background,
+ GdkColor *foreground );
+</verb></tscreen>
+
+Establece el color del texto y del fondo del <em/tooltip/. No se como
+se especifica el color.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Barras de progreso <label id="sec_ProgressBar">
+<p>
+Estas barras se usan para mostrar el estado de una operación. Son
+bastante sencillas de utilizar, tal y como se verá en los ejemplos
+siguientes. Pero primero vamos a ver cuales son las funciones que hay
+que utilizar para crear una nueva barra de progreso.
+
+Hay dos formas de crear una nueva barra de progreso, la sencilla no
+necesita de argumentos, y la otra recibe un objeto GtkAdjustment. Si
+se utiliza la primera forma, la barra de progreso creará su propio
+GtkAdjustment.
+
+<tscreen><verb>
+GtkWidget *gtk_progress_bar_new( void );
+
+GtkWidget *gtk_progress_bar_new_with_adjustment( GtkAdjustment *adjustment );
+</verb></tscreen>
+
+El segundo método tiene la ventaja de que podemos utilizar el objeto
+adjustment para especificar nuestro propio rango de parámetros para la
+barra de progreso.
+
+El ajuste de una barra de progreso se puede cambiar de forma dinámica
+utilizando:
+
+<tscreen><verb>
+void gtk_progress_set_adjustment( GtkProgress *progress,
+ GtkAdjustment *adjustment );
+</verb></tscreen>
+
+Ahora que hemos creado la barra de progreso ya podemos utilizarla.
+
+<tscreen><verb>
+void gtk_progress_bar_update( GtkProgressBar *pbar,
+ gfloat percentage );
+</verb></tscreen>
+
+El primer argumento es la barra que se quiere manejar, el segundo es
+tanto por ciento que ha sido `completado' (indica cuanto ha sido
+llenada la barra y oscila entre 0-100%). El valor que se le tiene que
+pasar oscila entre 0 y 1.
+
+GTK+ v1.2 ha añadido una nueva característica a la barra de progreso,
+y es que ahora permite mostrar su valor de varias maneras distintas, e
+informar al usuario del valor y rango actual.
+
+Una barra de progreso puede mostrarse con distintas orientaciones
+utilizando la función
+
+<tscreen><verb>
+void gtk_progress_bar_set_orientation( GtkProgressBar *pbar,
+ GtkProgressBarOrientation orientation );
+</verb></tscreen>
+
+Donde el argumento <tt/orientación/ puede tomar uno de los valores que
+vienen a continuación para indicar la dirección en la que se mueve la
+barra de progreso:
+
+<itemize>
+<item> GTK_PROGRESS_LEFT_TO_RIGHT
+<item> GTK_PROGRESS_RIGHT_TO_LEFT
+<item> GTK_PROGRESS_BOTTOM_TO_TOP
+<item> GTK_PROGRESS_TOP_TO_BOTTOM
+</itemize>
+
+Cuando se utiliza como una medida de cuanto se ha completado de un
+proceso, la barra de progreso puede configurarse para que muestre su
+valor de una forma continua o discreta. En modo continuo, la barra de
+progreso se actualiza mediante un número discreto de bloques, el
+número de bloques también es configurable.
+
+Se puede configurar el estilo de la barra de progreso utilizando la
+siguiente función:
+
+<tscreen><verb>
+void gtk_progress_bar_set_bar_style( GtkProgressBar *pbar,
+ GtkProgressBarStyle style );
+</verb></tscreen>
+
+El parámetro <tt/style/ puede tomar uno de los dos valores siguientes:
+
+<itemize>
+<item>GTK_PROGRESS_CONTINUOUS
+<item>GTK_PROGRESS_DISCRETE
+</itemize>
+
+El número de bloques se puede establecer utilizando
+
+<tscreen><verb>
+void gtk_progress_bar_set_discrete_blocks( GtkProgressBar *pbar,
+ guint blocks );
+</verb></tscreen>
+
+La barra de progreso también se puede utilizar, a parte de para
+indicar lo «avanzado» de una tarea, para indicar que hay algún tipo
+de actividad. Esto puede ser útil en situaciones donde no se pueda
+medir el progreso de una tarea con un rango de valores. Para el modo
+actividad, no sirve el estilo de barra que se ha descrito más
+arriba. Este modo hay que seleccionarlo utilizando la siguiente
+función:
+
+<tscreen><verb>
+void gtk_progress_set_activity_mode( GtkProgress *progress,
+ guint activity_mode );
+</verb></tscreen>
+
+El tamaño del paso del indicador de actividad, y el número de bloques
+se indican usando las siguientes funciones:
+
+<tscreen><verb>
+void gtk_progress_bar_set_activity_step( GtkProgressBar *pbar,
+ guint step );
+
+void gtk_progress_bar_set_activity_blocks( GtkProgressBar *pbar,
+ guint blocks );
+</verb></tscreen>
+
+Cuando estamos en modo continuo, la barra de progreso puede mostrar un
+texto configurable dentro la barra misma, utilizando la función
+siguiente:
+
+<tscreen><verb>
+void gtk_progress_set_format_string( GtkProgress *progress,
+ gchar *format);
+</verb></tscreen>
+
+El argumento <tt/format/ es parecido al que se utiliza en una orden
+<tt/printf/ de C. Se pueden utilizar las siguientes opciones para el
+formateado de la cadena:
+
+<itemize>
+<item> %p - porcentaje
+<item> %v - valor
+<item> %l - valor inferior del rango
+<item> %u - valor superior del rango
+</itemize>
+
+Puede activar o desactivar el texto utilizando:
+
+<tscreen><verb>
+void gtk_progress_set_show_text( GtkProgress *progress,
+ gint show_text );
+</verb></tscreen>
+
+El argumento <tt/show_text/ es un valor booleano TRUE/FALSE. La
+apariencia del texto puede modificarse utilizando:
+
+<tscreen><verb>
+void gtk_progress_set_text_alignment( GtkProgress *progress,
+ gfloat x_align,
+ gfloat y_align );
+</verb></tscreen>
+
+Los argumentos <tt/x_align/ y <tt/y_align/ toman un valor entre 0.0 y
+1.0. Este valor indica la posición de la cadena de texto dentro de la
+barra. Si ponemos 0.0 en los dos sitios la cadena de texto aparecerá
+en la esquina superior izquierda; un valor de 0.5 (el que se utiliza
+por defecto) centra el texto, y un valor de 1.0 coloca el texto en la
+esquina inferior derecha.
+
+Se pueden leer los parámetros actuales del texto de un objeto barra
+de progreso utilizando las dos funciones que se muestran a
+continuación. La cadena de carácteres devuelta por estas funciones
+debe liberarse en la aplicación (utilizando la función
+g_free()). Estas funciones devuelven el texto formateado que se
+mostrará en la barra.
+
+<tscreen><verb>
+gchar *gtk_progress_get_current_text( GtkProgress *progress );
+
+gchar *gtk_progress_get_text_from_value( GtkProgress *progress,
+ gfloat value );
+</verb></tscreen>
+
+Hay otra forma de cambiar el rango y el valor de un objeto barra de
+progreso utilizando la función:
+
+<tscreen><verb>
+void gtk_progress_configure( GtkProgress *progress,
+ gfloat value,
+ gfloat min,
+ gfloat max );
+</verb></tscreen>
+
+Esta función proporciona una interfaz sencilla al rango y valor de una
+barra de progreso.
+
+Las funciones restantes se pueden utilizar para obtener y establecer
+el valor actual de una barra de progreso utilizando distintos tipos y
+formatos para el valor.
+
+<tscreen><verb>
+void gtk_progress_set_percentage( GtkProgress *progress,
+ gfloat percentage );
+
+void gtk_progress_set_value( GtkProgress *progress,
+ gfloat value );
+
+gfloat gtk_progress_get_value( GtkProgress *progress );
+
+gfloat gtk_progress_get_current_percentage( GtkProgress *progress );
+
+gfloat gtk_progress_get_percentage_from_value( GtkProgress *progress,
+ gfloat value );
+</verb></tscreen>
+
+Estas funciones son autoexplicatorias. La última función utiliza el
+ajuste de la barra de progreso especificada para calcular el
+porcentaje dentro del rango de valores de la barra.
+
+Las barras de progreso se usan con otras funciones como los tiempos de
+espera (<em/timeouts/), sección <ref id="sec_timeouts"
+name="Tiempos de espera, E/S (I/O) y funciones ociosas (idle)">) para
+crear la ilusión de la multitarea. Todas usan la función
+gtk_progress_bar_update de la misma manera.
+
+Estudiemos un ejemplo de barras de progreso actualizada usando
+tiempos de espera. También se muestra como se debe reestablecer una
+barra.
+
+<tscreen><verb>
+/* comienzo del programa-ejemplo progressbar.c */
+
+#include <gtk/gtk.h>
+
+#include <gtk/gtk.h>
+
+typedef struct _ProgressData {
+ GtkWidget *ventana;
+ GtkWidget *pbar;
+ int timer;
+} ProgressData;
+
+/* Actualiza el valor de la barra de progreso para que
+ * podamos ver algún movimiento */
+gint progress_timeout( gpointer data )
+{
+ gfloat new_val;
+ GtkAdjustment *adj;
+
+ /* Calcula el valor de la barra de progreso utilizando
+ * el rango de valores establecido en el ajuste de la
+ * barra */
+
+ new_val = gtk_progress_get_value( GTK_PROGRESS(data) ) + 1;
+
+ adj = GTK_PROGRESS (data)->adjustment;
+ if (new_val > adj->upper)
+ new_val = adj->lower;
+
+ /* Establece el nuevo valor */
+
+ gtk_progress_set_value (GTK_PROGRESS (data), new_val);
+
+ /* Como esta es una función de espera, devolvemos TRUE
+ * para que continue siendo llamada */
+
+ return(TRUE);
+}
+
+/* Función de llamada que activa/desactiva el texto de dentro
+ * de la barra de progreso */
+void toggle_show_text( GtkWidget *widget,
+ ProgressData *pdata )
+{
+ gtk_progress_set_show_text (GTK_PROGRESS (pdata->pbar),
+ GTK_TOGGLE_BUTTON (widget)->active);
+}
+
+/* Función de llamada que activa/desactiva el modo actividad
+ * de la barra de progreso */
+void toggle_activity_mode( GtkWidget *widget,
+ ProgressData *pdata )
+{
+ gtk_progress_set_activity_mode (GTK_PROGRESS (pdata->pbar),
+ GTK_TOGGLE_BUTTON (widget)->active);
+}
+
+/* Función de llamada que activa/desactiva el modo continuo
+ * de la barra de progreso */
+void set_continuous_mode( GtkWidget *widget,
+ ProgressData *pdata )
+{
+ gtk_progress_bar_set_bar_style (GTK_PROGRESS_BAR (pdata->pbar),
+ GTK_PROGRESS_CONTINUOUS);
+}
+
+/* Función de llamada que activa/desactiva el modo discreto
+ * de la barra de progreso */
+void set_discrete_mode( GtkWidget *widget,
+ ProgressData *pdata )
+{
+ gtk_progress_bar_set_bar_style (GTK_PROGRESS_BAR (pdata->pbar),
+ GTK_PROGRESS_DISCRETE);
+}
+
+/* Libera la memoria y elimina el temporizador */
+void destroy_progress( GtkWidget *widget,
+ ProgressData *pdata)
+{
+ gtk_timeout_remove (pdata->timer);
+ pdata->timer = 0;
+ pdata->ventana = NULL;
+ g_free(pdata);
+ gtk_main_quit();
+}
+
+int main( int argc,
+ char *argv[])
+{
+ ProgressData *pdata;
+ GtkWidget *align;
+ GtkWidget *separator;
+ GtkWidget *table;
+ GtkAdjustment *adj;
+ GtkWidget *boton;
+ GtkWidget *check;
+ GtkWidget *vbox;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ /* Reserva memoria para los datos que se le pasan a las funciones
+ * de llamada */
+ pdata = g_malloc( sizeof(ProgressData) );
+
+ pdata->ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_policy (GTK_WINDOW (pdata->ventana), FALSE, FALSE, TRUE);
+
+ gtk_signal_connect (GTK_OBJECT (pdata->ventana), "destroy",
+ GTK_SIGNAL_FUNC (destroy_progress),
+ pdata);
+ gtk_window_set_title (GTK_WINDOW (pdata->ventana), "GtkProgressBar");
+ gtk_container_set_border_width (GTK_CONTAINER (pdata->ventana), 0);
+
+ vbox = gtk_vbox_new (FALSE, 5);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 10);
+ gtk_container_add (GTK_CONTAINER (pdata->ventana), vbox);
+ gtk_widget_show(vbox);
+
+ /* Crea un objeto de alineamiento centrado */
+ align = gtk_alignment_new (0.5, 0.5, 0, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), align, FALSE, FALSE, 5);
+ gtk_widget_show(align);
+
+ /* Crea un objeto GtkAdjusment para albergar el rango de la barra
+ * de progreso */
+ adj = (GtkAdjustment *) gtk_adjustment_new (0, 1, 150, 0, 0, 0);
+
+ /* Crea la GtkProgressBar utilizando el ajuste */
+ pdata->pbar = gtk_progress_bar_new_with_adjustment (adj);
+
+ /* Establece el formato de la cadena de texto que puede mostrarse
+ * en la barra de progreso:
+ * %p - porcentaje
+ * %v - valor
+ * %l - valor inferior del rango
+ * %u - valor superior del rango */
+ gtk_progress_set_format_string (GTK_PROGRESS (pdata->pbar),
+ "%v from [%l-%u] (=%p%%)");
+ gtk_container_add (GTK_CONTAINER (align), pdata->pbar);
+ gtk_widget_show(pdata->pbar);
+
+ /* Añade un temporizador para la actualización del valor de la
+ * barra de progreso */
+ pdata->timer = gtk_timeout_add (100, progress_timeout, pdata->pbar);
+
+ separator = gtk_hseparator_new ();
+ gtk_box_pack_start (GTK_BOX (vbox), separator, FALSE, FALSE, 0);
+ gtk_widget_show(separator);
+
+ /* filas, columnas, homogéneo */
+ table = gtk_table_new (2, 3, FALSE);
+ gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, TRUE, 0);
+ gtk_widget_show(table);
+
+ /* Añade un botón de comprobación para seleccionar si se debe
+ * mostrar el texto dentro de la barra */
+ check = gtk_check_button_new_with_label ("Show text");
+ gtk_table_attach (GTK_TABLE (table), check, 0, 1, 0, 1,
+ GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL,
+ 5, 5);
+ gtk_signal_connect (GTK_OBJECT (check), "clicked",
+ GTK_SIGNAL_FUNC (toggle_show_text),
+ pdata);
+ gtk_widget_show(check);
+
+ /* Añade un botón de comprobación para activar/desactivar el modo
+ * actividad */
+ check = gtk_check_button_new_with_label ("Activity mode");
+ gtk_table_attach (GTK_TABLE (table), check, 0, 1, 1, 2,
+ GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL,
+ 5, 5);
+ gtk_signal_connect (GTK_OBJECT (check), "clicked",
+ GTK_SIGNAL_FUNC (toggle_activity_mode),
+ pdata);
+ gtk_widget_show(check);
+
+ separator = gtk_vseparator_new ();
+ gtk_table_attach (GTK_TABLE (table), separator, 1, 2, 0, 2,
+ GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL,
+ 5, 5);
+ gtk_widget_show(separator);
+
+ /* Añade un botón circular para seleccionar el modo continuo */
+ boton = gtk_radio_button_new_with_label (NULL, "Continuous");
+ gtk_table_attach (GTK_TABLE (table), boton, 2, 3, 0, 1,
+ GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL,
+ 5, 5);
+ gtk_signal_connect (GTK_OBJECT (boton), "clicked",
+ GTK_SIGNAL_FUNC (set_continuous_mode),
+ pdata);
+ gtk_widget_show (boton);
+
+ /* Añade un botón circular para seleccionar el modo discreto */
+ boton = gtk_radio_button_new_with_label(
+ gtk_radio_button_group (GTK_RADIO_BUTTON (boton)),
+ "Discrete");
+ gtk_table_attach (GTK_TABLE (table), boton, 2, 3, 1, 2,
+ GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL,
+ 5, 5);
+ gtk_signal_connect (GTK_OBJECT (boton), "clicked",
+ GTK_SIGNAL_FUNC (set_discrete_mode),
+ pdata);
+ gtk_widget_show (boton);
+
+ separator = gtk_hseparator_new ();
+ gtk_box_pack_start (GTK_BOX (vbox), separator, FALSE, FALSE, 0);
+ gtk_widget_show(separator);
+
+ /* Añade un botón para salir del programa */
+ boton = gtk_button_new_with_label ("close");
+ gtk_signal_connect_object (GTK_OBJECT (boton), "clicked",
+ (GtkSignalFunc) gtk_widget_destroy,
+ GTK_OBJECT (pdata->ventana));
+ gtk_box_pack_start (GTK_BOX (vbox), boton, FALSE, FALSE, 0);
+
+ /* Esto hace que este botón sea el botón pueda utilizarse por
+ * defecto defecto. */
+ GTK_WIDGET_SET_FLAGS (boton, GTK_CAN_DEFAULT);
+
+ /* Esto marca este botón para que sea el botón por
+ * defecto. Simplemente utilizando la tecla "Intro" haremos que se
+ * active este botón. */
+ gtk_widget_grab_default (boton);
+ gtk_widget_show(boton);
+
+ gtk_widget_show (pdata->ventana);
+
+ gtk_main ();
+
+ return(0);
+}
+/* final del ejemplo */
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Cuadros de diálogo
+<p>
+El <em/widget/ del cuadro de diálogo es bastante simple, sólo es una
+ventana con algunas cosas ya preempaquetadas. Su estructura es la
+siguiente:
+
+<tscreen><verb>
+struct GtkDialog
+{
+ GtkWindow ventana;
+
+ GtkWidget *vbox;
+ GtkWidget *action_area;
+};
+</verb></tscreen>
+
+Simplemente se crea una ventana en la cual se empaqueta una vbox, un
+separador y una hbox llamada «action_area».
+
+Este tipo de <em/widgets/ pueden ser usados como mensages <em/pop-up/
+(pequeñas ventanas con texto en su interior que aparecen cuando el
+usuario hace algo y queremos informarle de alguna cosa) y otras cosas
+parecidas. Su manejo desde el punto de vista del programador
+es bastante fácil, sólo hay que usar una función:
+
+<tscreen><verb>
+GtkWidget *gtk_dialog_new( void );
+</verb></tscreen>
+
+Para crear un nuevo cuadro de diálogo hay que llamar a:
+
+<tscreen><verb>
+GtkWidget *ventana;
+ventana = gtk_dialog_new ();
+</verb></tscreen>
+
+Una vez que el cuadro ha sido creado sólo hay que usarlo. Por
+ejemplo para empaquetar un botón en la action_area escribiríamos
+algo así:
+
+<tscreen><verb>
+boton = ...
+gtk_box_pack_start (GTK_BOX (GTK_DIALOG (ventana)->action_area), boton,
+ TRUE, TRUE, 0);
+gtk_widget_show (boton);
+</verb></tscreen>
+
+Otra cosa que nos puede interesar es empaquetar una etiqueta en la
+vbox:
+
+<tscreen><verb>
+etiqueta = gtk_label_new ("Dialogs are groovy");
+gtk_box_pack_start (GTK_BOX (GTK_DIALOG (ventana)->vbox), etiqueta, TRUE,
+ TRUE, 0);
+gtk_widget_show (etiqueta);
+</verb></tscreen>
+
+Otros ejemplo posible es poner dos botones en el action_area (uno
+para cancelar y el otro para permitir algo) junto con una etiqueta en
+la vbox el usuario puede seleccionar lo que quiera.
+
+Si se precisa algo más complejo siempre se puede empaquetar otro
+<em/widget/ en cualquiera de las cajas (p.j. una tabla en una vbox).
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> <em/Pixmaps/ <label id="sec_Pixmaps">
+<p>
+Los <em/pixmaps/ son estructuras de datos que contienen dibujos. Estos
+pueden ser usados en diferentes lugares, pero los iconos y los
+cursores son los más comunes.
+
+Un <em/bitmap/ es un <em/pixmap/ que sólo tiene dos colores, y hay
+unas cuantas rutinas especiales para controlar este caso particular.
+
+Para comprender los <em/pixmaps/, puede ayudar entender como funciona
+X-windows. Bajo X-windows, las aplicaciones no tienen porque estar
+ejecutándose en el ordenador que está interactuando con el
+usuario. Las distintas aplicaciones, llamadas «clientes», comunican
+con un programa que muestra los gráficos y que controla el tecledo y
+el ratón. Este programa que interactua directamente con el usuario se
+llama un «<em/display server/» o «servidor X». Como la
+comunicación entre el servidor y el cliente puede llevarse a cabo
+mediante una red, es importante mantener alguna información en el
+servidor X. Los <em/pixmaps/ por ejemplo, se almacenan en la memoria
+del servidor X. Esto significa que una vez que se establecen los
+valores del <em/pixmap/, no tienen que estar transmitiéndose por la
+red; en su lugar lo único que hay que enviar es una orden del estilo
+«mostrar <em/pixmap/ número XYZ aquí». Incluso si no está utilizando
+X-windows con GTK, al utilizar construcciones como los <em/pixmaps/
+conseguirá que sus programas funciones de forma aceptable bajo
+X-windows.
+
+Para usar un <em/pixmap/ en GTK primero tiene que construir una
+estructura del tipo GdkPixmap usando rutinas de GDK. Los <em/pixmaps/
+se pueden crear usando datos que se encuentren en la memoria o en un
+archivo. Veremos con detalle cada una de las dos posibilidades.
+
+<tscreen><verb>
+GdkPixmap *gdk_bitmap_create_from_data( GdkWindow *ventana,
+ gchar *data,
+ gint width,
+ gint height );
+</verb></tscreen>
+
+Esta rutina se utiliza para crear un <em/bitmap/ a partir de datos
+almacenados en la memoria. Cada bit de información indica si el
+<em/pixel/ luce o no. Tanto la altura como la anchura estan expresadas
+en <em/pixels/. El puntero del tipo GdkWindow indica la ventana en
+cuestión, ya que los <em/pixmaps/ sólo tienen sentido dentro de
+la pantalla en la que van a ser mostrados.
+
+<tscreen><verb>
+GdkPixmap *gdk_pixmap_create_from_data( GdkWindow *ventana,
+ gchar *data,
+ gint width,
+ gint height,
+ gint depth,
+ GdkColor *fg,
+ GdkColor *bg );
+</verb></tscreen>
+
+Con esto creamos un <em/pixmap/ con la profundidad (número de
+colores) especificada en los datos del <em/bitmap/. Los valores
+<tt/fg/ y <tt/bg/ son los colores del frente y del fondo
+respectivamente.
+
+<tscreen><verb>
+GdkPixmap *gdk_pixmap_create_from_xpm( GdkWindow *ventana,
+ GdkBitmap **mask,
+ GdkColor *transparent_color,
+ const gchar *filename );
+</verb></tscreen>
+
+El formato XPM es una representacion de los <em/pixmaps/ para el
+sistema X Window. Es bastante popular y existen muchos programas para
+crear imágenes en este formato. El archivo especificado mediante
+<tt/filename/ debe contener una imagen en ese formato para que sea
+cargada en la estructura. La máscara especifica que bits son
+opacos. Todos los demás bits se colorean usando el color
+especificado en <tt/transparent_color/. Más adelante veremos un
+ejemplo.
+
+<tscreen><verb>
+GdkPixmap *gdk_pixmap_create_from_xpm_d( GdkWindow *ventana,
+ GdkBitmap **mask,
+ GdkColor *transparent_color,
+ gchar **data );
+</verb></tscreen>
+
+Se pueden incorporar imágenes pequeñas dentro de un programa en
+formato XPM. Un <em/pixmap/ se crea usando esta información, en
+lugar de leerla de un archivo. Un ejemplo sería:
+
+<tscreen><verb>
+/* XPM */
+static const char * xpm_data[] = {
+"16 16 3 1",
+" c None",
+". c #000000000000",
+"X c #FFFFFFFFFFFF",
+" ",
+" ...... ",
+" .XXX.X. ",
+" .XXX.XX. ",
+" .XXX.XXX. ",
+" .XXX..... ",
+" .XXXXXXX. ",
+" .XXXXXXX. ",
+" .XXXXXXX. ",
+" .XXXXXXX. ",
+" .XXXXXXX. ",
+" .XXXXXXX. ",
+" .XXXXXXX. ",
+" ......... ",
+" ",
+" "};
+</verb></tscreen>
+
+Cuando hayamos acabado de usar un <em/pixmap/ y no lo vayamos a usar
+durante un tiempo suele ser conveniente liberar el recurso mediante
+gdk_pixmap_unref(). (Los <em/pixmaps/ deben ser considerados recursos
+preciosos).
+
+Una vez que hemos creado el <em/pixmap/ lo podemos mostrar como un
+<em/widget/ GTK. Primero tenemos que crear un <em/widget pixmap/ que
+contenga un <em/pixmap/ GDK. Esto se hace usando:
+
+<tscreen><verb>
+GtkWidget *gtk_pixmap_new( GdkPixmap *pixmap,
+ GdkBitmap *mask );
+</verb></tscreen>
+
+Las otras funciones del <em/widget pixmap/ son:
+
+<tscreen><verb>
+guint gtk_pixmap_get_type( void );
+
+void gtk_pixmap_set( GtkPixmap *pixmap,
+ GdkPixmap *val,
+ GdkBitmap *mask );
+
+void gtk_pixmap_get( GtkPixmap *pixmap,
+ GdkPixmap **val,
+ GdkBitmap **mask);
+</verb></tscreen>
+
+La función gtk_pixmap_set se usa para cambiar los datos del
+<em/pixmap/ que el <em/widget/ está manejando en ese
+momento. <tt/val/ es el <em/pixmap/ creado usando GDK.
+
+El ejemplo siguiente usa un <em/pixmap/ en un botón:
+
+<tscreen><verb>
+/* comienzo del ejemplo pixmap.c */
+
+#include <gtk/gtk.h>
+
+
+/* Datos en formato XPM del icono de apertura de archivo */
+static const char * xpm_data[] = {
+"16 16 3 1",
+" c None",
+". c #000000000000",
+"X c #FFFFFFFFFFFF",
+" ",
+" ...... ",
+" .XXX.X. ",
+" .XXX.XX. ",
+" .XXX.XXX. ",
+" .XXX..... ",
+" .XXXXXXX. ",
+" .XXXXXXX. ",
+" .XXXXXXX. ",
+" .XXXXXXX. ",
+" .XXXXXXX. ",
+" .XXXXXXX. ",
+" .XXXXXXX. ",
+" ......... ",
+" ",
+" "};
+
+/* Cuando se llama a esta función (usando signal delete_event) se
+ * termina la aplicación*/
+
+void close_application( GtkWidget *widget, GdkEvent *event, gpointer data ) {
+ gtk_main_quit();
+}
+
+
+/* Al presionar el botón aparece el mensaje */
+void button_clicked( GtkWidget *widget, gpointer data ) {
+ printf( "botón pulsado\n" );
+}
+
+int main( int argc, char *argv[] )
+{
+
+ GtkWidget *ventana, *pixmapwid, *boton;
+ GdkPixmap *pixmap;
+ GdkBitmap *mask;
+ GtkStyle *style;
+
+ /* Creamos la ventana principal y relacionamos la señal
+ * delete_event con acabar el programa.*/
+ gtk_init( &amp;argc, &amp;argv );
+ ventana = gtk_window_new( GTK_WINDOW_TOPLEVEL );
+ gtk_signal_connect( GTK_OBJECT (ventana), "delete_event",
+ GTK_SIGNAL_FUNC (close_application), NULL );
+ gtk_container_border_width( GTK_CONTAINER (ventana), 10 );
+ gtk_widget_show( ventana );
+
+ /* Ahora para el pixmap de gdk */
+ style = gtk_widget_get_style( ventana );
+ pixmap = gdk_pixmap_create_from_xpm_d( ventana->window, &amp;mask,
+ &amp;style->bg[GTK_STATE_NORMAL],
+ (gchar **)xpm_data );
+
+ /* Un pixmap widget que contendrá al pixmap */
+ pixmapwid = gtk_pixmap_new( pixmap, mask );
+ gtk_widget_show( pixmapwid );
+
+ /* Un botón para contener al pixmap */
+ boton = gtk_button_new();
+ gtk_container_add( GTK_CONTAINER(boton), pixmapwid );
+ gtk_container_add( GTK_CONTAINER(ventana), boton );
+ gtk_widget_show( boton );
+
+ gtk_signal_connect( GTK_OBJECT(boton), "clicked",
+ GTK_SIGNAL_FUNC(button_clicked), NULL );
+
+ /* mostramos la ventana */
+ gtk_main ();
+
+ return 0;
+}
+/* final del ejemplo */
+</verb></tscreen>
+
+Para cargar un archivo llamado icon0.xpm con la información XPM (que
+se encuentra en en directorio actual) habríamos usado:
+
+<tscreen><verb>
+ /* cargar un pixmap desde un fichero */
+ pixmap = gdk_pixmap_create_from_xpm( ventana->window, &amp;mask,
+ &amp;style->bg[GTK_STATE_NORMAL],
+ "./icon0.xpm" );
+ pixmapwid = gtk_pixmap_new( pixmap, mask );
+ gtk_widget_show( pixmapwid );
+ gtk_container_add( GTK_CONTAINER(ventana), pixmapwid );
+</verb></tscreen>
+
+Una desventaja de los <em/pixmaps/ es que la imagen mostrada siempre
+es rectangular (independientemente de como sea la imagen en sí). Si
+queremos usar imágenes con otras formas debemos usar ventanas con
+forma (<em/shaped windows/).
+
+Este tipo de ventanas son pixmaps en los que el fondo es
+transparente. Así cuando la imagen del fondo tiene muchos colores
+no los sobreescribimos con el borde de nuestro icono. El ejemplo
+siguiente muestra la imagen de una carretilla en el escritorio.
+
+<tscreen><verb>
+/* comienzo del ejemplo carretilla wheelbarrow.c */
+
+#include <gtk/gtk.h>
+
+/* XPM */
+static char * WheelbarrowFull_xpm[] = {
+"48 48 64 1",
+" c None",
+". c #DF7DCF3CC71B",
+"X c #965875D669A6",
+"o c #71C671C671C6",
+"O c #A699A289A699",
+"+ c #965892489658",
+"@ c #8E38410330C2",
+"# c #D75C7DF769A6",
+"$ c #F7DECF3CC71B",
+"% c #96588A288E38",
+"&amp; c #A69992489E79",
+"* c #8E3886178E38",
+"= c #104008200820",
+"- c #596510401040",
+"; c #C71B30C230C2",
+": c #C71B9A699658",
+"> c #618561856185",
+", c #20811C712081",
+"< c #104000000000",
+"1 c #861720812081",
+"2 c #DF7D4D344103",
+"3 c #79E769A671C6",
+"4 c #861782078617",
+"5 c #41033CF34103",
+"6 c #000000000000",
+"7 c #49241C711040",
+"8 c #492445144924",
+"9 c #082008200820",
+"0 c #69A618611861",
+"q c #B6DA71C65144",
+"w c #410330C238E3",
+"e c #CF3CBAEAB6DA",
+"r c #71C6451430C2",
+"t c #EFBEDB6CD75C",
+"y c #28A208200820",
+"u c #186110401040",
+"i c #596528A21861",
+"p c #71C661855965",
+"a c #A69996589658",
+"s c #30C228A230C2",
+"d c #BEFBA289AEBA",
+"f c #596545145144",
+"g c #30C230C230C2",
+"h c #8E3882078617",
+"j c #208118612081",
+"k c #38E30C300820",
+"l c #30C2208128A2",
+"z c #38E328A238E3",
+"x c #514438E34924",
+"c c #618555555965",
+"v c #30C2208130C2",
+"b c #38E328A230C2",
+"n c #28A228A228A2",
+"m c #41032CB228A2",
+"M c #104010401040",
+"N c #492438E34103",
+"B c #28A2208128A2",
+"V c #A699596538E3",
+"C c #30C21C711040",
+"Z c #30C218611040",
+"A c #965865955965",
+"S c #618534D32081",
+"D c #38E31C711040",
+"F c #082000000820",
+" ",
+" .XoO ",
+" +@#$%o&amp; ",
+" *=-;#::o+ ",
+" >,<12#:34 ",
+" 45671#:X3 ",
+" +89<02qwo ",
+"e* >,67;ro ",
+"ty> 459@>+&amp;&amp; ",
+"$2u+ ><ipas8* ",
+"%$;=* *3:.Xa.dfg> ",
+"Oh$;ya *3d.a8j,Xe.d3g8+ ",
+" Oh$;ka *3d$a8lz,,xxc:.e3g54 ",
+" Oh$;kO *pd$%svbzz,sxxxxfX..&amp;wn> ",
+" Oh$@mO *3dthwlsslszjzxxxxxxx3:td8M4 ",
+" Oh$@g&amp; *3d$XNlvvvlllm,mNwxxxxxxxfa.:,B* ",
+" Oh$@,Od.czlllllzlmmqV@V#V@fxxxxxxxf:%j5&amp; ",
+" Oh$1hd5lllslllCCZrV#r#:#2AxxxxxxxxxcdwM* ",
+" OXq6c.%8vvvllZZiqqApA:mq:Xxcpcxxxxxfdc9* ",
+" 2r<6gde3bllZZrVi7S@SV77A::qApxxxxxxfdcM ",
+" :,q-6MN.dfmZZrrSS:#riirDSAX@Af5xxxxxfevo",
+" +A26jguXtAZZZC7iDiCCrVVii7Cmmmxxxxxx%3g",
+" *#16jszN..3DZZZZrCVSA2rZrV7Dmmwxxxx&amp;en",
+" p2yFvzssXe:fCZZCiiD7iiZDiDSSZwwxx8e*>",
+" OA1<jzxwwc:$d%NDZZZZCCCZCCZZCmxxfd.B ",
+" 3206Bwxxszx%et.eaAp77m77mmmf3&amp;eeeg* ",
+" @26MvzxNzvlbwfpdettttttttttt.c,n&amp; ",
+" *;16=lsNwwNwgsvslbwwvccc3pcfu<o ",
+" p;<69BvwwsszslllbBlllllllu<5+ ",
+" OS0y6FBlvvvzvzss,u=Blllj=54 ",
+" c1-699Blvlllllu7k96MMMg4 ",
+" *10y8n6FjvllllB<166668 ",
+" S-kg+>666<M<996-y6n<8* ",
+" p71=4 m69996kD8Z-66698&amp;&amp; ",
+" &amp;i0ycm6n4 ogk17,0<6666g ",
+" N-k-<> >=01-kuu666> ",
+" ,6ky&amp; &amp;46-10ul,66, ",
+" Ou0<> o66y<ulw<66&amp; ",
+" *kk5 >66By7=xu664 ",
+" <<M4 466lj<Mxu66o ",
+" *>> +66uv,zN666* ",
+" 566,xxj669 ",
+" 4666FF666> ",
+" >966666M ",
+" oM6668+ ",
+" *4 ",
+" ",
+" "};
+
+
+
+void close_application( GtkWidget *widget, GdkEvent *event, gpointer data ) {
+ gtk_main_quit();
+}
+
+int main (int argc, char *argv[])
+{
+
+ GtkWidget *ventana, *pixmap, *fixed;
+ GdkPixmap *gdk_pixmap;
+ GdkBitmap *mask;
+ GtkStyle *style;
+ GdkGC *gc;
+
+ /* Creamos la ventana principal y relacionamos la señal
+ * delete_event para terminar la aplicación. Conviene destacar
+ * que la ventana no tendrá título puesto que es popup.*/
+ gtk_init (&amp;argc, &amp;argv);
+ ventana = gtk_window_new( GTK_WINDOW_POPUP );
+ gtk_signal_connect (GTK_OBJECT (ventana), "delete_event",
+ GTK_SIGNAL_FUNC (close_application), NULL);
+ gtk_widget_show (ventana);
+
+ style = gtk_widget_get_default_style();
+ gc = style->black_gc;
+ gdk_pixmap = gdk_pixmap_create_from_xpm_d( ventana->window, &amp;mask,
+ &amp;style->bg[GTK_STATE_NORMAL],
+ WheelbarrowFull_xpm );
+ pixmap = gtk_pixmap_new( gdk_pixmap, mask );
+ gtk_widget_show( pixmap );
+
+
+ fixed = gtk_fixed_new();
+ gtk_widget_set_usize( fixed, 200, 200 );
+ gtk_fixed_put( GTK_FIXED(fixed), pixmap, 0, 0 );
+ gtk_container_add( GTK_CONTAINER(ventana), fixed );
+ gtk_widget_show( fixed );
+
+ /* Con esto cubrimos todo menos la imagen */
+ gtk_widget_shape_combine_mask( ventana, mask, 0, 0 );
+
+ /* mostramos la ventana */
+ gtk_widget_set_uposition( ventana, 20, 400 );
+ gtk_widget_show( ventana );
+ gtk_main ();
+
+ return 0;
+}
+/* final del ejemplo */
+</verb></tscreen>
+
+Para que la carretilla sea más realista podríamos relacionar la
+pulsación del botón con que haga algo. Con las líneas
+siguientes la pulsación del botón hace que se acabe el programa.
+
+<tscreen><verb>
+gtk_widget_set_events( ventana,
+ gtk_widget_get_events( ventana ) |
+ GDK_BUTTON_PRESS_MASK );
+
+gtk_signal_connect( GTK_OBJECT(ventana), "button_press_event",
+ GTK_SIGNAL_FUNC(close_application), NULL );
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Reglas
+<p>
+
+Las reglas son usadas para indicar la posición del puntero del
+ratón en una ventana dada. Una ventana puede tener una regla
+vertical a lo largo de su alto y una horizontal a lo largo de su
+ancho. Un pequeño indicador triangular muestra la relación entre
+el puntero del ratón y la regla.
+
+Las reglas (horizontales y verticales) se crean usando:
+
+<tscreen><verb>
+GtkWidget *gtk_hruler_new( void ); /* horizontal */
+GtkWidget *gtk_vruler_new( void ); /* vertical */
+</verb></tscreen>
+
+Las unidades de la regla pueden ser pixels, pulgadas o centímetros
+(GKD_PIXELS, GDK_INCHES, GDK_CENTIMETRES). Esto se hace usando:
+
+<tscreen><verb>
+void gtk_ruler_set_metric( GtkRuler *ruler,
+ GtkMetricType metric );
+</verb></tscreen>
+
+El valor por defecto es GTK_PIXELS.
+
+<tscreen><verb>
+gtk_ruler_set_metric( GTK_RULER(ruler), GTK_PIXELS );
+</verb></tscreen>
+
+Otra característica importante de las reglas es cómo mostrar las
+unidades de escala y la posicion inicial dónde se situa el indicador.
+Todo esto se consigue mediante:
+
+<tscreen><verb>
+void gtk_ruler_set_range( GtkRuler *ruler,
+ gfloat lower,
+ gfloat upper,
+ gfloat posicion,
+ gfloat max_size );
+</verb></tscreen>
+
+Los argumentos <tt/lower/ (valor más bajo) y <tt/upper/ (más
+alto) delimitan la extensión de la regla. El argumento
+<tt/max_size/ es el número más alto que será mostrado. Como
+es lógico <tt/posicion/ define la posición inicial del indicador
+dentro de la regla.
+
+Una regla vertical puede puede llegar a ser de 800 pixels:
+
+<tscreen><verb>
+gtk_ruler_set_range( GTK_RULER(vruler), 0, 800, 0, 800);
+</verb></tscreen>
+
+Las marcas dentro de la regla oscilarán entre 0 y 800 con una
+periodicidad de 100. Si queremos que varíe entre 7 y 16
+debemos usar:
+
+<tscreen><verb>
+gtk_ruler_set_range( GTK_RULER(vruler), 7, 16, 0, 20);
+</verb></tscreen>
+
+El indicador de la regla es un pequeño triángulo que señala la
+posición del puntero con relación a la regla. Si la regla debe
+seguir al puntero del ratón la señal motion_notify_event debe estar
+conectada con el motion_notify_event de la regla. Para seguir todos
+los movimientos dentro de una ventana conviene usar:
+
+<tscreen><verb>
+#define EVENT_METHOD(i, x) GTK_WIDGET_CLASS(GTK_OBJECT(i)->klass)->x
+
+gtk_signal_connect_object( GTK_OBJECT(area), "motion_notify_event",
+ (GtkSignalFunc)EVENT_METHOD(ruler, motion_notify_event),
+ GTK_OBJECT(ruler) );
+</verb></tscreen>
+
+El siguiente ejemplo crea una zona de dibujo con una regla horizontal
+y otra vertical. El tamaño de la zona de dibujo es de 600 x 400
+<em/pixels/. La regla horizontal oscila entre 7 y 13 con marcas cada
+100 <em/pixels/, mientras que la vertical va desde 0 a 400 con
+separaciones cada 100. La zona de dibujo y las reglas se sitúan
+usando una tabla.
+
+<tscreen><verb>
+/* comienzo del ejemplo rulers.c */
+
+#include <gtk/gtk.h>
+
+#define EVENT_METHOD(i, x) GTK_WIDGET_CLASS(GTK_OBJECT(i)->klass)->x
+
+#define XSIZE 600
+#define YSIZE 400
+
+/* Esta rutina toma el control cuando se pulsa el botón close
+ */
+void close_application( GtkWidget *widget, GdkEvent *event, gpointer data ) {
+ gtk_main_quit();
+}
+
+int main( int argc, char *argv[] ) {
+ GtkWidget *ventana, *table, *area, *hrule, *vrule;
+
+
+ gtk_init( &amp;argc, &amp;argv );
+
+ ventana = gtk_window_new( GTK_WINDOW_TOPLEVEL );
+ gtk_signal_connect (GTK_OBJECT (ventana), "delete_event",
+ GTK_SIGNAL_FUNC( close_application ), NULL);
+ gtk_container_border_width (GTK_CONTAINER (ventana), 10);
+
+ /* creación de la tabla donde pondremos las reglas y la zona de
+ * dibujo */
+ table = gtk_table_new( 3, 2, FALSE );
+ gtk_container_add( GTK_CONTAINER(ventana), table );
+
+ area = gtk_drawing_area_new();
+ gtk_drawing_area_size( (GtkDrawingArea *)area, XSIZE, YSIZE );
+ gtk_table_attach( GTK_TABLE(table), area, 1, 2, 1, 2,
+ GTK_EXPAND|GTK_FILL, GTK_FILL, 0, 0 );
+ gtk_widget_set_events( area, GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK );
+
+ /* La regla horizontal está arriba. Cuando el ratón se mueve
+ * a lo largo de la zona de dibujo el controlador de eventos de la
+ * regla recibe motion_notify_event. */
+ hrule = gtk_hruler_new();
+ gtk_ruler_set_metric( GTK_RULER(hrule), GTK_PIXELS );
+ gtk_ruler_set_range( GTK_RULER(hrule), 7, 13, 0, 20 );
+ gtk_signal_connect_object( GTK_OBJECT(area), "motion_notify_event",
+ (GtkSignalFunc)EVENT_METHOD(hrule,
+ motion_notify_event),
+ GTK_OBJECT(hrule) );
+ /* GTK_WIDGET_CLASS(GTK_OBJECT(hrule)->klass)->motion_notify_event, */
+ gtk_table_attach( GTK_TABLE(table), hrule, 1, 2, 0, 1,
+ GTK_EXPAND|GTK_SHRINK|GTK_FILL, GTK_FILL, 0, 0 );
+
+
+ /* la zona de dibujo el controlador de eventos de la regla recibe
+ * motion_notify_event. */
+ vrule = gtk_vruler_new();
+ gtk_ruler_set_metric( GTK_RULER(vrule), GTK_PIXELS );
+ gtk_ruler_set_range( GTK_RULER(vrule), 0, YSIZE, 10, YSIZE );
+ gtk_signal_connect_object( GTK_OBJECT(area), "motion_notify_event",
+ (GtkSignalFunc)
+ GTK_WIDGET_CLASS(GTK_OBJECT(vrule)->klass)->
+ motion_notify_event,
+ GTK_OBJECT(vrule) );
+ gtk_table_attach( GTK_TABLE(table), vrule, 0, 1, 1, 2,
+ GTK_FILL, GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0 );
+
+
+ /* mostramos todo */
+ gtk_widget_show( area );
+ gtk_widget_show( hrule );
+ gtk_widget_show( vrule );
+ gtk_widget_show( table );
+ gtk_widget_show( ventana );
+ gtk_main();
+
+ return 0;
+}
+/* final del ejemplo */
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Barras de estado
+<p>
+Las barras de estado son widgets usados para mostrar un mensaje. Todo
+aquello que haya sido mostrado se guarda en una pila, con lo que es
+muy fácil repetir el último mensaje.
+
+Para permitir que diferentes partes del programa usen la misma barra
+de estado éstas usan Identificadores por Contexto (Context
+Identifiers) para identificar a los `usuarios'. El mensaje que está
+en lo alto de la pila será el siguiente en mostrarse, sin importar
+el contexto en el que se esté. Los mensajes se almacenan en el
+orden el último en entrar es el primero en salir, y el
+Identificador por Contexto no influye en este orden.
+
+Las barras de estado se crean con una llamada a:
+
+<tscreen><verb>
+GtkWidget *gtk_statusbar_new( void );
+</verb></tscreen>
+
+Se pide un nuevo Identificador por Contexto con una pequeña
+descripción textual del contexto y una llamada a la función:
+
+<tscreen><verb>
+guint gtk_statusbar_get_context_id( GtkStatusbar *statusbar,
+ const gchar *context_description );
+</verb></tscreen>
+
+Hay tres funciones que pueden manipular las barras de estado:
+
+<tscreen><verb>
+guint gtk_statusbar_push( GtkStatusbar *statusbar,
+ guint context_id,
+ gchar *text );
+
+void gtk_statusbar_pop( GtkStatusbar *statusbar)
+ guint context_id );
+
+void gtk_statusbar_remove( GtkStatusbar *statusbar,
+ guint context_id,
+ guint message_id );
+</verb></tscreen>
+
+La primera, gtk_statusbar_push, se utiliza para añadir un nuevo
+mensaje a la barra de estado. Devuelve un Identificador de Mensaje,
+que podemos pasarle más tarde a la función gtk_statusbar_remove para
+eliminar el mensaje con los Identificadores de Contexto y de Mensaje
+que hay en la pila de barras de estado.
+
+La función gtk_statusbar_pop elimina el mensaje que se encuentra
+más alto en pila y que contiene el Identificador por Contexto
+especificado.
+
+El ejemplo siguiente crea una barra de estado y dos botones, uno para
+meter un elemento en la barra y el otro para sacar el último elemento
+introducido.
+
+<tscreen><verb>
+/* Principio del ejemplo de barras de estado statusbar.c */
+
+#include <gtk/gtk.h>
+#include <glib.h>
+
+GtkWidget *status_bar;
+
+void push_item (GtkWidget *widget, gpointer data)
+{
+ static int count = 1;
+ char buff[20];
+
+ g_snprintf(buff, 20, "Item %d", count++);
+ gtk_statusbar_push( GTK_STATUSBAR(status_bar), GPOINTER_TO_INT(data), buff);
+
+ return;
+}
+
+void pop_item (GtkWidget *widget, gpointer data)
+{
+ gtk_statusbar_pop( GTK_STATUSBAR(status_bar), GPOINTER_TO_INT(data) );
+ return;
+}
+
+int main (int argc, char *argv[])
+{
+
+ GtkWidget *ventana;
+ GtkWidget *vbox;
+ GtkWidget *boton;
+
+ int context_id;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ /* crear una nueva ventana */
+ ventana = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_widget_set_usize( GTK_WIDGET (ventana), 200, 100);
+ gtk_window_set_title(GTK_WINDOW (ventana), "GTK Statusbar Example");
+ gtk_signal_connect(GTK_OBJECT (ventana), "delete_event",
+ (GtkSignalFunc) gtk_exit, NULL);
+
+ vbox = gtk_vbox_new(FALSE, 1);
+ gtk_container_add(GTK_CONTAINER(ventana), vbox);
+ gtk_widget_show(vbox);
+
+ status_bar = gtk_statusbar_new();
+ gtk_box_pack_start (GTK_BOX (vbox), status_bar, TRUE, TRUE, 0);
+ gtk_widget_show (status_bar);
+
+ context_id = gtk_statusbar_get_context_id(
+ GTK_STATUSBAR(status_bar), "Statusbar example");
+
+ boton = gtk_button_new_with_label("push item");
+ gtk_signal_connect(GTK_OBJECT(boton), "clicked",
+ GTK_SIGNAL_FUNC (push_item), &amp;context_id);
+ gtk_box_pack_start(GTK_BOX(vbox), boton, TRUE, TRUE, 2);
+ gtk_widget_show(boton);
+
+ boton = gtk_button_new_with_label("pop last item");
+ gtk_signal_connect(GTK_OBJECT(boton), "clicked",
+ GTK_SIGNAL_FUNC (pop_item), &amp;context_id);
+ gtk_box_pack_start(GTK_BOX(vbox), boton, TRUE, TRUE, 2);
+ gtk_widget_show(boton);
+
+ /* siempre mostramos la ventana en el último paso para que todo se
+ * dibuje en la pantalla de un golpe. */
+ gtk_widget_show(ventana);
+
+ gtk_main ();
+
+ return 0;
+}
+/* Final del ejemplo */
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Entrada de texto
+<p>
+El <em/widget/ Entry permite mostrar e introducir texto en una línea
+de un cuadro de diálogo. El texto se puede poner con llamadas a funciones
+que permiten reemplazar, preañadir o añadir el texto al contenido
+actual del <em/widget/ Entry.
+
+Hay dos funciones para crear un <em/widget/ Entry:
+
+<tscreen><verb>
+GtkWidget *gtk_entry_new( void );
+
+GtkWidget *gtk_entry_new_with_max_length( guint16 max );
+</verb></tscreen>
+
+La primera sirve para crear un nuevo <em/widget/ Entry, mientras que la
+segunda crea el <em/widget/ y además establece un límite en la longitud
+del texto que irá en el mismo.
+
+hay varias funciones que sirven para alterar el que texto que se está
+en el <em/widget/ Entry.
+
+<tscreen><verb>
+void gtk_entry_set_text( GtkEntry *entry,
+ const gchar *text );
+
+void gtk_entry_append_text( GtkEntry *entry,
+ const gchar *text );
+
+void gtk_entry_prepend_text( GtkEntry *entry,
+ const gchar *text );
+</verb></tscreen>
+
+La función <tt/gtk_entry_set_text/ cambia el contenido actual del
+<em/widget/ Entry. Las funciones <tt/gtk_entry_append_text/ y
+<tt/gtk_entry_prepend_text/ permiten añadir o preañadir texto.
+
+Las función siguientes permiten decir donde poner el punto de inserción.
+
+<tscreen><verb>
+void gtk_entry_set_position( GtkEntry *entry,
+ gint posicion );
+</verb></tscreen>
+
+Se pueden obtener los contenidos del <em/widget/ llamando a la función
+que se describe a continuación. Obtener los contenidos del <em/widget/
+puede ser útil en las funciones de llamada descritas más adelante.
+
+<tscreen><verb>
+gchar *gtk_entry_get_text( GtkEntry *entry );
+</verb></tscreen>
+
+Si quiere impedir que alguien cambie el contenido del <em/widget/ escribiendo
+en él, utilice la función
+
+<tscreen><verb>
+void gtk_entry_set_editable( GtkEntry *entry,
+ gboolean editable );
+</verb></tscreen>
+
+Esta función permite camiar el estado de edición de un <em/widget/ Entry,
+siendo el argumento <tt/editable/ TRUE o FALSE.
+
+Si estamos utilizando el <em/widget/ Entry en un sitio donde no queremos
+que el texto que se introduce sea visible, como por ejemplo cuando estamos
+introduciendo una clave, podemos utilizar la función siguiente, que también
+admite como argumento una bandera booleana.
+
+<tscreen><verb>
+void gtk_entry_set_visibility( GtkEntry *entry,
+ gboolean visible );
+</verb></tscreen>
+
+Se puede seleccionar una región del texto utilizando la siguiente función.
+Esta función se puede utilizar después de poner algún texto por defecto en
+el <em/widget/, haciéndole fácil al usuario eliminar este texto.
+
+<tscreen><verb>
+void gtk_entry_select_region( GtkEntry *entry,
+ gint start,
+ gint end );
+</verb></tscreen>
+
+Si queremos saber el momento en el que el usuario ha introducido el texto,
+podemos conectar con las señales <tt/activate/ o <tt/changed/. <tt/activate/
+se activa cuando el usuario aprieta la tecla enter en el <em/widget/.
+<tt/changed/ se activa cuando cambia algo del texto, p.e. cuando se introduce
+o se elimina algún carácter.
+
+<tscreen><verb>
+/* Principio del ejemplo entry entry.c */
+
+#include <gtk/gtk.h>
+
+void enter_callback(GtkWidget *widget, GtkWidget *entry)
+{
+ gchar *entry_text;
+ entry_text = gtk_entry_get_text(GTK_ENTRY(entry));
+ printf("Entry contents: %s\n", entry_text);
+}
+
+void entry_toggle_editable (GtkWidget *checkbutton,
+ GtkWidget *entry)
+{
+ gtk_entry_set_editable(GTK_ENTRY(entry),
+ GTK_TOGGLE_BUTTON(checkbutton)->active);
+}
+
+void entry_toggle_visibility (GtkWidget *checkbutton,
+ GtkWidget *entry)
+{
+ gtk_entry_set_visibility(GTK_ENTRY(entry),
+ GTK_TOGGLE_BUTTON(checkbutton)->active);
+}
+
+int main (int argc, char *argv[])
+{
+
+ GtkWidget *ventana;
+ GtkWidget *vbox, *hbox;
+ GtkWidget *entry;
+ GtkWidget *boton;
+ GtkWidget *check;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ /* crear una nueva ventana */
+ ventana = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_widget_set_usize( GTK_WIDGET (ventana), 200, 100);
+ gtk_window_set_title(GTK_WINDOW (ventana), "GTK Entry");
+ gtk_signal_connect(GTK_OBJECT (ventana), "delete_event",
+ (GtkSignalFunc) gtk_exit, NULL);
+
+ vbox = gtk_vbox_new (FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (ventana), vbox);
+ gtk_widget_show (vbox);
+
+ entry = gtk_entry_new_with_max_length (50);
+ gtk_signal_connect(GTK_OBJECT(entry), "activate",
+ GTK_SIGNAL_FUNC(enter_callback),
+ entry);
+ gtk_entry_set_text (GTK_ENTRY (entry), "hello");
+ gtk_entry_append_text (GTK_ENTRY (entry), " world");
+ gtk_entry_select_region (GTK_ENTRY (entry),
+ 0, GTK_ENTRY(entry)->text_length);
+ gtk_box_pack_start (GTK_BOX (vbox), entry, TRUE, TRUE, 0);
+ gtk_widget_show (entry);
+
+ hbox = gtk_hbox_new (FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (vbox), hbox);
+ gtk_widget_show (hbox);
+
+ check = gtk_check_button_new_with_label("Editable");
+ gtk_box_pack_start (GTK_BOX (hbox), check, TRUE, TRUE, 0);
+ gtk_signal_connect (GTK_OBJECT(check), "toggled",
+ GTK_SIGNAL_FUNC(entry_toggle_editable), entry);
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(check), TRUE);
+ gtk_widget_show (check);
+
+ check = gtk_check_button_new_with_label("Visible");
+ gtk_box_pack_start (GTK_BOX (hbox), check, TRUE, TRUE, 0);
+ gtk_signal_connect (GTK_OBJECT(check), "toggled",
+ GTK_SIGNAL_FUNC(entry_toggle_visibility), entry);
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(check), TRUE);
+ gtk_widget_show (check);
+
+ boton = gtk_button_new_with_label ("Close");
+ gtk_signal_connect_object (GTK_OBJECT (boton), "clicked",
+ GTK_SIGNAL_FUNC(gtk_exit),
+ GTK_OBJECT (ventana));
+ gtk_box_pack_start (GTK_BOX (vbox), boton, TRUE, TRUE, 0);
+ GTK_WIDGET_SET_FLAGS (boton, GTK_CAN_DEFAULT);
+ gtk_widget_grab_default (boton);
+ gtk_widget_show (boton);
+
+ gtk_widget_show(ventana);
+
+ gtk_main();
+ return(0);
+}
+/* fin del ejemplo */
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Botones <em/spin/
+<p>
+El <em/widget/ botón <em/spin/ se utiliza normalmente para permitir
+que el usuario elija un valor de un rango de valores. Consiste en una
+caja para la entrada de texto con una flecha para arriba y otra para
+abajo justo al lado de la caja. Si utilizamos alguna de las flechas
+haremos que el valor suba o baje dentro del rango de los valores
+posibles. También podemos introducir directamente un valor específico
+(utilizando la caja de texto).
+
+El <em/widget/ botón <em/spin/ permite tener valores con un número de
+cifras decimales (o sin cifras decimales) y la posibilidad de
+incrementarlo/decrementarlo en pasos configurables. La acción de
+matener pulsado uno de los botones puede resultar (es opcional) en una
+aceleración del cambio en el valor de acuerdo con el tiempo que se
+mantenga pulsado.
+
+El botón <em/spin/ utiliza un objeto <ref id="sec_Adjustment"
+name="Ajuste"> para conservar la información referente al rango de
+valores que puede tomar el botón <em/spin/. Esto hace que el
+<em/widget/ botón <em/spin/ sea muy poderoso.
+
+Recuerde que un <em/widget/ ajuste puede crearse con la siguiente
+función, que ilustra la información que se guarda:
+
+<tscreen><verb>
+GtkObject *gtk_adjustment_new( gfloat valor,
+ gfloat inferior,
+ gfloat superior,
+ gfloat paso,
+ gfloat incremento_pagina,
+ gfloat tamano_pagina );
+</verb></tscreen>
+
+Estos atributos de un ajuste se utilizan en un botón <em/spin/ de la
+forma siguiente:
+
+<itemize>
+<item> <tt/valor/: valor inicial del botón <em/spin/
+<item> <tt/inferior/: valor inferior del rango
+<item> <tt/superior/: valor superior del rango
+<item> <tt/paso/: valor a incrementar/decrementar cuando pulsemos el
+botón 1 en una flecha
+<item> <tt/incremento_pagina/: valor a incrementar/decrementar cuando
+pulsemos el botón 2 en una flecha
+<item> <tt/tamano_pagina/: no se utiliza
+</itemize>
+
+Además, se puede utilizar el botón 3 para saltar directamente a los
+valores <tt/superior/ o <tt/inferior/ cuando se pulsa en una de las
+flechas. Veamos como crear un botón <em/spin/:
+
+<tscreen><verb>
+GtkWidget *gtk_spin_button_new( GtkAdjustment *ajuste,
+ gfloat aceleracion,
+ guint digitos );
+</verb></tscreen>
+
+El argumento <tt/aceleracion/ toma un valor entre 0.0 y 1.0 e indica
+la aceleración que tendrá el botón <em/spin/. El argumento
+<tt/digitos/ especifica el número de cifras decimales con que se
+mostrará el valor.
+
+Se puede reconfigurar un botón <em/spin/ después de su creación
+utilizando la función:
+
+<tscreen><verb>
+void gtk_spin_button_configure( GtkSpinButton *boton_spin,
+ GtkAdjustment *ajuste,
+ gfloat aceleracion,
+ guint digitos );
+</verb></tscreen>
+
+El argumento <tt/boton_spin/ especifica el botón <em/spin/ que va a
+reconfigurarse. El resto de argumentos son los que acabamos de
+explicar.
+
+Podemos establecer y obtener el ajuste utilizando las dos funciones
+siguientes:
+
+<tscreen><verb>
+void gtk_spin_button_set_adjustment( GtkSpinButton *boton_spin,
+ GtkAdjustment *ajuste );
+
+GtkAdjustment *gtk_spin_button_get_adjustment( GtkSpinButton *boton_spin );
+</verb></tscreen>
+
+El número de cifras decimales también puede alterarse utilizando:
+
+<tscreen><verb>
+void gtk_spin_button_set_digits( GtkSpinButton *boton_spin,
+ guint digitos) ;
+</verb></tscreen>
+
+El valor que un botón <em/spin/ está mostrando actualmente puede
+cambiarse utilizando las siguientes funciones:
+
+<tscreen><verb>
+void gtk_spin_button_set_value( GtkSpinButton *boton_spin,
+ gfloat valor );
+</verb></tscreen>
+
+El valor actual de un botón <em/spin/ puede obtenerse como un entero o
+como un flotante con las funciones siguientes:
+
+<tscreen><verb>
+gfloat gtk_spin_button_get_value_as_float( GtkSpinButton *boton_spin );
+
+gint gtk_spin_button_get_value_as_int( GtkSpinButton *boton_spin );
+</verb></tscreen>
+
+Si quiere alterar el valor de un <em/spin/ de forma relativa a su
+valor actual, puede utilizar la siguiente función:
+
+<tscreen><verb>
+void gtk_spin_button_spin( GtkSpinButton *boton_spin,
+ GtkSpinType direccion,
+ gfloat incremento );
+</verb></tscreen>
+
+El parámetro <tt/direccion/ puede tomar uno de los valores siguientes:
+
+<itemize>
+<item> GTK_SPIN_STEP_FORWARD
+<item> GTK_SPIN_STEP_BACKWARD
+<item> GTK_SPIN_PAGE_FORWARD
+<item> GTK_SPIN_PAGE_BACKWARD
+<item> GTK_SPIN_HOME
+<item> GTK_SPIN_END
+<item> GTK_SPIN_USER_DEFINED
+</itemize>
+
+Trataré de explicar todas las posibilidades que ofrece esta
+función. Algunos de los valores que puede utilizar <tt/direccion/
+hacen que se utilicen valores que están almacenados en el objeto
+Ajuste que está asociado con el botón <em/spin/.
+
+GTK_SPIN_STEP_FORWARD y GTK_SPIN_STEP_BACKWARD aumentan o disminuyen
+(respectivamente) el valor del botón <em/spin/ por la cantidad
+especificada por <tt/incremento/, a menos que <tt/incremento/ sea
+igual a 0, en cuyo caso el valor se aumentará o disminuirá por el
+valor especificado en <tt/paso/ dentro del Ajuste.
+
+GTK_SPIN_PAGE_FORWARD y GTK_SPIN_PAGE_BACKWARD sencillamente alteran
+el valor del botón <em/spin/ por la cantidad <tt/incremento/.
+
+GTK_SPIN_HOME hace que el botón <em/spin/ tenga el mismo valor que el
+valor inferior del rango Ajuste.
+
+GTK_SPIN_END hace que el botón <em/spin/ tenga el mismo valor que el
+valor superior del rango Ajuste.
+
+GTK_SPIN_USER_DEFINED cambia el valor del botón <em/spin/ por la
+cantidad especificada.
+
+Ahora vamos a dejar de lado las funciones para establecer y obtener el
+rango de los atributos del botón <em/spin/, y vamos a pasar a las
+funciones que afectan a la apariencia y al comportamiento del
+<em/widget/ botón <em/spin/ en sí mismo.
+
+La primera de estas funciones se utiliza para restringir el contenido
+de la caja de texto de un botón <em/spin/ a un valor numérico. Esto
+evita que un usuario introduzca cualquier valor no númerico.
+
+<tscreen><verb>
+void gtk_spin_button_set_numeric( GtkSpinButton *boton_spin,
+ gboolean numerico );
+</verb></tscreen>
+
+Puede indicar si un botón <em/spin/ pasará del límite superior al
+inferior utilizando la siguiente función:
+
+<tscreen><verb>
+void gtk_spin_button_set_wrap( GtkSpinButton *boton_spin,
+ gboolean wrap );
+</verb></tscreen>
+
+Puede hacer que un botón <em/spin/ redondee su valor al <tt/paso/ más
+cercano, que se indica cuando creamos el Ajuste que se utiliza con el
+botón <em/spin/. Para hacer que redondee tenemos que utilizar la
+función siguiente:
+
+<tscreen><verb>
+void gtk_spin_button_set_snap_to_ticks( GtkSpinButton *boton_spin,
+ gboolean redondear );
+</verb></tscreen>
+
+Para política de actualización de un botón <em/spin/ puede cambiarse
+con la siguiente función:
+
+<tscreen><verb>
+void gtk_spin_button_set_update_policy( GtkSpinButton *boton_spin,
+ GtkSpinButtonUpdatePolicy politica );
+</verb></tscreen>
+
+<!-- TODO: find out what this does - TRG -->
+
+Los valores posibles de <tt/politica/ son o GTK_UPDATE_ALWAYS o
+GTK_UPDATE_IF_VALID.
+
+Estas políticas afectan al comportamiento de un botón <em/spin/ cuando
+se lee el texto insertado en la caja de texto y se sincroniza con los
+valores del Ajuste.
+
+En el caso de GTK_UPDATE_IF_VALID el valor de un botón <em/spin/
+cambiará si el texto introducido es un valor numérico contenido dentro
+del rango especificado por el Ajuste. En caso contrario el texto
+introducido se convierte al valor del botón <em/spin/.
+
+En caso de utilizar GTK_UPDATE_ALWAYS se ignorarán los errores que
+puedan ocurrir en la conversión del texto en un valor numérico.
+
+El aspecto de los botones utilizados en un botón <em/spin/ pueden
+cambiarse utilizando las siguientes funciones:
+
+<tscreen><verb>
+void gtk_spin_button_set_shadow_type( GtkSpinButton *boton_spin,
+ GtkShadowType tipo_sombra );
+</verb></tscreen>
+
+Como siempre, el <tt/tipo_sombra/ puede ser uno de los siguientes:
+
+<itemize>
+<item> GTK_SHADOW_IN
+<item> GTK_SHADOW_OUT
+<item> GTK_SHADOW_ETCHED_IN
+<item> GTK_SHADOW_ETCHED_OUT
+</itemize>
+
+Finalmente, puede pedir de forma explícita que un botón <em/spin/ se
+actualice a sí mismo:
+
+<tscreen><verb>
+void gtk_spin_button_update( GtkSpinButton *boton_spin );
+</verb></tscreen>
+
+Es hora de un nuevo ejemplo.
+
+<tscreen><verb>
+/* principio del ejemplo spinbutton spinbutton.c */
+
+#include <gtk/gtk.h>
+
+static GtkWidget *spinner1;
+
+void toggle_snap( GtkWidget *widget,
+ GtkSpinButton *spin )
+{
+ gtk_spin_button_set_snap_to_ticks (spin, GTK_TOGGLE_BUTTON (widget)->active);
+}
+
+void toggle_numeric( GtkWidget *widget,
+ GtkSpinButton *spin )
+{
+ gtk_spin_button_set_numeric (spin, GTK_TOGGLE_BUTTON (widget)->active);
+}
+
+void change_digits( GtkWidget *widget,
+ GtkSpinButton *spin )
+{
+ gtk_spin_button_set_digits (GTK_SPIN_BUTTON (spinner1),
+ gtk_spin_button_get_value_as_int (spin));
+}
+
+void get_value( GtkWidget *widget,
+ gpointer data )
+{
+ gchar buf[32];
+ GtkLabel *etiqueta;
+ GtkSpinButton *spin;
+
+ spin = GTK_SPIN_BUTTON (spinner1);
+ etiqueta = GTK_LABEL (gtk_object_get_user_data (GTK_OBJECT (widget)));
+ if (GPOINTER_TO_INT (data) == 1)
+ sprintf (buf, "%d", gtk_spin_button_get_value_as_int (spin));
+ else
+ sprintf (buf, "%0.*f", spin->digits,
+ gtk_spin_button_get_value_as_float (spin));
+ gtk_label_set_text (etiqueta, buf);
+}
+
+
+int main( int argc,
+ char *argv[] )
+{
+ GtkWidget *ventana;
+ GtkWidget *frame;
+ GtkWidget *hbox;
+ GtkWidget *main_vbox;
+ GtkWidget *vbox;
+ GtkWidget *vbox2;
+ GtkWidget *spinner2;
+ GtkWidget *spinner;
+ GtkWidget *boton;
+ GtkWidget *etiqueta;
+ GtkWidget *val_label;
+ GtkAdjustment *adj;
+
+ /* Inicializar GTK */
+ gtk_init(&amp;argc, &amp;argv);
+
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
+ GTK_SIGNAL_FUNC (gtk_main_quit),
+ NULL);
+
+ gtk_window_set_title (GTK_WINDOW (ventana), "Spin Button");
+
+ main_vbox = gtk_vbox_new (FALSE, 5);
+ gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 10);
+ gtk_container_add (GTK_CONTAINER (ventana), main_vbox);
+
+ frame = gtk_frame_new ("Not accelerated");
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, TRUE, TRUE, 0);
+
+ vbox = gtk_vbox_new (FALSE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 5);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+
+ /* spin del día, mes y año */
+
+ hbox = gtk_hbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 5);
+
+ vbox2 = gtk_vbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), vbox2, TRUE, TRUE, 5);
+
+ etiqueta = gtk_label_new ("Day :");
+ gtk_misc_set_alignment (GTK_MISC (etiqueta), 0, 0.5);
+ gtk_box_pack_start (GTK_BOX (vbox2), etiqueta, FALSE, TRUE, 0);
+
+ adj = (GtkAdjustment *) gtk_adjustment_new (1.0, 1.0, 31.0, 1.0,
+ 5.0, 0.0);
+ spinner = gtk_spin_button_new (adj, 0, 0);
+ gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (spinner), TRUE);
+ gtk_spin_button_set_shadow_type (GTK_SPIN_BUTTON (spinner),
+ GTK_SHADOW_OUT);
+ gtk_box_pack_start (GTK_BOX (vbox2), spinner, FALSE, TRUE, 0);
+
+ vbox2 = gtk_vbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), vbox2, TRUE, TRUE, 5);
+
+ etiqueta = gtk_label_new ("Month :");
+ gtk_misc_set_alignment (GTK_MISC (etiqueta), 0, 0.5);
+ gtk_box_pack_start (GTK_BOX (vbox2), etiqueta, FALSE, TRUE, 0);
+
+ adj = (GtkAdjustment *) gtk_adjustment_new (1.0, 1.0, 12.0, 1.0,
+ 5.0, 0.0);
+ spinner = gtk_spin_button_new (adj, 0, 0);
+ gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (spinner), TRUE);
+ gtk_spin_button_set_shadow_type (GTK_SPIN_BUTTON (spinner),
+ GTK_SHADOW_ETCHED_IN);
+ gtk_box_pack_start (GTK_BOX (vbox2), spinner, FALSE, TRUE, 0);
+
+ vbox2 = gtk_vbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), vbox2, TRUE, TRUE, 5);
+
+ etiqueta = gtk_label_new ("Year :");
+ gtk_misc_set_alignment (GTK_MISC (etiqueta), 0, 0.5);
+ gtk_box_pack_start (GTK_BOX (vbox2), etiqueta, FALSE, TRUE, 0);
+
+ adj = (GtkAdjustment *) gtk_adjustment_new (1998.0, 0.0, 2100.0,
+ 1.0, 100.0, 0.0);
+ spinner = gtk_spin_button_new (adj, 0, 0);
+ gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (spinner), FALSE);
+ gtk_spin_button_set_shadow_type (GTK_SPIN_BUTTON (spinner),
+ GTK_SHADOW_IN);
+ gtk_widget_set_usize (spinner, 55, 0);
+ gtk_box_pack_start (GTK_BOX (vbox2), spinner, FALSE, TRUE, 0);
+
+ frame = gtk_frame_new ("Accelerated");
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, TRUE, TRUE, 0);
+
+ vbox = gtk_vbox_new (FALSE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 5);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+
+ hbox = gtk_hbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 5);
+
+ vbox2 = gtk_vbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), vbox2, TRUE, TRUE, 5);
+
+ etiqueta = gtk_label_new ("Value :");
+ gtk_misc_set_alignment (GTK_MISC (etiqueta), 0, 0.5);
+ gtk_box_pack_start (GTK_BOX (vbox2), etiqueta, FALSE, TRUE, 0);
+
+ adj = (GtkAdjustment *) gtk_adjustment_new (0.0, -10000.0, 10000.0,
+ 0.5, 100.0, 0.0);
+ spinner1 = gtk_spin_button_new (adj, 1.0, 2);
+ gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (spinner1), TRUE);
+ gtk_widget_set_usize (spinner1, 100, 0);
+ gtk_box_pack_start (GTK_BOX (vbox2), spinner1, FALSE, TRUE, 0);
+
+ vbox2 = gtk_vbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), vbox2, TRUE, TRUE, 5);
+
+ etiqueta = gtk_label_new ("Digits :");
+ gtk_misc_set_alignment (GTK_MISC (etiqueta), 0, 0.5);
+ gtk_box_pack_start (GTK_BOX (vbox2), etiqueta, FALSE, TRUE, 0);
+
+ adj = (GtkAdjustment *) gtk_adjustment_new (2, 1, 5, 1, 1, 0);
+ spinner2 = gtk_spin_button_new (adj, 0.0, 0);
+ gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (spinner2), TRUE);
+ gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
+ GTK_SIGNAL_FUNC (change_digits),
+ (gpointer) spinner2);
+ gtk_box_pack_start (GTK_BOX (vbox2), spinner2, FALSE, TRUE, 0);
+
+ hbox = gtk_hbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 5);
+
+ boton = gtk_check_button_new_with_label ("Snap to 0.5-ticks");
+ gtk_signal_connect (GTK_OBJECT (boton), "clicked",
+ GTK_SIGNAL_FUNC (toggle_snap),
+ spinner1);
+ gtk_box_pack_start (GTK_BOX (vbox), boton, TRUE, TRUE, 0);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (boton), TRUE);
+
+ boton = gtk_check_button_new_with_label ("Numeric only input mode");
+ gtk_signal_connect (GTK_OBJECT (boton), "clicked",
+ GTK_SIGNAL_FUNC (toggle_numeric),
+ spinner1);
+ gtk_box_pack_start (GTK_BOX (vbox), boton, TRUE, TRUE, 0);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (boton), TRUE);
+
+ val_label = gtk_label_new ("");
+
+ hbox = gtk_hbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 5);
+ boton = gtk_button_new_with_label ("Value as Int");
+ gtk_object_set_user_data (GTK_OBJECT (boton), val_label);
+ gtk_signal_connect (GTK_OBJECT (boton), "clicked",
+ GTK_SIGNAL_FUNC (get_value),
+ GINT_TO_POINTER (1));
+ gtk_box_pack_start (GTK_BOX (hbox), boton, TRUE, TRUE, 5);
+
+ boton = gtk_button_new_with_label ("Value as Float");
+ gtk_object_set_user_data (GTK_OBJECT (boton), val_label);
+ gtk_signal_connect (GTK_OBJECT (boton), "clicked",
+ GTK_SIGNAL_FUNC (get_value),
+ GINT_TO_POINTER (2));
+ gtk_box_pack_start (GTK_BOX (hbox), boton, TRUE, TRUE, 5);
+
+ gtk_box_pack_start (GTK_BOX (vbox), val_label, TRUE, TRUE, 0);
+ gtk_label_set_text (GTK_LABEL (val_label), "0");
+
+ hbox = gtk_hbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, TRUE, 0);
+
+ boton = gtk_button_new_with_label ("Close");
+ gtk_signal_connect_object (GTK_OBJECT (boton), "clicked",
+ GTK_SIGNAL_FUNC (gtk_widget_destroy),
+ GTK_OBJECT (ventana));
+ gtk_box_pack_start (GTK_BOX (hbox), boton, TRUE, TRUE, 5);
+
+ gtk_widget_show_all (ventana);
+
+ /* Entramos dentro del bucle de eventos */
+ gtk_main ();
+
+ return(0);
+}
+/* fin del ejemplo */
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Caja combinada (<em/Combo Box/)
+<p>
+La caja combinada o <em/combo box/ es otro sencillo <em/widget/
+exclusivamente compuesto por otros <em/widgets/. Desde el punto de
+vista del usuario, el <em/widget/ consiste en una caja para la
+introducción de texto y un menú desplegable desde el que el usuario
+puede seleccionar una de un conjunto predefinido de entradas. De forma
+alternativa, el usuario puede introducir una opción diferente en la
+caja de texto.
+
+El siguiente extracto de la estructura que define un Combo Box
+identifica algunos de sus componentes:
+
+<tscreen><verb>
+struct _GtkCombo {
+ GtkHBox hbox;
+ GtkWidget *entry;
+ GtkWidget *boton;
+ GtkWidget *popup;
+ GtkWidget *popwin;
+ GtkWidget *list;
+ ... };
+</verb></tscreen>
+
+Como puede ver, el Combo Box tiene dos partes principales que tiene
+que conocer: un <em/widget entry/ y un <em/widget list/ (lista).
+
+Lo primero, para crear un combo box, utilice:
+
+<tscreen><verb>
+GtkWidget *gtk_combo_new( void );
+</verb></tscreen>
+
+Ahora, si quiere indicar la cadena que debe aparecer en la sección
+entry del combo box, podrá hacerlo manipulando directamente el
+<em/widget/ <tt/entry/:
+
+<tscreen><verb>
+ gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(combo)->entry), "Mi cadena.");
+</verb></tscreen>
+
+Para introducir valores en la lista desplegable, puede utilizar la
+función:
+
+<tscreen><verb>
+void gtk_combo_set_popdown_strings( GtkCombo *combo,
+ GList *cadenas );
+</verb></tscreen>
+
+Antes de llamar a esta función, tiene que ensamblar una GList con las
+cadenas que quiere. GList es una implementación de una lista enlazada
+que forma parte de <ref id="sec_glib" name="glib">, una biblioteca
+base de GTK. Por el momento, la explicación fea y rápida es que tiene
+que crear un puntero GList, hacerlo igual a NULL, y añadirle cadenas
+de texto con la función
+
+<tscreen><verb>
+GList *g_list_append( GList *glist,
+ gpointer datos );
+</verb></tscreen>
+
+Es importante que inicialice el puntero GList a NULL antes de
+utilizarlo. El valor devuelto por la función g_list_append debe
+utilizarse como el nuevo puntero a la GList.
+
+Aquí tenemos un trozo de código típico para crear un conjunto de
+opciones:
+
+<tscreen><verb>
+ GList *glist=NULL;
+
+ glist = g_list_append(glist, "Cadena 1");
+ glist = g_list_append(glist, "Cadena 2");
+ glist = g_list_append(glist, "Cadena 3");
+ glist = g_list_append(glist, "Cadena 4");
+
+ gtk_combo_set_popdown_strings( GTK_COMBO(combo), glist) ;
+</verb></tscreen>
+
+A partir de este momento tendrá un combo box completo funcionando. Hay
+unos cuantos aspectos de su funcionamiento que puede cambiar. Para
+hacerlo tiene las funciones siguientes:
+
+<tscreen><verb>
+void gtk_combo_set_use_arrows( GtkCombo *combo,
+ gint valor );
+
+void gtk_combo_set_use_arrows_always( GtkCombo *combo,
+ gint valor );
+
+void gtk_combo_set_case_sensitive( GtkCombo *combo,
+ gint valor );
+</verb></tscreen>
+
+<tt/gtk_combo_set_use_arrows()/ le deja al usuario cambiar el valor
+del combo box utilizando las flechas de arriba/abajo. Utilizando estas
+teclas no haremos salir la lista, pero se reemplazará el texto actual
+del combo box con el siguiente elemento de la lista (superior o
+inferior, según la tecla que se pulse). Esto se consigue buscando en
+la lista el elemento correspondiente al valor actual del combo box y
+seleccionando el anterior o el posterior (según corresponda).
+Normalmente en una caja de entrada de texto las flechas se utilizan
+para cambiar el foco (ie. el <em/widget/ que recibe la entrada del
+teclado), pero en este caso será el TAB quien se ocupe. Cuando el
+elemento actual sea el último de la lista y presione la flecha abajo
+se cambiará el foco (lo mismo se aplica cuando estamos sobre el primer
+elemento y pulsamos la tecla arriba).
+
+Si el valor actual en la caja de entrada de texto no está en la lista,
+entonces se desactiva la función de <tt/gtk_combo_set_use_arrows()/.
+
+<tt/gtk_combo_set_use_arrows_always()/ igualmente permite la
+utilización de las flechas arriba/abajo para cambiar el elemento
+seleccionado por el siguiente/anterior de la lista, pero además trata
+la lista como si fuese circular (ie. pasa del último al primer
+elemento), desactivando completamente la utilidad de las teclas arriba
+y abajo para cambiar el foco.
+
+<tt/gtk_combo_set_case_sensitive()/ cambia entre una búsqueda por la
+lista que discrimine entre mayúsculas y minúsculas y una búsqueda que
+no discrimine. Se utiliza cuando se quiere que el <em/widget/ combo
+complete el valor que se está introduciendo con un valor de la
+lista. Dependiendo de esta función, se completará distinguiendo entre
+mayúsculas y minúsculas o no. El <em/widget/ combo completará la
+entrada actual si el usuario presiona la combinación de teclas MOD-1 y
+`Tab'. MOD-1 normalmente es la tecla `Alt'. Hay algunos
+administradores de ventanas que también utilizan esta combinación de
+teclas, con lo que perderemos su posible utilización por parte de GTK.
+
+Ahora que tenemos un combo box que actua como queremos que actue, todo
+lo que nos queda es saber como hay que hacer para obtener los datos
+que nos puede proporcionar. Esto es relativamente sencillo. La mayoría
+del tiempo, de lo único que tiene que preocuparse es de obtener datos
+de la caja de texto. Podemos acceder a la caja de texto mediante
+GTK_ENTRY(GTK_COMBO(combo)->entry). Las dos cosas que son interesantes
+hacer con esta caja son: enlazarla con la señal <tt/activate/, que
+indica que el usuario ha presionado la tecla «Intro», y leer el
+texto. Lo primero podemos hacerlo utilizando algo así:
+
+<tscreen><verb>
+ gtk_signal_connect(GTK_OBJECT(GTK_COMB(combo)->entry), "activate",
+ GTK_SIGNAL_FUNC (mi_funcion_respuesta), mis_datos);
+</verb></tscreen>
+
+Para conseguir el texto que hay en la caja en cualquier momento sólo
+tenemos que utilizar la función siguiente:
+
+<tscreen><verb>
+gchar *gtk_entry_get_text(GtkEntry *entry);
+</verb></tscreen>
+
+De esta forma:
+
+<tscreen><verb>
+ char *cadena;
+
+ cadena = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(combo)->entry));
+</verb></tscreen>
+
+Esto es todo lo interesante. Existe otra función,
+
+<tscreen><verb>
+void gtk_combo_disable_activate(GtkCombo *combo);
+</verb></tscreen>
+
+que permite desactivar la señal <tt/activate/ en el <em/widget/ entry
+dentro del combo box. Personalmente no se me ocurre ningún motivo para
+utilizarla, pero existir existe.
+
+<!-- There are also a function to set the string on a particular item, void
+gtk_combo_set_item_string(GtkCombo *combo, GtkItem *item, const gchar
+*item_value), but this requires that you have a pointer to the
+appropriate GtkItem. Frankly, I have no idea how to do that.
+-->
+
+<!-- ************************************** -->
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Selección de Color
+<p>
+El <em/widget/ selección de color, nos permite (¡sorpresa!) la selección
+interactiva de colores. Este <em/widget/ compuesto le permite al usuario
+seleccionar un color manipulando los tripletes RGB (rojo, verde y azul) y
+HSV (tono, saturación, valor). Para conseguirlo puede ajustar cada variable
+mediante las regletas o introduciendo directamente el valor deseado.
+También puede pinchar en la rueda de colores y seleccionar así el color
+deseado. También se puede establecer, opcionalmente, la transparencia
+del color.
+
+El <em/widget/ de selección de color emite (por ahora) sólo una señal,
+<tt/color_changed/, que se emite cuando cambia el color seleccionado,
+ya sea mediante un cambio que haga el usuario o median el resultado
+de una llamada a la función <tt/gtk_color_selection_set_color()/.
+
+Echémosle un vistazo a lo que nos ofrece el <em/widget/ de selección de color.
+El <em/widget/ tiene dos «sabores» diferentes; <tt/gtk_color_selection/
+y <tt/gtk_color_selection_dialog/:
+
+<tscreen><verb>
+GtkWidget *gtk_color_selection_new( void );
+</verb></tscreen>
+
+Probablemente no utilizará este constructor directamente. Crea un <em/widget/
+GtkColorSelection huérfano al que le tendrá que asignarle un padre. El
+<em/widget/ GtkColorSelection está heredado del <em/widget/ GtkVBox.
+
+<tscreen><verb>
+GtkWidget *gtk_color_selection_dialog_new( const gchar *title );
+</verb></tscreen>
+
+Éste es el constructor del cuadro de selección de color más común. Crea un
+<tt/GtkColorSelectionDialog/, heredado de un <tt/GtkDialog/. Consiste en un
+<tt/GtkFrame/ con un <tt/GtkColorSelection/, un <tt/GtkHSeparator/ y un
+<tt/GtkHBox/ con tres botones, «Aceptar», «Cancelar» y «Ayuda».
+Puede utilizar estos botones accediendo a los <em/widgets/ <tt/ok_button/,
+<tt/cancel_button/ y <tt/help_button/ de la estructura GtkColorSelectionDialog,
+(es decir GTK_COLOR_SELECTION_DIALOG(colorseldialog)->ok_button).
+
+<tscreen><verb>
+void gtk_color_selection_set_update_policy( GtkColorSelection *colorsel,
+ GtkUpdateType policy );
+</verb></tscreen>
+
+Esta función se utiliza para indicar la política de actuación. La política
+por defecto es <tt/GTK_UPDATE_CONTINUOUS/ que significa que el color
+seleccionado se actualiza continuamente cuando el usuario arrastra la barra
+o selecciona con el ratón un color de la rueda de colores. Si tiene problemas
+de rendimiento, puede poner la política <tt/GTK_UPDATE_DISCONTINUOUS/ o
+<tt/GTK_UPDATE_DELAYED/.
+
+<tscreen><verb>
+void gtk_color_selection_set_opacity( GtkColorSelection *colorsel,
+ gint use_opacity );
+</verb></tscreen>
+
+El <em/widget/ de selección de color admite el ajuste de la transparencia
+de un color (también conocido como el canal alfa). Esta opción está
+desactivada por defecto. Si se llama a esta función con <tt/use_opacity/
+como TRUE se activa la transparencia. Si se utiliza <tt/use_opacity/ como
+FALSE se desactiva la transparencia.
+
+<tscreen><verb>
+void gtk_color_selection_set_color( GtkColorSelection *colorsel,
+ gdouble *color );
+</verb></tscreen>
+
+Puede poner el color actual explicitamente haciendo uso de esta función con
+un puntero a un vector de colores (de tipo <tt/gdouble/). La longitud del
+vector depende de si está activada la transparencia. La posición 0 contiene
+la componente roja del color, la 1 contiene la verde, la 2 la azul y la
+transparencia está en la posición 3 (solamente si está activada la
+transparencia, ver <tt/gtk_color_selection_set_opacity()/). Todos los
+valores se encuentran entre 0.0 y 1.0.
+
+<tscreen><verb>
+void gtk_color_selection_get_color( GtkColorSelection *colorsel,
+ gdouble *color );
+</verb></tscreen>
+
+Cuando necesite preguntar por el color actual, normalmente cuando haya
+recibido una señal <tt/color_changed/, utilice esta función. <tt/color/
+es un puntero al vector de colores que se rellenará. Ver la descripción
+de la función <tt/gtk_color_selection_set_color()/ para conocer la
+estructura de este vector.
+
+<!-- Need to do a whole section on DnD - TRG
+Drag and drop
+-------------
+
+The color sample areas (right under the hue-saturation wheel) supports
+drag and drop. The type of drag and drop is "application/x-color". The
+message data consists of an array of 4 (or 5 if opacity is enabled)
+gdouble values, where the value at position 0 is 0.0 (opacity on) or
+1.0 (opacity off) followed by the red, green and blue values at
+positions 1,2 and 3 respectively. If opacity is enabled, the opacity
+is passed in the value at position 4.
+-->
+
+Aquí tenemos un pequeño ejemplo que muestra el uso de
+<tt/GtkColorSelectionDialog/. El programa muestra una ventana con una
+zona de dibujo. Pulsando en ella se abre un cuadro de diálogo de
+selección del color, y cambiando el color en el cuadro de diálogo se
+cambia el color de fondo de la zona de dibujo.
+
+<tscreen><verb>
+/* principio del ejemplo colorsel colorsel.c */
+
+#include <glib.h>
+#include <gdk/gdk.h>
+#include <gtk/gtk.h>
+
+GtkWidget *colorseldlg = NULL;
+GtkWidget *drawingarea = NULL;
+
+/* Manejador del cambio de color */
+
+void color_changed_cb (GtkWidget *widget, GtkColorSelection *colorsel)
+{
+ gdouble color[3];
+ GdkColor gdk_color;
+ GdkColormap *colormap;
+
+ /* Obtener el mapa de colores de la zona de dibujo */
+
+ colormap = gdk_window_get_colormap (drawingarea->window);
+
+ /* Obtener el color actual */
+
+ gtk_color_selection_get_color (colorsel,color);
+
+ /* Meterlo en un entero sin signo de 16 bits (0..65535) e insertarlo
+ en la estructura GdkColor */
+
+ gdk_color.red = (guint16)(color[0]*65535.0);
+ gdk_color.green = (guint16)(color[1]*65535.0);
+ gdk_color.blue = (guint16)(color[2]*65535.0);
+
+ /* Pedir memoria para el color */
+
+ gdk_color_alloc (colormap, &amp;gdk_color);
+
+ /* Poner el color de fondo de la ventana */
+
+ gdk_window_set_background (drawingarea->window, &amp;gdk_color);
+
+ /* Limpiar la ventana */
+
+ gdk_window_clear (drawingarea->window);
+}
+
+/* Manejador del evento Drawingarea */
+
+gint area_event (GtkWidget *widget, GdkEvent *event, gpointer client_data)
+{
+ gint handled = FALSE;
+ GtkWidget *colorsel;
+
+ /* Comprobar si hemos recibido un evento de pulsación de botón */
+
+ if (event->type == GDK_BUTTON_PRESS &amp;&amp; colorseldlg == NULL)
+ {
+ /* Sí, ¡tenemos un evento y todavía no está el colorseldlg! */
+
+ handled = TRUE;
+
+ /* Crear el cuadro de diálogo de selección del color */
+
+ colorseldlg = gtk_color_selection_dialog_new("Select background color");
+
+ /* Obtener el widget GtkColorSelection */
+
+ colorsel = GTK_COLOR_SELECTION_DIALOG(colorseldlg)->colorsel;
+
+ /* Conectar con la señal «color_changed», darle al dato del
+ cliente el valor del widget colorsel */
+
+ gtk_signal_connect(GTK_OBJECT(colorsel), "color_changed",
+ (GtkSignalFunc)color_changed_cb, (gpointer)colorsel);
+
+ /* Mostrar el cuadro de diálogo */
+
+ gtk_widget_show(colorseldlg);
+ }
+
+ return handled;
+}
+
+/* Manipulador de los eventos cerrar y salir */
+
+void destroy_window (GtkWidget *widget, gpointer client_data)
+{
+ gtk_main_quit ();
+}
+
+/* Principal */
+
+gint main (gint argc, gchar *argv[])
+{
+ GtkWidget *ventana;
+
+ /* Inicializa el toolkit, y elimina las opciones relacionadas con
+ gtk incluidas en la línea de órdenes */
+
+ gtk_init (&amp;argc,&amp;argv);
+
+ /* Crea la ventana de más alto nivel, le da título y la política */
+
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title (GTK_WINDOW(ventana), "Color selection test");
+ gtk_window_set_policy (GTK_WINDOW(ventana), TRUE, TRUE, TRUE);
+
+ /* Enlaza con los eventos «delete» y «destroy», para que podamos
+ salir */
+
+ gtk_signal_connect (GTK_OBJECT(ventana), "delete_event",
+ (GtkSignalFunc)destroy_window, (gpointer)ventana);
+
+ gtk_signal_connect (GTK_OBJECT(ventana), "destroy",
+ (GtkSignalFunc)destroy_window, (gpointer)ventana);
+
+ /* Crea la zona de dibujo, pone el tamaño y caza los eventos de los
+ botones */
+
+ drawingarea = gtk_drawing_area_new ();
+
+ gtk_drawing_area_size (GTK_DRAWING_AREA(drawingarea), 200, 200);
+
+ gtk_widget_set_events (drawingarea, GDK_BUTTON_PRESS_MASK);
+
+ gtk_signal_connect (GTK_OBJECT(drawingarea), "event",
+ (GtkSignalFunc)area_event, (gpointer)drawingarea);
+
+ /* Add drawingarea to window, then show them both */
+
+ gtk_container_add (GTK_CONTAINER(ventana), drawingarea);
+
+ gtk_widget_show (drawingarea);
+ gtk_widget_show (ventana);
+
+ /* Entrar en el bucle principal de gtk (nunca sale de aquí) */
+
+ gtk_main ();
+
+ /* Para satisfacer a los compiladores pijos */
+
+ return 0;
+}
+/* final del ejemplo */
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Selección de ficheros
+<p>
+El <em/widget/ de selección de ficheros nos proporciona una forma
+rápida y sencilla de mostrar un cuadro de diálogo para la selección de
+un fichero. Ya viene con los botones Aceptar, Cancelar y Ayuda. Una
+magnifica ayuda para acortar el tiempo de programación.
+
+Para crear un nuevo cuadro de diálogo de selección de ficheros
+utilice:
+
+<tscreen><verb>
+GtkWidget *gtk_file_selection_new( gchar *title );
+</verb></tscreen>
+
+Para poner el nombre del fichero en el cuadro de diálogo, por
+ejemplo para poder utilizar un directorio o un fichero por defecto,
+utilice la función:
+
+<tscreen><verb>
+void gtk_file_selection_set_filename( GtkFileSelection *filesel,
+ gchar *filename );
+</verb></tscreen>
+
+Para obtener el texto que el usuario ha introducido, utilice la función:
+
+<tscreen><verb>
+gchar *gtk_file_selection_get_filename( GtkFileSelection *filesel );
+</verb></tscreen>
+
+También hay punteros a los <em/widgets/ que contiene el cuadro de
+diálogo. Son los siguientes:
+
+<itemize>
+<item>dir_list
+<item>file_list
+<item>selection_entry
+<item>selection_text
+<item>main_vbox
+<item>ok_button
+<item>cancel_button
+<item>help_button
+</itemize>
+
+Lo más probable es que sólo utilice los punteros <tt/ok_button/,
+<tt/cancel_button/, y <tt/help_button/ para controlar cuando se pulsan.
+
+Aquí incluímos un ejemplo robado de <tt/testgtk.c/, modificado
+para que se puede ejecutar independientemente. Como puede ver, no es
+muy complicado crear un <em/widget/ para la selección de
+ficheros. Aunque aparezca el botón de ayuda en la pantalla, no hace
+nada y no tiene ninguna señal conectada.
+
+<tscreen><verb>
+/* principio del ejemplo filesel filesel.c */
+
+#include <gtk/gtk.h>
+
+/* Obtener el nombre del fichero e imprimirlo en la consola */
+void file_ok_sel (GtkWidget *w, GtkFileSelection *fs)
+{
+ g_print ("%s\n", gtk_file_selection_get_filename (GTK_FILE_SELECTION (fs)));
+}
+
+void destroy (GtkWidget *widget, gpointer data)
+{
+ gtk_main_quit ();
+}
+
+int main (int argc, char *argv[])
+{
+ GtkWidget *filew;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ /* Crear un nuevo widget de selección de ficheros */
+ filew = gtk_file_selection_new ("File selection");
+
+ gtk_signal_connect (GTK_OBJECT (filew), "destroy",
+ (GtkSignalFunc) destroy, &amp;filew);
+ /* Conectar el ok_button con la función file_ok_sel */
+ gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (filew)->ok_button),
+ "clicked", (GtkSignalFunc) file_ok_sel, filew );
+
+ /* Conectar el cancel_button con la destrucción del widget */
+ gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION (filew)->cancel_button),
+ "clicked", (GtkSignalFunc) gtk_widget_destroy,
+ GTK_OBJECT (filew));
+
+ /* Damos el nombre del fichero, como si fuese un cuadro de diálogo para
+ grabar ficheros y estuviesemos dando un nombre por defecto */
+ gtk_file_selection_set_filename (GTK_FILE_SELECTION(filew),
+ "penguin.png");
+
+ gtk_widget_show(filew);
+ gtk_main ();
+ return 0;
+}
+/* fin del ejemplo */
+</verb></tscreen>
+
+<!-- ***************************************************************** -->
+<sect> El <em/widget/ contenedor
+<!-- ***************************************************************** -->
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> El <em/widget/ EventBox<label id="sec_EventBox">
+<label id="sec_The_EventBox_Widget">
+<p>
+Algunos <em/widget/ gtk no tienen asociada una ventana X, por lo que
+sólo pueden dibujar en la de su padre. Debido a esto, no pueden
+recibir ningún evento y si tienen un tamaño incorrecto, no se
+recortarán correctamente por lo que puede que se sobreescriban ciertas
+zonas, etc... Si necesita este tipo de <em/widgets/, el EventBox es
+para usted.
+
+Cuando se ve por primera vez, el <em/widget/ EventBox puede parecer
+completamente inútil. No dibuja nada en la pantalla y no responde
+a ningún evento. Sin embargo, tiene una utilidad - proporciona una
+ventana X para su <em/widget/ hijo. Esto es importante ya que
+muchos <em/widgets/ GTK no tienen una ventana X asociada. No tener una
+ventana X ahorra memoria y mejora el rendimiento, pero tiene sus
+desventajas. Un <em/widget/ sin una ventana X no puede recibir
+eventos, y no realizará ningún recorte en sus contenidos. Aunque el
+nombre <em/EventBox/ enfatiza su función de manejador de eventos, el
+<em/widget/ también puede utilizarse para hacer los recortes.
+(Y más... ver el ejemplo más abajo.)
+
+Para crear un nuevo <em/widget/ EventBox, utilice:
+
+<tscreen><verb>
+GtkWidget *gtk_event_box_new( void );
+</verb></tscreen>
+
+Un <em/widget/ hijo puede añadirse a su EventBox así:
+
+<tscreen><verb>
+gtk_container_add( GTK_CONTAINER(event_box), widget );
+</verb></tscreen>
+
+El siguiente ejemplo demuestra los dos usos de EventBox - se crea
+una etiqueta que se recorta dentro de una pequeña caja, y hace
+que una pulsación del ratón en la misma finalice el programa.
+
+<tscreen><verb>
+/* principio del ejemplo eventbox eventbox.c */
+
+#include <gtk/gtk.h>
+
+int
+main (int argc, char *argv[])
+{
+ GtkWidget *ventana;
+ GtkWidget *event_box;
+ GtkWidget *etiqueta;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ gtk_window_set_title (GTK_WINDOW (ventana), "Event Box");
+
+ gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
+ GTK_SIGNAL_FUNC (gtk_exit), NULL);
+
+ gtk_container_border_width (GTK_CONTAINER (ventana), 10);
+
+ /* Crea un EventBox y lo añade a nuestra ventana superior */
+
+ event_box = gtk_event_box_new ();
+ gtk_container_add (GTK_CONTAINER(ventana), event_box);
+ gtk_widget_show (event_box);
+
+ /* Crea una larga etiqueta */
+
+ etiqueta = gtk_label_new ("Click here to quit, quit, quit, quit, quit");
+ gtk_container_add (GTK_CONTAINER (event_box), etiqueta);
+ gtk_widget_show (etiqueta);
+
+ /* La recortamos. */
+ gtk_widget_set_usize (etiqueta, 110, 20);
+
+ /* Y enlazamos una acción con la etiqueta */
+ gtk_widget_set_events (event_box, GDK_BUTTON_PRESS_MASK);
+ gtk_signal_connect (GTK_OBJECT(event_box), "button_press_event",
+ GTK_SIGNAL_FUNC (gtk_exit), NULL);
+
+ /* Otra cosa más que necesita una ventana X ... */
+
+ gtk_widget_realize (event_box);
+ gdk_window_set_cursor (event_box->window, gdk_cursor_new (GDK_HAND1));
+
+ gtk_widget_show (ventana);
+
+ gtk_main ();
+
+ return 0;
+}
+/* Final del ejemplo */
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>El <em/widget/ alineamiento <label id="sec_Alignment">
+<p>
+El <em/widget/ alineamiento (<em/alignment/) le permitirá colocar un
+<em/widget/ dentro de su ventana utilizando una posición y un tamaño
+relativos al mismo <em/widget/ de alineamiento. Por ejemplo, puede ser
+muy útil para centrar un <em/widget/ en la ventana.
+
+Sólo hay dos funciones asociadas con el <em/widget/ alineamiento:
+
+<tscreen><verb>
+GtkWidget* gtk_alignment_new( gfloat xalign,
+ gfloat yalign,
+ gfloat xscale,
+ gfloat yscale );
+
+void gtk_alignment_set( GtkAlignment *alignment,
+ gfloat xalign,
+ gfloat yalign,
+ gfloat xscale,
+ gfloat yscale );
+</verb></tscreen>
+
+La primera función crea un nuevo <em/widget/ alineamiento con los
+parámetros especificados. La segunda función permite alterar los
+parámetros de un <em/widget/ alineamiento ya existente.
+
+Los cuatro parámetros de alineamiento son números en coma flotante que
+pueden tener variar entre 0.0 y 1.0. Los argumentos <tt/xalign/ e
+<tt/yalign/ afectan a la posición del <em/widget/ colocado dentro del
+<em/widget/ de alineamiento. Los argumentos <tt/xscale/ e <tt/yscale/
+afectan a la cantidad de espacio que ocupa el <em/widget/.
+
+Se le puede añadir un <em/widget/ hijo a un alineamiento utilizando:
+
+<tscreen><verb>
+ gtk_container_add( GTK_CONTAINER(alignment), child_widget );
+</verb></tscreen>
+
+Para ver un ejemplo de utilización del <em/widget/ alineamiento,
+diríjase al ejemplo del <em/widget/ <ref id="sec_ProgressBar"
+name="Barra de progreso">.
+
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Contenedor fijo
+<p>
+El contenedor fijo le permite situar <em/widgets/ en una posición fija
+dentro de su ventana, relativa a la esquina superior izquierda. La
+posición de los <em/widgets/ puede cambiarse dinámicamente.
+
+Sólo hay tres funciones asociadas al <em/widget/ contenedor fijo:
+
+<tscreen><verb>
+GtkWidget* gtk_fixed_new( void );
+
+void gtk_fixed_put( GtkFixed *fixed,
+ GtkWidget *widget,
+ gint16 x,
+ gint16 y );
+
+void gtk_fixed_move( GtkFixed *fixed,
+ GtkWidget *widget,
+ gint16 x,
+ gint16 y );
+</verb></tscreen>
+
+La función <tt/gtk_fixed_new/ permite la creación de un nuevo
+contenedor fijo.
+
+<tt/gtk_fixed_put/ situa <tt/widget/ dentro del contenedor <tt/fixed/
+en la posición especificada por <tt/x/ e <tt/y/.
+
+<tt/gtk_fixed_move/ permite que mover hacia una nuevo posición el
+<em/widget/ especificado.
+
+El ejemplo siguiente muestra como utilizar el contenedor fijo.
+
+<tscreen><verb>
+/* principio del ejemplo fixed fixed.c */
+
+#include <gtk/gtk.h>
+
+/* Voy a ser un poco torpe y utilizar algunas variables
+ * globales para almacenar la posición del widget que
+ * hay dentro del contenedor */
+gint x=50;
+gint y=50;
+
+/* Esta función de llamada mueve el botón a una nueva
+ * posición dentro del contenedor fijo. */
+void move_button( GtkWidget *widget,
+ GtkWidget *fixed )
+{
+ x = (x+30)%300;
+ y = (y+50)%300;
+ gtk_fixed_move( GTK_FIXED(fixed), widget, x, y);
+}
+
+int main( int argc,
+ char *argv[] )
+{
+ /* GtkWidget es el tipo de almacenamiento para los widgets */
+ GtkWidget *ventana;
+ GtkWidget *fixed;
+ GtkWidget *boton;
+ gint i;
+
+ /* Inicializa GTK */
+ gtk_init(&amp;argc, &amp;argv);
+
+ /* Crear una nueva ventana */
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title(GTK_WINDOW(ventana), "Fixed Container");
+
+ /* Aquí conectamos el evento "destroy" al manejador de la señal */
+ gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
+ GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
+
+ /* Establecemos el ancho del borde la ventana */
+ gtk_container_set_border_width (GTK_CONTAINER (ventana), 10);
+
+ /* Creamos un contenedor fijo */
+ fixed = gtk_fixed_new();
+ gtk_container_add(GTK_CONTAINER(ventana), fixed);
+ gtk_widget_show(fixed);
+
+ for (i = 1 ; i <= 3 ; i++) {
+ /* Crea un nuevo botón con la etiqueta "Press me" */
+ boton = gtk_button_new_with_label ("Press me");
+
+ /* Cuando el botón reciba la señal "pulsado", llamará a la función
+ * move_button() pasándole el contenedor fijo como argumento. */
+ gtk_signal_connect (GTK_OBJECT (boton), "clicked",
+ GTK_SIGNAL_FUNC (move_button), fixed);
+
+ /* Esto mete el botón dentro de la ventana del window contenedor
+ * fijo. */
+ gtk_fixed_put (GTK_FIXED (fixed), boton, i*50, i*50);
+
+ /* El paso final es mostrar el widget recien creado */
+ gtk_widget_show (boton);
+ }
+
+ /* Mostrar la ventana */
+ gtk_widget_show (ventana);
+
+ /* Entrar en el bucle principal */
+ gtk_main ();
+
+ return(0);
+}
+/* fin del ejemplo */
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Contenedor capa
+<p>
+El contenedor capa es similar al contenedor fijo, excepto que permite
+implementar una zona de <em/scroll/ infinita (donde infinito significa
+menor de 2^32). Xwindows tiene una limitación en la que las ventanas
+pueden tener un máximo de 32767 <em/pixels/ de alto o de ancho. El
+contenedor capa sortea esta limitación con una exótica combinación de
+ventanas y <em/bits/ de gravedad, <!-- Si alguien entiende que
+significa esto: e98cuenc@criens.u-psud.fr --> para que puede tener un
+suave <em/scroll/ aún cuando utilice una gran cantidad de <em/widgets/
+hijos dentro de su zona de <em/scroll/.
+
+Podrá crear un contenedor capa utilizando:
+
+<tscreen><verb>
+GtkWidget *gtk_layout_new( GtkAdjustment *hadjustment,
+ GtkAdjustment *vadjustment );
+</verb></tscreen>
+
+Como puede observar, podrá especificar (de forma opcional) los objetos
+de ajuste que utilizará el <em/widget/ capa para hacer su <em/scroll/.
+
+Puede añadir y mover <em/widgets/ dentro del contenedor capa
+utilizando las dos funciones siguientes:
+
+<tscreen><verb>
+void gtk_layout_put( GtkLayout *layout,
+ GtkWidget *widget,
+ gint x,
+ gint y );
+
+void gtk_layout_move( GtkLayout *layout,
+ GtkWidget *widget,
+ gint x,
+ gint y );
+</verb></tscreen>
+
+El tamaño del contenedor capa se puede establecer utilizando la
+siguiente función:
+
+<tscreen><verb>
+void gtk_layout_set_size( GtkLayout *layout,
+ guint width,
+ guint height );
+</verb></tscreen>
+
+Los contenedores capa son uno de los poquísimos <em/widgets/ dentro de
+GTK que se repintan ellos mismos en la pantalla cuando se cambian
+utilizando las funciones anteriores (la inmensa mayoria de los
+<em/widgets/ mandan una petición a la cola que será procesada cuando
+se devuelva el control a la función <tt/gtk_main()/).
+
+Cuando quiera hacer una gran cantidad de cambios dentro del contenedor
+capa, podrá utilizar las dos funciones siguientes para desactivar y
+reactivar la característica de repintado:
+
+<tscreen><verb>
+void gtk_layout_freeze( GtkLayout *layout );
+
+void gtk_layout_thaw( GtkLayout *layout );
+</verb></tscreen>
+
+Las cuatro funciones finales a utilizar con los <em/widgets/capa son
+para la manipulación de los <em/widgets/ de ajuste horizontal y
+vertical:
+
+<tscreen><verb>
+GtkAdjustment* gtk_layout_get_hadjustment( GtkLayout *layout );
+
+GtkAdjustment* gtk_layout_get_vadjustment( GtkLayout *layout );
+
+void gtk_layout_set_hadjustment( GtkLayout *layout,
+ GtkAdjustment *adjustment );
+
+void gtk_layout_set_vadjustment( GtkLayout *layout,
+ GtkAdjustment *adjustment);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Marcos <label id="sec_Frames">
+<p>
+Los marcos pueden utilizarse para meter uno o un grupo de
+<em/widgets/dentro de una caja puede ser (de forma opcional)
+etiquetada. La posición de la etiqueta y el estilo de la caja pueden
+modificarse.
+
+Se puede crear un marco con la siguiente función:
+
+<tscreen><verb>
+GtkWidget *gtk_frame_new( const gchar *etiqueta );
+</verb></tscreen>
+
+La etiqueta se coloca por defecto en la esquina superior izquierda del
+marco. Si el argumento <tt/etiqueta/ es NULL no se mostrará ninguna
+etiqueta. Puede cambiarse el texto de la etiqueta utilizando la
+función siguiente.
+
+<tscreen><verb>
+void gtk_frame_set_label( GtkFrame *frame,
+ const gchar *etiqueta );
+</verb></tscreen>
+
+La posición de la etiqueta se puede cambiar utilizado la función:
+
+<tscreen><verb>
+void gtk_frame_set_label_align( GtkFrame *frame,
+ gfloat xalign,
+ gfloat yalign );
+</verb></tscreen>
+
+<tt/xalign/ e <tt/yalign/ toman valores entre 0.0 y 1.0. <tt/yalign/
+no se actualmente no se utiliza. El valor por defecto de <tt/xalign/
+es 0.0, lo que coloca la etiqueta a la izquierda del marco.
+
+La siguiente función altera el estilo de la caja que se utiliza para
+señalar el marco.
+
+<tscreen><verb>
+void gtk_frame_set_shadow_type( GtkFrame *frame,
+ GtkShadowType type);
+</verb></tscreen>
+
+El argumento <tt/type/ puede tomar uno de los valores siguientes:
+
+<itemize>
+<item> GTK_SHADOW_NONE
+<item> GTK_SHADOW_IN
+<item> GTK_SHADOW_OUT
+<item> GTK_SHADOW_ETCHED_IN (the default)
+<item> GTK_SHADOW_ETCHED_OUT
+</itemize>
+
+El código siguiente ilustra la utilización del <em/widget/ marco.
+
+<tscreen><verb>
+/* principio del ejemplo frame frame.c */
+
+#include <gtk/gtk.h>
+
+int main( int argc,
+ char *argv[] )
+{
+ /* GtkWidget es el tipo de almacenamiento para los widgets */
+ GtkWidget *ventana;
+ GtkWidget *frame;
+ GtkWidget *boton;
+ gint i;
+
+ /* Inicializa GTK */
+ gtk_init(&amp;argc, &amp;argv);
+
+ /* Crea una nueva ventana */
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title(GTK_WINDOW(ventana), "Frame Example");
+
+ /* Aquí conectamos el evento "destroy"al manejador de señal */
+ gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
+ GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
+
+ gtk_widget_set_usize(ventana, 300, 300);
+
+ /* Establecemos el ancho del borde de la ventana */
+ gtk_container_set_border_width (GTK_CONTAINER (ventana), 10);
+
+ /* Crea un marco */
+ frame = gtk_frame_new(NULL);
+ gtk_container_add(GTK_CONTAINER(ventana), frame);
+
+ /* Establece la etiqueta del marco */
+ gtk_frame_set_label( GTK_FRAME(frame), "GTK Frame Widget" );
+
+ /* Alinea la etiqueta a la derecha del marco */
+ gtk_frame_set_label_align( GTK_FRAME(frame), 1.0, 0.0);
+
+ /* Establece el estilo del marco */
+ gtk_frame_set_shadow_type( GTK_FRAME(frame), GTK_SHADOW_ETCHED_OUT);
+
+ gtk_widget_show(frame);
+
+ /* Muestra la ventana */
+ gtk_widget_show (ventana);
+
+ /* Entra dentro del bucle principal */
+ gtk_main ();
+
+ return(0);
+}
+/* fin del ejemplo */
+
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Marcos con proporciones fijas
+<p>
+El <em/widget aspect frame/ (marco proporcional) es como el <em/widget
+frame/ (marco), excepto que conserva las proporciones (esto es, la
+relación entre el ancho y el alto) del <em/widget/ hijo, añadiendo
+espacio extra en caso de ser necesario. Esto es útil, por ejemplo, si
+quiere hacer una vista previa de una gran imagen. El tamaño de la
+vista previa debería variar cuando el usuario redimensione la ventana,
+pero la proporción tiene que coincidir con la de la imagen original.
+
+Para crear un nuevo marco proporcional utilice:
+
+<tscreen><verb>
+GtkWidget *gtk_aspect_frame_new( const gchar *etiqueta,
+ gfloat xalign,
+ gfloat yalign,
+ gfloat ratio,
+ gint obey_child);
+</verb></tscreen>
+
+<tt/xalign/ e <tt/yalign/ indican la alineación exactamente igual que
+con los <em/widgets Alignment/. Si <tt/obey_child/ es TRUE, la
+proporción de un <em/widget/ hijo será la misma que la proporción del
+tamaño ideal que éste pida. En caso contrario, vendrá dada por
+<tt/ratio/.
+
+Para cambiar las opciones de un marco proporcional ya existente, puede
+utilizar:
+
+<tscreen><verb>
+void gtk_aspect_frame_set( GtkAspectFrame *aspect_frame,
+ gfloat xalign,
+ gfloat yalign,
+ gfloat ratio,
+ gint obey_child);
+</verb></tscreen>
+
+Como por ejemplo, el siguiente programa utiliza un marco proporcional
+para mostrar una zona de dibujo cuyas proporciones siempre será de
+2:1, no importa como el usuario redimensione la ventana.
+
+<tscreen><verb>
+/* principio del ejemplo aspectframe aspectframe.c */
+
+#include <gtk/gtk.h>
+
+int
+main (int argc, char *argv[])
+{
+ GtkWidget *ventana;
+ GtkWidget *aspect_frame;
+ GtkWidget *drawing_area;
+ gtk_init (&amp;argc, &amp;argv);
+
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title (GTK_WINDOW (ventana), "Aspect Frame");
+ gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
+ GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
+ gtk_container_border_width (GTK_CONTAINER (ventana), 10);
+
+ /* Crear un aspect_frame y añadirlo a nuestra ventana superior */
+
+ aspect_frame = gtk_aspect_frame_new ("2x1", /* etiqueta */
+ 0.5, /* centro x */
+ 0.5, /* centro y */
+ 2, /* tamañox/tamañoy = 2 */
+ FALSE /* ignorar el aspecto del hijo */);
+
+ gtk_container_add (GTK_CONTAINER(ventana), aspect_frame);
+ gtk_widget_show (aspect_frame);
+
+ /* Añadir un widget hijo al marco proporcional */
+
+ drawing_area = gtk_drawing_area_new ();
+
+ /* Pediremos una ventana de 200x200, pero el marco proporcional
+ * sólo no dejará una ventana de 200x100, ya que tenemos una
+ * relación de 2x1 */
+ gtk_widget_set_usize (drawing_area, 200, 200);
+ gtk_container_add (GTK_CONTAINER(aspect_frame), drawing_area);
+ gtk_widget_show (drawing_area);
+
+ gtk_widget_show (ventana);
+ gtk_main ();
+ return 0;
+}
+/* fin del ejemplo */
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+
+<sect1> El <em/widget/ ventana dividida (<em/Paned Window/)
+<p>
+El <em/widget/ ventana dividida es útil para cuando se quiere dividir
+una zona en dos partes, con un tamaño relativo controlado por el
+usuario. Entre las dos porciones de la ventana se dibuja un separador
+con un botoncito que el usuario puede arrastrar para cambiar el tamaño
+de cada zona. La división puede ser horizontal (HPaned) o vertical
+(VPaned).
+
+Para crear una nueva ventana dividida, utilice una de las siguientes
+funciones:
+
+<tscreen><verb>
+GtkWidget *gtk_hpaned_new (void);
+
+GtkWidget *gtk_vpaned_new (void);
+</verb></tscreen>
+
+Después de crear el <em/widget/ ventana dividida, tiene que añadirle
+un <em/widget/ hijo a cada mitad. Para hacerlo, utilice:
+
+<tscreen><verb>
+void gtk_paned_add1 (GtkPaned *paned, GtkWidget *hijo);
+
+void gtk_paned_add2 (GtkPaned *paned, GtkWidget *hijo);
+</verb></tscreen>
+
+<tt/gtk_paned_add1()/ añade el <em/widget/ hijo a la mitad que se
+encuentra en la parte izquierda o superior de la ventana
+dividida. <tt/gtk_paned_add2()/ añade el <em/widget/ a la mitad que
+hay en la parte derecha o inferior de la ventana.
+
+Por ejemplo, si queremos crear una parte del interface de usuario de
+un programa de correo-e imaginario. Dividiremos verticalmente una
+ventana en dos partes, teniendo en la parte superior una lista de los
+mensajes de correo-e y en la parte inferior el texto de uno de estos
+mensajes. El programa es bastante fácil de entender. Solo un par de
+cosillas: no se puede añadir texto en un <em/widget/ de texto (Text)
+si no se ha hecho antes <tt/gtk_widget_realize()/, pero como
+demostración de una técnica alternativa, para añadir el texto
+conectaremos un manipulador a la señal «realize». Y tenemos que
+añadir la opción <tt/GTK_SHRINK/ a algunos de los elementos que hay en
+la tabla con la ventana de texto y sus barras de desplazamiento, así
+cuando la porción de abajo se haga más pequeña, se encogerá
+correctamente en lugar de desaparecer por la parte de abajo de la
+ventana.
+
+<tscreen><verb>
+/* principio del ejemplo paned paned.c */
+
+#include <gtk/gtk.h>
+
+/* Crear la lista de "messages" */
+GtkWidget *
+create_list (void)
+{
+
+ GtkWidget *scrolled_window;
+ GtkWidget *list;
+ GtkWidget *list_item;
+
+ int i;
+ char buffer[16];
+
+ /* Crear una nueva ventana con barras de desplazamiento si hacen
+ falta */
+ scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+
+ /* Crear una nueva lista y poner en ella la ventana con barras */
+ list = gtk_list_new ();
+ gtk_container_add (GTK_CONTAINER(scrolled_window), list);
+ gtk_widget_show (list);
+
+ /* Añadir algunos mensajes a la ventana */
+ for (i=0; i<10; i++) {
+
+ sprintf(buffer,"Message #%d",i);
+ list_item = gtk_list_item_new_with_label (buffer);
+ gtk_container_add (GTK_CONTAINER(list), list_item);
+ gtk_widget_show (list_item);
+
+ }
+
+ return scrolled_window;
+}
+
+/* Añadir algún texto a nuestro widget de texto - esta función se
+ invoca cada vez que se produce una señal realize en la
+ ventana. Podemos forzar esta señal mediante gtk_widget_realize, pero
+ primero tiene que formar parte de una jerarquía */
+
+void
+realize_text (GtkWidget *text, gpointer data)
+{
+ gtk_text_freeze (GTK_TEXT (text));
+ gtk_text_insert (GTK_TEXT (text), NULL, &amp;text->style->black, NULL,
+ "From: pathfinder@nasa.gov\n"
+ "To: mom@nasa.gov\n"
+ "Subject: Made it!\n"
+ "\n"
+ "We just got in this morning. The weather has been\n"
+ "great - clear but cold, and there are lots of fun sights.\n"
+ "Sojourner says hi. See you soon.\n"
+ " -Path\n", -1);
+
+ gtk_text_thaw (GTK_TEXT (text));
+}
+
+/* Creamos una zona con texto que muestra un "message" */
+GtkWidget *
+create_text (void)
+{
+ GtkWidget *table;
+ GtkWidget *text;
+ GtkWidget *hscrollbar;
+ GtkWidget *vscrollbar;
+
+ /* Crea una tabla para contener el widget de texto y las barras de
+ desplazamiento */
+ table = gtk_table_new (2, 2, FALSE);
+
+ /* Pone un widget de texto en la esquina superior izquierda.
+ Observe la utilización de GTK_SHRINK en la dirección y */
+ text = gtk_text_new (NULL, NULL);
+ gtk_table_attach (GTK_TABLE (table), text, 0, 1, 0, 1,
+ GTK_FILL | GTK_EXPAND,
+ GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 0);
+ gtk_widget_show (text);
+
+ /* Pone una HScrollbar en la esquina inferior izquierda */
+ hscrollbar = gtk_hscrollbar_new (GTK_TEXT (text)->hadj);
+ gtk_table_attach (GTK_TABLE (table), hscrollbar, 0, 1, 1, 2,
+ GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (hscrollbar);
+
+ /* Y una VScrollbar en la esquina superior derecha */
+ vscrollbar = gtk_vscrollbar_new (GTK_TEXT (text)->vadj);
+ gtk_table_attach (GTK_TABLE (table), vscrollbar, 1, 2, 0, 1,
+ GTK_FILL, GTK_EXPAND | GTK_FILL | GTK_SHRINK, 0, 0);
+ gtk_widget_show (vscrollbar);
+
+ /* Y un manejador para poner un mensaje en el widget de texto
+ cuando reciba realize */
+ gtk_signal_connect (GTK_OBJECT (text), "realize",
+ GTK_SIGNAL_FUNC (realize_text), NULL);
+
+ return table;
+}
+
+int
+main (int argc, char *argv[])
+{
+ GtkWidget *ventana;
+ GtkWidget *vpaned;
+ GtkWidget *list;
+ GtkWidget *text;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title (GTK_WINDOW (ventana), "Paned Windows");
+ gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
+ GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
+ gtk_container_border_width (GTK_CONTAINER (ventana), 10);
+
+ /* crea un widget vpaned y lo añade a nuestra ventana superior */
+
+ vpaned = gtk_vpaned_new ();
+ gtk_container_add (GTK_CONTAINER(ventana), vpaned);
+ gtk_widget_show (vpaned);
+
+ /* Ahora crea los contenidos de las dos mitades de la ventana */
+
+ list = create_list ();
+ gtk_paned_add1 (GTK_PANED(vpaned), list);
+ gtk_widget_show (list);
+
+ text = create_text ();
+ gtk_paned_add2 (GTK_PANED(vpaned), text);
+ gtk_widget_show (text);
+ gtk_widget_show (ventana);
+ gtk_main ();
+ return 0;
+}
+/* fin del ejemplo */
+</verb></tscreen>
+
+<!-- XXX -->
+<!-- ----------------------------------------------------------------- -->
+<sect1> <em/Viewports/ <label id="sec_Viewports">
+<p>
+Probablemente nunca le llegue a hacer falta utilizar el <em/widget/
+Viewport directamente. Será mucho más probable que tenga que utilizar
+el <em/widget/ <ref id="sec_ScrolledWindows" name="Ventanas con barras
+de desplazamiento"> que a su vez hace uso de <em/viewport/.
+
+Un <em/widget viewport/ le permite meter dentro un gran <em/widget/,
+de forma que sólo verá una parte del mismo. Utiliza
+<ref id="sec_Adjustment" name="ajustes"> para definir la zona que se
+está viendo actualmente.
+
+Para crear un <em/viewport/ hay que utilizar la función:
+
+<tscreen><verb>
+GtkWidget *gtk_viewport_new( GtkAdjustment *hadjustment,
+ GtkAdjustment *vadjustment );
+</verb></tscreen>
+
+Como puede observar, se pueden especificar los ajustes horizontal y
+vertical que el <em/widget/ va a utilizar en el mismo momento de su
+creación. El <em/widget/ creará sus propios ajustes en caso de que
+reciba como argumento un valor NULL.
+
+Puede obtener y establecer los ajustes después de que se haya
+creado el <em/widget/ utilizado las cuatro funciones siguientes:
+
+<tscreen><verb>
+GtkAdjustment *gtk_viewport_get_hadjustment (GtkViewport *viewport );
+
+GtkAdjustment *gtk_viewport_get_vadjustment (GtkViewport *viewport );
+
+void gtk_viewport_set_hadjustment( GtkViewport *viewport,
+ GtkAdjustment *adjustment );
+
+void gtk_viewport_set_vadjustment( GtkViewport *viewport,
+ GtkAdjustment *adjustment );
+</verb></tscreen>
+
+La única función relativa al <em/viewport/ que queda que altera su
+apariencia es:
+
+<tscreen><verb>
+void gtk_viewport_set_shadow_type( GtkViewport *viewport,
+ GtkShadowType type );
+</verb></tscreen>
+
+Los valores posibles para el argumento <tt/type/ son:
+<itemize>
+<item> GTK_SHADOW_NONE,
+<item> GTK_SHADOW_IN,
+<item> GTK_SHADOW_OUT,
+<item> GTK_SHADOW_ETCHED_IN,
+<item> GTK_SHADOW_ETCHED_OUT
+</itemize>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Ventanas con barras de desplazamiento <label id="sec_ScrolledWindows">
+<p>
+
+Las ventanas con barras de desplazamiento se utilizan para crear una zona
+con barras de desplazamiento dentro de una ventana real. Puede insertar
+cualquier tipo de <em/widget/ en una ventana con barras de
+desplazamiento, y podrá utilizarlo sin importar su tamaño gracias a
+las barras de desplazamiento.
+
+La función siguiente se utiliza para crear una nueva ventana con
+barras de desplazamiento.
+
+<tscreen><verb>
+GtkWidget *gtk_scrolled_window_new( GtkAdjustment *hadjustment,
+ GtkAdjustment *vadjustment );
+</verb></tscreen>
+
+Donde el primer argumento es el ajuste para la dirección horizontal, y
+el segundo es el ajuste para la dirección vertical. Casi siempre valen
+NULL.
+
+<tscreen><verb>
+void gtk_scrolled_window_set_policy( GtkScrolledWindow *scrolled_window,
+ GtkPolicyType hscrollbar_policy,
+ GtkPolicyType vscrollbar_policy );
+</verb></tscreen>
+
+Esta función establece la política que se utilizará con respecto a las
+barras de desplazamiento. El primer argumento es la ventana con barras
+de desplazamiento sobre la que queremos actuar. El segundo establece
+la política para la barra de desplazamiento horizontal, y el tercero
+la política para la barra de desplazamiento vertical.
+
+La política puede ser GTK_POLICY_AUTOMATIC, o
+GTK_POLICY_ALWAYS. GTK_POLICY_AUTOMATIC decidirá automáticamente si
+necesita barras de desplazamiento, mientras que GTK_POLICY_ALWAYS pondrá
+siempre las barras de desplazamiento.
+
+Aquí tenemos un ejemplo sencillo que empaqueta 100 botones de
+selección en una ventana con barras de desplazamiento. Solamente he
+comentado las partes que debería ser nuevas para usted.
+
+<tscreen><verb>
+/* principio del ejemplo scrolledwin scrolledwin.c */
+
+#include <gtk/gtk.h>
+
+void destroy(GtkWidget *widget, gpointer data)
+{
+ gtk_main_quit();
+}
+
+int main (int argc, char *argv[])
+{
+ static GtkWidget *ventana;
+ GtkWidget *scrolled_window;
+ GtkWidget *table;
+ GtkWidget *boton;
+ char buffer[32];
+ int i, j;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ /* Crea un nuevo cuadro de diálogo para que la ventana con barras de
+ * desplazamiento se meta dentro. Un cuadro de diálogo es como una
+ * ventana normal excepto que tiene dentro una vbox y un separador
+ * horizontal. Es sólo un atajo para crear cuadros de diálogo */
+ ventana = gtk_dialog_new ();
+ gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
+ (GtkSignalFunc) destroy, NULL);
+ gtk_window_set_title (GTK_WINDOW (ventana), "dialog");
+ gtk_container_border_width (GTK_CONTAINER (ventana), 0);
+ gtk_widget_set_usize(ventana, 300, 300);
+
+ /* crea una nueva ventana con barras de desplazamiento. */
+ scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+
+ gtk_container_border_width (GTK_CONTAINER (scrolled_window), 10);
+
+ /* la política es GTK_POLICY_AUTOMATIC, o GTK_POLICY_ALWAYS.
+ * GTK_POLICY_AUTOMATIC decidirá automáticamente si necesita
+ * barras de desplazamiento, mientras que GTK_POLICY_ALWAYS pondrá
+ * siempre las barras de desplazamiento. El primer argumento se
+ * refiere a la barra horizontal, el segundo a la vertical. */
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
+ /* El cuadro de diálogo se crea con una vbox dentro de él. */
+ gtk_box_pack_start (GTK_BOX (GTK_DIALOG(ventana)->vbox), scrolled_window,
+ TRUE, TRUE, 0);
+ gtk_widget_show (scrolled_window);
+
+ /* crea una tabla de 10 por 10 casillas. */
+ table = gtk_table_new (10, 10, FALSE);
+
+ /* pone el espacio en x y en y a 10 */
+ gtk_table_set_row_spacings (GTK_TABLE (table), 10);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 10);
+
+ /* empaqueta la tabla en la ventana con barras de desplazamiento */
+ gtk_container_add (GTK_CONTAINER (scrolled_window), table);
+ gtk_widget_show (table);
+
+ /* crea una rejilla de botones de selección en la tabla para
+ * demostrar la ventana con barras de desplazamiento. */
+ for (i = 0; i < 10; i++)
+ for (j = 0; j < 10; j++) {
+ sprintf (buffer, "botón (%d,%d)\n", i, j);
+ boton = gtk_toggle_button_new_with_label (buffer);
+ gtk_table_attach_defaults (GTK_TABLE (table), boton,
+ i, i+1, j, j+1);
+ gtk_widget_show (boton);
+ }
+
+ /* Añade un botón "close" en la parte de abajo del cuadro de
+ * diálogo */
+ boton = gtk_button_new_with_label ("close");
+ gtk_signal_connect_object (GTK_OBJECT (boton), "clicked",
+ (GtkSignalFunc) gtk_widget_destroy,
+ GTK_OBJECT (ventana));
+
+ /* hace que el botón puede ser elegido por defecto. */
+
+ GTK_WIDGET_SET_FLAGS (boton, GTK_CAN_DEFAULT);
+ gtk_box_pack_start (GTK_BOX (GTK_DIALOG (ventana)->action_area), boton, TRUE, TRUE, 0);
+
+ /* Hace que el botón sea el elegido por defecto. Con pulsar la
+ * tecla "Enter" se activará este botón. */
+ gtk_widget_grab_default (boton);
+ gtk_widget_show (boton);
+
+ gtk_widget_show (ventana);
+
+ gtk_main();
+
+ return(0);
+}
+/* fin del ejemplo */
+</verb></tscreen>
+
+Juegue un poco redimensionando la ventana. Vea como actuan las barras
+de desplazamiento. También puede utilizar la función
+<tt/gtk_widget_set_usize()/ para poner el tamaño por defecto de la
+ventana o de cualquier otro <em/widget/.
+
+<!-- XXX -->
+<!-- ----------------------------------------------------------------- -->
+<sect1>Cajas de botones
+<p>
+Las cajas de botones son útiles para crear grupos de botones. Hay
+cajas horizontales y verticales. Puede crear una nueva caja de botones
+utilizando alguna de las funciones siguientes, que crean
+respectivamente una caja horizontal y otra vertical:
+
+<tscreen><verb>
+GtkWidget *gtk_hbutton_box_new( void );
+
+GtkWidget *gtk_vbutton_box_new( void );
+</verb></tscreen>
+
+Los únicos atributos pertenecientes a las cajas de botones son los
+que definen como se distribuyen los botones. Puede cambiar el
+espaciado que hay entre los botones con:
+
+<tscreen><verb>
+void gtk_hbutton_box_set_spacing_default( gint spacing );
+
+void gtk_vbutton_box_set_spacing_default( gint spacing );
+</verb></tscreen>
+
+Igualmente, se pueden obtener los actuales valores para el espaciado
+utilizando:
+
+<tscreen><verb>
+gint gtk_hbutton_box_get_spacing_default( void );
+
+gint gtk_vbutton_box_get_spacing_default( void );
+</verb></tscreen>
+
+El segundo atributo al que podemos acceder afecta al esquema de los
+botones dentro de la caja. Se establece utilizando:
+
+<tscreen><verb>
+void gtk_hbutton_box_set_layout_default( GtkButtonBoxStyle layout );
+
+void gtk_vbutton_box_set_layout_default( GtkButtonBoxStyle layout );
+</verb></tscreen>
+
+El argumento <tt/layout/ puede tomar uno de los siguientes valores:
+
+<itemize>
+<item> GTK_BUTTONBOX_DEFAULT_STYLE
+<item> GTK_BUTTONBOX_SPREAD
+<item> GTK_BUTTONBOX_EDGE
+<item> GTK_BUTTONBOX_START
+<item> GTK_BUTTONBOX_END
+</itemize>
+
+Puede obtenerse el esquema actual utilizando:
+
+<tscreen><verb>
+GtkButtonBoxStyle gtk_hbutton_box_get_layout_default( void );
+
+GtkButtonBoxStyle gtk_vbutton_box_get_layout_default( void );
+</verb></tscreen>
+
+Podemos añadir botones a una caja de botones utilizando (como
+siempre) la función:
+
+<tscreen><verb>
+ gtk_container_add( GTK_CONTAINER(button_box), child_widget );
+</verb></tscreen>
+
+Aquí hay un ejemplo que ilustra todos los diferentes esquemas que
+podemos utilizar con las cajas de botones.
+
+<tscreen><verb>
+/* principio del ejemplo buttonbox buttonbox.c */
+
+#include <gtk/gtk.h>
+
+/* Crear una Caja de Botones con los parámetros
+ * especificados */
+GtkWidget *create_bbox (gint horizontal,
+ char* title,
+ gint spacing,
+ gint child_w,
+ gint child_h,
+ gint layout)
+{
+ GtkWidget *frame;
+ GtkWidget *bbox;
+ GtkWidget *boton;
+
+ frame = gtk_frame_new (title);
+
+ if (horizontal)
+ bbox = gtk_hbutton_box_new ();
+ else
+ bbox = gtk_vbutton_box_new ();
+
+ gtk_container_set_border_width (GTK_CONTAINER (bbox), 5);
+ gtk_container_add (GTK_CONTAINER (frame), bbox);
+
+ /* Establece la apariencia de la Caja de Botones */
+ gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), layout);
+ gtk_button_box_set_spacing (GTK_BUTTON_BOX (bbox), spacing);
+ gtk_button_box_set_child_size (GTK_BUTTON_BOX (bbox), child_w, child_h);
+
+ boton = gtk_button_new_with_label ("OK");
+ gtk_container_add (GTK_CONTAINER (bbox), boton);
+
+ boton = gtk_button_new_with_label ("Cancel");
+ gtk_container_add (GTK_CONTAINER (bbox), boton);
+
+ boton = gtk_button_new_with_label ("Help");
+ gtk_container_add (GTK_CONTAINER (bbox), boton);
+
+ return(frame);
+}
+
+int main( int argc,
+ char *argv[] )
+{
+ static GtkWidget* ventana = NULL;
+ GtkWidget *main_vbox;
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ GtkWidget *frame_horz;
+ GtkWidget *frame_vert;
+
+ /* Inicializa GTK */
+ gtk_init( &amp;argc, &amp;argv );
+
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title (GTK_WINDOW (ventana), "Button Boxes");
+
+ gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
+ GTK_SIGNAL_FUNC(gtk_main_quit),
+ NULL);
+
+ gtk_container_set_border_width (GTK_CONTAINER (ventana), 10);
+
+ main_vbox = gtk_vbox_new (FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (ventana), main_vbox);
+
+ frame_horz = gtk_frame_new ("Horizontal Button Boxes");
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame_horz, TRUE, TRUE, 10);
+
+ vbox = gtk_vbox_new (FALSE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 10);
+ gtk_container_add (GTK_CONTAINER (frame_horz), vbox);
+
+ gtk_box_pack_start (GTK_BOX (vbox),
+ create_bbox (TRUE, "Spread (spacing 40)", 40, 85, 20, GTK_BUTTONBOX_SPREAD),
+ TRUE, TRUE, 0);
+
+ gtk_box_pack_start (GTK_BOX (vbox),
+ create_bbox (TRUE, "Edge (spacing 30)", 30, 85, 20, GTK_BUTTONBOX_EDGE),
+ TRUE, TRUE, 5);
+
+ gtk_box_pack_start (GTK_BOX (vbox),
+ create_bbox (TRUE, "Start (spacing 20)", 20, 85, 20, GTK_BUTTONBOX_START),
+ TRUE, TRUE, 5);
+
+ gtk_box_pack_start (GTK_BOX (vbox),
+ create_bbox (TRUE, "End (spacing 10)", 10, 85, 20, GTK_BUTTONBOX_END),
+ TRUE, TRUE, 5);
+
+ frame_vert = gtk_frame_new ("Vertical Button Boxes");
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame_vert, TRUE, TRUE, 10);
+
+ hbox = gtk_hbox_new (FALSE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (hbox), 10);
+ gtk_container_add (GTK_CONTAINER (frame_vert), hbox);
+
+ gtk_box_pack_start (GTK_BOX (hbox),
+ create_bbox (FALSE, "Spread (spacing 5)", 5, 85, 20, GTK_BUTTONBOX_SPREAD),
+ TRUE, TRUE, 0);
+
+ gtk_box_pack_start (GTK_BOX (hbox),
+ create_bbox (FALSE, "Edge (spacing 30)", 30, 85, 20, GTK_BUTTONBOX_EDGE),
+ TRUE, TRUE, 5);
+
+ gtk_box_pack_start (GTK_BOX (hbox),
+ create_bbox (FALSE, "Start (spacing 20)", 20, 85, 20, GTK_BUTTONBOX_START),
+ TRUE, TRUE, 5);
+
+ gtk_box_pack_start (GTK_BOX (hbox),
+ create_bbox (FALSE, "End (spacing 20)", 20, 85, 20, GTK_BUTTONBOX_END),
+ TRUE, TRUE, 5);
+
+ gtk_widget_show_all (ventana);
+
+ /* Entra dentro del bucle de eventos */
+ gtk_main ();
+
+ return(0);
+}
+/* fin del ejemplo */
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Barras de herramientas
+<p>
+Las barras de herramientas acostumbran a agrupar un conjunto de
+<em>widgets</em> para hacer más sencilla la personalización
+de su aspecto y composición. Típicamente una barra de herramientas
+consiste en botones con iconos, etiquetas y <em/tips/ para los iconos
+(pequeño texto descriptivo que aparece cuando se mantiene el ratón
+sobre el icono), pero en realidad en una barra se puede poner
+cualquier tipo de <em>widget</em>. Finalmente, los elementos se pueden
+disponer de forma horizontal o vertical, y los botones pueden mostrar
+iconos, etiquetas o ambos.
+
+La creación de una barra de herramientas se hace (como puede que ya
+haya sospechado) mediante la función siguiente:
+
+<tscreen><verb>
+GtkWidget *gtk_toolbar_new( GtkOrientation orientation,
+ GtkToolbarStyle style );
+</verb></tscreen>
+
+donde <tt/orientation/ puede ser:
+
+<tscreen><verb>
+ GTK_ORIENTATION_HORIZONTAL
+ GTK_ORIENTATION_VERTICAL
+</verb></tscreen>
+
+y <tt/style/:
+
+<tscreen><verb>
+ GTK_TOOLBAR_TEXT
+ GTK_TOOLBAR_ICONS
+ GTK_TOOLBAR_BOTH
+</verb></tscreen>
+
+La variable <tt/style/ se aplica a todos los botones que se crean con las
+funciones `item' (pero no a los botones insertados en la barra de
+herramientas como <em>widgets</em> separados).
+
+Después de crear una barra de herramientas, se pueden añadir,
+preañadir e insertar elementos (o sea, botones) en la misma. Los
+campos que describen un elemento son el texto de la etiqueta, el texto
+del <em/tip/, un texto para el <em/tip/ privado, un icono para el
+botón y una función de llamada para el mismo. Por ejemplo, para añadir
+un elemento puede utilizar la siguiente función:
+
+<tscreen><verb>
+GtkWidget *gtk_toolbar_append_item( GtkToolbar *toolbar,
+ const char *text,
+ const char *tooltip_text,
+ const char *tooltip_private_text,
+ GtkWidget *icon,
+ GtkSignalFunc callback,
+ gpointer user_data );
+</verb></tscreen>
+
+Si quiere utilizar <tt/gtk_toolbar_insert_item/, el único parámetro
+adicional que debería especificar es la posición en la que quiere que
+se introduzca el elemento.
+
+Para añadir un espacio en blanco entre los elementos de la barra de
+herramientas, puede utilizar la función siguiente:
+
+<tscreen><verb>
+void gtk_toolbar_append_space( GtkToolbar *toolbar );
+
+void gtk_toolbar_prepend_space( GtkToolbar *toolbar );
+
+void gtk_toolbar_insert_space( GtkToolbar *toolbar,
+ gint posicion );
+
+</verb></tscreen>
+
+Y el tamaño del espacio en blanco puede establecerse globalmente
+para toda una barra de herramientas con la función:
+
+<tscreen><verb>
+void gtk_toolbar_set_space_size( GtkToolbar *toolbar,
+ gint space_size) ;
+</verb></tscreen>
+
+Si tiene que establecer la orientación de una barra de herramientas y
+su estilo, puede hacerlo `al vuelo' con las funciones siguientes:
+
+<tscreen><verb>
+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 );
+</verb></tscreen>
+
+Para mostrar algunas otras cosas que pueden hacerse con una barra de
+herramientas, vamos a ver el siguiente programa (interrumpiremos el
+listado con alguna explicación adicional):
+
+<tscreen><verb>
+#include <gtk/gtk.h>
+
+#include "gtk.xpm"
+
+/* Esta función está conectada al botón Close o a la acción de cerrar
+ * la ventana desde el WM */
+void delete_event (GtkWidget *widget, GdkEvent *event, gpointer data)
+{
+ gtk_main_quit ();
+}
+</verb></tscreen>
+
+Este principio ya debería de sonarle familiar, a no ser que éste sea
+su primer programa GTK. En nuestro programa no habrá ninguna novedad,
+salvo un bonito dibujo XPM que utilizaremos como icono para todos los
+botones.
+
+<tscreen><verb>
+GtkWidget* close_button; // este botón emitirá la señal de cerrar el programa
+GtkWidget* tooltips_button; // para activar/desactivar los tooltips
+GtkWidget* text_button,
+ * icon_button,
+ * both_button; // botones circulares para el estilo de la barra
+GtkWidget* entry; // un widget para meter texto para mostrar como
+ // empaquetar widgets en la barra de herramientas
+</verb></tscreen>
+
+En realidad no necesitamos todos los <em>widgets</em> que acabo de
+poner, pero para aclarar las cosas un poco más los he puesto todos.
+
+<tscreen><verb>
+/* Esto es fácil... cuando uno de los botones cambia, sólo
+ * tenemos que comprobar quien está activo y hacer que el estilo
+ * de la barra de herramientas esté acorde con la elección
+ * ATENCIÓN: ¡nuestra barra de herramientas es data !
+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);
+}
+
+/* todavía más fácil, sólo hay que comprobar el botón de selección
+ * y activar/desactivar los tooltips */
+void toggle_event (GtkWidget *widget, gpointer data)
+{
+ gtk_toolbar_set_tooltips (GTK_TOOLBAR ( data ),
+ GTK_TOGGLE_BUTTON (widget)->active );
+}
+</verb></tscreen>
+
+Lo de arriba son sólo dos funciones de llamada que se invocarán cuando
+se presione uno de los botones de la barra de herramientas. Todo esto
+ya debería resultarle familiar si ha utilizado alguna vez los botones
+de selección (o los botones circulares)
+
+<tscreen><verb>
+int main (int argc, char *argv[])
+{
+ /* Aquí está nuestra ventana principal (un cuadro de diálogo) y una
+ * caja flotante */
+ GtkWidget* dialog;
+ GtkWidget* handlebox;
+
+ /* De acuerdo, necesitamos una barra de herramientas, un icono con
+ * una máscara (una para todos los botones) y un widget icono donde
+ * meter el icono (crearemos un widget diferente para cada botón) */
+ GtkWidget * toolbar;
+ GdkPixmap * icon;
+ GdkBitmap * mask;
+ GtkWidget * iconw;
+
+ /* a esta función se le llama en todas las aplicación GTK */
+ gtk_init (&amp;argc, &amp;argv);
+
+ /* crear una ventana nueva con un título y el tamaño adecuado */
+ dialog = gtk_dialog_new ();
+ gtk_window_set_title ( GTK_WINDOW ( dialog ) , "GTKToolbar Tutorial");
+ gtk_widget_set_usize( GTK_WIDGET ( dialog ) , 600 , 300 );
+ GTK_WINDOW ( dialog ) ->allow_shrink = TRUE;
+
+ /* salimos si alguien intenta cerrarnos */
+ gtk_signal_connect ( GTK_OBJECT ( dialog ), "delete_event",
+ GTK_SIGNAL_FUNC ( delete_event ), NULL);
+
+ /* tenemos que mandar la señalo realize porque utilizamos pixmaps
+ * para los elementos que hay en la barra de herramientas */
+ gtk_widget_realize ( dialog );
+
+ /* para hacerlo más bonito ponemos la barra de herramientas en la
+ * caja flotante, para que así se pueda desatar de la ventana
+ * principal */
+ handlebox = gtk_handle_box_new ();
+ gtk_box_pack_start ( GTK_BOX ( GTK_DIALOG(dialog)->vbox ),
+ handlebox, FALSE, FALSE, 5 );
+</verb></tscreen>
+
+Lo de arriba debería ser parecido en cualquier aplicación GTK. Sólo
+está la inicialización de GTK, la creación de la ventana, etc...
+Solamente hay una cosa que probablemente necesite una explicación:
+una barra de herramientas flotante. Una barra de herramientas flotante
+sólo es otra barra donde pueden empaquetarse <em>widgets</em>. La
+diferencia que tiene con una barra típica es que puede desatarse de la
+ventana padre (o, de hecho, la barra de herramientas flotante permanece
+en el padre, pero reducida a un rectángulo muy pequeño, mientras que
+todos sus contenidos se pasan a una nueva ventana flotante). Es bonito
+tener una barra de herramientas flotante, por lo que estos dos
+<em>widgets</em> suelen aparecer juntos.
+
+<tscreen><verb>
+ /* la barra de herramientas será horizontal, con iconos y texto, y
+ * con un espacio de 5pxl entre elementos y finalmente, la ponemos en
+ * nuestra caja flotante */
+ toolbar = gtk_toolbar_new ( GTK_ORIENTATION_HORIZONTAL,
+ GTK_TOOLBAR_BOTH );
+ gtk_container_border_width ( GTK_CONTAINER ( toolbar ) , 5 );
+ gtk_toolbar_set_space_size ( GTK_TOOLBAR ( toolbar ), 5 );
+ gtk_container_add ( GTK_CONTAINER ( handlebox ) , toolbar );
+
+ /* ahora creamos el icono con la máscara: utilizaremos el widget
+ * icon con todos los elementos de la barra de herramientas */
+ icon = gdk_pixmap_create_from_xpm_d ( dialog->window, &amp;mask,
+ &amp;dialog->style->white, gtk_xpm );
+</verb></tscreen>
+
+Bien, lo que acabamos de escribir es la inicialización del
+<em>widget</em> de la barra de herramientas y la creación de un
+<em>pixmap</em> GDK con su máscara. Si quiere saber algo más sobre la
+utilización de <em>pixmaps</em>, vea la documentación de GDK o la
+sección <ref id="sec_Pixmaps" name="Pixmaps"> en este tutorial.
+
+<tscreen><verb>
+ /* nuestro primer elemento es el botón <close> */
+ iconw = gtk_pixmap_new ( icon, mask ); // icon widget
+ close_button =
+ gtk_toolbar_append_item ( GTK_TOOLBAR (toolbar), // nuestra barra
+ "Close", // etiqueta del botón
+ "Closes this app", // tooltip para el botón
+ "Private", // cadena privada del tooltip
+ iconw, // widget del icono
+ GTK_SIGNAL_FUNC (delete_event), // una señal
+ NULL );
+ gtk_toolbar_append_space ( GTK_TOOLBAR ( toolbar ) ); // espacio después del elemento
+</verb></tscreen>
+
+En el trozo de código de arriba puede ver como se hace la acción más
+simple: añadir un botón a la barra de herramientas. Justo antes de
+añadir un nuevo elemento, tenemos que construir un <em>widget
+pixmap</em> para que sirva como icono para este elemento; este paso
+tendrá que repetirse para cada nuevo elemento. Después del elemento
+añadiremos un espacio en blanco en la barra de herramientas, para que
+los elementos que añadamos a continuación no se toquen los unos a los
+otros. Como puede ver, <tt/gtk_toolbar_append_item/ devuelve un
+puntero al <em>widget</em> de nuestro nuevo botón recien creado, por
+lo que podremos trabajar con él como siempre.
+
+<tscreen><verb>
+ /* ahora, vamos a hacer nuestro grupo de botones circulares... */
+ iconw = gtk_pixmap_new ( icon, mask );
+ icon_button =
+ gtk_toolbar_append_element(GTK_TOOLBAR(toolbar),
+ GTK_TOOLBAR_CHILD_RADIOBUTTON, // un tipo de elemento
+ NULL, // puntero al widget
+ "Icon", // etiqueta
+ "Only icons in toolbar", // tooltip
+ "Private", // cadena privada del tooltip
+ iconw, // icono
+ GTK_SIGNAL_FUNC (radio_event), // señal
+ toolbar); // dato para la señal
+ gtk_toolbar_append_space ( GTK_TOOLBAR ( toolbar ) );
+</verb></tscreen>
+
+Aquí empezamos creando un grupo de botones circulares. Para hacerlo
+hemos utilizado <tt/gtk_toolbar_append_element/. De hecho, utilizando
+esta función se pueden añadir tanto elementos simples como espacios en
+blanco (tipo = GTK_TOOLBAR_CHILD_SPACE o GTK_TOOLBAR_CHILD_BUTTON). En
+el caso de arriba, hemos empezado creando un grupo de botones circulares.
+Para crear más botones circulares para este grupo
+necesitaremos un puntero al botón anterior del grupo, mediante el que
+podremos construir fácilmente una lista de botones (ver la sección
+<ref id="sec_Radio_Buttons" name="Botones circulares"> que se encuentra
+más adelante en este tutorial).
+
+<tscreen><verb>
+ /* los botones circulares que vienen a continuación están
+ relacionados con los anteriores */
+ iconw = gtk_pixmap_new ( icon, mask );
+ 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_pixmap_new ( icon, mask );
+ 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_state(GTK_TOGGLE_BUTTON(both_button),TRUE);
+</verb></tscreen>
+
+Al final hemos activado manualmente uno de los botones (en caso
+contrario los botones permanecerían todos en estado activo,
+impidiéndonos poder cambiar de uno a otro).
+
+<tscreen><verb>
+ /* aquí tenemos un sencillo botón de selección */
+ iconw = gtk_pixmap_new ( icon, mask );
+ 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_state(GTK_TOGGLE_BUTTON(tooltips_button),TRUE);
+</verb></tscreen>
+
+Un botón de selección puede crearse de una forma obvia (si ya sabe como
+crear botones circulares).
+
+<tscreen><verb>
+ /* para empaquetar un widget en la barra de herramientas, sólo
+ * tenemos que crearlo y añadirlo en la barra con el tooltip
+ * apropiado */
+ entry = gtk_entry_new ();
+ gtk_toolbar_append_widget( GTK_TOOLBAR (toolbar),
+ entry,
+ "This is just an entry",
+ "Private" );
+
+ /* bien, no se ha creado con la barra, así que debemos mostrarlo
+ * explicitamente */
+ gtk_widget_show ( entry );
+</verb></tscreen>
+
+Como puede ver, añadir cualquier tipo de <em>widget</em> a la barra
+de herramientas es fácil. Lo único que debe recordar es que este
+<em>widget</em> debe mostrarse manualmente (al contrario que los demás
+elementos que se mostrarán junto con la barra de herramientas).
+
+<tscreen><verb>
+ /* ¡ Eso es ! mostremos algo. */
+ gtk_widget_show ( toolbar );
+ gtk_widget_show (handlebox);
+ gtk_widget_show ( dialog );
+
+ /* quedémonos en gtk_main y ¡esperemos a que empiece la diversión! */
+ gtk_main ();
+
+ return 0;
+}
+</verb></tscreen>
+
+Y ya estamos en el final del tutorial sobre la barra de herramientas.
+Por supuesto, para apreciar completamente el ejemplo, necesita además
+del código este precioso icono XPM que le mostramos a continuación:
+
+<tscreen><verb>
+/* XPM */
+static char * gtk_xpm[] = {
+"32 39 5 1",
+". c none",
+"+ c black",
+"@ c #3070E0",
+"# c #F05050",
+"$ c #35E035",
+"................+...............",
+"..............+++++.............",
+"............+++++@@++...........",
+"..........+++++@@@@@@++.........",
+"........++++@@@@@@@@@@++........",
+"......++++@@++++++++@@@++.......",
+".....+++@@@+++++++++++@@@++.....",
+"...+++@@@@+++@@@@@@++++@@@@+....",
+"..+++@@@@+++@@@@@@@@+++@@@@@++..",
+".++@@@@@@+++@@@@@@@@@@@@@@@@@@++",
+".+#+@@@@@@++@@@@+++@@@@@@@@@@@@+",
+".+##++@@@@+++@@@+++++@@@@@@@@$@.",
+".+###++@@@@+++@@@+++@@@@@++$$$@.",
+".+####+++@@@+++++++@@@@@+@$$$$@.",
+".+#####+++@@@@+++@@@@++@$$$$$$+.",
+".+######++++@@@@@@@++@$$$$$$$$+.",
+".+#######+##+@@@@+++$$$$$$@@$$+.",
+".+###+++##+##+@@++@$$$$$$++$$$+.",
+".+###++++##+##+@@$$$$$$$@+@$$@+.",
+".+###++++++#+++@$$@+@$$@++$$$@+.",
+".+####+++++++#++$$@+@$$++$$$$+..",
+".++####++++++#++$$@+@$++@$$$$+..",
+".+#####+++++##++$$++@+++$$$$$+..",
+".++####+++##+#++$$+++++@$$$$$+..",
+".++####+++####++$$++++++@$$$@+..",
+".+#####++#####++$$+++@++++@$@+..",
+".+#####++#####++$$++@$$@+++$@@..",
+".++####++#####++$$++$$$$$+@$@++.",
+".++####++#####++$$++$$$$$$$$+++.",
+".+++####+#####++$$++$$$$$$$@+++.",
+"..+++#########+@$$+@$$$$$$+++...",
+"...+++########+@$$$$$$$$@+++....",
+".....+++######+@$$$$$$$+++......",
+"......+++#####+@$$$$$@++........",
+".......+++####+@$$$$+++.........",
+".........++###+$$$@++...........",
+"..........++##+$@+++............",
+"...........+++++++..............",
+".............++++..............."};
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Libros de notas (<em/Notebooks/)
+<p>
+El <em/widget/ Notebook es una colección de `páginas' que se solapan
+las unas a las otras, cada una con un contenido diferente. Este
+<em/widget/ se ha vuelto cada vez más común últimamente en la
+programación de interfaces gráficos de usuario (GUI en inglés), y es
+una buena forma de mostrar bloques de información similar que
+necesitan aparecer de forma separada.
+
+La primera función que necesita conocer, como probablemente ya habrá
+adivinado, se utiliza para crear un nuevo <em/widget/ notebook.
+
+<tscreen><verb>
+GtkWidget *gtk_notebook_new( void );
+</verb></tscreen>
+
+Una vez haya crear el libro de notas, hay 12 funciones que se pueden
+utilizar para trabajar con él. Echémosles un vistazo una a una.
+
+La primera que estudiaremos será la que nos permita establecer la
+posición de los indicadores de la página. Estos indicadores se pueden
+poner en cuatro lugares diferentes: arriba, abajo, a la derecha o a la
+izquierda.
+
+<tscreen><verb>
+void gtk_notebook_set_tab_pos( GtkNotebook *notebook,
+ GtkPositionType pos );
+</verb></tscreen>
+
+<tt/GtkPositionType/ debe tener uno de los valores siguientes (su significado
+está bastante claro):
+
+<itemize>
+<item> GTK_POS_LEFT
+<item> GTK_POS_RIGHT
+<item> GTK_POS_TOP
+<item> GTK_POS_BOTTOM
+</itemize>
+
+GTK_POS_TOP es el valor por defecto.
+
+Lo siguiente que estudiaremos es como añadir páginas al libro de notas.
+Hay tres formas de añadirle páginas al <em/widget/. Veamos las dos primeras
+formas (son muy parecidas).
+
+<tscreen><verb>
+void gtk_notebook_append_page( GtkNotebook *notebook,
+ GtkWidget *hijo,
+ GtkWidget *tab_label );
+
+void gtk_notebook_prepend_page( GtkNotebook *notebook,
+ GtkWidget *hijo,
+ GtkWidget *tab_label );
+</verb></tscreen>
+
+Estas funciones le añaden páginas al libro de notas insertándolas desde
+el fondo del libro (añadiéndolas), o desde parte superior del libro
+(preañadiéndolas). <tt/hijo/ es el <em/widget/ que se mete en la página
+del libro de notas, y <tt/tab_label/ es la etiqueta para la página que
+estamos añadiendo.
+
+La función que queda que sirve para añadir una página contiene todas las
+propiedades de las anteriores, pero además permite especificar en que
+posición quiere que esté la página dentro del libro de notas.
+
+<tscreen><verb>
+void gtk_notebook_insert_page( GtkNotebook *notebook,
+ GtkWidget *hijo,
+ GtkWidget *tab_label,
+ gint posicion );
+</verb></tscreen>
+
+Los parámetros son los mismos que habían en las funciones _append_ y
+_prepend_ excepto que hay uno más que antes, <tt/posicion/. Este
+parámetro se utiliza para especificar en que lugar debe introducirse
+la página.
+
+Ahora que sabemos como añadir un página, veamos como podemos eliminar
+una página del libro de notas.
+
+<tscreen><verb>
+void gtk_notebook_remove_page( GtkNotebook *notebook,
+ gint page_num );
+</verb></tscreen>
+
+Esta función coge la página especificada por <tt/page_num/ y la
+elimina del <em/widget/ al que apunta <tt/notebook/.
+
+Para saber cual es la página actual del libro de notas utilice la
+función:
+
+<tscreen><verb>
+gint gtk_notebook_current_page( GtkNotebook *notebook );
+</verb></tscreen>
+
+Las dos funciones siguientes sirven para ir a la página siguiente o a
+la anterior del libro de notas. Para utilizarlas sólo hay que
+proporcionar el <em/widget/ notebook que queremos manipular. Nota:
+cuando el libro de notas está en la última página y se llama a
+<tt/gtk_notebook_next_page/, se pasará a la primera página. Sin
+embargo, si el libro de notas está en la primera página, y se llama a
+<tt/gtk_notebook_prev_page/, no se pasará a la última página.
+
+<tscreen><verb>
+void gtk_notebook_next_page( GtkNoteBook *notebook );
+
+void gtk_notebook_prev_page( GtkNoteBook *notebook );
+</verb></tscreen>
+
+La siguiente función establece la página `activa'. Si quiere que se
+abra el libro de notas por la página 5, por ejemplo, debe utilizar
+esta función. Si no utiliza esta función el libro de notas empezará
+por defecto en la primera página.
+
+<tscreen><verb>
+void gtk_notebook_set_page( GtkNotebook *notebook,
+ gint page_num );
+</verb></tscreen>
+
+Las dos funciones siguientes añaden o eliminan los indicadores de las
+páginas o el borde del libro, respectivamente.
+
+<tscreen><verb>
+void gtk_notebook_set_show_tabs( GtkNotebook *notebook,
+ gint show_tabs);
+
+void gtk_notebook_set_show_border( GtkNotebook *notebook,
+ gint show_border );
+</verb></tscreen>
+
+<tt/show_tabs/ y <tt/show_border/ puede ser TRUE o FALSE.
+
+Ahora echémosle un vistaza a un ejemplo, sacado del código de
+<tt/testgtk.c/ que viene con la distribución de GTK, y que muestra
+la utilización de las 13 funciones. Este pequeño programa crea una
+ventana con un libro de notas y seis botones. El libro de notas
+contiene 11 páginas, incluidas de tres formas diferentes, añadidas,
+insertadas, y preañadidas. Los botones le permiten rotar las
+posiciones de los indicadores, añadir y eliminar los indicadores y el
+borde, eliminar una página, cambiar páginas hacia delante y hacia
+detrás, y salir del programa.
+
+<tscreen><verb>
+/* principio del ejemplo notebook notebook.c */
+
+#include <gtk/gtk.h>
+
+/* Esta función rota la posición de los indicadores */
+void rotate_book (GtkButton *boton, GtkNotebook *notebook)
+{
+ gtk_notebook_set_tab_pos (notebook, (notebook->tab_pos +1) %4);
+}
+
+/* Añade/Elimina los indicadores de la página y los bordes */
+void tabsborder_book (GtkButton *boton, GtkNotebook *notebook)
+{
+ gint tval = FALSE;
+ gint bval = FALSE;
+ if (notebook->show_tabs == 0)
+ tval = TRUE;
+ if (notebook->show_border == 0)
+ bval = TRUE;
+
+ gtk_notebook_set_show_tabs (notebook, tval);
+ gtk_notebook_set_show_border (notebook, bval);
+}
+
+/* Elimina una página del libro de notas */
+void remove_book (GtkButton *boton, GtkNotebook *notebook)
+{
+ gint page;
+
+ page = gtk_notebook_current_page(notebook);
+ gtk_notebook_remove_page (notebook, page);
+ /* Hay que redibujar el widget --
+ Esto fuerza que el widget se autoredibuje */
+ gtk_widget_draw(GTK_WIDGET(notebook), NULL);
+}
+
+void delete (GtkWidget *widget, GtkWidget *event, gpointer data)
+{
+ gtk_main_quit ();
+}
+
+int main (int argc, char *argv[])
+{
+ GtkWidget *ventana;
+ GtkWidget *boton;
+ GtkWidget *table;
+ GtkWidget *notebook;
+ GtkWidget *frame;
+ GtkWidget *etiqueta;
+ GtkWidget *checkbutton;
+ int i;
+ char bufferf[32];
+ char bufferl[32];
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ gtk_signal_connect (GTK_OBJECT (ventana), "delete_event",
+ GTK_SIGNAL_FUNC (delete), NULL);
+
+ gtk_container_border_width (GTK_CONTAINER (ventana), 10);
+
+ table = gtk_table_new(2,6,TRUE);
+ gtk_container_add (GTK_CONTAINER (ventana), table);
+
+ /* Crea un nuevo libro de notas, indicando la posición de los
+ indicadores */
+ notebook = gtk_notebook_new ();
+ gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_TOP);
+ gtk_table_attach_defaults(GTK_TABLE(table), notebook, 0,6,0,1);
+ gtk_widget_show(notebook);
+
+ /* le añadimos un montón de páginas al libro de notas */
+ for (i=0; i < 5; i++) {
+ sprintf(bufferf, "Append Frame %d", i+1);
+ sprintf(bufferl, "Page %d", i+1);
+
+ frame = gtk_frame_new (bufferf);
+ gtk_container_border_width (GTK_CONTAINER (frame), 10);
+ gtk_widget_set_usize (frame, 100, 75);
+ gtk_widget_show (frame);
+
+ etiqueta = gtk_label_new (bufferf);
+ gtk_container_add (GTK_CONTAINER (frame), etiqueta);
+ gtk_widget_show (etiqueta);
+
+ etiqueta = gtk_label_new (bufferl);
+ gtk_notebook_append_page (GTK_NOTEBOOK (notebook), frame, etiqueta);
+ }
+
+
+ /* Ahora añadimos una página en punto específico */
+ checkbutton = gtk_check_button_new_with_label ("Check me please!");
+ gtk_widget_set_usize(checkbutton, 100, 75);
+ gtk_widget_show (checkbutton);
+
+ etiqueta = gtk_label_new ("Add spot");
+ gtk_container_add (GTK_CONTAINER (checkbutton), etiqueta);
+ gtk_widget_show (etiqueta);
+ etiqueta = gtk_label_new ("Add page");
+ gtk_notebook_insert_page (GTK_NOTEBOOK (notebook), checkbutton, etiqueta, 2);
+
+ /* Y finalmente preañadimos páginas en el libro de notas */
+ for (i=0; i < 5; i++) {
+ sprintf(bufferf, "Prepend Frame %d", i+1);
+ sprintf(bufferl, "PPage %d", i+1);
+
+ frame = gtk_frame_new (bufferf);
+ gtk_container_border_width (GTK_CONTAINER (frame), 10);
+ gtk_widget_set_usize (frame, 100, 75);
+ gtk_widget_show (frame);
+
+ etiqueta = gtk_label_new (bufferf);
+ gtk_container_add (GTK_CONTAINER (frame), etiqueta);
+ gtk_widget_show (etiqueta);
+
+ etiqueta = gtk_label_new (bufferl);
+ gtk_notebook_prepend_page (GTK_NOTEBOOK(notebook), frame, etiqueta);
+ }
+
+ /* Decimos en que página empezar (página 4) */
+ gtk_notebook_set_page (GTK_NOTEBOOK(notebook), 3);
+
+
+ /* creamos un montón de botones */
+ boton = gtk_button_new_with_label ("close");
+ gtk_signal_connect_object (GTK_OBJECT (boton), "clicked",
+ GTK_SIGNAL_FUNC (delete), NULL);
+ gtk_table_attach_defaults(GTK_TABLE(table), boton, 0,1,1,2);
+ gtk_widget_show(boton);
+
+ boton = gtk_button_new_with_label ("next page");
+ gtk_signal_connect_object (GTK_OBJECT (boton), "clicked",
+ (GtkSignalFunc) gtk_notebook_next_page,
+ GTK_OBJECT (notebook));
+ gtk_table_attach_defaults(GTK_TABLE(table), boton, 1,2,1,2);
+ gtk_widget_show(boton);
+
+ boton = gtk_button_new_with_label ("prev page");
+ gtk_signal_connect_object (GTK_OBJECT (boton), "clicked",
+ (GtkSignalFunc) gtk_notebook_prev_page,
+ GTK_OBJECT (notebook));
+ gtk_table_attach_defaults(GTK_TABLE(table), boton, 2,3,1,2);
+ gtk_widget_show(boton);
+
+ boton = gtk_button_new_with_label ("tab position");
+ gtk_signal_connect_object (GTK_OBJECT (boton), "clicked",
+ (GtkSignalFunc) rotate_book, GTK_OBJECT(notebook));
+ gtk_table_attach_defaults(GTK_TABLE(table), boton, 3,4,1,2);
+ gtk_widget_show(boton);
+
+ boton = gtk_button_new_with_label ("tabs/border on/off");
+ gtk_signal_connect_object (GTK_OBJECT (boton), "clicked",
+ (GtkSignalFunc) tabsborder_book,
+ GTK_OBJECT (notebook));
+ gtk_table_attach_defaults(GTK_TABLE(table), boton, 4,5,1,2);
+ gtk_widget_show(boton);
+
+ boton = gtk_button_new_with_label ("remove page");
+ gtk_signal_connect_object (GTK_OBJECT (boton), "clicked",
+ (GtkSignalFunc) remove_book,
+ GTK_OBJECT(notebook));
+ gtk_table_attach_defaults(GTK_TABLE(table), boton, 5,6,1,2);
+ gtk_widget_show(boton);
+
+ gtk_widget_show(table);
+ gtk_widget_show(ventana);
+
+ gtk_main ();
+
+ return 0;
+}
+/* fin del ejemplo */
+</verb></tscreen>
+
+Espero que la explicación le ayude de alguna manera a crear libros de
+notas en sus aplicaciones GTK.
+
+<!-- ***************************************************************** -->
+<sect> El <em/widget/ GtkCList
+<!-- ***************************************************************** -->
+<!-- ----------------------------------------------------------------- -->
+<p>
+El <em>widget</em> GtkCList ha reemplazado al <em>widget</em> GtkList
+(que sigue estando disponible).
+
+El <em>widget</em> GtkCList es un <em>widget</em> de una lista
+multicolumna que es capaz de manejar, literalmente, miles de filas de
+información. Cada columna puede tener (opcionalmente) un título, que
+puede estar activado (opcionalmente), permitiéndonos enlazar una
+función con la selección.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Creando un <em>widget</em> GtkCList
+<p>
+Crear un GtkCList es algo bastante sencillo, una vez que sabe como
+crear un <em>widget</em> en general. Se proporcionan al menos dos
+formas estándar de crearlo, la forma fácil y la forma difícil. Pero
+antes de crear una GtkCList, hay una cosa que debemos saber: ¿Cuántas
+columnas va a tener?
+
+No todas las columnas tienen que ser visibles y pueden utilizarse para
+almacenar datos que estén relacionados con una cierta celda de la
+lista.
+
+<tscreen><verb>
+GtkWidget *gtk_clist_new ( gint columns );
+
+GtkWidget *gtk_clist_new_with_titles( gint columns,
+ gchar *titles[] );
+</verb></tscreen>
+
+Esta primera aproximación al problema es muy sencilla, pero la segunda
+requerirá alguna explicación adicional. Cada columna puede tener un
+título asociado. Si utilizamos la segunda forma, deberemos proporcionar
+punteros al texto del título, y el número de punteros debe ser igual
+al número de columnas especificadas. Por supuesto, siempre podemos
+utilizar la primera forma de creación y añadir más tarde los títulos
+de forma manual.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Modos de operación
+<p>
+Hay varios atributos que pueden utilizarse para alterar el aspecto
+de un GtkCList. Primero tenemos
+
+<tscreen><verb>
+void gtk_clist_set_selection_mode( GtkCList *clist,
+ GtkSelectionMode mode );
+</verb></tscreen>
+
+que, como el propio nombre indica, establece el modo de selección de la
+lista GtkCList. El primer argumento es el <em>widget</em> GtkCList, y el
+segundo especifica el modo de selección de la celda (están definidos
+en <tt/gtkenums.h/). En el momento de escribir esto, estaban
+disponibles los siguientes modos:
+
+<itemize>
+<item> GTK_SELECTION_SINGLE - La selección o es NULL o contiene un
+puntero GList a un elemento seleccionado.
+
+<item> GTK_SELECTION_BROWSE - La selección es NULL si la lista no
+contiene <em>widgets</em> o si los que contiene son insensibles, en
+caso contrario contendrá un puntero GList hacia una estructura GList,
+y por tanto con exactamente un elemento.
+
+<item> GTK_SELECTION_MULTIPLE - La selección es NULL si no hay
+seleccionados una lista de elementos o un puntero GList para el primer
+elemento seleccionado.<!-- FIXME: Todo esto no se si tiene sentido -->
+Éste apunta de nuevo a una estructura GList para el segundo elemento
+seleccionado y continua así. Éste es, actualmente, el modo por
+<bf>defecto</bf> para el <em>widget</em> GtkCList.
+
+<item> GTK_SELECTION_EXTENDED - La selección siempre es NULL.
+</itemize>
+
+Puede que se añadan otros modos en versiones posteriores de GTK.
+
+También tenemos
+
+<tscreen><verb>
+void gtk_clist_set_policy (GtkCList *clist,
+ GtkPolicyType vscrollbar_policy,
+ GtkPolicyType hscrollbar_policy);
+</verb></tscreen>
+
+que define que es lo que ocurre con las barras de desplazamiento. Los
+siguientes valores son los posibles para las barras de desplazamiento
+horizontal y vertical:
+
+<itemize>
+<item> GTK_POLICY_ALWAYS - La barra de desplazamiento siempre está ahí.
+
+<item> GTK_POLICY_AUTOMATIC - La barra de desplazamiento estará ahí sólo
+cuando el número de elementos en la GtkCList supere el número que puede
+mostrarse en el <em>widget</em>.
+</itemize>
+
+También podemos definir como debería ser el aspecto del borde del
+<em>widget</em> GtkCList. Esto lo podemos hacer mediante
+
+<tscreen><verb>
+void gtk_clist_set_border( GtkCList *clist,
+ GtkShadowType border );
+</verb></tscreen>
+
+Y los posibles valores para el segundo argumento son
+
+<itemize>
+<item> GTK_SHADOW_NONE
+
+<item> GTK_SHADOW_IN
+
+<item> GTK_SHADOW_OUT
+
+<item> GTK_SHADOW_ETCHED_IN
+
+<item> GTK_SHADOW_ETCHED_OUT
+</itemize>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Trabajando con los títulos
+<p>
+Cuando cree un <em>widget</em> GtkCList, también obtendrá
+automáticamente un conjunto de botones título. Vivirán en lo alto de
+una ventana CList, y pueden actuar como botones normales que responden
+cuando se pulsa sobre ellos, o bien pueden ser pasivos, en cuyo caso
+no serán nada más que un título. Hay cuatro llamadas diferentes que
+nos ayudarán a establecer el estado de los botones título.
+
+<tscreen><verb>
+void gtk_clist_column_title_active( GtkCList *clist,
+ gint column );
+
+void gtk_clist_column_title_passive( GtkCList *clist,
+ gint column );
+
+void gtk_clist_column_titles_active( GtkCList *clist );
+
+void gtk_clist_column_titles_passive( GtkCList *clist );
+</verb></tscreen>
+
+Un título activo es aquel que actua como un botón normal, y uno pasivo
+es sólo una etiqueta. Las primeras dos llamadas de arriba
+activarán/desactivarán el botón título correspondiente a la columna
+<tt/column/, mientras que las dos llamadas siguientes
+activarán/desactivarán todos los botones título que hayan en el
+<em>widget</em> <tt/clist/ que se le proporcione a la función.
+
+Pero, por supuesto, habrá casos en el que no querremos utilizar los
+botones título, así que también tenemos la posibilidad de ocultarlos y
+de volverlos a mostrar utilizando las dos llamadas siguientes:
+
+<tscreen><verb>
+void gtk_clist_column_titles_show( GtkCList *clist );
+
+void gtk_clist_column_titles_hide( GtkCList *clist );
+</verb></tscreen>
+
+Para que los títulos sean realmente útiles necesitamos un mecanismo
+que nos permita darles el valor que nosotros queramos y cambiar ese
+valor, y podremos hacerlo mediante
+
+<tscreen><verb>
+void gtk_clist_set_column_title( GtkCList *clist,
+ gint column,
+ gchar *title );
+</verb></tscreen>
+
+Debe llevar cuidado, ya que sólo se puede especificar el título de una
+columna a la vez, por lo que si conoce todos los títulos desde el
+principio, le sugiero que utilice <tt/gtk_clist_new_with_titles/ (como
+se describe arriba) para establecerlos adecuadamente. Le ahorrará
+tiempo de programación, y hará su programa más pequeño. Hay algunos
+casos donde es mejor utilizar la forma manual, y uno de ellos es
+cuando no todos los títulos son texto. GtkCList nos proporciona
+botones título que pueden, de hecho, incorporar un <em>widget</em>
+entero, por ejemplo un <em>pixmap</em>. Todo esto se hace mediante
+
+<tscreen><verb>
+void gtk_clist_set_column_widget( GtkCList *clist,
+ gint column,
+ GtkWidget *widget );
+</verb></tscreen>
+
+que no debería necesitar de explicaciones adicionales.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Manipulando la lista en sí.
+<p>
+Es posible cambiar la justificación de una columna, y esto se hace
+mediante
+
+<tscreen><verb>
+void gtk_clist_set_column_justification( GtkCList *clist,
+ gint column,
+ GtkJustification justification );
+</verb></tscreen>
+
+El tipo GtkJustification puede tomar los valores siguientes:
+
+<itemize>
+<item>GTK_JUSTIFY_LEFT - El texto en la columna empezará desde el lado
+izquierdo.
+
+<item>GTK_JUSTIFY_RIGHT - El texto en la columna empezará desde el
+lado derecho.
+
+<item>GTK_JUSTIFY_CENTER - El texto se colocará en el centro de la
+columna.
+
+<item>GTK_JUSTIFY_FILL - El texto utilizará todo el espacio disponible
+en la columna. Normalmente se hace añadiendo espacios en blanco entre
+las palabras (o entre letras por separado, si se trata de una sola
+palabra). Más o menos de la misma forma en la que lo hace un
+procesador de textos WYSIWYG.
+</itemize>
+
+La siguiente función es muy importante, y debería ser un estándar
+para inicializar todos los <em>widgets</em> GtkCList. Cuando se crea
+la lista, los anchos de las distintas columnas se eligen para que
+coincidan con sus títulos, y éste es el ancho adecuado que tenemos que
+poner, utilizando
+
+<tscreen><verb>
+void gtk_clist_set_column_width( GtkCList *clist,
+ gint column,
+ gint width );
+</verb></tscreen>
+
+Observe que el ancho viene dado en pixeles y no en letras. Lo mismo
+vale para el alto de la celda en las columnas, pero como el valor por
+defecto es la altura del tipo de letra actual, no es algo tan crítico
+para la aplicación. De todas formas, la altura se cambia mediante
+
+<tscreen><verb>
+void gtk_clist_set_row_height( GtkCList *clist,
+ gint height );
+</verb></tscreen>
+
+De nuevo, hay que advertir que el ancho viene dado en pixeles.
+
+También podemos ir hacia un elemento sin la intervención del usuario,
+sin embargo hace falta que sepamos hacia donde queremos ir. O en otras
+palabras, necesitamos la fila y la columna del elemento al que queremos
+pasar.
+
+<tscreen><verb>
+void gtk_clist_moveto( GtkCList *clist,
+ gint row,
+ gint column,
+ gfloat row_align,
+ gfloat col_align );
+</verb></tscreen>
+
+Es importante comprender bien el significado de <tt/gfloat
+row_align/. Tiene un valor entre 0.0 y 1.0, donde 0.0 significa que
+debemos hacer que la fila seleccionada aparezca en la alto de la
+lista, mientras que 1.0 significa que la fila aparecerá en la parte de
+abajo. El resto de valores entre 0.0 y 1.0 son válidos y harán que la
+fila aparezca entre la parte superior y la inferior. El último
+argumento, <tt/gfloat col_align/ funciona igual, siendo 0.0 la
+izquierda y 1.0 la derecha.
+
+Dependiendo de las necesidades de la aplicación, puede que no tengamos
+que hacer un desplazamiento hacia un elemento que ya sea visible. Por
+tanto, ¿cómo podemos saber si ya es visible? Como siempre, hay una función
+que sirve para averiguarlo
+
+<tscreen><verb>
+GtkVisibility gtk_clist_row_is_visible( GtkCList *clist,
+ gint row );
+</verb></tscreen>
+
+El valor devuelto es uno de los siguientes:
+
+<itemize>
+<item>GTK_VISIBILITY_NONE
+
+<item>GTK_VISIBILITY_PARTIAL
+
+<item>GTK_VISIBILITY_FULL
+</itemize>
+
+Como puede ver, sólo nos dice si una fila es visible. Actualmente no hay
+ninguna forma de obtener el mismo dato para una columna. Sin embargo
+podemos obtener información parcial, porque si el valor devuelto es
+GTK_VISIBILITY_PARTIAL, entonces es que alguna parte está oculta,
+pero no sabemos si es la fila que está cortada por la parte de abajo
+de la lista, o si la fila tiene columnas que están fuera.
+
+También podemos cambiar el color del primer y del segundo plano de una
+fila en particular. Esto es útil para marcar la fila seleccionada por
+el usuario, y las dos funciones que hay que utilizar son
+
+<tscreen><verb>
+void gtk_clist_set_foreground( GtkCList *clist,
+ gint row,
+ GdkColor *color );
+
+void gtk_clist_set_background( GtkCList *clist,
+ gint row,
+ GdkColor *color );
+</verb></tscreen>
+
+Cuidado, ya que los colores deben estar asignados previamente en la
+memoria.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Añadiendo filas a la lista
+<p>
+Podemos añadir filas de dos formas. Se pueden añadir al final de la lista
+utilizando
+
+<tscreen><verb>
+gint gtk_clist_append( GtkCList *clist,
+ gchar *text[] );
+</verb></tscreen>
+
+o podemos insertar una fila en un lugar determinado utilizando
+
+<tscreen><verb>
+void gtk_clist_insert( GtkCList *clist,
+ gint row,
+ gchar *text[] );
+</verb></tscreen>
+
+En ambas llamadas podemos proporcionar un conjunto de punteros que
+serán los textos que queremos poner en las columnas. El número de
+punteros debe ser igual al número de columnas en la lista. Si el
+argumento <tt/text[]/ es NULL, entonces no habrá texto en las columnas
+de la fila. Esto sería útil, por ejemplo, si queremos añadir
+<em>pixmaps</em> en lugar de texto (en general para cualquier cosa que
+haya que hacer manualmente).
+
+De nuevo, cuidado ya que el número de filas y de columnas comienza en
+0.
+
+Para eliminar una fila individual podemos utilizar
+
+<tscreen><verb>
+void gtk_clist_remove( GtkCList *clist,
+ gint row );
+</verb></tscreen>
+
+Hay también una llamada que elimina todas las filas en la lista.
+Es mucho más rápido que llamar a <tt/gtk_clist_remove/ una vez por
+cada fila, que sería la única alternativa.
+
+<tscreen><verb>
+void gtk_clist_clear( GtkCList *clist );
+</verb></tscreen>
+
+También hay dos funciones que es conveniente utilizarlas cuando hay
+que hacerle muchos cambios a una lista. Son para evitar que la lista
+parpadee mientras es actualizada repetidamente, que puede ser muy
+molesto para el usuario. Por tanto es una buena idea congelar la
+lista, hacer los cambios, y descongelarla, que hará que la lista se
+actualice en la pantalla.
+
+<tscreen><verb>
+void gtk_clist_freeze( GtkCList * clist );
+
+void gtk_clist_thaw( GtkCList * clist );
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Poniendo texto y <em>pixmaps</em> en las celdas
+<p>
+Una celda puede contener un <em>pixmap</em>, texto o ambos. Para ponerlos
+en las celdas, podemos utilizar las siguientes funciones.
+
+<tscreen><verb>
+void gtk_clist_set_text( GtkCList *clist,
+ gint row,
+ gint column,
+ gchar *text );
+
+void gtk_clist_set_pixmap( GtkCList *clist,
+ gint row,
+ gint column,
+ GdkPixmap *pixmap,
+ GdkBitmap *mask );
+
+void gtk_clist_set_pixtext( GtkCList *clist,
+ gint row,
+ gint column,
+ gchar *text,
+ guint8 spacing,
+ GdkPixmap *pixmap,
+ GdkBitmap *mask );
+</verb></tscreen>
+
+Son bastante sencillas de entender. Todas las llamadas tienen la
+GtkCList como primer argumento, seguidas por la fila y la columna
+de la celda, y seguidas por el dato que debe ponerse en la celda. El
+argumento <tt/gint8 spacing/ en <tt/gtk_clist_set_pixtext/ es el
+número de <em>pixels</em> entre el <em>pixmap</em> y el principio del
+texto.
+
+Para leer los datos que hay en una celda, podemos utilizar
+
+<tscreen><verb>
+gint gtk_clist_get_text( GtkCList *clist,
+ gint row,
+ gint column,
+ gchar **text );
+
+gint gtk_clist_get_pixmap( GtkCList *clist,
+ gint row,
+ gint column,
+ GdkPixmap **pixmap,
+ GdkBitmap **mask );
+
+gint gtk_clist_get_pixtext( GtkCList *clist,
+ gint row,
+ gint column,
+ gchar **text,
+ guint8 *spacing,
+ GdkPixmap **pixmap,
+ GdkBitmap **mask );
+</verb></tscreen>
+
+No es necesario leer todos los datos en caso de que no estemos
+interesados. Cualquiera de los punteros que se supone contendrán los
+valores a devolver (cualquiera excepto el <tt/clist/) pueden ser
+NULL. Por lo que si sólo queremos leer el texto de una celda que es de
+tipo <tt/pixtext/, deberíamos hacer lo siguiente, suponiendo que
+<tt/clist/, <tt/row/ y <tt/column/ ya existan:
+
+<tscreen><verb>
+gchar *mytext;
+
+gtk_clist_get_pixtext(clist, row, column, &amp;mytext, NULL, NULL, NULL);
+</verb></tscreen>
+
+Hay una rutina más que está relacionada con lo que está dentro
+de una celda de una <tt/clist/, y es
+
+<tscreen><verb>
+GtkCellType gtk_clist_get_cell_type( GtkCList *clist,
+ gint row,
+ gint column );
+</verb></tscreen>
+
+que devuelve el tipo de datos que hay en la celda. El valor devuelto es
+uno de los siguientes
+
+<itemize>
+<item>GTK_CELL_EMPTY
+
+<item>GTK_CELL_TEXT
+
+<item>GTK_CELL_PIXMAP
+
+<item>GTK_CELL_PIXTEXT
+
+<item>GTK_CELL_WIDGET
+</itemize>
+
+También hay una función que nos permite especificar la indentación de
+un celda (horizontal o vertical). El valor de la indentación es del
+tipo <tt/gint/, viene dado en <em>pixeles</em>, y puede ser positivo o
+negativo.
+
+<tscreen><verb>
+void gtk_clist_set_shift( GtkCList *clist,
+ gint row,
+ gint column,
+ gint vertical,
+ gint horizontal );
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Almacenando punteros a datos
+<p>
+Con una GtkCList es posible poner un puntero a datos en una
+fila. Este puntero no será visible al usuario, pero puede serle útil
+al programador.
+
+De nuevo, las funciones son lo suficientemente autoexplicativas
+
+<tscreen><verb>
+void gtk_clist_set_row_data( GtkCList *clist,
+ gint row,
+ gpointer data );
+
+void gtk_clist_set_row_data_full( GtkCList *clist,
+ gint row,
+ gpointer data,
+ GtkDestroyNotify destroy );
+
+gpointer gtk_clist_get_row_data( GtkCList *clist,
+ gint row );
+
+gint gtk_clist_find_row_from_data( GtkCList *clist,
+ gpointer data );
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Trabajando con la selección
+<p>
+También hay funciones que nos permiten forzar la (de)selección de una
+fila. Son
+
+<tscreen><verb>
+void gtk_clist_select_row( GtkCList *clist,
+ gint row,
+ gint column );
+
+void gtk_clist_unselect_row( GtkCList *clist,
+ gint row,
+ gint column );
+</verb></tscreen>
+
+Y también una función que tomará las coordenadas x e y (por ejemplo,
+recibidas del ratón), mirará en la lista y devolverá la fila y la
+columna que les corresponden.
+
+<tscreen><verb>
+gint gtk_clist_get_selection_info( GtkCList *clist,
+ gint x,
+ gint y,
+ gint *row,
+ gint *column );
+</verb></tscreen>
+
+Cuando detectemos algo interesante, como por ejemplo el movimiento del
+ratón, o una pulsación en cualquier lugar de la lista, podemos leer
+las coordenadas del ratón y encontrar en que elemento de la lista se
+encuentra. ¿Engorroso? Afortunadamente, hay una forma más sencilla de
+hacer las cosas...
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Las señales que lo hacen todo
+<p>
+Como con el resto de <em>widgets</em>, hay unas cuantas señales que
+podemos utilizar. El <em>widget</em> GtkCList está derivado del
+<em>widget</em> GtkContainer, y por tanto tiene las mismas
+señales que éste, pero además añade las siguientes:
+
+<itemize>
+<item><tt/select_row/ - Esta señal enviará la siguiente información,
+en este orden: GtkCList *clist, gint row, gint column, GtkEventButton
+*event
+
+<item><tt/unselect_row/ - Cuando el usuario deselecciona una fila, se
+activará esta señal. Envia la misma información que <tt/select_row/
+
+<item><tt/click_column/ - Envia GtkCList *clist, gint column
+</itemize>
+
+Por tanto si queremos conectar una llamada a <tt/select_row/, la
+llamada se deberá declarar como
+
+<tscreen><verb>
+void select_row_callback(GtkWidget *widget,
+ gint row,
+ gint column,
+ GdkEventButton *event,
+ gpointer data);
+</verb></tscreen>
+
+La llamada se conectará, como siempre, con
+
+<tscreen><verb>
+gtk_signal_connect(GTK_OBJECT( clist),
+ "select_row"
+ GTK_SIGNAL_FUNC(select_row_callback),
+ NULL);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Un ejemplo GtkCList
+<p>
+
+<tscreen><verb>
+/* principio del ejemplo clist clist.c */
+
+#include <gtk/gtk.h>
+#include <glib.h>
+
+/* Aquí tenemos algunos prototipos de las funciones de llamada */
+void button_add_clicked( GtkWidget *boton, gpointer data);
+void button_clear_clicked( GtkWidget *boton, gpointer data);
+void button_hide_show_clicked( GtkWidget *boton, gpointer data);
+void selection_made( GtkWidget *clist, gint row, gint column,
+ GdkEventButton *event, gpointer data);
+
+gint main (int argc, gchar *argv[])
+{
+ GtkWidget *ventana;
+ GtkWidget *vbox, *hbox;
+ GtkWidget *clist;
+ GtkWidget *button_add, *button_clear, *button_hide_show;
+ gchar *titles[2] = {"Ingredients","Amount"};
+
+ gtk_init(&amp;argc, &amp;argv);
+
+
+ ventana=gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_widget_set_usize(GTK_WIDGET(ventana), 300, 150);
+
+ gtk_window_set_title(GTK_WINDOW(ventana), "GtkCList Example");
+ gtk_signal_connect(GTK_OBJECT(ventana),
+ "destroy",
+ GTK_SIGNAL_FUNC(gtk_main_quit),
+ NULL);
+
+ vbox=gtk_vbox_new(FALSE, 5);
+ gtk_container_border_width(GTK_CONTAINER(vbox), 5);
+ gtk_container_add(GTK_CONTAINER(ventana), vbox);
+ gtk_widget_show(vbox);
+
+ /* Crear el GtkCList. Para este ejemplo utilizaremos 2 columnas */
+ clist = gtk_clist_new_with_titles( 2, titles);
+
+ /* Cuando se hace una selección, queremos saber algo acerca de
+ * ella. La función de llamada utilizada es selection_made, y su
+ * código lo podemos encontrar más abajo */
+ gtk_signal_connect(GTK_OBJECT(clist), "select_row",
+ GTK_SIGNAL_FUNC(selection_made),
+ NULL);
+
+ /* No es necesario ponerle sombra al borde, pero es bonito :) */
+ gtk_clist_set_border(GTK_CLIST(clist), GTK_SHADOW_OUT);
+
+ /* Lo que sí que es importante, es poner el ancho de las columnas
+ * ya no tendrán el valor correcto en caso contrario. Recuerde que
+ * las columnas se numeran desde el 0 en adelante (hasta el 1 en
+ * este caso).
+ */
+ gtk_clist_set_column_width (GTK_CLIST(clist), 0, 150);
+
+ /* Scollbars _only when needed_ */
+ gtk_clist_set_policy(GTK_CLIST(clist), GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+
+ /* Añade el widget GtkCList a la caja vertical y lo muestra. */
+ gtk_box_pack_start(GTK_BOX(vbox), clist, TRUE, TRUE, 0);
+ gtk_widget_show(clist);
+
+ /* Crea los botones y los añade a la ventana. Ver la parte del
+ * tutorial sobre botones para ver más ejemplos y comentarios
+ * acerca de todo esto.
+ */
+ hbox = gtk_hbox_new(FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
+ gtk_widget_show(hbox);
+
+ button_add = gtk_button_new_with_label("Add List");
+ button_clear = gtk_button_new_with_label("Clear List");
+ button_hide_show = gtk_button_new_with_label("Hide/Show titles");
+
+ gtk_box_pack_start(GTK_BOX(hbox), button_add, TRUE, TRUE, 0);
+ gtk_box_pack_start(GTK_BOX(hbox), button_clear, TRUE, TRUE, 0);
+ gtk_box_pack_start(GTK_BOX(hbox), button_hide_show, TRUE, TRUE, 0);
+
+ /* Conectar nuestras funciones de llamada a los tres botones */
+ gtk_signal_connect_object(GTK_OBJECT(button_add), "clicked",
+ GTK_SIGNAL_FUNC(button_add_clicked),
+ (gpointer) clist);
+ gtk_signal_connect_object(GTK_OBJECT(button_clear), "clicked",
+ GTK_SIGNAL_FUNC(button_clear_clicked),
+ (gpointer) clist);
+ gtk_signal_connect_object(GTK_OBJECT(button_hide_show), "clicked",
+ GTK_SIGNAL_FUNC(button_hide_show_clicked),
+ (gpointer) clist);
+
+ gtk_widget_show(button_add);
+ gtk_widget_show(button_clear);
+ gtk_widget_show(button_hide_show);
+
+ /* Ahora hemos terminado el interface y sólo nos queda mostrar la
+ * ventana y entrar en el bucle gtk_main.
+ */
+ gtk_widget_show(ventana);
+ gtk_main();
+
+ return 0;
+}
+
+/* El usuario pulsó el botón "Add List". */
+void button_add_clicked( GtkWidget *boton, gpointer data)
+{
+ int indx;
+
+ /* Algo tonto que añadir a la lista. 4 filas con 2 columnas cada
+ * una
+ */
+ gchar *drink[4][2] = {{"Milk", "3 Oz"},
+ {"Water", "6 l"},
+ {"Carrots", "2"},
+ {"Snakes", "55"}};
+
+ /* Aquí hacemos la adición del texto. Se hace una vez por cada
+ * fila.
+ */
+ for( indx=0; indx < 4; indx++)
+ gtk_clist_append( (GtkCList*) data, drink[indx]);
+
+ return;
+}
+
+/* El usuario pulsó el botón "Clear List" */
+void button_clear_clicked( GtkWidget *boton, gpointer data)
+{
+ /* Borrar la lista utilizando gtk_clist_clear. Esto es mucho más
+ * rápido que llamar a gtk_clist_remove una vez por cada fila.
+ */
+ gtk_clist_clear((GtkCList*) data);
+
+ return;
+}
+
+/* El usuario pulsó el botón "Hide/Show titles". */
+void button_hide_show_clicked( GtkWidget *boton, gpointer data)
+{
+ /* Una bandera para recordar el estado. 0 = actualmente visible */
+ static short int flag = 0;
+
+ if (flag == 0)
+ {
+ /* Oculta los títulos y pone la bandera a 1 */
+ gtk_clist_column_titles_hide((GtkCList*) data);
+ flag++;
+ }
+ else
+ {
+ /* Muestra los títulos y pone la bandera a 0 */
+ gtk_clist_column_titles_show((GtkCList*) data);
+ flag--;
+ }
+
+ return;
+}
+
+/* Se llegamos aquí, entonces el usuario ha seleccionado una fila de
+ * la lista.
+ */
+void selection_made( GtkWidget *clist, gint row, gint column,
+ GdkEventButton *event, gpointer data)
+{
+ gchar *text;
+
+ /* Obtiene el texto que se ha almacenado en la fila y columna
+ * sobre las que se ha pulsado. Lo recibiremos como un puntero en
+ * el argumento text.
+ */
+ gtk_clist_get_text(GTK_CLIST(clist), row, column, &amp;text);
+
+ /* Imprime alguna información sobre la fila seleccionada */
+ g_print("You selected row %d. More specifically you clicked in column %d, and the text in this cell is %s\n\n", row, column, text);
+
+ return;
+}
+/* final del ejemplo */
+</verb></tscreen>
+
+<!-- ***************************************************************** -->
+<sect> El <em>widget</em> árbol<label id="sec_Tree_Widgets">
+<!-- ***************************************************************** -->
+<p>
+
+El propósito del <em>widget</em> GtkTree es mostrar datos organizados
+de forma jerárquica. El <em>widget</em> GtkTree en sí es un contenedor
+vertical para los <em>widgets</em> 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 <em>widgets</em> GtkTree que en los GtkList. La
+diferencia es que los <em>widgets</em> GtkTree pueden anidarse
+dentro de otros <em>widgets</em> GtkTree. Vamos a verlo de forma
+resumida.
+
+El <em>widget</em> 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.
+
+<sect1> Creando un árbol
+<p>
+Puede crear un GtkTree de la forma usual, utilizando:
+
+<tscreen><verb>
+GtkWidget* gtk_tree_new( void );
+</verb></tscreen>
+
+Como el <em>widget</em> 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 <tt/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. <ref id="sec_Tree_Item_Widget" name="El
+widget elemento de árbol"> más adelante explica todos los
+detalles de GtkTreeItem. Por ahora, es suficiente con saber como crear
+uno, utilizando:
+
+<tscreen><verb>
+GtkWidget* gtk_tree_item_new_with_label( gchar *etiqueta );
+</verb></tscreen>
+
+Puede añadirlo al árbol utilizando una de las siguientes funciones
+(ver <ref id="sec_GtkTree_Functions" name="Funciones y macros">
+más adelante para leer más opciones):
+
+<tscreen><verb>
+void gtk_tree_append( GtkTree *arbol,
+ GtkWidget *elemento_arbol );
+
+void gtk_tree_prepend( GtkTree *arbol,
+ GtkWidget *elemento_arbol );
+</verb></tscreen>
+
+Observe que debe añadir elementos a un GtkTree de uno en uno - no
+hay un equivalente a <tt/gtk_list_*_items()/.
+
+<sect1> Añadiendo un Subárbol
+<p>
+Un subárbol se crea como cualquier otro <em>widget</em> GtkTree. Un
+subárbol se añade a otro árbol bajo un elemento del mismo, utilizando:
+
+<tscreen><verb>
+void gtk_tree_item_set_subtree( GtkTreeItem *elemento_arbol,
+ GtkWidget *subarbol );
+</verb></tscreen>
+
+No necesita llamar a <tt/gtk_widget_show()/ en un subárbol ni antes ni
+después de añadirlo a GtkTreeItem. Sin embargo, <em>deberá</em> haber
+añadido el GtkTreeItem en cuestión a un árbol padre antes de llamar a
+<em/gtk_tree_item_set_subtree()/. Esto se debe a que, técnicamente,
+el padre del subárbol <em>no</em> 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.
+
+<sect1> Manejando la lista de selección
+<p>
+Como con GtkList, GtkTree tiene un campo <tt>selection</tt>, y
+es posible controlar el comportamiento del árbol (de alguna manera)
+estableciendo el tipo de selección, utilizando:
+
+<tscreen><verb>
+void gtk_tree_set_selection_mode( GtkTree *arbol,
+ GtkSelectionMode mode );
+</verb></tscreen>
+
+La semántica asociada con los distintos modos de selección está
+descrita en la sección del <em>widget</em> GtkList. Como ocurría con
+el <em>widget</em> GtkList, se enviarán las señales <tt/select_child/,
+<tt/unselect_child/ (realmente no - ver <ref id="sec_GtkTree_Signals"
+name="Señales"> más adelante para una explicación), y
+<tt/selection_changed/ cuando los elementos de la lista sean
+seleccionados o deseleccionados. Sin embargo, para aprovechar estas
+señales, necesita conocer por medio <em>de que</em> <em>widget</em>
+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 <em>widgets</em> GtkTree
+son creados iguales, algunos son más iguales que otros. Todos los
+<em>widgets</em> 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 <em>widget</em>
+GtkTree superior de la jerarquia, conocido como el «árbol raíz».
+
+Por tanto no es una buena idea acceder al campo <tt>selection</tt>
+directamente en un <em>widget</em> GtkTree arbitrario, a menos que
+<em>sepa</em> que es el árbol raíz. En vez de eso, utilice la
+macro GTK_TREE_SELECTION (arbol), que da la lista selección del árbol
+raíz como un puntero <tt/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 <tt/select_child/ (y tt/unselect_child/, en
+teoría) son emitidas por todos los árboles, pero la señal
+<em/selection_changed/ es emitida sólo por el árbol raíz. En
+consecuencia, si quiere manipular la señal <tt/select_child/ de un
+árbol y todos sus subárboles, tendrá que llamar a
+<tt/gtk_signal_connect()/ una vez por cada subárbol.
+
+<sect1> Estructura interna del <em>widget</em> árbol
+<p>
+La definición de la estructura GtkTree es ls siguiente:
+
+<tscreen><verb>
+struct _GtkTree
+{
+ GtkContainer container;
+
+ GList *child;
+
+ 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;
+};
+</verb></tscreen>
+
+Ya se han mencionado los peligros asociados con el acceso directo al
+campo <tt>selection</tt>. Se puede acceder a los otros campos
+importantes de la estructura mediante macros manipuladoras o
+funciones de clase. GTK_TREE_IS_ROOT_TREE (arbol) devuelve un valor
+booleano que indica si un árbol es árbol raíz de una jerarquia
+GtkTree, mientras que GTK_TREE_ROOT_TREE (arbol) devuelve el árbol
+raíz, un objeto de tipo GtkTree (recuerde transformarlo utilizando
+GTK_WIDGET (arbol) si quiere utilizar con él alguna de la funciones
+<tt/gtk_widget_*()/).
+
+En lugar de acceder directamente al campo hijo de un <em>widget</em>
+GtkTree, probablemente sea mejor transformarlo utilizando
+GTK_CONTAINER (arbol), y pasárselo a la función
+<tt/gtk_container_children()/. Con esto crearemos un duplicado de la
+lista original, por lo que deberá eliminarlo de la memoria utilizando
+<tt/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í:
+
+<tscreen><verb>
+hijo = gtk_container_children (GTK_CONTAINER (arbol));
+while (hijo) {
+ do_something_nice (GTK_TREE_ITEM (hijo->data));
+ hijo = g_list_remove_link (hijo, hijo);
+}
+</verb></tscreen>
+
+El campo <tt>tree_owner</tt> sólo está definido en subárboles, donde
+apunta al <em>widget</em> GtkTreeItem que contiene al árbol en
+cuestión. El campo <tt>level</tt> 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 <em>widget</em> GtkTree se dibuje en la pantalla.
+
+<sect2> Señales<label id="sec_GtkTree_Signals">
+<p>
+<tscreen><verb>
+void selection_changed( GtkTree *arbol );
+</verb></tscreen>
+
+Esta señal se emitirá cuando cambie el campo <tt>selection</tt> de
+un GtkTree. Esto ocurre cuando se selecciona o deselecciona un hijo del
+GtkTree.
+
+<tscreen><verb>
+void select_child( GtkTree *arbol,
+ GtkWidget *hijo );
+</verb></tscreen>
+
+Esta señal se emite cuando se está seleccionando un hijo del GtkTree.
+Esto ocurre en las llamadas a <tt/gtk_tree_select_item()/,
+<tt/gtk_tree_select_child()/, en <em>todas</em> las pulsaciones de
+botón y llamadas a <tt/gtk_tree_item_toggle()/ y
+<tt/gtk_item_toggle()/. Puede que a veces se invoque indirectamente en
+otras ocasiones, cuando el hijo se añada o elimine del GtkTree.
+
+<tscreen><verb>
+void unselect_child (GtkTree *arbol,
+ GtkWidget *hijo);
+</verb></tscreen>
+
+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
+<tt/gtk_tree_unselect_item()/ o a <tt/gtk_tree_unselect_child()/, y quizás
+en otras ocasiones, pero <em>no</em> cuando la pulsación de un botón
+deselecciona un hijo, y tampoco por la emisión de la señal «toggle»
+por <tt/gtk_item_toggle()/.
+
+<sect2> Funciones y macros<label id="sec_GtkTree_Functions">
+<p>
+<tscreen><verb>
+guint gtk_tree_get_type( void );
+</verb></tscreen>
+
+Devuelve el identificador de tipo de `GtkTree'.
+
+<tscreen><verb>
+GtkWidget* gtk_tree_new( void );
+</verb></tscreen>
+
+Crea un nuevo objeto GtkTree. El nuevo <em>widget</em> se devuelve como
+un puntero a un objeto GtkWidget. Se devolverá NULL si se produce algún
+error.
+
+<tscreen><verb>
+void gtk_tree_append( GtkTree *arbol,
+ GtkWidget *elemento_arbol );
+</verb></tscreen>
+
+Añade un árbol a un GtkTree.
+
+<tscreen><verb>
+void gtk_tree_prepend( GtkTree *arbol,
+ GtkWidget *elemento_arbol );
+</verb></tscreen>
+
+Preañade un árbol a un GtkTree.
+
+<tscreen><verb>
+void gtk_tree_insert( GtkTree *arbol,
+ GtkWidget *elemento_arbol,
+ gint posicion );
+</verb></tscreen>
+
+Inserta un árbol en un GtkTree en la posición de la lista especificada
+por <tt>posicion.</tt>
+
+<tscreen><verb>
+void gtk_tree_remove_items( GtkTree *arbol,
+ GList *items );
+</verb></tscreen>
+
+Elimina una lista de elementos (en forma de una <tt/GList */) de un
+GtkTree. Eliminar un elemento de un árbol lo dereferencia (y por tanto
+normalmente) lo destruye (""), a él <em>y</em> a su subárbol, de
+haberlo, <em>y</em> a todos los subárboles que contenga ese
+subárbol. Si quiere eliminar sólo un elemento, deberá utilizar
+<tt/gtk_container_remove()/.
+
+<tscreen><verb>
+void gtk_tree_clear_items( GtkTree *arbol,
+ gint start,
+ gint end );
+</verb></tscreen>
+
+Elimina los elementos de un GtkTree desde la posición <tt>start</tt>
+hasta la posición <tt>end</tt>. De nuevo hay que llevarse cuidado
+con donde se aplica la dereferencia, ya que <tt/gtk_tree_clear_items()/
+simplemente construye una lista y se la pasa a
+<tt/gtk_tree_remove_items()/.
+
+<tscreen><verb>
+void gtk_tree_select_item( GtkTree *arbol,
+ gint item );
+</verb></tscreen>
+
+Emite la señal <tt/select_item/ para el hijo que se encuentra en la
+posición <tt>item</tt>, y por tanto selecciona a ese hijo (a menos que
+lo deseleccione en un manejador de señal...)
+
+<tscreen><verb>
+void gtk_tree_unselect_item( GtkTree *arbol,
+ gint item );
+</verb></tscreen>
+
+Emite la señal <tt/unselect_item/ para el hijo en la posición
+<tt>item</tt>, y por tanto deselecciona al hijo.
+
+<tscreen><verb>
+void gtk_tree_select_child( GtkTree *arbol,
+ GtkWidget *elemento_arbol );
+</verb></tscreen>
+
+Emite la señal <tt/select_item/ para el hijo <tt>elemento_arbol</tt>, y por tanto
+lo selecciona.
+
+<tscreen><verb>
+void gtk_tree_unselect_child( GtkTree *arbol,
+ GtkWidget *elemento_arbol );
+</verb></tscreen>
+
+Emite la señal <tt/unselect_item/ para el hijo <tt>elemento_arbol</tt>, y por
+tanto lo deselecciona.
+
+<tscreen><verb>
+gint gtk_tree_child_position( GtkTree *arbol,
+ GtkWidget *hijo );
+</verb></tscreen>
+
+Devuelve la posición en el árbol de <tt>child</tt>, a menos que
+<tt>child</tt> no esté en el árbol, en cuya caso devuelve -1.
+
+<tscreen><verb>
+void gtk_tree_set_selection_mode( GtkTree *arbol,
+ GtkSelectionMode mode );
+</verb></tscreen>
+
+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.
+
+<tscreen><verb>
+void gtk_tree_set_view_mode( GtkTree *arbol,
+ GtkTreeViewMode mode );
+</verb></tscreen>
+
+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
+<em>widget</em> GtkTreeItem completo, mientras que si es
+GTK_TREE_VIEW_ITEM, sólo se resaltará el <em>widget</em> hijo (es
+decir, lo que normalmente es la etiqueta).
+
+<tscreen><verb>
+void gtk_tree_set_view_lines( GtkTree *arbol,
+ guint flag );
+</verb></tscreen>
+
+Controla si se dibujarán las líneas de conexión entre los elementos
+del árbol. <tt>flag</tt> es o TRUE, en cuyo caso se dibujarán, o
+FALSE, en cuyo caso no se dibujarán.
+
+<tscreen><verb>
+GtkTree *GTK_TREE (gpointer obj);
+</verb></tscreen>
+
+Convierte un puntero genérico a `GtkTree *'.
+
+<tscreen><verb>
+GtkTreeClass *GTK_TREE_CLASS (gpointer class);
+</verb></tscreen>
+
+Convierte un puntero genérico a `GtkTreeClass *'.
+
+<tscreen><verb>
+gint GTK_IS_TREE (gpointer obj);
+</verb></tscreen>
+
+Determina si un puntero genérico se refiere a un objeto `GtkTree'.
+
+<tscreen><verb>
+gint GTK_IS_ROOT_TREE (gpointer obj)
+</verb></tscreen>
+
+Determina si un puntero genérico se refiere a un objeto `GtkTree'
+<em>y</em> 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.
+
+<tscreen><verb>
+GtkTree *GTK_TREE_ROOT_TREE (gpointer obj)
+</verb></tscreen>
+
+Devuelve el árbol raíz de un puntero a un objeto `GtkTree'. Seguimos
+con el mismo problema que en el caso anterior.
+
+<tscreen><verb>
+GList *GTK_TREE_SELECTION(gpointer obj)
+</verb></tscreen>
+
+Devuelve la lista de selección del árbol raíz de un objeto
+`GtkTree'. Seguimos con el mismo problema que antes.
+
+<sect1> El <em>widget</em> elemento de árbol<label id="sec_Tree_Item_Widget">
+<p>
+El <em>widget</em> 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
+<em>widget</em> hijo, que puede ser de cualquier tipo. El <em>widget</em>
+GtkTreeItem tiene ciertos campos extra, pero el único que nos
+interesa ahora es el campo <em>subárbol</em>.
+
+La definición de la estructura GtkTreeItem es así:
+
+<tscreen><verb>
+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;
+};
+</verb></tscreen>
+
+El campo <tt>pixmaps_box</tt> es un GtkEventBox que caza las pulsaciones
+en el símbolo más/menos que controla la expansión y contracción. El
+campo <tt>pixmaps</tt> 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
+<em>realmente</em> sepa que es lo que está haciendo.
+
+Ya que está derivado directamente de un GtkItem, puede tratarse como
+tal utilizando la macro GTK_ITEM (ElementoArbol). 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():
+
+<tscreen><verb>
+elemento_arbol = gtk_tree_item_new ();
+etiqueta_widget = gtk_label_new (etiqueta);
+gtk_misc_set_alignment (GTK_MISC (etiqueta_widget), 0.0, 0.5);
+
+gtk_container_add (GTK_CONTAINER (elemento_arbol), etiqueta_widget);
+gtk_widget_show (etiqueta_widget);
+</verb></tscreen>
+
+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í:
+
+<tscreen><verb>
+gtk_widget_ref (arbol);
+propietario = GTK_TREE(arbol)->tree_owner;
+gtk_container_remove (GTK_CONTAINER(arbol), item);
+if (arbol->parent == NULL){
+ gtk_tree_item_expand (GTK_TREE_ITEM(propietario));
+ gtk_tree_item_set_subtree (GTK_TREE_ITEM(propietario), arbol);
+}
+else
+ gtk_widget_unref (arbol);
+</verb></tscreen>
+
+Finalmente, hay que mencionar que la opción de drag-n-drop (arrastar y
+soltar) <em>funciona</em> 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 <em>widget</em> padre tiene a su vez
+un padre, y así hasta llegar al nivel más alto o ventana de diálogo,
+cuando llamamos a <tt/gtk_widget_dnd_drag_set()/ o
+<tt/gtk_widget_dnd_drop_set()/. En caso contrario, podrían ocurrir
+cosas extrañas.
+
+<sect2> Señales
+<p>
+GtkTreeItem hereda las señales <tt/select/, <tt/deselect/, y
+<tt/toggle/ de GtkItem. Además, añade dos señales propias, <tt/expand/
+y <tt/collapse/.
+
+<tscreen><verb>
+void select( GtkItem *elemento_arbol );
+</verb></tscreen>
+
+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 <tt/gtk_tree_item_select()/,
+<tt/gtk_item_select()/, o a <tt/gtk_tree_select_child()/.
+
+<tscreen><verb>
+void deselect( GtkItem *elemento_arbol );
+</verb></tscreen>
+
+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 <tt/gtk_tree_item_deselect()/ o a
+<tt/gtk_item_deselect()/. En el caso de GtkTreeItems, también se
+emitirá por <tt/gtk_tree_unselect_child()/, y a veces por
+<tt/gtk_tree_select_child()/.
+
+<tscreen><verb>
+void toggle( GtkItem *elemento_arbol );
+</verb></tscreen>
+
+Esta señal se emite cuando el programa llama a <tt/gtk_item_toggle()/. El
+efecto que tiene cuando se emite en un GtkTreeItem es llamar a
+<tt/gtk_tree_select_child()/ (y nunca a
+<tt/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.
+
+<tscreen><verb>
+void expand( GtkTreeItem *elemento_arbol );
+</verb></tscreen>
+
+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
+<tt/gtk_tree_item_expand()/.
+
+<tscreen><verb>
+void collapse( GtkTreeItem *elemento_arbol );
+</verb></tscreen>
+
+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
+<tt/gtk_tree_item_collapse()/.
+
+<sect2> Funciones y Macros
+<p>
+<tscreen><verb>
+guint gtk_tree_item_get_type( void );
+</verb></tscreen>
+
+Devuelve el identificador de tipo de `GtkTreeItem'.
+
+<tscreen><verb>
+GtkWidget* gtk_tree_item_new( void );
+</verb></tscreen>
+
+Crea un nuevo objeto GtkTreeItem. El nuevo <em>widget</em> se devuelve
+como un puntero a un objeto GtkWidget. Se devolverá NULL si hay algún
+fallo.
+
+<tscreen><verb>
+GtkWidget* gtk_tree_item_new_with_label (gchar *etiqueta);
+</verb></tscreen>
+
+Crea un nuevo objeto GtkTreeItem, teniendo una simple GtkLabel
+como único hijo. El nuevo <em>widget</em> se devolverá como
+un puntero a un objeto GtkWidget. Se devolverá NULL en caso
+de haber algún fallo.
+
+<tscreen><verb>
+void gtk_tree_item_select( GtkTreeItem *elemento_arbol );
+</verb></tscreen>
+
+Esta función es básicamente un recubrimiento de una llamada a
+gtk_item_select (GTK_ITEM (elemento_arbol)) que emitirá la
+señal select.
+
+<tscreen><verb>
+void gtk_tree_item_deselect( GtkTreeItem *elemento_arbol );
+</verb></tscreen>
+
+Esta función es básicamente un recubrimiento de una llamada a
+gtk_item_deselect (GTK_ITEM (elemento_arbol)) que emitirá la
+señal deselect.
+
+<tscreen><verb>
+void gtk_tree_item_set_subtree( GtkTreeItem *elemento_arbol,
+ GtkWidget *subarbol );
+</verb></tscreen>
+
+Esta función añade <tt/subarbol/ a <tt/elemento_arbol/, mostrándolo si
+<tt/elemento_arbol/ está expandido, u ocultándolo si <tt/elemento_arbol/ está
+contraído. De nuevo, recuerde que el <tt/elemento_arbol/ ya debe de haber
+sido añadido a un árbol para que esto funcione.
+
+<tscreen><verb>
+void gtk_tree_item_remove_subtree( GtkTreeItem *elemento_arbol );
+</verb></tscreen>
+
+Esto elimina todos los hijos de los subárboles del <tt/elemento_arbol/
+(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.
+
+<tscreen><verb>
+void gtk_tree_item_expand( GtkTreeItem *elemento_arbol );
+</verb></tscreen>
+
+Esto emite la señal «expand» para el <tt/elemento_arbol/, que lo
+expande.
+
+<tscreen><verb>
+void gtk_tree_item_collapse( GtkTreeItem *elemento_arbol );
+</verb></tscreen>
+
+Esto emite la señal «collapse» en el <tt/elemento_arbol/, que lo
+contrae.
+
+<tscreen><verb>
+GtkTreeItem *GTK_TREE_ITEM (gpointer obj)
+</verb></tscreen>
+
+Convierte un puntero genérico en un `GtkTreeItem *'.
+
+<tscreen><verb>
+GtkTreeItemClass *GTK_TREE_ITEM_CLASS (gpointer obj)
+</verb></tscreen>
+
+Convierte un puntero genérico en un `GtkTreeItemClass'.
+
+<tscreen><verb>
+gint GTK_IS_TREE_ITEM (gpointer obj)
+</verb></tscreen>
+
+Determina si un puntero genérico se refiere a un objeto `GtkTreeItem'.
+
+<tscreen><verb>
+GtkWidget GTK_TREE_ITEM_SUBTREE (gpointer obj)
+</verb></tscreen>
+
+Devuelve un subárbol del elemento (<tt/obj/ debería apuntar a un
+objeto `GtkTreeItem').
+
+<sect1> Árbol ejemplo
+<p>
+Este ejemplo es muy parecido al árbol ejemplo que hay en
+<tt/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.
+
+<!-- Hay un comentario en el código que no se traducir -->
+<tscreen><verb>
+/* 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 *etiqueta;
+
+ /* Es un GtkBin, por lo que tiene un hijo, que sabemos que es una
+ * etiqueta, por lo que la cogemos */
+ etiqueta = GTK_LABEL (GTK_BIN (item)->child);
+ /* Conseguimos el texto de la etiqueta */
+ gtk_label_get (etiqueta, &amp;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 *arbol_raiz, GtkWidget *hijo,
+ GtkWidget *subarbol)
+{
+ g_print ("unselect_child called for root tree %p, subtree %p, child %p\n",
+ arbol_raiz, subarbol, hijo);
+}
+
+/* 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 *arbol_raiz, GtkWidget *hijo,
+ GtkWidget *subarbol)
+{
+ g_print ("select_child called for root tree %p, subtree %p, child %p\n",
+ arbol_raiz, subarbol, hijo);
+}
+
+static void cb_selection_changed (GtkWidget *arbol)
+{
+ GList *i;
+
+ g_print ("selection_change called for tree %p\n", arbol);
+ g_print ("selected objects are:\n");
+
+ i = GTK_TREE_SELECTION(arbol);
+ while (i){
+ gchar *name;
+ GtkLabel *etiqueta;
+ GtkWidget *item;
+
+ /* Get a GtkWidget pointer from the list node */
+ item = GTK_WIDGET (i->data);
+ etiqueta = GTK_LABEL (GTK_BIN (item)->child);
+ gtk_label_get (etiqueta, &amp;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 *ventana, *scrolled_win, *arbol;
+ static gchar *itemnames[] = {"Foo", "Bar", "Baz", "Quux",
+ "Maurice"};
+ gint i;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ /* una ventana general */
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_signal_connect (GTK_OBJECT(ventana), "delete_event",
+ GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
+ gtk_container_border_width (GTK_CONTAINER(ventana), 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(ventana), scrolled_win);
+ gtk_widget_show (scrolled_win);
+
+ /* Crear el árbol raíz */
+ arbol = gtk_tree_new();
+ g_print ("root tree is %p\n", arbol);
+ /* connect all GtkTree:: signals */
+ gtk_signal_connect (GTK_OBJECT(arbol), "select_child",
+ GTK_SIGNAL_FUNC(cb_select_child), arbol);
+ gtk_signal_connect (GTK_OBJECT(arbol), "unselect_child",
+ GTK_SIGNAL_FUNC(cb_unselect_child), arbol);
+ gtk_signal_connect (GTK_OBJECT(arbol), "selection_changed",
+ GTK_SIGNAL_FUNC(cb_selection_changed), arbol);
+ /* Añadirlo a la ventana con barras de desplazamiento */
+ gtk_container_add (GTK_CONTAINER(scrolled_win), arbol);
+ /* Poner el modo de selección */
+ gtk_tree_set_selection_mode (GTK_TREE(arbol),
+ GTK_SELECTION_MULTIPLE);
+ /* mostrar el árbol */
+ gtk_widget_show (arbol);
+
+ for (i = 0; i < 5; i++){
+ GtkWidget *subarbol, *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(arbol), item);
+ /* Mostrarlo - esto se puede hacer en cualquier momento */
+ gtk_widget_show (item);
+ /* Crear el subárbol de este elemento */
+ subarbol = gtk_tree_new();
+ g_print ("-> item %s->%p, subtree %p\n", itemnames[i], item,
+ subarbol);
+
+ /* 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(subarbol), "select_child",
+ GTK_SIGNAL_FUNC(cb_select_child), subarbol);
+ gtk_signal_connect (GTK_OBJECT(subarbol), "unselect_child",
+ GTK_SIGNAL_FUNC(cb_unselect_child), subarbol);
+ /* Esto no tiene absolutamente ningún efecto, ya que se ignora
+ * completamente en los subárboles */
+ gtk_tree_set_selection_mode (GTK_TREE(subarbol),
+ 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(subarbol), 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), subarbol);
+
+ 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(subarbol), subitem);
+ /* Mostrarlo */
+ gtk_widget_show (subitem);
+ }
+ }
+
+ /* Mostrar la ventana y entrar en el bucle final */
+ gtk_widget_show (ventana);
+ gtk_main();
+ return 0;
+}
+/* fin del ejemplo */
+</verb></tscreen>
+
+<!-- ***************************************************************** -->
+<sect> El <em>widget</em> menú
+<!-- ***************************************************************** -->
+<p>
+Hay dos formas de crear menús, la fácil, y la difícil. Ambas tienen su
+utilidad, aunque lo más probable es que normalmente utilice la
+menufactory (la forma fácil). La forma «difícil» consiste en crear
+todos los menús utilizando las llamadas directamente. La forma fácil
+consiste en utilizar las llamadas de <tt/gtk_item_factory/. Es mucho
+más fácil, pero aun así cada aproximación tiene sus ventajas y sus
+inconvenientes.
+
+La menufactory es mucho más fácil de utilizar, y tambíen es más fácil
+añadir nuevos menús, aunque a larga, escribiendo unas cuántas
+funciones de recubrimiento para crear menús utilizando el método
+manual puede acabar siendo más útil. Con la itemfactory, no es posible
+añadir imágenes o el carácter `/' a los menús.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Creación manual de menús
+<p>
+Siguiendo la auténtica tradición de la enseñanza, vamos a enseñarle
+primero la forma difícil. <tt>:)</tt>
+
+Se utilizan tres <em>widgets</em> para hacer una barra de menús y
+submenús:
+<itemize>
+<item>un elemento del menú, que es lo que el usuario quiere seleccionar,
+p.e. 'Guardar'
+<item>un menú, que actua como un contenedor para los elementos del menú, y
+<item>una barra de menú, que es un contenedor para cada uno de los menús,
+</itemize>
+
+Todo esto se complica ligeramente por el hecho de que los
+<em>widgets</em> de los elementos del menú se utilizan para dos cosas
+diferentes. Están los <em>widgets</em> que se empaquetan en el menú, y
+los que se empaquetan en una barra de menús, que cuando se selecciona,
+activa el menú.
+
+Vamos a ver las funciones que se utilizan para crear menús y barras
+de menús. ésta primera función se utiliza para crear una barra de menús.
+
+<tscreen><verb>
+GtkWidget *gtk_menu_bar_new( void );
+</verb></tscreen>
+
+Como el propio nombre indica, esta función crea una nueva barra de
+menús. Utilice <tt/gtk_container_add/ para empaquetarla en una
+ventana, o las funciones <tt/box_pack/ para empaquetarla en una caja -
+exactamente igual que si fuesen botones.
+
+<tscreen><verb>
+GtkWidget *gtk_menu_new( void );
+</verb></tscreen>
+
+Esta función devuelve un puntero a un nuevo menú, que no se debe
+mostrar nunca (no hace falta utilizar <tt/gtk_widget_show/), es sólo
+un contenedor para los elementos del menú. Espero que todo esto se
+aclare un poco cuando vea en el ejemplo que hay más abajo.
+
+Las siguientes dos llamadas se utilizan para crear elementos de menú
+que se empaquetarán en el menú (y en la barra de menú).
+
+<tscreen><verb>
+GtkWidget *gtk_menu_item_new( void );
+</verb></tscreen>
+
+y
+
+<tscreen><verb>
+GtkWidget *gtk_menu_item_new_with_label( const char *etiqueta );
+</verb></tscreen>
+
+Estas llamadas se utilizan para crear los elementos del menú que
+van a mostrarse. Recuerde que hay que distinguir entre un «menú»
+creado con <tt/gtk_menu_new/ y un «elemento del menú» creado con las
+funciones <tt/gtk_menu_item_new/. El elemento de menú será un botón
+con una acción asociada, y un menú será un contenedor con los
+elementos del menú.
+
+Las funciones <tt/gtk_menu_new_with_label/ y <tt/gtk_menu_new/ son
+sólo lo que espera que sean después de leer lo de los botones. Una
+crea un nuevo elemento del menú con una etiqueta ya dentro, y la otra
+crea un elemento del menú en blanco.
+
+Una vez ha creado un elemento del menú tiene que ponerlo en un menú.
+Esto se hace utilizando la función <tt/gtk_menu_append/. Para capturar
+el momento en el que el elemento se selecciona por el usuario,
+necesitamos conectar con la señal <tt/activate/ de la forma usual. Por
+tanto, si quiere crear un menú estándar <tt/File/, con las opciones
+<tt/Open/, <tt/Save/ y <tt/Quit/ el código debería ser algo como
+
+<tscreen><verb>
+file_menu = gtk_menu_new(); /* No hay que mostrar menús */
+
+/* Crear los elementos del menú */
+open_item = gtk_menu_item_new_with_label("Open");
+save_item = gtk_menu_item_new_with_label("Save");
+quit_item = gtk_menu_item_new_with_label("Quit");
+
+/* Añadirlos 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);
+
+/* Enlazar las función de llamada a la señal "activate" */
+gtk_signal_connect_object( GTK_OBJECT(open_items), "activate",
+ GTK_SIGNAL_FUNC(menuitem_response), (gpointer) "file.open");
+gtk_signal_connect_object( GTK_OBJECT(save_items), "activate",
+ GTK_SIGNAL_FUNC(menuitem_response), (gpointer) "file.save");
+
+/* Podemos enlazar el elemento de menú Quit con nuestra función de
+ * salida */
+gtk_signal_connect_object( GTK_OBJECT(quit_items), "activate",
+ GTK_SIGNAL_FUNC(destroy), (gpointer) "file.quit");
+
+/* Tenemos que mostrar los elementos del menú */We do need to show menu items */
+gtk_widget_show( open_item );
+gtk_widget_show( save_item );
+gtk_widget_show( quit_item );
+</verb></tscreen>
+
+En este momento tendremos nuestro menú. Ahora necesitamos crear una
+barra de menús y un elemento de menú para el elemento <tt/File/, que
+vamos a añadir a nuestro menú. El código es el siguiente
+
+<tscreen><verb>
+menu_bar = gtk_menu_bar_new();
+gtk_container_add( GTK_CONTAINER(ventana), menu_bar);
+gtk_widget_show( menu_bar );
+
+file_item = gtk_menu_item_new_with_label("File");
+gtk_widget_show(file_item);
+</verb></tscreen>
+
+Ahora necesitamos asociar el menú con <tt/file_item/. Esto se hace con
+la función
+
+<tscreen>
+void gtk_menu_item_set_submenu( GtkMenuItem *menu_item,
+ GtkWidget *submenu );
+</tscreen>
+
+Por lo que nuestro ejemplo continua con
+
+<tscreen><verb>
+gtk_menu_item_set_submenu( GTK_MENU_ITEM(file_item), file_menu );
+</verb></tscreen>
+
+Todo lo que queda por hacer es añadir el menú a la barra de menús, que
+se hace mediante la función
+
+<tscreen>
+void gtk_menu_bar_append( GtkMenuBar *menu_bar, GtkWidget *menu_item);
+</tscreen>
+
+que en nuestro caso habrá que utilizar así:
+
+<tscreen><verb>
+gtk_menu_bar_append( GTK_MENU_BAR (menu_bar), file_item );
+</verb></tscreen>
+
+Si queremos que el menú esté alineado a la derecha en la barra de
+menús, como suele estar la opción de ayuda, podemos utilizar la
+función siguiente (otra vez en <tt/file_item/ en el ejemplo actual)
+antes de enlazarla en la barra de menú.
+
+<tscreen><verb>
+void gtk_menu_item_right_justify( GtkMenuItem *menu_item );
+</verb></tscreen>
+
+Aquí hay un resumen de los pasos que son necesarios para crear una
+barra de menús con los menús correspondientes ya enlazados:
+
+<itemize>
+<item> Crear un nuevo menú utilizando <tt/gtk_menu_new()/
+<item> Utilizar multiples llamadas a <tt/gtk_menu_item_new()/ para
+cada elemento que desee tener en su menú. Y utilizar
+<tt/gtk_menu_append()/ para poner cada uno de esos nuevos elementos en
+el menú.
+<item> Crear un elemento de menú utilizando
+<tt/gtk_menu_item_new()/. Ésta será la raíz del menú, el texto que
+aparezca aquí estará en la barra de menús.
+<item> Utilizar <tt/gtk_menu_item_set_submenu()/ para enlazar el menú
+al elemento del menú raíz (el creado en el paso anterior).
+<item> Crear una nueva barra de menús utilizando
+<tt/gtk_menu_bar_new/. Este paso solo necesita hacerse una vez cuando
+se crea una serie de menús en una barra de menús.
+<item> Utilizar <tt/gtk_menu_bar_append/ para poner el menú raíz en la
+barra de menús.
+</itemize>
+
+Para hacer un menú desplegable hay que seguir prácticamente los mismos
+pasos. La única diferencia es que el menú no estará conectado
+`automáticamente' a una barra de menú, sino que para que aparezca
+deberá llamarse explícitamente a la función <tt/gtk_menu_popup()/
+utilizando, por ejemplo, un evento de pulsación de botón. Siga los
+pasos siguientes:
+
+<itemize>
+<item>Cree una función manejadora de eventos. Tiene que tener el
+siguiente prototipo
+<tscreen>
+static gint handler( GtkWidget *widget,
+ GdkEvent *event );
+</tscreen>
+
+y utilice el evento para encontrar donde debe aparecer el menú.
+
+<item>En el manejador de eventos, si el evento es una pulsación de un
+botón del ratón, tratar <tt>event</tt> como un evento de botón
+(que lo es) y utilizarlo como se indica en el código ejemplo para
+pasarle información a <tt/gtk_menu_popup()/.
+<item>Enlazar este manejador de eventos con el <em>widget</em> con
+<tscreen>
+gtk_signal_connect_object(GTK_OBJECT(widget), "event",
+ GTK_SIGNAL_FUNC (handler), GTK_OBJECT(menu));
+</tscreen>
+donde <tt>widget</tt> es el <em>widget</em> con el que esta conectando,
+<tt>handler</tt> es la función manejadora, y <tt>menu</tt> es un menú
+creado con <tt/gtk_menu_new()/. Éste puede ser un menú que esté
+contenido en una barra de menús, como se puede ver en el código de
+ejemplo.
+</itemize>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Ejemplo de la creación manual de un menú
+<p>
+Esto debería funcionar. Échele un vistazo al ejemplo para aclarar los
+conceptos.
+
+<tscreen><verb>
+/* principio del ejemplo menu menu.c */
+
+#include <gtk/gtk.h>
+
+static gint button_press (GtkWidget *, GdkEvent *);
+static void menuitem_response (gchar *);
+
+int main (int argc, char *argv[])
+{
+
+ GtkWidget *ventana;
+ GtkWidget *menu;
+ GtkWidget *menu_bar;
+ GtkWidget *root_menu;
+ GtkWidget *menu_items;
+ GtkWidget *vbox;
+ GtkWidget *boton;
+ char buf[128];
+ int i;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ /* crear una nueva ventana */
+ ventana = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_widget_set_usize( GTK_WIDGET (ventana), 200, 100);
+ gtk_window_set_title(GTK_WINDOW (ventana), "GTK Menu Test");
+ gtk_signal_connect(GTK_OBJECT (ventana), "delete_event",
+ (GtkSignalFunc) gtk_main_quit, NULL);
+
+ /* Inicializar el widget-menu, y recuerde -- ¡¡Nunca haga
+ * gtk_show_widget() con el widget menu!!
+ * Éste es el menú que contiene todos los elementos del menú, el
+ * que se desplegará cuando pulse en el "Root Menu" en la
+ * aplicación
+ */
+ menu = gtk_menu_new();
+
+ /* Ahora hacemos un pequeño bucle que crea tres elementos de menú
+ * para "test-menu". Recuerde llamar a gtk_menu_append. Aquí
+ * estamos añadiendo una lista de elementos de menú a nuestro
+ * menú. Normalmente tendríamos que cazar aquí la señal "clicked"
+ * de cada uno de los elementos del menú y le deberíamos dar una
+ * función de llamada a cada uno, pero lo vamos a omitimos para
+ * ahorrar espacio. */
+
+ for(i = 0; i < 3; i++)
+ {
+ /* Copia los nombres al búfer. */
+ sprintf(buf, "Test-undermenu - %d", i);
+
+ /* Crea un nuevo elemento de menú con un nombre... */
+ menu_items = gtk_menu_item_new_with_label(buf);
+
+ /* ...y lo añade al menú. */
+ gtk_menu_append(GTK_MENU (menu), menu_items);
+
+ /* Hace algo interesante cuando se selecciona el menuitem */
+ gtk_signal_connect_object(GTK_OBJECT(menu_items), "activate",
+ GTK_SIGNAL_FUNC(menuitem_response), (gpointer) g_strdup(buf));
+
+ /* Muestra el widget */
+ gtk_widget_show(menu_items);
+ }
+
+ /* Ésta es el menú raíz, y será la etiqueta mostrada en la
+ * barra de menús. No habrá ningún manejador de señal conectado, ya que
+ * lo único que hace es desplegar el resto del menú. */
+ root_menu = gtk_menu_item_new_with_label("Root Menu");
+
+ gtk_widget_show(root_menu);
+
+ /* Ahora especificamos que queremos que el recien creado "menu"
+ * sea el menú para el "root menu" */
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM (root_menu), menu);
+
+ /* Un vbox para poner dentro un menú y un botón */
+ vbox = gtk_vbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(ventana), vbox);
+ gtk_widget_show(vbox);
+
+ /* Crear una barra de menú para que contenga al menú y la añadamos
+ * a nuestra ventana principal */
+ menu_bar = gtk_menu_bar_new();
+ gtk_box_pack_start(GTK_BOX(vbox), menu_bar, FALSE, FALSE, 2);
+ gtk_widget_show(menu_bar);
+
+ /* Crear un botón al que atar los menús como un popup */
+ boton = gtk_button_new_with_label("press me");
+ gtk_signal_connect_object(GTK_OBJECT(boton), "event",
+ GTK_SIGNAL_FUNC (button_press), GTK_OBJECT(menu));
+ gtk_box_pack_end(GTK_BOX(vbox), boton, TRUE, TRUE, 2);
+ gtk_widget_show(boton);
+
+ /* Y finalmente añadimos el elemento de menú y la barra de menú --
+ * éste es el elemento de menú "raíz" sobre el que he estado
+ * delirando =) */
+ gtk_menu_bar_append(GTK_MENU_BAR (menu_bar), root_menu);
+
+ /* siempre mostramos la ventana como último paso para que todo se
+ * pongo en pantalla a la vez. */
+ gtk_widget_show(ventana);
+
+ gtk_main ();
+
+ return 0;
+}
+
+/* Responde a una pulsación del botón enviando un menú como un widget
+ * Recuerde que el argumento "widget" es el menú que se está enviando,
+ * NO el botón que se ha pulsado.
+ */
+
+static gint button_press (GtkWidget *widget, GdkEvent *event)
+{
+
+ if (event->type == GDK_BUTTON_PRESS) {
+ GdkEventButton *bevent = (GdkEventButton *) event;
+ gtk_menu_popup (GTK_MENU(widget), NULL, NULL, NULL, NULL,
+ bevent->button, bevent->time);
+ /* Le dice al que llamó a la rutina que hemos manejado el
+ * evento; la historia termina aquí. */
+ return TRUE;
+ }
+
+ /* Le dice al que llamó a la rutina que no hemos manejado el
+ * evento. */
+ return FALSE;
+}
+
+
+/* Imprime una cadena cuando se selecciona un elemento del menú */
+
+static void menuitem_response (gchar *string)
+{
+ printf("%s\n", string);
+}
+/* final del ejemplo */
+</verb></tscreen>
+
+También puede hacer que un elemento del menú sea insensible y, utilizando
+una tabla de teclas aceleradoras, conectar las teclas con las funciones
+del menú.
+
+<!-- XXX Las dos sect1 que vienen han cambiado -->
+<!-- ----------------------------------------------------------------- -->
+<sect1>Utilizando GtkItemFactory
+<p>
+Ahora que le hemos enseñado la forma difícil, le mostraremos como
+utilizar las llamadas <tt/gtk_item_factory/.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Ejemplo de la fábrica de elementos
+<p>
+Aquí hay un ejemplo de cómo utilizar la fábrica de elementos
+GTK.
+
+<tscreen><verb>
+/* principio del ejemplo menu itemfactory.h */
+
+#include <gtk/gtk.h>
+#include <strings.h>
+
+/* La obligatoria función de llamada */
+static void print_hello( GtkWidget *w,
+ gpointer data )
+{
+ g_message ("Hello, World!\n");
+}
+
+/* Esta es la estructura GtkItemFactoryEntry utilizada para crear
+ nuevos menúes.
+
+ This is the GtkItemFactoryEntry structure used to generate new menus.
+ Elemento 1: La dirección del menú. La letra que hay
+ después del subrayado indica una tecla aceleradora
+ una vez que el menú esté abierto.
+ Elemento 2: La tecla aceleradora para la entrada del menú.
+ Elemento 3: La función de llamada.
+ Elemento 4: La acción de llamada. Cambia los parámetros que
+ se le pasan a la función de llamada. El valor por
+ defecto es 0.
+ Elemento 5: El tipo de elemento, se utiliza para definir de que
+ tipo de elemento se trata. Los valores posibles son:
+
+ NULL -> "<Item>"
+ "" -> "<Item>"
+ "<Title>" -> crea un elemento título
+ "<Item>" -> crea un simple elemento
+ "<CheckItem>" -> crea un elemento de comprobación
+ "<ToggleItem>" -> crea un elemento de selección
+ "<RadioItem>" -> crea un elemento circular
+ <path> -> dirección de un elemento circular
+ con el que enlazar
+ "<Separator>" -> crea un separador
+ "<Branch>" -> crea un elemento para contener
+ subelementos (de forma opcional)
+ "<LastBranch>" -> crea una rama justificada a la
+ derecha
+*/
+
+static GtkItemFactoryEntry menu_items[] = {
+ { "/_File", NULL, NULL, 0, "<Branch>" },
+ { "/File/_New", "<control>N", print_hello, 0, NULL },
+ { "/File/_Open", "<control>O", print_hello, 0, NULL },
+ { "/File/_Save", "<control>S", print_hello, 0, NULL },
+ { "/File/Save _As", NULL, NULL, 0, NULL },
+ { "/File/sep1", NULL, NULL, 0, "<Separator>" },
+ { "/File/Quit", "<control>Q", gtk_main_quit, 0, NULL },
+ { "/_Options", NULL, NULL, 0, "<Branch>" },
+ { "/Options/Test", NULL, NULL, 0, NULL },
+ { "/_Help", NULL, NULL, 0, "<LastBranch>" },
+ { "/_Help/About", NULL, NULL, 0, NULL },
+};
+
+
+void get_main_menu( GtkWidget *ventana,
+ GtkWidget **menubar )
+{
+ GtkItemFactory *item_factory;
+ GtkAccelGroup *accel_group;
+ gint nmenu_items = sizeof (menu_items) / sizeof (menu_items[0]);
+
+ accel_group = gtk_accel_group_new ();
+
+ /* Esta función inicializa la fábrica de elementos
+ Param 1: El tipo de menú - puede ser GTK_TYPE_MENU_BAR,
+ GTK_TYPE_MENU, o GTK_TYPE_OPTION_MENU.
+ Param 2: La dirección del menú.
+ Param 3: Un puntero a un gtk_accel_group. La fábrica de
+ elementos actualiza la tabla de teclas aceleradoras
+ mientras genera los menúes.
+ */
+
+ item_factory = gtk_item_factory_new (GTK_TYPE_MENU_BAR, "<main>",
+ accel_group);
+
+ /* Esta función genera los elementos de menú. Pasa la
+ fábrica de elementos (item_factory), el número de elementos
+ del vector, el vector en sí, y cualquier dato de llamada para
+ los elementos de menú. */
+ gtk_item_factory_create_items (item_factory, nmenu_items, menu_items, NULL);
+
+ /* Enlaza el nuevo grupo acelerador a la ventana. */
+ gtk_accel_group_attach (accel_group, GTK_OBJECT (ventana));
+
+ if (menubar)
+ /* Finalmente, devuelve la barra de menú creada por la
+ * fábrica de elementos. */
+ *menubar = gtk_item_factory_get_widget (item_factory, "<main>");
+}
+
+int main( int argc,
+ char *argv[] )
+{
+ GtkWidget *ventana;
+ GtkWidget *main_vbox;
+ GtkWidget *menubar;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
+ GTK_SIGNAL_FUNC (gtk_main_quit),
+ "WM destroy");
+ gtk_window_set_title (GTK_WINDOW(ventana), "Item Factory");
+ gtk_widget_set_usize (GTK_WIDGET(ventana), 300, 200);
+
+ main_vbox = gtk_vbox_new (FALSE, 1);
+ gtk_container_border_width (GTK_CONTAINER (main_vbox), 1);
+ gtk_container_add (GTK_CONTAINER (ventana), main_vbox);
+ gtk_widget_show (main_vbox);
+
+ get_main_menu (ventana, &amp;menubar);
+ gtk_box_pack_start (GTK_BOX (main_vbox), menubar, FALSE, TRUE, 0);
+ gtk_widget_show (menubar);
+
+ gtk_widget_show (ventana);
+ gtk_main ();
+
+ return(0);
+}
+/* fin del ejemplo */
+</verb></tscreen>
+
+Por ahora, sólo está este ejemplo. Ya llegará una
+explicación del mismo y más comentarios.
+
+<!-- ***************************************************************** -->
+<sect> El <em>widget</em> texto
+<!-- ***************************************************************** -->
+<p>
+El <em>widget</em> texto permite mostrar y editar multiples líneas de
+texto. Admite texto en varios colores y con varios tipos de letra,
+permitiendo mezclarlos de cualquier forma que desee. También hay un
+gran número de teclas para la edición de textos, que son compatibles
+con Emacs.
+
+El <em>widget</em> texto admite copiar-y-pegar, incluyendo la
+utilización de doble y triple-click para seleccionar una palabra y una
+línea completa, respectivamente.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Creando y configurando un cuadro de texto
+<p>
+Sólo hay una función para crear un nuevo <em>widget</em> texto.
+<tscreen><verb>
+GtkWidget *gtk_text_new( GtkAdjustment *hadj,
+ GtkAdjustment *vadj );
+</verb></tscreen>
+
+Los argumentos nos permitirán dar al <em>widget</em> texto punteros a
+<tt/GtkAdjustement/ que pueden ser utilizados para controlar la visión
+de la posición del <em>widget</em>. Si le ponemos un valor NULL en
+cualquiera de los dos argumentos (o en los dos), la función
+<tt/gtk_text_new/ creará su propio ajuste.
+
+<tscreen><verb>
+void gtk_text_set_adjustments( GtkText *text,
+ GtkAdjustment *hadj,
+ GtkAdjustment *vadj );
+</verb></tscreen>
+
+La función de arriba permite cambiar en cualquier momento el ajuste
+horizontal y vertical de un <em>widget</em> texto.
+
+El <em>widget</em> texto no crea automáticamente sus propiar barras
+de desplazamiento cuando el texto a mostrar es demasiado largo
+para la ventana en la que se encuentra. Tenemos que crearlas y
+añadirlas a la capa del <em>display</em> nosotros mismos.
+
+<tscreen><verb>
+ vscrollbar = gtk_vscrollbar_new (GTK_TEXT(text)->vadj);
+ gtk_box_pack_start(GTK_BOX(hbox), vscrollbar, FALSE, FALSE, 0);
+ gtk_widget_show (vscrollbar);
+</verb></tscreen>
+
+El trozo de código de arriba crea una nueva barra de desplazamiento
+vertical, y la conecta con el ajuste vertical del <em>widget</em>
+de texto, <tt/text/. Entonces la empaqueta en un cuadro de la forma
+usual.
+
+Observe que, actualmente el <em>widget</em> GtkText no admite barras
+de desplazamiento horizontal.
+
+Principalmente hay dos maneras de utilizar un <em>widget</em> de
+texto: permitiendo al usuario editar el texto, o permitiéndonos
+mostrar varias líneas de texto al usuario. Para cambiar entre estos
+dos modos de operación, el <em>widget</em> de texto tiene las
+siguientes funciones:
+
+<tscreen><verb>
+void gtk_text_set_editable( GtkText *text,
+ gint editable );
+</verb></tscreen>
+
+El argumento <tt/editable/ es un valor TRUE o FALSE que especifica si se
+permite al usuario editar los contenidos del <em>widget</em> texto.
+Cuando el <em>widget</em> texto se pueda editar, mostrará un cursor
+en la posición actual de inserción.
+
+Sin embargo la utilización del <em>widget</em> en estos dos modos no
+es algo permanente, ya que puede cambiar el estado editable del
+<em>widget</em> texto e insertar texto en cualquier momento.
+
+El <em>widget</em> texto corta las líneas de texto que son demasiado
+largas para que quepan en una sólo línea en la ventana. Su
+comportamiento por defecto es cortar las palabras donde se terminan
+las líneas. Esto puede cambiarse utilizando la siguiente función:
+
+<tscreen><verb>
+void gtk_text_set_word_wrap( GtkText *text,
+ gint word_wrap );
+</verb></tscreen>
+
+Utilizando esta función podremos especificar que el <em>widget</em>
+texto debería cortar las líneas largas en los límites de las
+palabras. El argumento <tt/word_wrap/ es un valor TRUE o FALSE.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Manipulación de texto
+<P>
+El punto actual de inserción en un <em>widget</em> texto puede
+cambiarse utilizando
+<tscreen><verb>
+void gtk_text_set_point( GtkText *text,
+ guint index );
+</verb></tscreen>
+
+donde <tt/index/ es la posición en la que poner el punto de inserción.
+
+Análogamente tenemos la función para obtener la posición del punto
+de inserción:
+
+<tscreen><verb>
+guint gtk_text_get_point( GtkText *text );
+</verb></tscreen>
+
+Una función que es útil en combinación con las dos anteriores es
+
+<tscreen><verb>
+guint gtk_text_get_length( GtkText *text );
+</verb></tscreen>
+
+que devuelve la longitud actual del <em>widget</em> texto. La
+longitud es el número de caracteres que hay en el bloque de texto,
+incluyendo caracteres como el retorno de carro, que marca el final de
+las líneas.
+
+Para insertar texto en la posición actual del cursor, tendrá que
+utilizar la función <tt/gtk_text_insert/, que nos permitirá
+especificar los colores de fondo y de la letra y un tipo de letra para
+el texto.
+
+<tscreen><verb>
+void gtk_text_insert( GtkText *text,
+ GdkFont *font,
+ GdkColor *fore,
+ GdkColor *back,
+ const char *chars,
+ gint length );
+</verb></tscreen>
+
+Pasar un valor <tt/NULL/ como el color de la letra (<tt/fore/), el
+color de fondo (<tt/back/) o el tipo de letra (<tt/font/) hará que
+se utilicen los valores que indiquen el estilo del <em>widget</em>.
+Utilizar un valor de <tt/-1/ para el parámetro <tt/length/ hará
+que se inserte todo el texto.
+
+El <em/widget/ texto es uno de los pocos de GTK que se redibuja
+a sí mismo dinámicamente, fuera de la función <tt/gtk_main/. Esto
+significa que todos los cambios en el contenido del <em/widget/ texto
+se manifestarán inmediatamente. Para permitirnos realizar varias
+actualizaciones del <em/widget/ de texto sin que se redibuje
+continuamente, podemos congelar el <em/widget/, lo que hará que pare
+momentaneamente de redibujarse a sí mismo cada vez que haya algún
+cambio. Podemos descongelarlo cuando hayamos acabado con nuestras
+actualizaciones.
+
+Las siguientes dos funciones realizarán la acción de congelar y
+descongelar el <em/widget/:
+
+<tscreen><verb>
+void gtk_text_freeze( GtkText *text );
+
+void gtk_text_thaw( GtkText *text );
+</verb></tscreen>
+
+Se puede borrar el texto que se encuentra en el punto actual de
+inserción del <em/widget/ de texto mediante dos funciones. El valor
+devuelto es TRUE o FALSE en función del éxito de la operación.
+
+<tscreen><verb>
+gint gtk_text_backward_delete( GtkText *text,
+ guint nchars );
+
+gint gtk_text_forward_delete ( GtkText *text,
+ guint nchars );
+</verb></tscreen>
+
+Si quiere recuperar el contenido del <em/widget/ de texto, entonces
+la macro <tt/GTK_TEXT_INDEX(t, index)/ le permitirá obtener el
+carácter que se encuentra en la posición <tt/index/ del <em/widget/
+de texto <tt/t/.
+
+Para obtener mayores bloques de texto, podemos utilizar la función
+
+<tscreen><verb>
+gchar *gtk_editable_get_chars( GtkEditable *editable,
+ gint start_pos,
+ gint end_pos );
+</verb></tscreen>
+
+Esta es una función de la clase padre del <em/widget/ texto. Un valor
+de -1 en <tt/end_pos/ significa el final del texto. El índice del
+texto empieza en 0.
+
+La función reserva un espacio de memoria para el bloque de texto,
+por lo que no debe olvidarse de liberarlo con una llamada a
+<tt/g_free/ cuando haya acabado el bloque.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Atajos por teclado
+<p>
+El <em/widget/ texto tiene ciertos atajos de teclado preinstalados
+para las funciones de edición estándar, movimiento y selección. Pueden
+utilizarse mediante combinaciones de las teclas Control y Alt.
+
+Además, si se mantiene apretada la tecla de Control y se utilizan las
+teclas de movimiento, el cursor se moverá por palabras en lugar de por
+caracteres. Manteniendo apretada la tecla Shift, las teclas de movimiento
+harán que se extienda la selección.
+
+<sect2>Atajos para el movimiento
+<p>
+<itemize>
+<item> Ctrl-A Principio de línea
+<item> Ctrl-E Final de línea
+<item> Ctrl-N Línea siguiente
+<item> Ctrl-P Línea anterior
+<item> Ctrl-B Retrasarse un carácter
+<item> Ctrl-F Adelantarse un carácter
+<item> Alt-B Retrasarse una palabra
+<item> Alt-F Adelantarse una palabra
+</itemize>
+
+<sect2>Atajos para la edición
+<p>
+<itemize>
+<item> Ctrl-H Borrar el carácter anterior (Backspace)
+<item> Ctrl-D Borrar el carácter siguiente (Suprimir)
+<item> Ctrl-W Borrar la palabra anterior
+<item> Alt-D Borrar la palabra siguiente
+<item> Ctrl-K Borrar hasta el fin de la línea
+<item> Ctrl-U Borrar la línea
+</itemize>
+
+<sect2>Atajos de selección
+<p>
+<itemize>
+<item> Ctrl-X Cortar al portapapeles
+<item> Ctrl-C Copiar al portapapeles
+<item> Ctrl-V Pegar desde el portapapeles
+</itemize>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Un ejemplo de GtkText
+<p>
+<tscreen><verb>
+/* principio del ejemplo text text.c */
+
+/* text.c */
+
+#include <stdio.h>
+#include <gtk/gtk.h>
+
+void text_toggle_editable (GtkWidget *checkbutton,
+ GtkWidget *text)
+{
+ gtk_text_set_editable(GTK_TEXT(text),
+ GTK_TOGGLE_BUTTON(checkbutton)->active);
+}
+
+void text_toggle_word_wrap (GtkWidget *checkbutton,
+ GtkWidget *text)
+{
+ gtk_text_set_word_wrap(GTK_TEXT(text),
+ GTK_TOGGLE_BUTTON(checkbutton)->active);
+}
+
+void close_application( GtkWidget *widget, gpointer data )
+{
+ gtk_main_quit();
+}
+
+int main (int argc, char *argv[])
+{
+ GtkWidget *ventana;
+ GtkWidget *caja1;
+ GtkWidget *caja2;
+ GtkWidget *hbox;
+ GtkWidget *boton;
+ GtkWidget *check;
+ GtkWidget *separator;
+ GtkWidget *table;
+ GtkWidget *vscrollbar;
+ GtkWidget *text;
+ GdkColormap *cmap;
+ GdkColor colour;
+ GdkFont *fixed_font;
+
+ FILE *infile;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_widget_set_usize (ventana, 600, 500);
+ gtk_window_set_policy (GTK_WINDOW(ventana), TRUE, TRUE, FALSE);
+ gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
+ GTK_SIGNAL_FUNC(close_application),
+ NULL);
+ gtk_window_set_title (GTK_WINDOW (ventana), "Text Widget Example");
+ gtk_container_border_width (GTK_CONTAINER (ventana), 0);
+
+
+ caja1 = gtk_vbox_new (FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (ventana), caja1);
+ gtk_widget_show (caja1);
+
+
+ caja2 = gtk_vbox_new (FALSE, 10);
+ gtk_container_border_width (GTK_CONTAINER (caja2), 10);
+ gtk_box_pack_start (GTK_BOX (caja1), caja2, TRUE, TRUE, 0);
+ gtk_widget_show (caja2);
+
+
+ table = gtk_table_new (2, 2, FALSE);
+ gtk_table_set_row_spacing (GTK_TABLE (table), 0, 2);
+ gtk_table_set_col_spacing (GTK_TABLE (table), 0, 2);
+ gtk_box_pack_start (GTK_BOX (caja2), table, TRUE, TRUE, 0);
+ gtk_widget_show (table);
+
+ /* Crear el widget GtkText */
+ text = gtk_text_new (NULL, NULL);
+ gtk_text_set_editable (GTK_TEXT (text), TRUE);
+ gtk_table_attach (GTK_TABLE (table), text, 0, 1, 0, 1,
+ GTK_EXPAND | GTK_SHRINK | GTK_FILL,
+ GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (text);
+
+ /* Añadir una barra de desplazamiento vertical al widget GtkText */
+ vscrollbar = gtk_vscrollbar_new (GTK_TEXT (text)->vadj);
+ gtk_table_attach (GTK_TABLE (table), vscrollbar, 1, 2, 0, 1,
+ GTK_FILL, GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (vscrollbar);
+
+ /* Obtener el mapa de colores del sistema y conseguir el color rojo */
+ cmap = gdk_colormap_get_system();
+ colour.red = 0xffff;
+ colour.green = 0;
+ colour.blue = 0;
+ if (!gdk_color_alloc(cmap, &amp;colour)) {
+ g_error("couldn't allocate colour");
+ }
+
+ /* Cargar un fuente de tamaño fijo */
+ fixed_font = gdk_font_load ("-misc-fixed-medium-r-*-*-*-140-*-*-*-*-*-*");
+
+ /* Al enviar la señal relize a un widget se crea una ventana para el
+ * mismo, y nos permite insertar texto */
+ gtk_widget_realize (text);
+
+ /* Congela el widget text, lo que nos permite hacer varias
+ * actualizaciones */
+ gtk_text_freeze (GTK_TEXT (text));
+
+ /* Insertamos algún texto coloreado */
+ gtk_text_insert (GTK_TEXT (text), NULL, &amp;text->style->black, NULL,
+ "Supports ", -1);
+ gtk_text_insert (GTK_TEXT (text), NULL, &amp;colour, NULL,
+ "colored ", -1);
+ gtk_text_insert (GTK_TEXT (text), NULL, &amp;text->style->black, NULL,
+ "text and different ", -1);
+ gtk_text_insert (GTK_TEXT (text), fixed_font, &amp;text->style->black, NULL,
+ "fonts\n\n", -1);
+
+ /* Cargamos el fichero text.c en la ventana de texto */
+
+ infile = fopen("text.c", "r");
+
+ if (infile) {
+ char buffer[1024];
+ int nchars;
+
+ while (1)
+ {
+ nchars = fread(buffer, 1, 1024, infile);
+ gtk_text_insert (GTK_TEXT (text), fixed_font, NULL,
+ NULL, buffer, nchars);
+
+ if (nchars < 1024)
+ break;
+ }
+
+ fclose (infile);
+ }
+
+ /* Descongelamos el widget text, permitiéndonos ver todos los
+ * cambios */
+ gtk_text_thaw (GTK_TEXT (text));
+
+ hbox = gtk_hbutton_box_new ();
+ gtk_box_pack_start (GTK_BOX (caja2), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ check = gtk_check_button_new_with_label("Editable");
+ gtk_box_pack_start (GTK_BOX (hbox), check, FALSE, FALSE, 0);
+ gtk_signal_connect (GTK_OBJECT(check), "toggled",
+ GTK_SIGNAL_FUNC(text_toggle_editable), text);
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(check), TRUE);
+ gtk_widget_show (check);
+ check = gtk_check_button_new_with_label("Wrap Words");
+ gtk_box_pack_start (GTK_BOX (hbox), check, FALSE, TRUE, 0);
+ gtk_signal_connect (GTK_OBJECT(check), "toggled",
+ GTK_SIGNAL_FUNC(text_toggle_word_wrap), text);
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(check), FALSE);
+ gtk_widget_show (check);
+
+ separator = gtk_hseparator_new ();
+ gtk_box_pack_start (GTK_BOX (caja1), separator, FALSE, TRUE, 0);
+ gtk_widget_show (separator);
+
+ caja2 = gtk_vbox_new (FALSE, 10);
+ gtk_container_border_width (GTK_CONTAINER (caja2), 10);
+ gtk_box_pack_start (GTK_BOX (caja1), caja2, FALSE, TRUE, 0);
+ gtk_widget_show (caja2);
+
+ boton = gtk_button_new_with_label ("close");
+ gtk_signal_connect (GTK_OBJECT (boton), "clicked",
+ GTK_SIGNAL_FUNC(close_application),
+ NULL);
+ gtk_box_pack_start (GTK_BOX (caja2), boton, TRUE, TRUE, 0);
+ GTK_WIDGET_SET_FLAGS (boton, GTK_CAN_DEFAULT);
+ gtk_widget_grab_default (boton);
+ gtk_widget_show (boton);
+
+ gtk_widget_show (ventana);
+
+ gtk_main ();
+
+ return 0;
+}
+/* fin del ejemplo */
+</verb></tscreen>
+
+<!-- ***************************************************************** -->
+<sect> Los <em>widgets</em> no documentados
+<!-- ***************************************************************** -->
+<p>
+¡Todos ellos necesitan de gente que los documente! :) Por favor,
+considere el contribuir a nuestro tutorial.
+
+Si debe utilizar uno de estos <em/widgets/, que permanecen no
+documentados, le sugiero que le eche un vistazo a su fichero de
+cabecera respectivo en la distribución GTK. Los nombre de las
+funciones GTK son muy descriptivos. Una vez haya comprendido como
+funcionan las cosas, no le será difícil ver como hay que utilizar un
+<em/widget/ simplemente mirando su declaración de funciones. Con esto,
+y unos cuántos ejemplos del código de otros, no debería tener
+problemas.
+
+Cuando haya comprendido todas las funciones de un nuevo <em/widget/
+no documentado, por favor considere el hecho de escribir un tutorial
+para él, para que así otros se puedan beneficiar del tiempo que usted
+gastó.
+
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Calendar
+<p>
+<!-- ----------------------------------------------------------------- -->
+<sect1> CTree
+<p>
+<!-- ----------------------------------------------------------------- -->
+<sect1> Curves
+<p>
+<!-- ----------------------------------------------------------------- -->
+<sect1> Drawing Area
+<p>
+<!-- ----------------------------------------------------------------- -->
+<sect1> Font Selection Dialog
+<p>
+<!-- ----------------------------------------------------------------- -->
+<sect1> Gamma Curve
+<p>
+<!-- ----------------------------------------------------------------- -->
+<sect1> Image
+<p>
+<!-- ----------------------------------------------------------------- -->
+<sect1> Packer
+<p>
+<!-- ----------------------------------------------------------------- -->
+<sect1> Plugs and Sockets
+<p>
+<!-- ----------------------------------------------------------------- -->
+<sect1> Preview
+<p>
+
+<!--
+
+(This may need to be rewritten to follow the style of the rest of the tutorial)
+
+<tscreen><verb>
+
+Previews serve a number of purposes in GIMP/GTK. The most important one is
+this. High quality images may take up to tens of megabytes of memory - easy!
+Any operation on an image that big is bound to take a long time. If it takes
+you 5-10 trial-and-errors (i.e. 10-20 steps, since you have to revert after
+you make an error) to choose the desired modification, it make take you
+literally hours to make the right one - if you don't run out of memory
+first. People who have spent hours in color darkrooms know the feeling.
+Previews to the rescue!
+
+But the annoyance of the delay is not the only issue. Oftentimes it is
+helpful to compare the Before and After versions side-by-side or at least
+back-to-back. If you're working with big images and 10 second delays,
+obtaining the Before and After impressions is, to say the least, difficult.
+For 30M images (4"x6", 600dpi, 24 bit) the side-by-side comparison is right
+out for most people, while back-to-back is more like back-to-1001, 1002,
+..., 1010-back! Previews to the rescue!
+
+But there's more. Previews allow for side-by-side pre-previews. In other
+words, you write a plug-in (e.g. the filterpack simulation) which would have
+a number of here's-what-it-would-look-like-if-you-were-to-do-this previews.
+An approach like this acts as a sort of a preview palette and is very
+effective for subtle changes. Let's go previews!
+
+There's more. For certain plug-ins real-time image-specific human
+intervention maybe necessary. In the SuperNova plug-in, for example, the
+user is asked to enter the coordinates of the center of the future
+supernova. The easiest way to do this, really, is to present the user with a
+preview and ask him to interactively select the spot. Let's go previews!
+
+Finally, a couple of misc uses. One can use previews even when not working
+with big images. For example, they are useful when rendering complicated
+patterns. (Just check out the venerable Diffraction plug-in + many other
+ones!) As another example, take a look at the colormap rotation plug-in
+(work in progress). You can also use previews for little logos inside you
+plug-ins and even for an image of yourself, The Author. Let's go previews!
+
+When Not to Use Previews
+
+Don't use previews for graphs, drawing etc. GDK is much faster for that. Use
+previews only for rendered images!
+
+Let's go previews!
+
+You can stick a preview into just about anything. In a vbox, an hbox, a
+table, a button, etc. But they look their best in tight frames around them.
+Previews by themselves do not have borders and look flat without them. (Of
+course, if the flat look is what you want...) Tight frames provide the
+necessary borders.
+
+ [Image][Image]
+
+Previews in many ways are like any other widgets in GTK (whatever that
+means) except they possess an additional feature: they need to be filled with
+some sort of an image! First, we will deal exclusively with the GTK aspect
+of previews and then we'll discuss how to fill them.
+
+GtkWidget *preview!
+
+Without any ado:
+
+ /* Create a preview widget,
+ set its size, an show it */
+GtkWidget *preview;
+preview=gtk_preview_new(GTK_PREVIEW_COLOR)
+ /*Other option:
+ GTK_PREVIEW_GRAYSCALE);*/
+gtk_preview_size (GTK_PREVIEW (preview), WIDTH, HEIGHT);
+gtk_widget_show(preview);
+my_preview_rendering_function(preview);
+
+Oh yeah, like I said, previews look good inside frames, so how about:
+
+GtkWidget *create_a_preview(int Width,
+ int Height,
+ int Colorfulness)
+{
+ GtkWidget *preview;
+ GtkWidget *frame;
+
+ frame = gtk_frame_new(NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
+ gtk_container_set_border_width (GTK_CONTAINER(frame),0);
+ gtk_widget_show(frame);
+
+ preview=gtk_preview_new (Colorfulness?GTK_PREVIEW_COLOR
+ :GTK_PREVIEW_GRAYSCALE);
+ gtk_preview_size (GTK_PREVIEW (preview), Width, Height);
+ gtk_container_add(GTK_CONTAINER(frame),preview);
+ gtk_widget_show(preview);
+
+ my_preview_rendering_function(preview);
+ return frame;
+}
+
+That's my basic preview. This routine returns the "parent" frame so you can
+place it somewhere else in your interface. Of course, you can pass the
+parent frame to this routine as a parameter. In many situations, however,
+the contents of the preview are changed continually by your application. In
+this case you may want to pass a pointer to the preview to a
+"create_a_preview()" and thus have control of it later.
+
+One more important note that may one day save you a lot of time. Sometimes
+it is desirable to label you preview. For example, you may label the preview
+containing the original image as "Original" and the one containing the
+modified image as "Less Original". It might occur to you to pack the
+preview along with the appropriate label into a vbox. The unexpected caveat
+is that if the label is wider than the preview (which may happen for a
+variety of reasons unforseeable to you, from the dynamic decision on the
+size of the preview to the size of the font) the frame expands and no longer
+fits tightly over the preview. The same problem can probably arise in other
+situations as well.
+
+ [Image]
+
+The solution is to place the preview and the label into a 2x1 table and by
+attaching them with the following parameters (this is one possible variations
+of course. The key is no GTK_FILL in the second attachment):
+
+gtk_table_attach(GTK_TABLE(table),label,0,1,0,1,
+ 0,
+ GTK_EXPAND|GTK_FILL,
+ 0,0);
+gtk_table_attach(GTK_TABLE(table),frame,0,1,1,2,
+ GTK_EXPAND,
+ GTK_EXPAND,
+ 0,0);
+
+
+And here's the result:
+
+ [Image]
+
+Misc
+
+Making a preview clickable is achieved most easily by placing it in a
+boton. It also adds a nice border around the preview and you may not even
+need to place it in a frame. See the Filter Pack Simulation plug-in for an
+example.
+
+This is pretty much it as far as GTK is concerned.
+
+Filling In a Preview
+
+In order to familiarize ourselves with the basics of filling in previews,
+let's create the following pattern (contrived by trial and error):
+
+ [Image]
+
+void
+my_preview_rendering_function(GtkWidget *preview)
+{
+#define SIZE 100
+#define HALF (SIZE/2)
+
+ guchar *row=(guchar *) malloc(3*SIZE); /* 3 bits per dot */
+ gint i, j; /* Coordinates */
+ double r, alpha, x, y;
+
+ if (preview==NULL) return; /* I usually add this when I want */
+ /* to avoid silly crashes. You */
+ /* should probably make sure that */
+ /* everything has been nicely */
+ /* initialized! */
+ for (j=0; j < ABS(cos(2*alpha)) ) { /* Are we inside the shape? */
+ /* glib.h contains ABS(x). */
+ row[i*3+0] = sqrt(1-r)*255; /* Define Red */
+ row[i*3+1] = 128; /* Define Green */
+ row[i*3+2] = 224; /* Define Blue */
+ } /* "+0" is for alignment! */
+ else {
+ row[i*3+0] = r*255;
+ row[i*3+1] = ABS(sin((float)i/SIZE*2*PI))*255;
+ row[i*3+2] = ABS(sin((float)j/SIZE*2*PI))*255;
+ }
+ }
+ gtk_preview_draw_row( GTK_PREVIEW(preview),row,0,j,SIZE);
+ /* Insert "row" into "preview" starting at the point with */
+ /* coordinates (0,j) first column, j_th row extending SIZE */
+ /* pixels to the right */
+ }
+
+ free(row); /* save some space */
+ gtk_widget_draw(preview,NULL); /* what does this do? */
+ gdk_flush(); /* or this? */
+}
+
+Non-GIMP users can have probably seen enough to do a lot of things already.
+For the GIMP users I have a few pointers to add.
+
+Image Preview
+
+It is probably wise to keep a reduced version of the image around with just
+enough pixels to fill the preview. This is done by selecting every n'th
+pixel where n is the ratio of the size of the image to the size of the
+preview. All further operations (including filling in the previews) are then
+performed on the reduced number of pixels only. The following is my
+implementation of reducing the image. (Keep in mind that I've had only basic
+C!)
+
+(UNTESTED CODE ALERT!!!)
+
+typedef struct {
+ gint width;
+ gint height;
+ gint bbp;
+ guchar *rgb;
+ guchar *mask;
+} ReducedImage;
+
+enum {
+ SELECTION_ONLY,
+ SELECTION_IN_CONTEXT,
+ ENTIRE_IMAGE
+};
+
+ReducedImage *Reduce_The_Image(GDrawable *drawable,
+ GDrawable *mask,
+ gint LongerSize,
+ gint Selection)
+{
+ /* This function reduced the image down to the the selected preview size */
+ /* The preview size is determine by LongerSize, i.e. the greater of the */
+ /* two dimensions. Works for RGB images only! */
+ gint RH, RW; /* Reduced height and reduced width */
+ gint width, height; /* Width and Height of the area being reduced */
+ gint bytes=drawable->bpp;
+ ReducedImage *temp=(ReducedImage *)malloc(sizeof(ReducedImage));
+
+ guchar *tempRGB, *src_row, *tempmask, *src_mask_row,R,G,B;
+ gint i, j, whichcol, whichrow, x1, x2, y1, y2;
+ GPixelRgn srcPR, srcMask;
+ gint NoSelectionMade=TRUE; /* Assume that we're dealing with the entire */
+ /* image. */
+
+ gimp_drawable_mask_bounds (drawable->id, &amp;x1, &amp;y1, &amp;x2, &amp;y2);
+ width = x2-x1;
+ height = y2-y1;
+ /* If there's a SELECTION, we got its bounds!)
+
+ if (width != drawable->width &amp;&amp; height != drawable->height)
+ NoSelectionMade=FALSE;
+ /* Become aware of whether the user has made an active selection */
+ /* This will become important later, when creating a reduced mask. */
+
+ /* If we want to preview the entire image, overrule the above! */
+ /* Of course, if no selection has been made, this does nothing! */
+ if (Selection==ENTIRE_IMAGE) {
+ x1=0;
+ x2=drawable->width;
+ y1=0;
+ y2=drawable->height;
+ }
+
+ /* If we want to preview a selection with some surrounding area we */
+ /* have to expand it a little bit. Consider it a bit of a riddle. */
+ if (Selection==SELECTION_IN_CONTEXT) {
+ x1=MAX(0, x1-width/2.0);
+ x2=MIN(drawable->width, x2+width/2.0);
+ y1=MAX(0, y1-height/2.0);
+ y2=MIN(drawable->height, y2+height/2.0);
+ }
+
+ /* How we can determine the width and the height of the area being */
+ /* reduced. */
+ width = x2-x1;
+ height = y2-y1;
+
+ /* The lines below determine which dimension is to be the longer */
+ /* side. The idea borrowed from the supernova plug-in. I suspect I */
+ /* could've thought of it myself, but the truth must be told. */
+ /* Plagiarism stinks! */
+ if (width>height) {
+ RW=LongerSize;
+ RH=(float) height * (float) LongerSize/ (float) width;
+ }
+ else {
+ RH=LongerSize;
+ RW=(float)width * (float) LongerSize/ (float) height;
+ }
+
+ /* The entire image is stretched into a string! */
+ tempRGB = (guchar *) malloc(RW*RH*bytes);
+ tempmask = (guchar *) malloc(RW*RH);
+
+ gimp_pixel_rgn_init (&amp;srcPR, drawable, x1, y1, width, height,
+ FALSE, FALSE);
+ gimp_pixel_rgn_init (&amp;srcMask, mask, x1, y1, width, height,
+ FALSE, FALSE);
+
+ /* Grab enough to save a row of image and a row of mask. */
+ src_row = (guchar *) malloc (width*bytes);
+ src_mask_row = (guchar *) malloc (width);
+
+ for (i=0; i < RH; i++) {
+ whichrow=(float)i*(float)height/(float)RH;
+ gimp_pixel_rgn_get_row (&amp;srcPR, src_row, x1, y1+whichrow, width);
+ gimp_pixel_rgn_get_row (&amp;srcMask, src_mask_row, x1, y1+whichrow, width);
+
+ for (j=0; j < RW; j++) {
+ whichcol=(float)j*(float)width/(float)RW;
+
+ /* No selection made = each point is completely selected! */
+ if (NoSelectionMade)
+ tempmask[i*RW+j]=255;
+ else
+ tempmask[i*RW+j]=src_mask_row[whichcol];
+
+ /* Add the row to the one long string which now contains the image! */
+ tempRGB[i*RW*bytes+j*bytes+0]=src_row[whichcol*bytes+0];
+ tempRGB[i*RW*bytes+j*bytes+1]=src_row[whichcol*bytes+1];
+ tempRGB[i*RW*bytes+j*bytes+2]=src_row[whichcol*bytes+2];
+
+ /* Hold on to the alpha as well */
+ if (bytes==4)
+ tempRGB[i*RW*bytes+j*bytes+3]=src_row[whichcol*bytes+3];
+ }
+ }
+ temp->bpp=bytes;
+ temp->width=RW;
+ temp->height=RH;
+ temp->rgb=tempRGB;
+ temp->mask=tempmask;
+ return temp;
+}
+
+The following is a preview function which used the same ReducedImage type!
+Note that it uses fakes transparency (if one is present by means of
+fake_transparency which is defined as follows:
+
+gint fake_transparency(gint i, gint j)
+{
+ if ( ((i%20)- 10) * ((j%20)- 10)>0 )
+ return 64;
+ else
+ return 196;
+}
+
+Now here's the preview function:
+
+void
+my_preview_render_function(GtkWidget *preview,
+ gint changewhat,
+ gint changewhich)
+{
+ gint Inten, bytes=drawable->bpp;
+ gint i, j, k;
+ float partial;
+ gint RW=reduced->width;
+ gint RH=reduced->height;
+ guchar *row=malloc(bytes*RW);;
+
+
+ for (i=0; i < RH; i++) {
+ for (j=0; j < RW; j++) {
+
+ row[j*3+0] = reduced->rgb[i*RW*bytes + j*bytes + 0];
+ row[j*3+1] = reduced->rgb[i*RW*bytes + j*bytes + 1];
+ row[j*3+2] = reduced->rgb[i*RW*bytes + j*bytes + 2];
+
+ if (bytes==4)
+ for (k=0; k<3; k++) {
+ float transp=reduced->rgb[i*RW*bytes+j*bytes+3]/255.0;
+ row[3*j+k]=transp*a[3*j+k]+(1-transp)*fake_transparency(i,j);
+ }
+ }
+ gtk_preview_draw_row( GTK_PREVIEW(preview),row,0,i,RW);
+ }
+
+ free(a);
+ gtk_widget_draw(preview,NULL);
+ gdk_flush();
+}
+
+Applicable Routines
+
+guint gtk_preview_get_type (void);
+/* No idea */
+void gtk_preview_uninit (void);
+/* No idea */
+GtkWidget* gtk_preview_new (GtkPreviewType type);
+/* Described above */
+void gtk_preview_size (GtkPreview *preview,
+ gint width,
+ gint height);
+/* Allows you to resize an existing preview. */
+/* Apparently there's a bug in GTK which makes */
+/* this process messy. A way to clean up a mess */
+/* is to manually resize the window containing */
+/* the preview after resizing the preview. */
+
+void gtk_preview_put (GtkPreview *preview,
+ GdkWindow *ventana,
+ GdkGC *gc,
+ gint srcx,
+ gint srcy,
+ gint destx,
+ gint desty,
+ gint width,
+ gint height);
+/* No idea */
+
+void gtk_preview_put_row (GtkPreview *preview,
+ guchar *src,
+ guchar *dest,
+ gint x,
+ gint y,
+ gint w);
+/* No idea */
+
+void gtk_preview_draw_row (GtkPreview *preview,
+ guchar *data,
+ gint x,
+ gint y,
+ gint w);
+/* Described in the text */
+
+void gtk_preview_set_expand (GtkPreview *preview,
+ gint expand);
+/* No idea */
+
+/* No clue for any of the below but */
+/* should be standard for most widgets */
+void gtk_preview_set_gamma (double gamma);
+void gtk_preview_set_color_cube (guint nred_shades,
+ guint ngreen_shades,
+ guint nblue_shades,
+ guint ngray_shades);
+void gtk_preview_set_install_cmap (gint install_cmap);
+void gtk_preview_set_reserved (gint nreserved);
+GdkVisual* gtk_preview_get_visual (void);
+GdkColormap* gtk_preview_get_cmap (void);
+GtkPreviewInfo* gtk_preview_get_info (void);
+
+That's all, folks!
+
+</verb></tscreen>
+
+-->
+
+<!-- ***************************************************************** -->
+<sect>Estableciendo los atributos de un <em/widget/<label id="sec_setting_widget_attributes">
+<!-- ***************************************************************** -->
+<p>
+En este capítulo se describen las funciones utilizadas para manejar los
+<em/widgets/. Pueden utilizarse para establecer el estilo, relleno,
+tamaño, etc...
+
+(Puede que deba hacer una sección completa para los aceleradores.)
+
+<tscreen><verb>
+void gtk_widget_install_accelerator( GtkWidget *widget,
+ GtkAcceleratorTable *table,
+ gchar *signal_name,
+ gchar key,
+ guint8 modifiers );
+
+void gtk_widget_remove_accelerator ( GtkWidget *widget,
+ GtkAcceleratorTable *table,
+ gchar *signal_name);
+
+void gtk_widget_activate( GtkWidget *widget );
+
+void gtk_widget_set_name( GtkWidget *widget,
+ gchar *name );
+
+gchar *gtk_widget_get_name( GtkWidget *widget );
+
+void gtk_widget_set_sensitive( GtkWidget *widget,
+ gint sensitive );
+
+void gtk_widget_set_style( GtkWidget *widget,
+ GtkStyle *style );
+
+GtkStyle *gtk_widget_get_style( GtkWidget *widget );
+
+GtkStyle *gtk_widget_get_default_style( void );
+
+void gtk_widget_set_uposition( GtkWidget *widget,
+ gint x,
+ gint y );
+
+void gtk_widget_set_usize( GtkWidget *widget,
+ gint width,
+ gint height );
+
+void gtk_widget_grab_focus( GtkWidget *widget );
+
+void gtk_widget_show( GtkWidget *widget );
+
+void gtk_widget_hide( GtkWidget *widget );
+</verb></tscreen>
+
+<!-- ***************************************************************** -->
+<sect>Tiempos de espera, ES (<em/IO/) y funciones ociosas (<em/idle/)<label id="sec_timeouts">
+<!-- ***************************************************************** -->
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Tiempos de espera
+<p>
+Puede que se esté preguntando como hacer que GTK haga algo útil
+cuando se encuentra en <tt/gtk_main/. Bien, tiene varias
+opciones. Utilizando las rutinas siguientes puede crear una función
+a la que se llamará cada <tt/interval/ milisegundos.
+
+<tscreen><verb>
+gint gtk_timeout_add( guint32 interval,
+ GtkFunction function,
+ gpointer data );
+</verb></tscreen>
+
+El primer argumento es el número de milisegundos que habrá entre dos
+llamadas a su función. El segundo argumento es la función a la que
+desea llamar, y el tercero, los datos que le pasará a ésta función.
+El valor devuelto es un «identificador» (un valor entero) que puede
+utilizar para detener las llamadas haciendo:
+
+<tscreen><verb>
+void gtk_timeout_remove( gint tag );
+</verb></tscreen>
+
+También puede hacer que cesen las llamadas a la función haciendo que
+la misma devuelva cero o FALSE. Obviamente esto significa que si
+quiere que se continue llamando a su función, deberá devolver un valor
+distinto de cero, es decir TRUE.
+
+La declaración de su función debería ser algo como:
+
+<tscreen><verb>
+gint timeout_callback( gpointer data );
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Monitorizando la ES
+<p>
+Otra característica divertida de GTK, es la habilidad que tiene de
+comprobar datos por usted en un descriptor de fichero (tal y como
+se devuelven por <tt/open(2)/ o <tt/socket(2)/). Esto es especialmente
+útil para las aplicaciones de red. La función:
+
+<tscreen><verb>
+gint gdk_input_add( gint source,
+ GdkInputCondition condition,
+ GdkInputFunction function,
+ gpointer data );
+</verb></tscreen>
+
+Donde el primer argumento es el descriptor de fichero que desea vigilar,
+y el segundo especifica que es lo que quiere que GDK busque. Puede ser uno
+de los siguientes:
+
+<itemize>
+<item>GDK_INPUT_READ - Llama a su función cuando hay datos listos para
+leerse del fichero.
+
+<item>GDK_INPUT_WRITE - Llama a su función cuando el descriptor del
+fichero está listo para la escritura.
+</itemize>
+
+Tal y como se habrá imaginado, el tercer argumento es la función a la
+que desea que se llame cuando se den las condiciones anteriores, y el
+cuarto son los datos que se le pasarán a ésta función.
+
+El valor devuelto es un identificador que puede utilizarse para que GDK
+pare de vigilar ese fichero, utilizando la función
+
+<tscreen><verb>
+void gdk_input_remove( gint tag );
+</verb></tscreen>
+
+La función a la que quiere que se llame deberá declararse así:
+
+<tscreen><verb>
+void input_callback( gpointer data,
+ gint source,
+ GdkInputCondition condition );
+</verb></tscreen>
+
+Donde <tt/source/ y <tt/condition/ están especificados más arriba.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Funciones ociosas
+<p>
+<!-- Need to check on idle priorities - TRG -->
+¿Qué le parece si tuviese una función a la que se llamase cuando
+no ocurriese nada?
+
+<tscreen><verb>
+gint gtk_idle_add( GtkFunction function,
+ gpointer data );
+</verb></tscreen>
+
+Esto hace que GTK llame a la función especificada cuando no ocurra
+nada más.
+
+<tscreen><verb>
+void gtk_idle_remove( gint tag );
+</verb></tscreen>
+
+No voy a explicar el significado de los argumentos ya que se parece
+mucho a los que he explicado más arriba. La función a la que se apunta
+mediante el primer argumento de <tt/gtk_idle_add/ será a la que se
+llame cuando llegue el momento. Como antes, si devuelve FALSE hará que
+cese de llamarse a la función.
+
+<!-- ***************************************************************** -->
+<sect>Manejo avanzado de eventos y señales<label id="sec_Adv_Events_and_Signals">
+<!-- ***************************************************************** -->
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Funciones señal
+
+<!-- ----------------------------------------------------------------- -->
+<sect2>Conectando y desconectando los manejadores de señal
+<p>
+
+<tscreen><verb>
+guint gtk_signal_connect( GtkObject *object,
+ const gchar *name,
+ GtkSignalFunc func,
+ gpointer func_data );
+
+guint gtk_signal_connect_after( GtkObject *object,
+ const gchar *name,
+ GtkSignalFunc func,
+ gpointer func_data );
+
+guint gtk_signal_connect_object( GtkObject *object,
+ const gchar *name,
+ GtkSignalFunc func,
+ GtkObject *slot_object );
+
+guint gtk_signal_connect_object_after( GtkObject *object,
+ const gchar *name,
+ GtkSignalFunc func,
+ GtkObject *slot_object );
+
+guint gtk_signal_connect_full( GtkObject *object,
+ const gchar *name,
+ GtkSignalFunc func,
+ GtkCallbackMarshal marshal,
+ gpointer data,
+ GtkDestroyNotify destroy_func,
+ gint object_signal,
+ gint after );
+
+guint gtk_signal_connect_interp( GtkObject *object,
+ const gchar *name,
+ GtkCallbackMarshal func,
+ gpointer data,
+ GtkDestroyNotify destroy_func,
+ gint after );
+
+void gtk_signal_connect_object_while_alive( GtkObject *object,
+ const gchar *signal,
+ GtkSignalFunc func,
+ GtkObject *alive_object );
+
+void gtk_signal_connect_while_alive( GtkObject *object,
+ const gchar *signal,
+ GtkSignalFunc func,
+ gpointer func_data,
+ GtkObject *alive_object );
+
+void gtk_signal_disconnect( GtkObject *object,
+ guint handler_id );
+
+void gtk_signal_disconnect_by_func( GtkObject *object,
+ GtkSignalFunc func,
+ gpointer data );
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect2>Bloqueando y desbloqueando los manejadores de señal
+<p>
+<tscreen><verb>
+void gtk_signal_handler_block( GtkObject *object,
+ guint handler_id);
+
+void gtk_signal_handler_block_by_func( GtkObject *object,
+ GtkSignalFunc func,
+ gpointer data );
+
+void gtk_signal_handler_block_by_data( GtkObject *object,
+ gpointer data );
+
+void gtk_signal_handler_unblock( GtkObject *object,
+ guint handler_id );
+
+void gtk_signal_handler_unblock_by_func( GtkObject *object,
+ GtkSignalFunc func,
+ gpointer data );
+
+void gtk_signal_handler_unblock_by_data( GtkObject *object,
+ gpointer data );
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect2>Emitiendo y deteniendo señales
+<p>
+<tscreen><verb>
+void gtk_signal_emit( GtkObject *object,
+ guint signal_id,
+ ... );
+
+void gtk_signal_emit_by_name( GtkObject *object,
+ const gchar *name,
+ ... );
+
+void gtk_signal_emitv( GtkObject *object,
+ guint signal_id,
+ GtkArg *params );
+
+void gtk_signal_emitv_by_name( GtkObject *object,
+ const gchar *name,
+ GtkArg *params );
+
+guint gtk_signal_n_emissions( GtkObject *object,
+ guint signal_id );
+
+guint gtk_signal_n_emissions_by_name( GtkObject *object,
+ const gchar *name );
+
+void gtk_signal_emit_stop( GtkObject *object,
+ guint signal_id );
+
+void gtk_signal_emit_stop_by_name( GtkObject *object,
+ const gchar *name );
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Emisión y propagación de señales
+<p>
+La emisión de señales es el proceso mediante el cual GTK+ ejecuta
+todos los manejadores de un objeto y una señal en especial.
+
+Primero, observe que el valor devuelto por la emisión de una
+señal es el mismo que el valor devuelto por el <em>último</em>
+manipulador ejecutado. Ya que las señales de los eventos son todas
+del tipo GTK_RUN_LAST, el manejador por defecto (proporcionado por
+GTK+) será de este tipo, a menos que lo conecte con
+<tt/gtk_signal_connect_after()/.
+
+La forma en que se maneja un evento (digamos GTK_BUTTON_PRESS), es la
+siguiente:
+
+<itemize>
+<item>Empieza con el <em>widget</em> donde ocurrió el evento.
+
+<item>Emite la señal genérica <tt/event/. Si esta señal devuelve un
+valor TRUE, detiene todo el proceso.
+
+<item>En caso contrario, emite una señal especifica,
+«button_press_event» en nuestro caso. Si ésta devuelve TRUE, detiene
+todo el proceso.
+
+<item>En caso contrario, va al <em>widget</em> padre y repite los
+pasos anteriores.
+
+<item>Continua hasta que algún manejador de señal devuelva TRUE, o
+hasta que se llegue al <em>widget</em> de más alto nivel.
+</itemize>
+
+Algunas consecuencias son:
+<itemize>
+<item>El valor que devuelva su manejador no tendrá ningún efecto si
+hay un manejador por defecto, a menos que lo conecte mediante
+<tt/gtk_signal_connect_after()/.
+
+<item>Para evitar que el manejador por defecto se ejecute, necesita
+conectar mediante <tt/gtk_signal_connect()/ y utilizar
+<tt/gtk_signal_emit_stop_by_name()/ - el valor devuelto sólo se ve
+afectado si la señal se propaga, y no sólo por el hecho de emitirse.
+</itemize>
+
+<!-- ***************************************************************** -->
+<sect>Manejando selecciones
+<!-- ***************************************************************** -->
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Contenido
+<p>
+Un tipo de comunicación entre procesos que se puede utilizar con GTK
+son las <em/selecciones/. Una selección identifica un conjunto de
+datos, por ejemplo, un trozo de texto, seleccionado por el usuario de
+alguna manera, por ejemplo, cogiéndolo con el ratón. Sólo una
+aplicación en un <em/display/ (la <em>propietaria</em>) puede tener
+una selección en particular en un momento dado, por lo que cuando una
+aplicación pide una selección, el propietario previo debe indicar al
+usuario que la selección ya no es válida. Otras aplicaciones pueden
+pedir el contenido de la selección de diferentes formas, llamadas
+<em/objetivos/. Puede haber cualquier número de selecciones, pero la
+mayoría de las aplicacion X sólo pueden manejar una, la <em/selección
+primaria/.
+
+En muchos casos, no es necesario para una aplicación GTK tratar por
+sí misma con las selecciones. Los <em/widgets/ estándar, como el
+<em/widget/ Entry, ya tienen la posibilidad de crear la selección
+cuando sea necesario (p.e., cuando el usuario pase el ratón sobre el
+texto manteniendo el botón derecho del ratón pulsado), y de recoger
+los contenidos de la selección propiedad de otro <em/widget/, o de
+otra aplicación (p.e., cuando el usuario pulsa el segundo botón del
+ratón). Sin embargo, pueden haber casos en los que quiera darle a
+otros <em/widgets/ la posibilidad de proporcionar la selección, o
+puede que quiera recuperar objetivos que no estén admitidos por
+defecto.
+
+Un concepto fundamental que es necesario para comprender el manejo de
+la selección es el de <em>átomo</em>. Un átomo es un entero que
+identifica de una manera unívoca una cadena (en un cierto
+<em/display/). Ciertos átomos están predefinidos por el servidor X, y
+en algunos casos hay constantes en <tt>gtk.h</tt> que corresponden a
+estos átomos. Por ejemplo la constante <tt>GDK_PRIMARY_SELECTION</tt>
+corresponde a la cadena «PRIMARY». En otros casos, debería utilizar
+las funciones <tt>gdk_atom_intern()</tt>, para obtener el átomo
+correspondiente a una cadena, y <tt>gdk_atom_name()</tt>, para obtener
+el nombre de un átomo. Ambas, selecciones y objetivos, están
+identificados por átomos.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Recuperando la selección
+<p>
+Recuperar la selección es un proceso asíncrono. Para comenzar el
+proceso, deberá llamar a:
+
+<tscreen><verb>
+gint gtk_selection_convert( GtkWidget *widget,
+ GdkAtom selection,
+ GdkAtom target,
+ guint32 time );
+</verb</tscreen>
+
+Este proceso <em/convierte/ la selección en la forma especificada por
+<tt/target/. Si es posible, el campo <tt/time/ debe ser el tiempo
+desde que el evento lanzó la selección. Esto ayuda a asegurarse de que
+los eventos ocurran en el orden en que el usuario los ha pedido. Sin
+embargo, si no está disponible (por ejemplo, si se empezó la
+conversión por una señal de «pulsación»), entonces puede utilizar la
+constante <tt>GDK_CURRENT_TIME</tt>.
+
+Cuando el propietario de la selección responda a la petición, se
+enviará una señal «selection_received» a su aplicación. El manejador
+de esta señal recibe un puntero a una estructura
+<tt>GtkSelectionData</tt>, que se define como:
+
+<tscreen><verb>
+struct _GtkSelectionData
+{
+ GdkAtom selection;
+ GdkAtom target;
+ GdkAtom type;
+ gint format;
+ guchar *data;
+ gint length;
+};
+</verb></tscreen>
+
+<tt>selection</tt> y <tt>target</tt> son los valores que dió en su
+llamada a <tt>gtk_selection_convert()</tt>. <tt>type</tt> es un átomo
+que identifica el tipo de datos devueltos por el propietario de la
+selección. Algunos valores posibles son «STRING», un cadena de
+caracteres latin-1, «ATOM», una serie de átomos, «INTEGER», un
+entero, etc. Muchos objetivos sólo pueden devolver un
+tipo. <tt/format/ da la longitud de las unidades (por ejemplo
+caracteres) en bits. Normalmente, no tiene porque preocuparse de todo
+esto cuando recibe datos. <tt/data/ es un puntero a los datos
+devueltos, y <tt/length/ da la longitud de los datos devueltos, en
+bytes. Si <tt/length/ es negativo, es que a ocurrido un error y no se
+puede obtener la selección. Esto podría ocurrir si no hay ninguna
+aplicación que sea la propietaria de la selección, o si pide un
+objetivo que la aplicación no admite. Actualmente se garantiza que el
+búfer tendrá un byte más que <tt/length/; el byte extra siempre será
+cero, por lo que no es necesario hacer una copia de las cadenas sólo
+para añadirles un carácter nulo al final.
+
+En el siguiente ejemplo, recuperamos el objetivo especial «TARGETS»,
+que es una lista de todos los objetivos en los que se puede convertir
+la selección.
+
+<tscreen><verb>
+/* principio del ejemplo selection gettargets.c */
+
+#include <gtk/gtk.h>
+
+void selection_received (GtkWidget *widget,
+ GtkSelectionData *selection_data,
+ gpointer data);
+
+/* Manejador de señal invocado cuando el usuario pulsa en el botón
+"Get Targets" */
+void
+get_targets (GtkWidget *widget, gpointer data)
+{
+ static GdkAtom targets_atom = GDK_NONE;
+
+ /* Obtener el atom correpondiente a la cadena "TARGETS" */
+ if (targets_atom == GDK_NONE)
+ targets_atom = gdk_atom_intern ("TARGETS", FALSE);
+
+ /* Y pide el objetivo "TARGETS" de la selección primaria */
+ gtk_selection_convert (widget, GDK_SELECTION_PRIMARY, targets_atom,
+ GDK_CURRENT_TIME);
+}
+
+/* Manipulador de señal llamado cuando el propietario de la señal
+ * devuelve los datos */
+void
+selection_received (GtkWidget *widget, GtkSelectionData *selection_data,
+ gpointer data)
+{
+ GdkAtom *atoms;
+ GList *item_list;
+ int i;
+
+ /* **** IMPORTANTE **** Comprobar si se da la recuperación de los
+ * datos */
+ if (selection_data->length < 0)
+ {
+ g_print ("Selection retrieval failed\n");
+ return;
+ }
+ /* Asegurarse de que obtenemos los datos de la forma esperada */
+ if (selection_data->type != GDK_SELECTION_TYPE_ATOM)
+ {
+ g_print ("Selection \"TARGETS\" was not returned as atoms!\n");
+ return;
+ }
+
+ /* Imprimir los atoms que hemos recibido */
+ atoms = (GdkAtom *)selection_data->data;
+
+ item_list = NULL;
+ for (i=0; i<selection_data->length/sizeof(GdkAtom); i++)
+ {
+ char *name;
+ name = gdk_atom_name (atoms[i]);
+ if (name != NULL)
+ g_print ("%s\n",name);
+ else
+ g_print ("(bad atom)\n");
+ }
+
+ return;
+}
+
+int
+main (int argc, char *argv[])
+{
+ GtkWidget *ventana;
+ GtkWidget *boton;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ /* Crear la ventana superior */
+
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title (GTK_WINDOW (ventana), "Event Box");
+ gtk_container_border_width (GTK_CONTAINER (ventana), 10);
+
+ gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
+ GTK_SIGNAL_FUNC (gtk_exit), NULL);
+
+ /* Crear un botón que el usuario puede pulsar para obtener los
+ * objetivos */
+
+ boton = gtk_button_new_with_label ("Get Targets");
+ gtk_container_add (GTK_CONTAINER (ventana), boton);
+
+ gtk_signal_connect (GTK_OBJECT(boton), "clicked",
+ GTK_SIGNAL_FUNC (get_targets), NULL);
+ gtk_signal_connect (GTK_OBJECT(boton), "selection_received",
+ GTK_SIGNAL_FUNC (selection_received), NULL);
+
+ gtk_widget_show (boton);
+ gtk_widget_show (ventana);
+
+ gtk_main ();
+
+ return 0;
+}
+/* fin del ejemplo */
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Proporcionando la selección
+<p>
+Proporcionar la selección es un poco más complicado. Debe registrar
+los manejadores a los que se llamarán cuando se le pida la
+selección. Por cada par selección/objetivo que quiera manejar, deberá
+hacer una llamada a:
+
+<tscreen><verb>
+void gtk_selection_add_handler( GtkWidget *widget,
+ GdkAtom selection,
+ GdkAtom target,
+ GtkSelectionFunction function,
+ GtkRemoveFunction remove_func,
+ gpointer data );
+</verb></tscreen>
+
+<tt/widget/, <tt/selection/, y <tt/target/ identifican las peticiones
+que este manejador puede manipular. Si <tt/remove_func/ no es NULL, se
+le llamará cuando se elimine el manejador de la señal. Esto es útil,
+por ejemplo, para los lenguajes interpretados que necesitan mantener
+una memoria de las referencias a <tt/data/.
+
+La función de llamada tiene el prototipo:
+
+<tscreen><verb>
+typedef void (*GtkSelectionFunction)( GtkWidget *widget,
+ GtkSelectionData *selection_data,
+ gpointer data );
+
+</verb></tscreen>
+
+El <tt/GtkSelectionData/ es el mismo que hay más arriba, pero esta
+vez, seremos nosotros los responsables de rellenar los campos
+<tt/type/, <tt/format/, <tt/data/, y <tt/length/. (El campo
+<tt/format/ es importante - el servidor X lo utiliza para saber si
+tienen que intercambiarse los bytes que forman los datos o
+no. Normalmente será 8 - es decir un carácter - o 32 - es decir un
+entero.) Esto se hace llamando a la función:
+
+<tscreen><verb>
+void gtk_selection_data_set( GtkSelectionData *selection_data,
+ GdkAtom type,
+ gint format,
+ guchar *data,
+ gint length );
+</verb></tscreen>
+
+Esta función tiene la responsabilidad de hacer una copia de los datos
+para que no tenga que preocuparse de ir guardándolos. (No debería
+rellenar los campos de la estructura <tt/GtkSelectionData/ a mano.)
+
+Cuando haga falta, puede pedir el propietario de la selección llamando
+a:
+
+<tscreen><verb>
+gint gtk_selection_owner_set( GtkWidget *widget,
+ GdkAtom selection,
+ guint32 time );
+</verb></tscreen>
+
+Si otra aplicación pide el propietario de la selección, recibira un
+«selection_clear_event».
+
+Como ejemplo de proporciar la selección, el programa siguiente le añade
+la posibilidad de selección a un botón de comprobación. Cuando se presione
+el botón de comprobación, el programa pedirá la selección primaria. El
+único objetivo que admite es un objetivo «STRING» (aparte de ciertos
+objetivos como "TARGETS", proporcionados por GTK). Cuando se pida este
+objetivo, se devolverá una representación del tiempo.
+
+<tscreen><verb>
+/* principio del ejemplo selection setselection.c */
+
+#include <gtk/gtk.h>
+#include <time.h>
+
+/* Función de llamada para cuando el usuario cambia la selección */
+void
+selection_toggled (GtkWidget *widget, gint *have_selection)
+{
+ if (GTK_TOGGLE_BUTTON(widget)->active)
+ {
+ *have_selection = gtk_selection_owner_set (widget,
+ GDK_SELECTION_PRIMARY,
+ GDK_CURRENT_TIME);
+ /* Si la demanda de la selección ha fallado, ponemos el botón en
+ * estado apagado */
+ if (!*have_selection)
+ gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON(widget), FALSE);
+ }
+ else
+ {
+ if (*have_selection)
+ {
+ /* Antes de eliminar la seleción poniendo el propietario a
+ * NULL, comprobamos si somos el propietario actual */
+ if (gdk_selection_owner_get (GDK_SELECTION_PRIMARY) == widget->window)
+ gtk_selection_owner_set (NULL, GDK_SELECTION_PRIMARY,
+ GDK_CURRENT_TIME);
+ *have_selection = FALSE;
+ }
+ }
+}
+
+/* Llamado cuando otra aplicación pide la selección */
+gint
+selection_clear (GtkWidget *widget, GdkEventSelection *event,
+ gint *have_selection)
+{
+ *have_selection = FALSE;
+ gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON(widget), FALSE);
+
+ return TRUE;
+}
+
+/* Proporciona el tiempo actual como selección. */
+void
+selection_handle (GtkWidget *widget,
+ GtkSelectionData *selection_data,
+ gpointer data)
+{
+ gchar *timestr;
+ time_t current_time;
+
+ current_time = time (NULL);
+ timestr = asctime (localtime(&amp;current_time));
+ /* Cuando devolvemos una cadena, no debe terminar en NULL. La
+ * función lo hará por nosotros */
+
+ gtk_selection_data_set (selection_data, GDK_SELECTION_TYPE_STRING,
+ 8, timestr, strlen(timestr));
+}
+
+int
+main (int argc, char *argv[])
+{
+ GtkWidget *ventana;
+
+ GtkWidget *selection_button;
+
+ static int have_selection = FALSE;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ /* Crear la ventana superior */
+
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title (GTK_WINDOW (ventana), "Event Box");
+ gtk_container_border_width (GTK_CONTAINER (ventana), 10);
+
+ gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
+ GTK_SIGNAL_FUNC (gtk_exit), NULL);
+
+ /* Crear un botón de selección para que actue como la selección */
+
+ selection_button = gtk_toggle_button_new_with_label ("Claim Selection");
+ gtk_container_add (GTK_CONTAINER (ventana), selection_button);
+ gtk_widget_show (selection_button);
+
+ gtk_signal_connect (GTK_OBJECT(selection_button), "toggled",
+ GTK_SIGNAL_FUNC (selection_toggled), &amp;have_selection);
+ gtk_signal_connect (GTK_OBJECT(selection_button), "selection_clear_event",
+ GTK_SIGNAL_FUNC (selection_clear), &amp;have_selection);
+
+ gtk_selection_add_handler (selection_button, GDK_SELECTION_PRIMARY,
+ GDK_SELECTION_TYPE_STRING,
+ selection_handle, NULL);
+
+ gtk_widget_show (selection_button);
+ gtk_widget_show (ventana);
+
+ gtk_main ();
+
+ return 0;
+}
+/* fin del ejemplo */
+</verb></tscreen>
+
+
+<!-- ***************************************************************** -->
+<sect>Glib<label id="sec_glib">
+<!-- ***************************************************************** -->
+<p>
+Glib proporciona muchas definiciones y funciones útiles disponibles
+para su utilización cuando se crean aplicaciones GDK y GTK. Haré una
+lista con todas ellas incluyendo una pequeña explicación. Muchas no
+son más que duplicados de funciones estándar de libc por lo que no
+entraré en detalle en la explicación de las mismas. Esta sección está
+pensada principalmente para que se utilice como referencia, para que
+sepa que es lo que puede utilizar.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Definiciones
+<p>
+Las definiciones para los límites de muchos de los tipos estándar son:
+
+<tscreen><verb>
+G_MINFLOAT
+G_MAXFLOAT
+G_MINDOUBLE
+G_MAXDOUBLE
+G_MINSHORT
+G_MAXSHORT
+G_MININT
+G_MAXINT
+G_MINLONG
+G_MAXLONG
+</verb></tscreen>
+
+Y también, los siguientes <tt/typedefs/. Cuando no se especifica el
+tipo que debería aparecer a la izquierda significa que el mismo se
+establecerá dinámicamente en función de la arquitectura. ¡Recuerde
+evitar los calculos relativos al tamaño de un puntero si quiere que
+su aplicación sea portable! P.e., un puntero en un Alpha tiene 8
+bytes, pero 4 en Intel.
+
+<tscreen><verb>
+char gchar;
+short gshort;
+long glong;
+int gint;
+char gboolean;
+
+unsigned char guchar;
+unsigned short gushort;
+unsigned long gulong;
+unsigned int guint;
+
+float gfloat;
+double gdouble;
+long double gldouble;
+
+void* gpointer;
+
+gint8
+guint8
+gint16
+guint16
+gint32
+guint32
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Listas doblemente enlazadas
+<p>
+Las funciones siguientes se utilizan para crear, manipular, y destruir
+listas doblemente enlazadas. Supondré que sabe lo que son las listas
+enlazadas, ya que explicarlas va más allá del objetivo de este
+documento. Por supuesto, no es necesario que conozca como manejar
+todo esto para utilizar GTK, pero siempre es bonito aprender cosas.
+
+<tscreen><verb>
+GList *g_list_alloc( void );
+
+void g_list_free( GList *list );
+
+void g_list_free_1( GList *list );
+
+GList *g_list_append( GList *list,
+ gpointer data );
+
+GList *g_list_prepend( GList *list,
+ gpointer data );
+
+GList *g_list_insert( GList *list,
+ gpointer data,
+ gint posicion );
+
+GList *g_list_remove( GList *list,
+ gpointer data );
+
+GList *g_list_remove_link( GList *list,
+ GList *link );
+
+GList *g_list_reverse( GList *list );
+
+GList *g_list_nth( GList *list,
+ gint n );
+
+GList *g_list_find( GList *list,
+ gpointer data );
+
+GList *g_list_last( GList *list );
+
+GList *g_list_first( GList *list );
+
+gint g_list_length( GList *list );
+
+void g_list_foreach( GList *list,
+ GFunc func,
+ gpointer user_data );
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Listas simplemente enlazadas
+<p>
+Muchas de las funciones para las listas enlazadas son idénticas a las
+de más arriba. Aquí hay una lista completa:
+
+<tscreen><verb>
+GSList *g_slist_alloc( void );
+
+void g_slist_free( GSList *list );
+
+void g_slist_free_1( GSList *list );
+
+GSList *g_slist_append( GSList *list,
+ gpointer data );
+
+GSList *g_slist_prepend( GSList *list,
+ gpointer data );
+
+GSList *g_slist_insert( GSList *list,
+ gpointer data,
+ gint posicion );
+
+GSList *g_slist_remove( GSList *list,
+ gpointer data );
+
+GSList *g_slist_remove_link( GSList *list,
+ GSList *link );
+
+GSList *g_slist_reverse( GSList *list );
+
+GSList *g_slist_nth( GSList *list,
+ gint n );
+
+GSList *g_slist_find( GSList *list,
+ gpointer data );
+
+GSList *g_slist_last( GSList *list );
+
+gint g_slist_length( GSList *list );
+
+void g_slist_foreach( GSList *list,
+ GFunc func,
+ gpointer user_data );
+
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Control de la memoria
+<p>
+<tscreen><verb>
+gpointer g_malloc( gulong size );
+</verb></tscreen>
+
+Reemplaza a <tt/malloc()/. No necesita comprobar el valor devuelto, ya
+que ya lo hace por usted esta función.
+
+<tscreen><verb>
+gpointer g_malloc0( gulong size );
+</verb></tscreen>
+
+Lo mismo que antes, pero rellena con ceros la memoria antes de
+devolver un puntero a ella.
+
+<tscreen><verb>
+gpointer g_realloc( gpointer mem,
+ gulong size );
+</verb></tscreen>
+
+Vuelve a reservar <tt/size/ bites de memoria empezando en
+<tt/mem/. Obviamente, la memoria debe haber sido previamente reservada.
+
+<tscreen><verb>
+void g_free( gpointer mem );
+</verb></tscreen>
+
+Libera la memoria. Fácil.
+
+<tscreen><verb>
+void g_mem_profile( void );
+</verb></tscreen>
+
+Crea un fichero donde vuelca la memoria que se está utilizando, pero
+tiene que añadir <tt/#define MEM_PROFILE/ en lo alto de
+<tt>glib/gmem.c</tt> y tendrá que hacer un make y un make install.
+
+<tscreen><verb>
+void g_mem_check( gpointer mem );
+</verb></tscreen>
+
+Comprueba que una dirección de memoria es válida. Tiene que añadir
+<tt/#define MEM_CHECK/ en lo alto de <tt/gmem.c/ y tiene que hacer un
+make y un make install.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Timers
+<p>
+Temporizadores...
+
+<tscreen><verb>
+GTimer *g_timer_new( void );
+
+void g_timer_destroy( GTimer *timer );
+
+void g_timer_start( GTimer *timer );
+
+void g_timer_stop( GTimer *timer );
+
+void g_timer_reset( GTimer *timer );
+
+gdouble g_timer_elapsed( GTimer *timer,
+ gulong *microseconds );
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Manejo de cadenas de texto
+<p>
+Un puñado de funciones para manejar cadenas de texto. Parecen muy
+interesantes, y probablemente sean mejores en muchos aspectos que las
+funciones estándar de C, pero necesitan documentación.
+
+<tscreen><verb>
+GString *g_string_new( gchar *init );
+
+void g_string_free( GString *string,
+ gint free_segment );
+
+GString *g_string_assign( GString *lval,
+ gchar *rval );
+
+GString *g_string_truncate( GString *string,
+ gint len );
+
+GString *g_string_append( GString *string,
+ gchar *val );
+
+GString *g_string_append_c( GString *string,
+ gchar c );
+
+GString *g_string_prepend( GString *string,
+ gchar *val );
+
+GString *g_string_prepend_c( GString *string,
+ gchar c );
+
+void g_string_sprintf( GString *string,
+ gchar *fmt,
+ ...);
+
+void g_string_sprintfa ( GString *string,
+ gchar *fmt,
+ ... );
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Funciones de error y funciones varias
+<p>
+<tscreen><verb>
+gchar *g_strdup( const gchar *str );
+</verb></tscreen>
+
+Reemplaza a la función <tt/strdup/. Copia el contenido de la cadena
+original en un nuevo lugar en memoria, y devuelve un puntero al nuevo
+lugar.
+
+<tscreen><verb>
+gchar *g_strerror( gint errnum );
+</verb></tscreen>
+
+Recomiendo utilizar esta función para todos los mensages de error. Es
+mucho más bonita, y más portable que <tt/perror()/ y demás funciones
+clásicas. La salida es normalmente de la forma:
+
+<tscreen><verb>
+nombre del programa:función que falló:fichero o descripción adicional:strerror
+</verb></tscreen>
+
+Aquí hay un ejemplo de una llamada utilizada en nuestro programa
+<tt/hello_world/:
+
+<tscreen><verb>
+g_print("hello_world:open:%s:%s\n", filename, g_strerror(errno));
+</verb></tscreen>
+
+<tscreen><verb>
+void g_error( gchar *format, ... );
+</verb></tscreen>
+
+Imprime un mensaje de error. El formato es como el de <tt/printf/,
+pero le añade <tt/** ERROR **: / a su mensaje, y sale del
+programa. Sólo para errores fatales.
+
+<tscreen><verb>
+void g_warning( gchar *format, ... );
+</verb></tscreen>
+
+El mismo que el anterior, pero añade "** WARNING **: ", y no sale del
+programa.
+
+<tscreen><verb>
+void g_message( gchar *format, ... );
+</verb></tscreen>
+
+Imprime <tt/message: / antes de la cadena que le pase.
+
+<tscreen><verb>
+void g_print( gchar *format, ... );
+</verb></tscreen>
+
+Reemplazo de <tt/printf()/.
+
+Y nuestra última función:
+
+<tscreen><verb>
+gchar *g_strsignal( gint signum );
+</verb></tscreen>
+
+Imprime el nombre de la señal del sistema Unix que corresponde con el
+número <tt/signum/. Útil para las funciones genéricas de manejo de señal.
+
+Todo lo anterior está más o menos robado de <tt/glib.h/. Si alguien
+quiere documentar una función, ¡sólo tiene que enviarme un correo-e!
+
+<!-- ***************************************************************** -->
+<sect>Ficheros rc de GTK
+<!-- ***************************************************************** -->
+<p>
+GTK tiene su propia forma de conseguir los valores por defecto de una
+aplicación, y es utilizando los ficheros <tt/rc/. Pueden ser
+utilizados para poner los colores de cualquier <em/widget/, y también
+pueden utilizarse para poner imágenes como fondos de algunos <em/widgets/.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Funciones para los ficheros <tt/rc/
+<p>
+Cuando empiece su aplicación, debería incluir una llamada a:
+
+<tscreen><verb>
+void gtk_rc_parse( char *filename );
+</verb></tscreen>
+
+Poniendo el nombre del fichero de su rc. Esto hará que GTK analice
+este fichero, y utilice el estilo para los <em/widgets/ que se definan
+ahí.
+
+Si desea tener un conjunto especial de <em/widgets/ con un estilo
+diferente de los otros, o realizar cualquier otra división lógica de
+los <em/widgets/, haga una llamada a:
+
+<tscreen><verb>
+void gtk_widget_set_name( GtkWidget *widget,
+ gchar *name );
+</verb></tscreen>
+
+Pasándole su nuevo <em/widget/ como primer argumento, y el nombre que
+desea darle como el segundo. Mediante este nombre podrá cambiar los
+atributos de ese <em/widget/.
+
+Si hacemos algo así:
+
+<tscreen><verb>
+boton = gtk_button_new_with_label ("Botón especial");
+gtk_widget_set_name (boton, "botón especial");
+</verb></tscreen>
+
+El botón tendrá el nombre «botón especial» y podría hacersele
+referencia en el fichero <tt/rc/ como «botón especial.GtkButton».
+[<--- ¡Verificadme! ]
+
+El fichero de ejemplo <tt/rc/ que mostramos a continuación, establece las
+propiedades de la ventana principal, y deja que todos los hijos de la
+ventana principal hereden el estilo descrito por «main button». El
+código utilizado en la aplicación es:
+
+<tscreen><verb>
+ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+gtk_widget_set_name (ventana, "main window");
+</verb></tscreen>
+
+Y el estilo se define en el fichero <tt/rc/ utilizando:
+
+<tscreen><verb>
+widget "main window.*GtkButton*" style "main_button"
+</verb></tscreen>
+
+Qué hace que todos los <em/widgets/ GtkButton de la «main window»
+(ventana principal) tengan el estilo "main_buttons" tal y como se
+define en el fichero <tt/rc/.
+
+Como puede ver, es un sistema muy poderoso y flexible. Utilice su
+imaginación para aprovecharse al máximo de este sistema.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Formato de los ficheros <tt/rc/ de GTK
+<p>
+El formato de los ficheros GTK se muestra en el ejemplo de más
+abajo. Éste es el fichero <tt/testgtkrc/ de la distribución GTK, pero he
+añadido unos cuantos comentarios y alguna cosilla. Puede que quiera
+incluir esta explicación en su aplicación para permitir al usuario
+personalizar su aplicación.
+
+Hay varias directivas para cambiar los atributos de un <em/widget/.
+
+<itemize>
+<item>fg - Establece el color de primer plano de un <em/widget/.
+<item>bg - Establece el color de fondo de un <em/widget/.
+<item>bg_pixmap - Establece la imagen que servirá de fondo al
+<em/widget/ (como mosaico).
+<item>font - Establece el tipo de letra que se utilizará con el
+<em/widget/.
+</itemize>
+
+Además de esto, hay varios estados en el que puede estar un
+<em/widget/, y puede especificar diferentes colores, imágenes y tipos
+de letra para cada estado. Estos estados son:
+
+<itemize>
+<item>NORMAL - El estado normal de un <em/widget/, sin el ratón sobre
+él, y no siendo presionado, etc...
+<item>PRELIGHT - Cuando el ratón esté sobre este <em/widget/ se
+utilizarán los colores definidos para este estado.
+<item>ACTIVE - Cuando se presiona o se pulsa sobre el <em/widget/,
+estará activo, y los atributos asignados por está etiqueta serán
+utilizados.
+<item>INSENSITIVE - Cuando un <em/widget/ es insensible, y no se puede
+activar, tomará estos atributos.
+<item>SELECTED - Cuando se seleccione un objeto, tomará estos atributos.
+</itemize>
+
+Cuando se utilizan las directivas «fg» y «bg» para poner los colores de
+los <em/widgets/, se utilizará el formato siguiente:
+
+<tscreen><verb>
+fg[<STATE>] = { Red, Green, Blue }
+</verb></tscreen>
+
+Donde <tt/STATE/ es uno de los estados anteriores (PRELIGHT, ACTIVE,
+etc...), y el <tt/Red/, <tt/Green/ y <tt/Blue/ (Rojo, Verde y Azul)
+son valores en el rango 0 - 1.0, { 1.0, 1.0, 1.0 } es blanco. Deben
+estar en formato flotante, o serán un 0, por lo que "1" no funcionará,
+debe ser "1.0". Un "0" está bien ya que es lo mismo si no se
+reconoce. Los valores no reconocidos se pondrán a 0.
+
+<tt/bg_pixmap/ es muy similar al de arriba, salvo que los colores se
+reemplazan por un nombre de fichero.
+
+<tt/pixmap_path/ es una lista de los caminos (<em/paths/) separados por
+«:». Estos caminos se utilizarán para buscar cualquier imagen que
+indique.
+
+La directiva sobre el tipo de letra es simplemente:
+<tscreen><verb>
+font = "<nombre del tipo de letra>"
+</verb></tscreen>
+
+Donde lo único difícil es saber la cadena del tipo de letra a
+elegir. Utilizar <tt/xfontsel/ o un programa similar debería ayudar.
+
+El <tt/widget_class/ establece el estilo de una clase de
+<em/widgets/. Estas clases se muestran en el resumen de <em/widgets/
+dentro de la jerarquía de clases.
+
+La directiva <tt/widget/ hace que un conjunto específico de
+<em/widgets/ tenga un estido determinado, sobreescribiendo cualquier
+estilo anterior que tuviese esa clase de <em/widgets/. Estos
+<em/widgets/ se registran dentro de la aplicación utilizando una
+llamada a <tt/gtk_widget_set_name()/. Esto le permitirá especificar
+los atributos de un <em/widget/ uno a uno, en vez de establecer los
+atributos de toda una clase <em/widget/. Deberá documentar cualquiera
+de estos <em/widgets/ especiales para que los usuarios puedan
+personalizarlos.
+
+Cuando la palabra clave <tt/parent/ se utiliza como un atributo, el
+<em/widget/ tomará los atributos de su padre en la aplicación.
+
+Puede asignar los atributos de un estilo previamente definido a uno
+nuevo.
+
+<tscreen><verb>
+style "main_button" = "button"
+{
+ font = "-adobe-helvetica-medium-r-normal--*-100-*-*-*-*-*-*"
+ bg[PRELIGHT] = { 0.75, 0, 0 }
+}
+</verb></tscreen>
+
+Este ejemplo toma el estilo «button», y crea un nuevo estilo
+«main_button» cambiando simplemente el tipo de letra y cambiando el
+color de fondo cuando el <em/widget/ esté en estado <tt/PRELIGHT/.
+
+Por supuesto, muchos de estos atributos no se aplican a todos los
+<em/widgets/. Realmente es una cuestión de sentido común. Se utilizará
+cualquier atributo que se pueda aplicar.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Fichero <tt/rc/ de ejemplo
+<p>
+
+<!-- Esto hay que traducirlo -->
+<tscreen><verb>
+# pixmap_path "<dir 1>:<dir 2>:<dir 3>:..."
+#
+pixmap_path "/usr/include/X11R6/pixmaps:/home/imain/pixmaps"
+#
+# style <name> [= <name>]
+# {
+# <option>
+# }
+#
+# widget <widget_set> style <style_name>
+# widget_class <widget_class_set> style <style_name>
+
+
+# Here is a list of all the possible states. Note that some do not apply to
+# certain widgets.
+#
+# NORMAL - The normal state of a widget, without the mouse over top of
+# it, and not being pressed etc.
+#
+# PRELIGHT - When the mouse is over top of the widget, colors defined
+# using this state will be in effect.
+#
+# ACTIVE - When the widget is pressed or clicked it will be active, and
+# the attributes assigned by this tag will be in effect.
+#
+# INSENSITIVE - When a widget is set insensitive, and cannot be
+# activated, it will take these attributes.
+#
+# SELECTED - When an object is selected, it takes these attributes.
+#
+# Given these states, we can set the attributes of the widgets in each of
+# these states using the following directives.
+#
+# fg - Sets the foreground color of a widget.
+# fg - Sets the background color of a widget.
+# bg_pixmap - Sets the background of a widget to a tiled pixmap.
+# font - Sets the font to be used with the given widget.
+#
+
+# This sets a style called "button". The name is not really important, as
+# it is assigned to the actual widgets at the bottom of the file.
+
+style "window"
+{
+ #This sets the padding around the window to the pixmap specified.
+ #bg_pixmap[<STATE>] = "<pixmap filename>"
+ bg_pixmap[NORMAL] = "warning.xpm"
+}
+
+style "scale"
+{
+ #Sets the foreground color (font color) to red when in the "NORMAL"
+ #state.
+
+ fg[NORMAL] = { 1.0, 0, 0 }
+
+ #Sets the background pixmap of this widget to that of its parent.
+ bg_pixmap[NORMAL] = "<parent>"
+}
+
+style "button"
+{
+ # This shows all the possible states for a button. The only one that
+ # doesn't apply is the SELECTED state.
+
+ fg[PRELIGHT] = { 0, 1.0, 1.0 }
+ bg[PRELIGHT] = { 0, 0, 1.0 }
+ bg[ACTIVE] = { 1.0, 0, 0 }
+ fg[ACTIVE] = { 0, 1.0, 0 }
+ bg[NORMAL] = { 1.0, 1.0, 0 }
+ fg[NORMAL] = { .99, 0, .99 }
+ bg[INSENSITIVE] = { 1.0, 1.0, 1.0 }
+ fg[INSENSITIVE] = { 1.0, 0, 1.0 }
+}
+
+# In this example, we inherit the attributes of the "button" style and then
+# override the font and background color when prelit to create a new
+# "main_button" style.
+
+style "main_button" = "button"
+{
+ font = "-adobe-helvetica-medium-r-normal--*-100-*-*-*-*-*-*"
+ bg[PRELIGHT] = { 0.75, 0, 0 }
+}
+
+style "toggle_button" = "button"
+{
+ fg[NORMAL] = { 1.0, 0, 0 }
+ fg[ACTIVE] = { 1.0, 0, 0 }
+
+ # This sets the background pixmap of the toggle_button to that of its
+ # parent widget (as defined in the application).
+ bg_pixmap[NORMAL] = "<parent>"
+}
+
+style "text"
+{
+ bg_pixmap[NORMAL] = "marble.xpm"
+ fg[NORMAL] = { 1.0, 1.0, 1.0 }
+}
+
+style "ruler"
+{
+ font = "-adobe-helvetica-medium-r-normal--*-80-*-*-*-*-*-*"
+}
+
+# pixmap_path "~/.pixmaps"
+
+# These set the widget types to use the styles defined above.
+# The widget types are listed in the class hierarchy, but could probably be
+# just listed in this document for the users reference.
+
+widget_class "GtkWindow" style "window"
+widget_class "GtkDialog" style "window"
+widget_class "GtkFileSelection" style "window"
+widget_class "*Gtk*Scale" style "scale"
+widget_class "*GtkCheckButton*" style "toggle_button"
+widget_class "*GtkRadioButton*" style "toggle_button"
+widget_class "*GtkButton*" style "button"
+widget_class "*Ruler" style "ruler"
+widget_class "*GtkText" style "text"
+
+# This sets all the buttons that are children of the "main window" to
+# the main_button style. These must be documented to be taken advantage of.
+widget "main window.*GtkButton*" style "main_button"
+</verb></tscreen>
+
+<!-- ***************************************************************** -->
+<sect>Escribiendo sus propios <em/widgets/
+<!-- ***************************************************************** -->
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Visión general
+<p>
+Aunque la distribución de GTK viene con muchos tipos de <em/widgets/
+que debería cubrir todas la mayoría de las necesidades básicas, puede
+que haya llegado el momento en que necesite crear su propio
+<em/widget/. Debido a que GTK utiliza mucho la herencia de
+<em/widgets/, y si ya hay un <em/widget/ que se acerque lo suficiente
+a lo que quiere, tal vez pueda hacer un nuevo <em/widget/ con tan solo
+unas cuantas líneas de código. Pero antes de empezar a trabajar en un
+nuevo <em/widget/, asegúrese primero de que no hay nadie que ya haya
+hecho otro parecido. Así evitará la duplicación de esfuerzo y
+mantendrá el número de <em/widgets/ GTK en su valor mínimo, lo que
+ayudará a que el código y la interfaz de las diferentes aplicaciones
+sea consistente. Por otra parte, cuando haya acabado su <em/widget/,
+anúncielo al mundo entreo para que todo el mundo se pueda
+beneficiar. Probablemente el mejor lugar para hacerlo sea la
+<tt>gtk-list</tt>.
+
+Las fuentes completas de los <em/widgets/ de ejemplo están disponibles
+en el mismo lugar en el que consiguió este tutorial, o en:
+
+<htmlurl url="http://www.gtk.org/~otaylor/gtk/tutorial/"
+name="http://www.gtk.org/~otaylor/gtk/tutorial/">
+
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> La anatomía de un <em/widget/
+<p>
+Para crear un nuevo <em/widget/, es importante conocer como funcionan
+los objetos de GTK. Esta sección es sólo un breve resumen. Ver la
+documentación a la que se hace referencia para obtener más detalles.
+
+Los widgets GTK están implementados siguiendo una orientación a
+objetos. Sin embargo, están implementados en C estándar. De esta forma
+se mejora enormemente la portabilidad y la estabilidad con respecto a
+la actual generación de compiladores C++; sin embargo, con todo esto
+no queremos decir que el creador de <em/widgets/ tenga que prestar
+atención a ninguno de los detalles de implementación. La información
+que es común a todos los <em/widgets/ de una clase de <em/widgets/
+(p.e., a todos los <em/widgets/ botón) se almacena en la
+<em>estructura de clase</em>. Sólo hay una copia de ésta en la que se
+almacena información sobre las señales de la clase (que actuan como
+funciones virtuales en C). Para permitir la herencia, el primer campo
+en la estructura de la clase debe ser una copia de la estructura de la
+clase del padre. La declaración de la estructura de la clase de
+GtkButton debe ser algo así:
+
+<tscreen><verb>
+struct _GtkButtonClass
+{
+ GtkContainerClass parent_class;
+
+ void (* pressed) (GtkButton *button);
+ void (* released) (GtkButton *button);
+ void (* clicked) (GtkButton *button);
+ void (* enter) (GtkButton *button);
+ void (* leave) (GtkButton *button);
+};
+</verb></tscreen>
+
+Cuando un botón se trata como un contenedor (por ejemplo, cuando se le
+cambia el tamaño), su estructura de clase puede convertirse a
+GtkContainerClass, y los campos relevantes se utilizarán para manejar
+las señales.
+
+También hay una estructura que se crea para cada <em/widget/. Esta
+estructura tiene campos para almacenar la información que es diferente
+para cada copia del <em/widget/. Nosotros llamaremos a esta estructura
+la <em>estructura objeto</em>. Para la clase botón, es así:
+
+<tscreen><verb>
+struct _GtkButton
+{
+ GtkContainer container;
+
+ GtkWidget *child;
+
+ guint in_button : 1;
+ guint button_down : 1;
+};
+</verb></tscreen>
+
+Observe que, como en la estructura de clase, el primer campo es la
+estructura objeto de la clase padre, por lo que esta estructura puede
+convertirse en la estructura de la clase del objeto padre cuando haga
+falta.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Creando un <em/widget/ compuesto
+
+<!-- ----------------------------------------------------------------- -->
+<sect2> Introducción
+<p>
+Un tipo de widget que puede interesarnos es uno que sea un mero
+agregado de otros <em/widgets/ GTK. Este tipo de <em/widget/ no hace
+nada que no pueda hacerse sin la necesidad de crear un nuevo
+<em/widget/, pero proporciona una forma conveniente de empaquetar los
+elementos del interfaz de usuario para su reutilización. Los
+<em/widgets/ <tt/FileSelection/ y <tt/ColorSelection/ incluidos en la
+distribución estándar son ejemplos de este tipo de <em/widgets/.
+
+El <em/widget/ ejemplo que hemos creado en esta sección es el
+<em/widget/ Tictactoe, una matriz de 3x3 de botones de selección que
+lanza una señal cuando están deseleccionados tres botones en una misma
+fila, columna, o diagonal.
+
+<!-- ----------------------------------------------------------------- -->
+<sect2> Escogiendo una clase padre
+<p>
+Normalmente la clase padre para un <em/widget/ compuesto es la clase
+contenedor que tenga todos los elementos del <em/widget/
+compuesto. Por ejemplo, la clase padre del <em/widget/
+<tt/FileSelection/ es la clase <tt/Dialog/. Ya que nuestros botones se
+ordenarán en una tabla, parece natural hacer que nuestra clase padre
+sea la clase <tt/GtkTable/. Desafortunadamente, esto no
+funcionaría. La creación de un <em/widget/ se divide en dos funciones
+- una función <tt/NOMBREWIDGET_new()/ que utilizará el usuario, y una
+función <tt/NOMBREWIDGET_init()/ que hará el trabajo básico de
+inicializar el <em/widget/ que es independiente de los argumentos que
+se le pasen a la función <tt/_new()/. Los <em/widgets/ derivados sólo
+llaman a la función <tt/_init/ de su <em/widget/ padre. Pero esta
+división del trabajo no funciona bien con las tablas, que necesitan
+saber en el momento de su creación el número de filas y de columnas
+que deben tener. A menos que queramos duplicar la mayor parte de lo
+hecho en <tt/gtk_table_new()/ en nuestro <em/widget/ Tictactoe,
+haremos mejor si evitamos derivar de GtkTable. Por esta razón,
+derivaremos de <tt/GtkVBox/, y meteremos nuestra tabla dentro de la
+caja vertical.
+
+<!-- ----------------------------------------------------------------- -->
+<sect2> El fichero de cabecera
+<p>
+Cada clase <em/widget/ tiene un fichero de cabecera que declara el
+objeto y las estructuras de clase para ese <em/widget/, así como las
+funciones públicas. Un par de características que merecen dejarse
+aparte. Para evitar la duplicación de definiciones, meteremos el
+fichero de cabecera al completo entre:
+
+<tscreen><verb>
+#ifndef __TICTACTOE_H__
+#define __TICTACTOE_H__
+.
+.
+.
+#endif /* __TICTACTOE_H__ */
+</verb></tscreen>
+
+Y para que los programas en C++ incluyan sin problemas el fichero de
+cabecera, pondremos:
+
+<tscreen><verb>
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+.
+.
+.
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+</verb></tscreen>
+
+Con las funciones y las estructuras, declararemos tres macros estándar
+en nuestro fichero de cabecera, <tt/TICTACTOE(obj)/,
+<tt/TICTACTOE_CLASS(class)/, y <tt/IS_TICTACTOE(obj)/, que,
+convierten, respectivamente, un puntero en un puntero al objeto o a la
+estructura de la clase, y comprueba si un objeto es un <em/widget/
+Tictactoe.
+
+Aquí está el fichero de cabecera al completo:
+
+<tscreen><verb>
+/* tictactoe.h */
+
+#ifndef __TICTACTOE_H__
+#define __TICTACTOE_H__
+
+#include <gdk/gdk.h>
+#include <gtk/gtkvbox.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define TICTACTOE(obj) GTK_CHECK_CAST (obj, tictactoe_get_type (), Tictactoe)
+#define TICTACTOE_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, tictactoe_get_type (), TictactoeClass)
+#define IS_TICTACTOE(obj) GTK_CHECK_TYPE (obj, tictactoe_get_type ())
+
+
+typedef struct _Tictactoe Tictactoe;
+typedef struct _TictactoeClass TictactoeClass;
+
+struct _Tictactoe
+{
+ GtkVBox vbox;
+
+ GtkWidget *botones[3][3];
+};
+
+struct _TictactoeClass
+{
+ GtkVBoxClass parent_class;
+
+ void (* tictactoe) (Tictactoe *ttt);
+};
+
+guint tictactoe_get_type (void);
+GtkWidget* tictactoe_new (void);
+void tictactoe_clear (Tictactoe *ttt);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __TICTACTOE_H__ */
+
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect2> La función <tt/_get_type()/.
+<p>
+Ahora continuaremos con la implementación de nuestro <em/widget/. Una
+función del núcleo de todo <em/widget/ es
+<tt/NOMBREWIDGET_get_type()/. Cuando se llame a esta función por
+vez primera, le informará a GTK sobre la clase del <em/widget/, y
+devolverá un ID que identificará unívocamente la clase <em/widget/. En
+las llamadas siguientes, lo único que hará será devolver el ID.
+
+<tscreen><verb>
+guint
+tictactoe_get_type ()
+{
+ static guint ttt_type = 0;
+
+ if (!ttt_type)
+ {
+ GtkTypeInfo ttt_info =
+ {
+ "Tictactoe",
+ sizeof (Tictactoe),
+ sizeof (TictactoeClass),
+ (GtkClassInitFunc) tictactoe_class_init,
+ (GtkObjectInitFunc) tictactoe_init,
+ (GtkArgSetFunc) NULL,
+ (GtkArgGetFunc) NULL
+ };
+
+ ttt_type = gtk_type_unique (gtk_vbox_get_type (), &amp;ttt_info);
+ }
+
+ return ttt_type;
+}
+</verb></tscreen>
+
+La estructura GtkTypeInfo tiene la definición siguiente:
+
+<tscreen><verb>
+struct _GtkTypeInfo
+{
+ gchar *type_name;
+ guint object_size;
+ guint class_size;
+ GtkClassInitFunc class_init_func;
+ GtkObjectInitFunc object_init_func;
+ GtkArgSetFunc arg_set_func;
+ GtkArgGetFunc arg_get_func;
+};
+</verb></tscreen>
+
+Los utilidad de cada campo de esta estructura se explica por su propio
+nombre. Ignoraremos por ahora los campos <tt/arg_set_func/
+y <tt/arg_get_func/: son importantes, pero todavía es raro
+utilizarlos, su papel es permitir que las opciones de los <em/wdigets/
+puedan establecerse correctamente mediante lenguajes
+interpretados. Una vez que GTK tiene una copia de esta estructura
+correctamente rellenada, sabrá como crear objetos de un tipo
+particular de <em/widget/.
+
+<!-- ----------------------------------------------------------------- -->
+<sect2> La función <tt/_class_init()/
+<p>
+La función <tt/NOMBREWIDGET_class_init()/ inicializa los campos de la
+estructura clase del <em/widget/, y establece las señales de la
+clase. Para nuestro <em/widget/ Tictactoe será una cosa así:
+
+<tscreen><verb>
+
+enum {
+ TICTACTOE_SIGNAL,
+ LAST_SIGNAL
+};
+
+static gint tictactoe_signals[LAST_SIGNAL] = { 0 };
+
+static void
+tictactoe_class_init (TictactoeClass *class)
+{
+ GtkObjectClass *object_class;
+
+ object_class = (GtkObjectClass*) class;
+
+ tictactoe_signals[TICTACTOE_SIGNAL] = gtk_signal_new ("tictactoe",
+ GTK_RUN_FIRST,
+ object_class->type,
+ GTK_SIGNAL_OFFSET (TictactoeClass, tictactoe),
+ gtk_signal_default_marshaller, GTK_TYPE_NONE, 0);
+
+
+ gtk_object_class_add_signals (object_class, tictactoe_signals, LAST_SIGNAL);
+
+ class->tictactoe = NULL;
+}
+</verb></tscreen>
+
+Nuestro <em/widget/ sólo tiene una señal, la señal <tt/tictactoe/ que
+se invoca cuando una fila, columna, o diagonal se rellena
+completamente. No todos los <em/widgets/ compuestos necesitan señales,
+por lo que si está leyendo esto por primera vez, puede que sea mejor
+que pase a la sección siguiente, ya que las cosas van a complicarse un
+poco.
+
+La función:
+
+<tscreen><verb>
+gint gtk_signal_new( const gchar *name,
+ GtkSignalRunType run_type,
+ GtkType object_type,
+ gint function_offset,
+ GtkSignalMarshaller marshaller,
+ GtkType return_val,
+ guint nparams,
+ ...);
+</verb></tscreen>
+
+crea una nueva señal. Los parámetros son:
+
+<itemize>
+<item> <tt/name/: El nombre de la señal.
+<item> <tt/run_type/: Si el manejador por defecto se ejecuta antes o
+despues del manejador de usuario. Normalmente debe ser
+<tt/GTK_RUN_FIRST/, o <tt/GTK_RUN_LAST/, aunque hay otras
+posibilidades.
+<item> <tt/object_type/: El ID del objeto al que se le aplica esta
+señal. (También se aplicará a los descendientes de los objetos)
+<item> <tt/function_offset/: El desplazamiento en la estructura de la
+clase de un puntero al manejador por defecto.
+<item> <tt/marshaller/: Una función que se utiliza para invocar al
+manejador de señal. Para los manejadores de señal que no tengan más
+argumentos que el objeto que emitió la señal podemos utilizar la
+función marshaller por defecto <tt/gtk_signal_default_marshaller/.
+<item> <tt/return_val/: El tipo del valor devuelto.
+<item> <tt/nparams/: El número de parámetros del manejador de señal
+(distintos de los dos por defecto que hemos mencionado arriba).
+<item> <tt/.../: Los tipos de los parámetros.
+</itemize>
+
+Cuando se especifican los tipos, se utilizará la enumeración
+<tt/GtkType/:
+
+<tscreen><verb>
+typedef enum
+{
+ GTK_TYPE_INVALID,
+ GTK_TYPE_NONE,
+ GTK_TYPE_CHAR,
+ GTK_TYPE_BOOL,
+ GTK_TYPE_INT,
+ GTK_TYPE_UINT,
+ GTK_TYPE_LONG,
+ GTK_TYPE_ULONG,
+ GTK_TYPE_FLOAT,
+ GTK_TYPE_DOUBLE,
+ GTK_TYPE_STRING,
+ GTK_TYPE_ENUM,
+ GTK_TYPE_FLAGS,
+ GTK_TYPE_BOXED,
+ GTK_TYPE_FOREIGN,
+ GTK_TYPE_CALLBACK,
+ GTK_TYPE_ARGS,
+
+ GTK_TYPE_POINTER,
+
+ /* it'd be great if the next two could be removed eventually */
+ GTK_TYPE_SIGNAL,
+ GTK_TYPE_C_CALLBACK,
+
+ GTK_TYPE_OBJECT
+
+} GtkFundamentalType;
+</verb></tscreen>
+
+<tt/gtk_signal_new()/ devuelve un identificador entero único para la
+señal, que almacenamos en el vector <tt/tictactoe_signals/, que
+indexaremos utilizando una enumeración. (Convencionalmente, los
+elementos de la enumeración son el nombre de la señal, en mayúsculas,
+pero aquí tendríamos un conflicto con la macro <tt/TICTACTOE()/, por
+lo que lo llamaremos <tt/TICTACTOE_SIGNAL/.
+
+Después de crear nuestras señales, necesitamos llamar a GTK para
+asociarlas con la clase Tictactoe. Hacemos esto llamando a
+<tt/gtk_object_class_add_signals()/. Entonces haremos que el puntero
+que apunta al manejador por defecto para la señal `tictactoe' sea NULL,
+indicando que no hay ninguna acción por defecto.
+
+<!-- ----------------------------------------------------------------- -->
+<sect2> La función <tt/_init()/.
+<p>
+Cada clase <em/widget/ también necesita una función para inicializar
+la estructura del objeto. Normalmente, esta función tiene el limitado
+rol de poner los distintos campos de la estructura a su valor por
+defecto. Sin embargo para los <em/widgets/ de composición, esta
+función también crea los distintos <em/widgets/ componentes.
+
+<tscreen><verb>
+static void
+tictactoe_init (Tictactoe *ttt)
+{
+ GtkWidget *table;
+ gint i,j;
+
+ table = gtk_table_new (3, 3, TRUE);
+ gtk_container_add (GTK_CONTAINER(ttt), table);
+ gtk_widget_show (table);
+
+ for (i=0;i<3; i++)
+ for (j=0;j<3; j++)
+ {
+ ttt->buttons[i][j] = gtk_toggle_button_new ();
+ gtk_table_attach_defaults (GTK_TABLE(table), ttt->buttons[i][j],
+ i, i+1, j, j+1);
+ gtk_signal_connect (GTK_OBJECT (ttt->buttons[i][j]), "toggled",
+ GTK_SIGNAL_FUNC (tictactoe_toggle), ttt);
+ gtk_widget_set_usize (ttt->buttons[i][j], 20, 20);
+ gtk_widget_show (ttt->buttons[i][j]);
+ }
+}
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect2> Y el resto...
+<p>
+Hay una función más que cada <em/widget/ (excepto los <em/widget/ muy
+básicos como GtkBin que no pueden crear objetos) tiene que
+tener - la función que el usuario llama para crear un objeto de ese
+tipo. Normalmente se llama <tt/NOMBREWIDGET_new()/. En algunos
+<em/widgets/, que no es el caso del <em/widget/ Tictactoe, esta
+función toma argumentos, y hace alguna inicialización en función de
+estos. Las otras dos funciones son específicas al <em/widget/
+Tictactoe.
+
+<tt/tictactoe_clear()/ es una función pública que reinicia todos los
+botones en el <em/widget/ a la posición alta. Observe la utilización
+de <tt/gtk_signal_handler_block_by_data()/ para hacer que no se
+ejecute nuestro manejador de señal innecesariamente por cambios en los
+botones.
+
+<tt/tictactoe_toggle()/ es el manejador de señal que se invoca cuando
+el usuario pulsa un botón. Hace una comprobación para ver si hay
+alguna combinación ganadora, y si la hay, emite la señal
+«tictactoe».
+
+<tscreen><verb>
+GtkWidget*
+tictactoe_new ()
+{
+ return GTK_WIDGET ( gtk_type_new (tictactoe_get_type ()));
+}
+
+void
+tictactoe_clear (Tictactoe *ttt)
+{
+ int i,j;
+
+ for (i=0;i<3;i++)
+ for (j=0;j<3;j++)
+ {
+ gtk_signal_handler_block_by_data (GTK_OBJECT(ttt->buttons[i][j]), ttt);
+ gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (ttt->buttons[i][j]),
+ FALSE);
+ gtk_signal_handler_unblock_by_data (GTK_OBJECT(ttt->buttons[i][j]), ttt);
+ }
+}
+
+static void
+tictactoe_toggle (GtkWidget *widget, Tictactoe *ttt)
+{
+ int i,k;
+
+ static int rwins[8][3] = { { 0, 0, 0 }, { 1, 1, 1 }, { 2, 2, 2 },
+ { 0, 1, 2 }, { 0, 1, 2 }, { 0, 1, 2 },
+ { 0, 1, 2 }, { 0, 1, 2 } };
+ static int cwins[8][3] = { { 0, 1, 2 }, { 0, 1, 2 }, { 0, 1, 2 },
+ { 0, 0, 0 }, { 1, 1, 1 }, { 2, 2, 2 },
+ { 0, 1, 2 }, { 2, 1, 0 } };
+
+ int success, found;
+
+ for (k=0; k<8; k++)
+ {
+ success = TRUE;
+ found = FALSE;
+
+ for (i=0;i<3;i++)
+ {
+ success = success &amp;&amp;
+ GTK_TOGGLE_BUTTON(ttt->buttons[rwins[k][i]][cwins[k][i]])->active;
+ found = found ||
+ ttt->buttons[rwins[k][i]][cwins[k][i]] == widget;
+ }
+
+ if (success &amp;&amp; found)
+ {
+ gtk_signal_emit (GTK_OBJECT (ttt),
+ tictactoe_signals[TICTACTOE_SIGNAL]);
+ break;
+ }
+ }
+}
+</verb></tscreen>
+
+Y finalmente, un programa ejemplo que utiliza nuestro <em/widget/
+Tictactoe:
+
+<tscreen><verb>
+#include <gtk/gtk.h>
+#include "tictactoe.h"
+
+/* Invocado cuando se completa una fila, columna o diagonal */
+void
+win (GtkWidget *widget, gpointer data)
+{
+ g_print ("Yay!\n");
+ tictactoe_clear (TICTACTOE (widget));
+}
+
+int
+main (int argc, char *argv[])
+{
+ GtkWidget *ventana;
+ GtkWidget *ttt;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ gtk_window_set_title (GTK_WINDOW (ventana), "Aspect Frame");
+
+ gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
+ GTK_SIGNAL_FUNC (gtk_exit), NULL);
+
+ gtk_container_border_width (GTK_CONTAINER (ventana), 10);
+
+ /* Create a new Tictactoe widget */
+ ttt = tictactoe_new ();
+ gtk_container_add (GTK_CONTAINER (ventana), ttt);
+ gtk_widget_show (ttt);
+
+ /* And attach to its "tictactoe" signal */
+ gtk_signal_connect (GTK_OBJECT (ttt), "tictactoe",
+ GTK_SIGNAL_FUNC (win), NULL);
+
+ gtk_widget_show (ventana);
+
+ gtk_main ();
+
+ return 0;
+}
+
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Creando un <em/widget/ desde cero.
+
+<!-- ----------------------------------------------------------------- -->
+<sect2> Introducción
+<p>
+En esta sección, averiguaremos como se dibujan los <em/widgets/ a sí
+mismos en pantalla y como interactuan con los eventos. Como ejemplo,
+crearemos un marcador analógico con un puntero que el usuario
+podrá arrastrar para hacer que el marcador tenga un valor dado.
+
+<!-- ----------------------------------------------------------------- -->
+<sect2> Mostrando un <em/widget/ en la pantalla
+<p>
+Hay varios pasos que están involucrados en el dibujado en pantalla.
+Después de que el <em/widget/ se cree con una llamada a
+<tt/NOMBREWIDGET_new()/, se necesitarán muchas más funciones:
+
+<itemize>
+<item> <tt/NOMBREWIDGET_realize()/ es la responsable de crear una
+ventana X para el <em/widget/, si tiene alguna.
+<item> <tt/NOMBREWIDGET_map()/ se invoca después de las llamadas del
+usuario
+<tt/gtk_widget_show()/. Es la responsable de asegurarse de que el
+<em/widget/ está dibujado (<em/mapeado/) en la pantalla. Para una
+clase contenedor, también deberá ocuparse de llamar a las funciones
+<tt/map()/ de cada <em/widget/ hijo.
+<item> <tt/NOMBREWIDGET_draw()/ se invoca cuando se llama a
+<tt/gtk_widget_draw()/ desde el <em/widget/ de uno de sus
+antepasados. Hace las llamadas necesarias a las funciones de dibujo
+para dibujar el <em/widget/ en la pantalla. Para los <em/widgets/
+contenedores, esta función debe llamar a las <tt/gtk_widget_draw/ de
+sus <em/widgets/ hijos.
+<item> <tt/NOMBREWIDGET_expose()/ es un manejador de los eventos
+<tt/expose/ del <em/widget/. Hace las llamadas necesarias a las
+funciones de dibujo para dibujar la parte expuesta en la
+pantalla. Para los <em/widgets/ contenedores, esta función debe
+generar los eventos <tt/expose/ de sus <em/widgets/ hijos que no
+tengan su propia ventana. (Si tuviesen su propia ventana, X generaría
+los eventos <tt/expose/ necesarios)
+</itemize>
+
+Las últimas dos funciones son bastante similares - ambas son
+responsables de dibujar el <em/widget/ en pantalla. De hecho en muchos
+<em/widgets/ realmente no importa la diferencia que hay entre ambas
+funciones. La función <em/draw()/ que hay por defecto en
+la clase <em/widget/ simplemente genera un evento <tt/expose/
+artificial de la zona a redibujar. Sin embargo, algunos tipos de
+<em/widgets/ puede ahorrarse trabajo distinguiendo entre las dos
+funciones. Por ejemplo, si un <em/widget/ tiene varias ventanas X,
+entonces, como los eventos <tt/expose/ identifican a la ventana
+expuesta, podrán redibujar sólo la ventana afectada, lo que no es
+posible con llamadas a <tt/draw()/.
+
+Los <em/widgets/ contenedores, aunque no utilicen la diferecia
+existente entre las dos funciones por sí mismos, no pueden utilizar
+simplemente las funciones <tt/draw()/ que hay por defecto ya que sus
+<em/widgets/ hijos puede que tengan que utilizar la diferencia. Sin
+embargo, sería un derroche duplicar el código de dibujado entre las
+dos funciones. Lo normal es que cada <em/widget/ tenga una función
+llamada <tt/NOMBREWIDGET_paint()/ que haga el trabajo de dibujar el
+<em/widget/, ésta función será a la que se llame por las funciones
+<tt/draw()/ y <tt/expose()/.
+
+En nuestro ejemplo, como el <em/widget/ Dial no es un <em/widget/
+contenedor, y sólo tiene una ventana, podemos tomar el camino más
+corto, utilizar la función <tt/draw()/ por defecto y sólo
+implementar la función <tt/expose()/.
+
+<!-- ----------------------------------------------------------------- -->
+<sect2> Los orígenes del <em/widget/ Dial
+<p>
+Así como todos los animales terrestes son variaciones del primer
+anfíbio que salió del barro, los <em/widgets/ Gtk tienden a nacer
+como variaciones de algún otro <em/widget/ escrito previamente. Por
+tanto, aunque esta sección se titule `Creando un <em/widget/ de la
+nada', el <em/widget/ Dial empieza realmente con el código fuente
+del <em/widget/ Range. He tomado éste como punto de arranque porque
+sería bonito que nuestro dial tuviese la misma interfaz que los
+<em/widgets/ Scale, que son sólo una especialización del <em/widget/
+Range. Por tanto, aunque el código fuente se presente más adelante en
+su forma final, no implica que fuese escrito de esta forma <em>deus ex
+machina</em>. Si todavía no está familiarizado, desde el punto de
+vista del escritor de aplicaciones, con la forma de funcionar de los
+<em/widgets/ Scale, sería una buena idea echarles un vistazo antes de
+continuar.
+
+<!-- ----------------------------------------------------------------- -->
+<sect2> Los comienzos
+<p>
+Nuestro <em/widget/ tiene un aspecto algo parecido al del <em/widget/
+Tictactoe. Primero, tenemos un fichero de cabecera:
+
+<tscreen><verb>
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __GTK_DIAL_H__
+#define __GTK_DIAL_H__
+
+#include <gdk/gdk.h>
+#include <gtk/gtkadjustment.h>
+#include <gtk/gtkwidget.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+#define GTK_DIAL(obj) GTK_CHECK_CAST (obj, gtk_dial_get_type (), GtkDial)
+#define GTK_DIAL_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, gtk_dial_get_type (), GtkDialClass)
+#define GTK_IS_DIAL(obj) GTK_CHECK_TYPE (obj, gtk_dial_get_type ())
+
+
+typedef struct _GtkDial GtkDial;
+typedef struct _GtkDialClass GtkDialClass;
+
+struct _GtkDial
+{
+ GtkWidget widget;
+
+ /* política de actualización
+ * (GTK_UPDATE_[CONTINUOUS/DELAYED/DISCONTINUOUS]) */
+ guint policy : 2;
+
+ /* Botón actualmente presionado o 0 si no hay ninguno */
+ guint8 boton;
+
+ /* Dimensión de los componendes del dial */
+ gint radius;
+ gint pointer_width;
+
+ /* ID del temporizador de actualización, o 0 si no hay ninguno */
+ guint32 timer;
+
+ /* ángulo actual */
+ gfloat angle;
+
+ /* Viejos valores almacenados del adjustment, para que así no
+ * tengamos que saber cuando cambia algo */
+ gfloat old_value;
+ gfloat old_lower;
+ gfloat old_upper;
+
+ /* El objeto adjustment que almacena los datos para este dial */
+ GtkAdjustment *adjustment;
+};
+
+struct _GtkDialClass
+{
+ GtkWidgetClass parent_class;
+};
+
+
+GtkWidget* gtk_dial_new (GtkAdjustment *adjustment);
+guint gtk_dial_get_type (void);
+GtkAdjustment* gtk_dial_get_adjustment (GtkDial *dial);
+void gtk_dial_set_update_policy (GtkDial *dial,
+ GtkUpdateType policy);
+
+void gtk_dial_set_adjustment (GtkDial *dial,
+ GtkAdjustment *adjustment);
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* __GTK_DIAL_H__ */
+</verb></tscreen>
+
+Como vamos a ir con este <em/widget/ un poco más lejos que con el
+último que creamos, ahora tenemos unos cuantos campos más en la
+estructura de datos, pero el resto de las cosas son muy parecidas.
+
+Ahora, después de incluir los ficheros de cabecera, y declarar unas
+cuantas constantes, tenemos algunas funciones que proporcionan
+información sobre el <em/widget/ y lo inicializan:
+
+<tscreen><verb>
+#include <math.h>
+#include <stdio.h>
+#include <gtk/gtkmain.h>
+#include <gtk/gtksignal.h>
+
+#include "gtkdial.h"
+
+#define SCROLL_DELAY_LENGTH 300
+#define DIAL_DEFAULT_SIZE 100
+
+/* Declaraciones de funciones */
+
+[ omitido para salvar espacio ]
+
+/* datos locales */
+
+static GtkWidgetClass *parent_class = NULL;
+
+guint
+gtk_dial_get_type ()
+{
+ static guint dial_type = 0;
+
+ if (!dial_type)
+ {
+ GtkTypeInfo dial_info =
+ {
+ "GtkDial",
+ sizeof (GtkDial),
+ sizeof (GtkDialClass),
+ (GtkClassInitFunc) gtk_dial_class_init,
+ (GtkObjectInitFunc) gtk_dial_init,
+ (GtkArgSetFunc) NULL,
+ (GtkArgGetFunc) NULL,
+ };
+
+ dial_type = gtk_type_unique (gtk_widget_get_type (), &amp;dial_info);
+ }
+
+ return dial_type;
+}
+
+static void
+gtk_dial_class_init (GtkDialClass *class)
+{
+ GtkObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+
+ object_class = (GtkObjectClass*) class;
+ widget_class = (GtkWidgetClass*) class;
+
+ parent_class = gtk_type_class (gtk_widget_get_type ());
+
+ object_class->destroy = gtk_dial_destroy;
+
+ widget_class->realize = gtk_dial_realize;
+ widget_class->expose_event = gtk_dial_expose;
+ widget_class->size_request = gtk_dial_size_request;
+ widget_class->size_allocate = gtk_dial_size_allocate;
+ widget_class->button_press_event = gtk_dial_button_press;
+ widget_class->button_release_event = gtk_dial_button_release;
+ widget_class->motion_notify_event = gtk_dial_motion_notify;
+}
+
+static void
+gtk_dial_init (GtkDial *dial)
+{
+ dial->button = 0;
+ dial->policy = GTK_UPDATE_CONTINUOUS;
+ dial->timer = 0;
+ dial->radius = 0;
+ dial->pointer_width = 0;
+ dial->angle = 0.0;
+ dial->old_value = 0.0;
+ dial->old_lower = 0.0;
+ dial->old_upper = 0.0;
+ dial->adjustment = NULL;
+}
+
+GtkWidget*
+gtk_dial_new (GtkAdjustment *adjustment)
+{
+ GtkDial *dial;
+
+ dial = gtk_type_new (gtk_dial_get_type ());
+
+ if (!adjustment)
+ adjustment = (GtkAdjustment*) gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
+
+ gtk_dial_set_adjustment (dial, adjustment);
+
+ return GTK_WIDGET (dial);
+}
+
+static void
+gtk_dial_destroy (GtkObject *object)
+{
+ GtkDial *dial;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GTK_IS_DIAL (object));
+
+ dial = GTK_DIAL (object);
+
+ if (dial->adjustment)
+ gtk_object_unref (GTK_OBJECT (dial->adjustment));
+
+ if (GTK_OBJECT_CLASS (parent_class)->destroy)
+ (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+</verb></tscreen>
+
+Observe que ésta función <tt/init()/ hace menos cosas de las que hacía
+la función <tt/init()/ que utilizamos con el <em/widget/ Tictactoe, ya
+que éste no es un <em/widget/ compuesto, y la función <tt/new()/ hace
+más cosas, ya que ahora admite un argumento. Observe también que
+cuando almacenamos un puntero en un objeto Adjustment, incrementamos
+su contador interno, (y lo decrementamos cuando ya no lo utilizamos)
+por lo que GTK puede saber cuando se puede destruir sin que se
+produzcan problemas.
+
+<p>
+Aquí tenemos unas cuantas funciones para manipular las opciones del
+<em/widget/:
+
+<tscreen><verb>
+GtkAdjustment*
+gtk_dial_get_adjustment (GtkDial *dial)
+{
+ g_return_val_if_fail (dial != NULL, NULL);
+ g_return_val_if_fail (GTK_IS_DIAL (dial), NULL);
+
+ return dial->adjustment;
+}
+
+void
+gtk_dial_set_update_policy (GtkDial *dial,
+ GtkUpdateType policy)
+{
+ g_return_if_fail (dial != NULL);
+ g_return_if_fail (GTK_IS_DIAL (dial));
+
+ dial->policy = policy;
+}
+
+void
+gtk_dial_set_adjustment (GtkDial *dial,
+ GtkAdjustment *adjustment)
+{
+ g_return_if_fail (dial != NULL);
+ g_return_if_fail (GTK_IS_DIAL (dial));
+
+ if (dial->adjustment)
+ {
+ gtk_signal_disconnect_by_data (GTK_OBJECT (dial->adjustment), (gpointer) dial);
+ gtk_object_unref (GTK_OBJECT (dial->adjustment));
+ }
+
+ dial->adjustment = adjustment;
+ gtk_object_ref (GTK_OBJECT (dial->adjustment));
+
+ gtk_signal_connect (GTK_OBJECT (adjustment), "changed",
+ (GtkSignalFunc) gtk_dial_adjustment_changed,
+ (gpointer) dial);
+ gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed",
+ (GtkSignalFunc) gtk_dial_adjustment_value_changed,
+ (gpointer) dial);
+
+ dial->old_value = adjustment->value;
+ dial->old_lower = adjustment->lower;
+ dial->old_upper = adjustment->upper;
+
+ gtk_dial_update (dial);
+}
+</verb></tscreen>
+
+<sect2> <tt/gtk_dial_realize()/
+
+<p>
+Ahora vienen algunas funciones nuevas. Primero, tenemos una función
+que hace el trabajo de crear la ventana X. A la función se le pasará
+una máscara <tt/gdk_window_new()/ que especifica que campos de la
+estructura <tt/GdkWindowAttr/ tienen datos (los campos restantes
+tendrán los valores por defecto). También es bueno fijarse en la forma
+en que se crea la máscara de eventos. Llamamos a
+<tt/gtk_widget_get_events()/ para recuperar la máscara de eventos que
+el usuario ha especificado para su <em/widget/ (con
+<tt/gtk_widget_set_events()/), y añadir nosotros mismos los eventos
+en los que estemos interesados.
+
+<p>
+Después de crear la ventana, decidiremos su estilo y su fondo, y
+pondremos un puntero al <em/widget/ en el campo de datos del usuario
+de la <tt/GdkWindow/. Este último paso le permite a GTK despachar los
+eventos que hayan para esta ventana hacia el <em/widget/ correcto.
+
+<tscreen><verb>
+static void
+gtk_dial_realize (GtkWidget *widget)
+{
+ GtkDial *dial;
+ GdkWindowAttr attributes;
+ gint attributes_mask;
+
+ g_return_if_fail (widget != NULL);
+ g_return_if_fail (GTK_IS_DIAL (widget));
+
+ GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
+ dial = GTK_DIAL (widget);
+
+ attributes.x = widget->allocation.x;
+ attributes.y = widget->allocation.y;
+ attributes.width = widget->allocation.width;
+ attributes.height = widget->allocation.height;
+ attributes.wclass = GDK_INPUT_OUTPUT;
+ attributes.window_type = GDK_WINDOW_CHILD;
+ attributes.event_mask = gtk_widget_get_events (widget) |
+ GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK |
+ GDK_POINTER_MOTION_HINT_MASK;
+ attributes.visual = gtk_widget_get_visual (widget);
+ attributes.colormap = gtk_widget_get_colormap (widget);
+
+ attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+ widget->window = gdk_window_new (widget->parent->window, &amp;attributes, attributes_mask);
+
+ widget->style = gtk_style_attach (widget->style, widget->window);
+
+ gdk_window_set_user_data (widget->window, widget);
+
+ gtk_style_set_background (widget->style, widget->window, GTK_STATE_ACTIVE);
+}
+</verb></tscreen>
+
+<sect2> Negociación del tamaño
+
+<p>
+Antes de que se muestre por primera vez la ventana conteniendo un
+<em/widget/, y cuando quiera que la capa de la ventana cambie, GTK le
+preguntara a cada <em/widget/ hijo por su tamaño deseado. Esta
+petición se controla mediante la función
+<tt/gtk_dial_size_request()/. Como nuestro <em/widget/ no es un
+<em/widget/ contenedor, y no tiene ninguna limitación en su tamaño,
+nos contentaremos con devolver un valor por defecto.
+
+<tscreen><verb>
+static void
+gtk_dial_size_request (GtkWidget *widget,
+ GtkRequisition *requisition)
+{
+ requisition->width = DIAL_DEFAULT_SIZE;
+ requisition->height = DIAL_DEFAULT_SIZE;
+}
+</verb></tscreen>
+
+<p>
+Después de que todos los <em/widgets/ hayan pedido su tamaño ideal,
+se calculará la ventana y cada <em/widget/ hijo será informado de su
+tamaño actual. Normalmente, éste será al menos tan grande como el
+pedido, pero si por ejemplo, el usuario ha redimensionado la ventana,
+entonces puede que el tamaño que se le de al <em/widget/ sea menor
+que el que pidió. La notificación del tamaño se maneja mediante la
+función <tt/gtk_dial_size_allocate()/. Fíjese que esta función calcula
+los tamaños de los diferentes elementos que componen la ventana para
+su uso futuro, así como todo el trabajo sucio que poner los
+<em/widgets/ de la ventana X en la nueva posición y con el nuevo
+tamaño.
+
+<tscreen><verb>
+static void
+gtk_dial_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation)
+{
+ GtkDial *dial;
+
+ g_return_if_fail (widget != NULL);
+ g_return_if_fail (GTK_IS_DIAL (widget));
+ g_return_if_fail (allocation != NULL);
+
+ widget->allocation = *allocation;
+ if (GTK_WIDGET_REALIZED (widget))
+ {
+ dial = GTK_DIAL (widget);
+
+ gdk_window_move_resize (widget->window,
+ allocation->x, allocation->y,
+ allocation->width, allocation->height);
+
+ dial->radius = MAX(allocation->width,allocation->height) * 0.45;
+ dial->pointer_width = dial->radius / 5;
+ }
+}
+</verb></tscreen>.
+
+<!-- ----------------------------------------------------------------- -->
+<sect2> <tt/gtk_dial_expose()/
+
+<p>
+Como se mencionó arriba, todo el dibujado de este <em/widget/ se hace
+en el manejador de los eventos <tt/expose/. No hay mucho destacable
+aquí, excepto la utilización de la función <tt/gtk_draw_polygon/ para
+dibujar el puntero con un degradado tridimensional de acuerdo con los
+colores almacenados en el estilo del <em/widget/.
+
+<tscreen><verb>
+static gint
+gtk_dial_expose (GtkWidget *widget,
+ GdkEventExpose *event)
+{
+ GtkDial *dial;
+ GdkPoint points[3];
+ gdouble s,c;
+ gdouble theta;
+ gint xc, yc;
+ gint tick_length;
+ gint i;
+
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ if (event->count > 0)
+ return FALSE;
+
+ dial = GTK_DIAL (widget);
+
+ gdk_window_clear_area (widget->window,
+ 0, 0,
+ widget->allocation.width,
+ widget->allocation.height);
+
+ xc = widget->allocation.width/2;
+ yc = widget->allocation.height/2;
+
+ /* Dibujar las rayitas */
+
+ for (i=0; i<25; i++)
+ {
+ theta = (i*M_PI/18. - M_PI/6.);
+ s = sin(theta);
+ c = cos(theta);
+
+ tick_length = (i%6 == 0) ? dial->pointer_width : dial->pointer_width/2;
+
+ gdk_draw_line (widget->window,
+ widget->style->fg_gc[widget->state],
+ xc + c*(dial->radius - tick_length),
+ yc - s*(dial->radius - tick_length),
+ xc + c*dial->radius,
+ yc - s*dial->radius);
+ }
+
+ /* Dibujar el puntero */
+
+ s = sin(dial->angle);
+ c = cos(dial->angle);
+
+
+ points[0].x = xc + s*dial->pointer_width/2;
+ points[0].y = yc + c*dial->pointer_width/2;
+ points[1].x = xc + c*dial->radius;
+ points[1].y = yc - s*dial->radius;
+ points[2].x = xc - s*dial->pointer_width/2;
+ points[2].y = yc - c*dial->pointer_width/2;
+
+ gtk_draw_polygon (widget->style,
+ widget->window,
+ GTK_STATE_NORMAL,
+ GTK_SHADOW_OUT,
+ points, 3,
+ TRUE);
+
+ return FALSE;
+}
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect2> Manejo de eventos
+
+<p>
+El resto del código del <em/widget/ controla varios tipos de eventos,
+y no es muy diferente del que podemos encontrar en muchas aplicaciones
+GTK. Pueden ocurrir dos tipos de eventos - el usuario puede pulsar en
+el <em/widget/ con el ratón y arrastrar para mover el puntero, o el
+valor del objeto Adjustement puede cambiar debido a alguna
+circunstancia externa.
+
+<p>
+Cuando el usuario pulsa en el <em/widget/, haremos una comprobación
+para ver si la pulsación se hizo lo suficientemente cerca del
+puntero, y si así fue, almacenamos el botón que pulsó el usuario en
+en el campo <tt/button/ de la estructura del <em/widget/, y grabamos
+todos los eventos del ratón con una llamada a <tt/gtk_grab_add()/. El
+movimiento del ratón hará que se recalcule el valor del control
+(mediante la función <tt/gtk_dial_update_mouse/). Dependiendo de la
+política que sigamos, o bien se generarán instantáneamente los eventos
+<tt/value_changed/ (<tt/GTK_UPDATE_CONTINUOUS/), o bien después de una
+espera del temporizador establecido mediante <tt/gtk_timeout_add()/
+(<tt/GTK_UPDATE_DELAYED/), o bien sólo cuando se levante el botón
+(<tt/GTK_UPDATE_DISCONTINUOUS/).
+
+<tscreen><verb>
+static gint
+gtk_dial_button_press (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ GtkDial *dial;
+ gint dx, dy;
+ double s, c;
+ double d_parallel;
+ double d_perpendicular;
+
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ dial = GTK_DIAL (widget);
+
+ /* Determinar si la pulsación del botón fue dentro de la región del
+ puntero - esto lo hacemos calculando la distancia x e y del punto
+ donde se pulsó el botón ratón de la línea que se ha pasado mediante el
+ puntero */
+
+ dx = event->x - widget->allocation.width / 2;
+ dy = widget->allocation.height / 2 - event->y;
+
+ s = sin(dial->angle);
+ c = cos(dial->angle);
+
+ d_parallel = s*dy + c*dx;
+ d_perpendicular = fabs(s*dx - c*dy);
+
+ if (!dial->button &&
+ (d_perpendicular < dial->pointer_width/2) &&
+ (d_parallel > - dial->pointer_width))
+ {
+ gtk_grab_add (widget);
+
+ dial->button = event->button;
+
+ gtk_dial_update_mouse (dial, event->x, event->y);
+ }
+
+ return FALSE;
+}
+
+static gint
+gtk_dial_button_release (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ GtkDial *dial;
+
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ dial = GTK_DIAL (widget);
+
+ if (dial->button == event->button)
+ {
+ gtk_grab_remove (widget);
+
+ dial->button = 0;
+
+ if (dial->policy == GTK_UPDATE_DELAYED)
+ gtk_timeout_remove (dial->timer);
+
+ if ((dial->policy != GTK_UPDATE_CONTINUOUS) &&
+ (dial->old_value != dial->adjustment->value))
+ gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
+ }
+
+ return FALSE;
+}
+
+static gint
+gtk_dial_motion_notify (GtkWidget *widget,
+ GdkEventMotion *event)
+{
+ GtkDial *dial;
+ GdkModifierType mods;
+ gint x, y, mask;
+
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ dial = GTK_DIAL (widget);
+
+ if (dial->button != 0)
+ {
+ x = event->x;
+ y = event->y;
+
+ if (event->is_hint || (event->window != widget->window))
+ gdk_window_get_pointer (widget->window, &amp;x, &amp;y, &amp;mods);
+
+ switch (dial->button)
+ {
+ case 1:
+ mask = GDK_BUTTON1_MASK;
+ break;
+ case 2:
+ mask = GDK_BUTTON2_MASK;
+ break;
+ case 3:
+ mask = GDK_BUTTON3_MASK;
+ break;
+ default:
+ mask = 0;
+ break;
+ }
+
+ if (mods & mask)
+ gtk_dial_update_mouse (dial, x,y);
+ }
+
+ return FALSE;
+}
+
+static gint
+gtk_dial_timer (GtkDial *dial)
+{
+ g_return_val_if_fail (dial != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_DIAL (dial), FALSE);
+
+ if (dial->policy == GTK_UPDATE_DELAYED)
+ gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
+
+ return FALSE;
+}
+
+static void
+gtk_dial_update_mouse (GtkDial *dial, gint x, gint y)
+{
+ gint xc, yc;
+ gfloat old_value;
+
+ g_return_if_fail (dial != NULL);
+ g_return_if_fail (GTK_IS_DIAL (dial));
+
+ xc = GTK_WIDGET(dial)->allocation.width / 2;
+ yc = GTK_WIDGET(dial)->allocation.height / 2;
+
+ old_value = dial->adjustment->value;
+ dial->angle = atan2(yc-y, x-xc);
+
+ if (dial->angle < -M_PI/2.)
+ dial->angle += 2*M_PI;
+
+ if (dial->angle < -M_PI/6)
+ dial->angle = -M_PI/6;
+
+ if (dial->angle > 7.*M_PI/6.)
+ dial->angle = 7.*M_PI/6.;
+
+ dial->adjustment->value = dial->adjustment->lower + (7.*M_PI/6 - dial->angle) *
+ (dial->adjustment->upper - dial->adjustment->lower) / (4.*M_PI/3.);
+
+ if (dial->adjustment->value != old_value)
+ {
+ if (dial->policy == GTK_UPDATE_CONTINUOUS)
+ {
+ gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
+ }
+ else
+ {
+ gtk_widget_draw (GTK_WIDGET(dial), NULL);
+
+ if (dial->policy == GTK_UPDATE_DELAYED)
+ {
+ if (dial->timer)
+ gtk_timeout_remove (dial->timer);
+
+ dial->timer = gtk_timeout_add (SCROLL_DELAY_LENGTH,
+ (GtkFunction) gtk_dial_timer,
+ (gpointer) dial);
+ }
+ }
+ }
+}
+</verb></tscreen>
+
+<p>
+Cambios en el Adjustment por motivos externos significa que se le
+comunicarán a nuestro <em/widget/ mediante las señales <tt/changed/ y
+<tt/value_changed/. Los manejadores de estas funciones llaman a
+<tt/gtk_dial_update()/ para comprobar los argumentos, calcular el
+nuevo ángulo del puntero, y redibujar el <em/widget/ (llamando a
+<tt/gtk_widget_draw()/).
+
+<tscreen><verb>
+static void
+gtk_dial_update (GtkDial *dial)
+{
+ gfloat new_value;
+
+ g_return_if_fail (dial != NULL);
+ g_return_if_fail (GTK_IS_DIAL (dial));
+
+ new_value = dial->adjustment->value;
+
+ if (new_value < dial->adjustment->lower)
+ new_value = dial->adjustment->lower;
+
+ if (new_value > dial->adjustment->upper)
+ new_value = dial->adjustment->upper;
+
+ if (new_value != dial->adjustment->value)
+ {
+ dial->adjustment->value = new_value;
+ gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
+ }
+
+ dial->angle = 7.*M_PI/6. - (new_value - dial->adjustment->lower) * 4.*M_PI/3. /
+ (dial->adjustment->upper - dial->adjustment->lower);
+
+ gtk_widget_draw (GTK_WIDGET(dial), NULL);
+}
+
+static void
+gtk_dial_adjustment_changed (GtkAdjustment *adjustment,
+ gpointer data)
+{
+ GtkDial *dial;
+
+ g_return_if_fail (adjustment != NULL);
+ g_return_if_fail (data != NULL);
+
+ dial = GTK_DIAL (data);
+
+ if ((dial->old_value != adjustment->value) ||
+ (dial->old_lower != adjustment->lower) ||
+ (dial->old_upper != adjustment->upper))
+ {
+ gtk_dial_update (dial);
+
+ dial->old_value = adjustment->value;
+ dial->old_lower = adjustment->lower;
+ dial->old_upper = adjustment->upper;
+ }
+}
+
+static void
+gtk_dial_adjustment_value_changed (GtkAdjustment *adjustment,
+ gpointer data)
+{
+ GtkDial *dial;
+
+ g_return_if_fail (adjustment != NULL);
+ g_return_if_fail (data != NULL);
+
+ dial = GTK_DIAL (data);
+
+ if (dial->old_value != adjustment->value)
+ {
+ gtk_dial_update (dial);
+
+ dial->old_value = adjustment->value;
+ }
+}
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect2> Posibles mejoras
+<p>
+
+El <em/widget/ Dial tal y como lo hemos descrito tiene unas 670
+líneas de código. Aunque pueda parecer un poco exagerado, todavía
+no hemos escrito demasiado código, ya que la mayoría de las líneas
+son de ficheros de cabecera y de adornos. Todavía se le pueden hacer
+algunas mejoras a este <em/widget/:
+
+<itemize>
+<item> Si prueba el <em/widget/, verá que el puntero cambia a
+pantallazos cuando se le arrastra. Esto es debido a que todo el
+<em/widget/ se borra cada vez que se mueve el puntero, antes de
+redibujarse. Normalmente, la mejor forma de tratar este problema es
+dibujar en un <em/pixmap/ que no represente lo que se ve directamente
+en pantalla, y copiar el resultado final en la pantalla en sólo un
+paso. (El <em/widget/ ProgressBar funciona de esta forma.)
+
+<item> El usuario debería ser capaz de utilizar las flechas de arriba
+y abajo para aumentar y decrementar el valor.
+
+<item> Sería bonito si el <em/widget/ tuviese botones para
+incrementar y decrementar el valor a saltos más o menos
+grandes. Es posible utilizar <em/widgets/ botón, aunque también
+queremos que los botones pudiesen realizar la operación de incrementar
+o decrementar varias veces, mientras se mantenga el botón pulsado, tal
+y como lo hacen las flechas en una barra de desplazamiento. La mayoría
+del código para implementar todo esto lo podemos encontrar en el
+<em/widget/ GtkRange.
+
+<item> El <em/widget/ Dial puede utilizarse en un <em/widget/
+contenedor con un simple <em/widget/ hijo colocado en la parte
+inferior entre los botones antes mencionados. El usuario puede añadir
+(según prefiera) una etiqueta o un <em/widget/ entry para mostrar el
+valor actual del marcador.
+
+</itemize>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Aprendiendo más
+
+<p>
+Sólo se han descrito una pequeña parte de los muchos detalles
+involucrados en la creación de <em/widgets/, la mejor fuente de
+ejemplos es el código mismo de GTK. Hágase algunas preguntas acerca
+del <em/widget/ que desea crear: ¿es un <em/widget/ contenedor?
+¿Debe tener su propia ventana? ¿Es una modificación de un
+<em/widget/ existente? En ese momento busque un <em/widget/ similar, y
+comience a hacer los cambios.
+¡Buena suerte!
+
+<!-- ***************************************************************** -->
+<sect>Scribble, un sencillo programa de dibujo de ejemplo
+<!-- ***************************************************************** -->
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Objetivos
+
+<p>
+En esta sección, vamos a crear un sencillo programa de dibujo. En el
+proceso, vamos a examinar como se manejan los eventos de ratón, como
+dibujar en una ventana, y como mejorar el dibujado utilizando un
+<em/pixmap/ intermedio. Después de crear el programa de dibujo, lo
+ampliaremos añadiendole la posibilidad de utilizar dispositivos
+XInput, como tabletas digitalizadoras. GTK proporciona las rutinas que
+nos darán la posibilidad de obtener información extra, como la presión
+y la inclinación, de todo tipo de dispositivos de una forma sencilla.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Manejo de eventos
+
+<p>
+Las señales GTK sobre las que ya hemos discutido son para las
+acciones de alto nivel, como cuando se selecciona un elemento de un
+menú. Sin embargo a veces es útil tratar con los acontecimientos a
+bajo nivel, como cuando se mueve el ratón, o cuando se está
+presionando una tecla. También hay señales GTK relacionadas con
+estos <em/eventos/ de bajo nivel. Los manejadores de estas señales
+tienen un parámetro extra que es un puntero a una estructura
+conteniendo información sobre el evento. Por ejemplo, a los manejadores
+de los eventos de movimiento se les pasa una estructura
+<tt/GdkEventMotion/ que es (en parte) así:
+
+<tscreen><verb>
+struct _GdkEventMotion
+{
+ GdkEventType type;
+ GdkWindow *ventana;
+ guint32 time;
+ gdouble x;
+ gdouble y;
+ ...
+ guint state;
+ ...
+};
+</verb></tscreen>
+
+<tt/type/ adquirirá su valor adecuado dependiendo del tipo de evento,
+en nuestro caso <tt/GDK_MOTION_NOTIFY/, <tt/ventana/ es la ventana en
+la que ocurre el evento. <tt/x/ e <tt/y/ dan las coordenadas del
+evento, y <tt/state/ especifica cual es la modificación que ha habido
+cuando ocurrió el evento (esto es, especifica que teclas han cambiado
+su estado y que botones del ratón se han presionado.) Es la
+operación OR (O) de algunos de los siguientes valores:
+
+<tscreen><verb>
+GDK_SHIFT_MASK
+GDK_LOCK_MASK
+GDK_CONTROL_MASK
+GDK_MOD1_MASK
+GDK_MOD2_MASK
+GDK_MOD3_MASK
+GDK_MOD4_MASK
+GDK_MOD5_MASK
+GDK_BUTTON1_MASK
+GDK_BUTTON2_MASK
+GDK_BUTTON3_MASK
+GDK_BUTTON4_MASK
+GDK_BUTTON5_MASK
+</verb></tscreen>
+
+<p>
+Como con las otras señales, para especificar que es lo que pasa cuando
+ocurre un evento, llamaremos a <tt>gtk_signal_connect()</tt>. Pero
+también necesitamos decirle a GTK sobre que eventos queremos ser
+informados. Para ello, llamaremos a la función:
+
+<tscreen><verb>
+void gtk_widget_set_events (GtkWidget *widget,
+ gint events);
+</verb></tscreen>
+
+El segundo campo especifica los eventos en los que estamos
+interesados. Es el OR (O) de las constantes que especifican los
+diferentes tipos de eventos. Por las referencias futuras que podamos
+hacer, presentamos aquí los tipos de eventos que hay disponibles:
+
+<tscreen><verb>
+GDK_EXPOSURE_MASK
+GDK_POINTER_MOTION_MASK
+GDK_POINTER_MOTION_HINT_MASK
+GDK_BUTTON_MOTION_MASK
+GDK_BUTTON1_MOTION_MASK
+GDK_BUTTON2_MOTION_MASK
+GDK_BUTTON3_MOTION_MASK
+GDK_BUTTON_PRESS_MASK
+GDK_BUTTON_RELEASE_MASK
+GDK_KEY_PRESS_MASK
+GDK_KEY_RELEASE_MASK
+GDK_ENTER_NOTIFY_MASK
+GDK_LEAVE_NOTIFY_MASK
+GDK_FOCUS_CHANGE_MASK
+GDK_STRUCTURE_MASK
+GDK_PROPERTY_CHANGE_MASK
+GDK_PROXIMITY_IN_MASK
+GDK_PROXIMITY_OUT_MASK
+</verb></tscreen>
+
+Hay unos cuantas sutilezas que debemos respetar cuando llamamos a
+<tt/gtk_widget_set_events()/. Primero, debemos llamar a esta función
+antes de que se cree la ventana X para el <em/widget/ GTK. En
+términos prácticos, significa que debemos llamarla inmediatamente
+después de crear el <em/widget/. Segundo, el <em/widget/ debe tener
+una ventana X asociado. Por motivos de eficiencia, hay muchos
+<em/widgets/ que no tienen su propia ventana, sino que dibujan en la
+de su padre. Estos <em/widgets/ son:
+
+<tscreen><verb>
+GtkAlignment
+GtkArrow
+GtkBin
+GtkBox
+GtkImage
+GtkItem
+GtkLabel
+GtkPixmap
+GtkScrolledWindow
+GtkSeparator
+GtkTable
+GtkAspectFrame
+GtkFrame
+GtkVBox
+GtkHBox
+GtkVSeparator
+GtkHSeparator
+</verb></tscreen>
+
+Para capturar eventos para estos <em/widgets/, necesita utilizar un
+<em/widget/ EventBox. Vea la sección <ref
+id="sec_The_EventBox_Widget" name="El widget EventBox"> para más
+detalles.
+
+<p>
+Para nuestro programa de dibujo, queremos saber cuando se presiona el
+botón del ratón y cuando se mueve, por lo que debemos especificar
+los eventos <tt/GDK_POINTER_MOTION_MASK/ y
+<tt/GDK_BUTTON_PRESS_MASK/. También queremos saber cuando necesitamos
+redibujar nuestra ventana, por lo que especificaremos el evento
+<tt/GDK_EXPOSURE_MASK/. Aunque queremos estar informados mediante un
+evento <tt/Configure/ cuando cambie el tamaño de nuestra ventana, no
+tenemos que especificar la correspondiente <tt/GDK_STRUCTURE_MASK/,
+porque ya está activada por defecto para todas las ventanas.
+
+<p>
+Tenemos un problema con lo que acabamos de hacer, y tiene que ver con
+la utilización de <tt/GDK_POINTER_MOTION_MASK/. Si especificamos este
+evento, el servidor añadirá un evento de movimiento a la cola de
+eventos cada vez que el usuario mueva el ratón. Imagine que nos
+cuesta 0'1 segundo tratar el evento de movimiento, pero que el
+servidor X añade a la cola un nuevo evento de moviento cada 0'05
+segundos. Pronto nos iremos quedando retrasados con respecto al resto
+de los eventos. Si el usuario dibuja durante 5 segundos, ¡nos llevará
+otros 5 segundos el cazarle después de que hay levantado el botón
+del ratón! Lo que queremos es sólo un evento de movimiento por cada
+evento que procesemos. La manera de hacerlo es especificando
+<tt/GDK_POINTER_MOTION_HINT_MASK/.
+
+<p>
+Cuando especificamos <tt/GDK_POINTER_MOTION_HINT_MASK/, el servidor
+nos envia un evento de movimiento la primera ver que el puntero se
+mueve depués de entrar en nuestra ventana, o después de que se
+apriete o se suelte un botón (y se reciba el evento
+correspondiente). Los eventos de movimiento restantes se eliminarán a
+no ser que preguntemos especificamente por la posición del puntero
+utilizando la función:
+
+<tscreen><verb>
+GdkWindow* gdk_window_get_pointer (GdkWindow *ventana,
+ gint *x,
+ gint *y,
+ GdkModifierType *mask);
+</verb></tscreen>
+
+(Hay otra función, <tt>gtk_widget_get_pointer()</tt> que tiene una
+interfaz más sencillo, pero esta simplificación le resta utilidad, ya
+que sólo devuelve la posición del ratón, y no si alguno de sus botones
+está presionado.)
+
+<p>
+El código para establecer los eventos para nuestra ventana es el
+siguiente:
+
+<tscreen><verb>
+ gtk_signal_connect (GTK_OBJECT (drawing_area), "expose_event",
+ (GtkSignalFunc) expose_event, NULL);
+ gtk_signal_connect (GTK_OBJECT(drawing_area),"configure_event",
+ (GtkSignalFunc) configure_event, NULL);
+ gtk_signal_connect (GTK_OBJECT (drawing_area), "motion_notify_event",
+ (GtkSignalFunc) motion_notify_event, NULL);
+ gtk_signal_connect (GTK_OBJECT (drawing_area), "button_press_event",
+ (GtkSignalFunc) button_press_event, NULL);
+
+ gtk_widget_set_events (drawing_area, GDK_EXPOSURE_MASK
+ | GDK_LEAVE_NOTIFY_MASK
+ | GDK_BUTTON_PRESS_MASK
+ | GDK_POINTER_MOTION_MASK
+ | GDK_POINTER_MOTION_HINT_MASK);
+</verb></tscreen>
+
+Vamos a dejar los manejadores de los eventos <tt/expose_event/ y
+<tt/configure_event/ para después. Los manejadores de
+<tt/motion_notify_event/ y de <tt/button_press_event/ son bastante
+simples:
+
+<tscreen><verb>
+static gint
+button_press_event (GtkWidget *widget, GdkEventButton *event)
+{
+ if (event->button == 1 &amp;&amp; pixmap != NULL)
+ draw_brush (widget, event->x, event->y);
+
+ return TRUE;
+}
+
+static gint
+motion_notify_event (GtkWidget *widget, GdkEventMotion *event)
+{
+ int x, y;
+ GdkModifierType state;
+
+ if (event->is_hint)
+ gdk_window_get_pointer (event->window, &amp;x, &amp;y, &amp;state);
+ else
+ {
+ x = event->x;
+ y = event->y;
+ state = event->state;
+ }
+
+ if (state &amp; GDK_BUTTON1_MASK &amp;&amp; pixmap != NULL)
+ draw_brush (widget, x, y);
+
+ return TRUE;
+}
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> El <em/widget/ DrawingArea, y dibujando
+
+<p>
+Vamos a pasar al proceso de dibujar en la pantalla. El <em/widget/ que
+utilizaremos será el DrawingArea. Un <em/widget/ DrawingArea es
+esencialmente una ventana X y nada más. Es un lienzo en blanco en
+el que podemos dibujar lo que queramos. Crearemos un área de dibujo
+utilizando la llamada:
+
+<tscreen><verb>
+GtkWidget* gtk_drawing_area_new (void);
+</verb></tscreen>
+
+Se puede especificar un tamaño por defecto para el <em/widget/
+llamando a:
+
+<tscreen><verb>
+void gtk_drawing_area_size (GtkDrawingArea *darea,
+ gint width,
+ gint height);
+</verb></tscreen>
+
+Se puede cambiar el tamaño por defecto, como para todos los
+<em/widgets/, llamando a <tt/gtk_widget_set_usize()/, y esto, además,
+puede cambiarse si el usuario cambia manualmente el tamaño de la
+ventana que contiene el área de dibujo.
+
+<p>
+Debemos hacer notar que cuando creamos un <em/widget/ DrawingArea,
+seremos <em/completamente/ responsables de dibujar su contenido. Si
+nuestra ventana se tapa y se vuelve a poner al descubierto,
+obtendremos un evento de exposición y deberemos redibujar lo que se
+había tapado.
+
+<p>
+Tener que recordar todo lo que se dibujó en la pantalla para que
+podamos redibujarla convenientemente es, por decirlo de alguna manera
+suave, una locura. Además puede quedar mal si hay que borrar partes
+de la pantalla y hay que redibujarlas paso a paso. La solución a este
+problema es utilizar un <em>pixmap</em> intermedio. En lugar de
+dibujar directamente en la pantalla, dibujaremos en una imagen que
+estará almacenada en la memoria del servidor, pero que no se mostrará,
+y cuando cambie la imagen o se muestren nuevas partes de
+la misma, copiaremos las porciones relevantes en la pantalla.
+
+<p>
+Para crear un <em/pixmap/ intermedio, llamaremos a la función:
+
+<tscreen><verb>
+GdkPixmap* gdk_pixmap_new (GdkWindow *ventana,
+ gint width,
+ gint height,
+ gint depth);
+</verb></tscreen>
+
+El parámetro <tt/widget/ especifica una ventana GDK de las que este
+<em/pixmap/ tomará algunas propiedades. <tt/width/ y <tt/height/
+especifican el tamaño del <em/pixmap/. <tt/depth/ especifica la
+<em/profundidad del color/, que es el número de bits por pixel de la
+nueva ventana. Si la profundidad que se especifica es <tt/-1/, se
+utilizará la misma profundidad de color que tenga la <tt/ventana/.
+
+<p>
+Creamos nuestro <em/pixmap/ en nuestro manejador del evento
+<tt/configure_event/. Este evento se genera cada vez que cambia el
+tamaño de la ventana, incluyendo cuando ésta se crea.
+
+<tscreen><verb>
+/* Backing pixmap for drawing area */
+static GdkPixmap *pixmap = NULL;
+
+/* Create a new backing pixmap of the appropriate size */
+static gint
+configure_event (GtkWidget *widget, GdkEventConfigure *event)
+{
+ if (pixmap)
+ gdk_pixmap_unref(pixmap);
+
+ pixmap = gdk_pixmap_new(widget->window,
+ widget->allocation.width,
+ widget->allocation.height,
+ -1);
+ gdk_draw_rectangle (pixmap,
+ widget->style->white_gc,
+ TRUE,
+ 0, 0,
+ widget->allocation.width,
+ widget->allocation.height);
+
+ return TRUE;
+}
+</verb></tscreen>
+
+La llamada a <tt/gdk_draw_rectangle()/ rellena todo el <em/pixmap/ de
+blanco. Hablaremos más de todo esto en un momento.
+
+<p>
+Nuestro manejador del evento de exposición simplemente copia la
+porción relevante del <em/pixmap/ en la pantalla (determinaremos la
+zona a redibujar utilizando el campo <tt/event->area/ del evento de
+exposición):
+
+<tscreen><verb>
+/* Redraw the screen from the backing pixmap */
+static gint
+expose_event (GtkWidget *widget, GdkEventExpose *event)
+{
+ gdk_draw_pixmap(widget->window,
+ widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
+ pixmap,
+ event->area.x, event->area.y,
+ event->area.x, event->area.y,
+ event->area.width, event->area.height);
+
+ return FALSE;
+}
+</verb></tscreen>
+
+Ahora ya sabemos como mantener la pantalla actualizada con el
+contenido de nuestro <em/pixmap/, pero ¿cómo podemos dibujar algo
+interesante en nuestro <em/pixmap/? Hay un gran número de llamadas en
+la biblioteca GDK para dibujar en los <em/dibujables/. Un dibujable es
+simplemente algo sobre lo que se puede dibujar. Puede ser una ventana,
+un <em/pixmap/, un <em/bitmap/ (una imagen en blanco y negro), etc. Ya
+hemos visto arriba dos de estas llamadas,
+<tt>gdk_draw_rectangle()</tt> y <tt>gdk_draw_pixmap()</tt>. La lista
+completa de funciones para dibujar es:
+
+<tscreen><verb>
+gdk_draw_line ()
+gdk_draw_rectangle ()
+gdk_draw_arc ()
+gdk_draw_polygon ()
+gdk_draw_string ()
+gdk_draw_text ()
+gdk_draw_pixmap ()
+gdk_draw_bitmap ()
+gdk_draw_image ()
+gdk_draw_points ()
+gdk_draw_segments ()
+</verb></tscreen>
+
+Ver la documentación de estas funciones o el fichero de cabecera
+<tt>&lt;gdk/gdk.h&gt;</tt> para obtener más detalles sobre estas
+funciones. Todas comparten los dos primeros argumentos. El primero es
+el dibujable en el que se dibujará, y el segundo argumento es un
+<em/contexto gráfico/ (GC).
+
+<p>
+Un contexto gráfico reúne la información sobre cosas como el color
+de fondo y del color de lo que se dibuja, el ancho de la línea,
+etc... GDK tiene un conjunto completo de funciones para crear y
+modificar los contextos gráficos. Cada <em/widget/ tiene un GC
+asociado. (Que puede modificarse en un fichero gtkrc, ver la sección
+«Ficheros rc de GTK».) Estos, junto con otras cosas, almacenan
+GC's. Algunos ejemplos de como acceder a estos GC's son:
+
+<tscreen><verb>
+widget->style->white_gc
+widget->style->black_gc
+widget->style->fg_gc[GTK_STATE_NORMAL]
+widget->style->bg_gc[GTK_WIDGET_STATE(widget)]
+</verb></tscreen>
+
+Los campos <tt>fg_gc</tt>, <tt>bg_gc</tt>, <tt>dark_gc</tt>, y
+<tt>light_gc</tt> se indexan con un parámetro del tipo
+<tt/GtkStateType/ que puede tomar uno de los valores:
+
+<tscreen><verb>
+GTK_STATE_NORMAL,
+GTK_STATE_ACTIVE,
+GTK_STATE_PRELIGHT,
+GTK_STATE_SELECTED,
+GTK_STATE_INSENSITIVE
+</verb></tscreen>
+
+Por ejemplo, para el <tt/GTK_STATE_SELECTED/, el color que se utiliza
+para pintar por defecto es el blanco y el color del fondo por defecto,
+es el azul oscuro.
+
+<p>
+Nuestra función <tt/draw_brush()/, que es la que dibuja en la
+pantalla, será la siguiente:
+
+<tscreen><verb>
+/* Draw a rectangle on the screen */
+static void
+draw_brush (GtkWidget *widget, gdouble x, gdouble y)
+{
+ GdkRectangle update_rect;
+
+ update_rect.x = x - 5;
+ update_rect.y = y - 5;
+ update_rect.width = 10;
+ update_rect.height = 10;
+ gdk_draw_rectangle (pixmap,
+ widget->style->black_gc,
+ TRUE,
+ update_rect.x, update_rect.y,
+ update_rect.width, update_rect.height);
+ gtk_widget_draw (widget, &amp;update_rect);
+}
+</verb></tscreen>
+
+Después de que dibujemos el rectángulo representando la brocha en el
+<em/pixmap/ llamaremos a la función:
+
+<tscreen><verb>
+void gtk_widget_draw (GtkWidget *widget,
+ GdkRectangle *area);
+</verb></tscreen>
+
+que le informa a X de que la zona dada por el parámetro <tt/area/
+necesita actualizarse. X generará un evento de exposición
+(combinando posiblemente distintas zonas pasadas mediante distintas
+llamadas a <tt/gtk_widget_draw()/) que hará que nuestro manejador de
+eventos de exposición copie las porciones relevantes en la pantalla.
+
+<p>
+Ya hemos cubierto el programa de dibujo completo, excepto unos cuantos
+detalles mundanos como crear la ventana principal. El código completo
+está disponible en el mismo lugar en el que consiguió este tutorial,
+o en:
+
+<htmlurl url="http://www.gtk.org/~otaylor/gtk/tutorial/"
+name="http://www.gtk.org/~otaylor/gtk/tutorial/">
+
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Añadiendo la capacidad de utilizar XInput
+
+<p>
+Ahora es posible comprar dispositos de entrada bastante baratos, como
+tabletas digitalizadoras, que permiten dibujar de forma artística
+mucho más fácilmente de cómo lo haríamos con un ratón. La forma
+más sencilla de utilizar estos dispositivos es simplemente
+reemplazando a los ratones, pero así perdemos muchas de las ventajas
+de este tipo de dispositivos, como por ejemplo:
+
+<itemize>
+<item> Sensibilidad a la presión
+<item> Información sobre la inclinación
+<item> Colocación subpixel
+<item> Multiples entradas (por ejemplo, un lápiz con una punta y una
+goma)
+</itemize>
+
+Para información sobre la extensión XInput, ver el <htmlurl
+url="http://www.msc.cornell.edu/~otaylor/xinput/XInput-HOWTO.html"
+name="XInput-HOWTO">.
+
+<p>
+Si examinamos la definición completa de, por ejemplo, la estructura
+<tt/GdkEventMotion/, veremos que tiene campos para almacenar la
+información de los dispositivos extendidos.
+
+<tscreen><verb>
+struct _GdkEventMotion
+{
+ GdkEventType type;
+ GdkWindow *ventana;
+ guint32 time;
+ gdouble x;
+ gdouble y;
+ gdouble pressure;
+ gdouble xtilt;
+ gdouble ytilt;
+ guint state;
+ gint16 is_hint;
+ GdkInputSource source;
+ guint32 deviceid;
+};
+</verb></tscreen>
+
+<tt/pressure/ da la presión como un número de coma flotante entre 0
+y 1. <tt/xtilt/ e <tt/ytilt/ pueden tomar valores entre -1 y 1,
+correspondiendo al grado de inclinación en cada dirección. <tt/source/
+y <tt/deviceid/ especifican el dispositivo para el que ocurre el
+evento de dos maneras diferentes. <tt/source/ da alguna información
+simple sobre el tipo de dispositivo. Puede tomar los valores de la
+enumeración siguiente:
+
+<tscreen><verb>
+GDK_SOURCE_MOUSE
+GDK_SOURCE_PEN
+GDK_SOURCE_ERASER
+GDK_SOURCE_CURSOR
+</verb></tscreen>
+
+<tt/deviceid/ especifica un número único ID para el dispositivo. Puede
+utilizarse para obtener más información sobre el dispositivo
+utilizando la función <tt/gdk_input_list_devices()/ (ver abajo). El
+valor especial <tt/GDK_CORE_POINTER/ se utiliza para el núcleo del
+dispositivo apuntador. (Normalmente el ratón.)
+
+<sect2> Activando la información del dispositivo extendido
+
+<p>
+Para informar a GTK de nuestro interés en la información sobre los
+dispositivos extendidos, sólo tenemos que añadirle una línea a
+nuestro programa:
+
+<tscreen><verb>
+gtk_widget_set_extension_events (drawing_area, GDK_EXTENSION_EVENTS_CURSOR);
+</verb></tscreen>
+
+Dando el valor <tt/GDK_EXTENSION_EVENTS_CURSOR/ decimos que estamos
+interesados en los eventos de extensión, pero sólo si no tenemos que
+dibujar nuestro propio cursor. Ver la sección <ref
+id="sec_Further_Sophistications" name="Sofisticaciones adicionales">
+más abajo para obtener más información sobre el dibujado del
+cursor. También podríamos dar los valores
+<tt/GDK_EXTENSION_EVENTS_ALL/ si queremos dibujar nuestro propio
+cursor, o <tt/GDK_EXTENSION_EVENTS_NONE/ para volver al estado
+inicial.
+
+<p>
+Todavía no hemos llegado al final de la historia. Por defecto, no hay
+ningún dispositivo extra activado. Necesitamos un mecanismo que
+permita a los usuarios activar y configurar sus dispositivos
+extra. GTK proporciona el <em/widget/ InputDialog para automatizar el
+proceso. El siguiente procedimiento utiliza el <em/widget/
+InputDialog. Crea el cuadro de diálogo si no ha sido ya creado, y lo
+pone en primer plano en caso contrario.
+
+<tscreen><verb>
+void
+input_dialog_destroy (GtkWidget *w, gpointer data)
+{
+ *((GtkWidget **)data) = NULL;
+}
+
+void
+create_input_dialog ()
+{
+ static GtkWidget *inputd = NULL;
+
+ if (!inputd)
+ {
+ inputd = gtk_input_dialog_new();
+
+ gtk_signal_connect (GTK_OBJECT(inputd), "destroy",
+ (GtkSignalFunc)input_dialog_destroy, &amp;inputd);
+ gtk_signal_connect_object (GTK_OBJECT(GTK_INPUT_DIALOG(inputd)->close_button),
+ "clicked",
+ (GtkSignalFunc)gtk_widget_hide,
+ GTK_OBJECT(inputd));
+ gtk_widget_hide ( GTK_INPUT_DIALOG(inputd)->save_button);
+
+ gtk_widget_show (inputd);
+ }
+ else
+ {
+ if (!GTK_WIDGET_MAPPED(inputd))
+ gtk_widget_show(inputd);
+ else
+ gdk_window_raise(inputd->window);
+ }
+}
+</verb></tscreen>
+
+(Tome nota de la manera en que hemos manejado el cuadro de
+diálogo. Conectando la señal <tt/destroy/, nos aseguramos de que no
+tendremos un puntero al cuadro de diálogo después de que haya sido
+destruido, lo que nos podría llevar a un segfault.)
+
+<p>
+El InputDialog tiene dos botones «Cerrar» y «Guardar», que por
+defecto no tienen ninguna acción asignada. En la función anterior
+hemos hecho que «Cerrar» oculte el cuadro de diálogo, ocultando el
+botón «Guardar», ya que no implementaremos en este programa la
+acción de guardar las opciones de XInput.
+
+<sect2> Utilizando la información de los dispositivos extras
+
+<p>
+Una vez hemos activado el dispositivo, podemos utilizar la
+información que hay respecto a los dispositivos extendidos en los
+campos extras de las estructuras de los eventos. De hecho, es bueno
+utilizar esa información ya que esos campos tienen unos valores por
+defecto razonables aún cuando no se activen los eventos extendidos.
+
+<p>
+Un cambio que tenemos que hacer es llamar a
+<tt/gdk_input_window_get_pointer()/ en vez de a
+<tt/gdk_window_get_pointer/. Esto es necesario porque
+<tt/gdk_window_get_pointer/ no devuelve la información de los
+dispositivos extra.
+
+<tscreen><verb>
+void gdk_input_window_get_pointer (GdkWindow *ventana,
+ guint32 deviceid,
+ gdouble *x,
+ gdouble *y,
+ gdouble *pressure,
+ gdouble *xtilt,
+ gdouble *ytilt,
+ GdkModifierType *mask);
+</verb></tscreen>
+
+Cuando llamamos a esta función, necesitamos especificar tanto el ID
+del dispositivo como la ventana. Normalmente, obtendremos el ID del
+dispositivo del campo <tt/deviceid/ de una estructura de evento. De
+nuevo, esta función devolverá valores razonables cuando no estén
+activados los eventos extendidos. (En ese caso, <tt/event->deviceid/
+tendrá el valor <tt/GDK_CORE_POINTER/).
+
+Por tanto la estructura básica de nuestros manejadores de los
+eventos de movimiento y de pulsación del botón del ratón no
+cambiarán mucho - sólo tenemos que añadir código para manejar la
+información extra.
+
+<tscreen><verb>
+static gint
+button_press_event (GtkWidget *widget, GdkEventButton *event)
+{
+ print_button_press (event->deviceid);
+
+ if (event->button == 1 &amp;&amp; pixmap != NULL)
+ draw_brush (widget, event->source, event->x, event->y, event->pressure);
+
+ return TRUE;
+}
+
+static gint
+motion_notify_event (GtkWidget *widget, GdkEventMotion *event)
+{
+ gdouble x, y;
+ gdouble pressure;
+ GdkModifierType state;
+
+ if (event->is_hint)
+ gdk_input_window_get_pointer (event->window, event->deviceid,
+ &amp;x, &amp;y, &amp;pressure, NULL, NULL, &amp;state);
+ else
+ {
+ x = event->x;
+ y = event->y;
+ pressure = event->pressure;
+ state = event->state;
+ }
+
+ if (state &amp; GDK_BUTTON1_MASK &amp;&amp; pixmap != NULL)
+ draw_brush (widget, event->source, x, y, pressure);
+
+ return TRUE;
+}
+</verb></tscreen>
+
+También tenemos que hacer algo con la nueva información. Nuestra
+nueva función <tt/draw_brush()/ dibuja con un color diferente
+dependiendo de <tt/event->source/ y cambia el tamaño de la brocha
+dependiendo de la presión.
+
+<tscreen><verb>
+/* Draw a rectangle on the screen, size depending on pressure,
+ and color on the type of device */
+static void
+draw_brush (GtkWidget *widget, GdkInputSource source,
+ gdouble x, gdouble y, gdouble pressure)
+{
+ GdkGC *gc;
+ GdkRectangle update_rect;
+
+ switch (source)
+ {
+ case GDK_SOURCE_MOUSE:
+ gc = widget->style->dark_gc[GTK_WIDGET_STATE (widget)];
+ break;
+ case GDK_SOURCE_PEN:
+ gc = widget->style->black_gc;
+ break;
+ case GDK_SOURCE_ERASER:
+ gc = widget->style->white_gc;
+ break;
+ default:
+ gc = widget->style->light_gc[GTK_WIDGET_STATE (widget)];
+ }
+
+ update_rect.x = x - 10 * pressure;
+ update_rect.y = y - 10 * pressure;
+ update_rect.width = 20 * pressure;
+ update_rect.height = 20 * pressure;
+ gdk_draw_rectangle (pixmap, gc, TRUE,
+ update_rect.x, update_rect.y,
+ update_rect.width, update_rect.height);
+ gtk_widget_draw (widget, &amp;update_rect);
+}
+</verb></tscreen>
+
+<sect2> Obteniendo más información de un dispositivo
+
+<p>
+Como ejemplo de como podemos obtener más información de un
+dispositivo, nuestro programa imprimirá el nombre del dispositivo que
+genera cada pulsación de botón. Para encontrar el nombre de un
+dispositivo, llamaremos a la función:
+
+<tscreen><verb>
+GList *gdk_input_list_devices (void);
+</verb></tscreen>
+
+que devuelve una GList (una lista enlazada de la biblioteca glib)
+de estructuras <tt/GdkDeviceInfo/. La estructura <tt/GdkDeviceInfo/ se
+define como:
+
+<tscreen><verb>
+struct _GdkDeviceInfo
+{
+ guint32 deviceid;
+ gchar *name;
+ GdkInputSource source;
+ GdkInputMode mode;
+ gint has_cursor;
+ gint num_axes;
+ GdkAxisUse *axes;
+ gint num_keys;
+ GdkDeviceKey *keys;
+};
+</verb></tscreen>
+
+Muchos de estos campos son información de configuración que puede
+ignorar, a menos que quiera permitir la opción de grabar la
+configuración de XInput. El campo que nos interesa ahora es <tt/name/
+que es simplemente el nombre que X le asigna al dispositivo. El otro
+campo que no tiene información sobre la configuración es
+<tt/has_cursor/. Si <tt/has_cursor/ es falso, tendremos que dibujar
+nuestro propio cursor. Pero como hemos especificado
+<tt/GDK_EXTENSION_EVENTS_CURSOR/, no tendremos que preocuparnos por
+esto.
+
+<p>
+Nuestra función <tt/print_button_press()/ simplemente recorre la
+lista devuelta hasta que encuentra una coincidencia, y entonces
+imprime el nombre del dispositivo.
+
+<tscreen><verb>
+static void
+print_button_press (guint32 deviceid)
+{
+ GList *tmp_list;
+
+ /* gdk_input_list_devices returns an internal list, so we shouldn't
+ free it afterwards */
+ tmp_list = gdk_input_list_devices();
+
+ while (tmp_list)
+ {
+ GdkDeviceInfo *info = (GdkDeviceInfo *)tmp_list->data;
+
+ if (info->deviceid == deviceid)
+ {
+ printf("Button press on device '%s'\n", info->name);
+ return;
+ }
+
+ tmp_list = tmp_list->next;
+ }
+}
+</verb></tscreen>
+
+Con esto hemos completado los cambios para `XInputizar' nuestro
+programa. Como ocurría con la primera versión, el código completo se
+encuentra disponible en el mismo sitio donde obtuvo este tutorial, o
+desde:
+
+<htmlurl url="http://www.gtk.org/~otaylor/gtk/tutorial/"
+name="http://www.gtk.org/~otaylor/gtk/tutorial/">
+
+
+<sect2> Sofisticaciones adicionales <label id="sec_Further_Sophistications">
+
+<p>
+Aunque ahora nuestro programa admite XInput bastante bien, todavía
+falla en algunas características que deberían estar disponibles en una
+aplicación bien hecha. Primero, el usuario no debería tener que
+configurar su dispositivo cada vez que ejecute el programa, por lo que
+debería estar disponible la opción de guardar la configuración del
+dispositivo. Esto se hace recorriendo el resultado de
+<tt/gdk_input_list_devices()/ y escribiendo la configuración en un
+fichero.
+
+<p>
+Para cargar la configuración del dispositivo cuando se vuelva a
+ejecutar el programa, puede utilizar las funciones que proporciona GDK
+para cambiar la configuración de los dispositivos:
+
+<tscreen><verb>
+gdk_input_set_extension_events()
+gdk_input_set_source()
+gdk_input_set_mode()
+gdk_input_set_axes()
+gdk_input_set_key()
+</verb></tscreen>
+
+(La lista devuelta por <tt/gdk_input_list_devices()/ no debería
+modificarse directamente.) Podemos encontrar un ejemplo de como debe
+utilizarse en el programa de dibujo <tt/gsumi/. (Disponible en
+<htmlurl url="http://www.msc.cornell.edu/~otaylor/gsumi/"
+name="http://www.msc.cornell.edu/~otaylor/gsumi/">) Estaría bien
+tener un procedimiento estándar para poder hacer todo esto en
+cualquier aplicaciones. Probablemente se llegue a esto en una capa
+superior a GTK, quizás en la biblioteca GNOME.
+
+<p>
+El programa tiene otra carencia importante que ya hemos mencionado más
+arriba, y es la falta del cursor. Ninguna plataforma distinta de
+XFree86 permite utilizar simultaneamente un dispositivo como puntero
+núcleo y como dispositivo directamente utilizable por una
+aplicación. Ver el <url
+url="http://www.msc.cornell.edu/~otaylor/xinput/XInput-HOWTO.html"
+name="XInput-HOWTO"> para más información sobre esto. Con esto
+queremos decir que si quiere tener la máxima audiencia necesita
+dibujar su propio cursor.
+
+<p>
+Una aplicación que dibuja su propio cursor necesita hacer dos cosas:
+determinar si el dispositivo actual necesita que se dibuje un cursor o
+no, y determinar si el dispositivo está «próximo». (Si el
+dispositivo es una tableta digitalizadora, queda muy bonito que el
+cursor desaparezca cuando el lápiz se separa de la tableta. Cuando el
+lápiz está tocando la tableta, se dice que el dispositivo está
+«próximo»). Lo primero se hace buscando la lista de dispositivos,
+tal y como hicimos para encontrar el nombre del dispositivo. Lo
+segundo se consigue seleccionando los eventos
+<em/proximity_out/. Podemos encontrar un ejemplo de como dibujar
+nuestro propio cursor en el programa `testinput' que viene con la
+distribución de GTK.
+
+<!-- ***************************************************************** -->
+<sect>Trucos para escribir aplicaciones GTK
+<!-- ***************************************************************** -->
+
+<p>
+Esta sección es sólo un compendio de sabiduria, de guías generales
+de estilo y de consejos para crear buenas aplicaciones GTK. Y es
+totalmente inútil por ahora ya que esta frase es sólo un tópico :)
+
+¡Utilice GNU autoconf y automake! Son sus amigos :) Pretendo poner
+aquí una rápida introducción a ambos.
+
+<!-- ***************************************************************** -->
+<sect>Contribuyendo <label id="sec_Contributing">
+<!-- ***************************************************************** -->
+
+<p>
+Este documento, como muchos otros grandes paquetes de programas que
+hay por ahí, fue creado de forma libre por voluntarios. Si comprende
+algo de GTK que todavía no se ha documentado, por favor piense en
+contribuir a este documento.
+
+<p>
+Si decide contribuir, por favor mande un correo-e con su texto a Tony
+Gale, <tt><htmlurl url="mailto:gale@gtk.org"
+name="gale@gtk.org"></tt>. Recuerde que todas las partes que componen
+este documento son libre, y cualquier añadido que haga debe ser
+libre. Esto es, la gente debe de poder utilizar cualquier trozo de sus
+ejemplos en sus programas, podrán distribuir copias de su documento
+como deseen, etc...
+<p>
+Gracias.
+
+<!-- ***************************************************************** -->
+<sect>Créditos
+<!-- ***************************************************************** -->
+<p>
+Quiero agradecer a las siguientes personas por sus contribuciones a
+este texto.
+
+<itemize>
+<item>Bawer Dagdeviren, <tt><htmlurl url="mailto:chamele0n@geocities.com"
+name="chamele0n@geocities.com"></tt> por el tutorial sobre los menús.
+
+<item>Raph Levien, <tt><htmlurl url="mailto:raph@acm.org"
+name="raph@acm.org"></tt> por el «hola mundo» a la GTK, el
+empaquetado de <em/widgets/, y su sabiduría general. Ha donado
+generosamente un hogar para este tutorial.
+
+<item>Peter Mattis, <tt><htmlurl url="mailto:petm@xcf.berkeley.edu"
+name="petm@xcf.berkeley.edu"></tt> por el más simple de los programas
+GTK... y por la posibilidad de hacerlo :)
+
+<item>Werner Koch <tt><htmlurl url="mailto:werner.koch@guug.de"
+name="werner.koch@guug.de"></tt> por convertir el texto original a
+SGML, y por la jerarquia de clases de <em/widgets/.
+
+<item>Mark Crichton <tt><htmlurl url="mailto:crichton@expert.cc.purdue.edu"
+name="crichton@expert.cc.purdue.edu"></tt> por el código del menú
+factory, y el tutorial sobre el empaquetamiento de las tablas.
+
+<item>Owen Taylor <tt><htmlurl url="mailto:owt1@cornell.edu"
+name="owt1@cornell.edu"></tt> por la sección sobre el <em/widget/
+EventBox (y el parche para el distro). También es el responsable
+del código de las selecciones y el tutorial, así como de la
+sección de escribiendo su propio <em/widget/ GTK, y la aplicación de
+ejemplo. ¡Muchas gracias por toda tu ayuda, Owen!
+
+<item>Mark VanderBoom <tt><htmlurl url="mailto:mvboom42@calvin.edu"
+name="mvboom42@calvin.edu"></tt> por su fantástico trabajo sobre los
+<em/widgets/ Notebook, Progress Bar, Dialog, y selección de ficheros.
+¡Muchas gracias Mark!
+Has sido de una gran ayuda.
+
+<item>Tim Janik <tt><htmlurl url="mailto:timj@psynet.net"
+name="timj@psynet.net"></tt> por su gran trabajo en el <em/widget/ List.
+Gracias Tim :)
+
+<item>Rajat Datta <tt><htmlurl url="mailto:rajat@ix.netcom.com"
+name="rajat@ix.netcom.com"</tt> por el excelente trabajo con el
+tutorial Pixmap.
+
+<item>Michael K. Johnson <tt><htmlurl url="mailto:johnsonm@redhat.com"
+name="johnsonm@redhat.com"></tt> por la información y el código de
+los menús ("popup").
+
+<item>David Huggins-Daines <tt><htmlurl url="mailto:bn711@freenet.carleton.ca"
+name="bn711@freenet.carleton.ca"></tt> por las secciones sobre los
+<em/widgets/ Range y Tree.
+
+<item>Stefan Mars <tt><htmlurl url="mailto:mars@lysator.liu.se"
+name="mars@lysator.liu.se"></tt> por la sección GtkCList
+</itemize>
+<p>
+Y a todos los que han comentado y ayudado a refinar este documento.
+<p>
+Gracias.
+
+<!-- ***************************************************************** -->
+<sect> Copyright del Tutorial y notas sobre los permisos
+<!-- ***************************************************************** -->
+
+<p>
+Esta traducción está bajo la misma licencia bajo la que está
+el documento original. A continuación se presenta la traducción
+de la licencia y la licencia en versión original. En caso de haber
+alguna discrepancia entre la traducción y la licencia original, se
+aplicará esta última.
+
+El Tutorial GTK tiene Copyright (C) 1997 Ian Main.
+
+Copyright (C) 1998 Tony Gale.
+<p>
+Se da permiso para hacer y distribuir copias idénticas de este manual
+siempre que se incluya el copyright en todas las copias.
+<p>
+Se da permiso para copiar y distribuir versiones modificadas de este
+documento bajo las mismas condiciones que para las copias idénticas,
+siempre que el copyright se incluya exactamente tal y como se
+encuentra en el original, y que el trabajo completo derivado de este
+documento se distribuya bajo los términos de un permiso idéntico a
+éste.
+<P>
+Se da permiso para copiar y distribuir traducciones de este documento
+en otro lenguaje, bajo las condiciones arriba mencionadas para las
+versiones modificadas.
+<P>
+Si se propone incluir este documento en un trabajo que vaya a ser
+impreso, por favor contacte con el encargado del mantenimiento, y
+haremos un esfuerzo para asegurarnos de que dispone de la información
+lo más actualizada posible.
+<P>
+No hay ninguna garantia de que este documento se mantenga activo lo
+suficiente como para conseguir cumplir con su propósito. Se
+proporciona como un documento libre. Como tal, los autores y
+encargados del mantenimiento de la información que se da en el
+documento no pueden dar ninguna garantia de que la misma esté al día.
+<P>
+-----------------------------
+<p>
+The GTK Tutorial is Copyright (C) 1997 Ian Main.
+
+Copyright (C) 1998 Tony Gale.
+<p>
+Permission is granted to make and distribute verbatim copies of this
+manual provided the copyright notice and this permission notice are
+preserved on all copies.
+<P>Permission is granted to copy and distribute modified versions of
+this document under the conditions for verbatim copying, provided that
+this copyright notice is included exactly as in the original,
+and that the entire resulting derived work is distributed under
+the terms of a permission notice identical to this one.
+<P>Permission is granted to copy and distribute translations of this
+document into another language, under the above conditions for modified
+versions.
+<P>If you are intending to incorporate this document into a published
+work, please contact the maintainer, and we will make an effort
+to ensure that you have the most up to date information available.
+<P>There is no guarantee that this document lives up to its intended
+purpose. This is simply provided as a free resource. As such,
+the authors and maintainers of the information provided within can
+not make any guarantee that the information is even accurate.
+
+<sect1>Acerca de la traducción
+
+<p>
+Esta traduccion tiene copyright (C) 1999 de Joaquín Cuenca Abela
+<tt><htmlurl url="mailto:e98cuenc@criens.u-psud.fr"
+name="&lt;e98cuenc@criens.u-psud.fr&gt;"></tt>
+y de Eduardo Anglada Varela
+<tt><htmlurl url="mailto:eduardo.anglada@adi.uam.es"
+name="&lt;eduardo.anglada@adi.uam.es&gt;"></tt>.
+Si tiene cualquier
+duda, sugerencia o corrección no dude en consultarnos.
+
+Gracias a Manuel de Vega Barreiro <tt><htmlurl
+url="mailto:barreiro@arrakis.es"
+name="&lt;barreiro@arrakis.es&gt;"></tt> por haber hospedado las
+versiones beta y la versión actual de este tutorial en su página
+web Linux Landia <tt><url
+url="http://www.croftj.net/~barreiro/spain/gnome/"
+name="www.croftj.net/~barreiro/spain/gnome/"></tt>.
+
+</sect1>
+
+<!-- ***************************************************************** -->
+<appendix>
+<!-- ***************************************************************** -->
+
+<!-- ***************************************************************** -->
+<sect> Señales GTK <label id="sec_GTK_Signals">
+<!-- ***************************************************************** -->
+<p>
+GTK+, al ser un conjunto de <em/widgets/ orientado al objeto, tiene
+una jerarquía de herencias. Este mecanismo de herencia se aplica a las
+señales. Por eso, debe utilizar el árbol de jerarquías de los
+<em/widgets/ cuando utilice las señales que aparecen en esta sección.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GtkObject
+<!-- ----------------------------------------------------------------- -->
+<p>
+<tscreen><verb>
+void GtkObject::destroy (GtkObject *,
+ gpointer);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GtkWidget
+<!-- ----------------------------------------------------------------- -->
+<p>
+<tscreen><verb>
+
+void GtkWidget::show (GtkWidget *,
+ gpointer);
+void GtkWidget::hide (GtkWidget *,
+ gpointer);
+void GtkWidget::map (GtkWidget *,
+ gpointer);
+void GtkWidget::unmap (GtkWidget *,
+ gpointer);
+void GtkWidget::realize (GtkWidget *,
+ gpointer);
+void GtkWidget::unrealize (GtkWidget *,
+ gpointer);
+void GtkWidget::draw (GtkWidget *,
+ ggpointer,
+ gpointer);
+void GtkWidget::draw-focus (GtkWidget *,
+ gpointer);
+void GtkWidget::draw-default (GtkWidget *,
+ gpointer);
+void GtkWidget::size-request (GtkWidget *,
+ ggpointer,
+ gpointer);
+void GtkWidget::size-allocate (GtkWidget *,
+ ggpointer,
+ gpointer);
+void GtkWidget::state-changed (GtkWidget *,
+ GtkStateType,
+ gpointer);
+void GtkWidget::parent-set (GtkWidget *,
+ GtkObject *,
+ gpointer);
+void GtkWidget::style-set (GtkWidget *,
+ GtkStyle *,
+ gpointer);
+void GtkWidget::add-accelerator (GtkWidget *,
+ gguint,
+ GtkAccelGroup *,
+ gguint,
+ GdkModifierType,
+ GtkAccelFlags,
+ gpointer);
+void GtkWidget::remove-accelerator (GtkWidget *,
+ GtkAccelGroup *,
+ gguint,
+ GdkModifierType,
+ gpointer);
+gboolean GtkWidget::event (GtkWidget *,
+ GdkEvent *,
+ gpointer);
+gboolean GtkWidget::button-press-event (GtkWidget *,
+ GdkEvent *,
+ gpointer);
+gboolean GtkWidget::button-release-event (GtkWidget *,
+ GdkEvent *,
+ gpointer);
+gboolean GtkWidget::motion-notify-event (GtkWidget *,
+ GdkEvent *,
+ gpointer);
+gboolean GtkWidget::delete-event (GtkWidget *,
+ GdkEvent *,
+ gpointer);
+gboolean GtkWidget::destroy-event (GtkWidget *,
+ GdkEvent *,
+ gpointer);
+gboolean GtkWidget::expose-event (GtkWidget *,
+ GdkEvent *,
+ gpointer);
+gboolean GtkWidget::key-press-event (GtkWidget *,
+ GdkEvent *,
+ gpointer);
+gboolean GtkWidget::key-release-event (GtkWidget *,
+ GdkEvent *,
+ gpointer);
+gboolean GtkWidget::enter-notify-event (GtkWidget *,
+ GdkEvent *,
+ gpointer);
+gboolean GtkWidget::leave-notify-event (GtkWidget *,
+ GdkEvent *,
+ gpointer);
+gboolean GtkWidget::configure-event (GtkWidget *,
+ GdkEvent *,
+ gpointer);
+gboolean GtkWidget::focus-in-event (GtkWidget *,
+ GdkEvent *,
+ gpointer);
+gboolean GtkWidget::focus-out-event (GtkWidget *,
+ GdkEvent *,
+ gpointer);
+gboolean GtkWidget::map-event (GtkWidget *,
+ GdkEvent *,
+ gpointer);
+gboolean GtkWidget::unmap-event (GtkWidget *,
+ GdkEvent *,
+ gpointer);
+gboolean GtkWidget::property-notify-event (GtkWidget *,
+ GdkEvent *,
+ gpointer);
+gboolean GtkWidget::selection-clear-event (GtkWidget *,
+ GdkEvent *,
+ gpointer);
+gboolean GtkWidget::selection-request-event (GtkWidget *,
+ GdkEvent *,
+ gpointer);
+gboolean GtkWidget::selection-notify-event (GtkWidget *,
+ GdkEvent *,
+ gpointer);
+void GtkWidget::selection-get (GtkWidget *,
+ GtkSelectionData *,
+ gguint,
+ gpointer);
+void GtkWidget::selection-received (GtkWidget *,
+ GtkSelectionData *,
+ gguint,
+ gpointer);
+gboolean GtkWidget::proximity-in-event (GtkWidget *,
+ GdkEvent *,
+ gpointer);
+gboolean GtkWidget::proximity-out-event (GtkWidget *,
+ GdkEvent *,
+ gpointer);
+void GtkWidget::drag-begin (GtkWidget *,
+ GdkDragContext *,
+ gpointer);
+void GtkWidget::drag-end (GtkWidget *,
+ GdkDragContext *,
+ gpointer);
+void GtkWidget::drag-data-delete (GtkWidget *,
+ GdkDragContext *,
+ gpointer);
+void GtkWidget::drag-leave (GtkWidget *,
+ GdkDragContext *,
+ gguint,
+ gpointer);
+gboolean GtkWidget::drag-motion (GtkWidget *,
+ GdkDragContext *,
+ ggint,
+ ggint,
+ gguint,
+ gpointer);
+gboolean GtkWidget::drag-drop (GtkWidget *,
+ GdkDragContext *,
+ ggint,
+ ggint,
+ gguint,
+ gpointer);
+void GtkWidget::drag-data-get (GtkWidget *,
+ GdkDragContext *,
+ GtkSelectionData *,
+ gguint,
+ gguint,
+ gpointer);
+void GtkWidget::drag-data-received (GtkWidget *,
+ GdkDragContext *,
+ ggint,
+ ggint,
+ GtkSelectionData *,
+ gguint,
+ gguint,
+ gpointer);
+gboolean GtkWidget::client-event (GtkWidget *,
+ GdkEvent *,
+ gpointer);
+gboolean GtkWidget::no-expose-event (GtkWidget *,
+ GdkEvent *,
+ gpointer);
+gboolean GtkWidget::visibility-notify-event (GtkWidget *,
+ GdkEvent *,
+ gpointer);
+void GtkWidget::debug-msg (GtkWidget *,
+ GtkString *,
+ gpointer);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GtkData
+<!-- ----------------------------------------------------------------- -->
+<p>
+<tscreen><verb>
+void GtkData::disconnect (GtkData *,
+ gpointer);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GtkContainer
+<!-- ----------------------------------------------------------------- -->
+<p>
+<tscreen><verb>
+void GtkContainer::add (GtkContainer *,
+ GtkWidget *,
+ gpointer);
+void GtkContainer::remove (GtkContainer *,
+ GtkWidget *,
+ gpointer);
+void GtkContainer::check-resize (GtkContainer *,
+ gpointer);
+GtkDirectionType GtkContainer::focus (GtkContainer *,
+ GtkDirectionType,
+ gpointer);
+void GtkContainer::set-focus-child (GtkContainer *,
+ GtkWidget *,
+ gpointer);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GtkCalendar
+<!-- ----------------------------------------------------------------- -->
+<p>
+<tscreen><verb>
+void GtkCalendar::month-changed (GtkCalendar *,
+ gpointer);
+void GtkCalendar::day-selected (GtkCalendar *,
+ gpointer);
+void GtkCalendar::day-selected-double-click (GtkCalendar *,
+ gpointer);
+void GtkCalendar::prev-month (GtkCalendar *,
+ gpointer);
+void GtkCalendar::next-month (GtkCalendar *,
+ gpointer);
+void GtkCalendar::prev-year (GtkCalendar *,
+ gpointer);
+void GtkCalendar::next-year (GtkCalendar *,
+ gpointer);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GtkEditable
+<!-- ----------------------------------------------------------------- -->
+<p>
+<tscreen><verb>
+void GtkEditable::changed (GtkEditable *,
+ gpointer);
+void GtkEditable::insert-text (GtkEditable *,
+ GtkString *,
+ ggint,
+ ggpointer,
+ gpointer);
+void GtkEditable::delete-text (GtkEditable *,
+ ggint,
+ ggint,
+ gpointer);
+void GtkEditable::activate (GtkEditable *,
+ gpointer);
+void GtkEditable::set-editable (GtkEditable *,
+ gboolean,
+ gpointer);
+void GtkEditable::move-cursor (GtkEditable *,
+ ggint,
+ ggint,
+ gpointer);
+void GtkEditable::move-word (GtkEditable *,
+ ggint,
+ gpointer);
+void GtkEditable::move-page (GtkEditable *,
+ ggint,
+ ggint,
+ gpointer);
+void GtkEditable::move-to-row (GtkEditable *,
+ ggint,
+ gpointer);
+void GtkEditable::move-to-column (GtkEditable *,
+ ggint,
+ gpointer);
+void GtkEditable::kill-char (GtkEditable *,
+ ggint,
+ gpointer);
+void GtkEditable::kill-word (GtkEditable *,
+ ggint,
+ gpointer);
+void GtkEditable::kill-line (GtkEditable *,
+ ggint,
+ gpointer);
+void GtkEditable::cut-clipboard (GtkEditable *,
+ gpointer);
+void GtkEditable::copy-clipboard (GtkEditable *,
+ gpointer);
+void GtkEditable::paste-clipboard (GtkEditable *,
+ gpointer);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GtkTipsQuery
+<!-- ----------------------------------------------------------------- -->
+<p>
+<tscreen><verb>
+void GtkTipsQuery::start-query (GtkTipsQuery *,
+ gpointer);
+void GtkTipsQuery::stop-query (GtkTipsQuery *,
+ gpointer);
+void GtkTipsQuery::widget-entered (GtkTipsQuery *,
+ GtkWidget *,
+ GtkString *,
+ GtkString *,
+ gpointer);
+gboolean GtkTipsQuery::widget-selected (GtkTipsQuery *,
+ GtkWidget *,
+ GtkString *,
+ GtkString *,
+ GdkEvent *,
+ gpointer);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GtkCList
+<!-- ----------------------------------------------------------------- -->
+<p>
+<tscreen><verb>
+void GtkCList::select-row (GtkCList *,
+ ggint,
+ ggint,
+ GdkEvent *,
+ gpointer);
+void GtkCList::unselect-row (GtkCList *,
+ ggint,
+ ggint,
+ GdkEvent *,
+ gpointer);
+void GtkCList::row-move (GtkCList *,
+ ggint,
+ ggint,
+ gpointer);
+void GtkCList::click-column (GtkCList *,
+ ggint,
+ gpointer);
+void GtkCList::resize-column (GtkCList *,
+ ggint,
+ ggint,
+ gpointer);
+void GtkCList::toggle-focus-row (GtkCList *,
+ gpointer);
+void GtkCList::select-all (GtkCList *,
+ gpointer);
+void GtkCList::unselect-all (GtkCList *,
+ gpointer);
+void GtkCList::undo-selection (GtkCList *,
+ gpointer);
+void GtkCList::start-selection (GtkCList *,
+ gpointer);
+void GtkCList::end-selection (GtkCList *,
+ gpointer);
+void GtkCList::toggle-add-mode (GtkCList *,
+ gpointer);
+void GtkCList::extend-selection (GtkCList *,
+ GtkScrollType,
+ ggfloat,
+ gboolean,
+ gpointer);
+void GtkCList::scroll-vertical (GtkCList *,
+ GtkScrollType,
+ ggfloat,
+ gpointer);
+void GtkCList::scroll-horizontal (GtkCList *,
+ GtkScrollType,
+ ggfloat,
+ gpointer);
+void GtkCList::abort-column-resize (GtkCList *,
+ gpointer);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GtkNotebook
+<!-- ----------------------------------------------------------------- -->
+<p>
+<tscreen><verb>
+void GtkNotebook::switch-page (GtkNotebook *,
+ ggpointer,
+ gguint,
+ gpointer);
+
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GtkList
+<!-- ----------------------------------------------------------------- -->
+<p>
+<tscreen><verb>
+void GtkList::selection-changed (GtkList *,
+ gpointer);
+void GtkList::select-child (GtkList *,
+ GtkWidget *,
+ gpointer);
+void GtkList::unselect-child (GtkList *,
+ GtkWidget *,
+ gpointer);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GtkMenuShell
+<!-- ----------------------------------------------------------------- -->
+<p>
+<tscreen><verb>
+void GtkMenuShell::deactivate (GtkMenuShell *,
+ gpointer);
+void GtkMenuShell::selection-done (GtkMenuShell *,
+ gpointer);
+void GtkMenuShell::move-current (GtkMenuShell *,
+ GtkMenuDirectionType,
+ gpointer);
+void GtkMenuShell::activate-current (GtkMenuShell *,
+ gboolean,
+ gpointer);
+void GtkMenuShell::cancel (GtkMenuShell *,
+ gpointer);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GtkToolbar
+<!-- ----------------------------------------------------------------- -->
+<p>
+<tscreen><verb>
+void GtkToolbar::orientation-changed (GtkToolbar *,
+ ggint,
+ gpointer);
+void GtkToolbar::style-changed (GtkToolbar *,
+ ggint,
+ gpointer);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GtkTree
+<!-- ----------------------------------------------------------------- -->
+<p>
+<tscreen><verb>
+void GtkTree::selection-changed (GtkTree *,
+ gpointer);
+void GtkTree::select-child (GtkTree *,
+ GtkWidget *,
+ gpointer);
+void GtkTree::unselect-child (GtkTree *,
+ GtkWidget *,
+ gpointer);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GtkButton
+<!-- ----------------------------------------------------------------- -->
+<p>
+<tscreen><verb>
+void GtkButton::pressed (GtkButton *,
+ gpointer);
+void GtkButton::released (GtkButton *,
+ gpointer);
+void GtkButton::clicked (GtkButton *,
+ gpointer);
+void GtkButton::enter (GtkButton *,
+ gpointer);
+void GtkButton::leave (GtkButton *,
+ gpointer);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GtkItem
+<!-- ----------------------------------------------------------------- -->
+<p>
+<tscreen><verb>
+void GtkItem::select (GtkItem *,
+ gpointer);
+void GtkItem::deselect (GtkItem *,
+ gpointer);
+void GtkItem::toggle (GtkItem *,
+ gpointer);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GtkWindow
+<!-- ----------------------------------------------------------------- -->
+<p>
+<tscreen><verb>
+void GtkWindow::set-focus (GtkWindow *,
+ ggpointer,
+ gpointer);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GtkHandleBox
+<!-- ----------------------------------------------------------------- -->
+<p>
+<tscreen><verb>
+void GtkHandleBox::child-attached (GtkHandleBox *,
+ GtkWidget *,
+ gpointer);
+void GtkHandleBox::child-detached (GtkHandleBox *,
+ GtkWidget *,
+ gpointer);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GtkToggleButton
+<!-- ----------------------------------------------------------------- -->
+<p>
+<tscreen><verb>
+void GtkToggleButton::toggled (GtkToggleButton *,
+ gpointer);
+
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GtkMenuItem
+<!-- ----------------------------------------------------------------- -->
+<p>
+<tscreen><verb>
+void GtkMenuItem::activate (GtkMenuItem *,
+ gpointer);
+void GtkMenuItem::activate-item (GtkMenuItem *,
+ gpointer);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GtkListItem
+<!-- ----------------------------------------------------------------- -->
+<p>
+<tscreen><verb>
+void GtkListItem::toggle-focus-row (GtkListItem *,
+ gpointer);
+void GtkListItem::select-all (GtkListItem *,
+ gpointer);
+void GtkListItem::unselect-all (GtkListItem *,
+ gpointer);
+void GtkListItem::undo-selection (GtkListItem *,
+ gpointer);
+void GtkListItem::start-selection (GtkListItem *,
+ gpointer);
+void GtkListItem::end-selection (GtkListItem *,
+ gpointer);
+void GtkListItem::toggle-add-mode (GtkListItem *,
+ gpointer);
+void GtkListItem::extend-selection (GtkListItem *,
+ GtkEnum,
+ ggfloat,
+ gboolean,
+ gpointer);
+void GtkListItem::scroll-vertical (GtkListItem *,
+ GtkEnum,
+ ggfloat,
+ gpointer);
+void GtkListItem::scroll-horizontal (GtkListItem *,
+ GtkEnum,
+ ggfloat,
+ gpointer);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GtkTreeItem
+<!-- ----------------------------------------------------------------- -->
+<p>
+<tscreen><verb>
+void GtkTreeItem::collapse (GtkTreeItem *,
+ gpointer);
+void GtkTreeItem::expand (GtkTreeItem *,
+ gpointer);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GtkCheckMenuItem
+<!-- ----------------------------------------------------------------- -->
+<p>
+<tscreen><verb>
+void GtkCheckMenuItem::toggled (GtkCheckMenuItem *,
+ gpointer);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GtkInputDialog
+<!-- ----------------------------------------------------------------- -->
+<p>
+<tscreen><verb>
+void GtkInputDialog::enable-device (GtkInputDialog *,
+ ggint,
+ gpointer);
+void GtkInputDialog::disable-device (GtkInputDialog *,
+ ggint,
+ gpointer);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GtkColorSelection
+<!-- ----------------------------------------------------------------- -->
+<p>
+<tscreen><verb>
+void GtkColorSelection::color-changed (GtkColorSelection *,
+ gpointer);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GtkStatusBar
+<!-- ----------------------------------------------------------------- -->
+<p>
+<tscreen><verb>
+void GtkStatusbar::text-pushed (GtkStatusbar *,
+ gguint,
+ GtkString *,
+ gpointer);
+void GtkStatusbar::text-popped (GtkStatusbar *,
+ gguint,
+ GtkString *,
+ gpointer);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GtkCTree
+<!-- ----------------------------------------------------------------- -->
+<p>
+<tscreen><verb>
+void GtkCTree::tree-select-row (GtkCTree *,
+ GtkCTreeNode *,
+ ggint,
+ gpointer);
+void GtkCTree::tree-unselect-row (GtkCTree *,
+ GtkCTreeNode *,
+ ggint,
+ gpointer);
+void GtkCTree::tree-expand (GtkCTree *,
+ GtkCTreeNode *,
+ gpointer);
+void GtkCTree::tree-collapse (GtkCTree *,
+ ggpointer,
+ gpointer);
+void GtkCTree::tree-move (GtkCTree *,
+ GtkCTreeNode *,
+ GtkCTreeNode *,
+ GtkCTreeNode *,
+ gpointer);
+void GtkCTree::change-focus-row-expansion (GtkCTree *,
+ GtkCTreeExpansionType,
+ gpointer);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GtkCurve
+<!-- ----------------------------------------------------------------- -->
+<p>
+<tscreen><verb>
+void GtkCurve::curve-type-changed (GtkCurve *,
+ gpointer);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GtkAdjustment
+<!-- ----------------------------------------------------------------- -->
+<p>
+<tscreen><verb>
+void GtkAdjustment::changed (GtkAdjustment *,
+ gpointer);
+void GtkAdjustment::value-changed (GtkAdjustment *,
+ gpointer);
+</verb></tscreen>
+
+<!-- ***************************************************************** -->
+<sect> Tipos de eventos GDK<label id="sec_GDK_Event_Types">
+<!-- ***************************************************************** -->
+<p>
+Los siguientes tipos de datos se pasan en los manejadores de los
+eventos por GTK+. Para cada tipo de dato que se muestra, se muestran
+las señales que utilizan ese tipo de dato.
+
+<itemize>
+<item> GdkEvent
+ <itemize>
+ <item>drag_end_event
+ </itemize>
+
+<item> GdkEventType
+
+<item> GdkEventAny
+ <itemize>
+ <item>delete_event
+ <item>destroy_event
+ <item>map_event
+ <item>unmap_event
+ <item>no_expose_event
+ </itemize>
+
+<item> GdkEventExpose
+ <itemize>
+ <item>expose_event
+ </itemize>
+
+<item> GdkEventNoExpose
+
+<item> GdkEventVisibility
+
+<item> GdkEventMotion
+ <itemize>
+ <item>motion_notify_event
+ </itemize>
+
+<item> GdkEventButton
+ <itemize>
+ <item>button_press_event
+ <item>button_release_event
+ </itemize>
+
+<item> GdkEventKey
+ <itemize>
+ <item>key_press_event
+ <item>key_release_event
+ </itemize>
+
+<item> GdkEventCrossing
+ <itemize>
+ <item>enter_notify_event
+ <item>leave_notify_event
+ </itemize>
+
+<item> GdkEventFocus
+ <itemize>
+ <item>focus_in_event
+ <item>focus_out_event
+ </itemize>
+
+<item> GdkEventConfigure
+ <itemize>
+ <item>configure_event
+ </itemize>
+
+<item> GdkEventProperty
+ <itemize>
+ <item>property_notify_event
+ </itemize>
+
+<item> GdkEventSelection
+ <itemize>
+ <item>selection_clear_event
+ <item>selection_request_event
+ <item>selection_notify_event
+ </itemize>
+
+<item> GdkEventProximity
+ <itemize>
+ <item>proximity_in_event
+ <item>proximity_out_event
+ </itemize>
+
+<item> GdkEventDragBegin
+ <itemize>
+ <item>drag_begin_event
+ </itemize>
+
+<item> GdkEventDragRequest
+ <itemize>
+ <item>drag_request_event
+ </itemize>
+
+<item> GdkEventDropEnter
+ <itemize>
+ <item>drop_enter_event
+ </itemize>
+
+<item> GdkEventDropLeave
+ <itemize>
+ <item>drop_leave_event
+ </itemize>
+
+<item> GdkEventDropDataAvailable
+ <itemize>
+ <item>drop_data_available_event
+ </itemize>
+
+<item> GdkEventClient
+ <itemize>
+ <item>client_event
+ </itemize>
+
+<item> GdkEventOther
+ <itemize>
+ <item>other_event
+ </itemize>
+</itemize>
+
+El tipo de dato <tt/GdkEventType/ es un tipo de dato especial que se
+utiliza por todos los otros tipos de datos como un indicador del tipo
+de dato que se le está pasando al manejador de señal. Como verá
+más adelante, cada una de estructuras de los datos de los eventos
+tienen un miembro de este tipo. Se define como la siguiente
+enumeración:
+
+<tscreen><verb>
+typedef enum
+{
+ GDK_NOTHING = -1,
+ GDK_DELETE = 0,
+ GDK_DESTROY = 1,
+ GDK_EXPOSE = 2,
+ GDK_MOTION_NOTIFY = 3,
+ GDK_BUTTON_PRESS = 4,
+ GDK_2BUTTON_PRESS = 5,
+ GDK_3BUTTON_PRESS = 6,
+ GDK_BUTTON_RELEASE = 7,
+ GDK_KEY_PRESS = 8,
+ GDK_KEY_RELEASE = 9,
+ GDK_ENTER_NOTIFY = 10,
+ GDK_LEAVE_NOTIFY = 11,
+ GDK_FOCUS_CHANGE = 12,
+ GDK_CONFIGURE = 13,
+ GDK_MAP = 14,
+ GDK_UNMAP = 15,
+ GDK_PROPERTY_NOTIFY = 16,
+ GDK_SELECTION_CLEAR = 17,
+ GDK_SELECTION_REQUEST = 18,
+ GDK_SELECTION_NOTIFY = 19,
+ GDK_PROXIMITY_IN = 20,
+ GDK_PROXIMITY_OUT = 21,
+ GDK_DRAG_BEGIN = 22,
+ GDK_DRAG_REQUEST = 23,
+ GDK_DROP_ENTER = 24,
+ GDK_DROP_LEAVE = 25,
+ GDK_DROP_DATA_AVAIL = 26,
+ GDK_CLIENT_EVENT = 27,
+ GDK_VISIBILITY_NOTIFY = 28,
+ GDK_NO_EXPOSE = 29,
+ GDK_OTHER_EVENT = 9999 /* Anacrónico, utilice en su lugar los
+ filtros */
+} GdkEventType;
+</verb></tscreen>
+
+El otro tipo de evento que es diferente del resto es el mismo
+<tt/GdkEvent/. Ésta es una unión de todos los otros tipos de
+datos, que permite que se convierta en un tipo de dato de evento
+específico con un manejador de señal.
+
+<!-- Just a big list for now, needs expanding upon - TRG -->
+Por tanto, los tipos de los datos de los eventos se definen como
+sigue:
+
+<tscreen><verb>
+struct _GdkEventAny
+{
+ GdkEventType type;
+ GdkWindow *window;
+ gint8 send_event;
+};
+
+struct _GdkEventExpose
+{
+ GdkEventType type;
+ GdkWindow *window;
+ gint8 send_event;
+ GdkRectangle area;
+ gint count; /* Si count no es, entonces es el número de eventos que
+ * siguen. */
+};
+
+struct _GdkEventNoExpose
+{
+ GdkEventType type;
+ GdkWindow *window;
+ gint8 send_event;
+ /* XXX: ¿Hay alguien que necesite los campos major_code y minor_code
+ de X ? */
+};
+
+struct _GdkEventVisibility
+{
+ GdkEventType type;
+ GdkWindow *window;
+ gint8 send_event;
+ GdkVisibilityState state;
+};
+
+struct _GdkEventMotion
+{
+ GdkEventType type;
+ GdkWindow *window;
+ gint8 send_event;
+ guint32 time;
+ gdouble x;
+ gdouble y;
+ gdouble pressure;
+ gdouble xtilt;
+ gdouble ytilt;
+ guint state;
+ gint16 is_hint;
+ GdkInputSource source;
+ guint32 deviceid;
+ gdouble x_root, y_root;
+};
+
+struct _GdkEventButton
+{
+ GdkEventType type;
+ GdkWindow *window;
+ gint8 send_event;
+ guint32 time;
+ gdouble x;
+ gdouble y;
+ gdouble pressure;
+ gdouble xtilt;
+ gdouble ytilt;
+ guint state;
+ guint button;
+ GdkInputSource source;
+ guint32 deviceid;
+ gdouble x_root, y_root;
+};
+
+struct _GdkEventKey
+{
+ GdkEventType type;
+ GdkWindow *window;
+ gint8 send_event;
+ guint32 time;
+ guint state;
+ guint keyval;
+ gint length;
+ gchar *string;
+};
+
+struct _GdkEventCrossing
+{
+ GdkEventType type;
+ GdkWindow *window;
+ gint8 send_event;
+ GdkWindow *subwindow;
+ GdkNotifyType detail;
+};
+
+struct _GdkEventFocus
+{
+ GdkEventType type;
+ GdkWindow *window;
+ gint8 send_event;
+ gint16 in;
+};
+
+struct _GdkEventConfigure
+{
+ GdkEventType type;
+ GdkWindow *window;
+ gint8 send_event;
+ gint16 x, y;
+ gint16 width;
+ gint16 height;
+};
+
+struct _GdkEventProperty
+{
+ GdkEventType type;
+ GdkWindow *window;
+ gint8 send_event;
+ GdkAtom atom;
+ guint32 time;
+ guint state;
+};
+
+struct _GdkEventSelection
+{
+ GdkEventType type;
+ GdkWindow *window;
+ gint8 send_event;
+ GdkAtom selection;
+ GdkAtom target;
+ GdkAtom property;
+ guint32 requestor;
+ guint32 time;
+};
+
+/* Este tipo de evento se utiliza muy raramente. Solamente es
+ * importante para los programas que utilizan XInput y que dibujar su
+ * propio cursor */
+
+struct _GdkEventProximity
+{
+ GdkEventType type;
+ GdkWindow *window;
+ gint8 send_event;
+ guint32 time;
+ GdkInputSource source;
+ guint32 deviceid;
+};
+
+struct _GdkEventDragRequest
+{
+ GdkEventType type;
+ GdkWindow *window;
+ gint8 send_event;
+ guint32 requestor;
+ union {
+ struct {
+ guint protocol_version:4;
+ guint sendreply:1;
+ guint willaccept:1;
+ guint delete_data:1; /* No borrar si se ha mandado un enlace,
+ sólo si se ha mandado el dato */
+ guint senddata:1;
+ guint reserved:22;
+ } flags;
+ glong allflags;
+ } u;
+ guint8 isdrop; /* Este evento gdk puede ser generado por un par de
+ eventos X - esto le permite a las aplicaciones
+ saber si ha ocurrido realmente el soltado (drop),
+ o si sólo hemos cambiado el valor de algunos datos
+ */
+
+ GdkPoint drop_coords;
+ gchar *data_type;
+ guint32 timestamp;
+};
+
+struct _GdkEventDragBegin
+{
+ GdkEventType type;
+ GdkWindow *window;
+ gint8 send_event;
+ union {
+ struct {
+ guint protocol_version:4;
+ guint reserved:28;
+ } flags;
+ glong allflags;
+ } u;
+};
+
+struct _GdkEventDropEnter
+{
+ GdkEventType type;
+ GdkWindow *window;
+ gint8 send_event;
+ guint32 requestor;
+ union {
+ struct {
+ guint protocol_version:4;
+ guint sendreply:1;
+ guint extended_typelist:1;
+ guint reserved:26;
+ } flags;
+ glong allflags;
+ } u;
+};
+
+struct _GdkEventDropLeave
+{
+ GdkEventType type;
+ GdkWindow *window;
+ gint8 send_event;
+ guint32 requestor;
+ union {
+ struct {
+ guint protocol_version:4;
+ guint reserved:28;
+ } flags;
+ glong allflags;
+ } u;
+};
+
+struct _GdkEventDropDataAvailable
+{
+ GdkEventType type;
+ GdkWindow *window;
+ gint8 send_event;
+ guint32 requestor;
+ union {
+ struct {
+ guint protocol_version:4;
+ guint isdrop:1;
+ guint reserved:25;
+ } flags;
+ glong allflags;
+ } u;
+ gchar *data_type; /* tipo MIME */
+ gulong data_numbytes;
+ gpointer data;
+ guint32 timestamp;
+ GdkPoint coords;
+};
+
+struct _GdkEventClient
+{
+ GdkEventType type;
+ GdkWindow *window;
+ gint8 send_event;
+ GdkAtom message_type;
+ gushort data_format;
+ union {
+ char b[20];
+ short s[10];
+ long l[5];
+ } data;
+};
+
+struct _GdkEventOther
+{
+ GdkEventType type;
+ GdkWindow *window;
+ gint8 send_event;
+ GdkXEvent *xevent;
+};
+</verb></tscreen>
+
+<!-- ***************************************************************** -->
+<sect> Código ejemplo
+<!-- ***************************************************************** -->
+<p>
+A continuación tenemos el código ejemplo que se ha utilizado en el
+texto anterior y que no se ha incluido al completo en otro lugar.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Tictactoe
+<!-- ----------------------------------------------------------------- -->
+<sect2>tictactoe.h
+<p>
+<tscreen><verb>
+/* principio del ejemplo tictactoe tictactoe.h */
+
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifndef __TICTACTOE_H__
+#define __TICTACTOE_H__
+
+
+#include <gdk/gdk.h>
+#include <gtk/gtkvbox.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define TICTACTOE(obj) GTK_CHECK_CAST (obj, tictactoe_get_type (), Tictactoe)
+#define TICTACTOE_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, tictactoe_get_type (), TictactoeClass)
+#define IS_TICTACTOE(obj) GTK_CHECK_TYPE (obj, tictactoe_get_type ())
+
+
+typedef struct _Tictactoe Tictactoe;
+typedef struct _TictactoeClass TictactoeClass;
+
+struct _Tictactoe
+{
+ GtkVBox vbox;
+
+ GtkWidget *botones[3][3];
+};
+
+struct _TictactoeClass
+{
+ GtkVBoxClass parent_class;
+
+ void (* tictactoe) (Tictactoe *ttt);
+};
+
+guint tictactoe_get_type (void);
+GtkWidget* tictactoe_new (void);
+void tictactoe_clear (Tictactoe *ttt);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __TICTACTOE_H__ */
+
+/* fin del ejemplo */
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect2>tictactoe.c
+<p>
+<tscreen><verb>
+/* principio del ejemplo tictactoe tictactoe.c */
+
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#include "gtk/gtksignal.h"
+#include "gtk/gtktable.h"
+#include "gtk/gtktogglebutton.h"
+#include "tictactoe.h"
+
+enum {
+ TICTACTOE_SIGNAL,
+ LAST_SIGNAL
+};
+
+static void tictactoe_class_init (TictactoeClass *klass);
+static void tictactoe_init (Tictactoe *ttt);
+static void tictactoe_toggle (GtkWidget *widget, Tictactoe *ttt);
+
+static gint tictactoe_signals[LAST_SIGNAL] = { 0 };
+
+guint
+tictactoe_get_type ()
+{
+ static guint ttt_type = 0;
+
+ if (!ttt_type)
+ {
+ GtkTypeInfo ttt_info =
+ {
+ "Tictactoe",
+ sizeof (Tictactoe),
+ sizeof (TictactoeClass),
+ (GtkClassInitFunc) tictactoe_class_init,
+ (GtkObjectInitFunc) tictactoe_init,
+ (GtkArgSetFunc) NULL,
+ (GtkArgGetFunc) NULL
+ };
+
+ ttt_type = gtk_type_unique (gtk_vbox_get_type (), &amp;ttt_info);
+ }
+
+ return ttt_type;
+}
+
+static void
+tictactoe_class_init (TictactoeClass *class)
+{
+ GtkObjectClass *object_class;
+
+ object_class = (GtkObjectClass*) class;
+
+ tictactoe_signals[TICTACTOE_SIGNAL] = gtk_signal_new ("tictactoe",
+ GTK_RUN_FIRST,
+ object_class->type,
+ GTK_SIGNAL_OFFSET (TictactoeClass, tictactoe),
+ gtk_signal_default_marshaller, GTK_TYPE_NONE, 0);
+
+
+ gtk_object_class_add_signals (object_class, tictactoe_signals, LAST_SIGNAL);
+
+ class->tictactoe = NULL;
+}
+
+static void
+tictactoe_init (Tictactoe *ttt)
+{
+ GtkWidget *table;
+ gint i,j;
+
+ table = gtk_table_new (3, 3, TRUE);
+ gtk_container_add (GTK_CONTAINER(ttt), table);
+ gtk_widget_show (table);
+
+ for (i=0;i<3; i++)
+ for (j=0;j<3; j++)
+ {
+ ttt->buttons[i][j] = gtk_toggle_button_new ();
+ gtk_table_attach_defaults (GTK_TABLE(table), ttt->buttons[i][j],
+ i, i+1, j, j+1);
+ gtk_signal_connect (GTK_OBJECT (ttt->buttons[i][j]), "toggled",
+ GTK_SIGNAL_FUNC (tictactoe_toggle), ttt);
+ gtk_widget_set_usize (ttt->buttons[i][j], 20, 20);
+ gtk_widget_show (ttt->buttons[i][j]);
+ }
+}
+
+GtkWidget*
+tictactoe_new ()
+{
+ return GTK_WIDGET ( gtk_type_new (tictactoe_get_type ()));
+}
+
+void
+tictactoe_clear (Tictactoe *ttt)
+{
+ int i,j;
+
+ for (i=0;i<3;i++)
+ for (j=0;j<3;j++)
+ {
+ gtk_signal_handler_block_by_data (GTK_OBJECT(ttt->buttons[i][j]), ttt);
+ gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (ttt->buttons[i][j]),
+ FALSE);
+ gtk_signal_handler_unblock_by_data (GTK_OBJECT(ttt->buttons[i][j]), ttt);
+ }
+}
+
+static void
+tictactoe_toggle (GtkWidget *widget, Tictactoe *ttt)
+{
+ int i,k;
+
+ static int rwins[8][3] = { { 0, 0, 0 }, { 1, 1, 1 }, { 2, 2, 2 },
+ { 0, 1, 2 }, { 0, 1, 2 }, { 0, 1, 2 },
+ { 0, 1, 2 }, { 0, 1, 2 } };
+ static int cwins[8][3] = { { 0, 1, 2 }, { 0, 1, 2 }, { 0, 1, 2 },
+ { 0, 0, 0 }, { 1, 1, 1 }, { 2, 2, 2 },
+ { 0, 1, 2 }, { 2, 1, 0 } };
+
+ int success, found;
+
+ for (k=0; k<8; k++)
+ {
+ success = TRUE;
+ found = FALSE;
+
+ for (i=0;i<3;i++)
+ {
+ success = success &amp;&amp;
+ GTK_TOGGLE_BUTTON(ttt->buttons[rwins[k][i]][cwins[k][i]])->active;
+ found = found ||
+ ttt->buttons[rwins[k][i]][cwins[k][i]] == widget;
+ }
+
+ if (success &amp;&amp; found)
+ {
+ gtk_signal_emit (GTK_OBJECT (ttt),
+ tictactoe_signals[TICTACTOE_SIGNAL]);
+ break;
+ }
+ }
+}
+
+/* fin del ejemplo */
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect2>ttt_test.c
+<p>
+<tscreen><verb>
+/* principio del ejemplo tictactoe ttt_test.c */
+
+#include <gtk/gtk.h>
+#include "tictactoe.h"
+
+void
+win (GtkWidget *widget, gpointer data)
+{
+ g_print ("Yay!\n");
+ tictactoe_clear (TICTACTOE (widget));
+}
+
+int
+main (int argc, char *argv[])
+{
+ GtkWidget *ventana;
+ GtkWidget *ttt;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ gtk_window_set_title (GTK_WINDOW (ventana), "Aspect Frame");
+
+ gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
+ GTK_SIGNAL_FUNC (gtk_exit), NULL);
+
+ gtk_container_border_width (GTK_CONTAINER (ventana), 10);
+
+ ttt = tictactoe_new ();
+
+ gtk_container_add (GTK_CONTAINER (ventana), ttt);
+ gtk_widget_show (ttt);
+
+ gtk_signal_connect (GTK_OBJECT (ttt), "tictactoe",
+ GTK_SIGNAL_FUNC (win), NULL);
+
+ gtk_widget_show (ventana);
+
+ gtk_main ();
+
+ return 0;
+}
+
+/* fin del ejemplo */
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> GtkDial
+
+<!-- ----------------------------------------------------------------- -->
+<sect2> gtkdial.h
+<p>
+<tscreen><verb>
+/* principio del ejmplo gtkdial gtkdial.h */
+
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifndef __GTK_DIAL_H__
+#define __GTK_DIAL_H__
+
+
+#include <gdk/gdk.h>
+#include <gtk/gtkadjustment.h>
+#include <gtk/gtkwidget.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+#define GTK_DIAL(obj) GTK_CHECK_CAST (obj, gtk_dial_get_type (), GtkDial)
+#define GTK_DIAL_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, gtk_dial_get_type (), GtkDialClass)
+#define GTK_IS_DIAL(obj) GTK_CHECK_TYPE (obj, gtk_dial_get_type ())
+
+
+typedef struct _GtkDial GtkDial;
+typedef struct _GtkDialClass GtkDialClass;
+
+struct _GtkDial
+{
+ GtkWidget widget;
+
+ /* política de actualización
+ * (GTK_UPDATE_[CONTINUOUS/DELAYED/DISCONTINUOUS]) */
+ guint policy : 2;
+
+ /* Botón actualmente presionado o 0 si no hay ninguno */
+ guint8 boton;
+
+ /* Dimensión de los componendes del dial */
+ gint radius;
+ gint pointer_width;
+
+ /* ID del temporizador de actualización, o 0 si no hay ninguno */
+ guint32 timer;
+
+ /* ángulo actual */
+ gfloat angle;
+
+ /* Viejos valores almacenados del adjustment, para que así no
+ * tengamos que saber cuando cambia algo */
+ gfloat old_value;
+ gfloat old_lower;
+ gfloat old_upper;
+
+ /* El objeto adjustment que almacena los datos para este dial */
+ GtkAdjustment *adjustment;
+};
+
+struct _GtkDialClass
+{
+ GtkWidgetClass parent_class;
+};
+
+
+GtkWidget* gtk_dial_new (GtkAdjustment *adjustment);
+guint gtk_dial_get_type (void);
+GtkAdjustment* gtk_dial_get_adjustment (GtkDial *dial);
+void gtk_dial_set_update_policy (GtkDial *dial,
+ GtkUpdateType policy);
+
+void gtk_dial_set_adjustment (GtkDial *dial,
+ GtkAdjustment *adjustment);
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* __GTK_DIAL_H__ */
+/* fin del ejemplo */
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect2> gtkdial.c
+<p>
+<tscreen><verb>
+/* principio del ejemplo gtkdial gtkdial.c */
+
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#include <math.h>
+#include <stdio.h>
+#include <gtk/gtkmain.h>
+#include <gtk/gtksignal.h>
+
+#include "gtkdial.h"
+
+#define SCROLL_DELAY_LENGTH 300
+#define DIAL_DEFAULT_SIZE 100
+
+/* declaraciones de funciones */
+
+static void gtk_dial_class_init (GtkDialClass *klass);
+static void gtk_dial_init (GtkDial *dial);
+static void gtk_dial_destroy (GtkObject *object);
+static void gtk_dial_realize (GtkWidget *widget);
+static void gtk_dial_size_request (GtkWidget *widget,
+ GtkRequisition *requisition);
+static void gtk_dial_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation);
+static gint gtk_dial_expose (GtkWidget *widget,
+ GdkEventExpose *event);
+static gint gtk_dial_button_press (GtkWidget *widget,
+ GdkEventButton *event);
+static gint gtk_dial_button_release (GtkWidget *widget,
+ GdkEventButton *event);
+static gint gtk_dial_motion_notify (GtkWidget *widget,
+ GdkEventMotion *event);
+static gint gtk_dial_timer (GtkDial *dial);
+
+static void gtk_dial_update_mouse (GtkDial *dial, gint x, gint y);
+static void gtk_dial_update (GtkDial *dial);
+static void gtk_dial_adjustment_changed (GtkAdjustment *adjustment,
+ gpointer data);
+static void gtk_dial_adjustment_value_changed (GtkAdjustment *adjustment,
+ gpointer data);
+
+/* datos locales */
+
+static GtkWidgetClass *parent_class = NULL;
+
+guint
+gtk_dial_get_type ()
+{
+ static guint dial_type = 0;
+
+ if (!dial_type)
+ {
+ GtkTypeInfo dial_info =
+ {
+ "GtkDial",
+ sizeof (GtkDial),
+ sizeof (GtkDialClass),
+ (GtkClassInitFunc) gtk_dial_class_init,
+ (GtkObjectInitFunc) gtk_dial_init,
+ (GtkArgSetFunc) NULL,
+ (GtkArgGetFunc) NULL,
+ };
+
+ dial_type = gtk_type_unique (gtk_widget_get_type (), &amp;dial_info);
+ }
+
+ return dial_type;
+}
+
+static void
+gtk_dial_class_init (GtkDialClass *class)
+{
+ GtkObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+
+ object_class = (GtkObjectClass*) class;
+ widget_class = (GtkWidgetClass*) class;
+
+ parent_class = gtk_type_class (gtk_widget_get_type ());
+
+ object_class->destroy = gtk_dial_destroy;
+
+ widget_class->realize = gtk_dial_realize;
+ widget_class->expose_event = gtk_dial_expose;
+ widget_class->size_request = gtk_dial_size_request;
+ widget_class->size_allocate = gtk_dial_size_allocate;
+ widget_class->button_press_event = gtk_dial_button_press;
+ widget_class->button_release_event = gtk_dial_button_release;
+ widget_class->motion_notify_event = gtk_dial_motion_notify;
+}
+
+static void
+gtk_dial_init (GtkDial *dial)
+{
+ dial->button = 0;
+ dial->policy = GTK_UPDATE_CONTINUOUS;
+ dial->timer = 0;
+ dial->radius = 0;
+ dial->pointer_width = 0;
+ dial->angle = 0.0;
+ dial->old_value = 0.0;
+ dial->old_lower = 0.0;
+ dial->old_upper = 0.0;
+ dial->adjustment = NULL;
+}
+
+GtkWidget*
+gtk_dial_new (GtkAdjustment *adjustment)
+{
+ GtkDial *dial;
+
+ dial = gtk_type_new (gtk_dial_get_type ());
+
+ if (!adjustment)
+ adjustment = (GtkAdjustment*) gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
+
+ gtk_dial_set_adjustment (dial, adjustment);
+
+ return GTK_WIDGET (dial);
+}
+
+static void
+gtk_dial_destroy (GtkObject *object)
+{
+ GtkDial *dial;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GTK_IS_DIAL (object));
+
+ dial = GTK_DIAL (object);
+
+ if (dial->adjustment)
+ gtk_object_unref (GTK_OBJECT (dial->adjustment));
+
+ if (GTK_OBJECT_CLASS (parent_class)->destroy)
+ (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+GtkAdjustment*
+gtk_dial_get_adjustment (GtkDial *dial)
+{
+ g_return_val_if_fail (dial != NULL, NULL);
+ g_return_val_if_fail (GTK_IS_DIAL (dial), NULL);
+
+ return dial->adjustment;
+}
+
+void
+gtk_dial_set_update_policy (GtkDial *dial,
+ GtkUpdateType policy)
+{
+ g_return_if_fail (dial != NULL);
+ g_return_if_fail (GTK_IS_DIAL (dial));
+
+ dial->policy = policy;
+}
+
+void
+gtk_dial_set_adjustment (GtkDial *dial,
+ GtkAdjustment *adjustment)
+{
+ g_return_if_fail (dial != NULL);
+ g_return_if_fail (GTK_IS_DIAL (dial));
+
+ if (dial->adjustment)
+ {
+ gtk_signal_disconnect_by_data (GTK_OBJECT (dial->adjustment), (gpointer) dial);
+ gtk_object_unref (GTK_OBJECT (dial->adjustment));
+ }
+
+ dial->adjustment = adjustment;
+ gtk_object_ref (GTK_OBJECT (dial->adjustment));
+
+ gtk_signal_connect (GTK_OBJECT (adjustment), "changed",
+ (GtkSignalFunc) gtk_dial_adjustment_changed,
+ (gpointer) dial);
+ gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed",
+ (GtkSignalFunc) gtk_dial_adjustment_value_changed,
+ (gpointer) dial);
+
+ dial->old_value = adjustment->value;
+ dial->old_lower = adjustment->lower;
+ dial->old_upper = adjustment->upper;
+
+ gtk_dial_update (dial);
+}
+
+static void
+gtk_dial_realize (GtkWidget *widget)
+{
+ GtkDial *dial;
+ GdkWindowAttr attributes;
+ gint attributes_mask;
+
+ g_return_if_fail (widget != NULL);
+ g_return_if_fail (GTK_IS_DIAL (widget));
+
+ GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
+ dial = GTK_DIAL (widget);
+
+ attributes.x = widget->allocation.x;
+ attributes.y = widget->allocation.y;
+ attributes.width = widget->allocation.width;
+ attributes.height = widget->allocation.height;
+ attributes.wclass = GDK_INPUT_OUTPUT;
+ attributes.window_type = GDK_WINDOW_CHILD;
+ attributes.event_mask = gtk_widget_get_events (widget) |
+ GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK |
+ GDK_POINTER_MOTION_HINT_MASK;
+ attributes.visual = gtk_widget_get_visual (widget);
+ attributes.colormap = gtk_widget_get_colormap (widget);
+
+ attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+ widget->window = gdk_window_new (widget->parent->window, &amp;attributes, attributes_mask);
+
+ widget->style = gtk_style_attach (widget->style, widget->window);
+
+ gdk_window_set_user_data (widget->window, widget);
+
+ gtk_style_set_background (widget->style, widget->window, GTK_STATE_ACTIVE);
+}
+
+static void
+gtk_dial_size_request (GtkWidget *widget,
+ GtkRequisition *requisition)
+{
+ requisition->width = DIAL_DEFAULT_SIZE;
+ requisition->height = DIAL_DEFAULT_SIZE;
+}
+
+static void
+gtk_dial_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation)
+{
+ GtkDial *dial;
+
+ g_return_if_fail (widget != NULL);
+ g_return_if_fail (GTK_IS_DIAL (widget));
+ g_return_if_fail (allocation != NULL);
+
+ widget->allocation = *allocation;
+ dial = GTK_DIAL (widget);
+
+ if (GTK_WIDGET_REALIZED (widget))
+ {
+
+ gdk_window_move_resize (widget->window,
+ allocation->x, allocation->y,
+ allocation->width, allocation->height);
+
+ }
+ dial->radius = MIN(allocation->width,allocation->height) * 0.45;
+ dial->pointer_width = dial->radius / 5;
+}
+
+static gint
+gtk_dial_expose (GtkWidget *widget,
+ GdkEventExpose *event)
+{
+ GtkDial *dial;
+ GdkPoint points[3];
+ gdouble s,c;
+ gdouble theta;
+ gint xc, yc;
+ gint tick_length;
+ gint i;
+
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ if (event->count > 0)
+ return FALSE;
+
+ dial = GTK_DIAL (widget);
+
+ gdk_window_clear_area (widget->window,
+ 0, 0,
+ widget->allocation.width,
+ widget->allocation.height);
+
+ xc = widget->allocation.width/2;
+ yc = widget->allocation.height/2;
+
+ /* Dibuja las rayitas */
+
+ for (i=0; i<25; i++)
+ {
+ theta = (i*M_PI/18. - M_PI/6.);
+ s = sin(theta);
+ c = cos(theta);
+
+ tick_length = (i%6 == 0) ? dial->pointer_width : dial->pointer_width/2;
+
+ gdk_draw_line (widget->window,
+ widget->style->fg_gc[widget->state],
+ xc + c*(dial->radius - tick_length),
+ yc - s*(dial->radius - tick_length),
+ xc + c*dial->radius,
+ yc - s*dial->radius);
+ }
+
+ /* Dibuja el puntero */
+
+ s = sin(dial->angle);
+ c = cos(dial->angle);
+
+
+ points[0].x = xc + s*dial->pointer_width/2;
+ points[0].y = yc + c*dial->pointer_width/2;
+ points[1].x = xc + c*dial->radius;
+ points[1].y = yc - s*dial->radius;
+ points[2].x = xc - s*dial->pointer_width/2;
+ points[2].y = yc - c*dial->pointer_width/2;
+
+ gtk_draw_polygon (widget->style,
+ widget->window,
+ GTK_STATE_NORMAL,
+ GTK_SHADOW_OUT,
+ points, 3,
+ TRUE);
+
+ return FALSE;
+}
+
+static gint
+gtk_dial_button_press (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ GtkDial *dial;
+ gint dx, dy;
+ double s, c;
+ double d_parallel;
+ double d_perpendicular;
+
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ dial = GTK_DIAL (widget);
+
+
+ /* Determinar si la pulsación del botón fue dentro de la región del
+ puntero - esto lo hacemos calculando la distancia x e y del punto
+ donde se pulsó el botón ratón de la línea que se ha pasado mediante el
+ puntero */
+
+ dx = event->x - widget->allocation.width / 2;
+ dy = widget->allocation.height / 2 - event->y;
+
+ s = sin(dial->angle);
+ c = cos(dial->angle);
+
+ d_parallel = s*dy + c*dx;
+ d_perpendicular = fabs(s*dx - c*dy);
+
+ if (!dial->button &amp;&amp;
+ (d_perpendicular < dial->pointer_width/2) &amp;&amp;
+ (d_parallel > - dial->pointer_width))
+ {
+ gtk_grab_add (widget);
+
+ dial->button = event->button;
+
+ gtk_dial_update_mouse (dial, event->x, event->y);
+ }
+
+ return FALSE;
+}
+
+static gint
+gtk_dial_button_release (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ GtkDial *dial;
+
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ dial = GTK_DIAL (widget);
+
+ if (dial->button == event->button)
+ {
+ gtk_grab_remove (widget);
+
+ dial->button = 0;
+
+ if (dial->policy == GTK_UPDATE_DELAYED)
+ gtk_timeout_remove (dial->timer);
+
+ if ((dial->policy != GTK_UPDATE_CONTINUOUS) &amp;&amp;
+ (dial->old_value != dial->adjustment->value))
+ gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
+ }
+
+ return FALSE;
+}
+
+static gint
+gtk_dial_motion_notify (GtkWidget *widget,
+ GdkEventMotion *event)
+{
+ GtkDial *dial;
+ GdkModifierType mods;
+ gint x, y, mask;
+
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ dial = GTK_DIAL (widget);
+
+ if (dial->button != 0)
+ {
+ x = event->x;
+ y = event->y;
+
+ if (event->is_hint || (event->window != widget->window))
+ gdk_window_get_pointer (widget->window, &amp;x, &amp;y, &amp;mods);
+
+ switch (dial->button)
+ {
+ case 1:
+ mask = GDK_BUTTON1_MASK;
+ break;
+ case 2:
+ mask = GDK_BUTTON2_MASK;
+ break;
+ case 3:
+ mask = GDK_BUTTON3_MASK;
+ break;
+ default:
+ mask = 0;
+ break;
+ }
+
+ if (mods &amp; mask)
+ gtk_dial_update_mouse (dial, x,y);
+ }
+
+ return FALSE;
+}
+
+static gint
+gtk_dial_timer (GtkDial *dial)
+{
+ g_return_val_if_fail (dial != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_DIAL (dial), FALSE);
+
+ if (dial->policy == GTK_UPDATE_DELAYED)
+ gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
+
+ return FALSE;
+}
+
+static void
+gtk_dial_update_mouse (GtkDial *dial, gint x, gint y)
+{
+ gint xc, yc;
+ gfloat old_value;
+
+ g_return_if_fail (dial != NULL);
+ g_return_if_fail (GTK_IS_DIAL (dial));
+
+ xc = GTK_WIDGET(dial)->allocation.width / 2;
+ yc = GTK_WIDGET(dial)->allocation.height / 2;
+
+ old_value = dial->adjustment->value;
+ dial->angle = atan2(yc-y, x-xc);
+
+ if (dial->angle < -M_PI/2.)
+ dial->angle += 2*M_PI;
+
+ if (dial->angle < -M_PI/6)
+ dial->angle = -M_PI/6;
+
+ if (dial->angle > 7.*M_PI/6.)
+ dial->angle = 7.*M_PI/6.;
+
+ dial->adjustment->value = dial->adjustment->lower + (7.*M_PI/6 - dial->angle) *
+ (dial->adjustment->upper - dial->adjustment->lower) / (4.*M_PI/3.);
+
+ if (dial->adjustment->value != old_value)
+ {
+ if (dial->policy == GTK_UPDATE_CONTINUOUS)
+ {
+ gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
+ }
+ else
+ {
+ gtk_widget_draw (GTK_WIDGET(dial), NULL);
+
+ if (dial->policy == GTK_UPDATE_DELAYED)
+ {
+ if (dial->timer)
+ gtk_timeout_remove (dial->timer);
+
+ dial->timer = gtk_timeout_add (SCROLL_DELAY_LENGTH,
+ (GtkFunction) gtk_dial_timer,
+ (gpointer) dial);
+ }
+ }
+ }
+}
+
+static void
+gtk_dial_update (GtkDial *dial)
+{
+ gfloat new_value;
+
+ g_return_if_fail (dial != NULL);
+ g_return_if_fail (GTK_IS_DIAL (dial));
+
+ new_value = dial->adjustment->value;
+
+ if (new_value < dial->adjustment->lower)
+ new_value = dial->adjustment->lower;
+
+ if (new_value > dial->adjustment->upper)
+ new_value = dial->adjustment->upper;
+
+ if (new_value != dial->adjustment->value)
+ {
+ dial->adjustment->value = new_value;
+ gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
+ }
+
+ dial->angle = 7.*M_PI/6. - (new_value - dial->adjustment->lower) * 4.*M_PI/3. /
+ (dial->adjustment->upper - dial->adjustment->lower);
+
+ gtk_widget_draw (GTK_WIDGET(dial), NULL);
+}
+
+static void
+gtk_dial_adjustment_changed (GtkAdjustment *adjustment,
+ gpointer data)
+{
+ GtkDial *dial;
+
+ g_return_if_fail (adjustment != NULL);
+ g_return_if_fail (data != NULL);
+
+ dial = GTK_DIAL (data);
+
+ if ((dial->old_value != adjustment->value) ||
+ (dial->old_lower != adjustment->lower) ||
+ (dial->old_upper != adjustment->upper))
+ {
+ gtk_dial_update (dial);
+
+ dial->old_value = adjustment->value;
+ dial->old_lower = adjustment->lower;
+ dial->old_upper = adjustment->upper;
+ }
+}
+
+static void
+gtk_dial_adjustment_value_changed (GtkAdjustment *adjustment,
+ gpointer data)
+{
+ GtkDial *dial;
+
+ g_return_if_fail (adjustment != NULL);
+ g_return_if_fail (data != NULL);
+
+ dial = GTK_DIAL (data);
+
+ if (dial->old_value != adjustment->value)
+ {
+ gtk_dial_update (dial);
+
+ dial->old_value = adjustment->value;
+ }
+}
+/* fin del ejemplo */
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Scribble
+<p>
+<tscreen><verb>
+/* principio del ejemplo scribble-simple scribble-simple.c */
+
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <gtk/gtk.h>
+
+/* Creamos un backing pixmap para la zona donde dibujamos */
+static GdkPixmap *pixmap = NULL;
+
+/* Creamos un nuevo backing pixmap del tamaño apropiado */
+static gint
+configure_event (GtkWidget *widget, GdkEventConfigure *event)
+{
+ if (pixmap)
+ gdk_pixmap_unref(pixmap);
+
+ pixmap = gdk_pixmap_new(widget->window,
+ widget->allocation.width,
+ widget->allocation.height,
+ -1);
+ gdk_draw_rectangle (pixmap,
+ widget->style->white_gc,
+ TRUE,
+ 0, 0,
+ widget->allocation.width,
+ widget->allocation.height);
+
+ return TRUE;
+}
+
+/* Redibujamos la pantalla con el backing pixmap */
+static gint
+expose_event (GtkWidget *widget, GdkEventExpose *event)
+{
+ gdk_draw_pixmap(widget->window,
+ widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
+ pixmap,
+ event->area.x, event->area.y,
+ event->area.x, event->area.y,
+ event->area.width, event->area.height);
+
+ return FALSE;
+}
+
+/* Dibujamos un rectángulo en la pantalla */
+static void
+draw_brush (GtkWidget *widget, gdouble x, gdouble y)
+{
+ GdkRectangle update_rect;
+
+ update_rect.x = x - 5;
+ update_rect.y = y - 5;
+ update_rect.width = 10;
+ update_rect.height = 10;
+ gdk_draw_rectangle (pixmap,
+ widget->style->black_gc,
+ TRUE,
+ update_rect.x, update_rect.y,
+ update_rect.width, update_rect.height);
+ gtk_widget_draw (widget, &amp;update_rect);
+}
+
+static gint
+button_press_event (GtkWidget *widget, GdkEventButton *event)
+{
+ if (event->button == 1 &amp;&amp; pixmap != NULL)
+ draw_brush (widget, event->x, event->y);
+
+ return TRUE;
+}
+
+static gint
+motion_notify_event (GtkWidget *widget, GdkEventMotion *event)
+{
+ int x, y;
+ GdkModifierType state;
+
+ if (event->is_hint)
+ gdk_window_get_pointer (event->window, &amp;x, &amp;y, &amp;state);
+ else
+ {
+ x = event->x;
+ y = event->y;
+ state = event->state;
+ }
+
+ if (state &amp; GDK_BUTTON1_MASK &amp;&amp; pixmap != NULL)
+ draw_brush (widget, x, y);
+
+ return TRUE;
+}
+
+void
+quit ()
+{
+ gtk_exit (0);
+}
+
+int
+main (int argc, char *argv[])
+{
+ GtkWidget *ventana;
+ GtkWidget *drawing_area;
+ GtkWidget *vbox;
+
+ GtkWidget *boton;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_widget_set_name (ventana, "Test Input");
+
+ vbox = gtk_vbox_new (FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (ventana), vbox);
+ gtk_widget_show (vbox);
+
+ gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
+ GTK_SIGNAL_FUNC (quit), NULL);
+
+ /* Crear la zona de dibujado */
+
+ drawing_area = gtk_drawing_area_new ();
+ gtk_drawing_area_size (GTK_DRAWING_AREA (drawing_area), 200, 200);
+ gtk_box_pack_start (GTK_BOX (vbox), drawing_area, TRUE, TRUE, 0);
+
+ gtk_widget_show (drawing_area);
+
+ /* Las señales utilizadas para manejar el backing pixmap */
+
+ gtk_signal_connect (GTK_OBJECT (drawing_area), "expose_event",
+ (GtkSignalFunc) expose_event, NULL);
+ gtk_signal_connect (GTK_OBJECT(drawing_area),"configure_event",
+ (GtkSignalFunc) configure_event, NULL);
+
+ /* Señales evento */
+
+ gtk_signal_connect (GTK_OBJECT (drawing_area), "motion_notify_event",
+ (GtkSignalFunc) motion_notify_event, NULL);
+ gtk_signal_connect (GTK_OBJECT (drawing_area), "button_press_event",
+ (GtkSignalFunc) button_press_event, NULL);
+
+ gtk_widget_set_events (drawing_area, GDK_EXPOSURE_MASK
+ | GDK_LEAVE_NOTIFY_MASK
+ | GDK_BUTTON_PRESS_MASK
+ | GDK_POINTER_MOTION_MASK
+ | GDK_POINTER_MOTION_HINT_MASK);
+
+ /* .. Y un botón para salir */
+ boton = gtk_button_new_with_label ("Quit");
+ gtk_box_pack_start (GTK_BOX (vbox), boton, FALSE, FALSE, 0);
+
+ gtk_signal_connect_object (GTK_OBJECT (boton), "clicked",
+ GTK_SIGNAL_FUNC (gtk_widget_destroy),
+ GTK_OBJECT (ventana));
+ gtk_widget_show (boton);
+
+ gtk_widget_show (ventana);
+
+ gtk_main ();
+
+ return 0;
+}
+/* fin del ejemplo */
+</verb></tscreen>
+
+<!-- ***************************************************************** -->
+<sect> El <em>widget</em> lista
+<!-- ***************************************************************** -->
+<p>
+ATENCIÓN: El <em>widget</em> GtkList ha sido reemplazado por el
+<em>widget</em> GtkCList.
+
+El <em>widget</em> GtkList está diseñado para actuar como un
+contenedor vertical de <em>widgets</em> que deben ser del tipo
+GtkListItem.
+
+Un <em>widget</em> GtkList tiene su propia ventana para recibir
+eventos y su propio color de fondo, que normalmente es blanco.
+Como es un objeto derivado directamente de GtkContainer puede tratarse
+utilizando la macro GTK_CONTAINER(List), ver el <em>widget</em>
+GtkContainer para obtener más información.
+
+Debería familiarizarse con la utilización de un GList y con sus
+funciones relacionadas <tt/g_list_*()/ para ser capaz de explotar el
+<em>widget</em> GtkList hasta su límite.
+
+Sólo hay un campo dentro de la definición de la estructura del
+<em>widget</em> GtkList que nos es de interés, y es:
+
+<tscreen><verb>
+struct _GtkList
+{
+ ...
+ GList *selection;
+ guint selection_mode;
+ ...
+};
+</verb></tscreen>
+
+El campo <tt/selection/ de un GtkList apunta a una lista enlazada de
+todos los elementos que están actualmente seleccionados, o NULL si la
+selección está vacia. Por lo tanto para saber quien es la actual
+selección debemos leer el campo <tt/GTK_LIST()->selection/, pero no
+modificarlo ya que los campos de los que está constituido GtkList
+están controlados por las funciones gtk_list_*().
+
+El <tt/selection_mode/ de la GtkList determina las posibilidades de
+selección de una GtkList y por tanto los contenidos del campo
+<tt/GTK_LIST()->selection/. El <tt/selection_mode/ puede tener uno de
+los valores siguientes:
+
+<itemize>
+<item> GTK_SELECTION_SINGLE - La selección es o NULL o contiene un
+puntero a un GList con un solo elemento seleccionado.
+
+<item> GTK_SELECTION_BROWSE - La selección es NULL si la lista no
+contiene <em>widgets</em> o si los que contiene no son sensibles, en
+cualquier otro caso contiene un puntero GList a una estructura GList,
+y contendrá por tanto un solo elemento.
+
+<item> GTK_SELECTION_MULTIPLE - La selección es NULL si no hay
+elementos seleccionados o un puntero GList hacia el primer elemento
+seleccionado. ("That in turn") apunta a una estructura GList para
+el segundo elemento seleccionado y así.
+
+<item> GTK_SELECTION_EXTENDED - La selección siempre es NULL.
+</itemize>
+
+El valor por defecto es GTK_SELECTION_MULTIPLE.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Señales
+<p>
+<tscreen><verb>
+void selection_changed( GtkList *list );
+</verb></tscreen>
+
+Se invocará esta señal cuando cambie el campo <tt/selection/ de un
+GtkList. Es decir, cuando un hijo de una GtkList se selecciona o
+deselecciona.
+
+<tscreen><verb>
+void select_child( GtkList *list,
+ GtkWidget *hijo);
+</verb></tscreen>
+
+Se invoca esta señal cuando un hijo de la GtkList está siendo
+seleccionado. Esto ocurre principalmente en llamadas a
+<tt/gtk_list_select_item()/, a <tt/gtk_list_select_child()/, cuando se
+pulsa algún botón y a veces se lanza indirectamente cuando se añade o
+se elimina un hijo del GtkList.
+
+<tscreen><verb>
+void unselect_child( GtkList *list,
+ GtkWidget *hijo );
+</verb></tscreen>
+
+Se invoca esta señal cuando un hijo del GtkList está siendo
+deseleccionado. Esto ocurre principalmente cuando ocurre una llamada a
+<tt/gtk_list_unselect_item()/, <tt/gtk_list_unselect_item()/,
+pulsaciones de botón y a veces se lanza indirectamente cuando se añade
+o se elimina algún hijo de la GtkList.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Funciones
+<p>
+<tscreen><verb>
+guint gtk_list_get_type( void );
+</verb></tscreen>
+
+Devuelve el identificador de tipo `GtkList'.
+
+<tscreen><verb>
+GtkWidget *gtk_list_new( void );
+</verb></tscreen>
+
+Crea un nuevo objeto GtkList. Se devuelve el nuevo <em/widget/ como un
+puntero a un objeto GtkWidget. Se devuelve NULL en caso de producirse
+algún fallo.
+
+<tscreen><verb>
+void gtk_list_insert_items( GtkList *list,
+ GList *items,
+ gint posicion );
+</verb></tscreen>
+
+Introduce elementos en la lista, comenzando en la posición
+<tt/posicion/. <tt/items/ es una lista doblemente enlazada donde cada
+puntero de datos de cada nodo se supone que apunta a una nueva
+GtkListItem (recien creada). Los nodos GList de <tt/items/ son
+controlados por la lista.
+
+<tscreen><verb>
+void gtk_list_append_items( GtkList *list,
+ GList *items);
+</verb></tscreen>
+
+Introduce elementos tal y como lo hace <tt/gtk_list_insert_items()/,
+pero los mete en el final de la lista. Los nodos GList de <tt/items/
+son controlados por la lista.
+
+<tscreen><verb>
+void gtk_list_prepend_items( GtkList *list,
+ GList *items);
+</verb></tscreen>
+
+Introduce elementos tal y como lo hace <tt/gtk_list_insert_items()/,
+pero los mete al principio de la lista. Los nodos GList de <tt/items/
+son controlados por la lista.
+
+<tscreen><verb>
+void gtk_list_remove_items( GtkList *list,
+ GList *items);
+</verb></tscreen>
+
+Elimina elementos de la lista. <tt/items/ es una lista doblemente
+enlazada donde cada puntero de datos de cada nodo se supone que apunta
+a un hijo directo de la lista. El ejecutar o no
+<tt/g_list_free(items)/ cuando la función termine de ejecutarse es
+responsabilidad del que llama a la misma. Está bajo su responsabilidad
+la destrucción de los elementos de la lista.
+
+<tscreen><verb>
+void gtk_list_clear_items( GtkList *list,
+ gint start,
+ gint end );
+</verb></tscreen>
+
+Elimina y destruye los elementos de la lista. Esta operación afectará
+a todos los <em/widgets/ que se encuentren en la lista y en el rango
+especificado por <tt/start/ y <tt/end/.
+
+<tscreen><verb>
+void gtk_list_select_item( GtkList *list,
+ gint item );
+</verb></tscreen>
+
+Invoca la señal <tt/select_child/ para el elemento especificado
+mediante su posición actual en la lista.
+
+<tscreen><verb>
+void gtk_list_unselect_item( GtkList *list,
+ gint item);
+</verb></tscreen>
+
+Invoca la señal <tt/unselect_child/ para un elemento especificado
+mediante su posición actual en la lista.
+
+<tscreen><verb>
+void gtk_list_select_child( GtkList *list,
+ GtkWidget *hijo);
+</verb></tscreen>
+
+Invoca la señal <tt/select_child/ para el hijo especificado.
+
+<tscreen><verb>
+void gtk_list_unselect_child( GtkList *list,
+ GtkWidget *hijo);
+</verb></tscreen>
+
+Invoca la señal <tt/unselect_child/ para el hijo especificado.
+
+<tscreen><verb>
+gint gtk_list_child_position( GtkList *list,
+ GtkWidget *hijo);
+</verb></tscreen>
+
+Devuelve la posición de <tt/hijo/ en la lista. Se devuelve «-1» en
+caso de producirse algún error.
+
+<tscreen><verb>
+void gtk_list_set_selection_mode( GtkList *list,
+ GtkSelectionMode mode );
+</verb></tscreen>
+
+Pone el modo de selección, que puede ser <tt/GTK_SELECTION_SINGLE/,
+<tt/GTK_SELECTION_BROWSE/, <tt/GTK_SELECTION_MULTIPLE/ o
+<tt/GTK_SELECTION_EXTENDED/.
+
+<tscreen><verb>
+GtkList *GTK_LIST( gpointer obj );
+</verb></tscreen>
+
+Convierte un puntero general en `GtkList *'. Para más información *Note
+Standard Macros::.
+
+<tscreen><verb>
+GtkListClass *GTK_LIST_CLASS( gpointer class);
+</verb></tscreen>
+
+Convierte un puntero general en `GtkListClass *'. Para más información
+*Note Standard Macros::.
+
+<tscreen><verb>
+gint GTK_IS_LIST( gpointer obj);
+</verb></tscreen>
+
+Determina si un puntero general se refiere a un objeto `GtkList'. Para
+más información, *Note Standard Macros::.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Ejemplo
+<p>
+A continuación tenemos un programa ejemplo que muestra los cambios de
+la selección de un GtkList, y le deja «arrestar» elementos de la
+lista en una prisión, seleccionándolos con el botón derecho del ratón.
+
+<tscreen><verb>
+/* principio del ejemplo list list.c */
+
+/* incluye los ficheros de cabecera de gtk+
+ * incluye stdio.h, que necesitamos para la función printf()
+ */
+#include <gtk/gtk.h>
+#include <stdio.h>
+
+/* ésta es nuestra cadena de identificación para almacenar datos en la
+ * lista de elementos
+ */
+const gchar *list_item_data_key="list_item_data";
+
+
+/* prototipos para los manejadores de señal que vamos a conectar con
+ * el widget GtkList
+ */
+static void sigh_print_selection (GtkWidget *gtklist,
+ gpointer func_data);
+static void sigh_button_event (GtkWidget *gtklist,
+ GdkEventButton *event,
+ GtkWidget *frame);
+
+
+/* función principal donde se establece el interface con el usuario */
+
+gint main (int argc, gchar *argv[])
+{
+ GtkWidget *separator;
+ GtkWidget *ventana;
+ GtkWidget *vbox;
+ GtkWidget *scrolled_window;
+ GtkWidget *frame;
+ GtkWidget *gtklist;
+ GtkWidget *boton;
+ GtkWidget *list_item;
+ GList *dlist;
+ guint i;
+ gchar buffer[64];
+
+
+ /* inicializar gtk+ (y consecuentemente gdk) */
+
+ gtk_init(&amp;argc, &amp;argv);
+
+
+ /* crear una ventana donde meter todos los widgets y conectar
+ * gtk_main_quit() con el evento "destroy" de la ventana para
+ * poder controlar los eventos de cerrado de ventana del
+ * administrador de ventanas
+ */
+ ventana=gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title(GTK_WINDOW(ventana), "GtkList Example");
+ gtk_signal_connect(GTK_OBJECT(ventana),
+ "destroy",
+ GTK_SIGNAL_FUNC(gtk_main_quit),
+ NULL);
+
+
+ /* dentro de la ventana necesitamos una caja para alinear los
+ * widgets verticalmente */
+ vbox=gtk_vbox_new(FALSE, 5);
+ gtk_container_border_width(GTK_CONTAINER(vbox), 5);
+ gtk_container_add(GTK_CONTAINER(ventana), vbox);
+ gtk_widget_show(vbox);
+
+ /* Ésta es la ventana con barras de desplazamiento donde meteremos
+ * el widget GtkList */
+ scrolled_window=gtk_scrolled_window_new(NULL, NULL);
+ gtk_widget_set_usize(scrolled_window, 250, 150);
+ gtk_container_add(GTK_CONTAINER(vbox), scrolled_window);
+ gtk_widget_show(scrolled_window);
+
+ /* crear el widget GtkList
+ * conectar la función manipuladora de señal
+ * sigh_print_selection() a la señal "selection_changed" del
+ * GtkList para imprimir los elementos seleccionados cada vez que
+ * cambie la selección */
+ gtklist=gtk_list_new();
+ gtk_container_add(GTK_CONTAINER(scrolled_window), gtklist);
+ gtk_widget_show(gtklist);
+ gtk_signal_connect(GTK_OBJECT(gtklist),
+ "selection_changed",
+ GTK_SIGNAL_FUNC(sigh_print_selection),
+ NULL);
+
+ /* creamos una "Prisión" donde meteremos una lista de elementos ;)
+ */
+ frame=gtk_frame_new("Prison");
+ gtk_widget_set_usize(frame, 200, 50);
+ gtk_container_border_width(GTK_CONTAINER(frame), 5);
+ gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);
+ gtk_container_add(GTK_CONTAINER(vbox), frame);
+ gtk_widget_show(frame);
+
+ /* conectamos el manipulador de señal sigh_button_event() al
+ * GtkList que manejará la lista de elementos "arrestados"
+ */
+ gtk_signal_connect(GTK_OBJECT(gtklist),
+ "button_release_event",
+ GTK_SIGNAL_FUNC(sigh_button_event),
+ frame);
+
+ /* crear un separador
+ */
+ separator=gtk_hseparator_new();
+ gtk_container_add(GTK_CONTAINER(vbox), separator);
+ gtk_widget_show(separator);
+
+ /* crear finalmente un botón y conectar su señal "clicked" con la
+ * destrucción de la ventana
+ */
+ boton=gtk_button_new_with_label("Close");
+ gtk_container_add(GTK_CONTAINER(vbox), boton);
+ gtk_widget_show(boton);
+ gtk_signal_connect_object(GTK_OBJECT(boton),
+ "clicked",
+ GTK_SIGNAL_FUNC(gtk_widget_destroy),
+ GTK_OBJECT(ventana));
+
+
+ /* ahora creamos 5 elementos de lista, teniendo cada uno su propia
+ * etiqueta y añadiéndolos a la GtkList mediante
+ * gtk_container_add() también consultaremos la cadena de texto de
+ * la etiqueta y la asociaremos con la list_item_data_key para
+ * cada elemento de la lista
+ */
+ for (i=0; i<5; i++) {
+ GtkWidget *etiqueta;
+ gchar *string;
+
+ sprintf(buffer, "ListItemContainer with Label #%d", i);
+ etiqueta=gtk_label_new(buffer);
+ list_item=gtk_list_item_new();
+ gtk_container_add(GTK_CONTAINER(list_item), etiqueta);
+ gtk_widget_show(etiqueta);
+ gtk_container_add(GTK_CONTAINER(gtklist), list_item);
+ gtk_widget_show(list_item);
+ gtk_label_get(GTK_LABEL(etiqueta), &amp;string);
+ gtk_object_set_data(GTK_OBJECT(list_item),
+ list_item_data_key,
+ string);
+ }
+ /* aquí, estamos creando otras 5 etiquetas, esta vez utilizaremos
+ * gtk_list_item_new_with_label() para la creación
+ * no podemos consultar la cadena de texto de la etiqueta ya que
+ * no tenemos el puntero de etiquetas y por tanto lo único que
+ * haremos será asociar el list_item_data_key de cada elemento de
+ * la lista con la misma cadena de texto. Para añadirlo a la lista
+ * de elementos los pondremos en lista doblemente enlazada
+ * (GList), y entonces los añadimos mediante una simple llamada a
+ * gtk_list_append_items()
+ * como utilizamos g_list_prepend() para poner los elementos en la
+ * lista doblemente enlazada, su orden será descendente (en vez de
+ * ascendente como cuando utilizamos g_list_append())
+ */
+ dlist=NULL;
+ for (; i<10; i++) {
+ sprintf(buffer, "List Item with Label %d", i);
+ list_item=gtk_list_item_new_with_label(buffer);
+ dlist=g_list_prepend(dlist, list_item);
+ gtk_widget_show(list_item);
+ gtk_object_set_data(GTK_OBJECT(list_item),
+ list_item_data_key,
+ "ListItem with integrated Label");
+ }
+ gtk_list_append_items(GTK_LIST(gtklist), dlist);
+
+ /* finalmente queremos ver la ventana, ¿verdad? ;)
+ */
+ gtk_widget_show(ventana);
+
+ /* y nos metemos en el bucle de eventos de gtk
+ */
+ gtk_main();
+
+ /* llegaremos aquí después de que se llame a gtk_main_quit(), lo
+ * que ocurre si se destruye la ventana
+ */
+ return 0;
+}
+
+/* éste es el manejador de señal que se conectó a los eventos de
+ * pulsar/soltar de los botones de la GtkList
+ */
+void
+sigh_button_event (GtkWidget *gtklist,
+ GdkEventButton *event,
+ GtkWidget *frame)
+{
+ /* sólo hacemos algo si el tercer botón (el botón derecho) se
+ * levanta
+ */
+ if (event->type==GDK_BUTTON_RELEASE &amp;&amp;
+ event->button==3) {
+ GList *dlist, *free_list;
+ GtkWidget *new_prisoner;
+
+ /* sacar la lista de elementos que están actualmente
+ * seleccionados y que serán nuestro próximos prisioneros ;)
+ */
+ dlist=GTK_LIST(gtklist)->selection;
+ if (dlist)
+ new_prisoner=GTK_WIDGET(dlist->data);
+ else
+ new_prisoner=NULL;
+
+ /* buscar por elementos de la lista ya encarcelados, los
+ * volveremos a poner en la lista, recordar que hay que
+ * eliminar la lista doblemente enlazada que devuelve
+ * gtk_container_children()
+ */
+ dlist=gtk_container_children(GTK_CONTAINER(frame));
+ free_list=dlist;
+ while (dlist) {
+ GtkWidget *list_item;
+
+ list_item=dlist->data;
+
+ gtk_widget_reparent(list_item, gtklist);
+
+ dlist=dlist->next;
+ }
+ g_list_free(free_list);
+
+ /* si tenemos un nuevo prisionero, lo eliminamos de la GtkList
+ * y lo ponemos en el marco "Prisión". Primero tenemos que
+ * deseleccionarlo
+ */
+ if (new_prisoner) {
+ GList static_dlist;
+
+ static_dlist.data=new_prisoner;
+ static_dlist.next=NULL;
+ static_dlist.prev=NULL;
+
+ gtk_list_unselect_child(GTK_LIST(gtklist),
+ new_prisoner);
+ gtk_widget_reparent(new_prisoner, frame);
+ }
+ }
+}
+
+/* éste es el manipulador de señal que se llama si GtkList emite la
+ * señal "selection_changed"
+ */
+void
+sigh_print_selection (GtkWidget *gtklist,
+ gpointer func_data)
+{
+ GList *dlist;
+
+ /* sacar la lista doblemente enlazada de los elementos
+ * seleccionados en GtkList, ¡recuerde que hay que tratarla como
+ * de solo lectura!
+ */
+ dlist=GTK_LIST(gtklist)->selection;
+
+ /* si no hay elementos seleccionados no queda nada por hacer
+ * excepto informar al usuario
+ */
+ if (!dlist) {
+ g_print("Selection cleared\n");
+ return;
+ }
+ /* Bien, conseguimos una selección y la imprimimos
+ */
+ g_print("The selection is a ");
+
+ /* obtenemos la lista de elementos de la lista doblemente enlazada
+ * y entonces consultamos los datos asociados con la
+ * list_item_data_key que acabamos de imprimir
+ */
+ while (dlist) {
+ GtkObject *list_item;
+ gchar *item_data_string;
+
+ list_item=GTK_OBJECT(dlist->data);
+ item_data_string=gtk_object_get_data(list_item,
+ list_item_data_key);
+ g_print("%s ", item_data_string);
+
+ dlist=dlist->next;
+ }
+ g_print("\n");
+}
+/* fin del ejemplo */
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> El <em/widget/ GtkListItem
+<p>
+El <em/widget/ GtkListItem está diseñado para comportarse como un
+contenedor que tiene un hijo, proporcionando funciones para la
+selección/deselección justo como las necesitan los hijos del
+<em/widget/ GtkList.
+
+Un GtkListItem tiene su propia ventana para recibir eventos y tiene su
+propio color de fondo, que normalmente es blanco.
+
+Como está derivado directamente de un GtkItem, puede tratarse como tal
+utilizando la macro GTK_ITEM(ListItem), ver el <em/widget/ GtkItem
+para más detalles. Normalmente un GtkListItem sólo tiene una etiqueta
+para identificar, por ejemplo, el nombre de un fichero dentro de una
+GtkList -- por lo tanto se proporciona la función
+<tt/gtk_list_item_new_with_label()/. Se puede conseguir el mismo
+efecto creando un GtkLabel, poniendo su alineación a <tt/xalign=0/ e
+<tt/yalign=0.5/ y seguido de una adición al contenedor GtkListItem.
+
+Nadie le obliga a meter un GtkLabel en un GtkListItem, puede meter un
+GtkVBox o un GtkArrow, etc...
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Señales
+<p>
+Un GtkListItem no crea por sí misma nuevas señales, pero hereda las
+señales de un GtkItem. Para más información *Note GtkItem::.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Funciones
+<p>
+<tscreen><verb>
+guint gtk_list_item_get_type( void );
+</verb></tscreen>
+
+Devuelve el identificador de tipo `GtkListItem'.
+
+<tscreen><verb>
+GtkWidget *gtk_list_item_new( void );
+</verb></tscreen>
+
+Crea un nuevo objeto GtkListItem. Se devuelve el nuevo <em/widget/
+como un puntero a un objeto GtkWidget. Se devuelve NULL en caso de
+producirse algún error.
+
+<tscreen><verb>
+GtkWidget *gtk_list_item_new_with_label( gchar *etiqueta );
+</verb></tscreen>
+
+Crea un nuevo objeto GtkListItem, con una sola GtkLabel como único
+hijo. Se devuelve el nuevo <em/widget/ como un puntero a un objeto
+GtkWidget. Se devuelve NULL en caso de producirse algún error.
+
+<tscreen><verb>
+void gtk_list_item_select( GtkListItem *list_item );
+</verb></tscreen>
+
+Esta función es, básicamente, un recubrimiento de una llamada a
+<tt/gtk_item_select (GTK_ITEM (list_item))/, y emitirá la señal
+<tt/select/. Para más información *Note GtkItem::.
+
+<tscreen><verb>
+void gtk_list_item_deselect( GtkListItem *list_item );
+</verb></tscreen>
+
+Esta función es, básicamente, un recubrimiento de una llamada a
+<tt/gtk_item_deselect (GTK_ITEM (list_item))/, y emitirá la señal
+<tt/deselect/. Para más información *Note GtkItem::.
+
+<tscreen><verb>
+GtkListItem *GTK_LIST_ITEM( gpointer obj );
+</verb></tscreen>
+
+Convierte un puntero general a `GtkListItem *'. Para más información
+*Note Standard Macros::.
+
+<tscreen><verb>
+GtkListItemClass *GTK_LIST_ITEM_CLASS( gpointer class );
+</verb></tscreen>
+
+Convierte un puntero general a `GtkListItemClass *'. Para más
+información *Note Standard Macros::.
+
+<tscreen><verb>
+gint GTK_IS_LIST_ITEM( gpointer obj );
+</verb></tscreen>
+
+Determina si un puntero general se refiere a un puntero
+`GtkListItem'. Para más información *Note Standard Macros::.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Ejemplo
+<p>
+Para ver un ejemplo de todo esto, mire el de GtkList, que también
+cubre la utilización un GtkListItem.
+
+</article>
diff --git a/docs/tutorial/gtk_tut_12.es.sgml b/docs/tutorial/gtk_tut_12.es.sgml
new file mode 100755
index 000000000..38449c8ed
--- /dev/null
+++ b/docs/tutorial/gtk_tut_12.es.sgml
@@ -0,0 +1,17638 @@
+<!doctype linuxdoc system>
+
+<!--
+ Traducción realizada por:
+ Joaquín Cuenca Abela (e98cuenc@criens.u-psud.fr)
+ Eduardo Anglada Varela (eduardo.anglada@adi.uam.es)
+
+ Versión beta 2 de la traducción del GTK+
+ Tutorial 1.2. Cualquier sugerencia será bienvenida.
+ Los cambios más significativos de esta versión son la traducción
+ de las variables de los programas y XXX.
+
+ Si quiere obtener este documento puede encontrarlo en Linux Landia:
+ http://www.croftj.net/~barreiro/spanish/gnome-es/
+-->
+
+<article>
+<title>GTK Tutorial v1.2
+<author>Tony Gale <tt><htmlurl url="mailto:gale@gtk.org"
+ name="&lt;gale@gtk.org&gt;"></tt>,
+Ian Main <tt><htmlurl url="mailto:imain@gtk.org"
+ name="&lt;imain@gtk.org&gt;"></tt>
+<date>21 de Febrero de 1999
+<abstract>
+Este documento es un tutorial sobre como utilizar GTK (el GIMP
+Toolkit) en C
+</abstract>
+
+<toc>
+
+<!-- ***************************************************************** -->
+<sect>Introducción
+<!-- ***************************************************************** -->
+<p>
+GTK (GIMP Toolkit) es una biblioteca para crear interfaces gráficas
+de usuario. Su licencia es la LGPL, así que mediante GTK podrá
+desarrollar programas con licencias abiertas, gratuitas, libres, y
+hasta licencias comerciales no libres sin mayores problemas.
+
+Se llama el GIMP toolkit porque fue escrito para el desarrollo del
+General Image Manipulation Program (GIMP), pero ahora GTK se utiliza
+en un gran número de proyectos de programación, incluyendo el
+proyecto GNU Network Object Model Environment (GNOME). GTK está
+construido encima de GDK (GIMP Drawing Kit) que básicamente es un
+recubrimiento de las funciones de bajo nivel que deben haber para
+acceder al sistema de ventanas sobre el que se programe (Xlib en el
+caso de X windows). Los principales autores de GTK son:
+
+<itemize>
+<item> Peter Mattis <tt><htmlurl url="mailto:petm@xcf.berkeley.edu"
+ name="petm@xcf.berkeley.edu"></tt>
+<item> Spencer Kimball <tt><htmlurl url="mailto:spencer@xcf.berkeley.edu"
+ name="spencer@xcf.berkeley.edu"></tt>
+<item> Josh MacDonald <tt><htmlurl url="mailto:jmacd@xcf.berkeley.edu"
+ name="jmacd@xcf.berkeley.edu"></tt>
+</itemize>
+
+GTK es esencialmente una interfaz para la programación de
+aplicaciones orientadas al objeto (API). Aunque está completamente
+escrito en C, esta implementado haciendo uso de la idea de clases y de
+funciones respuesta o de <em/callback/ (punteros o funciones).
+
+Tenemos un tercer componente llamado glib, que contiene unas cuantas
+funciones para reemplazar algunas llamadas estándar, así como
+funciones adicionales para manejar listas enlazadas, etc... Se
+reemplazan algunas funciones para aumentar la portabilidad de GTK, ya
+que algunas de las funciones implementadas no están disponibles o
+no son estándar en otros unixs, como por ejemplo
+g_strerror(). Algunas otras contienen mejoras a la versión de libc,
+como g_malloc que mejora las posibilidades de encontrar errores.
+
+Este tutorial describe la interfaz C de GTK. Hay recubrimientos GTK
+para muchos otros lenguajes, incluyendo C++, Guile, Perl, Python, TOM,
+Ada95, Objective C, Free Pascal, y Eiffel. Si va a utilizar el
+recubrimiento para alguno de estos lenguajes, mire primero su
+documentación. En algunos casos la documentación puede describir
+algún convenio importante (que debería conocer de antemano) y
+después puede volver a este tutorial. También hay algún API
+multiplataforma (como wxWindows y V) que utilizan GTK como una de sus
+plataformas destino; de nuevo, consulte primero la documentación
+que viene con estos paquetes.
+
+Si está desarrollando su aplicación GTK en C++, hay algunas
+cosas que debería saber. Hay un recubriento a GTK para C++ llamado
+GTK--, que proporciona una interfaz C++ a GTK; probablemente
+debería empezar mirando ahí. Si no le gusta esa aproximación
+al problema, por los motivos que sean, tiene dos
+alternativas. Primero, puede ceñirse al subconjunto C de C++ cuando
+realice alguna llamada a GTK a través de su interfaz en C. Segundo,
+puede utilizar GTK y C++ al mismo tiempo declarando todas las
+funciones respuesta como funciones estáticas en clases C++, y de
+nuevo, llamar a GTK utilizando su interfaz C. Si elige esta última
+forma de actuar, puede incluir como dato de la función respuesta un
+puntero al objeto a manipular (el también llamado valor
+«this»). La elección de una u otra opción es cuestión de
+gustos personales, ya que de las tres maneras conseguirá utilizar
+GTK en C++. Ninguna de estas aproximaciones requiere el uso de un
+preprocesador especializado, por lo que sin importar la opción que
+escoja podrá utilizar C++ estándar en C++.
+
+Este tutorial es un intento de documentar GTK tanto como sea posible,
+pero no está completo. Este tutorial asume un buen conocimiento de
+C y de como crear programas bajo este lenguaje. Se verá beneficiado
+si tiene un conocimiento previo de la programación en X, pero no
+debería ser necesario. Si está aprendiendo GTK y es el primer
+conjunto de <em/widgets/ que utiliza, por favor envíenos sus
+comentarios sobre este tutorial y los problemas que ha
+encontrado.
+
+Este documento es un `trabajo pendiente de finalizar'. Para encontrar
+actualizaciones mire en http://www.gtk.org/ <htmlurl
+url="http://www.gtk.org/" name="http://www.gtk.org/">.
+
+Me gustaría escuchar cualquier problema que le surja mientras
+aprende GTK siguiendo este documento, y apreciaré cualquier
+información sobre como mejorarlo. Por favor, vea la sección <ref
+id="sec_Contributing" name="Contribuyendo"> para encontrar más
+información.
+
+<!-- ***************************************************************** -->
+<sect>Comenzando
+<!-- ***************************************************************** -->
+
+<p>
+Por supuesto lo primero que hay que hacer es descargar las fuentes de
+GTK e instalarlas. La última versión siempre se puede obtener de
+ftp.gtk.org (en el directorio /pub/gtk). En <htmlurl
+url="http://www.gtk.org/" name="http://www.gtk.org/"> hay más
+información sobre GTK. Para configurar GTK hay que usar GNU
+autoconf. Una vez descomprimido se pueden obtener las opciones usando
+<tt>./configure --help</tt>.
+
+El código de GTK además contiene las fuentes completas de todos
+los ejemplos usados en este manual, así como los makefiles para
+compilarlos.
+
+Para comenzar nuestra introducción a GTK vamos a empezar con el
+programa más sencillo posible. Con él vamos a crear una ventana de
+200x200 <em/pixels/ que sólo se puede destruir desde el shell.
+
+<tscreen><verb>
+/* principio del ejemplo base base.c */
+
+#include <gtk/gtk.h>
+
+int main (int argc, char *argv[])
+{
+ GtkWidget *ventana;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_widget_show (ventana);
+
+ gtk_main ();
+
+ return 0;
+}
+/* final del ejemplo */
+</verb></tscreen>
+
+Puede compilar el programa anterior con gcc tecleando:
+<tscreen><verb>
+gcc base.c -o base `gtk-config --cflags --libs`
+</verb></tscreen>
+
+El significado de la extraña opción de compilación se explica
+más adelante.
+
+Todo programa que use GTK debe llamar a <tt>gtk/gtk.h</tt> donde se
+declaran todas las variables, funciones, estructuras, etc. que serán
+usadas en el programa.
+
+La siguiente línea:
+
+<tscreen><verb>
+gtk_init (&amp;argc, &amp;argv);
+</verb></tscreen>
+
+Llama a la función gtk_init (gint *argc, gchar *** argv) responsable
+de `arrancar' la biblioteca y de establecer algunos parámetros (como son
+los colores y los visuales por defecto), llama a gdk_init (gint *argc,
+gchar *** argv) que inicializa la biblioteca para que pueda
+utilizarse, establece los controladores de las señales y comprueba los
+argumentos pasados a la aplicación desde la línea de comandos,
+buscando alguno de los siguientes:
+
+<itemize>
+<item> <tt/--gtk-module/
+<item> <tt/--g-fatal-warnings/
+<item> <tt/--gtk-debug/
+<item> <tt/--gtk-no-debug/
+<item> <tt/--gdk-debug/
+<item> <tt/--gdk-no-debug/
+<item> <tt/--display/
+<item> <tt/--sync/
+<item> <tt/--no-xshm/
+<item> <tt/--name/
+<item> <tt/--class/
+</itemize>
+
+En el caso de que encuentre alguno lo quita de la lista, dejando todo
+aquello que no reconozca para que el programa lo utilice o lo
+ignore. Así se consigue crear un conjunto de argumentos que son
+comunes a todas las aplicaciones basadas en GTK.
+
+Las dos líneas de código siguientes crean y muestran una ventana.
+
+<tscreen><verb>
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_widget_show (ventana);
+</verb></tscreen>
+
+El argumento GTK_WINDOW_TOPLEVEL especifica que queremos que el gestor
+de ventanas decore y sitúe la ventana. En lugar de crear una ventana
+de tamaño 0 x 0 toda ventana sin hijos por defecto es de 200 x 200, con
+lo que se consigue que pueda ser manipulada.
+
+La función gtk_widget_show() le comunica a GTK que hemos acabado de
+especificar los atributos del <em/widget/, y que por tanto puede
+mostrarlo.
+
+La última línea comienza el proceso del bucle principal de GTK.
+
+<tscreen><verb>
+gtk_main ();
+</verb></tscreen>
+
+Otra llamada que siempre está presente en cualquier aplicación es
+gtk_main(). Cuando el control llega a ella, GTK se queda dormido
+esperando a que suceda algún tipo de evento de las X (como puede ser
+pulsar un botón), que pase el tiempo necesario para que el usuario
+haga algo, o que se produzcan notificaciones de IO de archivos. En
+nuestro caso concreto todos los eventos serán ignorados.
+
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Programa «Hola Mundo» en GTK
+<p>
+El siguiente ejemplo es un programa con un <em/widget/ (un
+botón). Simplemente es la versión de GTK del clásico «hola mundo».
+
+<tscreen><verb>
+/* comienzo del ejemplo holamundo */
+#include <gtk/gtk.h>
+
+/* Ésta es una función respuesta (callback). Sus argumentos
+ son ignorados por en este ejemplo */
+void hello (GtkWidget *widget, gpointer data)
+{
+ g_print ("Hola mundo\n");
+}
+
+gint delete_event(GtkWidget *widget, GdkEvent *event, gpointer data)
+{
+ /* si se devuelve FALSE al administrador de llamadas
+ * "delete_event", GTK emitirá la señal de destrucción
+ * "destroy". Esto es útil para diálogos emergentes del
+ * tipo: ¿Seguro que desea salir?
+
+ g_print ("Ha ocurrido un evento delete\n");
+
+ /* Cambiando TRUE por FALSE la ventana se destruirá con
+ * "delete_event"*/
+
+ return (TRUE);
+}
+
+/* otra respuesta */
+void destroy (GtkWidget *widget, gpointer data)
+{
+ gtk_main_quit ();
+}
+
+int main (int argc, char *argv[])
+{
+
+ /* GtkWidget es el tipo de almacenamiento usado para los
+ * widgets */
+ GtkWidget *ventana;
+ GtkWidget *boton;
+
+ /* En cualquier aplicación hay que realizar la siguiente
+ * llamada. Los argumentos son tomados de la línea de comandos
+ * y devueltos a la aplicación. */
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ /* creamos una ventana nueva */
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ /* Cuando la ventana recibe la señal "delete_event" (emitida
+ * por el gestor de ventanas, normalmente mediante la opción
+ * 'close', o en la barra del título) hacemos que llame a la
+ * función delete_event() tal y como ya hemos visto. Los datos
+ * pasados a la función de respuesta son NULL, y serán ignorados. */
+ gtk_signal_connect (GTK_OBJECT (ventana), "delete_event",
+ GTK_SIGNAL_FUNC (delete_event), NULL);
+
+ /* Aquí conectamos el evento "destroy" con el administrador de
+ * señales. El evento se produce cuando llamamos a
+ * gtk_widget_destroy() desde la ventana o si devolvemos 'FALSE'
+ * en la respuesta "delete_event". */
+ gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
+ GTK_SIGNAL_FUNC (destroy), NULL);
+
+ /* establecemos el ancho del borde de la ventana. */
+ gtk_container_border_width (GTK_CONTAINER (ventana), 10);
+
+ /* creamos un botón nuevo con la etiqueta "Hola mundo" */
+ boton = gtk_button_new_with_label ("Hola mundo");
+
+ /* Cuando el botón recibe la señal "clicked" llama a la
+ * función hello() pasándole NULL como argumento. (La
+ * función ya ha sido definida arriba). */
+ gtk_signal_connect (GTK_OBJECT (boton), "clicked",
+ GTK_SIGNAL_FUNC (hello), NULL);
+
+ /* Esto hará que la ventana sea destruida llamando a
+ * gtk_widget_destroy(ventana) cuando se produzca "clicked". Una
+ * vez mas la señal de destrucción puede provenir del gestor
+ * de ventanas o de aquí. */
+ gtk_signal_connect_object (GTK_OBJECT (boton), "clicked",
+ GTK_SIGNAL_FUNC (gtk_widget_destroy),
+ GTK_OBJECT (ventana));
+
+ /* Ahora empaquetamos el botón en la ventana (usamos un gtk
+ * container ). */
+ gtk_container_add (GTK_CONTAINER (ventana), boton);
+
+ /* El último paso es representar el nuevo widget... */
+ gtk_widget_show (boton);
+
+ /* y la ventana */
+ gtk_widget_show (ventana);
+
+ /* Todas las aplicaciones basadas en GTK deben tener una llamada
+ * gtk_main() ya que el control termina justo aquí y debe
+ * esperar a que suceda algún evento */
+
+ gtk_main ();
+
+ return 0;
+}
+/* final del ejemplo*/
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Compilando Hello World
+<p>
+Para compilar el ejemplo hay que usar:
+
+<tscreen><verb>
+gcc -Wall -g helloworld.c -o hello_world `gtk-config --cflags` \
+ `gtk-config --libs`
+</verb></tscreen>
+
+Usamos el programa <tt>gtk-config</>, que ya viene (y se instala) con
+la biblioteca. Es muy útil porque `conoce' que opciones son
+necesarias para compilar programas que usen gtk. <tt>gtk-config
+--cflags</tt> dará una lista con los directorios donde el
+compilador debe buscar ficheros «include». A su vez <tt>gtk-config
+--libs</tt> nos permite saber las bibliotecas que el compilador
+intentará enlazar y dónde buscarlas.
+
+Hay que destacar que las comillas simples en la orden de
+compilación son absolutamente necesarias.
+
+Las bibliotecas que se enlazan normalmente son:
+
+<itemize>
+
+<item>La biblioteca GTK (-lgtk), la biblioteca de <em/widgets/ que se
+encuentra encima de GDK.
+
+<item>La biblioteca GDK (-lgdk), el wrapper de Xlib.
+
+<item>La biblioteca glib (-lglib), que contiene diversas funciones. En
+nuestro ejemplo sólo hemos usado g_print(). GTK está construida
+encima de glib por lo que simpre se usará. Vea la sección <ref
+id="sec_glib" name="glib"> para más detalles.
+
+<item>La biblioteca Xlib (-lX11) que es usada por GDK.
+
+<item> La biblioteca Xext (-lXext) contiene código para
+<em/pixmaps/ de memoria compartida y otras extensiones.
+
+<item>La biblioteca matemática (-lm). Es usada por GTK para
+diferentes cosas.
+
+</itemize>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Teoría de señales y respuestas
+<p>
+Antes de profundizar en <tt/holamundo/ vamos a discutir las
+señales y las respuestas. GTK es un toolkit (conjunto de
+herramientas) gestionadas mediante eventos. Esto quiere decir que GTK
+«duerme» en gtk_main hasta que se recibe un evento, momento en el
+cual se transfiere el control a la función adecuada.
+
+El control se transfiere mediante «señales». (Conviene destacar
+que las señales de GTK no son iguales que las de los sistemas
+UNIX, aunque la terminología es la misma.) Cuando sucede un evento,
+como por ejemplo la pulsación de un botón, se «emitirá» la
+señal apropiada por el <em/widget/ pulsado. Así es como GTK
+proporciona la mayor parte de su utilidad. Hay un conjunto de
+señales que todos los <em/widgets/ heredan, como por ejemplo
+«destroy» y hay señales que son específicas de cada
+<em/widget/, como por ejemplo la señal «toggled» de un botón
+de selección (botón <em/toggle/).
+
+Para que un botón haga algo crearemos un controlador que se encarga de
+recoger las señales y llamar a la función apropiada. Esto se hace
+usando una función como:
+
+<tscreen><verb>
+gint gtk_signal_connect( GtkObject *objeto,
+ gchar *nombre,
+ GtkSignalFunc func,
+ gpointer datos_func );
+</verb></tscreen>
+
+Donde el primer argumento es el <em/widget/ que emite la señal, el
+segundo el nombre de la señal que queremos `cazar', el tercero es
+la función a la que queremos que se llame cuando se `cace' la
+señal y el cuarto los datos que queremos pasarle a esta función.
+
+La función especificada en el tercer argumento se denomina «función
+de respuesta» y debe tener la forma siguiente:
+
+<tscreen><verb>
+void callback_func( GtkWidget *widget,
+ gpointer datos_respuesta );
+</verb></tscreen>
+
+Donde el primer argumento será un puntero al <em/widget/ que emitió la
+señal, y el segundo un puntero a los datos pasados a la función tal y
+como hemos visto en el último argumento a gtk_signal_connect().
+
+Conviene destacar que la declaración de la función de respuesta debe
+servir sólo como guía general, ya que algunas señales específicas
+pueden generar diferentes parámetros de llamada. Por ejemplo, la señal
+de GtkCList «select_row» proporciona los parámetros fila y columna.
+
+Otra llamada usada en el ejemplo del hola mundo es:
+
+<tscreen><verb>
+gint gtk_signal_connect_object( GtkObject *objeto,
+ gchar *nombre,
+ GtkSignalFunc func,
+ GtkObject *slot_object );
+</verb></tscreen>
+
+gtk_signal_connect_object() es idéntica a gtk_signal_connect() excepto
+en que la función de llamada sólo usa un argumento, un puntero a un
+objeto GTK. Por tanto cuando usemos esta función para conectar
+señales, la función de respuesta debe ser de la forma:
+
+<tscreen><verb>
+void callback_func( GtkObject *object );
+</verb></tscreen>
+
+Donde, por regla general, el objeto es un <em/widget/. Sin embargo no
+es normal establecer una respuesta para gtk_signal_connect_object. En
+lugar de ello llamamos a una función de GTK que acepte un <em/widget/
+o un objeto como un argumento, tal y como se vio en el ejemplo hola
+mundo.
+
+¿Para qué sirve tener dos funciones para conectar señales? Simplemente
+para permitir que las funciones de respuesta puedan tener un número
+diferente de argumentos. Muchas funciones de GTK sólo aceptan un
+puntero a un GtkWidget como argumento, por lo que tendrá que usar
+gtk_signal_connect_object() con estas funciones, mientras que
+probablemente tenga que suministrarle información adicional a sus
+funciones.
+
+<!-- XXX Completamente revisado hasta aquí -------------------------- -->
+<sect1>Eventos
+<p>
+Además del mecanismo de señales descrito arriba existe otro conjunto
+de <em>eventos</em> que reflejan como las X manejan los eventos. Se
+pueden asignar funciones de respuesta a estos eventos. Los eventos
+son:
+
+<itemize>
+<item> event
+<item> button_press_event
+<item> button_release_event
+<item> motion_notify_event
+<item> delete_event
+<item> destroy_event
+<item> expose_event
+<item> key_press_event
+<item> key_release_event
+<item> enter_notify_event
+<item> leave_notify_event
+<item> configure_event
+<item> focus_in_event
+<item> focus_out_event
+<item> map_event
+<item> unmap_event
+<item> property_notify_event
+<item> selection_clear_event
+<item> selection_request_event
+<item> selection_notify_event
+<item> proximity_in_event
+<item> proximity_out_event
+<item> drag_begin_event
+<item> drag_request_event
+<item> drag_end_event
+<item> drop_enter_event
+<item> drop_leave_event
+<item> drop_data_available_event
+<item> other_event
+</itemize>
+
+Para conectar una función de respuesta a alguno de los eventos
+anteriores debe usar la función gtk_signal_connect, tal y como se
+descrivió anteriormente, utilizando en el parámetro <tt/name/ uno de
+los nombres de los eventos que se acaban de mencionar. La función de
+respuesta para los eventos tiene un forma ligeramente diferente de la
+que tiene para las señales:
+
+<tscreen><verb>
+void callback_func( GtkWidget *widget,
+ GdkEvent *event,
+ gpointer callback_data );
+</verb></tscreen>
+
+GdkEvent es una estructura <tt/union/ cuyo tipo depende de cual de los
+eventos anteriores haya ocurrido. Para que podamos decir que evento se
+ha lanzado cada una de las posibles alternativas posee un parámetro
+<tt/type/ que refleja cual es el evento en cuestión. Los otros
+componentes de la estructura dependerán del tipo de evento. Algunos
+valores posibles son:
+
+<tscreen><verb>
+ GDK_NOTHING
+ GDK_DELETE
+ GDK_DESTROY
+ GDK_EXPOSE
+ GDK_MOTION_NOTIFY
+ GDK_BUTTON_PRESS
+ GDK_2BUTTON_PRESS
+ GDK_3BUTTON_PRESS
+ GDK_BUTTON_RELEASE
+ GDK_KEY_PRESS
+ GDK_KEY_RELEASE
+ GDK_ENTER_NOTIFY
+ GDK_LEAVE_NOTIFY
+ GDK_FOCUS_CHANGE
+ GDK_CONFIGURE
+ GDK_MAP
+ GDK_UNMAP
+ GDK_PROPERTY_NOTIFY
+ GDK_SELECTION_CLEAR
+ GDK_SELECTION_REQUEST
+ GDK_SELECTION_NOTIFY
+ GDK_PROXIMITY_IN
+ GDK_PROXIMITY_OUT
+ GDK_DRAG_BEGIN
+ GDK_DRAG_REQUEST
+ GDK_DROP_ENTER
+ GDK_DROP_LEAVE
+ GDK_DROP_DATA_AVAIL
+ GDK_CLIENT_EVENT
+ GDK_VISIBILITY_NOTIFY
+ GDK_NO_EXPOSE
+ GDK_OTHER_EVENT /* En desuso, usar filtros en lugar de ella */
+</verb></tscreen>
+
+Por lo tanto para conectar una función de respuesta a uno de estos
+eventos debemos usar algo como:
+
+<tscreen><verb>
+gtk_signal_connect( GTK_OBJECT(boton), "button_press_event",
+ GTK_SIGNAL_FUNC(button_press_callback),
+ NULL);
+</verb></tscreen>
+
+Por supuesto se asume que <tt/boton/ es un <em/widget/
+GtkButton. Cada vez que el puntero del ratón se encuentre sobre el
+botón y éste sea presionado, se llamará a la función
+<tt/button_press_callback/. Esta función puede declararse así:
+
+<tscreen><verb>
+static gint button_press_event (GtkWidget *widget,
+ GdkEventButton *event,
+ gpointer data);
+</verb></tscreen>
+
+Conviene destacar que se puede declarar el segundo argumento como
+<tt/GdkEventButton/ porque sabemos que este tipo de evento ocurrirá
+cuando se llame a la función.
+
+El valor devuelto por esta función es usado para saber si el evento
+debe ser propagado a un nivel más profundo dentro del mecanismo de
+GTK para gestionar los eventos. Si devuelve TRUE el evento ya ha sido
+gestionado y por tanto no tiene que ser tratado por el mecanismo de
+gestión. Por contra si devuelve FALSE se continua con la gestión
+normal del evento. Para más detalles se recomienda leer la sección
+donde se aclara como se produce el proceso de propagación.
+
+Para más detalles acerca de los tipos de información GdkEvent
+consultar el apéndice <ref id="sec_GDK_Event_Types" name="Tipos de
+eventos GDK">.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Aclaración de Hello World
+<p>
+Ahora que conocemos la teoría vamos a aclarar las ideas estudiando
+en detalle el programa <tt/helloworld/.
+
+Ésta es la función respuesta a la que se llamará cuando se
+pulse el botón. En el ejemplo ignoramos tanto el <em/widget/ como
+la información, pero no es difícil usarlos. El siguiente ejemplo
+usará la información que recibe como argumento para decirnos que
+botón fue presionado.
+
+<tscreen><verb>
+void hello (GtkWidget *widget, gpointer data)
+{
+ g_print ("Hello World\n");
+}
+</verb></tscreen>
+
+La siguiente respuesta es un poco especial, el «delete_event» ocurre
+cuando el gestor de ventanas envía este evento a la aplicación. Aquí
+podemos decidir que hacemos con estos eventos. Los podemos ignorar,
+dar algún tipo de respuesta, o simplemente terminar la aplicación.
+
+El valor devuelto en esta respuesta le permite a GTK saber que tiene
+que hacer. Si devolvemos TRUE, estamos diciendo que no queremos que se
+emita la señal «destroy» y por lo tanto queremos que nuestra
+aplicación siga ejecutándose. Si devolvemos FALSE, decimos que
+se emita «destroy», lo que hará que se ejecute nuestro manejador
+de señal de «destroy».
+
+<tscreen><verb>
+gint delete_event(GtkWidget *widget, GdkEvent *event, gpointer data)
+{
+ g_print ("delete event occured\n");
+
+ return (TRUE);
+}
+</verb></tscreen>
+
+Con el siguiente ejemplo presentamos otra función de respuesta que hace
+que el programa salga llamando a gtk_main_quit(). Con esta función le
+decimos a GTK que salga de la rutina gtk_main() cuando vuelva a estar
+en ella.
+
+<tscreen><verb>
+void destroy (GtkWidget *widget, gpointer data)
+{
+ gtk_main_quit ();
+}
+</verb></tscreen>
+
+Como el lector probablemente ya sabe toda aplicación debe tener una
+función main(), y una aplicación GTK no va a ser menos. Todas las
+aplicaciones GTK también tienen una función de este tipo.
+
+<tscreen><verb>
+int main (int argc, char *argv[])
+</verb></tscreen>
+
+Las líneas siguientes declaran un puntero a una estructura del tipo
+GtkWidget, que se utilizarán más adelante para crear una ventana y un
+botón.
+
+<tscreen><verb>
+ GtkWidget *ventana;
+ GtkWidget *boton;
+</verb></tscreen>
+
+Aquí tenemos otra vez a gtk_init. Como antes arranca el conjunto de
+herramientas y filtra las opciones introducidas en la línea de
+órdenes. Cualquier argumento que sea reconocido será borrado de la
+lista de argumentos, de modo que la aplicación recibirá el resto.
+
+<tscreen><verb>
+ gtk_init (&amp;argc, &amp;argv);
+</verb></tscreen>
+
+Ahora vamos a crear una ventana. Simplemente reservamos memoria para
+la estructura GtkWindow *ventana, con lo que ya tenemos una nueva
+ventana, ventana que no se mostrará hasta que llamemos a
+gtk_widget_show (ventana) hacia el final del programa.
+
+<tscreen><verb>
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+</verb></tscreen>
+
+Aquí tenemos un ejemplo de como conectar un manejador de señal a un
+objeto, en este caso, la ventana. La señal a cazar será
+«destroy». Esta señal se emite cuando utilizamos el administrador de
+ventanas para matar la ventana (y devolvemos TRUE en el manejador
+«delete_event»), o cuando usamos llamamos a gtk_widget_destroy()
+pasándole el <em/widget/ que representa la ventana como argumento.
+Así conseguimos manejar los dos casos con una simple llamada a la
+función destroy () (definida arriba) pasándole NULL como argumento y
+ella acabará con la aplicación por nosotros.
+
+GTK_OBJECT y GTK_SIGNAL_FUNC son macros que realizan la comprobación y
+transformación de tipos por nosotros. También aumentan la legibilidad
+del código.
+
+<tscreen><verb>
+ gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
+ GTK_SIGNAL_FUNC (destroy), NULL);
+</verb></tscreen>
+
+La siguiente función establece un atributo a un objeto contenedor
+(discutidos luego). En este caso le pone a la ventana un área
+negra de 10 <em/pixels/ de ancho donde no habrán <em/widgets/. Hay
+funciones similares que serán tratadas con más detalle en la sección
+<ref id="sec_setting_widget_attributes" name="Estableciendo los
+atributos de los <em/widgets/">
+
+De nuevo, GTK_CONTAINER es una macro que se encarga de la conversión
+entre tipos
+
+<tscreen><verb>
+ gtk_container_border_width (GTK_CONTAINER (ventana), 10);
+</verb></tscreen>
+
+La siguiente llamada crea un nuevo botón. Reserva espacio en la
+memoria para una nueva estructura del tipo GtkWidget, la inicializa
+y hace que el puntero <tt/boton/ apunte a esta estructura. Su etiqueta
+será: "Hola mundo".
+
+<tscreen><verb>
+ boton = gtk_button_new_with_label ("Hola mundo");
+</verb></tscreen>
+
+Ahora hacemos que el botón sea útil, para ello enlazamos el botón con
+el manejador de señales para que cuando emita la señal «clicked», se
+llame a nuestra función hola(). Los datos adicionales serán
+ignorados, por lo que simplemente le pasaremos NULL a la función
+respuesta. Obviamente se emitirá la señal «clicked» cuando pulsemos
+en el botón con el ratón.
+
+<tscreen><verb>
+ gtk_signal_connect (GTK_OBJECT (boton), "clicked",
+ GTK_SIGNAL_FUNC (hola), NULL);
+</verb></tscreen>
+XXX
+Ahora vamos a usar el botón para terminar nuestro programa. Así
+aclararemos cómo es posible que la señal «destroy» sea emitida tanto
+por el gestor de ventanas como por nuestro programa. Cuando el botón
+es pulsado, al igual que arriba, se llama a la primera función
+respuesta hello() y después se llamará a esta función. Las funciones
+respuesta serán ejecutadas en el orden en que sean conectadas. Como la
+función gtk_widget_destroy() sólo acepta un GtkWidget como argumento,
+utilizaremos gtk_signal_connect_object() en lugar de
+gtk_signal_connect().
+
+<tscreen><verb>
+gtk_signal_connect_object (GTK_OBJECT (boton), "clicked",
+ GTK_SIGNAL_FUNC (gtk_widget_destroy),
+ GTK_OBJECT (ventana));
+</verb></tscreen>
+
+La siguiente llamada sirve para empaquetar (más detalles luego). Se
+usa para decirle a GTK que el botón debe estar en la ventana dónde
+será mostrado. Conviene destacar que un contenedor GTK sólo puede
+contener un <em/widget/. Existen otros <em/widgets/ (descritos
+después) que sirven para contener y establecer la disposición de
+varios <em/widgets/ de diferentes formas.
+
+<tscreen><verb>
+ gtk_container_add (GTK_CONTAINER (ventana), boton);
+</verb></tscreen>
+
+Ahora ya tenemos todo bien organizado. Como todos los controladores de
+las señales ya están en su sitio, y el botón está situado en la
+ventana donde queremos que esté, sólo nos queda pedirle a GTK que
+muestre todos los <em/widgets/ en pantalla. El <em/widget/ ventana será
+el último en mostrarse queremos que aparezca todo de golpe, en vez de
+ver aparecer la ventana, y después ver aparecer el botón. De todas
+formas con un ejemplo tan simple nunca se notaría cual es el orden de
+aparición.
+
+<tscreen><verb>
+ gtk_widget_show (boton);
+
+ gtk_widget_show (ventana);
+</verb></tscreen>
+
+Llamamos a gtk_main() que espera hasta que el servidor X le comunique
+que se ha producido algún evento para emitir las señales apropiadas.
+
+<tscreen><verb>
+ gtk_main ();
+</verb></tscreen>
+
+Por último el `return' final que devuelve el control cuando gtk_quit()
+sea invocada.
+
+<tscreen><verb>
+ return 0;
+</verb></tscreen>
+
+Cuando pulsemos el botón del ratón el <em/widget/ emite la señal
+correspondiente «clicked». Para que podamos usar la información el
+programa activa el gestor de eventos que al recibir la señal llama a
+la función que hemos elegido. En nuestro ejemplo cuando pulsamos el
+botón se llama a la función hello() con NULL como argumento y además
+se invoca al siguiente manipulador de señal. Así conseguimos que se
+llame a la función gtk_widget_destroy() con el <em/widget/ asociado a
+la ventana como argumento, lo que destruye al <em/widget/. Esto hace
+que la ventana emita la señal «destroy», que es cazada, y que llama
+a nuestra función respuesta destroy(), que simplemente sale de GTK.
+
+Otra posibilidad es usar el gestor de ventanas para acabar con la
+aplicación. Esto emitirá «delete_event» que hará que se
+llame a nuestra función manejadora correspondiente. Si en la
+función manejadora «delete_event» devolvemos TRUE la ventana se
+quedará como si nada hubiese ocurrido, pero si devolvemos FALSE GTK
+emitirá la señal «destroy» que, por supuesto, llamará a la
+función respuesta «destroy», que saldrá de GTK.
+
+<!-- ***************************************************************** -->
+<sect>Avanzando
+<!-- ***************************************************************** -->
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Tipos de datos
+<p>
+Existen algunos detalles de los ejemplos anteriores que hay que aclarar.
+Los tipos gint, gchar, etc. que puede ver por ahí son typedefs a int y
+a char respectivamente. Sirven para que no haya que tener en cuenta el
+tamaño de cada uno de ellos a la hora de hacer cálculos.
+
+Un buen ejemplo es <tt/gint32/ que es un entero de 32 bits independientemente
+de la plataforma, bien sea un Alpha de 64 bits o un i386 de 32. Todas las
+definiciones son muy intuitivas y se encuentran definidas en glib/glib.h
+(que se incluye desde gtk.h).
+
+Probablemente el lector se haya dado cuenta de que se puede usar GtkWidget
+cuando la función llama a un GtkObject. Esto es debido a que GTK
+está orienta a objetos y un <em/widget/ es un GtkObject.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Más sobre el manejo de señales
+<p>
+Si estudiamos en mayor profundidad la declaración de
+gtk_signal_connect:
+
+<tscreen><verb>
+gint gtk_signal_connect( GtkObject *object,
+ gchar *name,
+ GtkSignalFunc func,
+ gpointer func_data );
+</verb></tscreen>
+
+Podemos darnos cuenta de que el valor devuelto es del tipo gint. Este
+valor es una etiqueta que identifica a la función de respuesta. Tal
+y como ya vimos podemos tener tantas funciones de respuesta por
+seÑal y objeto como sean necesarias, y cada una de ellas se
+ejecutará en el mismo orden en el que fueron enlazadas.
+
+Esta etiqueta nos permite eliminar la función respuesta de la lista
+usando:
+
+<tscreen><verb>
+void gtk_signal_disconnect( GtkObject *object,
+ gint id );
+</verb></tscreen>
+
+Por lo tanto podemos desconectar un manejador de señal pasándole
+a la función anterior el <em/widget/ del que queremos desconectar y
+la etiqueta o id devuelta por una de las funciones signal_connect.
+
+Otra función que se usa para quitar desconectar todos los
+controladores de un objeto es:
+
+<tscreen><verb>
+void gtk_signal_handlers_destroy( GtkObject *object );
+</verb></tscreen>
+
+Esta llamada es bastante auto explicativa. Simplemente quitamos todos los
+controladores de señales del objeto que pasamos como primer argumento.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Un Hello World mejorado.
+<p>
+Vamos a mejorar el ejemplo para obtener una visión más amplia
+sobre el manejo de señales y respuestas. También introduciremos
+los <em/widgets/ usados para empaquetar.
+
+<tscreen><verb>
+/* principio del ejemplo helloworld2 */
+
+#include <gtk/gtk.h>
+
+/* Nuestra respuesta mejorada. Los argumentos de la función se
+ * imprimen en el stdout.*/
+void callback (GtkWidget *widget, gpointer data)
+{
+ g_print ("Hello again - %s was pressed\n", (char *) data);
+}
+
+/* otra respuesta*/
+void delete_event (GtkWidget *widget, GdkEvent *event, gpointer data)
+{
+ gtk_main_quit ();
+}
+
+int main (int argc, char *argv[])
+{
+ /* GtkWidget es el tipo de almacenamiento usado para los wigtes*/
+ GtkWidget *ventana;
+ GtkWidget *boton;
+ GtkWidget *caja1;
+
+ /* Esta llamada está presente en todas las aplicaciones basadas
+ * en GTK. Los argumentos introducidos a la aplicación*/
+ gtk_init (&amp;argc, &amp;argv);
+
+ /* creamos una nueva ventana*/
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ /* Esta función es nueva, pone como título de la ventana
+ * "¡Hola botones!"*/
+
+ gtk_window_set_title (GTK_WINDOW (ventana), "¡Hola botones!");
+
+ /* Establecemos el controlador para la llamada delete_event que
+ * termina la aplicación inmediatamente. */
+
+ gtk_signal_connect (GTK_OBJECT (ventana), "delete_event",
+ GTK_SIGNAL_FUNC (delete_event), NULL);
+
+
+ /* Establecemos el ancho del borde de la ventana.*/
+ gtk_container_border_width (GTK_CONTAINER (ventana), 10);
+
+ /* Creamos una caja donde empaquetaremos los widgets. El
+ * procedimiento de empaquetamiento se describe en detalle en la
+ * sección correspondiente. La caja no se ve realmente, sólo
+ * sirve para introducir los widgets. */
+ caja1 = gtk_hbox_new(FALSE, 0);
+
+ /* ponemos la caja en la ventana principal */
+ gtk_container_add (GTK_CONTAINER (ventana), caja1);
+
+ /* Creamos un nuevo botón con la etiqueta "Botón 1". */
+ boton = gtk_button_new_with_label ("Botón 1");
+
+ /* Cada vez que el botón sea pulsado llamamos a la función
+ * "callback" con un puntero a "botón 1" como argumento. */
+ gtk_signal_connect (GTK_OBJECT (boton), "clicked",
+ GTK_SIGNAL_FUNC (callback), (gpointer) "botón 1");
+
+ /* En lugar de gtk_container_add empaquetamos el botón en la
+ * caja invisible, que a su vez ha sido empaquetado en la
+ * ventana. */
+ gtk_box_pack_start(GTK_BOX(caja1), boton, TRUE, TRUE, 0);
+
+ /* Siempre se debe realizar este paso. Sirve para decirle a GTK
+ * que los preparativos del botón ya se han finalizado y que
+ * por tanto puede ser mostrado. */
+ gtk_widget_show(boton);
+
+ /* hacemos lo mismo para crear un segundo botón. */
+ boton = gtk_button_new_with_label ("Botón 2");
+
+ /* Llamamos a la misma función de respuesta pero con diferente
+ * argumento: un puntero a "botón 2". */
+ gtk_signal_connect (GTK_OBJECT (boton), "clicked",
+ GTK_SIGNAL_FUNC (callback), (gpointer) "botón 2");
+
+ gtk_box_pack_start(GTK_BOX(caja1), boton, TRUE, TRUE, 0);
+
+ /* El orden en que mostramos los botones no es realmente
+ * importante, pero se recomienda mostrar la ventana la última
+ * para que todo aparezca de golpe. */
+ gtk_widget_show(boton);
+
+ gtk_widget_show(caja1);
+
+ gtk_widget_show (ventana);
+
+ /* Esperamos en gtk_main a que comience el espectáculo.*/
+ gtk_main ();
+
+ return 0;
+}
+/* final del ejemplo*/
+</verb></tscreen>
+
+Compile el programa usando los mismos argumentos que en el ejemplo
+anterior. Probablemente ya se habrá dado cuenta de que no hay una
+forma sencilla para terminar el programa, se debe usar el gestor de
+ventanas o la línea de comandos para ello. Un buen ejercicio para
+el lector es introducir un tercer botón que termine el
+programa. También puede resultar interesante probar las diferentes
+opciones de gtk_box_pack_start() mientras lee la siguiente
+sección. Intente cambiar el tamaño de la ventana y observe el
+comportamiento.
+
+Como última nota, existe otra definición bastante útil:
+gtk_widow_new() - GTK_WINDOW_DIALOG. Su comportamiento es un poco
+diferente y debe ser usado para ventanas intermedias (cuadros de
+diálogo).
+
+<!-- ***************************************************************** -->
+<sect><em/Widgets/ usados para empaquetar
+<!-- ***************************************************************** -->
+<p>
+Al crear una aplicación normalmente se quiere que haya más de un
+<em/widget/ por ventana. Nuestro primer ejemplo sólo usaba un
+<em/widget/ por lo que usábamos la función gtk_container_add
+para «empaquetar» el <em/widget/ en la ventana. Pero cuando cuando
+se quiere poner más de un <em/widget/ en una ventana, ¿Cómo
+podemos controlar donde aparecerá el <em/widget/?. Aquí es donde
+entra el empaquetamiento.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Empaquetamiento usando cajas
+<p>
+Normalmente para empaquetar se usan cajas, tal y como ya hemos
+visto. Éstas son <em/widgets/ invisibles que pueden contener
+nuestros <em/widgets/ de dos formas diferentes, horizontal o
+verticalmente. Al hacerlo de la primera forma los objetos son
+insertados de izquierda a derecha o al revés (dependiendo de que
+llamada se use). Lo mismo ocurre en los verticales (de arriba a bajo o
+al revés). Se pueden usar tantas cajas como se quieran para
+conseguir cualquier tipo de efecto.
+
+Para crear una caja horizontal llamamos a gtk_hbox_new() y para las
+verticales gtk_vbox_new(). Las funciones usadas para introducir
+objetos dentro son gtk_box_pack_start() y gtk_box_pack_end(). La
+primera llenará de arriba a abajo o de izquierda a derecha. La
+segunda lo hará al revés.
+Usando estas funciones podemos ir metiendo <em/widgets/ con una
+justificación a la izquierda o a la derecha y además podemos
+mezclarlas de cualquier manera para conseguir el efecto
+deseado. Nosotros usaremos gtk_box_pack_start() en la mayoria de
+nuestros ejemplos. Un objeto puede ser otro contenedor o un
+<em/widget/. De hecho, muchos <em/widgets/ son contenedores,
+incluyendo el <em/widget/ botón (button) (aunque normalmente lo
+único que meteremos dentro será una etiqueta de texto).
+
+Mediante el uso de estas funciones le decimos a GTK dónde queremos
+situar nuestros widgets, y GTK podrá, por ejemplo, cambiarles el
+tamaño de forma automática y hacer otras cosas de
+utilidad. También hay unas cuantas opciones que tienen que ver con
+la forma en la que los <em/widgets/ serán empaquetados. Como puede
+imaginarse, este método nos da una gran flexibilidad a la hora de
+colocar y crear <em/widgets/.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Detalles de la cajas.
+<p>
+Debido a esta flexibilidad el empaquetamiento puede ser confuso al
+principio. Hay muchas opciones y no es obvio como encajan unas con
+otras. Pero en la práctica sólo hay cinco estilos diferentes.
+
+<? <CENTER> >
+<?
+<IMG SRC="gtk_tut_packbox1.gif" VSPACE="15" HSPACE="10" WIDTH="528"
+HEIGHT="235" ALT="Imagen de ejemplo sobre el empaquetado con cajas">
+>
+<? </CENTER> >
+
+Cada línea contiene una caja horizontal (hbox) con diferentes
+botones. La llamada a gtk_box_pack es una manera de conseguir
+empaquetar cada uno de los botones dentro de la caja. Eso sí, cada uno
+de ellos se empaqueta de la misma forma que el resto (se llama con los
+mismos argumentos a gtk_box_pack_start()).
+
+Esta es la declaración de la función gtk_box_pack_start:
+
+<tscreen><verb>
+void gtk_box_pack_start( GtkBox *box,
+ GtkWidget *hijo,
+ gint expand,
+ gint fill,
+ gint padding );
+</verb></tscreen>
+
+El primer argumento es la caja dónde se empaqueta, el segundo el
+objeto. Por ahora el objeto será un botón, ya que estamos
+empaquetando botones dentro de las cajas.
+
+El argumento <tt/expand/ de gtk_box_pack_start() y de
+gtk_box_pack_end() controla si los <em/widgets/ son expandidos en la
+caja para rellenar todo el espacio de la misma (TRUE) o si por el
+contrario no se usa el espacio extra dentro de la caja
+(FALSE). Poniendo FALSE en <em/expand/ podremos hacer que nuestros
+<em/widgets/ tengan una justaficación a la derecha o a la
+izquierda. En caso contrario, los <em/widgets/ se expandirán para
+llenar toda la caja, y podemos conseguir el mismo efecto utilizando
+sólo una de las funciones gtk_box_pack_start o pack_end.
+
+El argumento <tt/fill/ de gtk_box controla si el espacio extra se mete
+dentro de los objetos (TRUE) o como relleno extra (FALSE). Sólo
+tiene efecto si el argumento de expansión también es TRUE.
+
+Al crear una nueva ventana la función debe ser parecida a esta:
+
+<tscreen><verb>
+GtkWidget *gtk_hbox_new (gint homogeneous,
+ gint spacing);
+</verb></tscreen>
+
+El argumento <tt/homogeneous/ (tanto para gtk_hbox_new como para
+gtk_vbox_new) controla si cada objeto en la caja tiene el mismo
+tamaño (anchura en una hbox o altura en una vbox). Si se activa, el
+argumento <tt/expand/ de las rutinas gtk_box_pack siempre estará
+activado.
+
+Puede que el lector se esté haciendo la siguiente pregunta:
+¿Cúal es la diferencia entre espaciar (establecido cuando se
+crea la caja) y rellenar (determinado cuando se empaquetan los
+elementos)? El espaciado se añade entre objetos, y el rellenado se
+hace en cada parte de cada objeto. La siguiente figura debe aclarar la
+cuestión.
+
+<? <CENTER> >
+<?
+<IMG ALIGN="center" SRC="gtk_tut_packbox2.gif" WIDTH="509" HEIGHT="213"
+VSPACE="15" HSPACE="10" ALT="Imagen de ejemplo sobre el empaquetado con cajas">
+>
+<? </CENTER> >
+
+Estudiemos el código usado para crear las imágenes
+anteriores. Con los comentarios no debería de haber ningún
+problema para entenderlo.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Programa demostración de empaquetamiento
+<p>
+<tscreen><verb>
+/* principio del ejemplo packbox packbox.c */
+
+#include <stdio.h>
+#include "gtk/gtk.h"
+
+void
+delete_event (GtkWidget *widget, GdkEvent *event, gpointer data)
+{
+ gtk_main_quit ();
+}
+
+/* Hacemos una hbox llena de etiquetas de botón. Los argumentos
+ * para las variables que estamos interesados son pasados a esta
+ * función. No mostramos la caja, pero hacemos todo lo que
+ * queremos. */
+GtkWidget *make_box (gint homogeneous, gint spacing,
+ gint expand, gint fill, gint padding)
+{
+ GtkWidget *box;
+ GtkWidget *boton;
+ char padstr[80];
+
+ /* creamos una nueva caja con los argumentos homogeneous y
+ * spacing */
+ box = gtk_hbox_new (homogeneous, spacing);
+
+ /* crear una serie de botones */
+ boton = gtk_button_new_with_label ("gtk_box_pack");
+ gtk_box_pack_start (GTK_BOX (box), boton, expand, fill, padding);
+ gtk_widget_show (boton);
+
+ boton = gtk_button_new_with_label ("(box,");
+ gtk_box_pack_start (GTK_BOX (box), boton, expand, fill, padding);
+ gtk_widget_show (boton);
+
+ boton = gtk_button_new_with_label ("boton,");
+ gtk_box_pack_start (GTK_BOX (box), boton, expand, fill, padding);
+ gtk_widget_show (boton);
+
+ /* Este botón llevará por etiqueta el valor de expand */
+ if (expand == TRUE)
+ boton = gtk_button_new_with_label ("TRUE,");
+ else
+ boton = gtk_button_new_with_label ("FALSE,");
+
+ gtk_box_pack_start (GTK_BOX (box), boton, expand, fill, padding);
+ gtk_widget_show (boton);
+
+ /* Este es el mismo caso que el de arriba, pero más compacto */
+ boton = gtk_button_new_with_label (fill ? "TRUE," : "FALSE,");
+ gtk_box_pack_start (GTK_BOX (box), boton, expand, fill, padding);
+ gtk_widget_show (boton);
+
+ sprintf (padstr, "%d);", padding);
+
+ boton = gtk_button_new_with_label (padstr);
+ gtk_box_pack_start (GTK_BOX (box), boton, expand, fill, padding);
+ gtk_widget_show (boton);
+
+ return box;
+}
+
+int
+main (int argc, char *argv[])
+{
+ GtkWidget *ventana;
+ GtkWidget *boton;
+ GtkWidget *caja1;
+ GtkWidget *caja2;
+ GtkWidget *separator;
+ GtkWidget *etiqueta;
+ GtkWidget *quitbox;
+ int which;
+
+ /* ¡No olvidar la siguiente llamada! */
+ gtk_init (&amp;argc, &amp;argv);
+
+ if (argc != 2) {
+ fprintf (stderr, "usage: packbox num, where num is 1, 2, or 3.\n");
+ /* hacemos limpieza en GTK y devolvemos el valor de 1 */
+ gtk_exit (1);
+ }
+
+ which = atoi (argv[1]);
+
+ /* Creamos la ventana */
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ /* Siempre hay que conectar la señal de destrucción con la
+ * ventana principal. Esto es muy importante para que el
+ * comportamiento de la ventana sea intuitivo. */
+ gtk_signal_connect (GTK_OBJECT (ventana), "delete_event",
+ GTK_SIGNAL_FUNC (delete_event), NULL);
+ gtk_container_border_width (GTK_CONTAINER (ventana), 10);
+
+ /* Creamos una caja vertical donde empaquetaremos las cajas
+ * horizontales. Así podemos apilar las cajas horizontales
+ * llenas con botones una encima de las otras. */
+ caja1 = gtk_vbox_new (FALSE, 0);
+
+ /* Aclaramos cúal es el ejemplo a mostrar. Se corresponde con
+ * las imágenes anteriores. */
+ switch (which) {
+ case 1:
+ /* creamos una nueva etiqueta. */
+ etiqueta = gtk_label_new ("gtk_hbox_new (FALSE, 0);");
+
+ /* Alineamos la etiqueta a la izquierda. Está función
+ * será discutida en detalle en la sección de los
+ * atributos de los widgets. */
+ gtk_misc_set_alignment (GTK_MISC (etiqueta), 0, 0);
+
+ /* Empaquetamos la etiqueta en la caja vertical (vbox
+ * caja1). Siempre hay que recordar que los widgets añadidos a
+ * una vbox serán empaquetados uno encimo de otro. */
+ gtk_box_pack_start (GTK_BOX (caja1), etiqueta, FALSE, FALSE, 0);
+
+ /* mostramos la etiqueta. */
+ gtk_widget_show (etiqueta);
+
+ /* llamada a la función que hace las cajas. Los argumentos
+ * son homogenous = FALSE, expand = FALSE, fill = FALSE,
+ * padding = 0 */
+ caja2 = make_box (FALSE, 0, FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (caja1), caja2, FALSE, FALSE, 0);
+ gtk_widget_show (caja2);
+
+ /* Llamad a la función para hacer cajas -
+ * homogeneous = FALSE, spacing = 0, expand = FALSE,
+ * fill = FALSE, padding = 0 */
+ caja2 = make_box (FALSE, 0, TRUE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (caja1), caja2, FALSE, FALSE, 0);
+ gtk_widget_show (caja2);
+
+ /* Los argumentos son: homogeneous, spacing, expand, fill,
+ * padding */
+ caja2 = make_box (FALSE, 0, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (caja1), caja2, FALSE, FALSE, 0);
+ gtk_widget_show (caja2);
+
+
+ /* creamos un separador. Más tarde aprenderemos más cosas
+ * sobre ellos, pero son bastante sencillos. */
+ separator = gtk_hseparator_new ();
+
+ /* empaquetamos el separador el la vbox. Los widgets serán
+ * apilados verticalmente. */
+ gtk_box_pack_start (GTK_BOX (caja1), separator, FALSE, TRUE, 5);
+ gtk_widget_show (separator);
+
+ /* creamos una nueva etiqueta y la mostramos */
+ etiqueta = gtk_label_new ("gtk_hbox_new (TRUE, 0);");
+ gtk_misc_set_alignment (GTK_MISC (etiqueta), 0, 0);
+ gtk_box_pack_start (GTK_BOX (caja1), etiqueta, FALSE, FALSE, 0);
+ gtk_widget_show (etiqueta);
+
+ /* Los argumentos son: homogeneous, spacing, expand, fill,
+ * padding */
+ caja2 = make_box (TRUE, 0, TRUE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (caja1), caja2, FALSE, FALSE, 0);
+ gtk_widget_show (caja2);
+
+ /* Los argumentos son: homogeneous, spacing, expand, fill,
+ * padding */
+ caja2 = make_box (TRUE, 0, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (caja1), caja2, FALSE, FALSE, 0);
+ gtk_widget_show (caja2);
+
+ /* un nuevo separador */
+ separator = gtk_hseparator_new ();
+ /* Los tres últimos argumentos son: expand, fill, padding. */
+ gtk_box_pack_start (GTK_BOX (caja1), separator, FALSE, TRUE, 5);
+ gtk_widget_show (separator);
+
+ break;
+
+ case 2:
+
+ /* Nueva etiqueta */
+ etiqueta = gtk_label_new ("gtk_hbox_new (FALSE, 10);");
+ gtk_misc_set_alignment (GTK_MISC (etiqueta), 0, 0);
+ gtk_box_pack_start (GTK_BOX (caja1), etiqueta, FALSE, FALSE, 0);
+ gtk_widget_show (etiqueta);
+
+ /* Los argumentos son: homogeneous, spacing, expand, fill,
+ * padding */
+ caja2 = make_box (FALSE, 10, TRUE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (caja1), caja2, FALSE, FALSE, 0);
+ gtk_widget_show (caja2);
+
+ /* Los argumentos son: homogeneous, spacing, expand, fill,
+ * padding */
+ caja2 = make_box (FALSE, 10, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (caja1), caja2, FALSE, FALSE, 0);
+ gtk_widget_show (caja2);
+
+ separator = gtk_hseparator_new ();
+ /* Los argumentos son: expand, fill, padding. */
+ gtk_box_pack_start (GTK_BOX (caja1), separator, FALSE, TRUE, 5);
+ gtk_widget_show (separator);
+
+ etiqueta = gtk_label_new ("gtk_hbox_new (FALSE, 0);");
+ gtk_misc_set_alignment (GTK_MISC (etiqueta), 0, 0);
+ gtk_box_pack_start (GTK_BOX (caja1), etiqueta, FALSE, FALSE, 0);
+ gtk_widget_show (etiqueta);
+
+ /* Los argumentos son: homogeneous, spacing, expand, fill,
+ * padding */
+ caja2 = make_box (FALSE, 0, TRUE, FALSE, 10);
+ gtk_box_pack_start (GTK_BOX (caja1), caja2, FALSE, FALSE, 0);
+ gtk_widget_show (caja2);
+
+ /* Los argumentos son: homogeneous, spacing, expand, fill,
+ * padding */
+ caja2 = make_box (FALSE, 0, TRUE, TRUE, 10);
+ gtk_box_pack_start (GTK_BOX (caja1), caja2, FALSE, FALSE, 0);
+ gtk_widget_show (caja2);
+
+ separator = gtk_hseparator_new ();
+ /* Los argumentos son: expand, fill, padding. */
+ gtk_box_pack_start (GTK_BOX (caja1), separator, FALSE, TRUE, 5);
+ gtk_widget_show (separator);
+ break;
+
+ case 3:
+
+ /* Con esto demostramos como hay que usar gtk_box_pack_end ()
+ * para conseguir que los
+ widgets esten alineados a la izquierda. */
+ caja2 = make_box (FALSE, 0, FALSE, FALSE, 0);
+
+ /* la última etiqueta*/
+ etiqueta = gtk_label_new ("end");
+
+ /* la empaquetamos usando gtk_box_pack_end(), por lo que se
+ * sitúa en el lado derecho de la hbox.*/
+ gtk_box_pack_end (GTK_BOX (caja2), etiqueta, FALSE, FALSE, 0);
+
+ /* mostrar la etiqueta */
+ gtk_widget_show (etiqueta);
+
+
+ /* empaquetamos caja2 en caja1 */
+ gtk_box_pack_start (GTK_BOX (caja1), caja2, FALSE, FALSE, 0);
+ gtk_widget_show (caja2);
+
+
+ /* el separador para la parte de abajo. */
+ separator = gtk_hseparator_new ();
+
+ /* Así se determina el tamaño del separador a 400 pixels
+ * de largo por 5 de alto. La hbox también tendrá 400
+ * pixels de largo y la etiqueta "end" estará separada de
+ * las demás etiquetas en la hbox. Si no establecemos estos
+ * parámetros todos los widgets en la hbox serán
+ * empaquetados tan juntos como se pueda.*/
+ gtk_widget_set_usize (separator, 400, 5);
+
+ /* Empaquetamos el separador creado al principio de main() en
+ * la vbox (caja1). */
+ gtk_box_pack_start (GTK_BOX (caja1), separator, FALSE, TRUE, 5);
+ gtk_widget_show (separator);
+ }
+
+ /* Creamos otra hbox... recordar que podemos crear tantas como
+ * queramos. */
+ quitbox = gtk_hbox_new (FALSE, 0);
+
+ /* El botón de salida. */
+ boton = gtk_button_new_with_label ("Quit");
+
+ /* Establecemos la señal de destrucción de la ventana.
+ * Recuerde que emitirá la señal de "destroy" que a su vez
+ * será procesada por el controlador de señales, tal y como
+ * ya hemos visto. */
+
+ gtk_signal_connect_object (GTK_OBJECT (boton), "clicked",
+ GTK_SIGNAL_FUNC (gtk_main_quit),
+ GTK_OBJECT (ventana));
+ /* Empaquetamos el botón en la caja de salida (quitbox).
+ * los tres últimos argumentos de gtk_box_pack_start
+ * son:expand, fill, padding. */
+ gtk_box_pack_start (GTK_BOX (caja1), quitbox, FALSE, FALSE, 0);
+
+ /* empaquetamos la vbox (caja1) que ya contiene todos los widgets
+ * en la ventana principal. */
+ gtk_container_add (GTK_CONTAINER (ventana), caja1);
+
+ /* mostramos todo aquello que faltaba por mostrar */
+ gtk_widget_show (boton);
+ gtk_widget_show (quitbox);
+
+ gtk_widget_show (caja1);
+
+ /* Si mostramos la ventana lo último todo aparece de golpe. */
+ gtk_widget_show (ventana);
+
+ /* por supuesto tenemos una función main. */
+ gtk_main ();
+
+ /* El programa llega aquí cuando se llama a gtk_main_quit(),
+ * pero no cuando se llama a gtk_exit(). */
+ return 0;
+}
+/* final del ejemplo*/
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Empaquetamiento usando tablas
+<p>
+Existe otra forma de empaquetar: usando tablas. Estas pueden llegar a
+ser extremadamente útiles.
+
+Usando tablas creamos una cuadrícula donde podemos poner los
+widgets. Estos pueden ocupar tanto espacio como queramos.
+
+La primera función que conviene estudiar es gtk_table_new:
+
+<tscreen><verb>
+GtkWidget *gtk_table_new( gint rows,
+ gint columns,
+ gint homogeneous );
+</verb></tscreen>
+
+Como es lógico el primer argumento es el número de filas y el
+segundo el de columnas.
+
+El tercero establece el tamaño de las celdas de la tabla. Si es TRUE
+se fuerza a que el tamaño de las celdas sea igual al de la celda
+mayor. Con FALSE se establece el ancho de toda una columna igual al de
+la celda más ancha de esa columna, y la altura de una fila será
+la de la celda más alta de esa fila.
+
+El número de filas y columnas varía entre 0 y n, donde n es el
+número especificado en la llamada a gtk_table_new. Así si se
+especifica columnas = 2 y filas = 2 la apariencia será parecida a:
+
+<tscreen><verb>
+ 0 1 2
+0+----------+----------+
+ | | |
+1+----------+----------+
+ | | |
+2+----------+----------+
+</verb></tscreen>
+
+Conviene destacar que el origen de coordenadas se sitúa en la esquina superior izquierda. Para
+situar un widget en una ventana se usa la siguiente función:
+
+<tscreen><verb>
+void gtk_table_attach( GtkTable *table,
+ GtkWidget *hijo,
+ gint left_attach,
+ gint right_attach,
+ gint top_attach,
+ gint bottom_attach,
+ gint xoptions,
+ gint yoptions,
+ gint xpadding,
+ gint ypadding );
+</verb></tscreen>
+
+El primer argumento (<tt/table/) es el nombre de la tabla y el segundo
+(<tt/hijo/) el <em/widget/ que quiere poner en la tabla.
+
+Los argumentos <tt/left_attach/, <tt/right_attach/ especifican donde
+se pone el widget y cuantas cajas se usan. Por ejemplo, supongamos que
+queremos poner un botón que sólo ocupe la esquina inferior
+izquierda en nuestra tabla 2x2. Los valores serán left_attach = 1,
+right_attach = 2, top_attach = 2, top_attach = 1, bottom_attach = 2.
+
+Supongamos que queremos ocupar toda la fila de nuestra tabla 2x2,
+usaríamos left_attach = 0, right_attach = 2, top_attach = 0,
+bottom_attach = 1.
+
+Las opciones <tt/xoptions/ e <tt/yoptions/ son usadas para especificar
+como queremos el empaquetamiento y podemos utilizar multiples
+opciones simultaneamente con OR.
+
+Las opciones son:
+
+<itemize>
+<item>GTK_FILL - Si el relleno es más grande que el widget, y se
+especifica GTK_FILL, el <em/widget/ se expandirá ocupando todo el
+espacio disponible.
+
+<item>GTK_SHRINK - En el caso de que hayamos dejado espacio sin usar
+cuando el usuario reajuste el tamaño de la ventana los <em/widgets/
+normalmente serán empujados al fondo de la ventana y
+desaparecerán. Si especifica GTK_SHRINK los widgets se reducirán
+con la tabla.
+
+<item>GTK_EXPAND - Mediante esta opción la tabla se expande usando
+todo el espacio libre de la ventana.
+</itemize>
+
+El relleno es igual que con las cajas. Simplemente se crea una zona
+vacía alrededor del widget (el tamaño se especifica en pixels).
+
+gtk_table_attach() tiene MUCHAS opciones. Asi que hay un atajo:
+
+<tscreen><verb>
+void gtk_table_attach_defaults( GtkTable *table,
+ GtkWidget *widget,
+ gint left_attach,
+ gint right_attach,
+ gint top_attach,
+ gint bottom_attach );
+</verb></tscreen>
+
+Las opciones X e Y se ponen por defecto a GTK_FILL | GTK_EXPAND, y el
+relleno X e Y se pone a 0. El resto de los argumentos son identicos a
+la función anterior.
+
+Existen otras funciones como gtk_table_set_row_spacing() y
+gtk_table_set_col_spacing(), que sirven para especificar el espaciado
+entre las columnas/filas en la columna/fila que queramos.
+
+<tscreen><verb>
+void gtk_table_set_row_spacing( GtkTable *table,
+ gint row,
+ gint spacing );
+</verb></tscreen>
+
+y
+
+<tscreen><verb>
+void gtk_table_set_col_spacing ( GtkTable *table,
+ gint column,
+ gint spacing );
+</verb></tscreen>
+
+Conviene destacar que el espaciado se sitúa a la derecha de la
+columna y debajo de la fila.
+
+Tambien se puede forzar que el espaciado sea el mismo para las filas
+y/o las columnas:
+
+<tscreen><verb>
+void gtk_table_set_row_spacings( GtkTable *table,
+ gint spacing );
+</verb></tscreen>
+
+y
+
+<tscreen><verb>
+void gtk_table_set_col_spacings( GtkTable *table,
+ gint spacing );
+</verb></tscreen>
+
+Usando estas funciones las últimas fila y columna no estarán
+espaciadas.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Ejemplo de empaquetamiento mediante tablas.
+<p>
+Haremos una ventana con tres botones en una tabla 2x2. Los dos
+primeros botones ocuparán la fila de arriba, mientras que el
+tercero (de salida) ocupará toda la fila de abajo. El resultado es
+el siguiente:
+
+<? <CENTER> >
+<?
+<IMG SRC="gtk_tut_table.gif" VSPACE="15" HSPACE="10"
+ALT="Imagen de ejemplo sobre el empaquetado mediante tablas" WIDTH="180" HEIGHT="120">
+>
+<? </CENTER> >
+
+Este es el código:
+<tscreen><verb>
+/* principio del ejemplo table table.c */
+
+#include <gtk/gtk.h>
+
+/* La respuesta, que además se imprime en stdout. */
+void callback (GtkWidget *widget, gpointer data)
+{
+ g_print ("Hello again - %s was pressed\n", (char *) data);
+}
+
+/* Con esta otra respuesta terminamos el programa. */
+void delete_event (GtkWidget *widget, GdkEvent *event, gpointer data)
+{
+ gtk_main_quit ();
+}
+
+int main (int argc, char *argv[])
+{
+ GtkWidget *ventana;
+ GtkWidget *boton;
+ GtkWidget *table;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ gtk_window_set_title (GTK_WINDOW (ventana), "Table");
+
+ gtk_signal_connect (GTK_OBJECT (ventana), "delete_event",
+ GTK_SIGNAL_FUNC (delete_event), NULL);
+
+ gtk_container_border_width (GTK_CONTAINER (ventana), 20);
+
+ table = gtk_table_new (2, 2, TRUE);
+
+ gtk_container_add (GTK_CONTAINER (ventana), table);
+
+ boton = gtk_button_new_with_label ("botón 1");
+
+ gtk_signal_connect (GTK_OBJECT (boton), "clicked",
+ GTK_SIGNAL_FUNC (callback), (gpointer) "botón 1");
+
+ gtk_table_attach_defaults (GTK_TABLE(table), boton, 0, 1, 0, 1);
+
+ gtk_widget_show (boton);
+
+ boton = gtk_button_new_with_label ("botón 2");
+
+ gtk_signal_connect (GTK_OBJECT (boton), "clicked",
+ GTK_SIGNAL_FUNC (callback), (gpointer) "botón 2");
+ gtk_table_attach_defaults (GTK_TABLE(table), boton, 1, 2, 0, 1);
+
+ gtk_widget_show (boton);
+
+ boton = gtk_button_new_with_label ("Quit");
+
+ gtk_signal_connect (GTK_OBJECT (boton), "clicked",
+ GTK_SIGNAL_FUNC (delete_event), NULL);
+ gtk_table_attach_defaults (GTK_TABLE(table), boton, 0, 2, 1, 2);
+
+ gtk_widget_show (boton);
+
+ gtk_widget_show (table);
+ gtk_widget_show (ventana);
+
+ gtk_main ();
+
+ return 0;
+}
+/* final del ejemplo */
+</verb></tscreen>
+
+<!-- ***************************************************************** -->
+<sect>Estudio general de los <em/widgets/
+<!-- ***************************************************************** -->
+<p>
+Los pasos generales a la hora de crear un <em/widget/ son:
+
+<enum>
+
+<item> Usar gtk_*_new - Una de las diferentes formas de crear un
+<em/widget/. (Todas serán explicadas en esta sección).
+
+<item> Connectar todas las señales y los eventos a los
+controladores apropiados.
+
+<item> Establecer los atributos del <em/widget/.
+
+<item> Empaquetar el <em/widget/ en un contenedor usando las llamadas
+apropiadas, como gtk_container_add() o gtk_box_pack_start().
+
+<item> Mostrar el <em/widget/ usando gtk_widget_show().
+
+</enum>
+
+Mediante esta última llamada GTK `sabe' que hemos acabado de
+establecer los atributos del <em/widget/, y que por lo tanto puede
+mostrarse. Se puede usar gtk_widget_hide para hacer que desaparezca.
+El orden en el que se muestran los <em/widgets/ no es importante, pero
+se recomienda mostrar al final la ventana para que todo aparezca de
+golpe. El hijo de un <em/widget/ no se muestra hasta que lo hace la
+propia ventana (que en este caso es un <em/widget/ padre) mediante
+gtk_widget_show().
+
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Conversión de tipos
+<p>
+GTK usa un sistema de conversión de tipos mediante macros que
+comprueban si se puede realizar la conversión y en caso
+afirmativo la hacen. Las más comunes son:
+
+<itemize>
+<item> GTK_WIDGET(widget)
+<item> GTK_OBJECT(object)
+<item> GTK_SIGNAL_FUNC(function)
+<item> GTK_CONTAINER(container)
+<item> GTK_WINDOW(ventana)
+<item> GTK_BOX(box)
+</itemize>
+
+Todas son usadas para cambiar de tipo los argumentos de una función.
+Aparecerán mucho en los ejemplos, para usarlas sólo hay que mirar la
+declaración de la función.
+
+Tal y como se puede ver en el árbol de clases (situado un poco
+más adelante) todos los <em/widgets/ derivan de la clase base
+GtkObject. Esto significa que siempre se puede usar un <em/widget/
+como argumento de una función (que acepte un objeto, claro)
+realizando la conversión de tipo GTK_OBJECT().
+
+Por ejemplo:
+
+<tscreen><verb>
+gtk_signal_connect( GTK_OBJECT(boton), "clicked",
+ GTK_SIGNAL_FUNC(callback_function), callback_data);
+</verb></tscreen>
+
+Hemos hecho que el botón pase a ser un objeto y que se cambie el
+puntero a la función a una función respuesta.
+
+Muchos <em/widgets/ son contenedores, por lo que unos pueden derivar
+de otros (la mayoría lo hace de GtkContainer). Cualquiera puede ser
+usado junto con la macro GTK_CONTAINER como argumento a funciones en
+forma de puntero.
+
+Desgraciadamente estas macros no son descritas en detalle en el
+tutorial, por lo que se recomienda echar un vistazo a los archivos de
+cabecera de GTK. En la práctica es posible aprender a manejar un
+<em/widget/ leyendo las declaraciones de las funciones.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Árbol formado por los <em/widgets/
+<p>
+A continuación se detallan todas las ramas del árbol que forman
+los <em/widgets/.
+
+<tscreen><verb>
+ GtkObject
+ +GtkWidget
+ | +GtkMisc
+ | | +GtkLabel
+ | | | +GtkAccelLabel
+ | | | `GtkTipsQuery
+ | | +GtkArrow
+ | | +GtkImage
+ | | `GtkPixmap
+ | +GtkContainer
+ | | +GtkBin
+ | | | +GtkAlignment
+ | | | +GtkFrame
+ | | | | `GtkAspectFrame
+ | | | +GtkButton
+ | | | | +GtkToggleButton
+ | | | | | `GtkCheckButton
+ | | | | | `GtkRadioButton
+ | | | | `GtkOptionMenu
+ | | | +GtkItem
+ | | | | +GtkMenuItem
+ | | | | | +GtkCheckMenuItem
+ | | | | | | `GtkRadioMenuItem
+ | | | | | `GtkTearoffMenuItem
+ | | | | +GtkListItem
+ | | | | `GtkTreeItem
+ | | | +GtkWindow
+ | | | | +GtkColorSelectionDialog
+ | | | | +GtkDialog
+ | | | | | `GtkInputDialog
+ | | | | +GtkDrawWindow
+ | | | | +GtkFileSelection
+ | | | | +GtkFontSelectionDialog
+ | | | | `GtkPlug
+ | | | +GtkEventBox
+ | | | +GtkHandleBox
+ | | | +GtkScrolledWindow
+ | | | `GtkViewport
+ | | +GtkBox
+ | | | +GtkButtonBox
+ | | | | +GtkHButtonBox
+ | | | | `GtkVButtonBox
+ | | | +GtkVBox
+ | | | | +GtkColorSelection
+ | | | | `GtkGammaCurve
+ | | | `GtkHBox
+ | | | +GtkCombo
+ | | | `GtkStatusbar
+ | | +GtkCList
+ | | | `GtkCTree
+ | | +GtkFixed
+ | | +GtkNotebook
+ | | | `GtkFontSelection
+ | | +GtkPaned
+ | | | +GtkHPaned
+ | | | `GtkVPaned
+ | | +GtkLayout
+ | | +GtkList
+ | | +GtkMenuShell
+ | | | +GtkMenuBar
+ | | | `GtkMenu
+ | | +GtkPacker
+ | | +GtkSocket
+ | | +GtkTable
+ | | +GtkToolbar
+ | | `GtkTree
+ | +GtkCalendar
+ | +GtkDrawingArea
+ | | `GtkCurve
+ | +GtkEditable
+ | | +GtkEntry
+ | | | `GtkSpinButton
+ | | `GtkText
+ | +GtkRuler
+ | | +GtkHRuler
+ | | `GtkVRuler
+ | +GtkRange
+ | | +GtkScale
+ | | | +GtkHScale
+ | | | `GtkVScale
+ | | `GtkScrollbar
+ | | +GtkHScrollbar
+ | | `GtkVScrollbar
+ | +GtkSeparator
+ | | +GtkHSeparator
+ | | `GtkVSeparator
+ | +GtkPreview
+ | `GtkProgress
+ | `GtkProgressBar
+ +GtkData
+ | +GtkAdjustment
+ | `GtkTooltips
+ `GtkItemFactory
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1><em/Widgets/ sin ventanas
+<p>
+Los siguientes <em/widgets/ no tienen ventanas asociadas. Si se
+quieren capturar eventos se tendrá que utilizar GtkEventBox. En la
+sección <ref id="sec_The_EventBox_Widget" name="El widget
+EventBox"> se pueden encontrar más detalles sobre su uso.
+
+<tscreen><verb>
+GtkAlignment
+GtkArrow
+GtkBin
+GtkBox
+GtkImage
+GtkItem
+GtkLabel
+GtkPixmap
+GtkScrolledWindow
+GtkSeparator
+GtkTable
+GtkAspectFrame
+GtkFrame
+GtkVBox
+GtkHBox
+GtkVSeparator
+GtkHSeparator
+</verb></tscreen>
+
+Vamos a continuar la explicación describiendo cada uno de los
+<em/widgets/ mediante ejemplos. También se puede consultar el
+programa testgtk.c (Se encuentra en gtk/testgtk.c).
+
+<!-- ***************************************************************** -->
+<sect>El <em/widget/ Botón
+<!-- ***************************************************************** -->
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Botones normales <label id="sec_Radio_Buttons">
+<p>
+Ya hemos visto prácticamente todo lo que hay que saber a cerca de
+este <em/widget/. Existen dos formas diferentes de crear un
+botón. Se puede usar gtk_button_new_with_label() para conseguir un
+botón con etiqueta o simplemente gtk_button_new(). Si se quiere se
+puede añadir una etiqueta a este último empaquetándola,
+primero se crea una nueva caja y luego se empaquetan los objetos que
+se quieran mediante gtk_box_pack_start. Una vez finalizado esto se
+relaciona la caja con el botón mediante gtk_container_add.
+
+Estudiemos un ejemplo de gtk_button_new para crear un botón con una
+imagen y una etiqueta. El código está dividido en dos para que
+pueda ser reusado.
+
+<tscreen><verb>
+/* principio del ejemplo buttons buttons.c */
+
+#include <gtk/gtk.h>
+
+/* Creamos la caja con una imagen y una etiqueta empaquetadas. Se
+ * devuelve la caja. */
+GtkWidget *xpm_label_box (GtkWidget *parent, gchar *xpm_filename, gchar *label_text)
+{
+ GtkWidget *caja1;
+ GtkWidget *etiqueta;
+ GtkWidget *pixmapwid;
+ GdkPixmap *pixmap;
+ GdkBitmap *mask;
+ GtkStyle *style;
+
+ /* create box for xpm and etiqueta */
+ caja1 = gtk_hbox_new (FALSE, 0);
+ gtk_container_border_width (GTK_CONTAINER (caja1), 2);
+
+ /* obtenemos el estilo del botón (probablemente para el color
+ * de fondo, pero no estoy seguro) */
+ style = gtk_widget_get_style(parent);
+
+ /* cargamos el pixmap. Hay una sección que describe el proceso
+ * en detalle */
+ pixmap = gdk_pixmap_create_from_xpm (parent->window, &amp;mask,
+ &amp;style->bg[GTK_STATE_NORMAL],
+ xpm_filename);
+ pixmapwid = gtk_pixmap_new (pixmap, mask);
+
+ etiqueta = gtk_label_new (label_text);
+
+ gtk_box_pack_start (GTK_BOX (caja1),
+ pixmapwid, FALSE, FALSE, 3);
+
+ gtk_box_pack_start (GTK_BOX (caja1), etiqueta, FALSE, FALSE, 3);
+
+ gtk_widget_show(pixmapwid);
+ gtk_widget_show(etiqueta);
+
+ return (caja1);
+}
+
+/* respuesta */
+void callback (GtkWidget *widget, gpointer data)
+{
+ g_print ("Hola de nuevo. Se ha pulsado %s\n", (char *) data);
+}
+
+
+int main (int argc, char *argv[])
+{
+
+ GtkWidget *ventana;
+ GtkWidget *boton;
+ GtkWidget *caja1;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ gtk_window_set_title (GTK_WINDOW (ventana), "Botones con dibujos");
+
+ /* It's a good idea to do this for all windows. */
+ gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
+ GTK_SIGNAL_FUNC (gtk_exit), NULL);
+
+ gtk_signal_connect (GTK_OBJECT (ventana), "delete_event",
+ GTK_SIGNAL_FUNC (gtk_exit), NULL);
+
+ gtk_container_border_width (GTK_CONTAINER (ventana), 10);
+ gtk_widget_realize(ventana);
+
+ boton = gtk_button_new ();
+
+ gtk_signal_connect (GTK_OBJECT (boton), "clicked",
+ GTK_SIGNAL_FUNC (callback),
+ (gpointer) "botón divertido");
+
+ caja1 = xpm_label_box(ventana, "info.xpm", "botón divertido");
+
+ gtk_widget_show(caja1);
+
+ gtk_container_add (GTK_CONTAINER (boton), caja1);
+
+ gtk_widget_show(boton);
+
+ gtk_container_add (GTK_CONTAINER (ventana), boton);
+
+ gtk_widget_show (ventana);
+
+ gtk_main ();
+
+ return 0;
+}
+/* final del ejemplo */
+</verb></tscreen>
+
+La función xpm_label_box puede ser usada para empaquetar xpm y
+etiquetas en cualquier widget que pueda ser un contenedor.
+
+El botón puede responder a las siguientes señales:
+
+<itemize>
+<item> pressed
+<item> released
+<item> clicked
+<item> enter
+<item> leave
+</itemize>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Botones de selección
+<p>
+Estos botones son muy similares a los normales. La única diferencia
+es que sólo pueden estar en dos posiciones diferentes alternadas
+mediante pulsaciones del ratón.
+
+Los botones de selección son la base de otros tipos: los de
+comprobación y los circulares. Por lo tanto muchas de sus llamadas
+seran heredadas por estos.
+
+Creamos un nuevo botón de selección:
+
+<tscreen><verb>
+GtkWidget *gtk_toggle_button_new( void );
+
+GtkWidget *gtk_toggle_button_new_with_label( gchar *etiqueta );
+</verb></tscreen>
+
+Como se ha podido imaginar estas funciones son iguales a las de un
+botón normal. La primera crea un botón, mientras que la segunda
+crea un botón con una etiqueta.
+
+Para saber cual es el estado de un botón de selección,
+comprobación o circular se usa una de las macros del ejemplo
+siguiente. En éstas se comprueba el estado del botón mediante
+una respuesta. La señal que queremos recibir es
+«toggled». Generalmente para comprobar el estado de una señal se
+establece un controlador de señales y luego se usa la siguiente
+macro. La función de respuesta debe ser de la forma:
+
+<tscreen><verb>
+void toggle_button_callback (GtkWidget *widget, gpointer data)
+{
+ if (GTK_TOGGLE_BUTTON (widget)->active)
+ {
+ /* Si el control llega aquí el botón está pulsado */
+
+ } else {
+
+ /* El botón no está pulsado (sobresale) */
+ }
+}
+</verb></tscreen>
+
+<tscreen><verb>
+void gtk_toggle_button_set_state( GtkToggleButton *toggle_button,
+ gint state );
+</verb></tscreen>
+
+La llamada de arriba puede ser usada para establecer el estado de un
+botón de selección (o de cualquiera de sus hijos: el circular o
+el de comprobación). El primer argumento es el botón, el segundo
+TRUE cuando queremos que el botón no esté pulsado o FALSE para
+cuando lo esté. Por defecto se establece FALSE.
+
+Hay que destacar que cuando se usa gtk_toggle_button_set_state() y se
+cambia el estado del botón este emite la señal «clicked».
+
+<tscreen><verb>
+void gtk_toggle_button_toggled (GtkToggleButton *toggle_button);
+</verb></tscreen>
+
+Cambia el estado del botón emitiendo la señal «toggled».
+<!-- ----------------------------------------------------------------- -->
+<sect1> Botones de comprobación
+<p>
+Los botones de comprobación son un poco diferentes a los anteriores, aunque
+sus propiedades y funciones son bastante similares. En lugar de ser botones
+con texto en su interior son pequeños cuadrados con texto a su derecha.
+Normalmente son usados para (des)seleccionar opciones.
+
+Las dos funciones que los crean son muy similares a las de los botones
+normales.
+
+<tscreen><verb>
+GtkWidget *gtk_check_button_new( void );
+
+GtkWidget *gtk_check_button_new_with_label ( gchar *etiqueta );
+</verb></tscreen>
+
+La función new_with_label crea un botón de comprobación con
+una etiqueta dentro.
+
+El proceso para comprobar el estado de un botón de este tipo es
+igual al de los de comprobación.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Botones circulares
+<p>
+Estos botones son similares a los de selección con la salvedad de
+que están agrupados, de modo que sólo uno puede estar
+seleccionado. Por tanto son usados para permitir al usuario
+seleccionar algo de una lista de opciones mutuamente excluyentes.
+
+Las llamadas para crear un botón circular son:
+<tscreen><verb>
+GtkWidget *gtk_radio_button_new( GSList *group );
+
+GtkWidget *gtk_radio_button_new_with_label( GSList *group,
+ gchar *etiqueta );
+</verb></tscreen>
+
+El nuevo argumento sirve para especificar el grupo al que
+pertenecen. La primera llamada debe pasar NULL como primer
+argumento. A continuación de ésta se puede crear el grupo
+usando:
+
+<tscreen><verb>
+GSList *gtk_radio_button_group( GtkRadioButton *radio_button );
+</verb></tscreen>
+
+Para añadir un nuevo botón a un grupo hay que usar
+gtk_radio_button_group con el anterior botón como argumento. El
+resultado se le pasa a gtk_radio_button_new o a
+gtk_radio_button_new_with_label. Así se consigue enlazar una cadena
+de botones. (El ejemplo siguiente sirve para aclarar el proceso)
+
+También se puede establecer cúal es el botón pulsado por
+defecto:
+
+<tscreen><verb>
+void gtk_toggle_button_set_state( GtkToggleButton *toggle_button,
+ gint state );
+</verb></tscreen>
+El siguiente ejemplo crea un grupo de tres botones:
+
+<tscreen><verb>
+/* Principio del ejemplo radiobuttons.c */
+
+#include <gtk/gtk.h>
+#include <glib.h>
+
+void close_application( GtkWidget *widget, GdkEvent *event, gpointer data ) {
+ gtk_main_quit();
+}
+
+main(int argc,char *argv[])
+{
+ static GtkWidget *ventana = NULL;
+ GtkWidget *caja1;
+ GtkWidget *caja2;
+ GtkWidget *boton;
+ GtkWidget *separator;
+ GSList *group;
+
+ gtk_init(&amp;argc,&amp;argv);
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ gtk_signal_connect (GTK_OBJECT (ventana), "delete_event",
+ GTK_SIGNAL_FUNC(close_application),
+ NULL);
+
+ gtk_window_set_title (GTK_WINDOW (ventana), "radio buttons");
+ gtk_container_border_width (GTK_CONTAINER (ventana), 0);
+
+ caja1 = gtk_vbox_new (FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (ventana), caja1);
+ gtk_widget_show (caja1);
+
+ caja2 = gtk_vbox_new (FALSE, 10);
+ gtk_container_border_width (GTK_CONTAINER (caja2), 10);
+ gtk_box_pack_start (GTK_BOX (caja1), caja2, TRUE, TRUE, 0);
+ gtk_widget_show (caja2);
+
+ boton = gtk_radio_button_new_with_label (NULL, "botón1");
+ gtk_box_pack_start (GTK_BOX (caja2), boton, TRUE, TRUE, 0);
+ gtk_widget_show (boton);
+
+ group = gtk_radio_button_group (GTK_RADIO_BUTTON (boton));
+ boton = gtk_radio_button_new_with_label(group, "botón2");
+ gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (boton), TRUE);
+ gtk_box_pack_start (GTK_BOX (caja2), boton, TRUE, TRUE, 0);
+ gtk_widget_show (boton);
+
+ group = gtk_radio_button_group (GTK_RADIO_BUTTON (boton));
+ boton = gtk_radio_button_new_with_label(group, "botón3");
+ gtk_box_pack_start (GTK_BOX (caja2), boton, TRUE, TRUE, 0);
+ gtk_widget_show (boton);
+
+ separator = gtk_hseparator_new ();
+ gtk_box_pack_start (GTK_BOX (caja1), separator, FALSE, TRUE, 0);
+ gtk_widget_show (separator);
+
+ caja2 = gtk_vbox_new (FALSE, 10);
+ gtk_container_border_width (GTK_CONTAINER (caja2), 10);
+ gtk_box_pack_start (GTK_BOX (caja1), caja2, FALSE, TRUE, 0);
+ gtk_widget_show (caja2);
+
+ boton = gtk_button_new_with_label ("close");
+ gtk_signal_connect_object (GTK_OBJECT (boton), "clicked",
+ GTK_SIGNAL_FUNC(close_application),
+ GTK_OBJECT (ventana));
+ gtk_box_pack_start (GTK_BOX (caja2), boton, TRUE, TRUE, 0);
+ GTK_WIDGET_SET_FLAGS (boton, GTK_CAN_DEFAULT);
+ gtk_widget_grab_default (boton);
+ gtk_widget_show (boton);
+ gtk_widget_show (ventana);
+
+ gtk_main();
+ return(0);
+}
+/* final del ejemplo */
+</verb></tscreen>
+
+<!-- TODO: checout out gtk_radio_button_new_from_widget function - TRG -->
+
+<!-- ***************************************************************** -->
+<sect>Ajustes (<em/Adjustment/) <label id="sec_Adjustment">
+<!-- ***************************************************************** -->
+<p>
+Existen diferentes <em/widgets/ en GTK+ que pueden ser ajustados
+visualmente por el usuario mediante el ratón o el teclado. Un
+ejemplo son los <em/widgets/ de selección descritos en la
+sección <ref id="sec_Range_Widgets" name="Widgets de selección
+de rango">. También hay otros widgets que pueden ser ajustados
+parcialmente, por ejemplo el <em/widget/ de texto o el <em/viewport/.
+
+Como es lógico el programa tiene que poder reaccionar a los
+cambios que el usuario realiza en los <em/widgets/ de selección de
+rango. Una forma de hacer que el programa reaccione sería tener
+cada <em/widget/ emitiendo su propio tipo de señal cuando cambie el
+ajuste, y bien pasar el nuevo valor al manejador de señal o bien
+obligarle a que mire dentro de la estructura de datos del <em/widget/
+para conocer este valor. Pero también puede ser que quiera conectar
+los ajustes de varios <em/widgets/, para que así cuando se ajuste
+uno, los demás se ajusten automáticamente. El ejemplo más
+obvio es conectar una barra de desplazamiento a una región con
+texto. Si cada <em/widget/ posee su propia forma de establecer u
+obtener sus valores de ajuste el programador puede que tenga que
+escribir sus propios controladores de señales para traducir el
+resultado de la señal producida por un <em/widget/ como el
+argumento de una función usada para determinar valores en otro
+<em/widget/.
+
+Para resolver este problema GTK+ usa objetos del tipo GtkAdjustment.
+Con ellos se consigue almacenar y traspasar información de una forma
+abstracta y flexible. El uso más obvio es el de almacenes de
+párametros para <em/widgets/ de escala (barras deslizantes y
+escalas). Como los GtkAdjustment derivan de GtkObject poseen
+cualidades intrínsecas que les permiten ser algo más que simples
+estructuras de datos. Lo más importante es que pueden emitir
+señales que a su vez pueden ser usadas tanto para reaccionar frente
+al cambio de datos introducidos por el usuario como para transferir
+los nuevos valores de forma transparente entre <em/widgets/ ajustables.
+
+<sect1>Creando un ajuste
+<p>
+Los ajustes se pueden crear usando:
+
+<tscreen><verb>
+GtkObject *gtk_adjustment_new( gfloat value,
+ gfloat lower,
+ gfloat upper,
+ gfloat step_increment,
+ gfloat page_increment,
+ gfloat page_size );
+</verb></tscreen>
+
+El argumento <tt/value/ es el valor inicial que le queremos dar
+al ajuste. Normalmente se corresponde con las posiciones situadas
+más arriba y a la izquierda de un <em/widget/ ajustable. El argumento
+<tt/lower/ especifica los valores más pequeños que el ajuste
+puede contener. A su vez con <tt/step_increment/ se especifica el
+valor más pequeño en el que se puede variar la magnitud en
+cuestión (valor de paso asociado), mientras que <tt/page_increment/
+es el mayor. Con <tt/page_size/ se determina el valor visible de un
+<em/widget/.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Forma sencilla de usar los ajustes
+<p>
+Los <em/widgets/ ajustábles se pueden dividir en dos categorias
+diferentes, aquellos que necesitan saber las unidades de la cantidad
+almacenada y los que no. Este último grupo incluye los <em/widgets/
+de tamaño (barras deslizantes, escalas, barras de estado, o botones
+giratorios). Normalmente estos <em/widgets/ son ajustados
+«directamente» por el usuario. Los argumentos <tt/lower/ y
+<tt/upper/ serán los limites dentro de los cuales el usuario puede
+manipular los ajustes. Por defecto sólo se modificará el
+<tt/value/ (valor) de un ajuste.
+
+El otro grupo incluye los <em/widgets/ de texto, la lista compuesta o
+la ventana con barra deslizante. Estos <em/widgets/ usan valores en
+pixels para sus ajustes, y normalmente son ajustados
+«indirectamente» mediante barras deslizantes. Aunque todos los
+<em/widgets/ pueden crear sus propios ajustes o usar otros creados por
+el programador con el segundo grupo suele ser conveniente dejarles que
+creen sus propios ajustes. Normalmente no tendrán en cuenta ninguno
+de los valores de un ajuste proporcionado por el programador, excepto
+<tt/value/, pero los resultados son, en general, indefinidos
+(entiendase que tendrá que leer el código fuente para saber que
+pasa con cada widget).
+
+Probablemente ya se habrá dado cuenta de que como los <em/widgets/
+de texto (y todos los <em/widgets/ del segundo grupo), insisten en
+establecer todos los valores excepto <tt/value/, mientras que las
+barras deslizantes sólo modifican <tt/value/, si se comparte un
+objeto de ajuste entre una barra deslizante y un <em/widget/ de texto
+al manipular la barra se modificará el <em/widget/ de texto. Ahora
+queda completamente demostrada la utilidad de los ajustes. Veamos un
+ejemplo:
+
+<tscreen><verb>
+ /* creamos un ajuste */
+ text = gtk_text_new (NULL, NULL);
+ /* lo usamos con la barra deslizante */
+ vscrollbar = gtk_vscrollbar_new (GTK_TEXT(text)->vadj);
+</verb></tscreen>
+
+</sect1>
+<!-- ----------------------------------------------------------------- -->
+<sect1> Descripción detallada de los ajustes
+<p>
+Puede que se esté preguntando cómo es posible crear sus propios
+controladores para responder a las modificaciones producidas por
+el usuario y cómo obtener el valor del ajuste hecho por este.
+Para aclarar esto y otras cosas vamos a estudiar la estructura
+del ajuste
+
+<tscreen><verb>
+struct _GtkAdjustment
+{
+ GtkData data;
+
+ gfloat lower;
+ gfloat upper;
+ gfloat value;
+ gfloat step_increment;
+ gfloat page_increment;
+ gfloat page_size;
+};
+</verb></tscreen>
+
+Lo primero que hay que aclarar es que no hay ninguna macro o función
+de acceso que permita obtener el <tt/value/ de un GtkAdjustment, por
+lo que tendrá que hacerlo usted mismo. Tampoco se preocupe mucho
+porque la macro <tt>GTK_ADJUSTMENT (Object)</tt> comprueba los tipos
+durante el proceso de ejecución (como hacen todas las macros de GTK+
+que sirven para comprobar los tipos).
+
+Cuando se establece el <tt/value/ de un ajuste normalmente se quiere
+que cualquier <em/widget/ se entere del cambio producido. Para ello
+GTK+ posee una función especial:
+
+<tscreen><verb>
+void gtk_adjustment_set_value( GtkAdjustment *adjustment,
+ gfloat value );
+</verb></tscreen>
+
+Tal y como se mencionó antes GtkAdjustment es una subclase de GtkObject
+y por tanto puede emitir señales. Así se consigue que se actualicen
+los valores de los ajustes cuando se comparten entre varios <em/widgets/.
+Por tanto todos los <em/widgets/ ajustables deben conectar controladores
+de señales a sus señales del tipo <tt/value_changed/. Esta es la
+definición de la señal como viene en <tt/struct _GtkAdjustmentClass/
+
+<tscreen><verb>
+ void (* value_changed) (GtkAdjustment *adjustment);
+</verb></tscreen>
+
+Todos los <em/widgets/ que usan GtkAdjustment deben emitir esta
+señal cuando cambie el valor de algún ajuste. Esto sucede cuando
+el usuario cambia algo o el programa modifica los ajustes
+mediante. Por ejemplo si queremos que rote una figura cuando
+modificamos un <em/widget/ de escala habría que usar una respuesta
+como esta:
+
+<tscreen><verb>
+void cb_rotate_picture (GtkAdjustment *adj, GtkWidget *picture)
+{
+ set_picture_rotation (picture, adj->value);
+...
+</verb></tscreen>
+
+y conectarla con el ajuste del <em/widget/ de escala mediante:
+
+<tscreen><verb>
+gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
+ GTK_SIGNAL_FUNC (cb_rotate_picture), picture);
+</verb></tscreen>
+¿Qué pasa cuando un <em/widget/ reconfigura los valores
+<tt/upper/ o <tt/lower/ (por ejemplo cuando se añade más texto)?
+Simplemente que se emite la señal <tt/changed/, que debe ser
+parecida a:
+
+<tscreen><verb>
+ void (* changed) (GtkAdjustment *adjustment);
+</verb></tscreen>
+
+Los <em/widgets/ de tamaño normalmente conectan un controlador a
+esta señal, que cambia el aspecto de éste para reflejar el
+cambio. Por ejemplo el tamaño de la guía en una barra deslizante
+que se alarga o encoge según la inversa de la diferencia de los
+valores <tt/lower/ y <tt/upper/.
+
+Probablemente nunca tenga que conectar un controlador a esta señal
+a no ser que esté escribiendo un nuevo tipo de <em/widget/. Pero si
+cambia directamente alguno de los valores de GtkAdjustment debe hacer
+que se emita la siguiente señal para reconfigurar todos aquellos
+<em/widgets/ que usen ese ajuste:
+
+<tscreen><verb>
+gtk_signal_emit_by_name (GTK_OBJECT (adjustment), "changed");
+</verb></tscreen>
+
+</sect1>
+</sect>
+<!-- ***************************************************************** -->
+<sect>Los <em/widgets/ de selección de rango <label id="sec_Range_Widgets">
+<!-- ***************************************************************** -->
+<p>
+Este tipo de <em/widgets/ incluye a las barras de desplazamiento
+(<em>scroollbar</em>) y la menos conocida escala
+(<em/scale</em>). Ambos pueden ser usados para muchas cosas, pero como
+sus funciones y su implementación son muy parecidas los describimos
+al mismo tiempo. Principalmente se utilizan para permitirle al usuario
+escoger un valor dentro de un rango ya prefijado.
+
+Todos los <em/widgets/ de selección comparten elementos
+gráficos, cada uno de los cuales tiene su propia ventana X window y
+recibe eventos. Todos contienen una guía y un rectángulo para
+determinar la posición dentro de la guía (en una procesador de
+textos con entorno gráfico se encuentra situado a la derecha del
+texto y sirve para situarnos en las diferentes partes del texto). Con
+el ratón podemos subir o bajar el rectángulo, mientras que si
+hacemos `click' dentro de la guía, pero no sobre el rectángulo,
+este se mueve hacia donde hemos hecho el click. Dependiendo del
+botón pulsado el rectángulo se moverá hasta la posición
+del click o una cantidad prefijada de ante mano.
+
+Tal y como se mencionó en <ref id="sec_Adjustment" name="Ajustes">
+todos los <em/widgets/ usados para seleccionar un rango estan
+asociados con un objeto de ajuste, a partir del cual calculan la
+longitud de la barra y su posición. Cuando el usuario manipula la
+barra de desplazamiento el widget cambiará el valor del ajuste.
+
+<sect1>El <em/widget/ barra de desplazamiento
+<p>
+El <em/widget/ barra de desplazamiento solamente debe utilizarse para
+hacer <em/scroll/ sobre otro <em/widget/, como una lista, una caja de
+texto, o un puerto de visión (y en muchos es más fácil utilizar
+el <em/widget/ scrolled window). Para el resto de los casos, debería
+utilizar los <em/widgets/ de escala, ya son más sencillos de usar y
+más potentes.
+
+Hay dos tipos separados de barras de desplazamiento, según sea
+horizontal o vertical. Realmente no hay mucho que añadir. Puede
+crear estos <em/widgets/ utilizar las funciones siguientes, definidas
+en <tt>&lt;gtk/gtkhscrollbar.h&gt;</tt> y
+<tt>&lt;gtk/gtkvscrollbar.h&gt;</tt>:
+
+<tscreen><verb>
+GtkWidget* gtk_hscrollbar_new( GtkAdjustment *adjustment );
+
+GtkWidget* gtk_vscrollbar_new( GtkAdjustment *adjustment );
+</verb></tscreen>
+
+y esto es todo lo que hay (si no me cree, ¡mire los ficheros de
+cabecera!). El argumento <tt/adjustment/ puede ser un puntero a un
+ajuste ya existente, o puede ser NULL, en cuyo caso se creará
+uno. Es útil especificar NULL si quiere pasar el ajuste recién
+creado a la función constructora de algún otro <em/widget/ (como
+por ejemplo el <em/widget/ texto) que se ocupará de configurarlo
+correctamente por usted.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1><em/Widgets/ de escala
+<p>
+Los <em/widgets/ de escala se usan para determinar el valor de una
+cantidad que se puede interpretar visualmente. El usuario
+probablemente fijará el valor a ojo. Por ejemplo el <em/widget/
+GtkColorSelection contiene <em/widgets/ de escala que controlan las
+componentes del color a seleccionar. Normalmente el valor preciso es
+menos importante que el efecto visual, por lo que el color se
+selecciona con el ratón y no mediante un número concreto.
+
+<!-- ----------------------------------------------------------------- -->
+<sect2>Creación de un <em/widget/ de escala
+<p>
+Existen dos tipos de <em/widgets/ de escala: GtkHScale (que es
+horizontal) y GtkVscale (vertical). Como funcionan de la misma manera
+los vamos a describir a la vez. Las funciones definidas en
+<tt>&lt;gtk/gtkvscale.h&gt;</tt> y <tt>&lt;gtk/gtkhscale.h&gt;</tt>,
+crean <em/widgets/ de escala verticales y horizontales
+respectivamente.
+
+<tscreen><verb>
+GtkWidget* gtk_vscale_new( GtkAdjustment *adjustment );
+
+GtkWidget* gtk_hscale_new( GtkAdjustment *adjustment );
+</verb></tscreen>
+
+El <tt/ajuste/ (adjustment) puede ser tanto un ajuste creado
+mediante <tt/gtk_adjustment_new()/ como <tt/NULL/. En este
+último caso se crea un GtkAdjustment anónimo con todos sus
+valores iguales a <tt/0.0/. Si no ha quedado claro el uso de esta
+función consulte la sección <ref id="sec_Adjustment"
+name="Ajustes"> para una discusión más detallada.
+
+<!-- ----------------------------------------------------------------- -->
+<sect2> Funciones y señales
+<p>
+Los <em/widgets/ de escala pueden indicar su valor actual como un
+número. Su comportamiento por defecto es mostrar este valor, pero
+se puede modificar usando:
+
+<tscreen><verb>
+void gtk_scale_set_draw_value( GtkScale *scale,
+ gint draw_value );
+</verb></tscreen>
+
+Los valores posibles de <tt/draw_value son/ son <tt/TRUE/ o <tt/FALSE/.
+Con el primero se muestra el valor y con el segundo no.
+
+El valor mostrado por un <em/widget/ de escala por defecto se redondea
+a un valor decimal (igual que con <tt/value/ en un GtkAdjustment). Se
+puede cambiar con:
+
+<tscreen>
+<verb>
+void gtk_scale_set_digits( GtkScale *scale,
+ gint digits );
+</verb>
+</tscreen>
+
+donde <tt/digits/ es el número de posiciones decimales que se
+quiera. En la práctica sólo se mostrarán 13 como máximo.
+
+Por último, el valor se puede dibujar en diferentes posiciones con
+respecto a la posición del rectangulo que hay dentro de la guía:
+
+<tscreen>
+<verb>
+void gtk_scale_set_value_pos( GtkScale *scale,
+ GtkPositionType pos );
+</verb>
+</tscreen>
+
+Si ha leido la sección acerca del <em/widget/ libro de notas
+entonces ya conoce cuales son los valores posibles de <tt/pos/. Estan
+definidos en <tt>&lt;gtk/gtkscale.h&gt;</tt> como <tt/enum GtkPositionType/
+y son auto explicatorios. Si se escoge un lateral de la guía,
+entonces seguirá al rectángulo a lo largo de la guía.
+
+Todas las funcioenes precedentes se encuentran definidas en:
+<tt>&lt;gtk/gtkscale.h&gt;</tt>.
+</sect2>
+</sect1>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Funciones comunes <label id="sec_funciones_range">
+<p>
+La descripción interna de la clase GtkRange es bastante complicada,
+pero al igual que con el resto de las «clases base» sólo es
+interesante si se quiere «hackear». Casi todas las señales y
+funciones sólo son útiles para desarrollar derivados. Para un
+usuario normal las funciones interesantes son aquellas definidas en:
+<tt>&lt;gtk/gtkrange.h&gt;</tt> y funcionan igual en todos los
+<em/widgets/ de rango.
+
+<!-- ----------------------------------------------------------------- -->
+<sect2> Estableciendo cada cúanto se actualizan
+<p>
+La política de actualización de un <em/widget/ define en que
+puntos de la interacción con el usuario debe cambiar el valor
+<tt/value/ en su GtkAdjustment y emitir la señal
+«value_changed». Las actualizaciones definidas en
+<tt>&lt;gtk/gtkenums.h&gt;</tt> como <tt>enum GtkUpdateType</tt>, son:
+
+<itemize>
+<item>GTK_UPDATE_POLICY_CONTINUOUS - Este es el valor por defecto.La
+señal «value_changed» se emite continuamente, por ejemplo cuando
+la barra deslizante se mueve incluso aunque sea un poquito.
+</item>
+<item>GTK_UPDATE_POLICY_DISCONTINUOUS - La señal «value_changed»
+sólo se emite cuando se ha parado de mover la barra y el usuario ha
+soltado el botón del ratón.
+</item>
+<item>GTK_UPDATE_POLICY_DELAYED - La señal sólo se emite cuando
+el usuario suelta el botón del ratón o si la barra no se mueve
+durante un periodo largo de tiempo.
+</item>
+</itemize>
+
+Para establecer la política de actualización se usa la
+conversión definida en la macro
+
+<tscreen><verb>
+void gtk_range_set_update_policy( GtkRange *range,
+ GtkUpdateType policy) ;
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect2>Obteniendo y estableciendo Ajustes
+<p>
+Para obtener o establecer el ajuste de un <em/widget/ de rango se usa:
+
+<tscreen><verb>
+GtkAdjustment* gtk_range_get_adjustment( GtkRange *range );
+
+void gtk_range_set_adjustment( GtkRange *range,
+ GtkAdjustment *adjustment );
+</verb></tscreen>
+
+La función <tt/gtk_range_get_adjustment()/ devuelve un puntero al
+ajuste al que <tt/range/ esté conectado.
+
+La función <tt/gtk_range_set_adjustment()/ no hace nada si se le
+pasa como argumento el valor <tt/range/ del ajuste que esta siendo
+usado (aunque se haya modificado algún valor). En el caso de que
+sea un ajuste nuevo (GtkAdjustment) dejará de usar el antiguo
+(probablemente lo destruirá) y conectará las señales
+apropiadas al nuevo. A continuación llamará a la función
+<tt/gtk_range_adjustment_changed()/ que en teoría recalculará el
+tamaño y/o la posición de la barra, redibujándola en caso de
+que sea necesario. Tal y como se mencionó en la sección de los
+ajustes si se quiere reusar el mismo GtkAdjustment cuando se modifican
+sus valores se debe emitir la señal «changed». Por ejemplo:
+
+<tscreen><verb>
+gtk_signal_emit_by_name (GTK_OBJECT (adjustment), "changed");
+</verb></tscreen>
+</sect2>
+</sect1>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Enlaces con el teclado y el ratón
+<p>
+Todos los <em/widgets/ de rango reaccionan más o menos de la misma
+manera a las pulsaciones del ratón. Al pulsar el botón 1 sobre
+el rectángulo de la barra el <tt/value/ del ajuste aumentará o
+disminuirá según <tt/page_increment/. Con el botón 2 la barra
+se desplazará al punto en el que el botón fue pulsado. Con cada
+pulsación de cualquier botón sobre las flechas el valor del
+ajuste se modifica una cantidad igual a <tt/step_increment/.
+
+
+Acostumbrarse a que tanto las barras deslizantes como los <em/widgets/ de
+escala puedan tomar la atención del teclado puede ser un proceso largo.
+Si que se cree que los usuarios no lo van a entender se puede anular
+mediante la función GTK_WIDGET_UNSET_FLAGS y con GTK_CAN_FOCUS como
+argumento:
+
+<tscreen><verb>
+GTK_WIDGET_UNSET_FLAGS (scrollbar, GTK_CAN_FOCUS);
+</verb></tscreen>
+
+Los enlaces entre teclas (que sólo estan activos cuando el
+<em/widget/ tiene la atención (focus)) se comportan de manera
+diferente para los <em/widgets/ de rango horizontales que para los
+verticales. También son diferentes para los <em/widgets/ de escala
+y para las barras deslizantes. (Simplemente para evitar confusiones
+entre las teclas de las barras deslizantes horizontales y verticales,
+ya que ambas actúan sobre la misma área)
+
+<sect2><em/Widgets/ de rango vertical
+<p>
+Todos los <em/widgets/ de rango pueden ser manipulados con las teclas
+arriba, abajo, <tt/Re Pág/, <tt/ Av Pág/. Las flechas mueven las
+barras la cantidad fijada mediante <tt/step_increment/, mientras que
+<tt/Re Pág/ y <tt/Av Pag/ lo hacen según <tt/page_increment/.
+
+El usuario también puede mover la barra de un extremo al otro de la
+guía mediante el teclado. Con el <em/widget/ GtkVScale podemos ir a
+los extremos utilizando las teclas <tt/Inicio/ y <tt/Final/ mientras
+que con el <em/widget/ GtkVScrollbar habrá que utilizar
+<tt/Control-Re Pág/ y <tt/Control-Av Pág/.
+
+<!-- ----------------------------------------------------------------- -->
+<sect2><em/Widgets/ de rango horizontal
+<p>
+Las teclas izquierda y derecha funcionan tal y como espera que
+funcionen en estos <em/widgets/: mueven la barra una cantidad dada por
+<tt/step_increment/. A su vez <tt/Inicio/ y <tt/Final/ sirven para
+pasar de un extremo al otro de la guía. Para el <em/widget/
+GtkHScale el mover la barra una cantidad dada por <tt/page_increment/
+se consigue mediante <tt>Control-Izquierda</tt> y
+<tt>Control-derecha</tt>, mientras que para el <em/widget/
+GtkHScrollbar se consigue con <tt/Control-Inicio/ y
+<tt/Control-Final/.
+</sect2>
+</sect1>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Ejemplo <label id="sec_Ejemplo_Rango">
+<p>
+Este ejemplo es una versión modificada del test «range controls»
+que a su vez forma parte de <tt/testgtk.c/. Simplemente dibuja una
+ventana con tres <em/widgets/ de rango conectados al mismo ajuste, y
+un conjunto de controles para ajustar algunos de los parámetros
+ya mencionados. Así se consigue ver como funcionan estos
+<em/widgets/ al ser manipulados por el usuario.
+
+<tscreen><verb>
+/* principio del ejemplo widgets de selección de rango rangewidgets.c */
+
+#include <gtk/gtk.h>
+
+GtkWidget *hscale, *vscale;
+
+void cb_pos_menu_select( GtkWidget *item,
+ GtkPositionType pos )
+{
+ /* Establece el valor position en los widgets de escala */
+ gtk_scale_set_value_pos (GTK_SCALE (hscale), pos);
+ gtk_scale_set_value_pos (GTK_SCALE (vscale), pos);
+}
+
+void cb_update_menu_select( GtkWidget *item,
+ GtkUpdateType policy )
+{
+ /* Establece la política de actualización para los widgets
+ * de escala */
+ gtk_range_set_update_policy (GTK_RANGE (hscale), policy);
+ gtk_range_set_update_policy (GTK_RANGE (vscale), policy);
+}
+
+void cb_digits_scale( GtkAdjustment *adj )
+{
+ /* Establece el número de cifras decimales a las que se
+ * redondeará adj->value */
+ gtk_scale_set_digits (GTK_SCALE (hscale), (gint) adj->value);
+ gtk_scale_set_digits (GTK_SCALE (vscale), (gint) adj->value);
+}
+
+void cb_page_size( GtkAdjustment *get,
+ GtkAdjustment *set )
+{
+ /* Establece el tamaño de la página y el incremento del
+ * ajuste al valor especificado en la escala "Page Size" */
+ set->page_size = get->value;
+ set->page_increment = get->value;
+ /* Ahora emite la señal "changed" para reconfigurar todos los
+ * widgets que están enlazados a este ajuste */
+ gtk_signal_emit_by_name (GTK_OBJECT (set), "changed");
+}
+
+void cb_draw_value( GtkToggleButton *boton )
+{
+ /* Activa o desactiva el valor display en los widgets de escala
+ * dependiendo del estado del botón de comprobación */
+ gtk_scale_set_draw_value (GTK_SCALE (hscale), boton->active);
+ gtk_scale_set_draw_value (GTK_SCALE (vscale), boton->active);
+}
+
+/* Funciones varias */
+
+GtkWidget *make_menu_item( gchar *name,
+ GtkSignalFunc callback,
+ gpointer data )
+{
+ GtkWidget *item;
+
+ item = gtk_menu_item_new_with_label (name);
+ gtk_signal_connect (GTK_OBJECT (item), "activate",
+ callback, data);
+ gtk_widget_show (item);
+
+ return(item);
+}
+
+void scale_set_default_values( GtkScale *scale )
+{
+ gtk_range_set_update_policy (GTK_RANGE (scale),
+ GTK_UPDATE_CONTINUOUS);
+ gtk_scale_set_digits (scale, 1);
+ gtk_scale_set_value_pos (scale, GTK_POS_TOP);
+ gtk_scale_set_draw_value (scale, TRUE);
+}
+
+/* crea la ventana principal */
+
+void create_range_controls( void )
+{
+ GtkWidget *ventana;
+ GtkWidget *caja1, *caja2, *caja3;
+ GtkWidget *boton;
+ GtkWidget *scrollbar;
+ GtkWidget *separator;
+ GtkWidget *opt, *menu, *item;
+ GtkWidget *etiqueta;
+ GtkWidget *scale;
+ GtkObject *adj1, *adj2;
+
+ /* creación estándar de una ventana */
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
+ GTK_SIGNAL_FUNC(gtk_main_quit),
+ NULL);
+ gtk_window_set_title (GTK_WINDOW (ventana), "range controls");
+
+ caja1 = gtk_vbox_new (FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (ventana), caja1);
+ gtk_widget_show (caja1);
+
+ caja2 = gtk_hbox_new (FALSE, 10);
+ gtk_container_border_width (GTK_CONTAINER (caja2), 10);
+ gtk_box_pack_start (GTK_BOX (caja1), caja2, TRUE, TRUE, 0);
+ gtk_widget_show (caja2);
+
+ /* value, lower, upper, step_increment, page_increment, page_size */
+ /* Observe que el valor de page_size solo sirve para los widgets
+ * barras de desplazamiento (scrollbar), y que el valor más
+ * alto que obtendrá será (upper - page_size). */
+ adj1 = gtk_adjustment_new (0.0, 0.0, 101.0, 0.1, 1.0, 1.0);
+
+ vscale = gtk_vscale_new (GTK_ADJUSTMENT (adj1));
+ scale_set_default_values (GTK_SCALE (vscale));
+ gtk_box_pack_start (GTK_BOX (caja2), vscale, TRUE, TRUE, 0);
+ gtk_widget_show (vscale);
+
+ caja3 = gtk_vbox_new (FALSE, 10);
+ gtk_box_pack_start (GTK_BOX (caja2), caja3, TRUE, TRUE, 0);
+ gtk_widget_show (caja3);
+
+ /* Reutilizamos el mismo ajuste */
+ hscale = gtk_hscale_new (GTK_ADJUSTMENT (adj1));
+ gtk_widget_set_usize (GTK_WIDGET (hscale), 200, 30);
+ scale_set_default_values (GTK_SCALE (hscale));
+ gtk_box_pack_start (GTK_BOX (caja3), hscale, TRUE, TRUE, 0);
+ gtk_widget_show (hscale);
+
+ /* Reutilizamos de nuevo el mismo ajuste */
+ scrollbar = gtk_hscrollbar_new (GTK_ADJUSTMENT (adj1));
+ /* Observe que con esto conseguimos que la escala siempre se
+ * actualice de una forma continua cuando se mueva la barra de
+ * desplazamiento */
+ gtk_range_set_update_policy (GTK_RANGE (scrollbar),
+ GTK_UPDATE_CONTINUOUS);
+ gtk_box_pack_start (GTK_BOX (caja3), scrollbar, TRUE, TRUE, 0);
+ gtk_widget_show (scrollbar);
+
+ caja2 = gtk_hbox_new (FALSE, 10);
+ gtk_container_border_width (GTK_CONTAINER (caja2), 10);
+ gtk_box_pack_start (GTK_BOX (caja1), caja2, TRUE, TRUE, 0);
+ gtk_widget_show (caja2);
+
+ /* Un botón para comprobar si el valor se muestra o no*/
+ boton = gtk_check_button_new_with_label("Display value on scale widgets");
+ gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (boton), TRUE);
+ gtk_signal_connect (GTK_OBJECT (boton), "toggled",
+ GTK_SIGNAL_FUNC(cb_draw_value), NULL);
+ gtk_box_pack_start (GTK_BOX (caja2), boton, TRUE, TRUE, 0);
+ gtk_widget_show (boton);
+
+ caja2 = gtk_hbox_new (FALSE, 10);
+ gtk_container_border_width (GTK_CONTAINER (caja2), 10);
+
+ /* Una opción en el menú para cambiar la posición del
+ * valor */
+ etiqueta = gtk_label_new ("Scale Value Position:");
+ gtk_box_pack_start (GTK_BOX (caja2), etiqueta, FALSE, FALSE, 0);
+ gtk_widget_show (etiqueta);
+
+ opt = gtk_option_menu_new();
+ menu = gtk_menu_new();
+
+ item = make_menu_item ("Top",
+ GTK_SIGNAL_FUNC(cb_pos_menu_select),
+ GINT_TO_POINTER (GTK_POS_TOP));
+ gtk_menu_append (GTK_MENU (menu), item);
+
+ item = make_menu_item ("Bottom", GTK_SIGNAL_FUNC (cb_pos_menu_select),
+ GINT_TO_POINTER (GTK_POS_BOTTOM));
+ gtk_menu_append (GTK_MENU (menu), item);
+
+ item = make_menu_item ("Left", GTK_SIGNAL_FUNC (cb_pos_menu_select),
+ GINT_TO_POINTER (GTK_POS_LEFT));
+ gtk_menu_append (GTK_MENU (menu), item);
+
+ item = make_menu_item ("Right", GTK_SIGNAL_FUNC (cb_pos_menu_select),
+ GINT_TO_POINTER (GTK_POS_RIGHT));
+ gtk_menu_append (GTK_MENU (menu), item);
+
+ gtk_option_menu_set_menu (GTK_OPTION_MENU (opt), menu);
+ gtk_box_pack_start (GTK_BOX (caja2), opt, TRUE, TRUE, 0);
+ gtk_widget_show (opt);
+
+ gtk_box_pack_start (GTK_BOX (caja1), caja2, TRUE, TRUE, 0);
+ gtk_widget_show (caja2);
+
+ caja2 = gtk_hbox_new (FALSE, 10);
+ gtk_container_border_width (GTK_CONTAINER (caja2), 10);
+
+ /* Sí, otra opción de menú, esta vez para la política
+ * de actualización de los widgets */
+ etiqueta = gtk_label_new ("Scale Update Policy:");
+ gtk_box_pack_start (GTK_BOX (caja2), etiqueta, FALSE, FALSE, 0);
+ gtk_widget_show (etiqueta);
+
+ opt = gtk_option_menu_new();
+ menu = gtk_menu_new();
+
+ item = make_menu_item ("Continuous",
+ GTK_SIGNAL_FUNC (cb_update_menu_select),
+ GINT_TO_POINTER (GTK_UPDATE_CONTINUOUS));
+ gtk_menu_append (GTK_MENU (menu), item);
+
+ item = make_menu_item ("Discontinuous",
+ GTK_SIGNAL_FUNC (cb_update_menu_select),
+ GINT_TO_POINTER (GTK_UPDATE_DISCONTINUOUS));
+ gtk_menu_append (GTK_MENU (menu), item);
+
+ item = make_menu_item ("Delayed",
+ GTK_SIGNAL_FUNC (cb_update_menu_select),
+ GINT_TO_POINTER (GTK_UPDATE_DELAYED));
+ gtk_menu_append (GTK_MENU (menu), item);
+
+ gtk_option_menu_set_menu (GTK_OPTION_MENU (opt), menu);
+ gtk_box_pack_start (GTK_BOX (caja2), opt, TRUE, TRUE, 0);
+ gtk_widget_show (opt);
+
+ gtk_box_pack_start (GTK_BOX (caja1), caja2, TRUE, TRUE, 0);
+ gtk_widget_show (caja2);
+
+ caja2 = gtk_hbox_new (FALSE, 10);
+ gtk_container_border_width (GTK_CONTAINER (caja2), 10);
+
+ /* Un widget GtkHScale para ajustar el número de dígitos en
+ * la escala. */
+ etiqueta = gtk_label_new ("Scale Digits:");
+ gtk_box_pack_start (GTK_BOX (caja2), etiqueta, FALSE, FALSE, 0);
+ gtk_widget_show (etiqueta);
+
+ adj2 = gtk_adjustment_new (1.0, 0.0, 5.0, 1.0, 1.0, 0.0);
+ gtk_signal_connect (GTK_OBJECT (adj2), "value_changed",
+ GTK_SIGNAL_FUNC (cb_digits_scale), NULL);
+ scale = gtk_hscale_new (GTK_ADJUSTMENT (adj2));
+ gtk_scale_set_digits (GTK_SCALE (scale), 0);
+ gtk_box_pack_start (GTK_BOX (caja2), scale, TRUE, TRUE, 0);
+ gtk_widget_show (scale);
+
+ gtk_box_pack_start (GTK_BOX (caja1), caja2, TRUE, TRUE, 0);
+ gtk_widget_show (caja2);
+
+ caja2 = gtk_hbox_new (FALSE, 10);
+ gtk_container_border_width (GTK_CONTAINER (caja2), 10);
+
+ /* Y un último widget GtkHScale para ajustar el tamaño de la
+ * página de la barra de desplazamiento. */
+ etiqueta = gtk_label_new ("Scrollbar Page Size:");
+ gtk_box_pack_start (GTK_BOX (caja2), etiqueta, FALSE, FALSE, 0);
+ gtk_widget_show (etiqueta);
+
+ adj2 = gtk_adjustment_new (1.0, 1.0, 101.0, 1.0, 1.0, 0.0);
+ gtk_signal_connect (GTK_OBJECT (adj2), "value_changed",
+ GTK_SIGNAL_FUNC (cb_page_size), adj1);
+ scale = gtk_hscale_new (GTK_ADJUSTMENT (adj2));
+ gtk_scale_set_digits (GTK_SCALE (scale), 0);
+ gtk_box_pack_start (GTK_BOX (caja2), scale, TRUE, TRUE, 0);
+ gtk_widget_show (scale);
+
+ gtk_box_pack_start (GTK_BOX (caja1), caja2, TRUE, TRUE, 0);
+ gtk_widget_show (caja2);
+
+ separator = gtk_hseparator_new ();
+ gtk_box_pack_start (GTK_BOX (caja1), separator, FALSE, TRUE, 0);
+ gtk_widget_show (separator);
+
+ caja2 = gtk_vbox_new (FALSE, 10);
+ gtk_container_border_width (GTK_CONTAINER (caja2), 10);
+ gtk_box_pack_start (GTK_BOX (caja1), caja2, FALSE, TRUE, 0);
+ gtk_widget_show (caja2);
+
+ boton = gtk_button_new_with_label ("Quit");
+ gtk_signal_connect_object (GTK_OBJECT (boton), "clicked",
+ GTK_SIGNAL_FUNC(gtk_main_quit),
+ NULL);
+ gtk_box_pack_start (GTK_BOX (caja2), boton, TRUE, TRUE, 0);
+ GTK_WIDGET_SET_FLAGS (boton, GTK_CAN_DEFAULT);
+ gtk_widget_grab_default (boton);
+ gtk_widget_show (boton);
+
+ gtk_widget_show (ventana);
+}
+
+int main( int argc,
+ char *argv[] )
+{
+ gtk_init(&amp;argc, &amp;argv);
+
+ create_range_controls();
+
+ gtk_main();
+
+ return(0);
+}
+
+/* fin del ejemplo */
+</verb></tscreen>
+
+Observe que el programa no llama a <tt/gtk_signal_connect/ para
+conectar el «delete_event», y que sólo conecta la señal
+«destroy». Con esto seguimos realizando la función deseada, ya que
+un «delete_event» no manejado desenboca en una señal «destroy»
+para la ventana.
+</sect1>
+</sect>
+
+<!-- ***************************************************************** -->
+<sect><em/Widgets/ varios
+<!-- ***************************************************************** -->
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Etiquetas
+<p>
+Las etiquetas se usan mucho en GTK y son bastante simples de manejar.
+No pueden emitir señales ya que no tienen ventanas X window
+asociadas. Si se desea capturar señales se debe usar el <em/widget/
+EventBox o un <em/widget/ botón.
+
+Para crear una nueva etiqueta se usa:
+
+<tscreen><verb>
+GtkWidget *gtk_label_new( char *str );
+</verb></tscreen>
+
+El único argumento es la cadena de texto que se quiere mostrar.
+
+Para cambiarla después de que haya sido creada se usa:
+
+<tscreen><verb>
+void gtk_label_set( GtkLabel *etiqueta,
+ char *str );
+</verb></tscreen>
+
+En este caso el primer argumento es la etiqueta ya creada (cambiado su
+tipo mediante la macro <tt/GTK_LABEL()/) y el segundo es la nueva cadena.
+El espacio que necesite la nueva etiqueta se ajustará
+automáticamente, si es necesario.
+
+Para obtener el estado de la cadena en un momento dado existe la
+función:
+
+<tscreen><verb>
+void gtk_label_get( GtkLabel *etiqueta,
+ char **str );
+</verb></tscreen>
+El primer argumento es la etiqueta, mientras que el segundo es el
+valor devuelto para la cadena. No libere la memoria de la cadena
+devuelta, ya que se utiliza internamente por GTK.
+
+El texto de la etiqueta se puede justificar utilizando:
+
+<tscreen><verb>
+void gtk_label_set_justify( GtkLabel *etiqueta,
+ GtkJustification jtype );
+</verb></tscreen>
+
+Los valores posibles para <tt/jtype/ son:
+<itemize>
+<item> GTK_JUSTIFY_LEFT
+<item> GTK_JUSTIFY_RIGHT
+<item> GTK_JUSTIFY_CENTER (the default)
+<item> GTK_JUSTIFY_FILL
+</itemize>
+
+El <em/widget/ etiqueta también es capaz de separar el texto de forma
+automática cuando se llega al final de una linea. Esto se puede
+conseguir utilizando:
+
+<tscreen><verb>
+void gtk_label_set_line_wrap (GtkLabel *etiqueta,
+ gboolean wrap);
+</verb></tscreen>
+
+El argumento <tt/wrap/ toma el valor TRUE o FALSE.
+
+Si quiere que su etiqueta salga subrayada, puede especificar un motivo
+para el subrayado con:
+
+<tscreen><verb>
+void gtk_label_set_pattern (GtkLabel *etiqueta,
+ const gchar *pattern);
+</verb></tscreen>
+
+El argumento <tt/pattern/ indica cual debe ser el aspecto del
+subrayado. Consiste en una cadena de espacios en blanco y carácteres
+de subrayado. Por ejemplo, la cadena <tt/"__ __"/ debe hacer que
+se subrayen los dos primeros y el octavo y el noveno carácter.
+
+A continuación tenemos un pequeño ejemplo que ilustra el uso de estas
+funciones. Este ejemplo utiliza el <em/widget/ marco (<em/frame/) para
+hacer una mejor demostración de los estilos de la etiqueta. Por ahora
+puede ignorarlo, ya que el <em/widget/ <ref id="sec_Frames"
+name="Frame"> se explicará más tarde.
+
+<tscreen><verb>
+/* principio del ejemplo label label.c */
+
+#include <gtk/gtk.h>
+
+int main( int argc,
+ char *argv[] )
+{
+ static GtkWidget *ventana = NULL;
+ GtkWidget *hbox;
+ GtkWidget *vbox;
+ GtkWidget *frame;
+ GtkWidget *etiqueta;
+
+ /* Inicializa GTK */
+ gtk_init(&amp;argc, &amp;argv);
+
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
+ GTK_SIGNAL_FUNC(gtk_main_quit),
+ NULL);
+
+ gtk_window_set_title (GTK_WINDOW (ventana), "Etiqueta");
+ vbox = gtk_vbox_new (FALSE, 5);
+ hbox = gtk_hbox_new (FALSE, 5);
+ gtk_container_add (GTK_CONTAINER (ventana), hbox);
+ gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (ventana), 5);
+
+ frame = gtk_frame_new ("Normal Label");
+ etiqueta = gtk_label_new ("This is a Normal label");
+ gtk_container_add (GTK_CONTAINER (frame), etiqueta);
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+
+ frame = gtk_frame_new ("Multi-line Label");
+ etiqueta = gtk_label_new ("This is a Multi-line label.\nSecond line\n" \
+ "Third line");
+ gtk_container_add (GTK_CONTAINER (frame), etiqueta);
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+
+ frame = gtk_frame_new ("Left Justified Label");
+ etiqueta = gtk_label_new ("This is a Left-Justified\n" \
+ "Multi-line label.\nThird line");
+ gtk_label_set_justify (GTK_LABEL (etiqueta), GTK_JUSTIFY_LEFT);
+ gtk_container_add (GTK_CONTAINER (frame), etiqueta);
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+
+ frame = gtk_frame_new ("Right Justified Label");
+ etiqueta = gtk_label_new ("This is a Right-Justified\nMulti-line label.\n" \
+ "Fourth line, (j/k)");
+ gtk_label_set_justify (GTK_LABEL (etiqueta), GTK_JUSTIFY_RIGHT);
+ gtk_container_add (GTK_CONTAINER (frame), etiqueta);
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+
+ vbox = gtk_vbox_new (FALSE, 5);
+ gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
+ frame = gtk_frame_new ("Line wrapped label");
+ etiqueta = gtk_label_new ("This is an example of a line-wrapped label. It " \
+ "should not be taking up the entire " /* big space to test spacing */\
+ "width allocated to it, but automatically " \
+ "wraps the words to fit. " \
+ "The time has come, for all good men, to come to " \
+ "the aid of their party. " \
+ "The sixth sheik's six sheep's sick.\n" \
+ " It supports multiple paragraphs correctly, " \
+ "and correctly adds "\
+ "many extra spaces. ");
+ gtk_label_set_line_wrap (GTK_LABEL (etiqueta), TRUE);
+ gtk_container_add (GTK_CONTAINER (frame), etiqueta);
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+
+ frame = gtk_frame_new ("Filled, wrapped label");
+ etiqueta = gtk_label_new ("This is an example of a line-wrapped, filled label. " \
+ "It should be taking "\
+ "up the entire width allocated to it. " \
+ "Here is a seneance to prove "\
+ "my point. Here is another sentence. "\
+ "Here comes the sun, do de do de do.\n"\
+ " This is a new paragraph.\n"\
+ " This is another newer, longer, better " \
+ "paragraph. It is coming to an end, "\
+ "unfortunately.");
+ gtk_label_set_justify (GTK_LABEL (etiqueta), GTK_JUSTIFY_FILL);
+ gtk_label_set_line_wrap (GTK_LABEL (etiqueta), TRUE);
+ gtk_container_add (GTK_CONTAINER (frame), etiqueta);
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+
+ frame = gtk_frame_new ("Underlined label");
+ etiqueta = gtk_label_new ("This label is underlined!\n"
+ "This one is underlined in quite a funky fashion");
+ gtk_label_set_justify (GTK_LABEL (etiqueta), GTK_JUSTIFY_LEFT);
+ gtk_label_set_pattern (GTK_LABEL (etiqueta),
+ "_________________________ _ _________ _ ______ __ _______ ___");
+ gtk_container_add (GTK_CONTAINER (frame), etiqueta);
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+
+ gtk_widget_show_all (ventana);
+
+ gtk_main ();
+
+ return(0);
+}
+/* fin del ejemplo */
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Flechas
+<p>
+En <em/widget/ flecha (<em/arrow/) dibuja la punta de una flecha,
+con un estilo y hacia una dirección a escoger. Puede ser muy útil
+en muchas aplicaciones cuando se coloca en un botón.
+
+Sólo hay dos funciones para manipular el <em/widget/ flecha:
+
+<tscreen><verb>
+GtkWidget *gtk_arrow_new( GtkArrowType arrow_type,
+ GtkShadowType shadow_type );
+
+void gtk_arrow_set( GtkArrow *arrow,
+ GtkArrowType arrow_type,
+ GtkShadowType shadow_type );
+</verb></tscreen>
+
+La primera crea un nuevo <em/widget/ flecha del tipo y apariencia
+indicados. La segunda permite alterar posteriormente estos valores. El
+argumento <tt/arrow_type/ puede tomar uno de los valores siguientes:
+
+<itemize>
+<item> GTK_ARROW_UP
+<item> GTK_ARROW_DOWN
+<item> GTK_ARROW_LEFT
+<item> GTK_ARROW_RIGHT
+</itemize>
+
+Naturalmente, estos valores indican la dirección a la que debe apuntar
+la flecha. El argumento <tt/shadow_type/ puede tomar uno de los
+valores siguientes:
+
+<itemize>
+<item> GTK_SHADOW_IN
+<item> GTK_SHADOW_OUT (por defecto)
+<item> GTK_SHADOW_ETCHED_IN
+<item> GTK_SHADOW_ETCHED_OUT
+</itemize>
+
+Aquí tenemos un pequeño ejemplo para ilustrar la utilización de la
+flecha.
+
+<tscreen><verb>
+/* principio del ejemplo arrow arrow.c */
+
+#include <gtk/gtk.h>
+
+/* Crea un widget flecha con los parámetros especificados
+ * y lo empaqueta en un botón */
+GtkWidget *create_arrow_button( GtkArrowType arrow_type,
+ GtkShadowType shadow_type )
+{
+ GtkWidget *boton;
+ GtkWidget *arrow;
+
+ boton = gtk_button_new();
+ arrow = gtk_arrow_new (arrow_type, shadow_type);
+
+ gtk_container_add (GTK_CONTAINER (boton), arrow);
+
+ gtk_widget_show(boton);
+ gtk_widget_show(arrow);
+
+ return(boton);
+}
+
+int main( int argc,
+ char *argv[] )
+{
+ /* GtkWidget es el tipo utilizado para los widgets */
+ GtkWidget *ventana;
+ GtkWidget *boton;
+ GtkWidget *box;
+
+ /* Inicializa el toolkit */
+ gtk_init (&amp;argc, &amp;argv);
+
+ /* Crea una nueva ventana */
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ gtk_window_set_title (GTK_WINDOW (ventana), "Arrow Buttons");
+
+ /* Es una buena idea hacer esto con todas las ventanas. */
+ gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
+ GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
+
+ /* Establece el ancho del borde de la ventana. */
+ gtk_container_set_border_width (GTK_CONTAINER (ventana), 10);
+
+ /* Crea una caja para almacenar las flechas/botones */
+ box = gtk_hbox_new (FALSE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (box), 2);
+ gtk_container_add (GTK_CONTAINER (ventana), box);
+
+ /* Empaqueta y muestra todos nuestros widgets */
+ gtk_widget_show(box);
+
+ boton = create_arrow_button(GTK_ARROW_UP, GTK_SHADOW_IN);
+ gtk_box_pack_start (GTK_BOX (box), boton, FALSE, FALSE, 3);
+
+ boton = create_arrow_button(GTK_ARROW_DOWN, GTK_SHADOW_OUT);
+ gtk_box_pack_start (GTK_BOX (box), boton, FALSE, FALSE, 3);
+
+ boton = create_arrow_button(GTK_ARROW_LEFT, GTK_SHADOW_ETCHED_IN);
+ gtk_box_pack_start (GTK_BOX (box), boton, FALSE, FALSE, 3);
+
+ boton = create_arrow_button(GTK_ARROW_RIGHT, GTK_SHADOW_ETCHED_OUT);
+ gtk_box_pack_start (GTK_BOX (box), boton, FALSE, FALSE, 3);
+
+ gtk_widget_show (ventana);
+
+ /* Nos quedamos en gtk_main y ¡esperamos que empiece la diversión! */
+ gtk_main ();
+
+ return(0);
+}
+/* fin del ejemplo */
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>El <em/widget/ de información rápida (<em/tooltip/)
+<p>
+Estos <em/widgets/ son las pequeñas etiquetas que texto que
+aparecen cuando se sitúa el puntero del ratón sobre un botón
+u otro <em/widget/ durante algunos segundos. Son bastante fáciles
+de usar, así que no se dará ningún ejemplo. Si quiere ver
+algún ejemplo se recomienda leer el programa testgtk.c que
+acompaña a GTK.
+
+Algunos <em/widgets/ (como la etiqueta) no pueden llevar asociado un
+<em/tooltip/.
+
+Para cada función sólo hay que hacer una llamada para conseguir
+un <em/tooltip/. El objeto <tt/GtkTooltip/ que devuelve la siguiente
+función puede ser usado para crear múltiples <em/widgets/.
+
+<tscreen><verb>
+GtkTooltips *gtk_tooltips_new( void );
+</verb></tscreen>
+
+Una vez que el <em/tooltip/ ha sido creado (y el <em/widget/ sobre el
+que se quiere usar) simplemente hay que usar la siguiente llamada para
+pegarlo:
+
+<tscreen><verb>
+void gtk_tooltips_set_tip( GtkTooltips *tooltips,
+ GtkWidget *widget,
+ const gchar *tip_text,
+ const gchar *tip_private );
+</verb></tscreen>
+
+El primer argumento es el <em/tooltip/ que ya ha creado, seguido del
+<em/widget/ al que se desea asociar el <em/tooltip/, el tercero es el
+texto que se quiere que aparezca y el último es una cadena de texto
+que puede ser usada como un identificador cuando se usa GtkTipsQuery
+para desarollar ayuda sensible al contexto. Por ahora conviene dejarlo
+como NULL.
+
+<!-- TODO: sort out what how to do the context sensitive help -->
+
+Veamos un ejemplo:
+
+<tscreen><verb>
+GtkTooltips *tooltips;
+GtkWidget *boton;
+...
+tooltips = gtk_tooltips_new ();
+boton = gtk_button_new_with_label ("botón 1");
+...
+gtk_tooltips_set_tip (tooltips, boton, "Este es el botón 1", NULL);
+</verb></tscreen>
+
+Existen otras funciones que pueden ser usadas con los <em/tooltips/.
+Solamente vamos a enumerlarlas añadiendo una pequeña descripción
+de que hace cada una.
+
+<tscreen><verb>
+void gtk_tooltips_enable( GtkTooltips *tooltips );
+</verb></tscreen>
+
+Permite que funcionen un conjunto de <em/tooltips/
+
+<tscreen><verb>
+void gtk_tooltips_disable( GtkTooltips *tooltips );
+</verb></tscreen>
+
+Oculta un conjunto de <em/tooltips/ para que no pueda ser mostrado.
+
+<tscreen><verb>
+void gtk_tooltips_set_delay( GtkTooltips *tooltips,
+ gint delay );
+
+</verb></tscreen>
+
+Establece cuantos milisegundos tiene que estar el puntero sobre el
+<em/widget/ para que aparezca el <em/tooltip/. Por defecto se usan 1000
+milisegundos (1 segundo).
+
+<tscreen><verb>
+void gtk_tooltips_set_colors( GtkTooltips *tooltips,
+ GdkColor *background,
+ GdkColor *foreground );
+</verb></tscreen>
+
+Establece el color del texto y del fondo del <em/tooltip/. No se como
+se especifica el color.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Barras de progreso <label id="sec_ProgressBar">
+<p>
+Estas barras se usan para mostrar el estado de una operación. Son
+bastante sencillas de utilizar, tal y como se verá en los ejemplos
+siguientes. Pero primero vamos a ver cuales son las funciones que hay
+que utilizar para crear una nueva barra de progreso.
+
+Hay dos formas de crear una nueva barra de progreso, la sencilla no
+necesita de argumentos, y la otra recibe un objeto GtkAdjustment. Si
+se utiliza la primera forma, la barra de progreso creará su propio
+GtkAdjustment.
+
+<tscreen><verb>
+GtkWidget *gtk_progress_bar_new( void );
+
+GtkWidget *gtk_progress_bar_new_with_adjustment( GtkAdjustment *adjustment );
+</verb></tscreen>
+
+El segundo método tiene la ventaja de que podemos utilizar el objeto
+adjustment para especificar nuestro propio rango de parámetros para la
+barra de progreso.
+
+El ajuste de una barra de progreso se puede cambiar de forma dinámica
+utilizando:
+
+<tscreen><verb>
+void gtk_progress_set_adjustment( GtkProgress *progress,
+ GtkAdjustment *adjustment );
+</verb></tscreen>
+
+Ahora que hemos creado la barra de progreso ya podemos utilizarla.
+
+<tscreen><verb>
+void gtk_progress_bar_update( GtkProgressBar *pbar,
+ gfloat percentage );
+</verb></tscreen>
+
+El primer argumento es la barra que se quiere manejar, el segundo es
+tanto por ciento que ha sido `completado' (indica cuanto ha sido
+llenada la barra y oscila entre 0-100%). El valor que se le tiene que
+pasar oscila entre 0 y 1.
+
+GTK+ v1.2 ha añadido una nueva característica a la barra de progreso,
+y es que ahora permite mostrar su valor de varias maneras distintas, e
+informar al usuario del valor y rango actual.
+
+Una barra de progreso puede mostrarse con distintas orientaciones
+utilizando la función
+
+<tscreen><verb>
+void gtk_progress_bar_set_orientation( GtkProgressBar *pbar,
+ GtkProgressBarOrientation orientation );
+</verb></tscreen>
+
+Donde el argumento <tt/orientación/ puede tomar uno de los valores que
+vienen a continuación para indicar la dirección en la que se mueve la
+barra de progreso:
+
+<itemize>
+<item> GTK_PROGRESS_LEFT_TO_RIGHT
+<item> GTK_PROGRESS_RIGHT_TO_LEFT
+<item> GTK_PROGRESS_BOTTOM_TO_TOP
+<item> GTK_PROGRESS_TOP_TO_BOTTOM
+</itemize>
+
+Cuando se utiliza como una medida de cuanto se ha completado de un
+proceso, la barra de progreso puede configurarse para que muestre su
+valor de una forma continua o discreta. En modo continuo, la barra de
+progreso se actualiza mediante un número discreto de bloques, el
+número de bloques también es configurable.
+
+Se puede configurar el estilo de la barra de progreso utilizando la
+siguiente función:
+
+<tscreen><verb>
+void gtk_progress_bar_set_bar_style( GtkProgressBar *pbar,
+ GtkProgressBarStyle style );
+</verb></tscreen>
+
+El parámetro <tt/style/ puede tomar uno de los dos valores siguientes:
+
+<itemize>
+<item>GTK_PROGRESS_CONTINUOUS
+<item>GTK_PROGRESS_DISCRETE
+</itemize>
+
+El número de bloques se puede establecer utilizando
+
+<tscreen><verb>
+void gtk_progress_bar_set_discrete_blocks( GtkProgressBar *pbar,
+ guint blocks );
+</verb></tscreen>
+
+La barra de progreso también se puede utilizar, a parte de para
+indicar lo «avanzado» de una tarea, para indicar que hay algún tipo
+de actividad. Esto puede ser útil en situaciones donde no se pueda
+medir el progreso de una tarea con un rango de valores. Para el modo
+actividad, no sirve el estilo de barra que se ha descrito más
+arriba. Este modo hay que seleccionarlo utilizando la siguiente
+función:
+
+<tscreen><verb>
+void gtk_progress_set_activity_mode( GtkProgress *progress,
+ guint activity_mode );
+</verb></tscreen>
+
+El tamaño del paso del indicador de actividad, y el número de bloques
+se indican usando las siguientes funciones:
+
+<tscreen><verb>
+void gtk_progress_bar_set_activity_step( GtkProgressBar *pbar,
+ guint step );
+
+void gtk_progress_bar_set_activity_blocks( GtkProgressBar *pbar,
+ guint blocks );
+</verb></tscreen>
+
+Cuando estamos en modo continuo, la barra de progreso puede mostrar un
+texto configurable dentro la barra misma, utilizando la función
+siguiente:
+
+<tscreen><verb>
+void gtk_progress_set_format_string( GtkProgress *progress,
+ gchar *format);
+</verb></tscreen>
+
+El argumento <tt/format/ es parecido al que se utiliza en una orden
+<tt/printf/ de C. Se pueden utilizar las siguientes opciones para el
+formateado de la cadena:
+
+<itemize>
+<item> %p - porcentaje
+<item> %v - valor
+<item> %l - valor inferior del rango
+<item> %u - valor superior del rango
+</itemize>
+
+Puede activar o desactivar el texto utilizando:
+
+<tscreen><verb>
+void gtk_progress_set_show_text( GtkProgress *progress,
+ gint show_text );
+</verb></tscreen>
+
+El argumento <tt/show_text/ es un valor booleano TRUE/FALSE. La
+apariencia del texto puede modificarse utilizando:
+
+<tscreen><verb>
+void gtk_progress_set_text_alignment( GtkProgress *progress,
+ gfloat x_align,
+ gfloat y_align );
+</verb></tscreen>
+
+Los argumentos <tt/x_align/ y <tt/y_align/ toman un valor entre 0.0 y
+1.0. Este valor indica la posición de la cadena de texto dentro de la
+barra. Si ponemos 0.0 en los dos sitios la cadena de texto aparecerá
+en la esquina superior izquierda; un valor de 0.5 (el que se utiliza
+por defecto) centra el texto, y un valor de 1.0 coloca el texto en la
+esquina inferior derecha.
+
+Se pueden leer los parámetros actuales del texto de un objeto barra
+de progreso utilizando las dos funciones que se muestran a
+continuación. La cadena de carácteres devuelta por estas funciones
+debe liberarse en la aplicación (utilizando la función
+g_free()). Estas funciones devuelven el texto formateado que se
+mostrará en la barra.
+
+<tscreen><verb>
+gchar *gtk_progress_get_current_text( GtkProgress *progress );
+
+gchar *gtk_progress_get_text_from_value( GtkProgress *progress,
+ gfloat value );
+</verb></tscreen>
+
+Hay otra forma de cambiar el rango y el valor de un objeto barra de
+progreso utilizando la función:
+
+<tscreen><verb>
+void gtk_progress_configure( GtkProgress *progress,
+ gfloat value,
+ gfloat min,
+ gfloat max );
+</verb></tscreen>
+
+Esta función proporciona una interfaz sencilla al rango y valor de una
+barra de progreso.
+
+Las funciones restantes se pueden utilizar para obtener y establecer
+el valor actual de una barra de progreso utilizando distintos tipos y
+formatos para el valor.
+
+<tscreen><verb>
+void gtk_progress_set_percentage( GtkProgress *progress,
+ gfloat percentage );
+
+void gtk_progress_set_value( GtkProgress *progress,
+ gfloat value );
+
+gfloat gtk_progress_get_value( GtkProgress *progress );
+
+gfloat gtk_progress_get_current_percentage( GtkProgress *progress );
+
+gfloat gtk_progress_get_percentage_from_value( GtkProgress *progress,
+ gfloat value );
+</verb></tscreen>
+
+Estas funciones son autoexplicatorias. La última función utiliza el
+ajuste de la barra de progreso especificada para calcular el
+porcentaje dentro del rango de valores de la barra.
+
+Las barras de progreso se usan con otras funciones como los tiempos de
+espera (<em/timeouts/), sección <ref id="sec_timeouts"
+name="Tiempos de espera, E/S (I/O) y funciones ociosas (idle)">) para
+crear la ilusión de la multitarea. Todas usan la función
+gtk_progress_bar_update de la misma manera.
+
+Estudiemos un ejemplo de barras de progreso actualizada usando
+tiempos de espera. También se muestra como se debe reestablecer una
+barra.
+
+<tscreen><verb>
+/* comienzo del programa-ejemplo progressbar.c */
+
+#include <gtk/gtk.h>
+
+#include <gtk/gtk.h>
+
+typedef struct _ProgressData {
+ GtkWidget *ventana;
+ GtkWidget *pbar;
+ int timer;
+} ProgressData;
+
+/* Actualiza el valor de la barra de progreso para que
+ * podamos ver algún movimiento */
+gint progress_timeout( gpointer data )
+{
+ gfloat new_val;
+ GtkAdjustment *adj;
+
+ /* Calcula el valor de la barra de progreso utilizando
+ * el rango de valores establecido en el ajuste de la
+ * barra */
+
+ new_val = gtk_progress_get_value( GTK_PROGRESS(data) ) + 1;
+
+ adj = GTK_PROGRESS (data)->adjustment;
+ if (new_val > adj->upper)
+ new_val = adj->lower;
+
+ /* Establece el nuevo valor */
+
+ gtk_progress_set_value (GTK_PROGRESS (data), new_val);
+
+ /* Como esta es una función de espera, devolvemos TRUE
+ * para que continue siendo llamada */
+
+ return(TRUE);
+}
+
+/* Función de llamada que activa/desactiva el texto de dentro
+ * de la barra de progreso */
+void toggle_show_text( GtkWidget *widget,
+ ProgressData *pdata )
+{
+ gtk_progress_set_show_text (GTK_PROGRESS (pdata->pbar),
+ GTK_TOGGLE_BUTTON (widget)->active);
+}
+
+/* Función de llamada que activa/desactiva el modo actividad
+ * de la barra de progreso */
+void toggle_activity_mode( GtkWidget *widget,
+ ProgressData *pdata )
+{
+ gtk_progress_set_activity_mode (GTK_PROGRESS (pdata->pbar),
+ GTK_TOGGLE_BUTTON (widget)->active);
+}
+
+/* Función de llamada que activa/desactiva el modo continuo
+ * de la barra de progreso */
+void set_continuous_mode( GtkWidget *widget,
+ ProgressData *pdata )
+{
+ gtk_progress_bar_set_bar_style (GTK_PROGRESS_BAR (pdata->pbar),
+ GTK_PROGRESS_CONTINUOUS);
+}
+
+/* Función de llamada que activa/desactiva el modo discreto
+ * de la barra de progreso */
+void set_discrete_mode( GtkWidget *widget,
+ ProgressData *pdata )
+{
+ gtk_progress_bar_set_bar_style (GTK_PROGRESS_BAR (pdata->pbar),
+ GTK_PROGRESS_DISCRETE);
+}
+
+/* Libera la memoria y elimina el temporizador */
+void destroy_progress( GtkWidget *widget,
+ ProgressData *pdata)
+{
+ gtk_timeout_remove (pdata->timer);
+ pdata->timer = 0;
+ pdata->ventana = NULL;
+ g_free(pdata);
+ gtk_main_quit();
+}
+
+int main( int argc,
+ char *argv[])
+{
+ ProgressData *pdata;
+ GtkWidget *align;
+ GtkWidget *separator;
+ GtkWidget *table;
+ GtkAdjustment *adj;
+ GtkWidget *boton;
+ GtkWidget *check;
+ GtkWidget *vbox;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ /* Reserva memoria para los datos que se le pasan a las funciones
+ * de llamada */
+ pdata = g_malloc( sizeof(ProgressData) );
+
+ pdata->ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_policy (GTK_WINDOW (pdata->ventana), FALSE, FALSE, TRUE);
+
+ gtk_signal_connect (GTK_OBJECT (pdata->ventana), "destroy",
+ GTK_SIGNAL_FUNC (destroy_progress),
+ pdata);
+ gtk_window_set_title (GTK_WINDOW (pdata->ventana), "GtkProgressBar");
+ gtk_container_set_border_width (GTK_CONTAINER (pdata->ventana), 0);
+
+ vbox = gtk_vbox_new (FALSE, 5);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 10);
+ gtk_container_add (GTK_CONTAINER (pdata->ventana), vbox);
+ gtk_widget_show(vbox);
+
+ /* Crea un objeto de alineamiento centrado */
+ align = gtk_alignment_new (0.5, 0.5, 0, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), align, FALSE, FALSE, 5);
+ gtk_widget_show(align);
+
+ /* Crea un objeto GtkAdjusment para albergar el rango de la barra
+ * de progreso */
+ adj = (GtkAdjustment *) gtk_adjustment_new (0, 1, 150, 0, 0, 0);
+
+ /* Crea la GtkProgressBar utilizando el ajuste */
+ pdata->pbar = gtk_progress_bar_new_with_adjustment (adj);
+
+ /* Establece el formato de la cadena de texto que puede mostrarse
+ * en la barra de progreso:
+ * %p - porcentaje
+ * %v - valor
+ * %l - valor inferior del rango
+ * %u - valor superior del rango */
+ gtk_progress_set_format_string (GTK_PROGRESS (pdata->pbar),
+ "%v from [%l-%u] (=%p%%)");
+ gtk_container_add (GTK_CONTAINER (align), pdata->pbar);
+ gtk_widget_show(pdata->pbar);
+
+ /* Añade un temporizador para la actualización del valor de la
+ * barra de progreso */
+ pdata->timer = gtk_timeout_add (100, progress_timeout, pdata->pbar);
+
+ separator = gtk_hseparator_new ();
+ gtk_box_pack_start (GTK_BOX (vbox), separator, FALSE, FALSE, 0);
+ gtk_widget_show(separator);
+
+ /* filas, columnas, homogéneo */
+ table = gtk_table_new (2, 3, FALSE);
+ gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, TRUE, 0);
+ gtk_widget_show(table);
+
+ /* Añade un botón de comprobación para seleccionar si se debe
+ * mostrar el texto dentro de la barra */
+ check = gtk_check_button_new_with_label ("Show text");
+ gtk_table_attach (GTK_TABLE (table), check, 0, 1, 0, 1,
+ GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL,
+ 5, 5);
+ gtk_signal_connect (GTK_OBJECT (check), "clicked",
+ GTK_SIGNAL_FUNC (toggle_show_text),
+ pdata);
+ gtk_widget_show(check);
+
+ /* Añade un botón de comprobación para activar/desactivar el modo
+ * actividad */
+ check = gtk_check_button_new_with_label ("Activity mode");
+ gtk_table_attach (GTK_TABLE (table), check, 0, 1, 1, 2,
+ GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL,
+ 5, 5);
+ gtk_signal_connect (GTK_OBJECT (check), "clicked",
+ GTK_SIGNAL_FUNC (toggle_activity_mode),
+ pdata);
+ gtk_widget_show(check);
+
+ separator = gtk_vseparator_new ();
+ gtk_table_attach (GTK_TABLE (table), separator, 1, 2, 0, 2,
+ GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL,
+ 5, 5);
+ gtk_widget_show(separator);
+
+ /* Añade un botón circular para seleccionar el modo continuo */
+ boton = gtk_radio_button_new_with_label (NULL, "Continuous");
+ gtk_table_attach (GTK_TABLE (table), boton, 2, 3, 0, 1,
+ GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL,
+ 5, 5);
+ gtk_signal_connect (GTK_OBJECT (boton), "clicked",
+ GTK_SIGNAL_FUNC (set_continuous_mode),
+ pdata);
+ gtk_widget_show (boton);
+
+ /* Añade un botón circular para seleccionar el modo discreto */
+ boton = gtk_radio_button_new_with_label(
+ gtk_radio_button_group (GTK_RADIO_BUTTON (boton)),
+ "Discrete");
+ gtk_table_attach (GTK_TABLE (table), boton, 2, 3, 1, 2,
+ GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL,
+ 5, 5);
+ gtk_signal_connect (GTK_OBJECT (boton), "clicked",
+ GTK_SIGNAL_FUNC (set_discrete_mode),
+ pdata);
+ gtk_widget_show (boton);
+
+ separator = gtk_hseparator_new ();
+ gtk_box_pack_start (GTK_BOX (vbox), separator, FALSE, FALSE, 0);
+ gtk_widget_show(separator);
+
+ /* Añade un botón para salir del programa */
+ boton = gtk_button_new_with_label ("close");
+ gtk_signal_connect_object (GTK_OBJECT (boton), "clicked",
+ (GtkSignalFunc) gtk_widget_destroy,
+ GTK_OBJECT (pdata->ventana));
+ gtk_box_pack_start (GTK_BOX (vbox), boton, FALSE, FALSE, 0);
+
+ /* Esto hace que este botón sea el botón pueda utilizarse por
+ * defecto defecto. */
+ GTK_WIDGET_SET_FLAGS (boton, GTK_CAN_DEFAULT);
+
+ /* Esto marca este botón para que sea el botón por
+ * defecto. Simplemente utilizando la tecla "Intro" haremos que se
+ * active este botón. */
+ gtk_widget_grab_default (boton);
+ gtk_widget_show(boton);
+
+ gtk_widget_show (pdata->ventana);
+
+ gtk_main ();
+
+ return(0);
+}
+/* final del ejemplo */
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Cuadros de diálogo
+<p>
+El <em/widget/ del cuadro de diálogo es bastante simple, sólo es una
+ventana con algunas cosas ya preempaquetadas. Su estructura es la
+siguiente:
+
+<tscreen><verb>
+struct GtkDialog
+{
+ GtkWindow ventana;
+
+ GtkWidget *vbox;
+ GtkWidget *action_area;
+};
+</verb></tscreen>
+
+Simplemente se crea una ventana en la cual se empaqueta una vbox, un
+separador y una hbox llamada «action_area».
+
+Este tipo de <em/widgets/ pueden ser usados como mensages <em/pop-up/
+(pequeñas ventanas con texto en su interior que aparecen cuando el
+usuario hace algo y queremos informarle de alguna cosa) y otras cosas
+parecidas. Su manejo desde el punto de vista del programador
+es bastante fácil, sólo hay que usar una función:
+
+<tscreen><verb>
+GtkWidget *gtk_dialog_new( void );
+</verb></tscreen>
+
+Para crear un nuevo cuadro de diálogo hay que llamar a:
+
+<tscreen><verb>
+GtkWidget *ventana;
+ventana = gtk_dialog_new ();
+</verb></tscreen>
+
+Una vez que el cuadro ha sido creado sólo hay que usarlo. Por
+ejemplo para empaquetar un botón en la action_area escribiríamos
+algo así:
+
+<tscreen><verb>
+boton = ...
+gtk_box_pack_start (GTK_BOX (GTK_DIALOG (ventana)->action_area), boton,
+ TRUE, TRUE, 0);
+gtk_widget_show (boton);
+</verb></tscreen>
+
+Otra cosa que nos puede interesar es empaquetar una etiqueta en la
+vbox:
+
+<tscreen><verb>
+etiqueta = gtk_label_new ("Dialogs are groovy");
+gtk_box_pack_start (GTK_BOX (GTK_DIALOG (ventana)->vbox), etiqueta, TRUE,
+ TRUE, 0);
+gtk_widget_show (etiqueta);
+</verb></tscreen>
+
+Otros ejemplo posible es poner dos botones en el action_area (uno
+para cancelar y el otro para permitir algo) junto con una etiqueta en
+la vbox el usuario puede seleccionar lo que quiera.
+
+Si se precisa algo más complejo siempre se puede empaquetar otro
+<em/widget/ en cualquiera de las cajas (p.j. una tabla en una vbox).
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> <em/Pixmaps/ <label id="sec_Pixmaps">
+<p>
+Los <em/pixmaps/ son estructuras de datos que contienen dibujos. Estos
+pueden ser usados en diferentes lugares, pero los iconos y los
+cursores son los más comunes.
+
+Un <em/bitmap/ es un <em/pixmap/ que sólo tiene dos colores, y hay
+unas cuantas rutinas especiales para controlar este caso particular.
+
+Para comprender los <em/pixmaps/, puede ayudar entender como funciona
+X-windows. Bajo X-windows, las aplicaciones no tienen porque estar
+ejecutándose en el ordenador que está interactuando con el
+usuario. Las distintas aplicaciones, llamadas «clientes», comunican
+con un programa que muestra los gráficos y que controla el tecledo y
+el ratón. Este programa que interactua directamente con el usuario se
+llama un «<em/display server/» o «servidor X». Como la
+comunicación entre el servidor y el cliente puede llevarse a cabo
+mediante una red, es importante mantener alguna información en el
+servidor X. Los <em/pixmaps/ por ejemplo, se almacenan en la memoria
+del servidor X. Esto significa que una vez que se establecen los
+valores del <em/pixmap/, no tienen que estar transmitiéndose por la
+red; en su lugar lo único que hay que enviar es una orden del estilo
+«mostrar <em/pixmap/ número XYZ aquí». Incluso si no está utilizando
+X-windows con GTK, al utilizar construcciones como los <em/pixmaps/
+conseguirá que sus programas funciones de forma aceptable bajo
+X-windows.
+
+Para usar un <em/pixmap/ en GTK primero tiene que construir una
+estructura del tipo GdkPixmap usando rutinas de GDK. Los <em/pixmaps/
+se pueden crear usando datos que se encuentren en la memoria o en un
+archivo. Veremos con detalle cada una de las dos posibilidades.
+
+<tscreen><verb>
+GdkPixmap *gdk_bitmap_create_from_data( GdkWindow *ventana,
+ gchar *data,
+ gint width,
+ gint height );
+</verb></tscreen>
+
+Esta rutina se utiliza para crear un <em/bitmap/ a partir de datos
+almacenados en la memoria. Cada bit de información indica si el
+<em/pixel/ luce o no. Tanto la altura como la anchura estan expresadas
+en <em/pixels/. El puntero del tipo GdkWindow indica la ventana en
+cuestión, ya que los <em/pixmaps/ sólo tienen sentido dentro de
+la pantalla en la que van a ser mostrados.
+
+<tscreen><verb>
+GdkPixmap *gdk_pixmap_create_from_data( GdkWindow *ventana,
+ gchar *data,
+ gint width,
+ gint height,
+ gint depth,
+ GdkColor *fg,
+ GdkColor *bg );
+</verb></tscreen>
+
+Con esto creamos un <em/pixmap/ con la profundidad (número de
+colores) especificada en los datos del <em/bitmap/. Los valores
+<tt/fg/ y <tt/bg/ son los colores del frente y del fondo
+respectivamente.
+
+<tscreen><verb>
+GdkPixmap *gdk_pixmap_create_from_xpm( GdkWindow *ventana,
+ GdkBitmap **mask,
+ GdkColor *transparent_color,
+ const gchar *filename );
+</verb></tscreen>
+
+El formato XPM es una representacion de los <em/pixmaps/ para el
+sistema X Window. Es bastante popular y existen muchos programas para
+crear imágenes en este formato. El archivo especificado mediante
+<tt/filename/ debe contener una imagen en ese formato para que sea
+cargada en la estructura. La máscara especifica que bits son
+opacos. Todos los demás bits se colorean usando el color
+especificado en <tt/transparent_color/. Más adelante veremos un
+ejemplo.
+
+<tscreen><verb>
+GdkPixmap *gdk_pixmap_create_from_xpm_d( GdkWindow *ventana,
+ GdkBitmap **mask,
+ GdkColor *transparent_color,
+ gchar **data );
+</verb></tscreen>
+
+Se pueden incorporar imágenes pequeñas dentro de un programa en
+formato XPM. Un <em/pixmap/ se crea usando esta información, en
+lugar de leerla de un archivo. Un ejemplo sería:
+
+<tscreen><verb>
+/* XPM */
+static const char * xpm_data[] = {
+"16 16 3 1",
+" c None",
+". c #000000000000",
+"X c #FFFFFFFFFFFF",
+" ",
+" ...... ",
+" .XXX.X. ",
+" .XXX.XX. ",
+" .XXX.XXX. ",
+" .XXX..... ",
+" .XXXXXXX. ",
+" .XXXXXXX. ",
+" .XXXXXXX. ",
+" .XXXXXXX. ",
+" .XXXXXXX. ",
+" .XXXXXXX. ",
+" .XXXXXXX. ",
+" ......... ",
+" ",
+" "};
+</verb></tscreen>
+
+Cuando hayamos acabado de usar un <em/pixmap/ y no lo vayamos a usar
+durante un tiempo suele ser conveniente liberar el recurso mediante
+gdk_pixmap_unref(). (Los <em/pixmaps/ deben ser considerados recursos
+preciosos).
+
+Una vez que hemos creado el <em/pixmap/ lo podemos mostrar como un
+<em/widget/ GTK. Primero tenemos que crear un <em/widget pixmap/ que
+contenga un <em/pixmap/ GDK. Esto se hace usando:
+
+<tscreen><verb>
+GtkWidget *gtk_pixmap_new( GdkPixmap *pixmap,
+ GdkBitmap *mask );
+</verb></tscreen>
+
+Las otras funciones del <em/widget pixmap/ son:
+
+<tscreen><verb>
+guint gtk_pixmap_get_type( void );
+
+void gtk_pixmap_set( GtkPixmap *pixmap,
+ GdkPixmap *val,
+ GdkBitmap *mask );
+
+void gtk_pixmap_get( GtkPixmap *pixmap,
+ GdkPixmap **val,
+ GdkBitmap **mask);
+</verb></tscreen>
+
+La función gtk_pixmap_set se usa para cambiar los datos del
+<em/pixmap/ que el <em/widget/ está manejando en ese
+momento. <tt/val/ es el <em/pixmap/ creado usando GDK.
+
+El ejemplo siguiente usa un <em/pixmap/ en un botón:
+
+<tscreen><verb>
+/* comienzo del ejemplo pixmap.c */
+
+#include <gtk/gtk.h>
+
+
+/* Datos en formato XPM del icono de apertura de archivo */
+static const char * xpm_data[] = {
+"16 16 3 1",
+" c None",
+". c #000000000000",
+"X c #FFFFFFFFFFFF",
+" ",
+" ...... ",
+" .XXX.X. ",
+" .XXX.XX. ",
+" .XXX.XXX. ",
+" .XXX..... ",
+" .XXXXXXX. ",
+" .XXXXXXX. ",
+" .XXXXXXX. ",
+" .XXXXXXX. ",
+" .XXXXXXX. ",
+" .XXXXXXX. ",
+" .XXXXXXX. ",
+" ......... ",
+" ",
+" "};
+
+/* Cuando se llama a esta función (usando signal delete_event) se
+ * termina la aplicación*/
+
+void close_application( GtkWidget *widget, GdkEvent *event, gpointer data ) {
+ gtk_main_quit();
+}
+
+
+/* Al presionar el botón aparece el mensaje */
+void button_clicked( GtkWidget *widget, gpointer data ) {
+ printf( "botón pulsado\n" );
+}
+
+int main( int argc, char *argv[] )
+{
+
+ GtkWidget *ventana, *pixmapwid, *boton;
+ GdkPixmap *pixmap;
+ GdkBitmap *mask;
+ GtkStyle *style;
+
+ /* Creamos la ventana principal y relacionamos la señal
+ * delete_event con acabar el programa.*/
+ gtk_init( &amp;argc, &amp;argv );
+ ventana = gtk_window_new( GTK_WINDOW_TOPLEVEL );
+ gtk_signal_connect( GTK_OBJECT (ventana), "delete_event",
+ GTK_SIGNAL_FUNC (close_application), NULL );
+ gtk_container_border_width( GTK_CONTAINER (ventana), 10 );
+ gtk_widget_show( ventana );
+
+ /* Ahora para el pixmap de gdk */
+ style = gtk_widget_get_style( ventana );
+ pixmap = gdk_pixmap_create_from_xpm_d( ventana->window, &amp;mask,
+ &amp;style->bg[GTK_STATE_NORMAL],
+ (gchar **)xpm_data );
+
+ /* Un pixmap widget que contendrá al pixmap */
+ pixmapwid = gtk_pixmap_new( pixmap, mask );
+ gtk_widget_show( pixmapwid );
+
+ /* Un botón para contener al pixmap */
+ boton = gtk_button_new();
+ gtk_container_add( GTK_CONTAINER(boton), pixmapwid );
+ gtk_container_add( GTK_CONTAINER(ventana), boton );
+ gtk_widget_show( boton );
+
+ gtk_signal_connect( GTK_OBJECT(boton), "clicked",
+ GTK_SIGNAL_FUNC(button_clicked), NULL );
+
+ /* mostramos la ventana */
+ gtk_main ();
+
+ return 0;
+}
+/* final del ejemplo */
+</verb></tscreen>
+
+Para cargar un archivo llamado icon0.xpm con la información XPM (que
+se encuentra en en directorio actual) habríamos usado:
+
+<tscreen><verb>
+ /* cargar un pixmap desde un fichero */
+ pixmap = gdk_pixmap_create_from_xpm( ventana->window, &amp;mask,
+ &amp;style->bg[GTK_STATE_NORMAL],
+ "./icon0.xpm" );
+ pixmapwid = gtk_pixmap_new( pixmap, mask );
+ gtk_widget_show( pixmapwid );
+ gtk_container_add( GTK_CONTAINER(ventana), pixmapwid );
+</verb></tscreen>
+
+Una desventaja de los <em/pixmaps/ es que la imagen mostrada siempre
+es rectangular (independientemente de como sea la imagen en sí). Si
+queremos usar imágenes con otras formas debemos usar ventanas con
+forma (<em/shaped windows/).
+
+Este tipo de ventanas son pixmaps en los que el fondo es
+transparente. Así cuando la imagen del fondo tiene muchos colores
+no los sobreescribimos con el borde de nuestro icono. El ejemplo
+siguiente muestra la imagen de una carretilla en el escritorio.
+
+<tscreen><verb>
+/* comienzo del ejemplo carretilla wheelbarrow.c */
+
+#include <gtk/gtk.h>
+
+/* XPM */
+static char * WheelbarrowFull_xpm[] = {
+"48 48 64 1",
+" c None",
+". c #DF7DCF3CC71B",
+"X c #965875D669A6",
+"o c #71C671C671C6",
+"O c #A699A289A699",
+"+ c #965892489658",
+"@ c #8E38410330C2",
+"# c #D75C7DF769A6",
+"$ c #F7DECF3CC71B",
+"% c #96588A288E38",
+"&amp; c #A69992489E79",
+"* c #8E3886178E38",
+"= c #104008200820",
+"- c #596510401040",
+"; c #C71B30C230C2",
+": c #C71B9A699658",
+"> c #618561856185",
+", c #20811C712081",
+"< c #104000000000",
+"1 c #861720812081",
+"2 c #DF7D4D344103",
+"3 c #79E769A671C6",
+"4 c #861782078617",
+"5 c #41033CF34103",
+"6 c #000000000000",
+"7 c #49241C711040",
+"8 c #492445144924",
+"9 c #082008200820",
+"0 c #69A618611861",
+"q c #B6DA71C65144",
+"w c #410330C238E3",
+"e c #CF3CBAEAB6DA",
+"r c #71C6451430C2",
+"t c #EFBEDB6CD75C",
+"y c #28A208200820",
+"u c #186110401040",
+"i c #596528A21861",
+"p c #71C661855965",
+"a c #A69996589658",
+"s c #30C228A230C2",
+"d c #BEFBA289AEBA",
+"f c #596545145144",
+"g c #30C230C230C2",
+"h c #8E3882078617",
+"j c #208118612081",
+"k c #38E30C300820",
+"l c #30C2208128A2",
+"z c #38E328A238E3",
+"x c #514438E34924",
+"c c #618555555965",
+"v c #30C2208130C2",
+"b c #38E328A230C2",
+"n c #28A228A228A2",
+"m c #41032CB228A2",
+"M c #104010401040",
+"N c #492438E34103",
+"B c #28A2208128A2",
+"V c #A699596538E3",
+"C c #30C21C711040",
+"Z c #30C218611040",
+"A c #965865955965",
+"S c #618534D32081",
+"D c #38E31C711040",
+"F c #082000000820",
+" ",
+" .XoO ",
+" +@#$%o&amp; ",
+" *=-;#::o+ ",
+" >,<12#:34 ",
+" 45671#:X3 ",
+" +89<02qwo ",
+"e* >,67;ro ",
+"ty> 459@>+&amp;&amp; ",
+"$2u+ ><ipas8* ",
+"%$;=* *3:.Xa.dfg> ",
+"Oh$;ya *3d.a8j,Xe.d3g8+ ",
+" Oh$;ka *3d$a8lz,,xxc:.e3g54 ",
+" Oh$;kO *pd$%svbzz,sxxxxfX..&amp;wn> ",
+" Oh$@mO *3dthwlsslszjzxxxxxxx3:td8M4 ",
+" Oh$@g&amp; *3d$XNlvvvlllm,mNwxxxxxxxfa.:,B* ",
+" Oh$@,Od.czlllllzlmmqV@V#V@fxxxxxxxf:%j5&amp; ",
+" Oh$1hd5lllslllCCZrV#r#:#2AxxxxxxxxxcdwM* ",
+" OXq6c.%8vvvllZZiqqApA:mq:Xxcpcxxxxxfdc9* ",
+" 2r<6gde3bllZZrVi7S@SV77A::qApxxxxxxfdcM ",
+" :,q-6MN.dfmZZrrSS:#riirDSAX@Af5xxxxxfevo",
+" +A26jguXtAZZZC7iDiCCrVVii7Cmmmxxxxxx%3g",
+" *#16jszN..3DZZZZrCVSA2rZrV7Dmmwxxxx&amp;en",
+" p2yFvzssXe:fCZZCiiD7iiZDiDSSZwwxx8e*>",
+" OA1<jzxwwc:$d%NDZZZZCCCZCCZZCmxxfd.B ",
+" 3206Bwxxszx%et.eaAp77m77mmmf3&amp;eeeg* ",
+" @26MvzxNzvlbwfpdettttttttttt.c,n&amp; ",
+" *;16=lsNwwNwgsvslbwwvccc3pcfu<o ",
+" p;<69BvwwsszslllbBlllllllu<5+ ",
+" OS0y6FBlvvvzvzss,u=Blllj=54 ",
+" c1-699Blvlllllu7k96MMMg4 ",
+" *10y8n6FjvllllB<166668 ",
+" S-kg+>666<M<996-y6n<8* ",
+" p71=4 m69996kD8Z-66698&amp;&amp; ",
+" &amp;i0ycm6n4 ogk17,0<6666g ",
+" N-k-<> >=01-kuu666> ",
+" ,6ky&amp; &amp;46-10ul,66, ",
+" Ou0<> o66y<ulw<66&amp; ",
+" *kk5 >66By7=xu664 ",
+" <<M4 466lj<Mxu66o ",
+" *>> +66uv,zN666* ",
+" 566,xxj669 ",
+" 4666FF666> ",
+" >966666M ",
+" oM6668+ ",
+" *4 ",
+" ",
+" "};
+
+
+
+void close_application( GtkWidget *widget, GdkEvent *event, gpointer data ) {
+ gtk_main_quit();
+}
+
+int main (int argc, char *argv[])
+{
+
+ GtkWidget *ventana, *pixmap, *fixed;
+ GdkPixmap *gdk_pixmap;
+ GdkBitmap *mask;
+ GtkStyle *style;
+ GdkGC *gc;
+
+ /* Creamos la ventana principal y relacionamos la señal
+ * delete_event para terminar la aplicación. Conviene destacar
+ * que la ventana no tendrá título puesto que es popup.*/
+ gtk_init (&amp;argc, &amp;argv);
+ ventana = gtk_window_new( GTK_WINDOW_POPUP );
+ gtk_signal_connect (GTK_OBJECT (ventana), "delete_event",
+ GTK_SIGNAL_FUNC (close_application), NULL);
+ gtk_widget_show (ventana);
+
+ style = gtk_widget_get_default_style();
+ gc = style->black_gc;
+ gdk_pixmap = gdk_pixmap_create_from_xpm_d( ventana->window, &amp;mask,
+ &amp;style->bg[GTK_STATE_NORMAL],
+ WheelbarrowFull_xpm );
+ pixmap = gtk_pixmap_new( gdk_pixmap, mask );
+ gtk_widget_show( pixmap );
+
+
+ fixed = gtk_fixed_new();
+ gtk_widget_set_usize( fixed, 200, 200 );
+ gtk_fixed_put( GTK_FIXED(fixed), pixmap, 0, 0 );
+ gtk_container_add( GTK_CONTAINER(ventana), fixed );
+ gtk_widget_show( fixed );
+
+ /* Con esto cubrimos todo menos la imagen */
+ gtk_widget_shape_combine_mask( ventana, mask, 0, 0 );
+
+ /* mostramos la ventana */
+ gtk_widget_set_uposition( ventana, 20, 400 );
+ gtk_widget_show( ventana );
+ gtk_main ();
+
+ return 0;
+}
+/* final del ejemplo */
+</verb></tscreen>
+
+Para que la carretilla sea más realista podríamos relacionar la
+pulsación del botón con que haga algo. Con las líneas
+siguientes la pulsación del botón hace que se acabe el programa.
+
+<tscreen><verb>
+gtk_widget_set_events( ventana,
+ gtk_widget_get_events( ventana ) |
+ GDK_BUTTON_PRESS_MASK );
+
+gtk_signal_connect( GTK_OBJECT(ventana), "button_press_event",
+ GTK_SIGNAL_FUNC(close_application), NULL );
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Reglas
+<p>
+
+Las reglas son usadas para indicar la posición del puntero del
+ratón en una ventana dada. Una ventana puede tener una regla
+vertical a lo largo de su alto y una horizontal a lo largo de su
+ancho. Un pequeño indicador triangular muestra la relación entre
+el puntero del ratón y la regla.
+
+Las reglas (horizontales y verticales) se crean usando:
+
+<tscreen><verb>
+GtkWidget *gtk_hruler_new( void ); /* horizontal */
+GtkWidget *gtk_vruler_new( void ); /* vertical */
+</verb></tscreen>
+
+Las unidades de la regla pueden ser pixels, pulgadas o centímetros
+(GKD_PIXELS, GDK_INCHES, GDK_CENTIMETRES). Esto se hace usando:
+
+<tscreen><verb>
+void gtk_ruler_set_metric( GtkRuler *ruler,
+ GtkMetricType metric );
+</verb></tscreen>
+
+El valor por defecto es GTK_PIXELS.
+
+<tscreen><verb>
+gtk_ruler_set_metric( GTK_RULER(ruler), GTK_PIXELS );
+</verb></tscreen>
+
+Otra característica importante de las reglas es cómo mostrar las
+unidades de escala y la posicion inicial dónde se situa el indicador.
+Todo esto se consigue mediante:
+
+<tscreen><verb>
+void gtk_ruler_set_range( GtkRuler *ruler,
+ gfloat lower,
+ gfloat upper,
+ gfloat posicion,
+ gfloat max_size );
+</verb></tscreen>
+
+Los argumentos <tt/lower/ (valor más bajo) y <tt/upper/ (más
+alto) delimitan la extensión de la regla. El argumento
+<tt/max_size/ es el número más alto que será mostrado. Como
+es lógico <tt/posicion/ define la posición inicial del indicador
+dentro de la regla.
+
+Una regla vertical puede puede llegar a ser de 800 pixels:
+
+<tscreen><verb>
+gtk_ruler_set_range( GTK_RULER(vruler), 0, 800, 0, 800);
+</verb></tscreen>
+
+Las marcas dentro de la regla oscilarán entre 0 y 800 con una
+periodicidad de 100. Si queremos que varíe entre 7 y 16
+debemos usar:
+
+<tscreen><verb>
+gtk_ruler_set_range( GTK_RULER(vruler), 7, 16, 0, 20);
+</verb></tscreen>
+
+El indicador de la regla es un pequeño triángulo que señala la
+posición del puntero con relación a la regla. Si la regla debe
+seguir al puntero del ratón la señal motion_notify_event debe estar
+conectada con el motion_notify_event de la regla. Para seguir todos
+los movimientos dentro de una ventana conviene usar:
+
+<tscreen><verb>
+#define EVENT_METHOD(i, x) GTK_WIDGET_CLASS(GTK_OBJECT(i)->klass)->x
+
+gtk_signal_connect_object( GTK_OBJECT(area), "motion_notify_event",
+ (GtkSignalFunc)EVENT_METHOD(ruler, motion_notify_event),
+ GTK_OBJECT(ruler) );
+</verb></tscreen>
+
+El siguiente ejemplo crea una zona de dibujo con una regla horizontal
+y otra vertical. El tamaño de la zona de dibujo es de 600 x 400
+<em/pixels/. La regla horizontal oscila entre 7 y 13 con marcas cada
+100 <em/pixels/, mientras que la vertical va desde 0 a 400 con
+separaciones cada 100. La zona de dibujo y las reglas se sitúan
+usando una tabla.
+
+<tscreen><verb>
+/* comienzo del ejemplo rulers.c */
+
+#include <gtk/gtk.h>
+
+#define EVENT_METHOD(i, x) GTK_WIDGET_CLASS(GTK_OBJECT(i)->klass)->x
+
+#define XSIZE 600
+#define YSIZE 400
+
+/* Esta rutina toma el control cuando se pulsa el botón close
+ */
+void close_application( GtkWidget *widget, GdkEvent *event, gpointer data ) {
+ gtk_main_quit();
+}
+
+int main( int argc, char *argv[] ) {
+ GtkWidget *ventana, *table, *area, *hrule, *vrule;
+
+
+ gtk_init( &amp;argc, &amp;argv );
+
+ ventana = gtk_window_new( GTK_WINDOW_TOPLEVEL );
+ gtk_signal_connect (GTK_OBJECT (ventana), "delete_event",
+ GTK_SIGNAL_FUNC( close_application ), NULL);
+ gtk_container_border_width (GTK_CONTAINER (ventana), 10);
+
+ /* creación de la tabla donde pondremos las reglas y la zona de
+ * dibujo */
+ table = gtk_table_new( 3, 2, FALSE );
+ gtk_container_add( GTK_CONTAINER(ventana), table );
+
+ area = gtk_drawing_area_new();
+ gtk_drawing_area_size( (GtkDrawingArea *)area, XSIZE, YSIZE );
+ gtk_table_attach( GTK_TABLE(table), area, 1, 2, 1, 2,
+ GTK_EXPAND|GTK_FILL, GTK_FILL, 0, 0 );
+ gtk_widget_set_events( area, GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK );
+
+ /* La regla horizontal está arriba. Cuando el ratón se mueve
+ * a lo largo de la zona de dibujo el controlador de eventos de la
+ * regla recibe motion_notify_event. */
+ hrule = gtk_hruler_new();
+ gtk_ruler_set_metric( GTK_RULER(hrule), GTK_PIXELS );
+ gtk_ruler_set_range( GTK_RULER(hrule), 7, 13, 0, 20 );
+ gtk_signal_connect_object( GTK_OBJECT(area), "motion_notify_event",
+ (GtkSignalFunc)EVENT_METHOD(hrule,
+ motion_notify_event),
+ GTK_OBJECT(hrule) );
+ /* GTK_WIDGET_CLASS(GTK_OBJECT(hrule)->klass)->motion_notify_event, */
+ gtk_table_attach( GTK_TABLE(table), hrule, 1, 2, 0, 1,
+ GTK_EXPAND|GTK_SHRINK|GTK_FILL, GTK_FILL, 0, 0 );
+
+
+ /* la zona de dibujo el controlador de eventos de la regla recibe
+ * motion_notify_event. */
+ vrule = gtk_vruler_new();
+ gtk_ruler_set_metric( GTK_RULER(vrule), GTK_PIXELS );
+ gtk_ruler_set_range( GTK_RULER(vrule), 0, YSIZE, 10, YSIZE );
+ gtk_signal_connect_object( GTK_OBJECT(area), "motion_notify_event",
+ (GtkSignalFunc)
+ GTK_WIDGET_CLASS(GTK_OBJECT(vrule)->klass)->
+ motion_notify_event,
+ GTK_OBJECT(vrule) );
+ gtk_table_attach( GTK_TABLE(table), vrule, 0, 1, 1, 2,
+ GTK_FILL, GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0 );
+
+
+ /* mostramos todo */
+ gtk_widget_show( area );
+ gtk_widget_show( hrule );
+ gtk_widget_show( vrule );
+ gtk_widget_show( table );
+ gtk_widget_show( ventana );
+ gtk_main();
+
+ return 0;
+}
+/* final del ejemplo */
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Barras de estado
+<p>
+Las barras de estado son widgets usados para mostrar un mensaje. Todo
+aquello que haya sido mostrado se guarda en una pila, con lo que es
+muy fácil repetir el último mensaje.
+
+Para permitir que diferentes partes del programa usen la misma barra
+de estado éstas usan Identificadores por Contexto (Context
+Identifiers) para identificar a los `usuarios'. El mensaje que está
+en lo alto de la pila será el siguiente en mostrarse, sin importar
+el contexto en el que se esté. Los mensajes se almacenan en el
+orden el último en entrar es el primero en salir, y el
+Identificador por Contexto no influye en este orden.
+
+Las barras de estado se crean con una llamada a:
+
+<tscreen><verb>
+GtkWidget *gtk_statusbar_new( void );
+</verb></tscreen>
+
+Se pide un nuevo Identificador por Contexto con una pequeña
+descripción textual del contexto y una llamada a la función:
+
+<tscreen><verb>
+guint gtk_statusbar_get_context_id( GtkStatusbar *statusbar,
+ const gchar *context_description );
+</verb></tscreen>
+
+Hay tres funciones que pueden manipular las barras de estado:
+
+<tscreen><verb>
+guint gtk_statusbar_push( GtkStatusbar *statusbar,
+ guint context_id,
+ gchar *text );
+
+void gtk_statusbar_pop( GtkStatusbar *statusbar)
+ guint context_id );
+
+void gtk_statusbar_remove( GtkStatusbar *statusbar,
+ guint context_id,
+ guint message_id );
+</verb></tscreen>
+
+La primera, gtk_statusbar_push, se utiliza para añadir un nuevo
+mensaje a la barra de estado. Devuelve un Identificador de Mensaje,
+que podemos pasarle más tarde a la función gtk_statusbar_remove para
+eliminar el mensaje con los Identificadores de Contexto y de Mensaje
+que hay en la pila de barras de estado.
+
+La función gtk_statusbar_pop elimina el mensaje que se encuentra
+más alto en pila y que contiene el Identificador por Contexto
+especificado.
+
+El ejemplo siguiente crea una barra de estado y dos botones, uno para
+meter un elemento en la barra y el otro para sacar el último elemento
+introducido.
+
+<tscreen><verb>
+/* Principio del ejemplo de barras de estado statusbar.c */
+
+#include <gtk/gtk.h>
+#include <glib.h>
+
+GtkWidget *status_bar;
+
+void push_item (GtkWidget *widget, gpointer data)
+{
+ static int count = 1;
+ char buff[20];
+
+ g_snprintf(buff, 20, "Item %d", count++);
+ gtk_statusbar_push( GTK_STATUSBAR(status_bar), GPOINTER_TO_INT(data), buff);
+
+ return;
+}
+
+void pop_item (GtkWidget *widget, gpointer data)
+{
+ gtk_statusbar_pop( GTK_STATUSBAR(status_bar), GPOINTER_TO_INT(data) );
+ return;
+}
+
+int main (int argc, char *argv[])
+{
+
+ GtkWidget *ventana;
+ GtkWidget *vbox;
+ GtkWidget *boton;
+
+ int context_id;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ /* crear una nueva ventana */
+ ventana = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_widget_set_usize( GTK_WIDGET (ventana), 200, 100);
+ gtk_window_set_title(GTK_WINDOW (ventana), "GTK Statusbar Example");
+ gtk_signal_connect(GTK_OBJECT (ventana), "delete_event",
+ (GtkSignalFunc) gtk_exit, NULL);
+
+ vbox = gtk_vbox_new(FALSE, 1);
+ gtk_container_add(GTK_CONTAINER(ventana), vbox);
+ gtk_widget_show(vbox);
+
+ status_bar = gtk_statusbar_new();
+ gtk_box_pack_start (GTK_BOX (vbox), status_bar, TRUE, TRUE, 0);
+ gtk_widget_show (status_bar);
+
+ context_id = gtk_statusbar_get_context_id(
+ GTK_STATUSBAR(status_bar), "Statusbar example");
+
+ boton = gtk_button_new_with_label("push item");
+ gtk_signal_connect(GTK_OBJECT(boton), "clicked",
+ GTK_SIGNAL_FUNC (push_item), &amp;context_id);
+ gtk_box_pack_start(GTK_BOX(vbox), boton, TRUE, TRUE, 2);
+ gtk_widget_show(boton);
+
+ boton = gtk_button_new_with_label("pop last item");
+ gtk_signal_connect(GTK_OBJECT(boton), "clicked",
+ GTK_SIGNAL_FUNC (pop_item), &amp;context_id);
+ gtk_box_pack_start(GTK_BOX(vbox), boton, TRUE, TRUE, 2);
+ gtk_widget_show(boton);
+
+ /* siempre mostramos la ventana en el último paso para que todo se
+ * dibuje en la pantalla de un golpe. */
+ gtk_widget_show(ventana);
+
+ gtk_main ();
+
+ return 0;
+}
+/* Final del ejemplo */
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Entrada de texto
+<p>
+El <em/widget/ Entry permite mostrar e introducir texto en una línea
+de un cuadro de diálogo. El texto se puede poner con llamadas a funciones
+que permiten reemplazar, preañadir o añadir el texto al contenido
+actual del <em/widget/ Entry.
+
+Hay dos funciones para crear un <em/widget/ Entry:
+
+<tscreen><verb>
+GtkWidget *gtk_entry_new( void );
+
+GtkWidget *gtk_entry_new_with_max_length( guint16 max );
+</verb></tscreen>
+
+La primera sirve para crear un nuevo <em/widget/ Entry, mientras que la
+segunda crea el <em/widget/ y además establece un límite en la longitud
+del texto que irá en el mismo.
+
+hay varias funciones que sirven para alterar el que texto que se está
+en el <em/widget/ Entry.
+
+<tscreen><verb>
+void gtk_entry_set_text( GtkEntry *entry,
+ const gchar *text );
+
+void gtk_entry_append_text( GtkEntry *entry,
+ const gchar *text );
+
+void gtk_entry_prepend_text( GtkEntry *entry,
+ const gchar *text );
+</verb></tscreen>
+
+La función <tt/gtk_entry_set_text/ cambia el contenido actual del
+<em/widget/ Entry. Las funciones <tt/gtk_entry_append_text/ y
+<tt/gtk_entry_prepend_text/ permiten añadir o preañadir texto.
+
+Las función siguientes permiten decir donde poner el punto de inserción.
+
+<tscreen><verb>
+void gtk_entry_set_position( GtkEntry *entry,
+ gint posicion );
+</verb></tscreen>
+
+Se pueden obtener los contenidos del <em/widget/ llamando a la función
+que se describe a continuación. Obtener los contenidos del <em/widget/
+puede ser útil en las funciones de llamada descritas más adelante.
+
+<tscreen><verb>
+gchar *gtk_entry_get_text( GtkEntry *entry );
+</verb></tscreen>
+
+Si quiere impedir que alguien cambie el contenido del <em/widget/ escribiendo
+en él, utilice la función
+
+<tscreen><verb>
+void gtk_entry_set_editable( GtkEntry *entry,
+ gboolean editable );
+</verb></tscreen>
+
+Esta función permite camiar el estado de edición de un <em/widget/ Entry,
+siendo el argumento <tt/editable/ TRUE o FALSE.
+
+Si estamos utilizando el <em/widget/ Entry en un sitio donde no queremos
+que el texto que se introduce sea visible, como por ejemplo cuando estamos
+introduciendo una clave, podemos utilizar la función siguiente, que también
+admite como argumento una bandera booleana.
+
+<tscreen><verb>
+void gtk_entry_set_visibility( GtkEntry *entry,
+ gboolean visible );
+</verb></tscreen>
+
+Se puede seleccionar una región del texto utilizando la siguiente función.
+Esta función se puede utilizar después de poner algún texto por defecto en
+el <em/widget/, haciéndole fácil al usuario eliminar este texto.
+
+<tscreen><verb>
+void gtk_entry_select_region( GtkEntry *entry,
+ gint start,
+ gint end );
+</verb></tscreen>
+
+Si queremos saber el momento en el que el usuario ha introducido el texto,
+podemos conectar con las señales <tt/activate/ o <tt/changed/. <tt/activate/
+se activa cuando el usuario aprieta la tecla enter en el <em/widget/.
+<tt/changed/ se activa cuando cambia algo del texto, p.e. cuando se introduce
+o se elimina algún carácter.
+
+<tscreen><verb>
+/* Principio del ejemplo entry entry.c */
+
+#include <gtk/gtk.h>
+
+void enter_callback(GtkWidget *widget, GtkWidget *entry)
+{
+ gchar *entry_text;
+ entry_text = gtk_entry_get_text(GTK_ENTRY(entry));
+ printf("Entry contents: %s\n", entry_text);
+}
+
+void entry_toggle_editable (GtkWidget *checkbutton,
+ GtkWidget *entry)
+{
+ gtk_entry_set_editable(GTK_ENTRY(entry),
+ GTK_TOGGLE_BUTTON(checkbutton)->active);
+}
+
+void entry_toggle_visibility (GtkWidget *checkbutton,
+ GtkWidget *entry)
+{
+ gtk_entry_set_visibility(GTK_ENTRY(entry),
+ GTK_TOGGLE_BUTTON(checkbutton)->active);
+}
+
+int main (int argc, char *argv[])
+{
+
+ GtkWidget *ventana;
+ GtkWidget *vbox, *hbox;
+ GtkWidget *entry;
+ GtkWidget *boton;
+ GtkWidget *check;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ /* crear una nueva ventana */
+ ventana = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_widget_set_usize( GTK_WIDGET (ventana), 200, 100);
+ gtk_window_set_title(GTK_WINDOW (ventana), "GTK Entry");
+ gtk_signal_connect(GTK_OBJECT (ventana), "delete_event",
+ (GtkSignalFunc) gtk_exit, NULL);
+
+ vbox = gtk_vbox_new (FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (ventana), vbox);
+ gtk_widget_show (vbox);
+
+ entry = gtk_entry_new_with_max_length (50);
+ gtk_signal_connect(GTK_OBJECT(entry), "activate",
+ GTK_SIGNAL_FUNC(enter_callback),
+ entry);
+ gtk_entry_set_text (GTK_ENTRY (entry), "hello");
+ gtk_entry_append_text (GTK_ENTRY (entry), " world");
+ gtk_entry_select_region (GTK_ENTRY (entry),
+ 0, GTK_ENTRY(entry)->text_length);
+ gtk_box_pack_start (GTK_BOX (vbox), entry, TRUE, TRUE, 0);
+ gtk_widget_show (entry);
+
+ hbox = gtk_hbox_new (FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (vbox), hbox);
+ gtk_widget_show (hbox);
+
+ check = gtk_check_button_new_with_label("Editable");
+ gtk_box_pack_start (GTK_BOX (hbox), check, TRUE, TRUE, 0);
+ gtk_signal_connect (GTK_OBJECT(check), "toggled",
+ GTK_SIGNAL_FUNC(entry_toggle_editable), entry);
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(check), TRUE);
+ gtk_widget_show (check);
+
+ check = gtk_check_button_new_with_label("Visible");
+ gtk_box_pack_start (GTK_BOX (hbox), check, TRUE, TRUE, 0);
+ gtk_signal_connect (GTK_OBJECT(check), "toggled",
+ GTK_SIGNAL_FUNC(entry_toggle_visibility), entry);
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(check), TRUE);
+ gtk_widget_show (check);
+
+ boton = gtk_button_new_with_label ("Close");
+ gtk_signal_connect_object (GTK_OBJECT (boton), "clicked",
+ GTK_SIGNAL_FUNC(gtk_exit),
+ GTK_OBJECT (ventana));
+ gtk_box_pack_start (GTK_BOX (vbox), boton, TRUE, TRUE, 0);
+ GTK_WIDGET_SET_FLAGS (boton, GTK_CAN_DEFAULT);
+ gtk_widget_grab_default (boton);
+ gtk_widget_show (boton);
+
+ gtk_widget_show(ventana);
+
+ gtk_main();
+ return(0);
+}
+/* fin del ejemplo */
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Botones <em/spin/
+<p>
+El <em/widget/ botón <em/spin/ se utiliza normalmente para permitir
+que el usuario elija un valor de un rango de valores. Consiste en una
+caja para la entrada de texto con una flecha para arriba y otra para
+abajo justo al lado de la caja. Si utilizamos alguna de las flechas
+haremos que el valor suba o baje dentro del rango de los valores
+posibles. También podemos introducir directamente un valor específico
+(utilizando la caja de texto).
+
+El <em/widget/ botón <em/spin/ permite tener valores con un número de
+cifras decimales (o sin cifras decimales) y la posibilidad de
+incrementarlo/decrementarlo en pasos configurables. La acción de
+matener pulsado uno de los botones puede resultar (es opcional) en una
+aceleración del cambio en el valor de acuerdo con el tiempo que se
+mantenga pulsado.
+
+El botón <em/spin/ utiliza un objeto <ref id="sec_Adjustment"
+name="Ajuste"> para conservar la información referente al rango de
+valores que puede tomar el botón <em/spin/. Esto hace que el
+<em/widget/ botón <em/spin/ sea muy poderoso.
+
+Recuerde que un <em/widget/ ajuste puede crearse con la siguiente
+función, que ilustra la información que se guarda:
+
+<tscreen><verb>
+GtkObject *gtk_adjustment_new( gfloat valor,
+ gfloat inferior,
+ gfloat superior,
+ gfloat paso,
+ gfloat incremento_pagina,
+ gfloat tamano_pagina );
+</verb></tscreen>
+
+Estos atributos de un ajuste se utilizan en un botón <em/spin/ de la
+forma siguiente:
+
+<itemize>
+<item> <tt/valor/: valor inicial del botón <em/spin/
+<item> <tt/inferior/: valor inferior del rango
+<item> <tt/superior/: valor superior del rango
+<item> <tt/paso/: valor a incrementar/decrementar cuando pulsemos el
+botón 1 en una flecha
+<item> <tt/incremento_pagina/: valor a incrementar/decrementar cuando
+pulsemos el botón 2 en una flecha
+<item> <tt/tamano_pagina/: no se utiliza
+</itemize>
+
+Además, se puede utilizar el botón 3 para saltar directamente a los
+valores <tt/superior/ o <tt/inferior/ cuando se pulsa en una de las
+flechas. Veamos como crear un botón <em/spin/:
+
+<tscreen><verb>
+GtkWidget *gtk_spin_button_new( GtkAdjustment *ajuste,
+ gfloat aceleracion,
+ guint digitos );
+</verb></tscreen>
+
+El argumento <tt/aceleracion/ toma un valor entre 0.0 y 1.0 e indica
+la aceleración que tendrá el botón <em/spin/. El argumento
+<tt/digitos/ especifica el número de cifras decimales con que se
+mostrará el valor.
+
+Se puede reconfigurar un botón <em/spin/ después de su creación
+utilizando la función:
+
+<tscreen><verb>
+void gtk_spin_button_configure( GtkSpinButton *boton_spin,
+ GtkAdjustment *ajuste,
+ gfloat aceleracion,
+ guint digitos );
+</verb></tscreen>
+
+El argumento <tt/boton_spin/ especifica el botón <em/spin/ que va a
+reconfigurarse. El resto de argumentos son los que acabamos de
+explicar.
+
+Podemos establecer y obtener el ajuste utilizando las dos funciones
+siguientes:
+
+<tscreen><verb>
+void gtk_spin_button_set_adjustment( GtkSpinButton *boton_spin,
+ GtkAdjustment *ajuste );
+
+GtkAdjustment *gtk_spin_button_get_adjustment( GtkSpinButton *boton_spin );
+</verb></tscreen>
+
+El número de cifras decimales también puede alterarse utilizando:
+
+<tscreen><verb>
+void gtk_spin_button_set_digits( GtkSpinButton *boton_spin,
+ guint digitos) ;
+</verb></tscreen>
+
+El valor que un botón <em/spin/ está mostrando actualmente puede
+cambiarse utilizando las siguientes funciones:
+
+<tscreen><verb>
+void gtk_spin_button_set_value( GtkSpinButton *boton_spin,
+ gfloat valor );
+</verb></tscreen>
+
+El valor actual de un botón <em/spin/ puede obtenerse como un entero o
+como un flotante con las funciones siguientes:
+
+<tscreen><verb>
+gfloat gtk_spin_button_get_value_as_float( GtkSpinButton *boton_spin );
+
+gint gtk_spin_button_get_value_as_int( GtkSpinButton *boton_spin );
+</verb></tscreen>
+
+Si quiere alterar el valor de un <em/spin/ de forma relativa a su
+valor actual, puede utilizar la siguiente función:
+
+<tscreen><verb>
+void gtk_spin_button_spin( GtkSpinButton *boton_spin,
+ GtkSpinType direccion,
+ gfloat incremento );
+</verb></tscreen>
+
+El parámetro <tt/direccion/ puede tomar uno de los valores siguientes:
+
+<itemize>
+<item> GTK_SPIN_STEP_FORWARD
+<item> GTK_SPIN_STEP_BACKWARD
+<item> GTK_SPIN_PAGE_FORWARD
+<item> GTK_SPIN_PAGE_BACKWARD
+<item> GTK_SPIN_HOME
+<item> GTK_SPIN_END
+<item> GTK_SPIN_USER_DEFINED
+</itemize>
+
+Trataré de explicar todas las posibilidades que ofrece esta
+función. Algunos de los valores que puede utilizar <tt/direccion/
+hacen que se utilicen valores que están almacenados en el objeto
+Ajuste que está asociado con el botón <em/spin/.
+
+GTK_SPIN_STEP_FORWARD y GTK_SPIN_STEP_BACKWARD aumentan o disminuyen
+(respectivamente) el valor del botón <em/spin/ por la cantidad
+especificada por <tt/incremento/, a menos que <tt/incremento/ sea
+igual a 0, en cuyo caso el valor se aumentará o disminuirá por el
+valor especificado en <tt/paso/ dentro del Ajuste.
+
+GTK_SPIN_PAGE_FORWARD y GTK_SPIN_PAGE_BACKWARD sencillamente alteran
+el valor del botón <em/spin/ por la cantidad <tt/incremento/.
+
+GTK_SPIN_HOME hace que el botón <em/spin/ tenga el mismo valor que el
+valor inferior del rango Ajuste.
+
+GTK_SPIN_END hace que el botón <em/spin/ tenga el mismo valor que el
+valor superior del rango Ajuste.
+
+GTK_SPIN_USER_DEFINED cambia el valor del botón <em/spin/ por la
+cantidad especificada.
+
+Ahora vamos a dejar de lado las funciones para establecer y obtener el
+rango de los atributos del botón <em/spin/, y vamos a pasar a las
+funciones que afectan a la apariencia y al comportamiento del
+<em/widget/ botón <em/spin/ en sí mismo.
+
+La primera de estas funciones se utiliza para restringir el contenido
+de la caja de texto de un botón <em/spin/ a un valor numérico. Esto
+evita que un usuario introduzca cualquier valor no númerico.
+
+<tscreen><verb>
+void gtk_spin_button_set_numeric( GtkSpinButton *boton_spin,
+ gboolean numerico );
+</verb></tscreen>
+
+Puede indicar si un botón <em/spin/ pasará del límite superior al
+inferior utilizando la siguiente función:
+
+<tscreen><verb>
+void gtk_spin_button_set_wrap( GtkSpinButton *boton_spin,
+ gboolean wrap );
+</verb></tscreen>
+
+Puede hacer que un botón <em/spin/ redondee su valor al <tt/paso/ más
+cercano, que se indica cuando creamos el Ajuste que se utiliza con el
+botón <em/spin/. Para hacer que redondee tenemos que utilizar la
+función siguiente:
+
+<tscreen><verb>
+void gtk_spin_button_set_snap_to_ticks( GtkSpinButton *boton_spin,
+ gboolean redondear );
+</verb></tscreen>
+
+Para política de actualización de un botón <em/spin/ puede cambiarse
+con la siguiente función:
+
+<tscreen><verb>
+void gtk_spin_button_set_update_policy( GtkSpinButton *boton_spin,
+ GtkSpinButtonUpdatePolicy politica );
+</verb></tscreen>
+
+<!-- TODO: find out what this does - TRG -->
+
+Los valores posibles de <tt/politica/ son o GTK_UPDATE_ALWAYS o
+GTK_UPDATE_IF_VALID.
+
+Estas políticas afectan al comportamiento de un botón <em/spin/ cuando
+se lee el texto insertado en la caja de texto y se sincroniza con los
+valores del Ajuste.
+
+En el caso de GTK_UPDATE_IF_VALID el valor de un botón <em/spin/
+cambiará si el texto introducido es un valor numérico contenido dentro
+del rango especificado por el Ajuste. En caso contrario el texto
+introducido se convierte al valor del botón <em/spin/.
+
+En caso de utilizar GTK_UPDATE_ALWAYS se ignorarán los errores que
+puedan ocurrir en la conversión del texto en un valor numérico.
+
+El aspecto de los botones utilizados en un botón <em/spin/ pueden
+cambiarse utilizando las siguientes funciones:
+
+<tscreen><verb>
+void gtk_spin_button_set_shadow_type( GtkSpinButton *boton_spin,
+ GtkShadowType tipo_sombra );
+</verb></tscreen>
+
+Como siempre, el <tt/tipo_sombra/ puede ser uno de los siguientes:
+
+<itemize>
+<item> GTK_SHADOW_IN
+<item> GTK_SHADOW_OUT
+<item> GTK_SHADOW_ETCHED_IN
+<item> GTK_SHADOW_ETCHED_OUT
+</itemize>
+
+Finalmente, puede pedir de forma explícita que un botón <em/spin/ se
+actualice a sí mismo:
+
+<tscreen><verb>
+void gtk_spin_button_update( GtkSpinButton *boton_spin );
+</verb></tscreen>
+
+Es hora de un nuevo ejemplo.
+
+<tscreen><verb>
+/* principio del ejemplo spinbutton spinbutton.c */
+
+#include <gtk/gtk.h>
+
+static GtkWidget *spinner1;
+
+void toggle_snap( GtkWidget *widget,
+ GtkSpinButton *spin )
+{
+ gtk_spin_button_set_snap_to_ticks (spin, GTK_TOGGLE_BUTTON (widget)->active);
+}
+
+void toggle_numeric( GtkWidget *widget,
+ GtkSpinButton *spin )
+{
+ gtk_spin_button_set_numeric (spin, GTK_TOGGLE_BUTTON (widget)->active);
+}
+
+void change_digits( GtkWidget *widget,
+ GtkSpinButton *spin )
+{
+ gtk_spin_button_set_digits (GTK_SPIN_BUTTON (spinner1),
+ gtk_spin_button_get_value_as_int (spin));
+}
+
+void get_value( GtkWidget *widget,
+ gpointer data )
+{
+ gchar buf[32];
+ GtkLabel *etiqueta;
+ GtkSpinButton *spin;
+
+ spin = GTK_SPIN_BUTTON (spinner1);
+ etiqueta = GTK_LABEL (gtk_object_get_user_data (GTK_OBJECT (widget)));
+ if (GPOINTER_TO_INT (data) == 1)
+ sprintf (buf, "%d", gtk_spin_button_get_value_as_int (spin));
+ else
+ sprintf (buf, "%0.*f", spin->digits,
+ gtk_spin_button_get_value_as_float (spin));
+ gtk_label_set_text (etiqueta, buf);
+}
+
+
+int main( int argc,
+ char *argv[] )
+{
+ GtkWidget *ventana;
+ GtkWidget *frame;
+ GtkWidget *hbox;
+ GtkWidget *main_vbox;
+ GtkWidget *vbox;
+ GtkWidget *vbox2;
+ GtkWidget *spinner2;
+ GtkWidget *spinner;
+ GtkWidget *boton;
+ GtkWidget *etiqueta;
+ GtkWidget *val_label;
+ GtkAdjustment *adj;
+
+ /* Inicializar GTK */
+ gtk_init(&amp;argc, &amp;argv);
+
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
+ GTK_SIGNAL_FUNC (gtk_main_quit),
+ NULL);
+
+ gtk_window_set_title (GTK_WINDOW (ventana), "Spin Button");
+
+ main_vbox = gtk_vbox_new (FALSE, 5);
+ gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 10);
+ gtk_container_add (GTK_CONTAINER (ventana), main_vbox);
+
+ frame = gtk_frame_new ("Not accelerated");
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, TRUE, TRUE, 0);
+
+ vbox = gtk_vbox_new (FALSE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 5);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+
+ /* spin del día, mes y año */
+
+ hbox = gtk_hbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 5);
+
+ vbox2 = gtk_vbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), vbox2, TRUE, TRUE, 5);
+
+ etiqueta = gtk_label_new ("Day :");
+ gtk_misc_set_alignment (GTK_MISC (etiqueta), 0, 0.5);
+ gtk_box_pack_start (GTK_BOX (vbox2), etiqueta, FALSE, TRUE, 0);
+
+ adj = (GtkAdjustment *) gtk_adjustment_new (1.0, 1.0, 31.0, 1.0,
+ 5.0, 0.0);
+ spinner = gtk_spin_button_new (adj, 0, 0);
+ gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (spinner), TRUE);
+ gtk_spin_button_set_shadow_type (GTK_SPIN_BUTTON (spinner),
+ GTK_SHADOW_OUT);
+ gtk_box_pack_start (GTK_BOX (vbox2), spinner, FALSE, TRUE, 0);
+
+ vbox2 = gtk_vbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), vbox2, TRUE, TRUE, 5);
+
+ etiqueta = gtk_label_new ("Month :");
+ gtk_misc_set_alignment (GTK_MISC (etiqueta), 0, 0.5);
+ gtk_box_pack_start (GTK_BOX (vbox2), etiqueta, FALSE, TRUE, 0);
+
+ adj = (GtkAdjustment *) gtk_adjustment_new (1.0, 1.0, 12.0, 1.0,
+ 5.0, 0.0);
+ spinner = gtk_spin_button_new (adj, 0, 0);
+ gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (spinner), TRUE);
+ gtk_spin_button_set_shadow_type (GTK_SPIN_BUTTON (spinner),
+ GTK_SHADOW_ETCHED_IN);
+ gtk_box_pack_start (GTK_BOX (vbox2), spinner, FALSE, TRUE, 0);
+
+ vbox2 = gtk_vbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), vbox2, TRUE, TRUE, 5);
+
+ etiqueta = gtk_label_new ("Year :");
+ gtk_misc_set_alignment (GTK_MISC (etiqueta), 0, 0.5);
+ gtk_box_pack_start (GTK_BOX (vbox2), etiqueta, FALSE, TRUE, 0);
+
+ adj = (GtkAdjustment *) gtk_adjustment_new (1998.0, 0.0, 2100.0,
+ 1.0, 100.0, 0.0);
+ spinner = gtk_spin_button_new (adj, 0, 0);
+ gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (spinner), FALSE);
+ gtk_spin_button_set_shadow_type (GTK_SPIN_BUTTON (spinner),
+ GTK_SHADOW_IN);
+ gtk_widget_set_usize (spinner, 55, 0);
+ gtk_box_pack_start (GTK_BOX (vbox2), spinner, FALSE, TRUE, 0);
+
+ frame = gtk_frame_new ("Accelerated");
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, TRUE, TRUE, 0);
+
+ vbox = gtk_vbox_new (FALSE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 5);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+
+ hbox = gtk_hbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 5);
+
+ vbox2 = gtk_vbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), vbox2, TRUE, TRUE, 5);
+
+ etiqueta = gtk_label_new ("Value :");
+ gtk_misc_set_alignment (GTK_MISC (etiqueta), 0, 0.5);
+ gtk_box_pack_start (GTK_BOX (vbox2), etiqueta, FALSE, TRUE, 0);
+
+ adj = (GtkAdjustment *) gtk_adjustment_new (0.0, -10000.0, 10000.0,
+ 0.5, 100.0, 0.0);
+ spinner1 = gtk_spin_button_new (adj, 1.0, 2);
+ gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (spinner1), TRUE);
+ gtk_widget_set_usize (spinner1, 100, 0);
+ gtk_box_pack_start (GTK_BOX (vbox2), spinner1, FALSE, TRUE, 0);
+
+ vbox2 = gtk_vbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), vbox2, TRUE, TRUE, 5);
+
+ etiqueta = gtk_label_new ("Digits :");
+ gtk_misc_set_alignment (GTK_MISC (etiqueta), 0, 0.5);
+ gtk_box_pack_start (GTK_BOX (vbox2), etiqueta, FALSE, TRUE, 0);
+
+ adj = (GtkAdjustment *) gtk_adjustment_new (2, 1, 5, 1, 1, 0);
+ spinner2 = gtk_spin_button_new (adj, 0.0, 0);
+ gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (spinner2), TRUE);
+ gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
+ GTK_SIGNAL_FUNC (change_digits),
+ (gpointer) spinner2);
+ gtk_box_pack_start (GTK_BOX (vbox2), spinner2, FALSE, TRUE, 0);
+
+ hbox = gtk_hbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 5);
+
+ boton = gtk_check_button_new_with_label ("Snap to 0.5-ticks");
+ gtk_signal_connect (GTK_OBJECT (boton), "clicked",
+ GTK_SIGNAL_FUNC (toggle_snap),
+ spinner1);
+ gtk_box_pack_start (GTK_BOX (vbox), boton, TRUE, TRUE, 0);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (boton), TRUE);
+
+ boton = gtk_check_button_new_with_label ("Numeric only input mode");
+ gtk_signal_connect (GTK_OBJECT (boton), "clicked",
+ GTK_SIGNAL_FUNC (toggle_numeric),
+ spinner1);
+ gtk_box_pack_start (GTK_BOX (vbox), boton, TRUE, TRUE, 0);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (boton), TRUE);
+
+ val_label = gtk_label_new ("");
+
+ hbox = gtk_hbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 5);
+ boton = gtk_button_new_with_label ("Value as Int");
+ gtk_object_set_user_data (GTK_OBJECT (boton), val_label);
+ gtk_signal_connect (GTK_OBJECT (boton), "clicked",
+ GTK_SIGNAL_FUNC (get_value),
+ GINT_TO_POINTER (1));
+ gtk_box_pack_start (GTK_BOX (hbox), boton, TRUE, TRUE, 5);
+
+ boton = gtk_button_new_with_label ("Value as Float");
+ gtk_object_set_user_data (GTK_OBJECT (boton), val_label);
+ gtk_signal_connect (GTK_OBJECT (boton), "clicked",
+ GTK_SIGNAL_FUNC (get_value),
+ GINT_TO_POINTER (2));
+ gtk_box_pack_start (GTK_BOX (hbox), boton, TRUE, TRUE, 5);
+
+ gtk_box_pack_start (GTK_BOX (vbox), val_label, TRUE, TRUE, 0);
+ gtk_label_set_text (GTK_LABEL (val_label), "0");
+
+ hbox = gtk_hbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, TRUE, 0);
+
+ boton = gtk_button_new_with_label ("Close");
+ gtk_signal_connect_object (GTK_OBJECT (boton), "clicked",
+ GTK_SIGNAL_FUNC (gtk_widget_destroy),
+ GTK_OBJECT (ventana));
+ gtk_box_pack_start (GTK_BOX (hbox), boton, TRUE, TRUE, 5);
+
+ gtk_widget_show_all (ventana);
+
+ /* Entramos dentro del bucle de eventos */
+ gtk_main ();
+
+ return(0);
+}
+/* fin del ejemplo */
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Caja combinada (<em/Combo Box/)
+<p>
+La caja combinada o <em/combo box/ es otro sencillo <em/widget/
+exclusivamente compuesto por otros <em/widgets/. Desde el punto de
+vista del usuario, el <em/widget/ consiste en una caja para la
+introducción de texto y un menú desplegable desde el que el usuario
+puede seleccionar una de un conjunto predefinido de entradas. De forma
+alternativa, el usuario puede introducir una opción diferente en la
+caja de texto.
+
+El siguiente extracto de la estructura que define un Combo Box
+identifica algunos de sus componentes:
+
+<tscreen><verb>
+struct _GtkCombo {
+ GtkHBox hbox;
+ GtkWidget *entry;
+ GtkWidget *boton;
+ GtkWidget *popup;
+ GtkWidget *popwin;
+ GtkWidget *list;
+ ... };
+</verb></tscreen>
+
+Como puede ver, el Combo Box tiene dos partes principales que tiene
+que conocer: un <em/widget entry/ y un <em/widget list/ (lista).
+
+Lo primero, para crear un combo box, utilice:
+
+<tscreen><verb>
+GtkWidget *gtk_combo_new( void );
+</verb></tscreen>
+
+Ahora, si quiere indicar la cadena que debe aparecer en la sección
+entry del combo box, podrá hacerlo manipulando directamente el
+<em/widget/ <tt/entry/:
+
+<tscreen><verb>
+ gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(combo)->entry), "Mi cadena.");
+</verb></tscreen>
+
+Para introducir valores en la lista desplegable, puede utilizar la
+función:
+
+<tscreen><verb>
+void gtk_combo_set_popdown_strings( GtkCombo *combo,
+ GList *cadenas );
+</verb></tscreen>
+
+Antes de llamar a esta función, tiene que ensamblar una GList con las
+cadenas que quiere. GList es una implementación de una lista enlazada
+que forma parte de <ref id="sec_glib" name="glib">, una biblioteca
+base de GTK. Por el momento, la explicación fea y rápida es que tiene
+que crear un puntero GList, hacerlo igual a NULL, y añadirle cadenas
+de texto con la función
+
+<tscreen><verb>
+GList *g_list_append( GList *glist,
+ gpointer datos );
+</verb></tscreen>
+
+Es importante que inicialice el puntero GList a NULL antes de
+utilizarlo. El valor devuelto por la función g_list_append debe
+utilizarse como el nuevo puntero a la GList.
+
+Aquí tenemos un trozo de código típico para crear un conjunto de
+opciones:
+
+<tscreen><verb>
+ GList *glist=NULL;
+
+ glist = g_list_append(glist, "Cadena 1");
+ glist = g_list_append(glist, "Cadena 2");
+ glist = g_list_append(glist, "Cadena 3");
+ glist = g_list_append(glist, "Cadena 4");
+
+ gtk_combo_set_popdown_strings( GTK_COMBO(combo), glist) ;
+</verb></tscreen>
+
+A partir de este momento tendrá un combo box completo funcionando. Hay
+unos cuantos aspectos de su funcionamiento que puede cambiar. Para
+hacerlo tiene las funciones siguientes:
+
+<tscreen><verb>
+void gtk_combo_set_use_arrows( GtkCombo *combo,
+ gint valor );
+
+void gtk_combo_set_use_arrows_always( GtkCombo *combo,
+ gint valor );
+
+void gtk_combo_set_case_sensitive( GtkCombo *combo,
+ gint valor );
+</verb></tscreen>
+
+<tt/gtk_combo_set_use_arrows()/ le deja al usuario cambiar el valor
+del combo box utilizando las flechas de arriba/abajo. Utilizando estas
+teclas no haremos salir la lista, pero se reemplazará el texto actual
+del combo box con el siguiente elemento de la lista (superior o
+inferior, según la tecla que se pulse). Esto se consigue buscando en
+la lista el elemento correspondiente al valor actual del combo box y
+seleccionando el anterior o el posterior (según corresponda).
+Normalmente en una caja de entrada de texto las flechas se utilizan
+para cambiar el foco (ie. el <em/widget/ que recibe la entrada del
+teclado), pero en este caso será el TAB quien se ocupe. Cuando el
+elemento actual sea el último de la lista y presione la flecha abajo
+se cambiará el foco (lo mismo se aplica cuando estamos sobre el primer
+elemento y pulsamos la tecla arriba).
+
+Si el valor actual en la caja de entrada de texto no está en la lista,
+entonces se desactiva la función de <tt/gtk_combo_set_use_arrows()/.
+
+<tt/gtk_combo_set_use_arrows_always()/ igualmente permite la
+utilización de las flechas arriba/abajo para cambiar el elemento
+seleccionado por el siguiente/anterior de la lista, pero además trata
+la lista como si fuese circular (ie. pasa del último al primer
+elemento), desactivando completamente la utilidad de las teclas arriba
+y abajo para cambiar el foco.
+
+<tt/gtk_combo_set_case_sensitive()/ cambia entre una búsqueda por la
+lista que discrimine entre mayúsculas y minúsculas y una búsqueda que
+no discrimine. Se utiliza cuando se quiere que el <em/widget/ combo
+complete el valor que se está introduciendo con un valor de la
+lista. Dependiendo de esta función, se completará distinguiendo entre
+mayúsculas y minúsculas o no. El <em/widget/ combo completará la
+entrada actual si el usuario presiona la combinación de teclas MOD-1 y
+`Tab'. MOD-1 normalmente es la tecla `Alt'. Hay algunos
+administradores de ventanas que también utilizan esta combinación de
+teclas, con lo que perderemos su posible utilización por parte de GTK.
+
+Ahora que tenemos un combo box que actua como queremos que actue, todo
+lo que nos queda es saber como hay que hacer para obtener los datos
+que nos puede proporcionar. Esto es relativamente sencillo. La mayoría
+del tiempo, de lo único que tiene que preocuparse es de obtener datos
+de la caja de texto. Podemos acceder a la caja de texto mediante
+GTK_ENTRY(GTK_COMBO(combo)->entry). Las dos cosas que son interesantes
+hacer con esta caja son: enlazarla con la señal <tt/activate/, que
+indica que el usuario ha presionado la tecla «Intro», y leer el
+texto. Lo primero podemos hacerlo utilizando algo así:
+
+<tscreen><verb>
+ gtk_signal_connect(GTK_OBJECT(GTK_COMB(combo)->entry), "activate",
+ GTK_SIGNAL_FUNC (mi_funcion_respuesta), mis_datos);
+</verb></tscreen>
+
+Para conseguir el texto que hay en la caja en cualquier momento sólo
+tenemos que utilizar la función siguiente:
+
+<tscreen><verb>
+gchar *gtk_entry_get_text(GtkEntry *entry);
+</verb></tscreen>
+
+De esta forma:
+
+<tscreen><verb>
+ char *cadena;
+
+ cadena = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(combo)->entry));
+</verb></tscreen>
+
+Esto es todo lo interesante. Existe otra función,
+
+<tscreen><verb>
+void gtk_combo_disable_activate(GtkCombo *combo);
+</verb></tscreen>
+
+que permite desactivar la señal <tt/activate/ en el <em/widget/ entry
+dentro del combo box. Personalmente no se me ocurre ningún motivo para
+utilizarla, pero existir existe.
+
+<!-- There are also a function to set the string on a particular item, void
+gtk_combo_set_item_string(GtkCombo *combo, GtkItem *item, const gchar
+*item_value), but this requires that you have a pointer to the
+appropriate GtkItem. Frankly, I have no idea how to do that.
+-->
+
+<!-- ************************************** -->
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Selección de Color
+<p>
+El <em/widget/ selección de color, nos permite (¡sorpresa!) la selección
+interactiva de colores. Este <em/widget/ compuesto le permite al usuario
+seleccionar un color manipulando los tripletes RGB (rojo, verde y azul) y
+HSV (tono, saturación, valor). Para conseguirlo puede ajustar cada variable
+mediante las regletas o introduciendo directamente el valor deseado.
+También puede pinchar en la rueda de colores y seleccionar así el color
+deseado. También se puede establecer, opcionalmente, la transparencia
+del color.
+
+El <em/widget/ de selección de color emite (por ahora) sólo una señal,
+<tt/color_changed/, que se emite cuando cambia el color seleccionado,
+ya sea mediante un cambio que haga el usuario o median el resultado
+de una llamada a la función <tt/gtk_color_selection_set_color()/.
+
+Echémosle un vistazo a lo que nos ofrece el <em/widget/ de selección de color.
+El <em/widget/ tiene dos «sabores» diferentes; <tt/gtk_color_selection/
+y <tt/gtk_color_selection_dialog/:
+
+<tscreen><verb>
+GtkWidget *gtk_color_selection_new( void );
+</verb></tscreen>
+
+Probablemente no utilizará este constructor directamente. Crea un <em/widget/
+GtkColorSelection huérfano al que le tendrá que asignarle un padre. El
+<em/widget/ GtkColorSelection está heredado del <em/widget/ GtkVBox.
+
+<tscreen><verb>
+GtkWidget *gtk_color_selection_dialog_new( const gchar *title );
+</verb></tscreen>
+
+Éste es el constructor del cuadro de selección de color más común. Crea un
+<tt/GtkColorSelectionDialog/, heredado de un <tt/GtkDialog/. Consiste en un
+<tt/GtkFrame/ con un <tt/GtkColorSelection/, un <tt/GtkHSeparator/ y un
+<tt/GtkHBox/ con tres botones, «Aceptar», «Cancelar» y «Ayuda».
+Puede utilizar estos botones accediendo a los <em/widgets/ <tt/ok_button/,
+<tt/cancel_button/ y <tt/help_button/ de la estructura GtkColorSelectionDialog,
+(es decir GTK_COLOR_SELECTION_DIALOG(colorseldialog)->ok_button).
+
+<tscreen><verb>
+void gtk_color_selection_set_update_policy( GtkColorSelection *colorsel,
+ GtkUpdateType policy );
+</verb></tscreen>
+
+Esta función se utiliza para indicar la política de actuación. La política
+por defecto es <tt/GTK_UPDATE_CONTINUOUS/ que significa que el color
+seleccionado se actualiza continuamente cuando el usuario arrastra la barra
+o selecciona con el ratón un color de la rueda de colores. Si tiene problemas
+de rendimiento, puede poner la política <tt/GTK_UPDATE_DISCONTINUOUS/ o
+<tt/GTK_UPDATE_DELAYED/.
+
+<tscreen><verb>
+void gtk_color_selection_set_opacity( GtkColorSelection *colorsel,
+ gint use_opacity );
+</verb></tscreen>
+
+El <em/widget/ de selección de color admite el ajuste de la transparencia
+de un color (también conocido como el canal alfa). Esta opción está
+desactivada por defecto. Si se llama a esta función con <tt/use_opacity/
+como TRUE se activa la transparencia. Si se utiliza <tt/use_opacity/ como
+FALSE se desactiva la transparencia.
+
+<tscreen><verb>
+void gtk_color_selection_set_color( GtkColorSelection *colorsel,
+ gdouble *color );
+</verb></tscreen>
+
+Puede poner el color actual explicitamente haciendo uso de esta función con
+un puntero a un vector de colores (de tipo <tt/gdouble/). La longitud del
+vector depende de si está activada la transparencia. La posición 0 contiene
+la componente roja del color, la 1 contiene la verde, la 2 la azul y la
+transparencia está en la posición 3 (solamente si está activada la
+transparencia, ver <tt/gtk_color_selection_set_opacity()/). Todos los
+valores se encuentran entre 0.0 y 1.0.
+
+<tscreen><verb>
+void gtk_color_selection_get_color( GtkColorSelection *colorsel,
+ gdouble *color );
+</verb></tscreen>
+
+Cuando necesite preguntar por el color actual, normalmente cuando haya
+recibido una señal <tt/color_changed/, utilice esta función. <tt/color/
+es un puntero al vector de colores que se rellenará. Ver la descripción
+de la función <tt/gtk_color_selection_set_color()/ para conocer la
+estructura de este vector.
+
+<!-- Need to do a whole section on DnD - TRG
+Drag and drop
+-------------
+
+The color sample areas (right under the hue-saturation wheel) supports
+drag and drop. The type of drag and drop is "application/x-color". The
+message data consists of an array of 4 (or 5 if opacity is enabled)
+gdouble values, where the value at position 0 is 0.0 (opacity on) or
+1.0 (opacity off) followed by the red, green and blue values at
+positions 1,2 and 3 respectively. If opacity is enabled, the opacity
+is passed in the value at position 4.
+-->
+
+Aquí tenemos un pequeño ejemplo que muestra el uso de
+<tt/GtkColorSelectionDialog/. El programa muestra una ventana con una
+zona de dibujo. Pulsando en ella se abre un cuadro de diálogo de
+selección del color, y cambiando el color en el cuadro de diálogo se
+cambia el color de fondo de la zona de dibujo.
+
+<tscreen><verb>
+/* principio del ejemplo colorsel colorsel.c */
+
+#include <glib.h>
+#include <gdk/gdk.h>
+#include <gtk/gtk.h>
+
+GtkWidget *colorseldlg = NULL;
+GtkWidget *drawingarea = NULL;
+
+/* Manejador del cambio de color */
+
+void color_changed_cb (GtkWidget *widget, GtkColorSelection *colorsel)
+{
+ gdouble color[3];
+ GdkColor gdk_color;
+ GdkColormap *colormap;
+
+ /* Obtener el mapa de colores de la zona de dibujo */
+
+ colormap = gdk_window_get_colormap (drawingarea->window);
+
+ /* Obtener el color actual */
+
+ gtk_color_selection_get_color (colorsel,color);
+
+ /* Meterlo en un entero sin signo de 16 bits (0..65535) e insertarlo
+ en la estructura GdkColor */
+
+ gdk_color.red = (guint16)(color[0]*65535.0);
+ gdk_color.green = (guint16)(color[1]*65535.0);
+ gdk_color.blue = (guint16)(color[2]*65535.0);
+
+ /* Pedir memoria para el color */
+
+ gdk_color_alloc (colormap, &amp;gdk_color);
+
+ /* Poner el color de fondo de la ventana */
+
+ gdk_window_set_background (drawingarea->window, &amp;gdk_color);
+
+ /* Limpiar la ventana */
+
+ gdk_window_clear (drawingarea->window);
+}
+
+/* Manejador del evento Drawingarea */
+
+gint area_event (GtkWidget *widget, GdkEvent *event, gpointer client_data)
+{
+ gint handled = FALSE;
+ GtkWidget *colorsel;
+
+ /* Comprobar si hemos recibido un evento de pulsación de botón */
+
+ if (event->type == GDK_BUTTON_PRESS &amp;&amp; colorseldlg == NULL)
+ {
+ /* Sí, ¡tenemos un evento y todavía no está el colorseldlg! */
+
+ handled = TRUE;
+
+ /* Crear el cuadro de diálogo de selección del color */
+
+ colorseldlg = gtk_color_selection_dialog_new("Select background color");
+
+ /* Obtener el widget GtkColorSelection */
+
+ colorsel = GTK_COLOR_SELECTION_DIALOG(colorseldlg)->colorsel;
+
+ /* Conectar con la señal «color_changed», darle al dato del
+ cliente el valor del widget colorsel */
+
+ gtk_signal_connect(GTK_OBJECT(colorsel), "color_changed",
+ (GtkSignalFunc)color_changed_cb, (gpointer)colorsel);
+
+ /* Mostrar el cuadro de diálogo */
+
+ gtk_widget_show(colorseldlg);
+ }
+
+ return handled;
+}
+
+/* Manipulador de los eventos cerrar y salir */
+
+void destroy_window (GtkWidget *widget, gpointer client_data)
+{
+ gtk_main_quit ();
+}
+
+/* Principal */
+
+gint main (gint argc, gchar *argv[])
+{
+ GtkWidget *ventana;
+
+ /* Inicializa el toolkit, y elimina las opciones relacionadas con
+ gtk incluidas en la línea de órdenes */
+
+ gtk_init (&amp;argc,&amp;argv);
+
+ /* Crea la ventana de más alto nivel, le da título y la política */
+
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title (GTK_WINDOW(ventana), "Color selection test");
+ gtk_window_set_policy (GTK_WINDOW(ventana), TRUE, TRUE, TRUE);
+
+ /* Enlaza con los eventos «delete» y «destroy», para que podamos
+ salir */
+
+ gtk_signal_connect (GTK_OBJECT(ventana), "delete_event",
+ (GtkSignalFunc)destroy_window, (gpointer)ventana);
+
+ gtk_signal_connect (GTK_OBJECT(ventana), "destroy",
+ (GtkSignalFunc)destroy_window, (gpointer)ventana);
+
+ /* Crea la zona de dibujo, pone el tamaño y caza los eventos de los
+ botones */
+
+ drawingarea = gtk_drawing_area_new ();
+
+ gtk_drawing_area_size (GTK_DRAWING_AREA(drawingarea), 200, 200);
+
+ gtk_widget_set_events (drawingarea, GDK_BUTTON_PRESS_MASK);
+
+ gtk_signal_connect (GTK_OBJECT(drawingarea), "event",
+ (GtkSignalFunc)area_event, (gpointer)drawingarea);
+
+ /* Add drawingarea to window, then show them both */
+
+ gtk_container_add (GTK_CONTAINER(ventana), drawingarea);
+
+ gtk_widget_show (drawingarea);
+ gtk_widget_show (ventana);
+
+ /* Entrar en el bucle principal de gtk (nunca sale de aquí) */
+
+ gtk_main ();
+
+ /* Para satisfacer a los compiladores pijos */
+
+ return 0;
+}
+/* final del ejemplo */
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Selección de ficheros
+<p>
+El <em/widget/ de selección de ficheros nos proporciona una forma
+rápida y sencilla de mostrar un cuadro de diálogo para la selección de
+un fichero. Ya viene con los botones Aceptar, Cancelar y Ayuda. Una
+magnifica ayuda para acortar el tiempo de programación.
+
+Para crear un nuevo cuadro de diálogo de selección de ficheros
+utilice:
+
+<tscreen><verb>
+GtkWidget *gtk_file_selection_new( gchar *title );
+</verb></tscreen>
+
+Para poner el nombre del fichero en el cuadro de diálogo, por
+ejemplo para poder utilizar un directorio o un fichero por defecto,
+utilice la función:
+
+<tscreen><verb>
+void gtk_file_selection_set_filename( GtkFileSelection *filesel,
+ gchar *filename );
+</verb></tscreen>
+
+Para obtener el texto que el usuario ha introducido, utilice la función:
+
+<tscreen><verb>
+gchar *gtk_file_selection_get_filename( GtkFileSelection *filesel );
+</verb></tscreen>
+
+También hay punteros a los <em/widgets/ que contiene el cuadro de
+diálogo. Son los siguientes:
+
+<itemize>
+<item>dir_list
+<item>file_list
+<item>selection_entry
+<item>selection_text
+<item>main_vbox
+<item>ok_button
+<item>cancel_button
+<item>help_button
+</itemize>
+
+Lo más probable es que sólo utilice los punteros <tt/ok_button/,
+<tt/cancel_button/, y <tt/help_button/ para controlar cuando se pulsan.
+
+Aquí incluímos un ejemplo robado de <tt/testgtk.c/, modificado
+para que se puede ejecutar independientemente. Como puede ver, no es
+muy complicado crear un <em/widget/ para la selección de
+ficheros. Aunque aparezca el botón de ayuda en la pantalla, no hace
+nada y no tiene ninguna señal conectada.
+
+<tscreen><verb>
+/* principio del ejemplo filesel filesel.c */
+
+#include <gtk/gtk.h>
+
+/* Obtener el nombre del fichero e imprimirlo en la consola */
+void file_ok_sel (GtkWidget *w, GtkFileSelection *fs)
+{
+ g_print ("%s\n", gtk_file_selection_get_filename (GTK_FILE_SELECTION (fs)));
+}
+
+void destroy (GtkWidget *widget, gpointer data)
+{
+ gtk_main_quit ();
+}
+
+int main (int argc, char *argv[])
+{
+ GtkWidget *filew;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ /* Crear un nuevo widget de selección de ficheros */
+ filew = gtk_file_selection_new ("File selection");
+
+ gtk_signal_connect (GTK_OBJECT (filew), "destroy",
+ (GtkSignalFunc) destroy, &amp;filew);
+ /* Conectar el ok_button con la función file_ok_sel */
+ gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (filew)->ok_button),
+ "clicked", (GtkSignalFunc) file_ok_sel, filew );
+
+ /* Conectar el cancel_button con la destrucción del widget */
+ gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION (filew)->cancel_button),
+ "clicked", (GtkSignalFunc) gtk_widget_destroy,
+ GTK_OBJECT (filew));
+
+ /* Damos el nombre del fichero, como si fuese un cuadro de diálogo para
+ grabar ficheros y estuviesemos dando un nombre por defecto */
+ gtk_file_selection_set_filename (GTK_FILE_SELECTION(filew),
+ "penguin.png");
+
+ gtk_widget_show(filew);
+ gtk_main ();
+ return 0;
+}
+/* fin del ejemplo */
+</verb></tscreen>
+
+<!-- ***************************************************************** -->
+<sect> El <em/widget/ contenedor
+<!-- ***************************************************************** -->
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> El <em/widget/ EventBox<label id="sec_EventBox">
+<label id="sec_The_EventBox_Widget">
+<p>
+Algunos <em/widget/ gtk no tienen asociada una ventana X, por lo que
+sólo pueden dibujar en la de su padre. Debido a esto, no pueden
+recibir ningún evento y si tienen un tamaño incorrecto, no se
+recortarán correctamente por lo que puede que se sobreescriban ciertas
+zonas, etc... Si necesita este tipo de <em/widgets/, el EventBox es
+para usted.
+
+Cuando se ve por primera vez, el <em/widget/ EventBox puede parecer
+completamente inútil. No dibuja nada en la pantalla y no responde
+a ningún evento. Sin embargo, tiene una utilidad - proporciona una
+ventana X para su <em/widget/ hijo. Esto es importante ya que
+muchos <em/widgets/ GTK no tienen una ventana X asociada. No tener una
+ventana X ahorra memoria y mejora el rendimiento, pero tiene sus
+desventajas. Un <em/widget/ sin una ventana X no puede recibir
+eventos, y no realizará ningún recorte en sus contenidos. Aunque el
+nombre <em/EventBox/ enfatiza su función de manejador de eventos, el
+<em/widget/ también puede utilizarse para hacer los recortes.
+(Y más... ver el ejemplo más abajo.)
+
+Para crear un nuevo <em/widget/ EventBox, utilice:
+
+<tscreen><verb>
+GtkWidget *gtk_event_box_new( void );
+</verb></tscreen>
+
+Un <em/widget/ hijo puede añadirse a su EventBox así:
+
+<tscreen><verb>
+gtk_container_add( GTK_CONTAINER(event_box), widget );
+</verb></tscreen>
+
+El siguiente ejemplo demuestra los dos usos de EventBox - se crea
+una etiqueta que se recorta dentro de una pequeña caja, y hace
+que una pulsación del ratón en la misma finalice el programa.
+
+<tscreen><verb>
+/* principio del ejemplo eventbox eventbox.c */
+
+#include <gtk/gtk.h>
+
+int
+main (int argc, char *argv[])
+{
+ GtkWidget *ventana;
+ GtkWidget *event_box;
+ GtkWidget *etiqueta;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ gtk_window_set_title (GTK_WINDOW (ventana), "Event Box");
+
+ gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
+ GTK_SIGNAL_FUNC (gtk_exit), NULL);
+
+ gtk_container_border_width (GTK_CONTAINER (ventana), 10);
+
+ /* Crea un EventBox y lo añade a nuestra ventana superior */
+
+ event_box = gtk_event_box_new ();
+ gtk_container_add (GTK_CONTAINER(ventana), event_box);
+ gtk_widget_show (event_box);
+
+ /* Crea una larga etiqueta */
+
+ etiqueta = gtk_label_new ("Click here to quit, quit, quit, quit, quit");
+ gtk_container_add (GTK_CONTAINER (event_box), etiqueta);
+ gtk_widget_show (etiqueta);
+
+ /* La recortamos. */
+ gtk_widget_set_usize (etiqueta, 110, 20);
+
+ /* Y enlazamos una acción con la etiqueta */
+ gtk_widget_set_events (event_box, GDK_BUTTON_PRESS_MASK);
+ gtk_signal_connect (GTK_OBJECT(event_box), "button_press_event",
+ GTK_SIGNAL_FUNC (gtk_exit), NULL);
+
+ /* Otra cosa más que necesita una ventana X ... */
+
+ gtk_widget_realize (event_box);
+ gdk_window_set_cursor (event_box->window, gdk_cursor_new (GDK_HAND1));
+
+ gtk_widget_show (ventana);
+
+ gtk_main ();
+
+ return 0;
+}
+/* Final del ejemplo */
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>El <em/widget/ alineamiento <label id="sec_Alignment">
+<p>
+El <em/widget/ alineamiento (<em/alignment/) le permitirá colocar un
+<em/widget/ dentro de su ventana utilizando una posición y un tamaño
+relativos al mismo <em/widget/ de alineamiento. Por ejemplo, puede ser
+muy útil para centrar un <em/widget/ en la ventana.
+
+Sólo hay dos funciones asociadas con el <em/widget/ alineamiento:
+
+<tscreen><verb>
+GtkWidget* gtk_alignment_new( gfloat xalign,
+ gfloat yalign,
+ gfloat xscale,
+ gfloat yscale );
+
+void gtk_alignment_set( GtkAlignment *alignment,
+ gfloat xalign,
+ gfloat yalign,
+ gfloat xscale,
+ gfloat yscale );
+</verb></tscreen>
+
+La primera función crea un nuevo <em/widget/ alineamiento con los
+parámetros especificados. La segunda función permite alterar los
+parámetros de un <em/widget/ alineamiento ya existente.
+
+Los cuatro parámetros de alineamiento son números en coma flotante que
+pueden tener variar entre 0.0 y 1.0. Los argumentos <tt/xalign/ e
+<tt/yalign/ afectan a la posición del <em/widget/ colocado dentro del
+<em/widget/ de alineamiento. Los argumentos <tt/xscale/ e <tt/yscale/
+afectan a la cantidad de espacio que ocupa el <em/widget/.
+
+Se le puede añadir un <em/widget/ hijo a un alineamiento utilizando:
+
+<tscreen><verb>
+ gtk_container_add( GTK_CONTAINER(alignment), child_widget );
+</verb></tscreen>
+
+Para ver un ejemplo de utilización del <em/widget/ alineamiento,
+diríjase al ejemplo del <em/widget/ <ref id="sec_ProgressBar"
+name="Barra de progreso">.
+
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Contenedor fijo
+<p>
+El contenedor fijo le permite situar <em/widgets/ en una posición fija
+dentro de su ventana, relativa a la esquina superior izquierda. La
+posición de los <em/widgets/ puede cambiarse dinámicamente.
+
+Sólo hay tres funciones asociadas al <em/widget/ contenedor fijo:
+
+<tscreen><verb>
+GtkWidget* gtk_fixed_new( void );
+
+void gtk_fixed_put( GtkFixed *fixed,
+ GtkWidget *widget,
+ gint16 x,
+ gint16 y );
+
+void gtk_fixed_move( GtkFixed *fixed,
+ GtkWidget *widget,
+ gint16 x,
+ gint16 y );
+</verb></tscreen>
+
+La función <tt/gtk_fixed_new/ permite la creación de un nuevo
+contenedor fijo.
+
+<tt/gtk_fixed_put/ situa <tt/widget/ dentro del contenedor <tt/fixed/
+en la posición especificada por <tt/x/ e <tt/y/.
+
+<tt/gtk_fixed_move/ permite que mover hacia una nuevo posición el
+<em/widget/ especificado.
+
+El ejemplo siguiente muestra como utilizar el contenedor fijo.
+
+<tscreen><verb>
+/* principio del ejemplo fixed fixed.c */
+
+#include <gtk/gtk.h>
+
+/* Voy a ser un poco torpe y utilizar algunas variables
+ * globales para almacenar la posición del widget que
+ * hay dentro del contenedor */
+gint x=50;
+gint y=50;
+
+/* Esta función de llamada mueve el botón a una nueva
+ * posición dentro del contenedor fijo. */
+void move_button( GtkWidget *widget,
+ GtkWidget *fixed )
+{
+ x = (x+30)%300;
+ y = (y+50)%300;
+ gtk_fixed_move( GTK_FIXED(fixed), widget, x, y);
+}
+
+int main( int argc,
+ char *argv[] )
+{
+ /* GtkWidget es el tipo de almacenamiento para los widgets */
+ GtkWidget *ventana;
+ GtkWidget *fixed;
+ GtkWidget *boton;
+ gint i;
+
+ /* Inicializa GTK */
+ gtk_init(&amp;argc, &amp;argv);
+
+ /* Crear una nueva ventana */
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title(GTK_WINDOW(ventana), "Fixed Container");
+
+ /* Aquí conectamos el evento "destroy" al manejador de la señal */
+ gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
+ GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
+
+ /* Establecemos el ancho del borde la ventana */
+ gtk_container_set_border_width (GTK_CONTAINER (ventana), 10);
+
+ /* Creamos un contenedor fijo */
+ fixed = gtk_fixed_new();
+ gtk_container_add(GTK_CONTAINER(ventana), fixed);
+ gtk_widget_show(fixed);
+
+ for (i = 1 ; i <= 3 ; i++) {
+ /* Crea un nuevo botón con la etiqueta "Press me" */
+ boton = gtk_button_new_with_label ("Press me");
+
+ /* Cuando el botón reciba la señal "pulsado", llamará a la función
+ * move_button() pasándole el contenedor fijo como argumento. */
+ gtk_signal_connect (GTK_OBJECT (boton), "clicked",
+ GTK_SIGNAL_FUNC (move_button), fixed);
+
+ /* Esto mete el botón dentro de la ventana del window contenedor
+ * fijo. */
+ gtk_fixed_put (GTK_FIXED (fixed), boton, i*50, i*50);
+
+ /* El paso final es mostrar el widget recien creado */
+ gtk_widget_show (boton);
+ }
+
+ /* Mostrar la ventana */
+ gtk_widget_show (ventana);
+
+ /* Entrar en el bucle principal */
+ gtk_main ();
+
+ return(0);
+}
+/* fin del ejemplo */
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Contenedor capa
+<p>
+El contenedor capa es similar al contenedor fijo, excepto que permite
+implementar una zona de <em/scroll/ infinita (donde infinito significa
+menor de 2^32). Xwindows tiene una limitación en la que las ventanas
+pueden tener un máximo de 32767 <em/pixels/ de alto o de ancho. El
+contenedor capa sortea esta limitación con una exótica combinación de
+ventanas y <em/bits/ de gravedad, <!-- Si alguien entiende que
+significa esto: e98cuenc@criens.u-psud.fr --> para que puede tener un
+suave <em/scroll/ aún cuando utilice una gran cantidad de <em/widgets/
+hijos dentro de su zona de <em/scroll/.
+
+Podrá crear un contenedor capa utilizando:
+
+<tscreen><verb>
+GtkWidget *gtk_layout_new( GtkAdjustment *hadjustment,
+ GtkAdjustment *vadjustment );
+</verb></tscreen>
+
+Como puede observar, podrá especificar (de forma opcional) los objetos
+de ajuste que utilizará el <em/widget/ capa para hacer su <em/scroll/.
+
+Puede añadir y mover <em/widgets/ dentro del contenedor capa
+utilizando las dos funciones siguientes:
+
+<tscreen><verb>
+void gtk_layout_put( GtkLayout *layout,
+ GtkWidget *widget,
+ gint x,
+ gint y );
+
+void gtk_layout_move( GtkLayout *layout,
+ GtkWidget *widget,
+ gint x,
+ gint y );
+</verb></tscreen>
+
+El tamaño del contenedor capa se puede establecer utilizando la
+siguiente función:
+
+<tscreen><verb>
+void gtk_layout_set_size( GtkLayout *layout,
+ guint width,
+ guint height );
+</verb></tscreen>
+
+Los contenedores capa son uno de los poquísimos <em/widgets/ dentro de
+GTK que se repintan ellos mismos en la pantalla cuando se cambian
+utilizando las funciones anteriores (la inmensa mayoria de los
+<em/widgets/ mandan una petición a la cola que será procesada cuando
+se devuelva el control a la función <tt/gtk_main()/).
+
+Cuando quiera hacer una gran cantidad de cambios dentro del contenedor
+capa, podrá utilizar las dos funciones siguientes para desactivar y
+reactivar la característica de repintado:
+
+<tscreen><verb>
+void gtk_layout_freeze( GtkLayout *layout );
+
+void gtk_layout_thaw( GtkLayout *layout );
+</verb></tscreen>
+
+Las cuatro funciones finales a utilizar con los <em/widgets/capa son
+para la manipulación de los <em/widgets/ de ajuste horizontal y
+vertical:
+
+<tscreen><verb>
+GtkAdjustment* gtk_layout_get_hadjustment( GtkLayout *layout );
+
+GtkAdjustment* gtk_layout_get_vadjustment( GtkLayout *layout );
+
+void gtk_layout_set_hadjustment( GtkLayout *layout,
+ GtkAdjustment *adjustment );
+
+void gtk_layout_set_vadjustment( GtkLayout *layout,
+ GtkAdjustment *adjustment);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Marcos <label id="sec_Frames">
+<p>
+Los marcos pueden utilizarse para meter uno o un grupo de
+<em/widgets/dentro de una caja puede ser (de forma opcional)
+etiquetada. La posición de la etiqueta y el estilo de la caja pueden
+modificarse.
+
+Se puede crear un marco con la siguiente función:
+
+<tscreen><verb>
+GtkWidget *gtk_frame_new( const gchar *etiqueta );
+</verb></tscreen>
+
+La etiqueta se coloca por defecto en la esquina superior izquierda del
+marco. Si el argumento <tt/etiqueta/ es NULL no se mostrará ninguna
+etiqueta. Puede cambiarse el texto de la etiqueta utilizando la
+función siguiente.
+
+<tscreen><verb>
+void gtk_frame_set_label( GtkFrame *frame,
+ const gchar *etiqueta );
+</verb></tscreen>
+
+La posición de la etiqueta se puede cambiar utilizado la función:
+
+<tscreen><verb>
+void gtk_frame_set_label_align( GtkFrame *frame,
+ gfloat xalign,
+ gfloat yalign );
+</verb></tscreen>
+
+<tt/xalign/ e <tt/yalign/ toman valores entre 0.0 y 1.0. <tt/yalign/
+no se actualmente no se utiliza. El valor por defecto de <tt/xalign/
+es 0.0, lo que coloca la etiqueta a la izquierda del marco.
+
+La siguiente función altera el estilo de la caja que se utiliza para
+señalar el marco.
+
+<tscreen><verb>
+void gtk_frame_set_shadow_type( GtkFrame *frame,
+ GtkShadowType type);
+</verb></tscreen>
+
+El argumento <tt/type/ puede tomar uno de los valores siguientes:
+
+<itemize>
+<item> GTK_SHADOW_NONE
+<item> GTK_SHADOW_IN
+<item> GTK_SHADOW_OUT
+<item> GTK_SHADOW_ETCHED_IN (the default)
+<item> GTK_SHADOW_ETCHED_OUT
+</itemize>
+
+El código siguiente ilustra la utilización del <em/widget/ marco.
+
+<tscreen><verb>
+/* principio del ejemplo frame frame.c */
+
+#include <gtk/gtk.h>
+
+int main( int argc,
+ char *argv[] )
+{
+ /* GtkWidget es el tipo de almacenamiento para los widgets */
+ GtkWidget *ventana;
+ GtkWidget *frame;
+ GtkWidget *boton;
+ gint i;
+
+ /* Inicializa GTK */
+ gtk_init(&amp;argc, &amp;argv);
+
+ /* Crea una nueva ventana */
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title(GTK_WINDOW(ventana), "Frame Example");
+
+ /* Aquí conectamos el evento "destroy"al manejador de señal */
+ gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
+ GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
+
+ gtk_widget_set_usize(ventana, 300, 300);
+
+ /* Establecemos el ancho del borde de la ventana */
+ gtk_container_set_border_width (GTK_CONTAINER (ventana), 10);
+
+ /* Crea un marco */
+ frame = gtk_frame_new(NULL);
+ gtk_container_add(GTK_CONTAINER(ventana), frame);
+
+ /* Establece la etiqueta del marco */
+ gtk_frame_set_label( GTK_FRAME(frame), "GTK Frame Widget" );
+
+ /* Alinea la etiqueta a la derecha del marco */
+ gtk_frame_set_label_align( GTK_FRAME(frame), 1.0, 0.0);
+
+ /* Establece el estilo del marco */
+ gtk_frame_set_shadow_type( GTK_FRAME(frame), GTK_SHADOW_ETCHED_OUT);
+
+ gtk_widget_show(frame);
+
+ /* Muestra la ventana */
+ gtk_widget_show (ventana);
+
+ /* Entra dentro del bucle principal */
+ gtk_main ();
+
+ return(0);
+}
+/* fin del ejemplo */
+
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Marcos con proporciones fijas
+<p>
+El <em/widget aspect frame/ (marco proporcional) es como el <em/widget
+frame/ (marco), excepto que conserva las proporciones (esto es, la
+relación entre el ancho y el alto) del <em/widget/ hijo, añadiendo
+espacio extra en caso de ser necesario. Esto es útil, por ejemplo, si
+quiere hacer una vista previa de una gran imagen. El tamaño de la
+vista previa debería variar cuando el usuario redimensione la ventana,
+pero la proporción tiene que coincidir con la de la imagen original.
+
+Para crear un nuevo marco proporcional utilice:
+
+<tscreen><verb>
+GtkWidget *gtk_aspect_frame_new( const gchar *etiqueta,
+ gfloat xalign,
+ gfloat yalign,
+ gfloat ratio,
+ gint obey_child);
+</verb></tscreen>
+
+<tt/xalign/ e <tt/yalign/ indican la alineación exactamente igual que
+con los <em/widgets Alignment/. Si <tt/obey_child/ es TRUE, la
+proporción de un <em/widget/ hijo será la misma que la proporción del
+tamaño ideal que éste pida. En caso contrario, vendrá dada por
+<tt/ratio/.
+
+Para cambiar las opciones de un marco proporcional ya existente, puede
+utilizar:
+
+<tscreen><verb>
+void gtk_aspect_frame_set( GtkAspectFrame *aspect_frame,
+ gfloat xalign,
+ gfloat yalign,
+ gfloat ratio,
+ gint obey_child);
+</verb></tscreen>
+
+Como por ejemplo, el siguiente programa utiliza un marco proporcional
+para mostrar una zona de dibujo cuyas proporciones siempre será de
+2:1, no importa como el usuario redimensione la ventana.
+
+<tscreen><verb>
+/* principio del ejemplo aspectframe aspectframe.c */
+
+#include <gtk/gtk.h>
+
+int
+main (int argc, char *argv[])
+{
+ GtkWidget *ventana;
+ GtkWidget *aspect_frame;
+ GtkWidget *drawing_area;
+ gtk_init (&amp;argc, &amp;argv);
+
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title (GTK_WINDOW (ventana), "Aspect Frame");
+ gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
+ GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
+ gtk_container_border_width (GTK_CONTAINER (ventana), 10);
+
+ /* Crear un aspect_frame y añadirlo a nuestra ventana superior */
+
+ aspect_frame = gtk_aspect_frame_new ("2x1", /* etiqueta */
+ 0.5, /* centro x */
+ 0.5, /* centro y */
+ 2, /* tamañox/tamañoy = 2 */
+ FALSE /* ignorar el aspecto del hijo */);
+
+ gtk_container_add (GTK_CONTAINER(ventana), aspect_frame);
+ gtk_widget_show (aspect_frame);
+
+ /* Añadir un widget hijo al marco proporcional */
+
+ drawing_area = gtk_drawing_area_new ();
+
+ /* Pediremos una ventana de 200x200, pero el marco proporcional
+ * sólo no dejará una ventana de 200x100, ya que tenemos una
+ * relación de 2x1 */
+ gtk_widget_set_usize (drawing_area, 200, 200);
+ gtk_container_add (GTK_CONTAINER(aspect_frame), drawing_area);
+ gtk_widget_show (drawing_area);
+
+ gtk_widget_show (ventana);
+ gtk_main ();
+ return 0;
+}
+/* fin del ejemplo */
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+
+<sect1> El <em/widget/ ventana dividida (<em/Paned Window/)
+<p>
+El <em/widget/ ventana dividida es útil para cuando se quiere dividir
+una zona en dos partes, con un tamaño relativo controlado por el
+usuario. Entre las dos porciones de la ventana se dibuja un separador
+con un botoncito que el usuario puede arrastrar para cambiar el tamaño
+de cada zona. La división puede ser horizontal (HPaned) o vertical
+(VPaned).
+
+Para crear una nueva ventana dividida, utilice una de las siguientes
+funciones:
+
+<tscreen><verb>
+GtkWidget *gtk_hpaned_new (void);
+
+GtkWidget *gtk_vpaned_new (void);
+</verb></tscreen>
+
+Después de crear el <em/widget/ ventana dividida, tiene que añadirle
+un <em/widget/ hijo a cada mitad. Para hacerlo, utilice:
+
+<tscreen><verb>
+void gtk_paned_add1 (GtkPaned *paned, GtkWidget *hijo);
+
+void gtk_paned_add2 (GtkPaned *paned, GtkWidget *hijo);
+</verb></tscreen>
+
+<tt/gtk_paned_add1()/ añade el <em/widget/ hijo a la mitad que se
+encuentra en la parte izquierda o superior de la ventana
+dividida. <tt/gtk_paned_add2()/ añade el <em/widget/ a la mitad que
+hay en la parte derecha o inferior de la ventana.
+
+Por ejemplo, si queremos crear una parte del interface de usuario de
+un programa de correo-e imaginario. Dividiremos verticalmente una
+ventana en dos partes, teniendo en la parte superior una lista de los
+mensajes de correo-e y en la parte inferior el texto de uno de estos
+mensajes. El programa es bastante fácil de entender. Solo un par de
+cosillas: no se puede añadir texto en un <em/widget/ de texto (Text)
+si no se ha hecho antes <tt/gtk_widget_realize()/, pero como
+demostración de una técnica alternativa, para añadir el texto
+conectaremos un manipulador a la señal «realize». Y tenemos que
+añadir la opción <tt/GTK_SHRINK/ a algunos de los elementos que hay en
+la tabla con la ventana de texto y sus barras de desplazamiento, así
+cuando la porción de abajo se haga más pequeña, se encogerá
+correctamente en lugar de desaparecer por la parte de abajo de la
+ventana.
+
+<tscreen><verb>
+/* principio del ejemplo paned paned.c */
+
+#include <gtk/gtk.h>
+
+/* Crear la lista de "messages" */
+GtkWidget *
+create_list (void)
+{
+
+ GtkWidget *scrolled_window;
+ GtkWidget *list;
+ GtkWidget *list_item;
+
+ int i;
+ char buffer[16];
+
+ /* Crear una nueva ventana con barras de desplazamiento si hacen
+ falta */
+ scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+
+ /* Crear una nueva lista y poner en ella la ventana con barras */
+ list = gtk_list_new ();
+ gtk_container_add (GTK_CONTAINER(scrolled_window), list);
+ gtk_widget_show (list);
+
+ /* Añadir algunos mensajes a la ventana */
+ for (i=0; i<10; i++) {
+
+ sprintf(buffer,"Message #%d",i);
+ list_item = gtk_list_item_new_with_label (buffer);
+ gtk_container_add (GTK_CONTAINER(list), list_item);
+ gtk_widget_show (list_item);
+
+ }
+
+ return scrolled_window;
+}
+
+/* Añadir algún texto a nuestro widget de texto - esta función se
+ invoca cada vez que se produce una señal realize en la
+ ventana. Podemos forzar esta señal mediante gtk_widget_realize, pero
+ primero tiene que formar parte de una jerarquía */
+
+void
+realize_text (GtkWidget *text, gpointer data)
+{
+ gtk_text_freeze (GTK_TEXT (text));
+ gtk_text_insert (GTK_TEXT (text), NULL, &amp;text->style->black, NULL,
+ "From: pathfinder@nasa.gov\n"
+ "To: mom@nasa.gov\n"
+ "Subject: Made it!\n"
+ "\n"
+ "We just got in this morning. The weather has been\n"
+ "great - clear but cold, and there are lots of fun sights.\n"
+ "Sojourner says hi. See you soon.\n"
+ " -Path\n", -1);
+
+ gtk_text_thaw (GTK_TEXT (text));
+}
+
+/* Creamos una zona con texto que muestra un "message" */
+GtkWidget *
+create_text (void)
+{
+ GtkWidget *table;
+ GtkWidget *text;
+ GtkWidget *hscrollbar;
+ GtkWidget *vscrollbar;
+
+ /* Crea una tabla para contener el widget de texto y las barras de
+ desplazamiento */
+ table = gtk_table_new (2, 2, FALSE);
+
+ /* Pone un widget de texto en la esquina superior izquierda.
+ Observe la utilización de GTK_SHRINK en la dirección y */
+ text = gtk_text_new (NULL, NULL);
+ gtk_table_attach (GTK_TABLE (table), text, 0, 1, 0, 1,
+ GTK_FILL | GTK_EXPAND,
+ GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 0);
+ gtk_widget_show (text);
+
+ /* Pone una HScrollbar en la esquina inferior izquierda */
+ hscrollbar = gtk_hscrollbar_new (GTK_TEXT (text)->hadj);
+ gtk_table_attach (GTK_TABLE (table), hscrollbar, 0, 1, 1, 2,
+ GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (hscrollbar);
+
+ /* Y una VScrollbar en la esquina superior derecha */
+ vscrollbar = gtk_vscrollbar_new (GTK_TEXT (text)->vadj);
+ gtk_table_attach (GTK_TABLE (table), vscrollbar, 1, 2, 0, 1,
+ GTK_FILL, GTK_EXPAND | GTK_FILL | GTK_SHRINK, 0, 0);
+ gtk_widget_show (vscrollbar);
+
+ /* Y un manejador para poner un mensaje en el widget de texto
+ cuando reciba realize */
+ gtk_signal_connect (GTK_OBJECT (text), "realize",
+ GTK_SIGNAL_FUNC (realize_text), NULL);
+
+ return table;
+}
+
+int
+main (int argc, char *argv[])
+{
+ GtkWidget *ventana;
+ GtkWidget *vpaned;
+ GtkWidget *list;
+ GtkWidget *text;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title (GTK_WINDOW (ventana), "Paned Windows");
+ gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
+ GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
+ gtk_container_border_width (GTK_CONTAINER (ventana), 10);
+
+ /* crea un widget vpaned y lo añade a nuestra ventana superior */
+
+ vpaned = gtk_vpaned_new ();
+ gtk_container_add (GTK_CONTAINER(ventana), vpaned);
+ gtk_widget_show (vpaned);
+
+ /* Ahora crea los contenidos de las dos mitades de la ventana */
+
+ list = create_list ();
+ gtk_paned_add1 (GTK_PANED(vpaned), list);
+ gtk_widget_show (list);
+
+ text = create_text ();
+ gtk_paned_add2 (GTK_PANED(vpaned), text);
+ gtk_widget_show (text);
+ gtk_widget_show (ventana);
+ gtk_main ();
+ return 0;
+}
+/* fin del ejemplo */
+</verb></tscreen>
+
+<!-- XXX -->
+<!-- ----------------------------------------------------------------- -->
+<sect1> <em/Viewports/ <label id="sec_Viewports">
+<p>
+Probablemente nunca le llegue a hacer falta utilizar el <em/widget/
+Viewport directamente. Será mucho más probable que tenga que utilizar
+el <em/widget/ <ref id="sec_ScrolledWindows" name="Ventanas con barras
+de desplazamiento"> que a su vez hace uso de <em/viewport/.
+
+Un <em/widget viewport/ le permite meter dentro un gran <em/widget/,
+de forma que sólo verá una parte del mismo. Utiliza
+<ref id="sec_Adjustment" name="ajustes"> para definir la zona que se
+está viendo actualmente.
+
+Para crear un <em/viewport/ hay que utilizar la función:
+
+<tscreen><verb>
+GtkWidget *gtk_viewport_new( GtkAdjustment *hadjustment,
+ GtkAdjustment *vadjustment );
+</verb></tscreen>
+
+Como puede observar, se pueden especificar los ajustes horizontal y
+vertical que el <em/widget/ va a utilizar en el mismo momento de su
+creación. El <em/widget/ creará sus propios ajustes en caso de que
+reciba como argumento un valor NULL.
+
+Puede obtener y establecer los ajustes después de que se haya
+creado el <em/widget/ utilizado las cuatro funciones siguientes:
+
+<tscreen><verb>
+GtkAdjustment *gtk_viewport_get_hadjustment (GtkViewport *viewport );
+
+GtkAdjustment *gtk_viewport_get_vadjustment (GtkViewport *viewport );
+
+void gtk_viewport_set_hadjustment( GtkViewport *viewport,
+ GtkAdjustment *adjustment );
+
+void gtk_viewport_set_vadjustment( GtkViewport *viewport,
+ GtkAdjustment *adjustment );
+</verb></tscreen>
+
+La única función relativa al <em/viewport/ que queda que altera su
+apariencia es:
+
+<tscreen><verb>
+void gtk_viewport_set_shadow_type( GtkViewport *viewport,
+ GtkShadowType type );
+</verb></tscreen>
+
+Los valores posibles para el argumento <tt/type/ son:
+<itemize>
+<item> GTK_SHADOW_NONE,
+<item> GTK_SHADOW_IN,
+<item> GTK_SHADOW_OUT,
+<item> GTK_SHADOW_ETCHED_IN,
+<item> GTK_SHADOW_ETCHED_OUT
+</itemize>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Ventanas con barras de desplazamiento <label id="sec_ScrolledWindows">
+<p>
+
+Las ventanas con barras de desplazamiento se utilizan para crear una zona
+con barras de desplazamiento dentro de una ventana real. Puede insertar
+cualquier tipo de <em/widget/ en una ventana con barras de
+desplazamiento, y podrá utilizarlo sin importar su tamaño gracias a
+las barras de desplazamiento.
+
+La función siguiente se utiliza para crear una nueva ventana con
+barras de desplazamiento.
+
+<tscreen><verb>
+GtkWidget *gtk_scrolled_window_new( GtkAdjustment *hadjustment,
+ GtkAdjustment *vadjustment );
+</verb></tscreen>
+
+Donde el primer argumento es el ajuste para la dirección horizontal, y
+el segundo es el ajuste para la dirección vertical. Casi siempre valen
+NULL.
+
+<tscreen><verb>
+void gtk_scrolled_window_set_policy( GtkScrolledWindow *scrolled_window,
+ GtkPolicyType hscrollbar_policy,
+ GtkPolicyType vscrollbar_policy );
+</verb></tscreen>
+
+Esta función establece la política que se utilizará con respecto a las
+barras de desplazamiento. El primer argumento es la ventana con barras
+de desplazamiento sobre la que queremos actuar. El segundo establece
+la política para la barra de desplazamiento horizontal, y el tercero
+la política para la barra de desplazamiento vertical.
+
+La política puede ser GTK_POLICY_AUTOMATIC, o
+GTK_POLICY_ALWAYS. GTK_POLICY_AUTOMATIC decidirá automáticamente si
+necesita barras de desplazamiento, mientras que GTK_POLICY_ALWAYS pondrá
+siempre las barras de desplazamiento.
+
+Aquí tenemos un ejemplo sencillo que empaqueta 100 botones de
+selección en una ventana con barras de desplazamiento. Solamente he
+comentado las partes que debería ser nuevas para usted.
+
+<tscreen><verb>
+/* principio del ejemplo scrolledwin scrolledwin.c */
+
+#include <gtk/gtk.h>
+
+void destroy(GtkWidget *widget, gpointer data)
+{
+ gtk_main_quit();
+}
+
+int main (int argc, char *argv[])
+{
+ static GtkWidget *ventana;
+ GtkWidget *scrolled_window;
+ GtkWidget *table;
+ GtkWidget *boton;
+ char buffer[32];
+ int i, j;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ /* Crea un nuevo cuadro de diálogo para que la ventana con barras de
+ * desplazamiento se meta dentro. Un cuadro de diálogo es como una
+ * ventana normal excepto que tiene dentro una vbox y un separador
+ * horizontal. Es sólo un atajo para crear cuadros de diálogo */
+ ventana = gtk_dialog_new ();
+ gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
+ (GtkSignalFunc) destroy, NULL);
+ gtk_window_set_title (GTK_WINDOW (ventana), "dialog");
+ gtk_container_border_width (GTK_CONTAINER (ventana), 0);
+ gtk_widget_set_usize(ventana, 300, 300);
+
+ /* crea una nueva ventana con barras de desplazamiento. */
+ scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+
+ gtk_container_border_width (GTK_CONTAINER (scrolled_window), 10);
+
+ /* la política es GTK_POLICY_AUTOMATIC, o GTK_POLICY_ALWAYS.
+ * GTK_POLICY_AUTOMATIC decidirá automáticamente si necesita
+ * barras de desplazamiento, mientras que GTK_POLICY_ALWAYS pondrá
+ * siempre las barras de desplazamiento. El primer argumento se
+ * refiere a la barra horizontal, el segundo a la vertical. */
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
+ /* El cuadro de diálogo se crea con una vbox dentro de él. */
+ gtk_box_pack_start (GTK_BOX (GTK_DIALOG(ventana)->vbox), scrolled_window,
+ TRUE, TRUE, 0);
+ gtk_widget_show (scrolled_window);
+
+ /* crea una tabla de 10 por 10 casillas. */
+ table = gtk_table_new (10, 10, FALSE);
+
+ /* pone el espacio en x y en y a 10 */
+ gtk_table_set_row_spacings (GTK_TABLE (table), 10);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 10);
+
+ /* empaqueta la tabla en la ventana con barras de desplazamiento */
+ gtk_container_add (GTK_CONTAINER (scrolled_window), table);
+ gtk_widget_show (table);
+
+ /* crea una rejilla de botones de selección en la tabla para
+ * demostrar la ventana con barras de desplazamiento. */
+ for (i = 0; i < 10; i++)
+ for (j = 0; j < 10; j++) {
+ sprintf (buffer, "botón (%d,%d)\n", i, j);
+ boton = gtk_toggle_button_new_with_label (buffer);
+ gtk_table_attach_defaults (GTK_TABLE (table), boton,
+ i, i+1, j, j+1);
+ gtk_widget_show (boton);
+ }
+
+ /* Añade un botón "close" en la parte de abajo del cuadro de
+ * diálogo */
+ boton = gtk_button_new_with_label ("close");
+ gtk_signal_connect_object (GTK_OBJECT (boton), "clicked",
+ (GtkSignalFunc) gtk_widget_destroy,
+ GTK_OBJECT (ventana));
+
+ /* hace que el botón puede ser elegido por defecto. */
+
+ GTK_WIDGET_SET_FLAGS (boton, GTK_CAN_DEFAULT);
+ gtk_box_pack_start (GTK_BOX (GTK_DIALOG (ventana)->action_area), boton, TRUE, TRUE, 0);
+
+ /* Hace que el botón sea el elegido por defecto. Con pulsar la
+ * tecla "Enter" se activará este botón. */
+ gtk_widget_grab_default (boton);
+ gtk_widget_show (boton);
+
+ gtk_widget_show (ventana);
+
+ gtk_main();
+
+ return(0);
+}
+/* fin del ejemplo */
+</verb></tscreen>
+
+Juegue un poco redimensionando la ventana. Vea como actuan las barras
+de desplazamiento. También puede utilizar la función
+<tt/gtk_widget_set_usize()/ para poner el tamaño por defecto de la
+ventana o de cualquier otro <em/widget/.
+
+<!-- XXX -->
+<!-- ----------------------------------------------------------------- -->
+<sect1>Cajas de botones
+<p>
+Las cajas de botones son útiles para crear grupos de botones. Hay
+cajas horizontales y verticales. Puede crear una nueva caja de botones
+utilizando alguna de las funciones siguientes, que crean
+respectivamente una caja horizontal y otra vertical:
+
+<tscreen><verb>
+GtkWidget *gtk_hbutton_box_new( void );
+
+GtkWidget *gtk_vbutton_box_new( void );
+</verb></tscreen>
+
+Los únicos atributos pertenecientes a las cajas de botones son los
+que definen como se distribuyen los botones. Puede cambiar el
+espaciado que hay entre los botones con:
+
+<tscreen><verb>
+void gtk_hbutton_box_set_spacing_default( gint spacing );
+
+void gtk_vbutton_box_set_spacing_default( gint spacing );
+</verb></tscreen>
+
+Igualmente, se pueden obtener los actuales valores para el espaciado
+utilizando:
+
+<tscreen><verb>
+gint gtk_hbutton_box_get_spacing_default( void );
+
+gint gtk_vbutton_box_get_spacing_default( void );
+</verb></tscreen>
+
+El segundo atributo al que podemos acceder afecta al esquema de los
+botones dentro de la caja. Se establece utilizando:
+
+<tscreen><verb>
+void gtk_hbutton_box_set_layout_default( GtkButtonBoxStyle layout );
+
+void gtk_vbutton_box_set_layout_default( GtkButtonBoxStyle layout );
+</verb></tscreen>
+
+El argumento <tt/layout/ puede tomar uno de los siguientes valores:
+
+<itemize>
+<item> GTK_BUTTONBOX_DEFAULT_STYLE
+<item> GTK_BUTTONBOX_SPREAD
+<item> GTK_BUTTONBOX_EDGE
+<item> GTK_BUTTONBOX_START
+<item> GTK_BUTTONBOX_END
+</itemize>
+
+Puede obtenerse el esquema actual utilizando:
+
+<tscreen><verb>
+GtkButtonBoxStyle gtk_hbutton_box_get_layout_default( void );
+
+GtkButtonBoxStyle gtk_vbutton_box_get_layout_default( void );
+</verb></tscreen>
+
+Podemos añadir botones a una caja de botones utilizando (como
+siempre) la función:
+
+<tscreen><verb>
+ gtk_container_add( GTK_CONTAINER(button_box), child_widget );
+</verb></tscreen>
+
+Aquí hay un ejemplo que ilustra todos los diferentes esquemas que
+podemos utilizar con las cajas de botones.
+
+<tscreen><verb>
+/* principio del ejemplo buttonbox buttonbox.c */
+
+#include <gtk/gtk.h>
+
+/* Crear una Caja de Botones con los parámetros
+ * especificados */
+GtkWidget *create_bbox (gint horizontal,
+ char* title,
+ gint spacing,
+ gint child_w,
+ gint child_h,
+ gint layout)
+{
+ GtkWidget *frame;
+ GtkWidget *bbox;
+ GtkWidget *boton;
+
+ frame = gtk_frame_new (title);
+
+ if (horizontal)
+ bbox = gtk_hbutton_box_new ();
+ else
+ bbox = gtk_vbutton_box_new ();
+
+ gtk_container_set_border_width (GTK_CONTAINER (bbox), 5);
+ gtk_container_add (GTK_CONTAINER (frame), bbox);
+
+ /* Establece la apariencia de la Caja de Botones */
+ gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), layout);
+ gtk_button_box_set_spacing (GTK_BUTTON_BOX (bbox), spacing);
+ gtk_button_box_set_child_size (GTK_BUTTON_BOX (bbox), child_w, child_h);
+
+ boton = gtk_button_new_with_label ("OK");
+ gtk_container_add (GTK_CONTAINER (bbox), boton);
+
+ boton = gtk_button_new_with_label ("Cancel");
+ gtk_container_add (GTK_CONTAINER (bbox), boton);
+
+ boton = gtk_button_new_with_label ("Help");
+ gtk_container_add (GTK_CONTAINER (bbox), boton);
+
+ return(frame);
+}
+
+int main( int argc,
+ char *argv[] )
+{
+ static GtkWidget* ventana = NULL;
+ GtkWidget *main_vbox;
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ GtkWidget *frame_horz;
+ GtkWidget *frame_vert;
+
+ /* Inicializa GTK */
+ gtk_init( &amp;argc, &amp;argv );
+
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title (GTK_WINDOW (ventana), "Button Boxes");
+
+ gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
+ GTK_SIGNAL_FUNC(gtk_main_quit),
+ NULL);
+
+ gtk_container_set_border_width (GTK_CONTAINER (ventana), 10);
+
+ main_vbox = gtk_vbox_new (FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (ventana), main_vbox);
+
+ frame_horz = gtk_frame_new ("Horizontal Button Boxes");
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame_horz, TRUE, TRUE, 10);
+
+ vbox = gtk_vbox_new (FALSE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 10);
+ gtk_container_add (GTK_CONTAINER (frame_horz), vbox);
+
+ gtk_box_pack_start (GTK_BOX (vbox),
+ create_bbox (TRUE, "Spread (spacing 40)", 40, 85, 20, GTK_BUTTONBOX_SPREAD),
+ TRUE, TRUE, 0);
+
+ gtk_box_pack_start (GTK_BOX (vbox),
+ create_bbox (TRUE, "Edge (spacing 30)", 30, 85, 20, GTK_BUTTONBOX_EDGE),
+ TRUE, TRUE, 5);
+
+ gtk_box_pack_start (GTK_BOX (vbox),
+ create_bbox (TRUE, "Start (spacing 20)", 20, 85, 20, GTK_BUTTONBOX_START),
+ TRUE, TRUE, 5);
+
+ gtk_box_pack_start (GTK_BOX (vbox),
+ create_bbox (TRUE, "End (spacing 10)", 10, 85, 20, GTK_BUTTONBOX_END),
+ TRUE, TRUE, 5);
+
+ frame_vert = gtk_frame_new ("Vertical Button Boxes");
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame_vert, TRUE, TRUE, 10);
+
+ hbox = gtk_hbox_new (FALSE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (hbox), 10);
+ gtk_container_add (GTK_CONTAINER (frame_vert), hbox);
+
+ gtk_box_pack_start (GTK_BOX (hbox),
+ create_bbox (FALSE, "Spread (spacing 5)", 5, 85, 20, GTK_BUTTONBOX_SPREAD),
+ TRUE, TRUE, 0);
+
+ gtk_box_pack_start (GTK_BOX (hbox),
+ create_bbox (FALSE, "Edge (spacing 30)", 30, 85, 20, GTK_BUTTONBOX_EDGE),
+ TRUE, TRUE, 5);
+
+ gtk_box_pack_start (GTK_BOX (hbox),
+ create_bbox (FALSE, "Start (spacing 20)", 20, 85, 20, GTK_BUTTONBOX_START),
+ TRUE, TRUE, 5);
+
+ gtk_box_pack_start (GTK_BOX (hbox),
+ create_bbox (FALSE, "End (spacing 20)", 20, 85, 20, GTK_BUTTONBOX_END),
+ TRUE, TRUE, 5);
+
+ gtk_widget_show_all (ventana);
+
+ /* Entra dentro del bucle de eventos */
+ gtk_main ();
+
+ return(0);
+}
+/* fin del ejemplo */
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Barras de herramientas
+<p>
+Las barras de herramientas acostumbran a agrupar un conjunto de
+<em>widgets</em> para hacer más sencilla la personalización
+de su aspecto y composición. Típicamente una barra de herramientas
+consiste en botones con iconos, etiquetas y <em/tips/ para los iconos
+(pequeño texto descriptivo que aparece cuando se mantiene el ratón
+sobre el icono), pero en realidad en una barra se puede poner
+cualquier tipo de <em>widget</em>. Finalmente, los elementos se pueden
+disponer de forma horizontal o vertical, y los botones pueden mostrar
+iconos, etiquetas o ambos.
+
+La creación de una barra de herramientas se hace (como puede que ya
+haya sospechado) mediante la función siguiente:
+
+<tscreen><verb>
+GtkWidget *gtk_toolbar_new( GtkOrientation orientation,
+ GtkToolbarStyle style );
+</verb></tscreen>
+
+donde <tt/orientation/ puede ser:
+
+<tscreen><verb>
+ GTK_ORIENTATION_HORIZONTAL
+ GTK_ORIENTATION_VERTICAL
+</verb></tscreen>
+
+y <tt/style/:
+
+<tscreen><verb>
+ GTK_TOOLBAR_TEXT
+ GTK_TOOLBAR_ICONS
+ GTK_TOOLBAR_BOTH
+</verb></tscreen>
+
+La variable <tt/style/ se aplica a todos los botones que se crean con las
+funciones `item' (pero no a los botones insertados en la barra de
+herramientas como <em>widgets</em> separados).
+
+Después de crear una barra de herramientas, se pueden añadir,
+preañadir e insertar elementos (o sea, botones) en la misma. Los
+campos que describen un elemento son el texto de la etiqueta, el texto
+del <em/tip/, un texto para el <em/tip/ privado, un icono para el
+botón y una función de llamada para el mismo. Por ejemplo, para añadir
+un elemento puede utilizar la siguiente función:
+
+<tscreen><verb>
+GtkWidget *gtk_toolbar_append_item( GtkToolbar *toolbar,
+ const char *text,
+ const char *tooltip_text,
+ const char *tooltip_private_text,
+ GtkWidget *icon,
+ GtkSignalFunc callback,
+ gpointer user_data );
+</verb></tscreen>
+
+Si quiere utilizar <tt/gtk_toolbar_insert_item/, el único parámetro
+adicional que debería especificar es la posición en la que quiere que
+se introduzca el elemento.
+
+Para añadir un espacio en blanco entre los elementos de la barra de
+herramientas, puede utilizar la función siguiente:
+
+<tscreen><verb>
+void gtk_toolbar_append_space( GtkToolbar *toolbar );
+
+void gtk_toolbar_prepend_space( GtkToolbar *toolbar );
+
+void gtk_toolbar_insert_space( GtkToolbar *toolbar,
+ gint posicion );
+
+</verb></tscreen>
+
+Y el tamaño del espacio en blanco puede establecerse globalmente
+para toda una barra de herramientas con la función:
+
+<tscreen><verb>
+void gtk_toolbar_set_space_size( GtkToolbar *toolbar,
+ gint space_size) ;
+</verb></tscreen>
+
+Si tiene que establecer la orientación de una barra de herramientas y
+su estilo, puede hacerlo `al vuelo' con las funciones siguientes:
+
+<tscreen><verb>
+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 );
+</verb></tscreen>
+
+Para mostrar algunas otras cosas que pueden hacerse con una barra de
+herramientas, vamos a ver el siguiente programa (interrumpiremos el
+listado con alguna explicación adicional):
+
+<tscreen><verb>
+#include <gtk/gtk.h>
+
+#include "gtk.xpm"
+
+/* Esta función está conectada al botón Close o a la acción de cerrar
+ * la ventana desde el WM */
+void delete_event (GtkWidget *widget, GdkEvent *event, gpointer data)
+{
+ gtk_main_quit ();
+}
+</verb></tscreen>
+
+Este principio ya debería de sonarle familiar, a no ser que éste sea
+su primer programa GTK. En nuestro programa no habrá ninguna novedad,
+salvo un bonito dibujo XPM que utilizaremos como icono para todos los
+botones.
+
+<tscreen><verb>
+GtkWidget* close_button; // este botón emitirá la señal de cerrar el programa
+GtkWidget* tooltips_button; // para activar/desactivar los tooltips
+GtkWidget* text_button,
+ * icon_button,
+ * both_button; // botones circulares para el estilo de la barra
+GtkWidget* entry; // un widget para meter texto para mostrar como
+ // empaquetar widgets en la barra de herramientas
+</verb></tscreen>
+
+En realidad no necesitamos todos los <em>widgets</em> que acabo de
+poner, pero para aclarar las cosas un poco más los he puesto todos.
+
+<tscreen><verb>
+/* Esto es fácil... cuando uno de los botones cambia, sólo
+ * tenemos que comprobar quien está activo y hacer que el estilo
+ * de la barra de herramientas esté acorde con la elección
+ * ATENCIÓN: ¡nuestra barra de herramientas es data !
+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);
+}
+
+/* todavía más fácil, sólo hay que comprobar el botón de selección
+ * y activar/desactivar los tooltips */
+void toggle_event (GtkWidget *widget, gpointer data)
+{
+ gtk_toolbar_set_tooltips (GTK_TOOLBAR ( data ),
+ GTK_TOGGLE_BUTTON (widget)->active );
+}
+</verb></tscreen>
+
+Lo de arriba son sólo dos funciones de llamada que se invocarán cuando
+se presione uno de los botones de la barra de herramientas. Todo esto
+ya debería resultarle familiar si ha utilizado alguna vez los botones
+de selección (o los botones circulares)
+
+<tscreen><verb>
+int main (int argc, char *argv[])
+{
+ /* Aquí está nuestra ventana principal (un cuadro de diálogo) y una
+ * caja flotante */
+ GtkWidget* dialog;
+ GtkWidget* handlebox;
+
+ /* De acuerdo, necesitamos una barra de herramientas, un icono con
+ * una máscara (una para todos los botones) y un widget icono donde
+ * meter el icono (crearemos un widget diferente para cada botón) */
+ GtkWidget * toolbar;
+ GdkPixmap * icon;
+ GdkBitmap * mask;
+ GtkWidget * iconw;
+
+ /* a esta función se le llama en todas las aplicación GTK */
+ gtk_init (&amp;argc, &amp;argv);
+
+ /* crear una ventana nueva con un título y el tamaño adecuado */
+ dialog = gtk_dialog_new ();
+ gtk_window_set_title ( GTK_WINDOW ( dialog ) , "GTKToolbar Tutorial");
+ gtk_widget_set_usize( GTK_WIDGET ( dialog ) , 600 , 300 );
+ GTK_WINDOW ( dialog ) ->allow_shrink = TRUE;
+
+ /* salimos si alguien intenta cerrarnos */
+ gtk_signal_connect ( GTK_OBJECT ( dialog ), "delete_event",
+ GTK_SIGNAL_FUNC ( delete_event ), NULL);
+
+ /* tenemos que mandar la señalo realize porque utilizamos pixmaps
+ * para los elementos que hay en la barra de herramientas */
+ gtk_widget_realize ( dialog );
+
+ /* para hacerlo más bonito ponemos la barra de herramientas en la
+ * caja flotante, para que así se pueda desatar de la ventana
+ * principal */
+ handlebox = gtk_handle_box_new ();
+ gtk_box_pack_start ( GTK_BOX ( GTK_DIALOG(dialog)->vbox ),
+ handlebox, FALSE, FALSE, 5 );
+</verb></tscreen>
+
+Lo de arriba debería ser parecido en cualquier aplicación GTK. Sólo
+está la inicialización de GTK, la creación de la ventana, etc...
+Solamente hay una cosa que probablemente necesite una explicación:
+una barra de herramientas flotante. Una barra de herramientas flotante
+sólo es otra barra donde pueden empaquetarse <em>widgets</em>. La
+diferencia que tiene con una barra típica es que puede desatarse de la
+ventana padre (o, de hecho, la barra de herramientas flotante permanece
+en el padre, pero reducida a un rectángulo muy pequeño, mientras que
+todos sus contenidos se pasan a una nueva ventana flotante). Es bonito
+tener una barra de herramientas flotante, por lo que estos dos
+<em>widgets</em> suelen aparecer juntos.
+
+<tscreen><verb>
+ /* la barra de herramientas será horizontal, con iconos y texto, y
+ * con un espacio de 5pxl entre elementos y finalmente, la ponemos en
+ * nuestra caja flotante */
+ toolbar = gtk_toolbar_new ( GTK_ORIENTATION_HORIZONTAL,
+ GTK_TOOLBAR_BOTH );
+ gtk_container_border_width ( GTK_CONTAINER ( toolbar ) , 5 );
+ gtk_toolbar_set_space_size ( GTK_TOOLBAR ( toolbar ), 5 );
+ gtk_container_add ( GTK_CONTAINER ( handlebox ) , toolbar );
+
+ /* ahora creamos el icono con la máscara: utilizaremos el widget
+ * icon con todos los elementos de la barra de herramientas */
+ icon = gdk_pixmap_create_from_xpm_d ( dialog->window, &amp;mask,
+ &amp;dialog->style->white, gtk_xpm );
+</verb></tscreen>
+
+Bien, lo que acabamos de escribir es la inicialización del
+<em>widget</em> de la barra de herramientas y la creación de un
+<em>pixmap</em> GDK con su máscara. Si quiere saber algo más sobre la
+utilización de <em>pixmaps</em>, vea la documentación de GDK o la
+sección <ref id="sec_Pixmaps" name="Pixmaps"> en este tutorial.
+
+<tscreen><verb>
+ /* nuestro primer elemento es el botón <close> */
+ iconw = gtk_pixmap_new ( icon, mask ); // icon widget
+ close_button =
+ gtk_toolbar_append_item ( GTK_TOOLBAR (toolbar), // nuestra barra
+ "Close", // etiqueta del botón
+ "Closes this app", // tooltip para el botón
+ "Private", // cadena privada del tooltip
+ iconw, // widget del icono
+ GTK_SIGNAL_FUNC (delete_event), // una señal
+ NULL );
+ gtk_toolbar_append_space ( GTK_TOOLBAR ( toolbar ) ); // espacio después del elemento
+</verb></tscreen>
+
+En el trozo de código de arriba puede ver como se hace la acción más
+simple: añadir un botón a la barra de herramientas. Justo antes de
+añadir un nuevo elemento, tenemos que construir un <em>widget
+pixmap</em> para que sirva como icono para este elemento; este paso
+tendrá que repetirse para cada nuevo elemento. Después del elemento
+añadiremos un espacio en blanco en la barra de herramientas, para que
+los elementos que añadamos a continuación no se toquen los unos a los
+otros. Como puede ver, <tt/gtk_toolbar_append_item/ devuelve un
+puntero al <em>widget</em> de nuestro nuevo botón recien creado, por
+lo que podremos trabajar con él como siempre.
+
+<tscreen><verb>
+ /* ahora, vamos a hacer nuestro grupo de botones circulares... */
+ iconw = gtk_pixmap_new ( icon, mask );
+ icon_button =
+ gtk_toolbar_append_element(GTK_TOOLBAR(toolbar),
+ GTK_TOOLBAR_CHILD_RADIOBUTTON, // un tipo de elemento
+ NULL, // puntero al widget
+ "Icon", // etiqueta
+ "Only icons in toolbar", // tooltip
+ "Private", // cadena privada del tooltip
+ iconw, // icono
+ GTK_SIGNAL_FUNC (radio_event), // señal
+ toolbar); // dato para la señal
+ gtk_toolbar_append_space ( GTK_TOOLBAR ( toolbar ) );
+</verb></tscreen>
+
+Aquí empezamos creando un grupo de botones circulares. Para hacerlo
+hemos utilizado <tt/gtk_toolbar_append_element/. De hecho, utilizando
+esta función se pueden añadir tanto elementos simples como espacios en
+blanco (tipo = GTK_TOOLBAR_CHILD_SPACE o GTK_TOOLBAR_CHILD_BUTTON). En
+el caso de arriba, hemos empezado creando un grupo de botones circulares.
+Para crear más botones circulares para este grupo
+necesitaremos un puntero al botón anterior del grupo, mediante el que
+podremos construir fácilmente una lista de botones (ver la sección
+<ref id="sec_Radio_Buttons" name="Botones circulares"> que se encuentra
+más adelante en este tutorial).
+
+<tscreen><verb>
+ /* los botones circulares que vienen a continuación están
+ relacionados con los anteriores */
+ iconw = gtk_pixmap_new ( icon, mask );
+ 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_pixmap_new ( icon, mask );
+ 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_state(GTK_TOGGLE_BUTTON(both_button),TRUE);
+</verb></tscreen>
+
+Al final hemos activado manualmente uno de los botones (en caso
+contrario los botones permanecerían todos en estado activo,
+impidiéndonos poder cambiar de uno a otro).
+
+<tscreen><verb>
+ /* aquí tenemos un sencillo botón de selección */
+ iconw = gtk_pixmap_new ( icon, mask );
+ 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_state(GTK_TOGGLE_BUTTON(tooltips_button),TRUE);
+</verb></tscreen>
+
+Un botón de selección puede crearse de una forma obvia (si ya sabe como
+crear botones circulares).
+
+<tscreen><verb>
+ /* para empaquetar un widget en la barra de herramientas, sólo
+ * tenemos que crearlo y añadirlo en la barra con el tooltip
+ * apropiado */
+ entry = gtk_entry_new ();
+ gtk_toolbar_append_widget( GTK_TOOLBAR (toolbar),
+ entry,
+ "This is just an entry",
+ "Private" );
+
+ /* bien, no se ha creado con la barra, así que debemos mostrarlo
+ * explicitamente */
+ gtk_widget_show ( entry );
+</verb></tscreen>
+
+Como puede ver, añadir cualquier tipo de <em>widget</em> a la barra
+de herramientas es fácil. Lo único que debe recordar es que este
+<em>widget</em> debe mostrarse manualmente (al contrario que los demás
+elementos que se mostrarán junto con la barra de herramientas).
+
+<tscreen><verb>
+ /* ¡ Eso es ! mostremos algo. */
+ gtk_widget_show ( toolbar );
+ gtk_widget_show (handlebox);
+ gtk_widget_show ( dialog );
+
+ /* quedémonos en gtk_main y ¡esperemos a que empiece la diversión! */
+ gtk_main ();
+
+ return 0;
+}
+</verb></tscreen>
+
+Y ya estamos en el final del tutorial sobre la barra de herramientas.
+Por supuesto, para apreciar completamente el ejemplo, necesita además
+del código este precioso icono XPM que le mostramos a continuación:
+
+<tscreen><verb>
+/* XPM */
+static char * gtk_xpm[] = {
+"32 39 5 1",
+". c none",
+"+ c black",
+"@ c #3070E0",
+"# c #F05050",
+"$ c #35E035",
+"................+...............",
+"..............+++++.............",
+"............+++++@@++...........",
+"..........+++++@@@@@@++.........",
+"........++++@@@@@@@@@@++........",
+"......++++@@++++++++@@@++.......",
+".....+++@@@+++++++++++@@@++.....",
+"...+++@@@@+++@@@@@@++++@@@@+....",
+"..+++@@@@+++@@@@@@@@+++@@@@@++..",
+".++@@@@@@+++@@@@@@@@@@@@@@@@@@++",
+".+#+@@@@@@++@@@@+++@@@@@@@@@@@@+",
+".+##++@@@@+++@@@+++++@@@@@@@@$@.",
+".+###++@@@@+++@@@+++@@@@@++$$$@.",
+".+####+++@@@+++++++@@@@@+@$$$$@.",
+".+#####+++@@@@+++@@@@++@$$$$$$+.",
+".+######++++@@@@@@@++@$$$$$$$$+.",
+".+#######+##+@@@@+++$$$$$$@@$$+.",
+".+###+++##+##+@@++@$$$$$$++$$$+.",
+".+###++++##+##+@@$$$$$$$@+@$$@+.",
+".+###++++++#+++@$$@+@$$@++$$$@+.",
+".+####+++++++#++$$@+@$$++$$$$+..",
+".++####++++++#++$$@+@$++@$$$$+..",
+".+#####+++++##++$$++@+++$$$$$+..",
+".++####+++##+#++$$+++++@$$$$$+..",
+".++####+++####++$$++++++@$$$@+..",
+".+#####++#####++$$+++@++++@$@+..",
+".+#####++#####++$$++@$$@+++$@@..",
+".++####++#####++$$++$$$$$+@$@++.",
+".++####++#####++$$++$$$$$$$$+++.",
+".+++####+#####++$$++$$$$$$$@+++.",
+"..+++#########+@$$+@$$$$$$+++...",
+"...+++########+@$$$$$$$$@+++....",
+".....+++######+@$$$$$$$+++......",
+"......+++#####+@$$$$$@++........",
+".......+++####+@$$$$+++.........",
+".........++###+$$$@++...........",
+"..........++##+$@+++............",
+"...........+++++++..............",
+".............++++..............."};
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Libros de notas (<em/Notebooks/)
+<p>
+El <em/widget/ Notebook es una colección de `páginas' que se solapan
+las unas a las otras, cada una con un contenido diferente. Este
+<em/widget/ se ha vuelto cada vez más común últimamente en la
+programación de interfaces gráficos de usuario (GUI en inglés), y es
+una buena forma de mostrar bloques de información similar que
+necesitan aparecer de forma separada.
+
+La primera función que necesita conocer, como probablemente ya habrá
+adivinado, se utiliza para crear un nuevo <em/widget/ notebook.
+
+<tscreen><verb>
+GtkWidget *gtk_notebook_new( void );
+</verb></tscreen>
+
+Una vez haya crear el libro de notas, hay 12 funciones que se pueden
+utilizar para trabajar con él. Echémosles un vistazo una a una.
+
+La primera que estudiaremos será la que nos permita establecer la
+posición de los indicadores de la página. Estos indicadores se pueden
+poner en cuatro lugares diferentes: arriba, abajo, a la derecha o a la
+izquierda.
+
+<tscreen><verb>
+void gtk_notebook_set_tab_pos( GtkNotebook *notebook,
+ GtkPositionType pos );
+</verb></tscreen>
+
+<tt/GtkPositionType/ debe tener uno de los valores siguientes (su significado
+está bastante claro):
+
+<itemize>
+<item> GTK_POS_LEFT
+<item> GTK_POS_RIGHT
+<item> GTK_POS_TOP
+<item> GTK_POS_BOTTOM
+</itemize>
+
+GTK_POS_TOP es el valor por defecto.
+
+Lo siguiente que estudiaremos es como añadir páginas al libro de notas.
+Hay tres formas de añadirle páginas al <em/widget/. Veamos las dos primeras
+formas (son muy parecidas).
+
+<tscreen><verb>
+void gtk_notebook_append_page( GtkNotebook *notebook,
+ GtkWidget *hijo,
+ GtkWidget *tab_label );
+
+void gtk_notebook_prepend_page( GtkNotebook *notebook,
+ GtkWidget *hijo,
+ GtkWidget *tab_label );
+</verb></tscreen>
+
+Estas funciones le añaden páginas al libro de notas insertándolas desde
+el fondo del libro (añadiéndolas), o desde parte superior del libro
+(preañadiéndolas). <tt/hijo/ es el <em/widget/ que se mete en la página
+del libro de notas, y <tt/tab_label/ es la etiqueta para la página que
+estamos añadiendo.
+
+La función que queda que sirve para añadir una página contiene todas las
+propiedades de las anteriores, pero además permite especificar en que
+posición quiere que esté la página dentro del libro de notas.
+
+<tscreen><verb>
+void gtk_notebook_insert_page( GtkNotebook *notebook,
+ GtkWidget *hijo,
+ GtkWidget *tab_label,
+ gint posicion );
+</verb></tscreen>
+
+Los parámetros son los mismos que habían en las funciones _append_ y
+_prepend_ excepto que hay uno más que antes, <tt/posicion/. Este
+parámetro se utiliza para especificar en que lugar debe introducirse
+la página.
+
+Ahora que sabemos como añadir un página, veamos como podemos eliminar
+una página del libro de notas.
+
+<tscreen><verb>
+void gtk_notebook_remove_page( GtkNotebook *notebook,
+ gint page_num );
+</verb></tscreen>
+
+Esta función coge la página especificada por <tt/page_num/ y la
+elimina del <em/widget/ al que apunta <tt/notebook/.
+
+Para saber cual es la página actual del libro de notas utilice la
+función:
+
+<tscreen><verb>
+gint gtk_notebook_current_page( GtkNotebook *notebook );
+</verb></tscreen>
+
+Las dos funciones siguientes sirven para ir a la página siguiente o a
+la anterior del libro de notas. Para utilizarlas sólo hay que
+proporcionar el <em/widget/ notebook que queremos manipular. Nota:
+cuando el libro de notas está en la última página y se llama a
+<tt/gtk_notebook_next_page/, se pasará a la primera página. Sin
+embargo, si el libro de notas está en la primera página, y se llama a
+<tt/gtk_notebook_prev_page/, no se pasará a la última página.
+
+<tscreen><verb>
+void gtk_notebook_next_page( GtkNoteBook *notebook );
+
+void gtk_notebook_prev_page( GtkNoteBook *notebook );
+</verb></tscreen>
+
+La siguiente función establece la página `activa'. Si quiere que se
+abra el libro de notas por la página 5, por ejemplo, debe utilizar
+esta función. Si no utiliza esta función el libro de notas empezará
+por defecto en la primera página.
+
+<tscreen><verb>
+void gtk_notebook_set_page( GtkNotebook *notebook,
+ gint page_num );
+</verb></tscreen>
+
+Las dos funciones siguientes añaden o eliminan los indicadores de las
+páginas o el borde del libro, respectivamente.
+
+<tscreen><verb>
+void gtk_notebook_set_show_tabs( GtkNotebook *notebook,
+ gint show_tabs);
+
+void gtk_notebook_set_show_border( GtkNotebook *notebook,
+ gint show_border );
+</verb></tscreen>
+
+<tt/show_tabs/ y <tt/show_border/ puede ser TRUE o FALSE.
+
+Ahora echémosle un vistaza a un ejemplo, sacado del código de
+<tt/testgtk.c/ que viene con la distribución de GTK, y que muestra
+la utilización de las 13 funciones. Este pequeño programa crea una
+ventana con un libro de notas y seis botones. El libro de notas
+contiene 11 páginas, incluidas de tres formas diferentes, añadidas,
+insertadas, y preañadidas. Los botones le permiten rotar las
+posiciones de los indicadores, añadir y eliminar los indicadores y el
+borde, eliminar una página, cambiar páginas hacia delante y hacia
+detrás, y salir del programa.
+
+<tscreen><verb>
+/* principio del ejemplo notebook notebook.c */
+
+#include <gtk/gtk.h>
+
+/* Esta función rota la posición de los indicadores */
+void rotate_book (GtkButton *boton, GtkNotebook *notebook)
+{
+ gtk_notebook_set_tab_pos (notebook, (notebook->tab_pos +1) %4);
+}
+
+/* Añade/Elimina los indicadores de la página y los bordes */
+void tabsborder_book (GtkButton *boton, GtkNotebook *notebook)
+{
+ gint tval = FALSE;
+ gint bval = FALSE;
+ if (notebook->show_tabs == 0)
+ tval = TRUE;
+ if (notebook->show_border == 0)
+ bval = TRUE;
+
+ gtk_notebook_set_show_tabs (notebook, tval);
+ gtk_notebook_set_show_border (notebook, bval);
+}
+
+/* Elimina una página del libro de notas */
+void remove_book (GtkButton *boton, GtkNotebook *notebook)
+{
+ gint page;
+
+ page = gtk_notebook_current_page(notebook);
+ gtk_notebook_remove_page (notebook, page);
+ /* Hay que redibujar el widget --
+ Esto fuerza que el widget se autoredibuje */
+ gtk_widget_draw(GTK_WIDGET(notebook), NULL);
+}
+
+void delete (GtkWidget *widget, GtkWidget *event, gpointer data)
+{
+ gtk_main_quit ();
+}
+
+int main (int argc, char *argv[])
+{
+ GtkWidget *ventana;
+ GtkWidget *boton;
+ GtkWidget *table;
+ GtkWidget *notebook;
+ GtkWidget *frame;
+ GtkWidget *etiqueta;
+ GtkWidget *checkbutton;
+ int i;
+ char bufferf[32];
+ char bufferl[32];
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ gtk_signal_connect (GTK_OBJECT (ventana), "delete_event",
+ GTK_SIGNAL_FUNC (delete), NULL);
+
+ gtk_container_border_width (GTK_CONTAINER (ventana), 10);
+
+ table = gtk_table_new(2,6,TRUE);
+ gtk_container_add (GTK_CONTAINER (ventana), table);
+
+ /* Crea un nuevo libro de notas, indicando la posición de los
+ indicadores */
+ notebook = gtk_notebook_new ();
+ gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_TOP);
+ gtk_table_attach_defaults(GTK_TABLE(table), notebook, 0,6,0,1);
+ gtk_widget_show(notebook);
+
+ /* le añadimos un montón de páginas al libro de notas */
+ for (i=0; i < 5; i++) {
+ sprintf(bufferf, "Append Frame %d", i+1);
+ sprintf(bufferl, "Page %d", i+1);
+
+ frame = gtk_frame_new (bufferf);
+ gtk_container_border_width (GTK_CONTAINER (frame), 10);
+ gtk_widget_set_usize (frame, 100, 75);
+ gtk_widget_show (frame);
+
+ etiqueta = gtk_label_new (bufferf);
+ gtk_container_add (GTK_CONTAINER (frame), etiqueta);
+ gtk_widget_show (etiqueta);
+
+ etiqueta = gtk_label_new (bufferl);
+ gtk_notebook_append_page (GTK_NOTEBOOK (notebook), frame, etiqueta);
+ }
+
+
+ /* Ahora añadimos una página en punto específico */
+ checkbutton = gtk_check_button_new_with_label ("Check me please!");
+ gtk_widget_set_usize(checkbutton, 100, 75);
+ gtk_widget_show (checkbutton);
+
+ etiqueta = gtk_label_new ("Add spot");
+ gtk_container_add (GTK_CONTAINER (checkbutton), etiqueta);
+ gtk_widget_show (etiqueta);
+ etiqueta = gtk_label_new ("Add page");
+ gtk_notebook_insert_page (GTK_NOTEBOOK (notebook), checkbutton, etiqueta, 2);
+
+ /* Y finalmente preañadimos páginas en el libro de notas */
+ for (i=0; i < 5; i++) {
+ sprintf(bufferf, "Prepend Frame %d", i+1);
+ sprintf(bufferl, "PPage %d", i+1);
+
+ frame = gtk_frame_new (bufferf);
+ gtk_container_border_width (GTK_CONTAINER (frame), 10);
+ gtk_widget_set_usize (frame, 100, 75);
+ gtk_widget_show (frame);
+
+ etiqueta = gtk_label_new (bufferf);
+ gtk_container_add (GTK_CONTAINER (frame), etiqueta);
+ gtk_widget_show (etiqueta);
+
+ etiqueta = gtk_label_new (bufferl);
+ gtk_notebook_prepend_page (GTK_NOTEBOOK(notebook), frame, etiqueta);
+ }
+
+ /* Decimos en que página empezar (página 4) */
+ gtk_notebook_set_page (GTK_NOTEBOOK(notebook), 3);
+
+
+ /* creamos un montón de botones */
+ boton = gtk_button_new_with_label ("close");
+ gtk_signal_connect_object (GTK_OBJECT (boton), "clicked",
+ GTK_SIGNAL_FUNC (delete), NULL);
+ gtk_table_attach_defaults(GTK_TABLE(table), boton, 0,1,1,2);
+ gtk_widget_show(boton);
+
+ boton = gtk_button_new_with_label ("next page");
+ gtk_signal_connect_object (GTK_OBJECT (boton), "clicked",
+ (GtkSignalFunc) gtk_notebook_next_page,
+ GTK_OBJECT (notebook));
+ gtk_table_attach_defaults(GTK_TABLE(table), boton, 1,2,1,2);
+ gtk_widget_show(boton);
+
+ boton = gtk_button_new_with_label ("prev page");
+ gtk_signal_connect_object (GTK_OBJECT (boton), "clicked",
+ (GtkSignalFunc) gtk_notebook_prev_page,
+ GTK_OBJECT (notebook));
+ gtk_table_attach_defaults(GTK_TABLE(table), boton, 2,3,1,2);
+ gtk_widget_show(boton);
+
+ boton = gtk_button_new_with_label ("tab position");
+ gtk_signal_connect_object (GTK_OBJECT (boton), "clicked",
+ (GtkSignalFunc) rotate_book, GTK_OBJECT(notebook));
+ gtk_table_attach_defaults(GTK_TABLE(table), boton, 3,4,1,2);
+ gtk_widget_show(boton);
+
+ boton = gtk_button_new_with_label ("tabs/border on/off");
+ gtk_signal_connect_object (GTK_OBJECT (boton), "clicked",
+ (GtkSignalFunc) tabsborder_book,
+ GTK_OBJECT (notebook));
+ gtk_table_attach_defaults(GTK_TABLE(table), boton, 4,5,1,2);
+ gtk_widget_show(boton);
+
+ boton = gtk_button_new_with_label ("remove page");
+ gtk_signal_connect_object (GTK_OBJECT (boton), "clicked",
+ (GtkSignalFunc) remove_book,
+ GTK_OBJECT(notebook));
+ gtk_table_attach_defaults(GTK_TABLE(table), boton, 5,6,1,2);
+ gtk_widget_show(boton);
+
+ gtk_widget_show(table);
+ gtk_widget_show(ventana);
+
+ gtk_main ();
+
+ return 0;
+}
+/* fin del ejemplo */
+</verb></tscreen>
+
+Espero que la explicación le ayude de alguna manera a crear libros de
+notas en sus aplicaciones GTK.
+
+<!-- ***************************************************************** -->
+<sect> El <em/widget/ GtkCList
+<!-- ***************************************************************** -->
+<!-- ----------------------------------------------------------------- -->
+<p>
+El <em>widget</em> GtkCList ha reemplazado al <em>widget</em> GtkList
+(que sigue estando disponible).
+
+El <em>widget</em> GtkCList es un <em>widget</em> de una lista
+multicolumna que es capaz de manejar, literalmente, miles de filas de
+información. Cada columna puede tener (opcionalmente) un título, que
+puede estar activado (opcionalmente), permitiéndonos enlazar una
+función con la selección.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Creando un <em>widget</em> GtkCList
+<p>
+Crear un GtkCList es algo bastante sencillo, una vez que sabe como
+crear un <em>widget</em> en general. Se proporcionan al menos dos
+formas estándar de crearlo, la forma fácil y la forma difícil. Pero
+antes de crear una GtkCList, hay una cosa que debemos saber: ¿Cuántas
+columnas va a tener?
+
+No todas las columnas tienen que ser visibles y pueden utilizarse para
+almacenar datos que estén relacionados con una cierta celda de la
+lista.
+
+<tscreen><verb>
+GtkWidget *gtk_clist_new ( gint columns );
+
+GtkWidget *gtk_clist_new_with_titles( gint columns,
+ gchar *titles[] );
+</verb></tscreen>
+
+Esta primera aproximación al problema es muy sencilla, pero la segunda
+requerirá alguna explicación adicional. Cada columna puede tener un
+título asociado. Si utilizamos la segunda forma, deberemos proporcionar
+punteros al texto del título, y el número de punteros debe ser igual
+al número de columnas especificadas. Por supuesto, siempre podemos
+utilizar la primera forma de creación y añadir más tarde los títulos
+de forma manual.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Modos de operación
+<p>
+Hay varios atributos que pueden utilizarse para alterar el aspecto
+de un GtkCList. Primero tenemos
+
+<tscreen><verb>
+void gtk_clist_set_selection_mode( GtkCList *clist,
+ GtkSelectionMode mode );
+</verb></tscreen>
+
+que, como el propio nombre indica, establece el modo de selección de la
+lista GtkCList. El primer argumento es el <em>widget</em> GtkCList, y el
+segundo especifica el modo de selección de la celda (están definidos
+en <tt/gtkenums.h/). En el momento de escribir esto, estaban
+disponibles los siguientes modos:
+
+<itemize>
+<item> GTK_SELECTION_SINGLE - La selección o es NULL o contiene un
+puntero GList a un elemento seleccionado.
+
+<item> GTK_SELECTION_BROWSE - La selección es NULL si la lista no
+contiene <em>widgets</em> o si los que contiene son insensibles, en
+caso contrario contendrá un puntero GList hacia una estructura GList,
+y por tanto con exactamente un elemento.
+
+<item> GTK_SELECTION_MULTIPLE - La selección es NULL si no hay
+seleccionados una lista de elementos o un puntero GList para el primer
+elemento seleccionado.<!-- FIXME: Todo esto no se si tiene sentido -->
+Éste apunta de nuevo a una estructura GList para el segundo elemento
+seleccionado y continua así. Éste es, actualmente, el modo por
+<bf>defecto</bf> para el <em>widget</em> GtkCList.
+
+<item> GTK_SELECTION_EXTENDED - La selección siempre es NULL.
+</itemize>
+
+Puede que se añadan otros modos en versiones posteriores de GTK.
+
+También tenemos
+
+<tscreen><verb>
+void gtk_clist_set_policy (GtkCList *clist,
+ GtkPolicyType vscrollbar_policy,
+ GtkPolicyType hscrollbar_policy);
+</verb></tscreen>
+
+que define que es lo que ocurre con las barras de desplazamiento. Los
+siguientes valores son los posibles para las barras de desplazamiento
+horizontal y vertical:
+
+<itemize>
+<item> GTK_POLICY_ALWAYS - La barra de desplazamiento siempre está ahí.
+
+<item> GTK_POLICY_AUTOMATIC - La barra de desplazamiento estará ahí sólo
+cuando el número de elementos en la GtkCList supere el número que puede
+mostrarse en el <em>widget</em>.
+</itemize>
+
+También podemos definir como debería ser el aspecto del borde del
+<em>widget</em> GtkCList. Esto lo podemos hacer mediante
+
+<tscreen><verb>
+void gtk_clist_set_border( GtkCList *clist,
+ GtkShadowType border );
+</verb></tscreen>
+
+Y los posibles valores para el segundo argumento son
+
+<itemize>
+<item> GTK_SHADOW_NONE
+
+<item> GTK_SHADOW_IN
+
+<item> GTK_SHADOW_OUT
+
+<item> GTK_SHADOW_ETCHED_IN
+
+<item> GTK_SHADOW_ETCHED_OUT
+</itemize>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Trabajando con los títulos
+<p>
+Cuando cree un <em>widget</em> GtkCList, también obtendrá
+automáticamente un conjunto de botones título. Vivirán en lo alto de
+una ventana CList, y pueden actuar como botones normales que responden
+cuando se pulsa sobre ellos, o bien pueden ser pasivos, en cuyo caso
+no serán nada más que un título. Hay cuatro llamadas diferentes que
+nos ayudarán a establecer el estado de los botones título.
+
+<tscreen><verb>
+void gtk_clist_column_title_active( GtkCList *clist,
+ gint column );
+
+void gtk_clist_column_title_passive( GtkCList *clist,
+ gint column );
+
+void gtk_clist_column_titles_active( GtkCList *clist );
+
+void gtk_clist_column_titles_passive( GtkCList *clist );
+</verb></tscreen>
+
+Un título activo es aquel que actua como un botón normal, y uno pasivo
+es sólo una etiqueta. Las primeras dos llamadas de arriba
+activarán/desactivarán el botón título correspondiente a la columna
+<tt/column/, mientras que las dos llamadas siguientes
+activarán/desactivarán todos los botones título que hayan en el
+<em>widget</em> <tt/clist/ que se le proporcione a la función.
+
+Pero, por supuesto, habrá casos en el que no querremos utilizar los
+botones título, así que también tenemos la posibilidad de ocultarlos y
+de volverlos a mostrar utilizando las dos llamadas siguientes:
+
+<tscreen><verb>
+void gtk_clist_column_titles_show( GtkCList *clist );
+
+void gtk_clist_column_titles_hide( GtkCList *clist );
+</verb></tscreen>
+
+Para que los títulos sean realmente útiles necesitamos un mecanismo
+que nos permita darles el valor que nosotros queramos y cambiar ese
+valor, y podremos hacerlo mediante
+
+<tscreen><verb>
+void gtk_clist_set_column_title( GtkCList *clist,
+ gint column,
+ gchar *title );
+</verb></tscreen>
+
+Debe llevar cuidado, ya que sólo se puede especificar el título de una
+columna a la vez, por lo que si conoce todos los títulos desde el
+principio, le sugiero que utilice <tt/gtk_clist_new_with_titles/ (como
+se describe arriba) para establecerlos adecuadamente. Le ahorrará
+tiempo de programación, y hará su programa más pequeño. Hay algunos
+casos donde es mejor utilizar la forma manual, y uno de ellos es
+cuando no todos los títulos son texto. GtkCList nos proporciona
+botones título que pueden, de hecho, incorporar un <em>widget</em>
+entero, por ejemplo un <em>pixmap</em>. Todo esto se hace mediante
+
+<tscreen><verb>
+void gtk_clist_set_column_widget( GtkCList *clist,
+ gint column,
+ GtkWidget *widget );
+</verb></tscreen>
+
+que no debería necesitar de explicaciones adicionales.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Manipulando la lista en sí.
+<p>
+Es posible cambiar la justificación de una columna, y esto se hace
+mediante
+
+<tscreen><verb>
+void gtk_clist_set_column_justification( GtkCList *clist,
+ gint column,
+ GtkJustification justification );
+</verb></tscreen>
+
+El tipo GtkJustification puede tomar los valores siguientes:
+
+<itemize>
+<item>GTK_JUSTIFY_LEFT - El texto en la columna empezará desde el lado
+izquierdo.
+
+<item>GTK_JUSTIFY_RIGHT - El texto en la columna empezará desde el
+lado derecho.
+
+<item>GTK_JUSTIFY_CENTER - El texto se colocará en el centro de la
+columna.
+
+<item>GTK_JUSTIFY_FILL - El texto utilizará todo el espacio disponible
+en la columna. Normalmente se hace añadiendo espacios en blanco entre
+las palabras (o entre letras por separado, si se trata de una sola
+palabra). Más o menos de la misma forma en la que lo hace un
+procesador de textos WYSIWYG.
+</itemize>
+
+La siguiente función es muy importante, y debería ser un estándar
+para inicializar todos los <em>widgets</em> GtkCList. Cuando se crea
+la lista, los anchos de las distintas columnas se eligen para que
+coincidan con sus títulos, y éste es el ancho adecuado que tenemos que
+poner, utilizando
+
+<tscreen><verb>
+void gtk_clist_set_column_width( GtkCList *clist,
+ gint column,
+ gint width );
+</verb></tscreen>
+
+Observe que el ancho viene dado en pixeles y no en letras. Lo mismo
+vale para el alto de la celda en las columnas, pero como el valor por
+defecto es la altura del tipo de letra actual, no es algo tan crítico
+para la aplicación. De todas formas, la altura se cambia mediante
+
+<tscreen><verb>
+void gtk_clist_set_row_height( GtkCList *clist,
+ gint height );
+</verb></tscreen>
+
+De nuevo, hay que advertir que el ancho viene dado en pixeles.
+
+También podemos ir hacia un elemento sin la intervención del usuario,
+sin embargo hace falta que sepamos hacia donde queremos ir. O en otras
+palabras, necesitamos la fila y la columna del elemento al que queremos
+pasar.
+
+<tscreen><verb>
+void gtk_clist_moveto( GtkCList *clist,
+ gint row,
+ gint column,
+ gfloat row_align,
+ gfloat col_align );
+</verb></tscreen>
+
+Es importante comprender bien el significado de <tt/gfloat
+row_align/. Tiene un valor entre 0.0 y 1.0, donde 0.0 significa que
+debemos hacer que la fila seleccionada aparezca en la alto de la
+lista, mientras que 1.0 significa que la fila aparecerá en la parte de
+abajo. El resto de valores entre 0.0 y 1.0 son válidos y harán que la
+fila aparezca entre la parte superior y la inferior. El último
+argumento, <tt/gfloat col_align/ funciona igual, siendo 0.0 la
+izquierda y 1.0 la derecha.
+
+Dependiendo de las necesidades de la aplicación, puede que no tengamos
+que hacer un desplazamiento hacia un elemento que ya sea visible. Por
+tanto, ¿cómo podemos saber si ya es visible? Como siempre, hay una función
+que sirve para averiguarlo
+
+<tscreen><verb>
+GtkVisibility gtk_clist_row_is_visible( GtkCList *clist,
+ gint row );
+</verb></tscreen>
+
+El valor devuelto es uno de los siguientes:
+
+<itemize>
+<item>GTK_VISIBILITY_NONE
+
+<item>GTK_VISIBILITY_PARTIAL
+
+<item>GTK_VISIBILITY_FULL
+</itemize>
+
+Como puede ver, sólo nos dice si una fila es visible. Actualmente no hay
+ninguna forma de obtener el mismo dato para una columna. Sin embargo
+podemos obtener información parcial, porque si el valor devuelto es
+GTK_VISIBILITY_PARTIAL, entonces es que alguna parte está oculta,
+pero no sabemos si es la fila que está cortada por la parte de abajo
+de la lista, o si la fila tiene columnas que están fuera.
+
+También podemos cambiar el color del primer y del segundo plano de una
+fila en particular. Esto es útil para marcar la fila seleccionada por
+el usuario, y las dos funciones que hay que utilizar son
+
+<tscreen><verb>
+void gtk_clist_set_foreground( GtkCList *clist,
+ gint row,
+ GdkColor *color );
+
+void gtk_clist_set_background( GtkCList *clist,
+ gint row,
+ GdkColor *color );
+</verb></tscreen>
+
+Cuidado, ya que los colores deben estar asignados previamente en la
+memoria.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Añadiendo filas a la lista
+<p>
+Podemos añadir filas de dos formas. Se pueden añadir al final de la lista
+utilizando
+
+<tscreen><verb>
+gint gtk_clist_append( GtkCList *clist,
+ gchar *text[] );
+</verb></tscreen>
+
+o podemos insertar una fila en un lugar determinado utilizando
+
+<tscreen><verb>
+void gtk_clist_insert( GtkCList *clist,
+ gint row,
+ gchar *text[] );
+</verb></tscreen>
+
+En ambas llamadas podemos proporcionar un conjunto de punteros que
+serán los textos que queremos poner en las columnas. El número de
+punteros debe ser igual al número de columnas en la lista. Si el
+argumento <tt/text[]/ es NULL, entonces no habrá texto en las columnas
+de la fila. Esto sería útil, por ejemplo, si queremos añadir
+<em>pixmaps</em> en lugar de texto (en general para cualquier cosa que
+haya que hacer manualmente).
+
+De nuevo, cuidado ya que el número de filas y de columnas comienza en
+0.
+
+Para eliminar una fila individual podemos utilizar
+
+<tscreen><verb>
+void gtk_clist_remove( GtkCList *clist,
+ gint row );
+</verb></tscreen>
+
+Hay también una llamada que elimina todas las filas en la lista.
+Es mucho más rápido que llamar a <tt/gtk_clist_remove/ una vez por
+cada fila, que sería la única alternativa.
+
+<tscreen><verb>
+void gtk_clist_clear( GtkCList *clist );
+</verb></tscreen>
+
+También hay dos funciones que es conveniente utilizarlas cuando hay
+que hacerle muchos cambios a una lista. Son para evitar que la lista
+parpadee mientras es actualizada repetidamente, que puede ser muy
+molesto para el usuario. Por tanto es una buena idea congelar la
+lista, hacer los cambios, y descongelarla, que hará que la lista se
+actualice en la pantalla.
+
+<tscreen><verb>
+void gtk_clist_freeze( GtkCList * clist );
+
+void gtk_clist_thaw( GtkCList * clist );
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Poniendo texto y <em>pixmaps</em> en las celdas
+<p>
+Una celda puede contener un <em>pixmap</em>, texto o ambos. Para ponerlos
+en las celdas, podemos utilizar las siguientes funciones.
+
+<tscreen><verb>
+void gtk_clist_set_text( GtkCList *clist,
+ gint row,
+ gint column,
+ gchar *text );
+
+void gtk_clist_set_pixmap( GtkCList *clist,
+ gint row,
+ gint column,
+ GdkPixmap *pixmap,
+ GdkBitmap *mask );
+
+void gtk_clist_set_pixtext( GtkCList *clist,
+ gint row,
+ gint column,
+ gchar *text,
+ guint8 spacing,
+ GdkPixmap *pixmap,
+ GdkBitmap *mask );
+</verb></tscreen>
+
+Son bastante sencillas de entender. Todas las llamadas tienen la
+GtkCList como primer argumento, seguidas por la fila y la columna
+de la celda, y seguidas por el dato que debe ponerse en la celda. El
+argumento <tt/gint8 spacing/ en <tt/gtk_clist_set_pixtext/ es el
+número de <em>pixels</em> entre el <em>pixmap</em> y el principio del
+texto.
+
+Para leer los datos que hay en una celda, podemos utilizar
+
+<tscreen><verb>
+gint gtk_clist_get_text( GtkCList *clist,
+ gint row,
+ gint column,
+ gchar **text );
+
+gint gtk_clist_get_pixmap( GtkCList *clist,
+ gint row,
+ gint column,
+ GdkPixmap **pixmap,
+ GdkBitmap **mask );
+
+gint gtk_clist_get_pixtext( GtkCList *clist,
+ gint row,
+ gint column,
+ gchar **text,
+ guint8 *spacing,
+ GdkPixmap **pixmap,
+ GdkBitmap **mask );
+</verb></tscreen>
+
+No es necesario leer todos los datos en caso de que no estemos
+interesados. Cualquiera de los punteros que se supone contendrán los
+valores a devolver (cualquiera excepto el <tt/clist/) pueden ser
+NULL. Por lo que si sólo queremos leer el texto de una celda que es de
+tipo <tt/pixtext/, deberíamos hacer lo siguiente, suponiendo que
+<tt/clist/, <tt/row/ y <tt/column/ ya existan:
+
+<tscreen><verb>
+gchar *mytext;
+
+gtk_clist_get_pixtext(clist, row, column, &amp;mytext, NULL, NULL, NULL);
+</verb></tscreen>
+
+Hay una rutina más que está relacionada con lo que está dentro
+de una celda de una <tt/clist/, y es
+
+<tscreen><verb>
+GtkCellType gtk_clist_get_cell_type( GtkCList *clist,
+ gint row,
+ gint column );
+</verb></tscreen>
+
+que devuelve el tipo de datos que hay en la celda. El valor devuelto es
+uno de los siguientes
+
+<itemize>
+<item>GTK_CELL_EMPTY
+
+<item>GTK_CELL_TEXT
+
+<item>GTK_CELL_PIXMAP
+
+<item>GTK_CELL_PIXTEXT
+
+<item>GTK_CELL_WIDGET
+</itemize>
+
+También hay una función que nos permite especificar la indentación de
+un celda (horizontal o vertical). El valor de la indentación es del
+tipo <tt/gint/, viene dado en <em>pixeles</em>, y puede ser positivo o
+negativo.
+
+<tscreen><verb>
+void gtk_clist_set_shift( GtkCList *clist,
+ gint row,
+ gint column,
+ gint vertical,
+ gint horizontal );
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Almacenando punteros a datos
+<p>
+Con una GtkCList es posible poner un puntero a datos en una
+fila. Este puntero no será visible al usuario, pero puede serle útil
+al programador.
+
+De nuevo, las funciones son lo suficientemente autoexplicativas
+
+<tscreen><verb>
+void gtk_clist_set_row_data( GtkCList *clist,
+ gint row,
+ gpointer data );
+
+void gtk_clist_set_row_data_full( GtkCList *clist,
+ gint row,
+ gpointer data,
+ GtkDestroyNotify destroy );
+
+gpointer gtk_clist_get_row_data( GtkCList *clist,
+ gint row );
+
+gint gtk_clist_find_row_from_data( GtkCList *clist,
+ gpointer data );
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Trabajando con la selección
+<p>
+También hay funciones que nos permiten forzar la (de)selección de una
+fila. Son
+
+<tscreen><verb>
+void gtk_clist_select_row( GtkCList *clist,
+ gint row,
+ gint column );
+
+void gtk_clist_unselect_row( GtkCList *clist,
+ gint row,
+ gint column );
+</verb></tscreen>
+
+Y también una función que tomará las coordenadas x e y (por ejemplo,
+recibidas del ratón), mirará en la lista y devolverá la fila y la
+columna que les corresponden.
+
+<tscreen><verb>
+gint gtk_clist_get_selection_info( GtkCList *clist,
+ gint x,
+ gint y,
+ gint *row,
+ gint *column );
+</verb></tscreen>
+
+Cuando detectemos algo interesante, como por ejemplo el movimiento del
+ratón, o una pulsación en cualquier lugar de la lista, podemos leer
+las coordenadas del ratón y encontrar en que elemento de la lista se
+encuentra. ¿Engorroso? Afortunadamente, hay una forma más sencilla de
+hacer las cosas...
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Las señales que lo hacen todo
+<p>
+Como con el resto de <em>widgets</em>, hay unas cuantas señales que
+podemos utilizar. El <em>widget</em> GtkCList está derivado del
+<em>widget</em> GtkContainer, y por tanto tiene las mismas
+señales que éste, pero además añade las siguientes:
+
+<itemize>
+<item><tt/select_row/ - Esta señal enviará la siguiente información,
+en este orden: GtkCList *clist, gint row, gint column, GtkEventButton
+*event
+
+<item><tt/unselect_row/ - Cuando el usuario deselecciona una fila, se
+activará esta señal. Envia la misma información que <tt/select_row/
+
+<item><tt/click_column/ - Envia GtkCList *clist, gint column
+</itemize>
+
+Por tanto si queremos conectar una llamada a <tt/select_row/, la
+llamada se deberá declarar como
+
+<tscreen><verb>
+void select_row_callback(GtkWidget *widget,
+ gint row,
+ gint column,
+ GdkEventButton *event,
+ gpointer data);
+</verb></tscreen>
+
+La llamada se conectará, como siempre, con
+
+<tscreen><verb>
+gtk_signal_connect(GTK_OBJECT( clist),
+ "select_row"
+ GTK_SIGNAL_FUNC(select_row_callback),
+ NULL);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Un ejemplo GtkCList
+<p>
+
+<tscreen><verb>
+/* principio del ejemplo clist clist.c */
+
+#include <gtk/gtk.h>
+#include <glib.h>
+
+/* Aquí tenemos algunos prototipos de las funciones de llamada */
+void button_add_clicked( GtkWidget *boton, gpointer data);
+void button_clear_clicked( GtkWidget *boton, gpointer data);
+void button_hide_show_clicked( GtkWidget *boton, gpointer data);
+void selection_made( GtkWidget *clist, gint row, gint column,
+ GdkEventButton *event, gpointer data);
+
+gint main (int argc, gchar *argv[])
+{
+ GtkWidget *ventana;
+ GtkWidget *vbox, *hbox;
+ GtkWidget *clist;
+ GtkWidget *button_add, *button_clear, *button_hide_show;
+ gchar *titles[2] = {"Ingredients","Amount"};
+
+ gtk_init(&amp;argc, &amp;argv);
+
+
+ ventana=gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_widget_set_usize(GTK_WIDGET(ventana), 300, 150);
+
+ gtk_window_set_title(GTK_WINDOW(ventana), "GtkCList Example");
+ gtk_signal_connect(GTK_OBJECT(ventana),
+ "destroy",
+ GTK_SIGNAL_FUNC(gtk_main_quit),
+ NULL);
+
+ vbox=gtk_vbox_new(FALSE, 5);
+ gtk_container_border_width(GTK_CONTAINER(vbox), 5);
+ gtk_container_add(GTK_CONTAINER(ventana), vbox);
+ gtk_widget_show(vbox);
+
+ /* Crear el GtkCList. Para este ejemplo utilizaremos 2 columnas */
+ clist = gtk_clist_new_with_titles( 2, titles);
+
+ /* Cuando se hace una selección, queremos saber algo acerca de
+ * ella. La función de llamada utilizada es selection_made, y su
+ * código lo podemos encontrar más abajo */
+ gtk_signal_connect(GTK_OBJECT(clist), "select_row",
+ GTK_SIGNAL_FUNC(selection_made),
+ NULL);
+
+ /* No es necesario ponerle sombra al borde, pero es bonito :) */
+ gtk_clist_set_border(GTK_CLIST(clist), GTK_SHADOW_OUT);
+
+ /* Lo que sí que es importante, es poner el ancho de las columnas
+ * ya no tendrán el valor correcto en caso contrario. Recuerde que
+ * las columnas se numeran desde el 0 en adelante (hasta el 1 en
+ * este caso).
+ */
+ gtk_clist_set_column_width (GTK_CLIST(clist), 0, 150);
+
+ /* Scollbars _only when needed_ */
+ gtk_clist_set_policy(GTK_CLIST(clist), GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+
+ /* Añade el widget GtkCList a la caja vertical y lo muestra. */
+ gtk_box_pack_start(GTK_BOX(vbox), clist, TRUE, TRUE, 0);
+ gtk_widget_show(clist);
+
+ /* Crea los botones y los añade a la ventana. Ver la parte del
+ * tutorial sobre botones para ver más ejemplos y comentarios
+ * acerca de todo esto.
+ */
+ hbox = gtk_hbox_new(FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
+ gtk_widget_show(hbox);
+
+ button_add = gtk_button_new_with_label("Add List");
+ button_clear = gtk_button_new_with_label("Clear List");
+ button_hide_show = gtk_button_new_with_label("Hide/Show titles");
+
+ gtk_box_pack_start(GTK_BOX(hbox), button_add, TRUE, TRUE, 0);
+ gtk_box_pack_start(GTK_BOX(hbox), button_clear, TRUE, TRUE, 0);
+ gtk_box_pack_start(GTK_BOX(hbox), button_hide_show, TRUE, TRUE, 0);
+
+ /* Conectar nuestras funciones de llamada a los tres botones */
+ gtk_signal_connect_object(GTK_OBJECT(button_add), "clicked",
+ GTK_SIGNAL_FUNC(button_add_clicked),
+ (gpointer) clist);
+ gtk_signal_connect_object(GTK_OBJECT(button_clear), "clicked",
+ GTK_SIGNAL_FUNC(button_clear_clicked),
+ (gpointer) clist);
+ gtk_signal_connect_object(GTK_OBJECT(button_hide_show), "clicked",
+ GTK_SIGNAL_FUNC(button_hide_show_clicked),
+ (gpointer) clist);
+
+ gtk_widget_show(button_add);
+ gtk_widget_show(button_clear);
+ gtk_widget_show(button_hide_show);
+
+ /* Ahora hemos terminado el interface y sólo nos queda mostrar la
+ * ventana y entrar en el bucle gtk_main.
+ */
+ gtk_widget_show(ventana);
+ gtk_main();
+
+ return 0;
+}
+
+/* El usuario pulsó el botón "Add List". */
+void button_add_clicked( GtkWidget *boton, gpointer data)
+{
+ int indx;
+
+ /* Algo tonto que añadir a la lista. 4 filas con 2 columnas cada
+ * una
+ */
+ gchar *drink[4][2] = {{"Milk", "3 Oz"},
+ {"Water", "6 l"},
+ {"Carrots", "2"},
+ {"Snakes", "55"}};
+
+ /* Aquí hacemos la adición del texto. Se hace una vez por cada
+ * fila.
+ */
+ for( indx=0; indx < 4; indx++)
+ gtk_clist_append( (GtkCList*) data, drink[indx]);
+
+ return;
+}
+
+/* El usuario pulsó el botón "Clear List" */
+void button_clear_clicked( GtkWidget *boton, gpointer data)
+{
+ /* Borrar la lista utilizando gtk_clist_clear. Esto es mucho más
+ * rápido que llamar a gtk_clist_remove una vez por cada fila.
+ */
+ gtk_clist_clear((GtkCList*) data);
+
+ return;
+}
+
+/* El usuario pulsó el botón "Hide/Show titles". */
+void button_hide_show_clicked( GtkWidget *boton, gpointer data)
+{
+ /* Una bandera para recordar el estado. 0 = actualmente visible */
+ static short int flag = 0;
+
+ if (flag == 0)
+ {
+ /* Oculta los títulos y pone la bandera a 1 */
+ gtk_clist_column_titles_hide((GtkCList*) data);
+ flag++;
+ }
+ else
+ {
+ /* Muestra los títulos y pone la bandera a 0 */
+ gtk_clist_column_titles_show((GtkCList*) data);
+ flag--;
+ }
+
+ return;
+}
+
+/* Se llegamos aquí, entonces el usuario ha seleccionado una fila de
+ * la lista.
+ */
+void selection_made( GtkWidget *clist, gint row, gint column,
+ GdkEventButton *event, gpointer data)
+{
+ gchar *text;
+
+ /* Obtiene el texto que se ha almacenado en la fila y columna
+ * sobre las que se ha pulsado. Lo recibiremos como un puntero en
+ * el argumento text.
+ */
+ gtk_clist_get_text(GTK_CLIST(clist), row, column, &amp;text);
+
+ /* Imprime alguna información sobre la fila seleccionada */
+ g_print("You selected row %d. More specifically you clicked in column %d, and the text in this cell is %s\n\n", row, column, text);
+
+ return;
+}
+/* final del ejemplo */
+</verb></tscreen>
+
+<!-- ***************************************************************** -->
+<sect> El <em>widget</em> árbol<label id="sec_Tree_Widgets">
+<!-- ***************************************************************** -->
+<p>
+
+El propósito del <em>widget</em> GtkTree es mostrar datos organizados
+de forma jerárquica. El <em>widget</em> GtkTree en sí es un contenedor
+vertical para los <em>widgets</em> 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 <em>widgets</em> GtkTree que en los GtkList. La
+diferencia es que los <em>widgets</em> GtkTree pueden anidarse
+dentro de otros <em>widgets</em> GtkTree. Vamos a verlo de forma
+resumida.
+
+El <em>widget</em> 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.
+
+<sect1> Creando un árbol
+<p>
+Puede crear un GtkTree de la forma usual, utilizando:
+
+<tscreen><verb>
+GtkWidget* gtk_tree_new( void );
+</verb></tscreen>
+
+Como el <em>widget</em> 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 <tt/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. <ref id="sec_Tree_Item_Widget" name="El
+widget elemento de árbol"> más adelante explica todos los
+detalles de GtkTreeItem. Por ahora, es suficiente con saber como crear
+uno, utilizando:
+
+<tscreen><verb>
+GtkWidget* gtk_tree_item_new_with_label( gchar *etiqueta );
+</verb></tscreen>
+
+Puede añadirlo al árbol utilizando una de las siguientes funciones
+(ver <ref id="sec_GtkTree_Functions" name="Funciones y macros">
+más adelante para leer más opciones):
+
+<tscreen><verb>
+void gtk_tree_append( GtkTree *arbol,
+ GtkWidget *elemento_arbol );
+
+void gtk_tree_prepend( GtkTree *arbol,
+ GtkWidget *elemento_arbol );
+</verb></tscreen>
+
+Observe que debe añadir elementos a un GtkTree de uno en uno - no
+hay un equivalente a <tt/gtk_list_*_items()/.
+
+<sect1> Añadiendo un Subárbol
+<p>
+Un subárbol se crea como cualquier otro <em>widget</em> GtkTree. Un
+subárbol se añade a otro árbol bajo un elemento del mismo, utilizando:
+
+<tscreen><verb>
+void gtk_tree_item_set_subtree( GtkTreeItem *elemento_arbol,
+ GtkWidget *subarbol );
+</verb></tscreen>
+
+No necesita llamar a <tt/gtk_widget_show()/ en un subárbol ni antes ni
+después de añadirlo a GtkTreeItem. Sin embargo, <em>deberá</em> haber
+añadido el GtkTreeItem en cuestión a un árbol padre antes de llamar a
+<em/gtk_tree_item_set_subtree()/. Esto se debe a que, técnicamente,
+el padre del subárbol <em>no</em> 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.
+
+<sect1> Manejando la lista de selección
+<p>
+Como con GtkList, GtkTree tiene un campo <tt>selection</tt>, y
+es posible controlar el comportamiento del árbol (de alguna manera)
+estableciendo el tipo de selección, utilizando:
+
+<tscreen><verb>
+void gtk_tree_set_selection_mode( GtkTree *arbol,
+ GtkSelectionMode mode );
+</verb></tscreen>
+
+La semántica asociada con los distintos modos de selección está
+descrita en la sección del <em>widget</em> GtkList. Como ocurría con
+el <em>widget</em> GtkList, se enviarán las señales <tt/select_child/,
+<tt/unselect_child/ (realmente no - ver <ref id="sec_GtkTree_Signals"
+name="Señales"> más adelante para una explicación), y
+<tt/selection_changed/ cuando los elementos de la lista sean
+seleccionados o deseleccionados. Sin embargo, para aprovechar estas
+señales, necesita conocer por medio <em>de que</em> <em>widget</em>
+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 <em>widgets</em> GtkTree
+son creados iguales, algunos son más iguales que otros. Todos los
+<em>widgets</em> 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 <em>widget</em>
+GtkTree superior de la jerarquia, conocido como el «árbol raíz».
+
+Por tanto no es una buena idea acceder al campo <tt>selection</tt>
+directamente en un <em>widget</em> GtkTree arbitrario, a menos que
+<em>sepa</em> que es el árbol raíz. En vez de eso, utilice la
+macro GTK_TREE_SELECTION (arbol), que da la lista selección del árbol
+raíz como un puntero <tt/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 <tt/select_child/ (y tt/unselect_child/, en
+teoría) son emitidas por todos los árboles, pero la señal
+<em/selection_changed/ es emitida sólo por el árbol raíz. En
+consecuencia, si quiere manipular la señal <tt/select_child/ de un
+árbol y todos sus subárboles, tendrá que llamar a
+<tt/gtk_signal_connect()/ una vez por cada subárbol.
+
+<sect1> Estructura interna del <em>widget</em> árbol
+<p>
+La definición de la estructura GtkTree es ls siguiente:
+
+<tscreen><verb>
+struct _GtkTree
+{
+ GtkContainer container;
+
+ GList *child;
+
+ 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;
+};
+</verb></tscreen>
+
+Ya se han mencionado los peligros asociados con el acceso directo al
+campo <tt>selection</tt>. Se puede acceder a los otros campos
+importantes de la estructura mediante macros manipuladoras o
+funciones de clase. GTK_TREE_IS_ROOT_TREE (arbol) devuelve un valor
+booleano que indica si un árbol es árbol raíz de una jerarquia
+GtkTree, mientras que GTK_TREE_ROOT_TREE (arbol) devuelve el árbol
+raíz, un objeto de tipo GtkTree (recuerde transformarlo utilizando
+GTK_WIDGET (arbol) si quiere utilizar con él alguna de la funciones
+<tt/gtk_widget_*()/).
+
+En lugar de acceder directamente al campo hijo de un <em>widget</em>
+GtkTree, probablemente sea mejor transformarlo utilizando
+GTK_CONTAINER (arbol), y pasárselo a la función
+<tt/gtk_container_children()/. Con esto crearemos un duplicado de la
+lista original, por lo que deberá eliminarlo de la memoria utilizando
+<tt/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í:
+
+<tscreen><verb>
+hijo = gtk_container_children (GTK_CONTAINER (arbol));
+while (hijo) {
+ do_something_nice (GTK_TREE_ITEM (hijo->data));
+ hijo = g_list_remove_link (hijo, hijo);
+}
+</verb></tscreen>
+
+El campo <tt>tree_owner</tt> sólo está definido en subárboles, donde
+apunta al <em>widget</em> GtkTreeItem que contiene al árbol en
+cuestión. El campo <tt>level</tt> 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 <em>widget</em> GtkTree se dibuje en la pantalla.
+
+<sect2> Señales<label id="sec_GtkTree_Signals">
+<p>
+<tscreen><verb>
+void selection_changed( GtkTree *arbol );
+</verb></tscreen>
+
+Esta señal se emitirá cuando cambie el campo <tt>selection</tt> de
+un GtkTree. Esto ocurre cuando se selecciona o deselecciona un hijo del
+GtkTree.
+
+<tscreen><verb>
+void select_child( GtkTree *arbol,
+ GtkWidget *hijo );
+</verb></tscreen>
+
+Esta señal se emite cuando se está seleccionando un hijo del GtkTree.
+Esto ocurre en las llamadas a <tt/gtk_tree_select_item()/,
+<tt/gtk_tree_select_child()/, en <em>todas</em> las pulsaciones de
+botón y llamadas a <tt/gtk_tree_item_toggle()/ y
+<tt/gtk_item_toggle()/. Puede que a veces se invoque indirectamente en
+otras ocasiones, cuando el hijo se añada o elimine del GtkTree.
+
+<tscreen><verb>
+void unselect_child (GtkTree *arbol,
+ GtkWidget *hijo);
+</verb></tscreen>
+
+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
+<tt/gtk_tree_unselect_item()/ o a <tt/gtk_tree_unselect_child()/, y quizás
+en otras ocasiones, pero <em>no</em> cuando la pulsación de un botón
+deselecciona un hijo, y tampoco por la emisión de la señal «toggle»
+por <tt/gtk_item_toggle()/.
+
+<sect2> Funciones y macros<label id="sec_GtkTree_Functions">
+<p>
+<tscreen><verb>
+guint gtk_tree_get_type( void );
+</verb></tscreen>
+
+Devuelve el identificador de tipo de `GtkTree'.
+
+<tscreen><verb>
+GtkWidget* gtk_tree_new( void );
+</verb></tscreen>
+
+Crea un nuevo objeto GtkTree. El nuevo <em>widget</em> se devuelve como
+un puntero a un objeto GtkWidget. Se devolverá NULL si se produce algún
+error.
+
+<tscreen><verb>
+void gtk_tree_append( GtkTree *arbol,
+ GtkWidget *elemento_arbol );
+</verb></tscreen>
+
+Añade un árbol a un GtkTree.
+
+<tscreen><verb>
+void gtk_tree_prepend( GtkTree *arbol,
+ GtkWidget *elemento_arbol );
+</verb></tscreen>
+
+Preañade un árbol a un GtkTree.
+
+<tscreen><verb>
+void gtk_tree_insert( GtkTree *arbol,
+ GtkWidget *elemento_arbol,
+ gint posicion );
+</verb></tscreen>
+
+Inserta un árbol en un GtkTree en la posición de la lista especificada
+por <tt>posicion.</tt>
+
+<tscreen><verb>
+void gtk_tree_remove_items( GtkTree *arbol,
+ GList *items );
+</verb></tscreen>
+
+Elimina una lista de elementos (en forma de una <tt/GList */) de un
+GtkTree. Eliminar un elemento de un árbol lo dereferencia (y por tanto
+normalmente) lo destruye (""), a él <em>y</em> a su subárbol, de
+haberlo, <em>y</em> a todos los subárboles que contenga ese
+subárbol. Si quiere eliminar sólo un elemento, deberá utilizar
+<tt/gtk_container_remove()/.
+
+<tscreen><verb>
+void gtk_tree_clear_items( GtkTree *arbol,
+ gint start,
+ gint end );
+</verb></tscreen>
+
+Elimina los elementos de un GtkTree desde la posición <tt>start</tt>
+hasta la posición <tt>end</tt>. De nuevo hay que llevarse cuidado
+con donde se aplica la dereferencia, ya que <tt/gtk_tree_clear_items()/
+simplemente construye una lista y se la pasa a
+<tt/gtk_tree_remove_items()/.
+
+<tscreen><verb>
+void gtk_tree_select_item( GtkTree *arbol,
+ gint item );
+</verb></tscreen>
+
+Emite la señal <tt/select_item/ para el hijo que se encuentra en la
+posición <tt>item</tt>, y por tanto selecciona a ese hijo (a menos que
+lo deseleccione en un manejador de señal...)
+
+<tscreen><verb>
+void gtk_tree_unselect_item( GtkTree *arbol,
+ gint item );
+</verb></tscreen>
+
+Emite la señal <tt/unselect_item/ para el hijo en la posición
+<tt>item</tt>, y por tanto deselecciona al hijo.
+
+<tscreen><verb>
+void gtk_tree_select_child( GtkTree *arbol,
+ GtkWidget *elemento_arbol );
+</verb></tscreen>
+
+Emite la señal <tt/select_item/ para el hijo <tt>elemento_arbol</tt>, y por tanto
+lo selecciona.
+
+<tscreen><verb>
+void gtk_tree_unselect_child( GtkTree *arbol,
+ GtkWidget *elemento_arbol );
+</verb></tscreen>
+
+Emite la señal <tt/unselect_item/ para el hijo <tt>elemento_arbol</tt>, y por
+tanto lo deselecciona.
+
+<tscreen><verb>
+gint gtk_tree_child_position( GtkTree *arbol,
+ GtkWidget *hijo );
+</verb></tscreen>
+
+Devuelve la posición en el árbol de <tt>child</tt>, a menos que
+<tt>child</tt> no esté en el árbol, en cuya caso devuelve -1.
+
+<tscreen><verb>
+void gtk_tree_set_selection_mode( GtkTree *arbol,
+ GtkSelectionMode mode );
+</verb></tscreen>
+
+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.
+
+<tscreen><verb>
+void gtk_tree_set_view_mode( GtkTree *arbol,
+ GtkTreeViewMode mode );
+</verb></tscreen>
+
+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
+<em>widget</em> GtkTreeItem completo, mientras que si es
+GTK_TREE_VIEW_ITEM, sólo se resaltará el <em>widget</em> hijo (es
+decir, lo que normalmente es la etiqueta).
+
+<tscreen><verb>
+void gtk_tree_set_view_lines( GtkTree *arbol,
+ guint flag );
+</verb></tscreen>
+
+Controla si se dibujarán las líneas de conexión entre los elementos
+del árbol. <tt>flag</tt> es o TRUE, en cuyo caso se dibujarán, o
+FALSE, en cuyo caso no se dibujarán.
+
+<tscreen><verb>
+GtkTree *GTK_TREE (gpointer obj);
+</verb></tscreen>
+
+Convierte un puntero genérico a `GtkTree *'.
+
+<tscreen><verb>
+GtkTreeClass *GTK_TREE_CLASS (gpointer class);
+</verb></tscreen>
+
+Convierte un puntero genérico a `GtkTreeClass *'.
+
+<tscreen><verb>
+gint GTK_IS_TREE (gpointer obj);
+</verb></tscreen>
+
+Determina si un puntero genérico se refiere a un objeto `GtkTree'.
+
+<tscreen><verb>
+gint GTK_IS_ROOT_TREE (gpointer obj)
+</verb></tscreen>
+
+Determina si un puntero genérico se refiere a un objeto `GtkTree'
+<em>y</em> 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.
+
+<tscreen><verb>
+GtkTree *GTK_TREE_ROOT_TREE (gpointer obj)
+</verb></tscreen>
+
+Devuelve el árbol raíz de un puntero a un objeto `GtkTree'. Seguimos
+con el mismo problema que en el caso anterior.
+
+<tscreen><verb>
+GList *GTK_TREE_SELECTION(gpointer obj)
+</verb></tscreen>
+
+Devuelve la lista de selección del árbol raíz de un objeto
+`GtkTree'. Seguimos con el mismo problema que antes.
+
+<sect1> El <em>widget</em> elemento de árbol<label id="sec_Tree_Item_Widget">
+<p>
+El <em>widget</em> 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
+<em>widget</em> hijo, que puede ser de cualquier tipo. El <em>widget</em>
+GtkTreeItem tiene ciertos campos extra, pero el único que nos
+interesa ahora es el campo <em>subárbol</em>.
+
+La definición de la estructura GtkTreeItem es así:
+
+<tscreen><verb>
+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;
+};
+</verb></tscreen>
+
+El campo <tt>pixmaps_box</tt> es un GtkEventBox que caza las pulsaciones
+en el símbolo más/menos que controla la expansión y contracción. El
+campo <tt>pixmaps</tt> 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
+<em>realmente</em> sepa que es lo que está haciendo.
+
+Ya que está derivado directamente de un GtkItem, puede tratarse como
+tal utilizando la macro GTK_ITEM (ElementoArbol). 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():
+
+<tscreen><verb>
+elemento_arbol = gtk_tree_item_new ();
+etiqueta_widget = gtk_label_new (etiqueta);
+gtk_misc_set_alignment (GTK_MISC (etiqueta_widget), 0.0, 0.5);
+
+gtk_container_add (GTK_CONTAINER (elemento_arbol), etiqueta_widget);
+gtk_widget_show (etiqueta_widget);
+</verb></tscreen>
+
+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í:
+
+<tscreen><verb>
+gtk_widget_ref (arbol);
+propietario = GTK_TREE(arbol)->tree_owner;
+gtk_container_remove (GTK_CONTAINER(arbol), item);
+if (arbol->parent == NULL){
+ gtk_tree_item_expand (GTK_TREE_ITEM(propietario));
+ gtk_tree_item_set_subtree (GTK_TREE_ITEM(propietario), arbol);
+}
+else
+ gtk_widget_unref (arbol);
+</verb></tscreen>
+
+Finalmente, hay que mencionar que la opción de drag-n-drop (arrastar y
+soltar) <em>funciona</em> 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 <em>widget</em> padre tiene a su vez
+un padre, y así hasta llegar al nivel más alto o ventana de diálogo,
+cuando llamamos a <tt/gtk_widget_dnd_drag_set()/ o
+<tt/gtk_widget_dnd_drop_set()/. En caso contrario, podrían ocurrir
+cosas extrañas.
+
+<sect2> Señales
+<p>
+GtkTreeItem hereda las señales <tt/select/, <tt/deselect/, y
+<tt/toggle/ de GtkItem. Además, añade dos señales propias, <tt/expand/
+y <tt/collapse/.
+
+<tscreen><verb>
+void select( GtkItem *elemento_arbol );
+</verb></tscreen>
+
+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 <tt/gtk_tree_item_select()/,
+<tt/gtk_item_select()/, o a <tt/gtk_tree_select_child()/.
+
+<tscreen><verb>
+void deselect( GtkItem *elemento_arbol );
+</verb></tscreen>
+
+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 <tt/gtk_tree_item_deselect()/ o a
+<tt/gtk_item_deselect()/. En el caso de GtkTreeItems, también se
+emitirá por <tt/gtk_tree_unselect_child()/, y a veces por
+<tt/gtk_tree_select_child()/.
+
+<tscreen><verb>
+void toggle( GtkItem *elemento_arbol );
+</verb></tscreen>
+
+Esta señal se emite cuando el programa llama a <tt/gtk_item_toggle()/. El
+efecto que tiene cuando se emite en un GtkTreeItem es llamar a
+<tt/gtk_tree_select_child()/ (y nunca a
+<tt/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.
+
+<tscreen><verb>
+void expand( GtkTreeItem *elemento_arbol );
+</verb></tscreen>
+
+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
+<tt/gtk_tree_item_expand()/.
+
+<tscreen><verb>
+void collapse( GtkTreeItem *elemento_arbol );
+</verb></tscreen>
+
+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
+<tt/gtk_tree_item_collapse()/.
+
+<sect2> Funciones y Macros
+<p>
+<tscreen><verb>
+guint gtk_tree_item_get_type( void );
+</verb></tscreen>
+
+Devuelve el identificador de tipo de `GtkTreeItem'.
+
+<tscreen><verb>
+GtkWidget* gtk_tree_item_new( void );
+</verb></tscreen>
+
+Crea un nuevo objeto GtkTreeItem. El nuevo <em>widget</em> se devuelve
+como un puntero a un objeto GtkWidget. Se devolverá NULL si hay algún
+fallo.
+
+<tscreen><verb>
+GtkWidget* gtk_tree_item_new_with_label (gchar *etiqueta);
+</verb></tscreen>
+
+Crea un nuevo objeto GtkTreeItem, teniendo una simple GtkLabel
+como único hijo. El nuevo <em>widget</em> se devolverá como
+un puntero a un objeto GtkWidget. Se devolverá NULL en caso
+de haber algún fallo.
+
+<tscreen><verb>
+void gtk_tree_item_select( GtkTreeItem *elemento_arbol );
+</verb></tscreen>
+
+Esta función es básicamente un recubrimiento de una llamada a
+gtk_item_select (GTK_ITEM (elemento_arbol)) que emitirá la
+señal select.
+
+<tscreen><verb>
+void gtk_tree_item_deselect( GtkTreeItem *elemento_arbol );
+</verb></tscreen>
+
+Esta función es básicamente un recubrimiento de una llamada a
+gtk_item_deselect (GTK_ITEM (elemento_arbol)) que emitirá la
+señal deselect.
+
+<tscreen><verb>
+void gtk_tree_item_set_subtree( GtkTreeItem *elemento_arbol,
+ GtkWidget *subarbol );
+</verb></tscreen>
+
+Esta función añade <tt/subarbol/ a <tt/elemento_arbol/, mostrándolo si
+<tt/elemento_arbol/ está expandido, u ocultándolo si <tt/elemento_arbol/ está
+contraído. De nuevo, recuerde que el <tt/elemento_arbol/ ya debe de haber
+sido añadido a un árbol para que esto funcione.
+
+<tscreen><verb>
+void gtk_tree_item_remove_subtree( GtkTreeItem *elemento_arbol );
+</verb></tscreen>
+
+Esto elimina todos los hijos de los subárboles del <tt/elemento_arbol/
+(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.
+
+<tscreen><verb>
+void gtk_tree_item_expand( GtkTreeItem *elemento_arbol );
+</verb></tscreen>
+
+Esto emite la señal «expand» para el <tt/elemento_arbol/, que lo
+expande.
+
+<tscreen><verb>
+void gtk_tree_item_collapse( GtkTreeItem *elemento_arbol );
+</verb></tscreen>
+
+Esto emite la señal «collapse» en el <tt/elemento_arbol/, que lo
+contrae.
+
+<tscreen><verb>
+GtkTreeItem *GTK_TREE_ITEM (gpointer obj)
+</verb></tscreen>
+
+Convierte un puntero genérico en un `GtkTreeItem *'.
+
+<tscreen><verb>
+GtkTreeItemClass *GTK_TREE_ITEM_CLASS (gpointer obj)
+</verb></tscreen>
+
+Convierte un puntero genérico en un `GtkTreeItemClass'.
+
+<tscreen><verb>
+gint GTK_IS_TREE_ITEM (gpointer obj)
+</verb></tscreen>
+
+Determina si un puntero genérico se refiere a un objeto `GtkTreeItem'.
+
+<tscreen><verb>
+GtkWidget GTK_TREE_ITEM_SUBTREE (gpointer obj)
+</verb></tscreen>
+
+Devuelve un subárbol del elemento (<tt/obj/ debería apuntar a un
+objeto `GtkTreeItem').
+
+<sect1> Árbol ejemplo
+<p>
+Este ejemplo es muy parecido al árbol ejemplo que hay en
+<tt/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.
+
+<!-- Hay un comentario en el código que no se traducir -->
+<tscreen><verb>
+/* 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 *etiqueta;
+
+ /* Es un GtkBin, por lo que tiene un hijo, que sabemos que es una
+ * etiqueta, por lo que la cogemos */
+ etiqueta = GTK_LABEL (GTK_BIN (item)->child);
+ /* Conseguimos el texto de la etiqueta */
+ gtk_label_get (etiqueta, &amp;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 *arbol_raiz, GtkWidget *hijo,
+ GtkWidget *subarbol)
+{
+ g_print ("unselect_child called for root tree %p, subtree %p, child %p\n",
+ arbol_raiz, subarbol, hijo);
+}
+
+/* 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 *arbol_raiz, GtkWidget *hijo,
+ GtkWidget *subarbol)
+{
+ g_print ("select_child called for root tree %p, subtree %p, child %p\n",
+ arbol_raiz, subarbol, hijo);
+}
+
+static void cb_selection_changed (GtkWidget *arbol)
+{
+ GList *i;
+
+ g_print ("selection_change called for tree %p\n", arbol);
+ g_print ("selected objects are:\n");
+
+ i = GTK_TREE_SELECTION(arbol);
+ while (i){
+ gchar *name;
+ GtkLabel *etiqueta;
+ GtkWidget *item;
+
+ /* Get a GtkWidget pointer from the list node */
+ item = GTK_WIDGET (i->data);
+ etiqueta = GTK_LABEL (GTK_BIN (item)->child);
+ gtk_label_get (etiqueta, &amp;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 *ventana, *scrolled_win, *arbol;
+ static gchar *itemnames[] = {"Foo", "Bar", "Baz", "Quux",
+ "Maurice"};
+ gint i;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ /* una ventana general */
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_signal_connect (GTK_OBJECT(ventana), "delete_event",
+ GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
+ gtk_container_border_width (GTK_CONTAINER(ventana), 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(ventana), scrolled_win);
+ gtk_widget_show (scrolled_win);
+
+ /* Crear el árbol raíz */
+ arbol = gtk_tree_new();
+ g_print ("root tree is %p\n", arbol);
+ /* connect all GtkTree:: signals */
+ gtk_signal_connect (GTK_OBJECT(arbol), "select_child",
+ GTK_SIGNAL_FUNC(cb_select_child), arbol);
+ gtk_signal_connect (GTK_OBJECT(arbol), "unselect_child",
+ GTK_SIGNAL_FUNC(cb_unselect_child), arbol);
+ gtk_signal_connect (GTK_OBJECT(arbol), "selection_changed",
+ GTK_SIGNAL_FUNC(cb_selection_changed), arbol);
+ /* Añadirlo a la ventana con barras de desplazamiento */
+ gtk_container_add (GTK_CONTAINER(scrolled_win), arbol);
+ /* Poner el modo de selección */
+ gtk_tree_set_selection_mode (GTK_TREE(arbol),
+ GTK_SELECTION_MULTIPLE);
+ /* mostrar el árbol */
+ gtk_widget_show (arbol);
+
+ for (i = 0; i < 5; i++){
+ GtkWidget *subarbol, *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(arbol), item);
+ /* Mostrarlo - esto se puede hacer en cualquier momento */
+ gtk_widget_show (item);
+ /* Crear el subárbol de este elemento */
+ subarbol = gtk_tree_new();
+ g_print ("-> item %s->%p, subtree %p\n", itemnames[i], item,
+ subarbol);
+
+ /* 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(subarbol), "select_child",
+ GTK_SIGNAL_FUNC(cb_select_child), subarbol);
+ gtk_signal_connect (GTK_OBJECT(subarbol), "unselect_child",
+ GTK_SIGNAL_FUNC(cb_unselect_child), subarbol);
+ /* Esto no tiene absolutamente ningún efecto, ya que se ignora
+ * completamente en los subárboles */
+ gtk_tree_set_selection_mode (GTK_TREE(subarbol),
+ 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(subarbol), 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), subarbol);
+
+ 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(subarbol), subitem);
+ /* Mostrarlo */
+ gtk_widget_show (subitem);
+ }
+ }
+
+ /* Mostrar la ventana y entrar en el bucle final */
+ gtk_widget_show (ventana);
+ gtk_main();
+ return 0;
+}
+/* fin del ejemplo */
+</verb></tscreen>
+
+<!-- ***************************************************************** -->
+<sect> El <em>widget</em> menú
+<!-- ***************************************************************** -->
+<p>
+Hay dos formas de crear menús, la fácil, y la difícil. Ambas tienen su
+utilidad, aunque lo más probable es que normalmente utilice la
+menufactory (la forma fácil). La forma «difícil» consiste en crear
+todos los menús utilizando las llamadas directamente. La forma fácil
+consiste en utilizar las llamadas de <tt/gtk_item_factory/. Es mucho
+más fácil, pero aun así cada aproximación tiene sus ventajas y sus
+inconvenientes.
+
+La menufactory es mucho más fácil de utilizar, y tambíen es más fácil
+añadir nuevos menús, aunque a larga, escribiendo unas cuántas
+funciones de recubrimiento para crear menús utilizando el método
+manual puede acabar siendo más útil. Con la itemfactory, no es posible
+añadir imágenes o el carácter `/' a los menús.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Creación manual de menús
+<p>
+Siguiendo la auténtica tradición de la enseñanza, vamos a enseñarle
+primero la forma difícil. <tt>:)</tt>
+
+Se utilizan tres <em>widgets</em> para hacer una barra de menús y
+submenús:
+<itemize>
+<item>un elemento del menú, que es lo que el usuario quiere seleccionar,
+p.e. 'Guardar'
+<item>un menú, que actua como un contenedor para los elementos del menú, y
+<item>una barra de menú, que es un contenedor para cada uno de los menús,
+</itemize>
+
+Todo esto se complica ligeramente por el hecho de que los
+<em>widgets</em> de los elementos del menú se utilizan para dos cosas
+diferentes. Están los <em>widgets</em> que se empaquetan en el menú, y
+los que se empaquetan en una barra de menús, que cuando se selecciona,
+activa el menú.
+
+Vamos a ver las funciones que se utilizan para crear menús y barras
+de menús. ésta primera función se utiliza para crear una barra de menús.
+
+<tscreen><verb>
+GtkWidget *gtk_menu_bar_new( void );
+</verb></tscreen>
+
+Como el propio nombre indica, esta función crea una nueva barra de
+menús. Utilice <tt/gtk_container_add/ para empaquetarla en una
+ventana, o las funciones <tt/box_pack/ para empaquetarla en una caja -
+exactamente igual que si fuesen botones.
+
+<tscreen><verb>
+GtkWidget *gtk_menu_new( void );
+</verb></tscreen>
+
+Esta función devuelve un puntero a un nuevo menú, que no se debe
+mostrar nunca (no hace falta utilizar <tt/gtk_widget_show/), es sólo
+un contenedor para los elementos del menú. Espero que todo esto se
+aclare un poco cuando vea en el ejemplo que hay más abajo.
+
+Las siguientes dos llamadas se utilizan para crear elementos de menú
+que se empaquetarán en el menú (y en la barra de menú).
+
+<tscreen><verb>
+GtkWidget *gtk_menu_item_new( void );
+</verb></tscreen>
+
+y
+
+<tscreen><verb>
+GtkWidget *gtk_menu_item_new_with_label( const char *etiqueta );
+</verb></tscreen>
+
+Estas llamadas se utilizan para crear los elementos del menú que
+van a mostrarse. Recuerde que hay que distinguir entre un «menú»
+creado con <tt/gtk_menu_new/ y un «elemento del menú» creado con las
+funciones <tt/gtk_menu_item_new/. El elemento de menú será un botón
+con una acción asociada, y un menú será un contenedor con los
+elementos del menú.
+
+Las funciones <tt/gtk_menu_new_with_label/ y <tt/gtk_menu_new/ son
+sólo lo que espera que sean después de leer lo de los botones. Una
+crea un nuevo elemento del menú con una etiqueta ya dentro, y la otra
+crea un elemento del menú en blanco.
+
+Una vez ha creado un elemento del menú tiene que ponerlo en un menú.
+Esto se hace utilizando la función <tt/gtk_menu_append/. Para capturar
+el momento en el que el elemento se selecciona por el usuario,
+necesitamos conectar con la señal <tt/activate/ de la forma usual. Por
+tanto, si quiere crear un menú estándar <tt/File/, con las opciones
+<tt/Open/, <tt/Save/ y <tt/Quit/ el código debería ser algo como
+
+<tscreen><verb>
+file_menu = gtk_menu_new(); /* No hay que mostrar menús */
+
+/* Crear los elementos del menú */
+open_item = gtk_menu_item_new_with_label("Open");
+save_item = gtk_menu_item_new_with_label("Save");
+quit_item = gtk_menu_item_new_with_label("Quit");
+
+/* Añadirlos 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);
+
+/* Enlazar las función de llamada a la señal "activate" */
+gtk_signal_connect_object( GTK_OBJECT(open_items), "activate",
+ GTK_SIGNAL_FUNC(menuitem_response), (gpointer) "file.open");
+gtk_signal_connect_object( GTK_OBJECT(save_items), "activate",
+ GTK_SIGNAL_FUNC(menuitem_response), (gpointer) "file.save");
+
+/* Podemos enlazar el elemento de menú Quit con nuestra función de
+ * salida */
+gtk_signal_connect_object( GTK_OBJECT(quit_items), "activate",
+ GTK_SIGNAL_FUNC(destroy), (gpointer) "file.quit");
+
+/* Tenemos que mostrar los elementos del menú */We do need to show menu items */
+gtk_widget_show( open_item );
+gtk_widget_show( save_item );
+gtk_widget_show( quit_item );
+</verb></tscreen>
+
+En este momento tendremos nuestro menú. Ahora necesitamos crear una
+barra de menús y un elemento de menú para el elemento <tt/File/, que
+vamos a añadir a nuestro menú. El código es el siguiente
+
+<tscreen><verb>
+menu_bar = gtk_menu_bar_new();
+gtk_container_add( GTK_CONTAINER(ventana), menu_bar);
+gtk_widget_show( menu_bar );
+
+file_item = gtk_menu_item_new_with_label("File");
+gtk_widget_show(file_item);
+</verb></tscreen>
+
+Ahora necesitamos asociar el menú con <tt/file_item/. Esto se hace con
+la función
+
+<tscreen>
+void gtk_menu_item_set_submenu( GtkMenuItem *menu_item,
+ GtkWidget *submenu );
+</tscreen>
+
+Por lo que nuestro ejemplo continua con
+
+<tscreen><verb>
+gtk_menu_item_set_submenu( GTK_MENU_ITEM(file_item), file_menu );
+</verb></tscreen>
+
+Todo lo que queda por hacer es añadir el menú a la barra de menús, que
+se hace mediante la función
+
+<tscreen>
+void gtk_menu_bar_append( GtkMenuBar *menu_bar, GtkWidget *menu_item);
+</tscreen>
+
+que en nuestro caso habrá que utilizar así:
+
+<tscreen><verb>
+gtk_menu_bar_append( GTK_MENU_BAR (menu_bar), file_item );
+</verb></tscreen>
+
+Si queremos que el menú esté alineado a la derecha en la barra de
+menús, como suele estar la opción de ayuda, podemos utilizar la
+función siguiente (otra vez en <tt/file_item/ en el ejemplo actual)
+antes de enlazarla en la barra de menú.
+
+<tscreen><verb>
+void gtk_menu_item_right_justify( GtkMenuItem *menu_item );
+</verb></tscreen>
+
+Aquí hay un resumen de los pasos que son necesarios para crear una
+barra de menús con los menús correspondientes ya enlazados:
+
+<itemize>
+<item> Crear un nuevo menú utilizando <tt/gtk_menu_new()/
+<item> Utilizar multiples llamadas a <tt/gtk_menu_item_new()/ para
+cada elemento que desee tener en su menú. Y utilizar
+<tt/gtk_menu_append()/ para poner cada uno de esos nuevos elementos en
+el menú.
+<item> Crear un elemento de menú utilizando
+<tt/gtk_menu_item_new()/. Ésta será la raíz del menú, el texto que
+aparezca aquí estará en la barra de menús.
+<item> Utilizar <tt/gtk_menu_item_set_submenu()/ para enlazar el menú
+al elemento del menú raíz (el creado en el paso anterior).
+<item> Crear una nueva barra de menús utilizando
+<tt/gtk_menu_bar_new/. Este paso solo necesita hacerse una vez cuando
+se crea una serie de menús en una barra de menús.
+<item> Utilizar <tt/gtk_menu_bar_append/ para poner el menú raíz en la
+barra de menús.
+</itemize>
+
+Para hacer un menú desplegable hay que seguir prácticamente los mismos
+pasos. La única diferencia es que el menú no estará conectado
+`automáticamente' a una barra de menú, sino que para que aparezca
+deberá llamarse explícitamente a la función <tt/gtk_menu_popup()/
+utilizando, por ejemplo, un evento de pulsación de botón. Siga los
+pasos siguientes:
+
+<itemize>
+<item>Cree una función manejadora de eventos. Tiene que tener el
+siguiente prototipo
+<tscreen>
+static gint handler( GtkWidget *widget,
+ GdkEvent *event );
+</tscreen>
+
+y utilice el evento para encontrar donde debe aparecer el menú.
+
+<item>En el manejador de eventos, si el evento es una pulsación de un
+botón del ratón, tratar <tt>event</tt> como un evento de botón
+(que lo es) y utilizarlo como se indica en el código ejemplo para
+pasarle información a <tt/gtk_menu_popup()/.
+<item>Enlazar este manejador de eventos con el <em>widget</em> con
+<tscreen>
+gtk_signal_connect_object(GTK_OBJECT(widget), "event",
+ GTK_SIGNAL_FUNC (handler), GTK_OBJECT(menu));
+</tscreen>
+donde <tt>widget</tt> es el <em>widget</em> con el que esta conectando,
+<tt>handler</tt> es la función manejadora, y <tt>menu</tt> es un menú
+creado con <tt/gtk_menu_new()/. Éste puede ser un menú que esté
+contenido en una barra de menús, como se puede ver en el código de
+ejemplo.
+</itemize>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Ejemplo de la creación manual de un menú
+<p>
+Esto debería funcionar. Échele un vistazo al ejemplo para aclarar los
+conceptos.
+
+<tscreen><verb>
+/* principio del ejemplo menu menu.c */
+
+#include <gtk/gtk.h>
+
+static gint button_press (GtkWidget *, GdkEvent *);
+static void menuitem_response (gchar *);
+
+int main (int argc, char *argv[])
+{
+
+ GtkWidget *ventana;
+ GtkWidget *menu;
+ GtkWidget *menu_bar;
+ GtkWidget *root_menu;
+ GtkWidget *menu_items;
+ GtkWidget *vbox;
+ GtkWidget *boton;
+ char buf[128];
+ int i;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ /* crear una nueva ventana */
+ ventana = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_widget_set_usize( GTK_WIDGET (ventana), 200, 100);
+ gtk_window_set_title(GTK_WINDOW (ventana), "GTK Menu Test");
+ gtk_signal_connect(GTK_OBJECT (ventana), "delete_event",
+ (GtkSignalFunc) gtk_main_quit, NULL);
+
+ /* Inicializar el widget-menu, y recuerde -- ¡¡Nunca haga
+ * gtk_show_widget() con el widget menu!!
+ * Éste es el menú que contiene todos los elementos del menú, el
+ * que se desplegará cuando pulse en el "Root Menu" en la
+ * aplicación
+ */
+ menu = gtk_menu_new();
+
+ /* Ahora hacemos un pequeño bucle que crea tres elementos de menú
+ * para "test-menu". Recuerde llamar a gtk_menu_append. Aquí
+ * estamos añadiendo una lista de elementos de menú a nuestro
+ * menú. Normalmente tendríamos que cazar aquí la señal "clicked"
+ * de cada uno de los elementos del menú y le deberíamos dar una
+ * función de llamada a cada uno, pero lo vamos a omitimos para
+ * ahorrar espacio. */
+
+ for(i = 0; i < 3; i++)
+ {
+ /* Copia los nombres al búfer. */
+ sprintf(buf, "Test-undermenu - %d", i);
+
+ /* Crea un nuevo elemento de menú con un nombre... */
+ menu_items = gtk_menu_item_new_with_label(buf);
+
+ /* ...y lo añade al menú. */
+ gtk_menu_append(GTK_MENU (menu), menu_items);
+
+ /* Hace algo interesante cuando se selecciona el menuitem */
+ gtk_signal_connect_object(GTK_OBJECT(menu_items), "activate",
+ GTK_SIGNAL_FUNC(menuitem_response), (gpointer) g_strdup(buf));
+
+ /* Muestra el widget */
+ gtk_widget_show(menu_items);
+ }
+
+ /* Ésta es el menú raíz, y será la etiqueta mostrada en la
+ * barra de menús. No habrá ningún manejador de señal conectado, ya que
+ * lo único que hace es desplegar el resto del menú. */
+ root_menu = gtk_menu_item_new_with_label("Root Menu");
+
+ gtk_widget_show(root_menu);
+
+ /* Ahora especificamos que queremos que el recien creado "menu"
+ * sea el menú para el "root menu" */
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM (root_menu), menu);
+
+ /* Un vbox para poner dentro un menú y un botón */
+ vbox = gtk_vbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(ventana), vbox);
+ gtk_widget_show(vbox);
+
+ /* Crear una barra de menú para que contenga al menú y la añadamos
+ * a nuestra ventana principal */
+ menu_bar = gtk_menu_bar_new();
+ gtk_box_pack_start(GTK_BOX(vbox), menu_bar, FALSE, FALSE, 2);
+ gtk_widget_show(menu_bar);
+
+ /* Crear un botón al que atar los menús como un popup */
+ boton = gtk_button_new_with_label("press me");
+ gtk_signal_connect_object(GTK_OBJECT(boton), "event",
+ GTK_SIGNAL_FUNC (button_press), GTK_OBJECT(menu));
+ gtk_box_pack_end(GTK_BOX(vbox), boton, TRUE, TRUE, 2);
+ gtk_widget_show(boton);
+
+ /* Y finalmente añadimos el elemento de menú y la barra de menú --
+ * éste es el elemento de menú "raíz" sobre el que he estado
+ * delirando =) */
+ gtk_menu_bar_append(GTK_MENU_BAR (menu_bar), root_menu);
+
+ /* siempre mostramos la ventana como último paso para que todo se
+ * pongo en pantalla a la vez. */
+ gtk_widget_show(ventana);
+
+ gtk_main ();
+
+ return 0;
+}
+
+/* Responde a una pulsación del botón enviando un menú como un widget
+ * Recuerde que el argumento "widget" es el menú que se está enviando,
+ * NO el botón que se ha pulsado.
+ */
+
+static gint button_press (GtkWidget *widget, GdkEvent *event)
+{
+
+ if (event->type == GDK_BUTTON_PRESS) {
+ GdkEventButton *bevent = (GdkEventButton *) event;
+ gtk_menu_popup (GTK_MENU(widget), NULL, NULL, NULL, NULL,
+ bevent->button, bevent->time);
+ /* Le dice al que llamó a la rutina que hemos manejado el
+ * evento; la historia termina aquí. */
+ return TRUE;
+ }
+
+ /* Le dice al que llamó a la rutina que no hemos manejado el
+ * evento. */
+ return FALSE;
+}
+
+
+/* Imprime una cadena cuando se selecciona un elemento del menú */
+
+static void menuitem_response (gchar *string)
+{
+ printf("%s\n", string);
+}
+/* final del ejemplo */
+</verb></tscreen>
+
+También puede hacer que un elemento del menú sea insensible y, utilizando
+una tabla de teclas aceleradoras, conectar las teclas con las funciones
+del menú.
+
+<!-- XXX Las dos sect1 que vienen han cambiado -->
+<!-- ----------------------------------------------------------------- -->
+<sect1>Utilizando GtkItemFactory
+<p>
+Ahora que le hemos enseñado la forma difícil, le mostraremos como
+utilizar las llamadas <tt/gtk_item_factory/.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Ejemplo de la fábrica de elementos
+<p>
+Aquí hay un ejemplo de cómo utilizar la fábrica de elementos
+GTK.
+
+<tscreen><verb>
+/* principio del ejemplo menu itemfactory.h */
+
+#include <gtk/gtk.h>
+#include <strings.h>
+
+/* La obligatoria función de llamada */
+static void print_hello( GtkWidget *w,
+ gpointer data )
+{
+ g_message ("Hello, World!\n");
+}
+
+/* Esta es la estructura GtkItemFactoryEntry utilizada para crear
+ nuevos menúes.
+
+ This is the GtkItemFactoryEntry structure used to generate new menus.
+ Elemento 1: La dirección del menú. La letra que hay
+ después del subrayado indica una tecla aceleradora
+ una vez que el menú esté abierto.
+ Elemento 2: La tecla aceleradora para la entrada del menú.
+ Elemento 3: La función de llamada.
+ Elemento 4: La acción de llamada. Cambia los parámetros que
+ se le pasan a la función de llamada. El valor por
+ defecto es 0.
+ Elemento 5: El tipo de elemento, se utiliza para definir de que
+ tipo de elemento se trata. Los valores posibles son:
+
+ NULL -> "<Item>"
+ "" -> "<Item>"
+ "<Title>" -> crea un elemento título
+ "<Item>" -> crea un simple elemento
+ "<CheckItem>" -> crea un elemento de comprobación
+ "<ToggleItem>" -> crea un elemento de selección
+ "<RadioItem>" -> crea un elemento circular
+ <path> -> dirección de un elemento circular
+ con el que enlazar
+ "<Separator>" -> crea un separador
+ "<Branch>" -> crea un elemento para contener
+ subelementos (de forma opcional)
+ "<LastBranch>" -> crea una rama justificada a la
+ derecha
+*/
+
+static GtkItemFactoryEntry menu_items[] = {
+ { "/_File", NULL, NULL, 0, "<Branch>" },
+ { "/File/_New", "<control>N", print_hello, 0, NULL },
+ { "/File/_Open", "<control>O", print_hello, 0, NULL },
+ { "/File/_Save", "<control>S", print_hello, 0, NULL },
+ { "/File/Save _As", NULL, NULL, 0, NULL },
+ { "/File/sep1", NULL, NULL, 0, "<Separator>" },
+ { "/File/Quit", "<control>Q", gtk_main_quit, 0, NULL },
+ { "/_Options", NULL, NULL, 0, "<Branch>" },
+ { "/Options/Test", NULL, NULL, 0, NULL },
+ { "/_Help", NULL, NULL, 0, "<LastBranch>" },
+ { "/_Help/About", NULL, NULL, 0, NULL },
+};
+
+
+void get_main_menu( GtkWidget *ventana,
+ GtkWidget **menubar )
+{
+ GtkItemFactory *item_factory;
+ GtkAccelGroup *accel_group;
+ gint nmenu_items = sizeof (menu_items) / sizeof (menu_items[0]);
+
+ accel_group = gtk_accel_group_new ();
+
+ /* Esta función inicializa la fábrica de elementos
+ Param 1: El tipo de menú - puede ser GTK_TYPE_MENU_BAR,
+ GTK_TYPE_MENU, o GTK_TYPE_OPTION_MENU.
+ Param 2: La dirección del menú.
+ Param 3: Un puntero a un gtk_accel_group. La fábrica de
+ elementos actualiza la tabla de teclas aceleradoras
+ mientras genera los menúes.
+ */
+
+ item_factory = gtk_item_factory_new (GTK_TYPE_MENU_BAR, "<main>",
+ accel_group);
+
+ /* Esta función genera los elementos de menú. Pasa la
+ fábrica de elementos (item_factory), el número de elementos
+ del vector, el vector en sí, y cualquier dato de llamada para
+ los elementos de menú. */
+ gtk_item_factory_create_items (item_factory, nmenu_items, menu_items, NULL);
+
+ /* Enlaza el nuevo grupo acelerador a la ventana. */
+ gtk_accel_group_attach (accel_group, GTK_OBJECT (ventana));
+
+ if (menubar)
+ /* Finalmente, devuelve la barra de menú creada por la
+ * fábrica de elementos. */
+ *menubar = gtk_item_factory_get_widget (item_factory, "<main>");
+}
+
+int main( int argc,
+ char *argv[] )
+{
+ GtkWidget *ventana;
+ GtkWidget *main_vbox;
+ GtkWidget *menubar;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
+ GTK_SIGNAL_FUNC (gtk_main_quit),
+ "WM destroy");
+ gtk_window_set_title (GTK_WINDOW(ventana), "Item Factory");
+ gtk_widget_set_usize (GTK_WIDGET(ventana), 300, 200);
+
+ main_vbox = gtk_vbox_new (FALSE, 1);
+ gtk_container_border_width (GTK_CONTAINER (main_vbox), 1);
+ gtk_container_add (GTK_CONTAINER (ventana), main_vbox);
+ gtk_widget_show (main_vbox);
+
+ get_main_menu (ventana, &amp;menubar);
+ gtk_box_pack_start (GTK_BOX (main_vbox), menubar, FALSE, TRUE, 0);
+ gtk_widget_show (menubar);
+
+ gtk_widget_show (ventana);
+ gtk_main ();
+
+ return(0);
+}
+/* fin del ejemplo */
+</verb></tscreen>
+
+Por ahora, sólo está este ejemplo. Ya llegará una
+explicación del mismo y más comentarios.
+
+<!-- ***************************************************************** -->
+<sect> El <em>widget</em> texto
+<!-- ***************************************************************** -->
+<p>
+El <em>widget</em> texto permite mostrar y editar multiples líneas de
+texto. Admite texto en varios colores y con varios tipos de letra,
+permitiendo mezclarlos de cualquier forma que desee. También hay un
+gran número de teclas para la edición de textos, que son compatibles
+con Emacs.
+
+El <em>widget</em> texto admite copiar-y-pegar, incluyendo la
+utilización de doble y triple-click para seleccionar una palabra y una
+línea completa, respectivamente.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Creando y configurando un cuadro de texto
+<p>
+Sólo hay una función para crear un nuevo <em>widget</em> texto.
+<tscreen><verb>
+GtkWidget *gtk_text_new( GtkAdjustment *hadj,
+ GtkAdjustment *vadj );
+</verb></tscreen>
+
+Los argumentos nos permitirán dar al <em>widget</em> texto punteros a
+<tt/GtkAdjustement/ que pueden ser utilizados para controlar la visión
+de la posición del <em>widget</em>. Si le ponemos un valor NULL en
+cualquiera de los dos argumentos (o en los dos), la función
+<tt/gtk_text_new/ creará su propio ajuste.
+
+<tscreen><verb>
+void gtk_text_set_adjustments( GtkText *text,
+ GtkAdjustment *hadj,
+ GtkAdjustment *vadj );
+</verb></tscreen>
+
+La función de arriba permite cambiar en cualquier momento el ajuste
+horizontal y vertical de un <em>widget</em> texto.
+
+El <em>widget</em> texto no crea automáticamente sus propiar barras
+de desplazamiento cuando el texto a mostrar es demasiado largo
+para la ventana en la que se encuentra. Tenemos que crearlas y
+añadirlas a la capa del <em>display</em> nosotros mismos.
+
+<tscreen><verb>
+ vscrollbar = gtk_vscrollbar_new (GTK_TEXT(text)->vadj);
+ gtk_box_pack_start(GTK_BOX(hbox), vscrollbar, FALSE, FALSE, 0);
+ gtk_widget_show (vscrollbar);
+</verb></tscreen>
+
+El trozo de código de arriba crea una nueva barra de desplazamiento
+vertical, y la conecta con el ajuste vertical del <em>widget</em>
+de texto, <tt/text/. Entonces la empaqueta en un cuadro de la forma
+usual.
+
+Observe que, actualmente el <em>widget</em> GtkText no admite barras
+de desplazamiento horizontal.
+
+Principalmente hay dos maneras de utilizar un <em>widget</em> de
+texto: permitiendo al usuario editar el texto, o permitiéndonos
+mostrar varias líneas de texto al usuario. Para cambiar entre estos
+dos modos de operación, el <em>widget</em> de texto tiene las
+siguientes funciones:
+
+<tscreen><verb>
+void gtk_text_set_editable( GtkText *text,
+ gint editable );
+</verb></tscreen>
+
+El argumento <tt/editable/ es un valor TRUE o FALSE que especifica si se
+permite al usuario editar los contenidos del <em>widget</em> texto.
+Cuando el <em>widget</em> texto se pueda editar, mostrará un cursor
+en la posición actual de inserción.
+
+Sin embargo la utilización del <em>widget</em> en estos dos modos no
+es algo permanente, ya que puede cambiar el estado editable del
+<em>widget</em> texto e insertar texto en cualquier momento.
+
+El <em>widget</em> texto corta las líneas de texto que son demasiado
+largas para que quepan en una sólo línea en la ventana. Su
+comportamiento por defecto es cortar las palabras donde se terminan
+las líneas. Esto puede cambiarse utilizando la siguiente función:
+
+<tscreen><verb>
+void gtk_text_set_word_wrap( GtkText *text,
+ gint word_wrap );
+</verb></tscreen>
+
+Utilizando esta función podremos especificar que el <em>widget</em>
+texto debería cortar las líneas largas en los límites de las
+palabras. El argumento <tt/word_wrap/ es un valor TRUE o FALSE.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Manipulación de texto
+<P>
+El punto actual de inserción en un <em>widget</em> texto puede
+cambiarse utilizando
+<tscreen><verb>
+void gtk_text_set_point( GtkText *text,
+ guint index );
+</verb></tscreen>
+
+donde <tt/index/ es la posición en la que poner el punto de inserción.
+
+Análogamente tenemos la función para obtener la posición del punto
+de inserción:
+
+<tscreen><verb>
+guint gtk_text_get_point( GtkText *text );
+</verb></tscreen>
+
+Una función que es útil en combinación con las dos anteriores es
+
+<tscreen><verb>
+guint gtk_text_get_length( GtkText *text );
+</verb></tscreen>
+
+que devuelve la longitud actual del <em>widget</em> texto. La
+longitud es el número de caracteres que hay en el bloque de texto,
+incluyendo caracteres como el retorno de carro, que marca el final de
+las líneas.
+
+Para insertar texto en la posición actual del cursor, tendrá que
+utilizar la función <tt/gtk_text_insert/, que nos permitirá
+especificar los colores de fondo y de la letra y un tipo de letra para
+el texto.
+
+<tscreen><verb>
+void gtk_text_insert( GtkText *text,
+ GdkFont *font,
+ GdkColor *fore,
+ GdkColor *back,
+ const char *chars,
+ gint length );
+</verb></tscreen>
+
+Pasar un valor <tt/NULL/ como el color de la letra (<tt/fore/), el
+color de fondo (<tt/back/) o el tipo de letra (<tt/font/) hará que
+se utilicen los valores que indiquen el estilo del <em>widget</em>.
+Utilizar un valor de <tt/-1/ para el parámetro <tt/length/ hará
+que se inserte todo el texto.
+
+El <em/widget/ texto es uno de los pocos de GTK que se redibuja
+a sí mismo dinámicamente, fuera de la función <tt/gtk_main/. Esto
+significa que todos los cambios en el contenido del <em/widget/ texto
+se manifestarán inmediatamente. Para permitirnos realizar varias
+actualizaciones del <em/widget/ de texto sin que se redibuje
+continuamente, podemos congelar el <em/widget/, lo que hará que pare
+momentaneamente de redibujarse a sí mismo cada vez que haya algún
+cambio. Podemos descongelarlo cuando hayamos acabado con nuestras
+actualizaciones.
+
+Las siguientes dos funciones realizarán la acción de congelar y
+descongelar el <em/widget/:
+
+<tscreen><verb>
+void gtk_text_freeze( GtkText *text );
+
+void gtk_text_thaw( GtkText *text );
+</verb></tscreen>
+
+Se puede borrar el texto que se encuentra en el punto actual de
+inserción del <em/widget/ de texto mediante dos funciones. El valor
+devuelto es TRUE o FALSE en función del éxito de la operación.
+
+<tscreen><verb>
+gint gtk_text_backward_delete( GtkText *text,
+ guint nchars );
+
+gint gtk_text_forward_delete ( GtkText *text,
+ guint nchars );
+</verb></tscreen>
+
+Si quiere recuperar el contenido del <em/widget/ de texto, entonces
+la macro <tt/GTK_TEXT_INDEX(t, index)/ le permitirá obtener el
+carácter que se encuentra en la posición <tt/index/ del <em/widget/
+de texto <tt/t/.
+
+Para obtener mayores bloques de texto, podemos utilizar la función
+
+<tscreen><verb>
+gchar *gtk_editable_get_chars( GtkEditable *editable,
+ gint start_pos,
+ gint end_pos );
+</verb></tscreen>
+
+Esta es una función de la clase padre del <em/widget/ texto. Un valor
+de -1 en <tt/end_pos/ significa el final del texto. El índice del
+texto empieza en 0.
+
+La función reserva un espacio de memoria para el bloque de texto,
+por lo que no debe olvidarse de liberarlo con una llamada a
+<tt/g_free/ cuando haya acabado el bloque.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Atajos por teclado
+<p>
+El <em/widget/ texto tiene ciertos atajos de teclado preinstalados
+para las funciones de edición estándar, movimiento y selección. Pueden
+utilizarse mediante combinaciones de las teclas Control y Alt.
+
+Además, si se mantiene apretada la tecla de Control y se utilizan las
+teclas de movimiento, el cursor se moverá por palabras en lugar de por
+caracteres. Manteniendo apretada la tecla Shift, las teclas de movimiento
+harán que se extienda la selección.
+
+<sect2>Atajos para el movimiento
+<p>
+<itemize>
+<item> Ctrl-A Principio de línea
+<item> Ctrl-E Final de línea
+<item> Ctrl-N Línea siguiente
+<item> Ctrl-P Línea anterior
+<item> Ctrl-B Retrasarse un carácter
+<item> Ctrl-F Adelantarse un carácter
+<item> Alt-B Retrasarse una palabra
+<item> Alt-F Adelantarse una palabra
+</itemize>
+
+<sect2>Atajos para la edición
+<p>
+<itemize>
+<item> Ctrl-H Borrar el carácter anterior (Backspace)
+<item> Ctrl-D Borrar el carácter siguiente (Suprimir)
+<item> Ctrl-W Borrar la palabra anterior
+<item> Alt-D Borrar la palabra siguiente
+<item> Ctrl-K Borrar hasta el fin de la línea
+<item> Ctrl-U Borrar la línea
+</itemize>
+
+<sect2>Atajos de selección
+<p>
+<itemize>
+<item> Ctrl-X Cortar al portapapeles
+<item> Ctrl-C Copiar al portapapeles
+<item> Ctrl-V Pegar desde el portapapeles
+</itemize>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Un ejemplo de GtkText
+<p>
+<tscreen><verb>
+/* principio del ejemplo text text.c */
+
+/* text.c */
+
+#include <stdio.h>
+#include <gtk/gtk.h>
+
+void text_toggle_editable (GtkWidget *checkbutton,
+ GtkWidget *text)
+{
+ gtk_text_set_editable(GTK_TEXT(text),
+ GTK_TOGGLE_BUTTON(checkbutton)->active);
+}
+
+void text_toggle_word_wrap (GtkWidget *checkbutton,
+ GtkWidget *text)
+{
+ gtk_text_set_word_wrap(GTK_TEXT(text),
+ GTK_TOGGLE_BUTTON(checkbutton)->active);
+}
+
+void close_application( GtkWidget *widget, gpointer data )
+{
+ gtk_main_quit();
+}
+
+int main (int argc, char *argv[])
+{
+ GtkWidget *ventana;
+ GtkWidget *caja1;
+ GtkWidget *caja2;
+ GtkWidget *hbox;
+ GtkWidget *boton;
+ GtkWidget *check;
+ GtkWidget *separator;
+ GtkWidget *table;
+ GtkWidget *vscrollbar;
+ GtkWidget *text;
+ GdkColormap *cmap;
+ GdkColor colour;
+ GdkFont *fixed_font;
+
+ FILE *infile;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_widget_set_usize (ventana, 600, 500);
+ gtk_window_set_policy (GTK_WINDOW(ventana), TRUE, TRUE, FALSE);
+ gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
+ GTK_SIGNAL_FUNC(close_application),
+ NULL);
+ gtk_window_set_title (GTK_WINDOW (ventana), "Text Widget Example");
+ gtk_container_border_width (GTK_CONTAINER (ventana), 0);
+
+
+ caja1 = gtk_vbox_new (FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (ventana), caja1);
+ gtk_widget_show (caja1);
+
+
+ caja2 = gtk_vbox_new (FALSE, 10);
+ gtk_container_border_width (GTK_CONTAINER (caja2), 10);
+ gtk_box_pack_start (GTK_BOX (caja1), caja2, TRUE, TRUE, 0);
+ gtk_widget_show (caja2);
+
+
+ table = gtk_table_new (2, 2, FALSE);
+ gtk_table_set_row_spacing (GTK_TABLE (table), 0, 2);
+ gtk_table_set_col_spacing (GTK_TABLE (table), 0, 2);
+ gtk_box_pack_start (GTK_BOX (caja2), table, TRUE, TRUE, 0);
+ gtk_widget_show (table);
+
+ /* Crear el widget GtkText */
+ text = gtk_text_new (NULL, NULL);
+ gtk_text_set_editable (GTK_TEXT (text), TRUE);
+ gtk_table_attach (GTK_TABLE (table), text, 0, 1, 0, 1,
+ GTK_EXPAND | GTK_SHRINK | GTK_FILL,
+ GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (text);
+
+ /* Añadir una barra de desplazamiento vertical al widget GtkText */
+ vscrollbar = gtk_vscrollbar_new (GTK_TEXT (text)->vadj);
+ gtk_table_attach (GTK_TABLE (table), vscrollbar, 1, 2, 0, 1,
+ GTK_FILL, GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (vscrollbar);
+
+ /* Obtener el mapa de colores del sistema y conseguir el color rojo */
+ cmap = gdk_colormap_get_system();
+ colour.red = 0xffff;
+ colour.green = 0;
+ colour.blue = 0;
+ if (!gdk_color_alloc(cmap, &amp;colour)) {
+ g_error("couldn't allocate colour");
+ }
+
+ /* Cargar un fuente de tamaño fijo */
+ fixed_font = gdk_font_load ("-misc-fixed-medium-r-*-*-*-140-*-*-*-*-*-*");
+
+ /* Al enviar la señal relize a un widget se crea una ventana para el
+ * mismo, y nos permite insertar texto */
+ gtk_widget_realize (text);
+
+ /* Congela el widget text, lo que nos permite hacer varias
+ * actualizaciones */
+ gtk_text_freeze (GTK_TEXT (text));
+
+ /* Insertamos algún texto coloreado */
+ gtk_text_insert (GTK_TEXT (text), NULL, &amp;text->style->black, NULL,
+ "Supports ", -1);
+ gtk_text_insert (GTK_TEXT (text), NULL, &amp;colour, NULL,
+ "colored ", -1);
+ gtk_text_insert (GTK_TEXT (text), NULL, &amp;text->style->black, NULL,
+ "text and different ", -1);
+ gtk_text_insert (GTK_TEXT (text), fixed_font, &amp;text->style->black, NULL,
+ "fonts\n\n", -1);
+
+ /* Cargamos el fichero text.c en la ventana de texto */
+
+ infile = fopen("text.c", "r");
+
+ if (infile) {
+ char buffer[1024];
+ int nchars;
+
+ while (1)
+ {
+ nchars = fread(buffer, 1, 1024, infile);
+ gtk_text_insert (GTK_TEXT (text), fixed_font, NULL,
+ NULL, buffer, nchars);
+
+ if (nchars < 1024)
+ break;
+ }
+
+ fclose (infile);
+ }
+
+ /* Descongelamos el widget text, permitiéndonos ver todos los
+ * cambios */
+ gtk_text_thaw (GTK_TEXT (text));
+
+ hbox = gtk_hbutton_box_new ();
+ gtk_box_pack_start (GTK_BOX (caja2), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ check = gtk_check_button_new_with_label("Editable");
+ gtk_box_pack_start (GTK_BOX (hbox), check, FALSE, FALSE, 0);
+ gtk_signal_connect (GTK_OBJECT(check), "toggled",
+ GTK_SIGNAL_FUNC(text_toggle_editable), text);
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(check), TRUE);
+ gtk_widget_show (check);
+ check = gtk_check_button_new_with_label("Wrap Words");
+ gtk_box_pack_start (GTK_BOX (hbox), check, FALSE, TRUE, 0);
+ gtk_signal_connect (GTK_OBJECT(check), "toggled",
+ GTK_SIGNAL_FUNC(text_toggle_word_wrap), text);
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(check), FALSE);
+ gtk_widget_show (check);
+
+ separator = gtk_hseparator_new ();
+ gtk_box_pack_start (GTK_BOX (caja1), separator, FALSE, TRUE, 0);
+ gtk_widget_show (separator);
+
+ caja2 = gtk_vbox_new (FALSE, 10);
+ gtk_container_border_width (GTK_CONTAINER (caja2), 10);
+ gtk_box_pack_start (GTK_BOX (caja1), caja2, FALSE, TRUE, 0);
+ gtk_widget_show (caja2);
+
+ boton = gtk_button_new_with_label ("close");
+ gtk_signal_connect (GTK_OBJECT (boton), "clicked",
+ GTK_SIGNAL_FUNC(close_application),
+ NULL);
+ gtk_box_pack_start (GTK_BOX (caja2), boton, TRUE, TRUE, 0);
+ GTK_WIDGET_SET_FLAGS (boton, GTK_CAN_DEFAULT);
+ gtk_widget_grab_default (boton);
+ gtk_widget_show (boton);
+
+ gtk_widget_show (ventana);
+
+ gtk_main ();
+
+ return 0;
+}
+/* fin del ejemplo */
+</verb></tscreen>
+
+<!-- ***************************************************************** -->
+<sect> Los <em>widgets</em> no documentados
+<!-- ***************************************************************** -->
+<p>
+¡Todos ellos necesitan de gente que los documente! :) Por favor,
+considere el contribuir a nuestro tutorial.
+
+Si debe utilizar uno de estos <em/widgets/, que permanecen no
+documentados, le sugiero que le eche un vistazo a su fichero de
+cabecera respectivo en la distribución GTK. Los nombre de las
+funciones GTK son muy descriptivos. Una vez haya comprendido como
+funcionan las cosas, no le será difícil ver como hay que utilizar un
+<em/widget/ simplemente mirando su declaración de funciones. Con esto,
+y unos cuántos ejemplos del código de otros, no debería tener
+problemas.
+
+Cuando haya comprendido todas las funciones de un nuevo <em/widget/
+no documentado, por favor considere el hecho de escribir un tutorial
+para él, para que así otros se puedan beneficiar del tiempo que usted
+gastó.
+
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Calendar
+<p>
+<!-- ----------------------------------------------------------------- -->
+<sect1> CTree
+<p>
+<!-- ----------------------------------------------------------------- -->
+<sect1> Curves
+<p>
+<!-- ----------------------------------------------------------------- -->
+<sect1> Drawing Area
+<p>
+<!-- ----------------------------------------------------------------- -->
+<sect1> Font Selection Dialog
+<p>
+<!-- ----------------------------------------------------------------- -->
+<sect1> Gamma Curve
+<p>
+<!-- ----------------------------------------------------------------- -->
+<sect1> Image
+<p>
+<!-- ----------------------------------------------------------------- -->
+<sect1> Packer
+<p>
+<!-- ----------------------------------------------------------------- -->
+<sect1> Plugs and Sockets
+<p>
+<!-- ----------------------------------------------------------------- -->
+<sect1> Preview
+<p>
+
+<!--
+
+(This may need to be rewritten to follow the style of the rest of the tutorial)
+
+<tscreen><verb>
+
+Previews serve a number of purposes in GIMP/GTK. The most important one is
+this. High quality images may take up to tens of megabytes of memory - easy!
+Any operation on an image that big is bound to take a long time. If it takes
+you 5-10 trial-and-errors (i.e. 10-20 steps, since you have to revert after
+you make an error) to choose the desired modification, it make take you
+literally hours to make the right one - if you don't run out of memory
+first. People who have spent hours in color darkrooms know the feeling.
+Previews to the rescue!
+
+But the annoyance of the delay is not the only issue. Oftentimes it is
+helpful to compare the Before and After versions side-by-side or at least
+back-to-back. If you're working with big images and 10 second delays,
+obtaining the Before and After impressions is, to say the least, difficult.
+For 30M images (4"x6", 600dpi, 24 bit) the side-by-side comparison is right
+out for most people, while back-to-back is more like back-to-1001, 1002,
+..., 1010-back! Previews to the rescue!
+
+But there's more. Previews allow for side-by-side pre-previews. In other
+words, you write a plug-in (e.g. the filterpack simulation) which would have
+a number of here's-what-it-would-look-like-if-you-were-to-do-this previews.
+An approach like this acts as a sort of a preview palette and is very
+effective for subtle changes. Let's go previews!
+
+There's more. For certain plug-ins real-time image-specific human
+intervention maybe necessary. In the SuperNova plug-in, for example, the
+user is asked to enter the coordinates of the center of the future
+supernova. The easiest way to do this, really, is to present the user with a
+preview and ask him to interactively select the spot. Let's go previews!
+
+Finally, a couple of misc uses. One can use previews even when not working
+with big images. For example, they are useful when rendering complicated
+patterns. (Just check out the venerable Diffraction plug-in + many other
+ones!) As another example, take a look at the colormap rotation plug-in
+(work in progress). You can also use previews for little logos inside you
+plug-ins and even for an image of yourself, The Author. Let's go previews!
+
+When Not to Use Previews
+
+Don't use previews for graphs, drawing etc. GDK is much faster for that. Use
+previews only for rendered images!
+
+Let's go previews!
+
+You can stick a preview into just about anything. In a vbox, an hbox, a
+table, a button, etc. But they look their best in tight frames around them.
+Previews by themselves do not have borders and look flat without them. (Of
+course, if the flat look is what you want...) Tight frames provide the
+necessary borders.
+
+ [Image][Image]
+
+Previews in many ways are like any other widgets in GTK (whatever that
+means) except they possess an additional feature: they need to be filled with
+some sort of an image! First, we will deal exclusively with the GTK aspect
+of previews and then we'll discuss how to fill them.
+
+GtkWidget *preview!
+
+Without any ado:
+
+ /* Create a preview widget,
+ set its size, an show it */
+GtkWidget *preview;
+preview=gtk_preview_new(GTK_PREVIEW_COLOR)
+ /*Other option:
+ GTK_PREVIEW_GRAYSCALE);*/
+gtk_preview_size (GTK_PREVIEW (preview), WIDTH, HEIGHT);
+gtk_widget_show(preview);
+my_preview_rendering_function(preview);
+
+Oh yeah, like I said, previews look good inside frames, so how about:
+
+GtkWidget *create_a_preview(int Width,
+ int Height,
+ int Colorfulness)
+{
+ GtkWidget *preview;
+ GtkWidget *frame;
+
+ frame = gtk_frame_new(NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
+ gtk_container_set_border_width (GTK_CONTAINER(frame),0);
+ gtk_widget_show(frame);
+
+ preview=gtk_preview_new (Colorfulness?GTK_PREVIEW_COLOR
+ :GTK_PREVIEW_GRAYSCALE);
+ gtk_preview_size (GTK_PREVIEW (preview), Width, Height);
+ gtk_container_add(GTK_CONTAINER(frame),preview);
+ gtk_widget_show(preview);
+
+ my_preview_rendering_function(preview);
+ return frame;
+}
+
+That's my basic preview. This routine returns the "parent" frame so you can
+place it somewhere else in your interface. Of course, you can pass the
+parent frame to this routine as a parameter. In many situations, however,
+the contents of the preview are changed continually by your application. In
+this case you may want to pass a pointer to the preview to a
+"create_a_preview()" and thus have control of it later.
+
+One more important note that may one day save you a lot of time. Sometimes
+it is desirable to label you preview. For example, you may label the preview
+containing the original image as "Original" and the one containing the
+modified image as "Less Original". It might occur to you to pack the
+preview along with the appropriate label into a vbox. The unexpected caveat
+is that if the label is wider than the preview (which may happen for a
+variety of reasons unforseeable to you, from the dynamic decision on the
+size of the preview to the size of the font) the frame expands and no longer
+fits tightly over the preview. The same problem can probably arise in other
+situations as well.
+
+ [Image]
+
+The solution is to place the preview and the label into a 2x1 table and by
+attaching them with the following parameters (this is one possible variations
+of course. The key is no GTK_FILL in the second attachment):
+
+gtk_table_attach(GTK_TABLE(table),label,0,1,0,1,
+ 0,
+ GTK_EXPAND|GTK_FILL,
+ 0,0);
+gtk_table_attach(GTK_TABLE(table),frame,0,1,1,2,
+ GTK_EXPAND,
+ GTK_EXPAND,
+ 0,0);
+
+
+And here's the result:
+
+ [Image]
+
+Misc
+
+Making a preview clickable is achieved most easily by placing it in a
+boton. It also adds a nice border around the preview and you may not even
+need to place it in a frame. See the Filter Pack Simulation plug-in for an
+example.
+
+This is pretty much it as far as GTK is concerned.
+
+Filling In a Preview
+
+In order to familiarize ourselves with the basics of filling in previews,
+let's create the following pattern (contrived by trial and error):
+
+ [Image]
+
+void
+my_preview_rendering_function(GtkWidget *preview)
+{
+#define SIZE 100
+#define HALF (SIZE/2)
+
+ guchar *row=(guchar *) malloc(3*SIZE); /* 3 bits per dot */
+ gint i, j; /* Coordinates */
+ double r, alpha, x, y;
+
+ if (preview==NULL) return; /* I usually add this when I want */
+ /* to avoid silly crashes. You */
+ /* should probably make sure that */
+ /* everything has been nicely */
+ /* initialized! */
+ for (j=0; j < ABS(cos(2*alpha)) ) { /* Are we inside the shape? */
+ /* glib.h contains ABS(x). */
+ row[i*3+0] = sqrt(1-r)*255; /* Define Red */
+ row[i*3+1] = 128; /* Define Green */
+ row[i*3+2] = 224; /* Define Blue */
+ } /* "+0" is for alignment! */
+ else {
+ row[i*3+0] = r*255;
+ row[i*3+1] = ABS(sin((float)i/SIZE*2*PI))*255;
+ row[i*3+2] = ABS(sin((float)j/SIZE*2*PI))*255;
+ }
+ }
+ gtk_preview_draw_row( GTK_PREVIEW(preview),row,0,j,SIZE);
+ /* Insert "row" into "preview" starting at the point with */
+ /* coordinates (0,j) first column, j_th row extending SIZE */
+ /* pixels to the right */
+ }
+
+ free(row); /* save some space */
+ gtk_widget_draw(preview,NULL); /* what does this do? */
+ gdk_flush(); /* or this? */
+}
+
+Non-GIMP users can have probably seen enough to do a lot of things already.
+For the GIMP users I have a few pointers to add.
+
+Image Preview
+
+It is probably wise to keep a reduced version of the image around with just
+enough pixels to fill the preview. This is done by selecting every n'th
+pixel where n is the ratio of the size of the image to the size of the
+preview. All further operations (including filling in the previews) are then
+performed on the reduced number of pixels only. The following is my
+implementation of reducing the image. (Keep in mind that I've had only basic
+C!)
+
+(UNTESTED CODE ALERT!!!)
+
+typedef struct {
+ gint width;
+ gint height;
+ gint bbp;
+ guchar *rgb;
+ guchar *mask;
+} ReducedImage;
+
+enum {
+ SELECTION_ONLY,
+ SELECTION_IN_CONTEXT,
+ ENTIRE_IMAGE
+};
+
+ReducedImage *Reduce_The_Image(GDrawable *drawable,
+ GDrawable *mask,
+ gint LongerSize,
+ gint Selection)
+{
+ /* This function reduced the image down to the the selected preview size */
+ /* The preview size is determine by LongerSize, i.e. the greater of the */
+ /* two dimensions. Works for RGB images only! */
+ gint RH, RW; /* Reduced height and reduced width */
+ gint width, height; /* Width and Height of the area being reduced */
+ gint bytes=drawable->bpp;
+ ReducedImage *temp=(ReducedImage *)malloc(sizeof(ReducedImage));
+
+ guchar *tempRGB, *src_row, *tempmask, *src_mask_row,R,G,B;
+ gint i, j, whichcol, whichrow, x1, x2, y1, y2;
+ GPixelRgn srcPR, srcMask;
+ gint NoSelectionMade=TRUE; /* Assume that we're dealing with the entire */
+ /* image. */
+
+ gimp_drawable_mask_bounds (drawable->id, &amp;x1, &amp;y1, &amp;x2, &amp;y2);
+ width = x2-x1;
+ height = y2-y1;
+ /* If there's a SELECTION, we got its bounds!)
+
+ if (width != drawable->width &amp;&amp; height != drawable->height)
+ NoSelectionMade=FALSE;
+ /* Become aware of whether the user has made an active selection */
+ /* This will become important later, when creating a reduced mask. */
+
+ /* If we want to preview the entire image, overrule the above! */
+ /* Of course, if no selection has been made, this does nothing! */
+ if (Selection==ENTIRE_IMAGE) {
+ x1=0;
+ x2=drawable->width;
+ y1=0;
+ y2=drawable->height;
+ }
+
+ /* If we want to preview a selection with some surrounding area we */
+ /* have to expand it a little bit. Consider it a bit of a riddle. */
+ if (Selection==SELECTION_IN_CONTEXT) {
+ x1=MAX(0, x1-width/2.0);
+ x2=MIN(drawable->width, x2+width/2.0);
+ y1=MAX(0, y1-height/2.0);
+ y2=MIN(drawable->height, y2+height/2.0);
+ }
+
+ /* How we can determine the width and the height of the area being */
+ /* reduced. */
+ width = x2-x1;
+ height = y2-y1;
+
+ /* The lines below determine which dimension is to be the longer */
+ /* side. The idea borrowed from the supernova plug-in. I suspect I */
+ /* could've thought of it myself, but the truth must be told. */
+ /* Plagiarism stinks! */
+ if (width>height) {
+ RW=LongerSize;
+ RH=(float) height * (float) LongerSize/ (float) width;
+ }
+ else {
+ RH=LongerSize;
+ RW=(float)width * (float) LongerSize/ (float) height;
+ }
+
+ /* The entire image is stretched into a string! */
+ tempRGB = (guchar *) malloc(RW*RH*bytes);
+ tempmask = (guchar *) malloc(RW*RH);
+
+ gimp_pixel_rgn_init (&amp;srcPR, drawable, x1, y1, width, height,
+ FALSE, FALSE);
+ gimp_pixel_rgn_init (&amp;srcMask, mask, x1, y1, width, height,
+ FALSE, FALSE);
+
+ /* Grab enough to save a row of image and a row of mask. */
+ src_row = (guchar *) malloc (width*bytes);
+ src_mask_row = (guchar *) malloc (width);
+
+ for (i=0; i < RH; i++) {
+ whichrow=(float)i*(float)height/(float)RH;
+ gimp_pixel_rgn_get_row (&amp;srcPR, src_row, x1, y1+whichrow, width);
+ gimp_pixel_rgn_get_row (&amp;srcMask, src_mask_row, x1, y1+whichrow, width);
+
+ for (j=0; j < RW; j++) {
+ whichcol=(float)j*(float)width/(float)RW;
+
+ /* No selection made = each point is completely selected! */
+ if (NoSelectionMade)
+ tempmask[i*RW+j]=255;
+ else
+ tempmask[i*RW+j]=src_mask_row[whichcol];
+
+ /* Add the row to the one long string which now contains the image! */
+ tempRGB[i*RW*bytes+j*bytes+0]=src_row[whichcol*bytes+0];
+ tempRGB[i*RW*bytes+j*bytes+1]=src_row[whichcol*bytes+1];
+ tempRGB[i*RW*bytes+j*bytes+2]=src_row[whichcol*bytes+2];
+
+ /* Hold on to the alpha as well */
+ if (bytes==4)
+ tempRGB[i*RW*bytes+j*bytes+3]=src_row[whichcol*bytes+3];
+ }
+ }
+ temp->bpp=bytes;
+ temp->width=RW;
+ temp->height=RH;
+ temp->rgb=tempRGB;
+ temp->mask=tempmask;
+ return temp;
+}
+
+The following is a preview function which used the same ReducedImage type!
+Note that it uses fakes transparency (if one is present by means of
+fake_transparency which is defined as follows:
+
+gint fake_transparency(gint i, gint j)
+{
+ if ( ((i%20)- 10) * ((j%20)- 10)>0 )
+ return 64;
+ else
+ return 196;
+}
+
+Now here's the preview function:
+
+void
+my_preview_render_function(GtkWidget *preview,
+ gint changewhat,
+ gint changewhich)
+{
+ gint Inten, bytes=drawable->bpp;
+ gint i, j, k;
+ float partial;
+ gint RW=reduced->width;
+ gint RH=reduced->height;
+ guchar *row=malloc(bytes*RW);;
+
+
+ for (i=0; i < RH; i++) {
+ for (j=0; j < RW; j++) {
+
+ row[j*3+0] = reduced->rgb[i*RW*bytes + j*bytes + 0];
+ row[j*3+1] = reduced->rgb[i*RW*bytes + j*bytes + 1];
+ row[j*3+2] = reduced->rgb[i*RW*bytes + j*bytes + 2];
+
+ if (bytes==4)
+ for (k=0; k<3; k++) {
+ float transp=reduced->rgb[i*RW*bytes+j*bytes+3]/255.0;
+ row[3*j+k]=transp*a[3*j+k]+(1-transp)*fake_transparency(i,j);
+ }
+ }
+ gtk_preview_draw_row( GTK_PREVIEW(preview),row,0,i,RW);
+ }
+
+ free(a);
+ gtk_widget_draw(preview,NULL);
+ gdk_flush();
+}
+
+Applicable Routines
+
+guint gtk_preview_get_type (void);
+/* No idea */
+void gtk_preview_uninit (void);
+/* No idea */
+GtkWidget* gtk_preview_new (GtkPreviewType type);
+/* Described above */
+void gtk_preview_size (GtkPreview *preview,
+ gint width,
+ gint height);
+/* Allows you to resize an existing preview. */
+/* Apparently there's a bug in GTK which makes */
+/* this process messy. A way to clean up a mess */
+/* is to manually resize the window containing */
+/* the preview after resizing the preview. */
+
+void gtk_preview_put (GtkPreview *preview,
+ GdkWindow *ventana,
+ GdkGC *gc,
+ gint srcx,
+ gint srcy,
+ gint destx,
+ gint desty,
+ gint width,
+ gint height);
+/* No idea */
+
+void gtk_preview_put_row (GtkPreview *preview,
+ guchar *src,
+ guchar *dest,
+ gint x,
+ gint y,
+ gint w);
+/* No idea */
+
+void gtk_preview_draw_row (GtkPreview *preview,
+ guchar *data,
+ gint x,
+ gint y,
+ gint w);
+/* Described in the text */
+
+void gtk_preview_set_expand (GtkPreview *preview,
+ gint expand);
+/* No idea */
+
+/* No clue for any of the below but */
+/* should be standard for most widgets */
+void gtk_preview_set_gamma (double gamma);
+void gtk_preview_set_color_cube (guint nred_shades,
+ guint ngreen_shades,
+ guint nblue_shades,
+ guint ngray_shades);
+void gtk_preview_set_install_cmap (gint install_cmap);
+void gtk_preview_set_reserved (gint nreserved);
+GdkVisual* gtk_preview_get_visual (void);
+GdkColormap* gtk_preview_get_cmap (void);
+GtkPreviewInfo* gtk_preview_get_info (void);
+
+That's all, folks!
+
+</verb></tscreen>
+
+-->
+
+<!-- ***************************************************************** -->
+<sect>Estableciendo los atributos de un <em/widget/<label id="sec_setting_widget_attributes">
+<!-- ***************************************************************** -->
+<p>
+En este capítulo se describen las funciones utilizadas para manejar los
+<em/widgets/. Pueden utilizarse para establecer el estilo, relleno,
+tamaño, etc...
+
+(Puede que deba hacer una sección completa para los aceleradores.)
+
+<tscreen><verb>
+void gtk_widget_install_accelerator( GtkWidget *widget,
+ GtkAcceleratorTable *table,
+ gchar *signal_name,
+ gchar key,
+ guint8 modifiers );
+
+void gtk_widget_remove_accelerator ( GtkWidget *widget,
+ GtkAcceleratorTable *table,
+ gchar *signal_name);
+
+void gtk_widget_activate( GtkWidget *widget );
+
+void gtk_widget_set_name( GtkWidget *widget,
+ gchar *name );
+
+gchar *gtk_widget_get_name( GtkWidget *widget );
+
+void gtk_widget_set_sensitive( GtkWidget *widget,
+ gint sensitive );
+
+void gtk_widget_set_style( GtkWidget *widget,
+ GtkStyle *style );
+
+GtkStyle *gtk_widget_get_style( GtkWidget *widget );
+
+GtkStyle *gtk_widget_get_default_style( void );
+
+void gtk_widget_set_uposition( GtkWidget *widget,
+ gint x,
+ gint y );
+
+void gtk_widget_set_usize( GtkWidget *widget,
+ gint width,
+ gint height );
+
+void gtk_widget_grab_focus( GtkWidget *widget );
+
+void gtk_widget_show( GtkWidget *widget );
+
+void gtk_widget_hide( GtkWidget *widget );
+</verb></tscreen>
+
+<!-- ***************************************************************** -->
+<sect>Tiempos de espera, ES (<em/IO/) y funciones ociosas (<em/idle/)<label id="sec_timeouts">
+<!-- ***************************************************************** -->
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Tiempos de espera
+<p>
+Puede que se esté preguntando como hacer que GTK haga algo útil
+cuando se encuentra en <tt/gtk_main/. Bien, tiene varias
+opciones. Utilizando las rutinas siguientes puede crear una función
+a la que se llamará cada <tt/interval/ milisegundos.
+
+<tscreen><verb>
+gint gtk_timeout_add( guint32 interval,
+ GtkFunction function,
+ gpointer data );
+</verb></tscreen>
+
+El primer argumento es el número de milisegundos que habrá entre dos
+llamadas a su función. El segundo argumento es la función a la que
+desea llamar, y el tercero, los datos que le pasará a ésta función.
+El valor devuelto es un «identificador» (un valor entero) que puede
+utilizar para detener las llamadas haciendo:
+
+<tscreen><verb>
+void gtk_timeout_remove( gint tag );
+</verb></tscreen>
+
+También puede hacer que cesen las llamadas a la función haciendo que
+la misma devuelva cero o FALSE. Obviamente esto significa que si
+quiere que se continue llamando a su función, deberá devolver un valor
+distinto de cero, es decir TRUE.
+
+La declaración de su función debería ser algo como:
+
+<tscreen><verb>
+gint timeout_callback( gpointer data );
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Monitorizando la ES
+<p>
+Otra característica divertida de GTK, es la habilidad que tiene de
+comprobar datos por usted en un descriptor de fichero (tal y como
+se devuelven por <tt/open(2)/ o <tt/socket(2)/). Esto es especialmente
+útil para las aplicaciones de red. La función:
+
+<tscreen><verb>
+gint gdk_input_add( gint source,
+ GdkInputCondition condition,
+ GdkInputFunction function,
+ gpointer data );
+</verb></tscreen>
+
+Donde el primer argumento es el descriptor de fichero que desea vigilar,
+y el segundo especifica que es lo que quiere que GDK busque. Puede ser uno
+de los siguientes:
+
+<itemize>
+<item>GDK_INPUT_READ - Llama a su función cuando hay datos listos para
+leerse del fichero.
+
+<item>GDK_INPUT_WRITE - Llama a su función cuando el descriptor del
+fichero está listo para la escritura.
+</itemize>
+
+Tal y como se habrá imaginado, el tercer argumento es la función a la
+que desea que se llame cuando se den las condiciones anteriores, y el
+cuarto son los datos que se le pasarán a ésta función.
+
+El valor devuelto es un identificador que puede utilizarse para que GDK
+pare de vigilar ese fichero, utilizando la función
+
+<tscreen><verb>
+void gdk_input_remove( gint tag );
+</verb></tscreen>
+
+La función a la que quiere que se llame deberá declararse así:
+
+<tscreen><verb>
+void input_callback( gpointer data,
+ gint source,
+ GdkInputCondition condition );
+</verb></tscreen>
+
+Donde <tt/source/ y <tt/condition/ están especificados más arriba.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Funciones ociosas
+<p>
+<!-- Need to check on idle priorities - TRG -->
+¿Qué le parece si tuviese una función a la que se llamase cuando
+no ocurriese nada?
+
+<tscreen><verb>
+gint gtk_idle_add( GtkFunction function,
+ gpointer data );
+</verb></tscreen>
+
+Esto hace que GTK llame a la función especificada cuando no ocurra
+nada más.
+
+<tscreen><verb>
+void gtk_idle_remove( gint tag );
+</verb></tscreen>
+
+No voy a explicar el significado de los argumentos ya que se parece
+mucho a los que he explicado más arriba. La función a la que se apunta
+mediante el primer argumento de <tt/gtk_idle_add/ será a la que se
+llame cuando llegue el momento. Como antes, si devuelve FALSE hará que
+cese de llamarse a la función.
+
+<!-- ***************************************************************** -->
+<sect>Manejo avanzado de eventos y señales<label id="sec_Adv_Events_and_Signals">
+<!-- ***************************************************************** -->
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Funciones señal
+
+<!-- ----------------------------------------------------------------- -->
+<sect2>Conectando y desconectando los manejadores de señal
+<p>
+
+<tscreen><verb>
+guint gtk_signal_connect( GtkObject *object,
+ const gchar *name,
+ GtkSignalFunc func,
+ gpointer func_data );
+
+guint gtk_signal_connect_after( GtkObject *object,
+ const gchar *name,
+ GtkSignalFunc func,
+ gpointer func_data );
+
+guint gtk_signal_connect_object( GtkObject *object,
+ const gchar *name,
+ GtkSignalFunc func,
+ GtkObject *slot_object );
+
+guint gtk_signal_connect_object_after( GtkObject *object,
+ const gchar *name,
+ GtkSignalFunc func,
+ GtkObject *slot_object );
+
+guint gtk_signal_connect_full( GtkObject *object,
+ const gchar *name,
+ GtkSignalFunc func,
+ GtkCallbackMarshal marshal,
+ gpointer data,
+ GtkDestroyNotify destroy_func,
+ gint object_signal,
+ gint after );
+
+guint gtk_signal_connect_interp( GtkObject *object,
+ const gchar *name,
+ GtkCallbackMarshal func,
+ gpointer data,
+ GtkDestroyNotify destroy_func,
+ gint after );
+
+void gtk_signal_connect_object_while_alive( GtkObject *object,
+ const gchar *signal,
+ GtkSignalFunc func,
+ GtkObject *alive_object );
+
+void gtk_signal_connect_while_alive( GtkObject *object,
+ const gchar *signal,
+ GtkSignalFunc func,
+ gpointer func_data,
+ GtkObject *alive_object );
+
+void gtk_signal_disconnect( GtkObject *object,
+ guint handler_id );
+
+void gtk_signal_disconnect_by_func( GtkObject *object,
+ GtkSignalFunc func,
+ gpointer data );
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect2>Bloqueando y desbloqueando los manejadores de señal
+<p>
+<tscreen><verb>
+void gtk_signal_handler_block( GtkObject *object,
+ guint handler_id);
+
+void gtk_signal_handler_block_by_func( GtkObject *object,
+ GtkSignalFunc func,
+ gpointer data );
+
+void gtk_signal_handler_block_by_data( GtkObject *object,
+ gpointer data );
+
+void gtk_signal_handler_unblock( GtkObject *object,
+ guint handler_id );
+
+void gtk_signal_handler_unblock_by_func( GtkObject *object,
+ GtkSignalFunc func,
+ gpointer data );
+
+void gtk_signal_handler_unblock_by_data( GtkObject *object,
+ gpointer data );
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect2>Emitiendo y deteniendo señales
+<p>
+<tscreen><verb>
+void gtk_signal_emit( GtkObject *object,
+ guint signal_id,
+ ... );
+
+void gtk_signal_emit_by_name( GtkObject *object,
+ const gchar *name,
+ ... );
+
+void gtk_signal_emitv( GtkObject *object,
+ guint signal_id,
+ GtkArg *params );
+
+void gtk_signal_emitv_by_name( GtkObject *object,
+ const gchar *name,
+ GtkArg *params );
+
+guint gtk_signal_n_emissions( GtkObject *object,
+ guint signal_id );
+
+guint gtk_signal_n_emissions_by_name( GtkObject *object,
+ const gchar *name );
+
+void gtk_signal_emit_stop( GtkObject *object,
+ guint signal_id );
+
+void gtk_signal_emit_stop_by_name( GtkObject *object,
+ const gchar *name );
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Emisión y propagación de señales
+<p>
+La emisión de señales es el proceso mediante el cual GTK+ ejecuta
+todos los manejadores de un objeto y una señal en especial.
+
+Primero, observe que el valor devuelto por la emisión de una
+señal es el mismo que el valor devuelto por el <em>último</em>
+manipulador ejecutado. Ya que las señales de los eventos son todas
+del tipo GTK_RUN_LAST, el manejador por defecto (proporcionado por
+GTK+) será de este tipo, a menos que lo conecte con
+<tt/gtk_signal_connect_after()/.
+
+La forma en que se maneja un evento (digamos GTK_BUTTON_PRESS), es la
+siguiente:
+
+<itemize>
+<item>Empieza con el <em>widget</em> donde ocurrió el evento.
+
+<item>Emite la señal genérica <tt/event/. Si esta señal devuelve un
+valor TRUE, detiene todo el proceso.
+
+<item>En caso contrario, emite una señal especifica,
+«button_press_event» en nuestro caso. Si ésta devuelve TRUE, detiene
+todo el proceso.
+
+<item>En caso contrario, va al <em>widget</em> padre y repite los
+pasos anteriores.
+
+<item>Continua hasta que algún manejador de señal devuelva TRUE, o
+hasta que se llegue al <em>widget</em> de más alto nivel.
+</itemize>
+
+Algunas consecuencias son:
+<itemize>
+<item>El valor que devuelva su manejador no tendrá ningún efecto si
+hay un manejador por defecto, a menos que lo conecte mediante
+<tt/gtk_signal_connect_after()/.
+
+<item>Para evitar que el manejador por defecto se ejecute, necesita
+conectar mediante <tt/gtk_signal_connect()/ y utilizar
+<tt/gtk_signal_emit_stop_by_name()/ - el valor devuelto sólo se ve
+afectado si la señal se propaga, y no sólo por el hecho de emitirse.
+</itemize>
+
+<!-- ***************************************************************** -->
+<sect>Manejando selecciones
+<!-- ***************************************************************** -->
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Contenido
+<p>
+Un tipo de comunicación entre procesos que se puede utilizar con GTK
+son las <em/selecciones/. Una selección identifica un conjunto de
+datos, por ejemplo, un trozo de texto, seleccionado por el usuario de
+alguna manera, por ejemplo, cogiéndolo con el ratón. Sólo una
+aplicación en un <em/display/ (la <em>propietaria</em>) puede tener
+una selección en particular en un momento dado, por lo que cuando una
+aplicación pide una selección, el propietario previo debe indicar al
+usuario que la selección ya no es válida. Otras aplicaciones pueden
+pedir el contenido de la selección de diferentes formas, llamadas
+<em/objetivos/. Puede haber cualquier número de selecciones, pero la
+mayoría de las aplicacion X sólo pueden manejar una, la <em/selección
+primaria/.
+
+En muchos casos, no es necesario para una aplicación GTK tratar por
+sí misma con las selecciones. Los <em/widgets/ estándar, como el
+<em/widget/ Entry, ya tienen la posibilidad de crear la selección
+cuando sea necesario (p.e., cuando el usuario pase el ratón sobre el
+texto manteniendo el botón derecho del ratón pulsado), y de recoger
+los contenidos de la selección propiedad de otro <em/widget/, o de
+otra aplicación (p.e., cuando el usuario pulsa el segundo botón del
+ratón). Sin embargo, pueden haber casos en los que quiera darle a
+otros <em/widgets/ la posibilidad de proporcionar la selección, o
+puede que quiera recuperar objetivos que no estén admitidos por
+defecto.
+
+Un concepto fundamental que es necesario para comprender el manejo de
+la selección es el de <em>átomo</em>. Un átomo es un entero que
+identifica de una manera unívoca una cadena (en un cierto
+<em/display/). Ciertos átomos están predefinidos por el servidor X, y
+en algunos casos hay constantes en <tt>gtk.h</tt> que corresponden a
+estos átomos. Por ejemplo la constante <tt>GDK_PRIMARY_SELECTION</tt>
+corresponde a la cadena «PRIMARY». En otros casos, debería utilizar
+las funciones <tt>gdk_atom_intern()</tt>, para obtener el átomo
+correspondiente a una cadena, y <tt>gdk_atom_name()</tt>, para obtener
+el nombre de un átomo. Ambas, selecciones y objetivos, están
+identificados por átomos.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Recuperando la selección
+<p>
+Recuperar la selección es un proceso asíncrono. Para comenzar el
+proceso, deberá llamar a:
+
+<tscreen><verb>
+gint gtk_selection_convert( GtkWidget *widget,
+ GdkAtom selection,
+ GdkAtom target,
+ guint32 time );
+</verb</tscreen>
+
+Este proceso <em/convierte/ la selección en la forma especificada por
+<tt/target/. Si es posible, el campo <tt/time/ debe ser el tiempo
+desde que el evento lanzó la selección. Esto ayuda a asegurarse de que
+los eventos ocurran en el orden en que el usuario los ha pedido. Sin
+embargo, si no está disponible (por ejemplo, si se empezó la
+conversión por una señal de «pulsación»), entonces puede utilizar la
+constante <tt>GDK_CURRENT_TIME</tt>.
+
+Cuando el propietario de la selección responda a la petición, se
+enviará una señal «selection_received» a su aplicación. El manejador
+de esta señal recibe un puntero a una estructura
+<tt>GtkSelectionData</tt>, que se define como:
+
+<tscreen><verb>
+struct _GtkSelectionData
+{
+ GdkAtom selection;
+ GdkAtom target;
+ GdkAtom type;
+ gint format;
+ guchar *data;
+ gint length;
+};
+</verb></tscreen>
+
+<tt>selection</tt> y <tt>target</tt> son los valores que dió en su
+llamada a <tt>gtk_selection_convert()</tt>. <tt>type</tt> es un átomo
+que identifica el tipo de datos devueltos por el propietario de la
+selección. Algunos valores posibles son «STRING», un cadena de
+caracteres latin-1, «ATOM», una serie de átomos, «INTEGER», un
+entero, etc. Muchos objetivos sólo pueden devolver un
+tipo. <tt/format/ da la longitud de las unidades (por ejemplo
+caracteres) en bits. Normalmente, no tiene porque preocuparse de todo
+esto cuando recibe datos. <tt/data/ es un puntero a los datos
+devueltos, y <tt/length/ da la longitud de los datos devueltos, en
+bytes. Si <tt/length/ es negativo, es que a ocurrido un error y no se
+puede obtener la selección. Esto podría ocurrir si no hay ninguna
+aplicación que sea la propietaria de la selección, o si pide un
+objetivo que la aplicación no admite. Actualmente se garantiza que el
+búfer tendrá un byte más que <tt/length/; el byte extra siempre será
+cero, por lo que no es necesario hacer una copia de las cadenas sólo
+para añadirles un carácter nulo al final.
+
+En el siguiente ejemplo, recuperamos el objetivo especial «TARGETS»,
+que es una lista de todos los objetivos en los que se puede convertir
+la selección.
+
+<tscreen><verb>
+/* principio del ejemplo selection gettargets.c */
+
+#include <gtk/gtk.h>
+
+void selection_received (GtkWidget *widget,
+ GtkSelectionData *selection_data,
+ gpointer data);
+
+/* Manejador de señal invocado cuando el usuario pulsa en el botón
+"Get Targets" */
+void
+get_targets (GtkWidget *widget, gpointer data)
+{
+ static GdkAtom targets_atom = GDK_NONE;
+
+ /* Obtener el atom correpondiente a la cadena "TARGETS" */
+ if (targets_atom == GDK_NONE)
+ targets_atom = gdk_atom_intern ("TARGETS", FALSE);
+
+ /* Y pide el objetivo "TARGETS" de la selección primaria */
+ gtk_selection_convert (widget, GDK_SELECTION_PRIMARY, targets_atom,
+ GDK_CURRENT_TIME);
+}
+
+/* Manipulador de señal llamado cuando el propietario de la señal
+ * devuelve los datos */
+void
+selection_received (GtkWidget *widget, GtkSelectionData *selection_data,
+ gpointer data)
+{
+ GdkAtom *atoms;
+ GList *item_list;
+ int i;
+
+ /* **** IMPORTANTE **** Comprobar si se da la recuperación de los
+ * datos */
+ if (selection_data->length < 0)
+ {
+ g_print ("Selection retrieval failed\n");
+ return;
+ }
+ /* Asegurarse de que obtenemos los datos de la forma esperada */
+ if (selection_data->type != GDK_SELECTION_TYPE_ATOM)
+ {
+ g_print ("Selection \"TARGETS\" was not returned as atoms!\n");
+ return;
+ }
+
+ /* Imprimir los atoms que hemos recibido */
+ atoms = (GdkAtom *)selection_data->data;
+
+ item_list = NULL;
+ for (i=0; i<selection_data->length/sizeof(GdkAtom); i++)
+ {
+ char *name;
+ name = gdk_atom_name (atoms[i]);
+ if (name != NULL)
+ g_print ("%s\n",name);
+ else
+ g_print ("(bad atom)\n");
+ }
+
+ return;
+}
+
+int
+main (int argc, char *argv[])
+{
+ GtkWidget *ventana;
+ GtkWidget *boton;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ /* Crear la ventana superior */
+
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title (GTK_WINDOW (ventana), "Event Box");
+ gtk_container_border_width (GTK_CONTAINER (ventana), 10);
+
+ gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
+ GTK_SIGNAL_FUNC (gtk_exit), NULL);
+
+ /* Crear un botón que el usuario puede pulsar para obtener los
+ * objetivos */
+
+ boton = gtk_button_new_with_label ("Get Targets");
+ gtk_container_add (GTK_CONTAINER (ventana), boton);
+
+ gtk_signal_connect (GTK_OBJECT(boton), "clicked",
+ GTK_SIGNAL_FUNC (get_targets), NULL);
+ gtk_signal_connect (GTK_OBJECT(boton), "selection_received",
+ GTK_SIGNAL_FUNC (selection_received), NULL);
+
+ gtk_widget_show (boton);
+ gtk_widget_show (ventana);
+
+ gtk_main ();
+
+ return 0;
+}
+/* fin del ejemplo */
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Proporcionando la selección
+<p>
+Proporcionar la selección es un poco más complicado. Debe registrar
+los manejadores a los que se llamarán cuando se le pida la
+selección. Por cada par selección/objetivo que quiera manejar, deberá
+hacer una llamada a:
+
+<tscreen><verb>
+void gtk_selection_add_handler( GtkWidget *widget,
+ GdkAtom selection,
+ GdkAtom target,
+ GtkSelectionFunction function,
+ GtkRemoveFunction remove_func,
+ gpointer data );
+</verb></tscreen>
+
+<tt/widget/, <tt/selection/, y <tt/target/ identifican las peticiones
+que este manejador puede manipular. Si <tt/remove_func/ no es NULL, se
+le llamará cuando se elimine el manejador de la señal. Esto es útil,
+por ejemplo, para los lenguajes interpretados que necesitan mantener
+una memoria de las referencias a <tt/data/.
+
+La función de llamada tiene el prototipo:
+
+<tscreen><verb>
+typedef void (*GtkSelectionFunction)( GtkWidget *widget,
+ GtkSelectionData *selection_data,
+ gpointer data );
+
+</verb></tscreen>
+
+El <tt/GtkSelectionData/ es el mismo que hay más arriba, pero esta
+vez, seremos nosotros los responsables de rellenar los campos
+<tt/type/, <tt/format/, <tt/data/, y <tt/length/. (El campo
+<tt/format/ es importante - el servidor X lo utiliza para saber si
+tienen que intercambiarse los bytes que forman los datos o
+no. Normalmente será 8 - es decir un carácter - o 32 - es decir un
+entero.) Esto se hace llamando a la función:
+
+<tscreen><verb>
+void gtk_selection_data_set( GtkSelectionData *selection_data,
+ GdkAtom type,
+ gint format,
+ guchar *data,
+ gint length );
+</verb></tscreen>
+
+Esta función tiene la responsabilidad de hacer una copia de los datos
+para que no tenga que preocuparse de ir guardándolos. (No debería
+rellenar los campos de la estructura <tt/GtkSelectionData/ a mano.)
+
+Cuando haga falta, puede pedir el propietario de la selección llamando
+a:
+
+<tscreen><verb>
+gint gtk_selection_owner_set( GtkWidget *widget,
+ GdkAtom selection,
+ guint32 time );
+</verb></tscreen>
+
+Si otra aplicación pide el propietario de la selección, recibira un
+«selection_clear_event».
+
+Como ejemplo de proporciar la selección, el programa siguiente le añade
+la posibilidad de selección a un botón de comprobación. Cuando se presione
+el botón de comprobación, el programa pedirá la selección primaria. El
+único objetivo que admite es un objetivo «STRING» (aparte de ciertos
+objetivos como "TARGETS", proporcionados por GTK). Cuando se pida este
+objetivo, se devolverá una representación del tiempo.
+
+<tscreen><verb>
+/* principio del ejemplo selection setselection.c */
+
+#include <gtk/gtk.h>
+#include <time.h>
+
+/* Función de llamada para cuando el usuario cambia la selección */
+void
+selection_toggled (GtkWidget *widget, gint *have_selection)
+{
+ if (GTK_TOGGLE_BUTTON(widget)->active)
+ {
+ *have_selection = gtk_selection_owner_set (widget,
+ GDK_SELECTION_PRIMARY,
+ GDK_CURRENT_TIME);
+ /* Si la demanda de la selección ha fallado, ponemos el botón en
+ * estado apagado */
+ if (!*have_selection)
+ gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON(widget), FALSE);
+ }
+ else
+ {
+ if (*have_selection)
+ {
+ /* Antes de eliminar la seleción poniendo el propietario a
+ * NULL, comprobamos si somos el propietario actual */
+ if (gdk_selection_owner_get (GDK_SELECTION_PRIMARY) == widget->window)
+ gtk_selection_owner_set (NULL, GDK_SELECTION_PRIMARY,
+ GDK_CURRENT_TIME);
+ *have_selection = FALSE;
+ }
+ }
+}
+
+/* Llamado cuando otra aplicación pide la selección */
+gint
+selection_clear (GtkWidget *widget, GdkEventSelection *event,
+ gint *have_selection)
+{
+ *have_selection = FALSE;
+ gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON(widget), FALSE);
+
+ return TRUE;
+}
+
+/* Proporciona el tiempo actual como selección. */
+void
+selection_handle (GtkWidget *widget,
+ GtkSelectionData *selection_data,
+ gpointer data)
+{
+ gchar *timestr;
+ time_t current_time;
+
+ current_time = time (NULL);
+ timestr = asctime (localtime(&amp;current_time));
+ /* Cuando devolvemos una cadena, no debe terminar en NULL. La
+ * función lo hará por nosotros */
+
+ gtk_selection_data_set (selection_data, GDK_SELECTION_TYPE_STRING,
+ 8, timestr, strlen(timestr));
+}
+
+int
+main (int argc, char *argv[])
+{
+ GtkWidget *ventana;
+
+ GtkWidget *selection_button;
+
+ static int have_selection = FALSE;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ /* Crear la ventana superior */
+
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title (GTK_WINDOW (ventana), "Event Box");
+ gtk_container_border_width (GTK_CONTAINER (ventana), 10);
+
+ gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
+ GTK_SIGNAL_FUNC (gtk_exit), NULL);
+
+ /* Crear un botón de selección para que actue como la selección */
+
+ selection_button = gtk_toggle_button_new_with_label ("Claim Selection");
+ gtk_container_add (GTK_CONTAINER (ventana), selection_button);
+ gtk_widget_show (selection_button);
+
+ gtk_signal_connect (GTK_OBJECT(selection_button), "toggled",
+ GTK_SIGNAL_FUNC (selection_toggled), &amp;have_selection);
+ gtk_signal_connect (GTK_OBJECT(selection_button), "selection_clear_event",
+ GTK_SIGNAL_FUNC (selection_clear), &amp;have_selection);
+
+ gtk_selection_add_handler (selection_button, GDK_SELECTION_PRIMARY,
+ GDK_SELECTION_TYPE_STRING,
+ selection_handle, NULL);
+
+ gtk_widget_show (selection_button);
+ gtk_widget_show (ventana);
+
+ gtk_main ();
+
+ return 0;
+}
+/* fin del ejemplo */
+</verb></tscreen>
+
+
+<!-- ***************************************************************** -->
+<sect>Glib<label id="sec_glib">
+<!-- ***************************************************************** -->
+<p>
+Glib proporciona muchas definiciones y funciones útiles disponibles
+para su utilización cuando se crean aplicaciones GDK y GTK. Haré una
+lista con todas ellas incluyendo una pequeña explicación. Muchas no
+son más que duplicados de funciones estándar de libc por lo que no
+entraré en detalle en la explicación de las mismas. Esta sección está
+pensada principalmente para que se utilice como referencia, para que
+sepa que es lo que puede utilizar.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Definiciones
+<p>
+Las definiciones para los límites de muchos de los tipos estándar son:
+
+<tscreen><verb>
+G_MINFLOAT
+G_MAXFLOAT
+G_MINDOUBLE
+G_MAXDOUBLE
+G_MINSHORT
+G_MAXSHORT
+G_MININT
+G_MAXINT
+G_MINLONG
+G_MAXLONG
+</verb></tscreen>
+
+Y también, los siguientes <tt/typedefs/. Cuando no se especifica el
+tipo que debería aparecer a la izquierda significa que el mismo se
+establecerá dinámicamente en función de la arquitectura. ¡Recuerde
+evitar los calculos relativos al tamaño de un puntero si quiere que
+su aplicación sea portable! P.e., un puntero en un Alpha tiene 8
+bytes, pero 4 en Intel.
+
+<tscreen><verb>
+char gchar;
+short gshort;
+long glong;
+int gint;
+char gboolean;
+
+unsigned char guchar;
+unsigned short gushort;
+unsigned long gulong;
+unsigned int guint;
+
+float gfloat;
+double gdouble;
+long double gldouble;
+
+void* gpointer;
+
+gint8
+guint8
+gint16
+guint16
+gint32
+guint32
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Listas doblemente enlazadas
+<p>
+Las funciones siguientes se utilizan para crear, manipular, y destruir
+listas doblemente enlazadas. Supondré que sabe lo que son las listas
+enlazadas, ya que explicarlas va más allá del objetivo de este
+documento. Por supuesto, no es necesario que conozca como manejar
+todo esto para utilizar GTK, pero siempre es bonito aprender cosas.
+
+<tscreen><verb>
+GList *g_list_alloc( void );
+
+void g_list_free( GList *list );
+
+void g_list_free_1( GList *list );
+
+GList *g_list_append( GList *list,
+ gpointer data );
+
+GList *g_list_prepend( GList *list,
+ gpointer data );
+
+GList *g_list_insert( GList *list,
+ gpointer data,
+ gint posicion );
+
+GList *g_list_remove( GList *list,
+ gpointer data );
+
+GList *g_list_remove_link( GList *list,
+ GList *link );
+
+GList *g_list_reverse( GList *list );
+
+GList *g_list_nth( GList *list,
+ gint n );
+
+GList *g_list_find( GList *list,
+ gpointer data );
+
+GList *g_list_last( GList *list );
+
+GList *g_list_first( GList *list );
+
+gint g_list_length( GList *list );
+
+void g_list_foreach( GList *list,
+ GFunc func,
+ gpointer user_data );
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Listas simplemente enlazadas
+<p>
+Muchas de las funciones para las listas enlazadas son idénticas a las
+de más arriba. Aquí hay una lista completa:
+
+<tscreen><verb>
+GSList *g_slist_alloc( void );
+
+void g_slist_free( GSList *list );
+
+void g_slist_free_1( GSList *list );
+
+GSList *g_slist_append( GSList *list,
+ gpointer data );
+
+GSList *g_slist_prepend( GSList *list,
+ gpointer data );
+
+GSList *g_slist_insert( GSList *list,
+ gpointer data,
+ gint posicion );
+
+GSList *g_slist_remove( GSList *list,
+ gpointer data );
+
+GSList *g_slist_remove_link( GSList *list,
+ GSList *link );
+
+GSList *g_slist_reverse( GSList *list );
+
+GSList *g_slist_nth( GSList *list,
+ gint n );
+
+GSList *g_slist_find( GSList *list,
+ gpointer data );
+
+GSList *g_slist_last( GSList *list );
+
+gint g_slist_length( GSList *list );
+
+void g_slist_foreach( GSList *list,
+ GFunc func,
+ gpointer user_data );
+
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Control de la memoria
+<p>
+<tscreen><verb>
+gpointer g_malloc( gulong size );
+</verb></tscreen>
+
+Reemplaza a <tt/malloc()/. No necesita comprobar el valor devuelto, ya
+que ya lo hace por usted esta función.
+
+<tscreen><verb>
+gpointer g_malloc0( gulong size );
+</verb></tscreen>
+
+Lo mismo que antes, pero rellena con ceros la memoria antes de
+devolver un puntero a ella.
+
+<tscreen><verb>
+gpointer g_realloc( gpointer mem,
+ gulong size );
+</verb></tscreen>
+
+Vuelve a reservar <tt/size/ bites de memoria empezando en
+<tt/mem/. Obviamente, la memoria debe haber sido previamente reservada.
+
+<tscreen><verb>
+void g_free( gpointer mem );
+</verb></tscreen>
+
+Libera la memoria. Fácil.
+
+<tscreen><verb>
+void g_mem_profile( void );
+</verb></tscreen>
+
+Crea un fichero donde vuelca la memoria que se está utilizando, pero
+tiene que añadir <tt/#define MEM_PROFILE/ en lo alto de
+<tt>glib/gmem.c</tt> y tendrá que hacer un make y un make install.
+
+<tscreen><verb>
+void g_mem_check( gpointer mem );
+</verb></tscreen>
+
+Comprueba que una dirección de memoria es válida. Tiene que añadir
+<tt/#define MEM_CHECK/ en lo alto de <tt/gmem.c/ y tiene que hacer un
+make y un make install.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Timers
+<p>
+Temporizadores...
+
+<tscreen><verb>
+GTimer *g_timer_new( void );
+
+void g_timer_destroy( GTimer *timer );
+
+void g_timer_start( GTimer *timer );
+
+void g_timer_stop( GTimer *timer );
+
+void g_timer_reset( GTimer *timer );
+
+gdouble g_timer_elapsed( GTimer *timer,
+ gulong *microseconds );
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Manejo de cadenas de texto
+<p>
+Un puñado de funciones para manejar cadenas de texto. Parecen muy
+interesantes, y probablemente sean mejores en muchos aspectos que las
+funciones estándar de C, pero necesitan documentación.
+
+<tscreen><verb>
+GString *g_string_new( gchar *init );
+
+void g_string_free( GString *string,
+ gint free_segment );
+
+GString *g_string_assign( GString *lval,
+ gchar *rval );
+
+GString *g_string_truncate( GString *string,
+ gint len );
+
+GString *g_string_append( GString *string,
+ gchar *val );
+
+GString *g_string_append_c( GString *string,
+ gchar c );
+
+GString *g_string_prepend( GString *string,
+ gchar *val );
+
+GString *g_string_prepend_c( GString *string,
+ gchar c );
+
+void g_string_sprintf( GString *string,
+ gchar *fmt,
+ ...);
+
+void g_string_sprintfa ( GString *string,
+ gchar *fmt,
+ ... );
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Funciones de error y funciones varias
+<p>
+<tscreen><verb>
+gchar *g_strdup( const gchar *str );
+</verb></tscreen>
+
+Reemplaza a la función <tt/strdup/. Copia el contenido de la cadena
+original en un nuevo lugar en memoria, y devuelve un puntero al nuevo
+lugar.
+
+<tscreen><verb>
+gchar *g_strerror( gint errnum );
+</verb></tscreen>
+
+Recomiendo utilizar esta función para todos los mensages de error. Es
+mucho más bonita, y más portable que <tt/perror()/ y demás funciones
+clásicas. La salida es normalmente de la forma:
+
+<tscreen><verb>
+nombre del programa:función que falló:fichero o descripción adicional:strerror
+</verb></tscreen>
+
+Aquí hay un ejemplo de una llamada utilizada en nuestro programa
+<tt/hello_world/:
+
+<tscreen><verb>
+g_print("hello_world:open:%s:%s\n", filename, g_strerror(errno));
+</verb></tscreen>
+
+<tscreen><verb>
+void g_error( gchar *format, ... );
+</verb></tscreen>
+
+Imprime un mensaje de error. El formato es como el de <tt/printf/,
+pero le añade <tt/** ERROR **: / a su mensaje, y sale del
+programa. Sólo para errores fatales.
+
+<tscreen><verb>
+void g_warning( gchar *format, ... );
+</verb></tscreen>
+
+El mismo que el anterior, pero añade "** WARNING **: ", y no sale del
+programa.
+
+<tscreen><verb>
+void g_message( gchar *format, ... );
+</verb></tscreen>
+
+Imprime <tt/message: / antes de la cadena que le pase.
+
+<tscreen><verb>
+void g_print( gchar *format, ... );
+</verb></tscreen>
+
+Reemplazo de <tt/printf()/.
+
+Y nuestra última función:
+
+<tscreen><verb>
+gchar *g_strsignal( gint signum );
+</verb></tscreen>
+
+Imprime el nombre de la señal del sistema Unix que corresponde con el
+número <tt/signum/. Útil para las funciones genéricas de manejo de señal.
+
+Todo lo anterior está más o menos robado de <tt/glib.h/. Si alguien
+quiere documentar una función, ¡sólo tiene que enviarme un correo-e!
+
+<!-- ***************************************************************** -->
+<sect>Ficheros rc de GTK
+<!-- ***************************************************************** -->
+<p>
+GTK tiene su propia forma de conseguir los valores por defecto de una
+aplicación, y es utilizando los ficheros <tt/rc/. Pueden ser
+utilizados para poner los colores de cualquier <em/widget/, y también
+pueden utilizarse para poner imágenes como fondos de algunos <em/widgets/.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Funciones para los ficheros <tt/rc/
+<p>
+Cuando empiece su aplicación, debería incluir una llamada a:
+
+<tscreen><verb>
+void gtk_rc_parse( char *filename );
+</verb></tscreen>
+
+Poniendo el nombre del fichero de su rc. Esto hará que GTK analice
+este fichero, y utilice el estilo para los <em/widgets/ que se definan
+ahí.
+
+Si desea tener un conjunto especial de <em/widgets/ con un estilo
+diferente de los otros, o realizar cualquier otra división lógica de
+los <em/widgets/, haga una llamada a:
+
+<tscreen><verb>
+void gtk_widget_set_name( GtkWidget *widget,
+ gchar *name );
+</verb></tscreen>
+
+Pasándole su nuevo <em/widget/ como primer argumento, y el nombre que
+desea darle como el segundo. Mediante este nombre podrá cambiar los
+atributos de ese <em/widget/.
+
+Si hacemos algo así:
+
+<tscreen><verb>
+boton = gtk_button_new_with_label ("Botón especial");
+gtk_widget_set_name (boton, "botón especial");
+</verb></tscreen>
+
+El botón tendrá el nombre «botón especial» y podría hacersele
+referencia en el fichero <tt/rc/ como «botón especial.GtkButton».
+[<--- ¡Verificadme! ]
+
+El fichero de ejemplo <tt/rc/ que mostramos a continuación, establece las
+propiedades de la ventana principal, y deja que todos los hijos de la
+ventana principal hereden el estilo descrito por «main button». El
+código utilizado en la aplicación es:
+
+<tscreen><verb>
+ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+gtk_widget_set_name (ventana, "main window");
+</verb></tscreen>
+
+Y el estilo se define en el fichero <tt/rc/ utilizando:
+
+<tscreen><verb>
+widget "main window.*GtkButton*" style "main_button"
+</verb></tscreen>
+
+Qué hace que todos los <em/widgets/ GtkButton de la «main window»
+(ventana principal) tengan el estilo "main_buttons" tal y como se
+define en el fichero <tt/rc/.
+
+Como puede ver, es un sistema muy poderoso y flexible. Utilice su
+imaginación para aprovecharse al máximo de este sistema.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Formato de los ficheros <tt/rc/ de GTK
+<p>
+El formato de los ficheros GTK se muestra en el ejemplo de más
+abajo. Éste es el fichero <tt/testgtkrc/ de la distribución GTK, pero he
+añadido unos cuantos comentarios y alguna cosilla. Puede que quiera
+incluir esta explicación en su aplicación para permitir al usuario
+personalizar su aplicación.
+
+Hay varias directivas para cambiar los atributos de un <em/widget/.
+
+<itemize>
+<item>fg - Establece el color de primer plano de un <em/widget/.
+<item>bg - Establece el color de fondo de un <em/widget/.
+<item>bg_pixmap - Establece la imagen que servirá de fondo al
+<em/widget/ (como mosaico).
+<item>font - Establece el tipo de letra que se utilizará con el
+<em/widget/.
+</itemize>
+
+Además de esto, hay varios estados en el que puede estar un
+<em/widget/, y puede especificar diferentes colores, imágenes y tipos
+de letra para cada estado. Estos estados son:
+
+<itemize>
+<item>NORMAL - El estado normal de un <em/widget/, sin el ratón sobre
+él, y no siendo presionado, etc...
+<item>PRELIGHT - Cuando el ratón esté sobre este <em/widget/ se
+utilizarán los colores definidos para este estado.
+<item>ACTIVE - Cuando se presiona o se pulsa sobre el <em/widget/,
+estará activo, y los atributos asignados por está etiqueta serán
+utilizados.
+<item>INSENSITIVE - Cuando un <em/widget/ es insensible, y no se puede
+activar, tomará estos atributos.
+<item>SELECTED - Cuando se seleccione un objeto, tomará estos atributos.
+</itemize>
+
+Cuando se utilizan las directivas «fg» y «bg» para poner los colores de
+los <em/widgets/, se utilizará el formato siguiente:
+
+<tscreen><verb>
+fg[<STATE>] = { Red, Green, Blue }
+</verb></tscreen>
+
+Donde <tt/STATE/ es uno de los estados anteriores (PRELIGHT, ACTIVE,
+etc...), y el <tt/Red/, <tt/Green/ y <tt/Blue/ (Rojo, Verde y Azul)
+son valores en el rango 0 - 1.0, { 1.0, 1.0, 1.0 } es blanco. Deben
+estar en formato flotante, o serán un 0, por lo que "1" no funcionará,
+debe ser "1.0". Un "0" está bien ya que es lo mismo si no se
+reconoce. Los valores no reconocidos se pondrán a 0.
+
+<tt/bg_pixmap/ es muy similar al de arriba, salvo que los colores se
+reemplazan por un nombre de fichero.
+
+<tt/pixmap_path/ es una lista de los caminos (<em/paths/) separados por
+«:». Estos caminos se utilizarán para buscar cualquier imagen que
+indique.
+
+La directiva sobre el tipo de letra es simplemente:
+<tscreen><verb>
+font = "<nombre del tipo de letra>"
+</verb></tscreen>
+
+Donde lo único difícil es saber la cadena del tipo de letra a
+elegir. Utilizar <tt/xfontsel/ o un programa similar debería ayudar.
+
+El <tt/widget_class/ establece el estilo de una clase de
+<em/widgets/. Estas clases se muestran en el resumen de <em/widgets/
+dentro de la jerarquía de clases.
+
+La directiva <tt/widget/ hace que un conjunto específico de
+<em/widgets/ tenga un estido determinado, sobreescribiendo cualquier
+estilo anterior que tuviese esa clase de <em/widgets/. Estos
+<em/widgets/ se registran dentro de la aplicación utilizando una
+llamada a <tt/gtk_widget_set_name()/. Esto le permitirá especificar
+los atributos de un <em/widget/ uno a uno, en vez de establecer los
+atributos de toda una clase <em/widget/. Deberá documentar cualquiera
+de estos <em/widgets/ especiales para que los usuarios puedan
+personalizarlos.
+
+Cuando la palabra clave <tt/parent/ se utiliza como un atributo, el
+<em/widget/ tomará los atributos de su padre en la aplicación.
+
+Puede asignar los atributos de un estilo previamente definido a uno
+nuevo.
+
+<tscreen><verb>
+style "main_button" = "button"
+{
+ font = "-adobe-helvetica-medium-r-normal--*-100-*-*-*-*-*-*"
+ bg[PRELIGHT] = { 0.75, 0, 0 }
+}
+</verb></tscreen>
+
+Este ejemplo toma el estilo «button», y crea un nuevo estilo
+«main_button» cambiando simplemente el tipo de letra y cambiando el
+color de fondo cuando el <em/widget/ esté en estado <tt/PRELIGHT/.
+
+Por supuesto, muchos de estos atributos no se aplican a todos los
+<em/widgets/. Realmente es una cuestión de sentido común. Se utilizará
+cualquier atributo que se pueda aplicar.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Fichero <tt/rc/ de ejemplo
+<p>
+
+<!-- Esto hay que traducirlo -->
+<tscreen><verb>
+# pixmap_path "<dir 1>:<dir 2>:<dir 3>:..."
+#
+pixmap_path "/usr/include/X11R6/pixmaps:/home/imain/pixmaps"
+#
+# style <name> [= <name>]
+# {
+# <option>
+# }
+#
+# widget <widget_set> style <style_name>
+# widget_class <widget_class_set> style <style_name>
+
+
+# Here is a list of all the possible states. Note that some do not apply to
+# certain widgets.
+#
+# NORMAL - The normal state of a widget, without the mouse over top of
+# it, and not being pressed etc.
+#
+# PRELIGHT - When the mouse is over top of the widget, colors defined
+# using this state will be in effect.
+#
+# ACTIVE - When the widget is pressed or clicked it will be active, and
+# the attributes assigned by this tag will be in effect.
+#
+# INSENSITIVE - When a widget is set insensitive, and cannot be
+# activated, it will take these attributes.
+#
+# SELECTED - When an object is selected, it takes these attributes.
+#
+# Given these states, we can set the attributes of the widgets in each of
+# these states using the following directives.
+#
+# fg - Sets the foreground color of a widget.
+# fg - Sets the background color of a widget.
+# bg_pixmap - Sets the background of a widget to a tiled pixmap.
+# font - Sets the font to be used with the given widget.
+#
+
+# This sets a style called "button". The name is not really important, as
+# it is assigned to the actual widgets at the bottom of the file.
+
+style "window"
+{
+ #This sets the padding around the window to the pixmap specified.
+ #bg_pixmap[<STATE>] = "<pixmap filename>"
+ bg_pixmap[NORMAL] = "warning.xpm"
+}
+
+style "scale"
+{
+ #Sets the foreground color (font color) to red when in the "NORMAL"
+ #state.
+
+ fg[NORMAL] = { 1.0, 0, 0 }
+
+ #Sets the background pixmap of this widget to that of its parent.
+ bg_pixmap[NORMAL] = "<parent>"
+}
+
+style "button"
+{
+ # This shows all the possible states for a button. The only one that
+ # doesn't apply is the SELECTED state.
+
+ fg[PRELIGHT] = { 0, 1.0, 1.0 }
+ bg[PRELIGHT] = { 0, 0, 1.0 }
+ bg[ACTIVE] = { 1.0, 0, 0 }
+ fg[ACTIVE] = { 0, 1.0, 0 }
+ bg[NORMAL] = { 1.0, 1.0, 0 }
+ fg[NORMAL] = { .99, 0, .99 }
+ bg[INSENSITIVE] = { 1.0, 1.0, 1.0 }
+ fg[INSENSITIVE] = { 1.0, 0, 1.0 }
+}
+
+# In this example, we inherit the attributes of the "button" style and then
+# override the font and background color when prelit to create a new
+# "main_button" style.
+
+style "main_button" = "button"
+{
+ font = "-adobe-helvetica-medium-r-normal--*-100-*-*-*-*-*-*"
+ bg[PRELIGHT] = { 0.75, 0, 0 }
+}
+
+style "toggle_button" = "button"
+{
+ fg[NORMAL] = { 1.0, 0, 0 }
+ fg[ACTIVE] = { 1.0, 0, 0 }
+
+ # This sets the background pixmap of the toggle_button to that of its
+ # parent widget (as defined in the application).
+ bg_pixmap[NORMAL] = "<parent>"
+}
+
+style "text"
+{
+ bg_pixmap[NORMAL] = "marble.xpm"
+ fg[NORMAL] = { 1.0, 1.0, 1.0 }
+}
+
+style "ruler"
+{
+ font = "-adobe-helvetica-medium-r-normal--*-80-*-*-*-*-*-*"
+}
+
+# pixmap_path "~/.pixmaps"
+
+# These set the widget types to use the styles defined above.
+# The widget types are listed in the class hierarchy, but could probably be
+# just listed in this document for the users reference.
+
+widget_class "GtkWindow" style "window"
+widget_class "GtkDialog" style "window"
+widget_class "GtkFileSelection" style "window"
+widget_class "*Gtk*Scale" style "scale"
+widget_class "*GtkCheckButton*" style "toggle_button"
+widget_class "*GtkRadioButton*" style "toggle_button"
+widget_class "*GtkButton*" style "button"
+widget_class "*Ruler" style "ruler"
+widget_class "*GtkText" style "text"
+
+# This sets all the buttons that are children of the "main window" to
+# the main_button style. These must be documented to be taken advantage of.
+widget "main window.*GtkButton*" style "main_button"
+</verb></tscreen>
+
+<!-- ***************************************************************** -->
+<sect>Escribiendo sus propios <em/widgets/
+<!-- ***************************************************************** -->
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Visión general
+<p>
+Aunque la distribución de GTK viene con muchos tipos de <em/widgets/
+que debería cubrir todas la mayoría de las necesidades básicas, puede
+que haya llegado el momento en que necesite crear su propio
+<em/widget/. Debido a que GTK utiliza mucho la herencia de
+<em/widgets/, y si ya hay un <em/widget/ que se acerque lo suficiente
+a lo que quiere, tal vez pueda hacer un nuevo <em/widget/ con tan solo
+unas cuantas líneas de código. Pero antes de empezar a trabajar en un
+nuevo <em/widget/, asegúrese primero de que no hay nadie que ya haya
+hecho otro parecido. Así evitará la duplicación de esfuerzo y
+mantendrá el número de <em/widgets/ GTK en su valor mínimo, lo que
+ayudará a que el código y la interfaz de las diferentes aplicaciones
+sea consistente. Por otra parte, cuando haya acabado su <em/widget/,
+anúncielo al mundo entreo para que todo el mundo se pueda
+beneficiar. Probablemente el mejor lugar para hacerlo sea la
+<tt>gtk-list</tt>.
+
+Las fuentes completas de los <em/widgets/ de ejemplo están disponibles
+en el mismo lugar en el que consiguió este tutorial, o en:
+
+<htmlurl url="http://www.gtk.org/~otaylor/gtk/tutorial/"
+name="http://www.gtk.org/~otaylor/gtk/tutorial/">
+
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> La anatomía de un <em/widget/
+<p>
+Para crear un nuevo <em/widget/, es importante conocer como funcionan
+los objetos de GTK. Esta sección es sólo un breve resumen. Ver la
+documentación a la que se hace referencia para obtener más detalles.
+
+Los widgets GTK están implementados siguiendo una orientación a
+objetos. Sin embargo, están implementados en C estándar. De esta forma
+se mejora enormemente la portabilidad y la estabilidad con respecto a
+la actual generación de compiladores C++; sin embargo, con todo esto
+no queremos decir que el creador de <em/widgets/ tenga que prestar
+atención a ninguno de los detalles de implementación. La información
+que es común a todos los <em/widgets/ de una clase de <em/widgets/
+(p.e., a todos los <em/widgets/ botón) se almacena en la
+<em>estructura de clase</em>. Sólo hay una copia de ésta en la que se
+almacena información sobre las señales de la clase (que actuan como
+funciones virtuales en C). Para permitir la herencia, el primer campo
+en la estructura de la clase debe ser una copia de la estructura de la
+clase del padre. La declaración de la estructura de la clase de
+GtkButton debe ser algo así:
+
+<tscreen><verb>
+struct _GtkButtonClass
+{
+ GtkContainerClass parent_class;
+
+ void (* pressed) (GtkButton *button);
+ void (* released) (GtkButton *button);
+ void (* clicked) (GtkButton *button);
+ void (* enter) (GtkButton *button);
+ void (* leave) (GtkButton *button);
+};
+</verb></tscreen>
+
+Cuando un botón se trata como un contenedor (por ejemplo, cuando se le
+cambia el tamaño), su estructura de clase puede convertirse a
+GtkContainerClass, y los campos relevantes se utilizarán para manejar
+las señales.
+
+También hay una estructura que se crea para cada <em/widget/. Esta
+estructura tiene campos para almacenar la información que es diferente
+para cada copia del <em/widget/. Nosotros llamaremos a esta estructura
+la <em>estructura objeto</em>. Para la clase botón, es así:
+
+<tscreen><verb>
+struct _GtkButton
+{
+ GtkContainer container;
+
+ GtkWidget *child;
+
+ guint in_button : 1;
+ guint button_down : 1;
+};
+</verb></tscreen>
+
+Observe que, como en la estructura de clase, el primer campo es la
+estructura objeto de la clase padre, por lo que esta estructura puede
+convertirse en la estructura de la clase del objeto padre cuando haga
+falta.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Creando un <em/widget/ compuesto
+
+<!-- ----------------------------------------------------------------- -->
+<sect2> Introducción
+<p>
+Un tipo de widget que puede interesarnos es uno que sea un mero
+agregado de otros <em/widgets/ GTK. Este tipo de <em/widget/ no hace
+nada que no pueda hacerse sin la necesidad de crear un nuevo
+<em/widget/, pero proporciona una forma conveniente de empaquetar los
+elementos del interfaz de usuario para su reutilización. Los
+<em/widgets/ <tt/FileSelection/ y <tt/ColorSelection/ incluidos en la
+distribución estándar son ejemplos de este tipo de <em/widgets/.
+
+El <em/widget/ ejemplo que hemos creado en esta sección es el
+<em/widget/ Tictactoe, una matriz de 3x3 de botones de selección que
+lanza una señal cuando están deseleccionados tres botones en una misma
+fila, columna, o diagonal.
+
+<!-- ----------------------------------------------------------------- -->
+<sect2> Escogiendo una clase padre
+<p>
+Normalmente la clase padre para un <em/widget/ compuesto es la clase
+contenedor que tenga todos los elementos del <em/widget/
+compuesto. Por ejemplo, la clase padre del <em/widget/
+<tt/FileSelection/ es la clase <tt/Dialog/. Ya que nuestros botones se
+ordenarán en una tabla, parece natural hacer que nuestra clase padre
+sea la clase <tt/GtkTable/. Desafortunadamente, esto no
+funcionaría. La creación de un <em/widget/ se divide en dos funciones
+- una función <tt/NOMBREWIDGET_new()/ que utilizará el usuario, y una
+función <tt/NOMBREWIDGET_init()/ que hará el trabajo básico de
+inicializar el <em/widget/ que es independiente de los argumentos que
+se le pasen a la función <tt/_new()/. Los <em/widgets/ derivados sólo
+llaman a la función <tt/_init/ de su <em/widget/ padre. Pero esta
+división del trabajo no funciona bien con las tablas, que necesitan
+saber en el momento de su creación el número de filas y de columnas
+que deben tener. A menos que queramos duplicar la mayor parte de lo
+hecho en <tt/gtk_table_new()/ en nuestro <em/widget/ Tictactoe,
+haremos mejor si evitamos derivar de GtkTable. Por esta razón,
+derivaremos de <tt/GtkVBox/, y meteremos nuestra tabla dentro de la
+caja vertical.
+
+<!-- ----------------------------------------------------------------- -->
+<sect2> El fichero de cabecera
+<p>
+Cada clase <em/widget/ tiene un fichero de cabecera que declara el
+objeto y las estructuras de clase para ese <em/widget/, así como las
+funciones públicas. Un par de características que merecen dejarse
+aparte. Para evitar la duplicación de definiciones, meteremos el
+fichero de cabecera al completo entre:
+
+<tscreen><verb>
+#ifndef __TICTACTOE_H__
+#define __TICTACTOE_H__
+.
+.
+.
+#endif /* __TICTACTOE_H__ */
+</verb></tscreen>
+
+Y para que los programas en C++ incluyan sin problemas el fichero de
+cabecera, pondremos:
+
+<tscreen><verb>
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+.
+.
+.
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+</verb></tscreen>
+
+Con las funciones y las estructuras, declararemos tres macros estándar
+en nuestro fichero de cabecera, <tt/TICTACTOE(obj)/,
+<tt/TICTACTOE_CLASS(class)/, y <tt/IS_TICTACTOE(obj)/, que,
+convierten, respectivamente, un puntero en un puntero al objeto o a la
+estructura de la clase, y comprueba si un objeto es un <em/widget/
+Tictactoe.
+
+Aquí está el fichero de cabecera al completo:
+
+<tscreen><verb>
+/* tictactoe.h */
+
+#ifndef __TICTACTOE_H__
+#define __TICTACTOE_H__
+
+#include <gdk/gdk.h>
+#include <gtk/gtkvbox.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define TICTACTOE(obj) GTK_CHECK_CAST (obj, tictactoe_get_type (), Tictactoe)
+#define TICTACTOE_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, tictactoe_get_type (), TictactoeClass)
+#define IS_TICTACTOE(obj) GTK_CHECK_TYPE (obj, tictactoe_get_type ())
+
+
+typedef struct _Tictactoe Tictactoe;
+typedef struct _TictactoeClass TictactoeClass;
+
+struct _Tictactoe
+{
+ GtkVBox vbox;
+
+ GtkWidget *botones[3][3];
+};
+
+struct _TictactoeClass
+{
+ GtkVBoxClass parent_class;
+
+ void (* tictactoe) (Tictactoe *ttt);
+};
+
+guint tictactoe_get_type (void);
+GtkWidget* tictactoe_new (void);
+void tictactoe_clear (Tictactoe *ttt);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __TICTACTOE_H__ */
+
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect2> La función <tt/_get_type()/.
+<p>
+Ahora continuaremos con la implementación de nuestro <em/widget/. Una
+función del núcleo de todo <em/widget/ es
+<tt/NOMBREWIDGET_get_type()/. Cuando se llame a esta función por
+vez primera, le informará a GTK sobre la clase del <em/widget/, y
+devolverá un ID que identificará unívocamente la clase <em/widget/. En
+las llamadas siguientes, lo único que hará será devolver el ID.
+
+<tscreen><verb>
+guint
+tictactoe_get_type ()
+{
+ static guint ttt_type = 0;
+
+ if (!ttt_type)
+ {
+ GtkTypeInfo ttt_info =
+ {
+ "Tictactoe",
+ sizeof (Tictactoe),
+ sizeof (TictactoeClass),
+ (GtkClassInitFunc) tictactoe_class_init,
+ (GtkObjectInitFunc) tictactoe_init,
+ (GtkArgSetFunc) NULL,
+ (GtkArgGetFunc) NULL
+ };
+
+ ttt_type = gtk_type_unique (gtk_vbox_get_type (), &amp;ttt_info);
+ }
+
+ return ttt_type;
+}
+</verb></tscreen>
+
+La estructura GtkTypeInfo tiene la definición siguiente:
+
+<tscreen><verb>
+struct _GtkTypeInfo
+{
+ gchar *type_name;
+ guint object_size;
+ guint class_size;
+ GtkClassInitFunc class_init_func;
+ GtkObjectInitFunc object_init_func;
+ GtkArgSetFunc arg_set_func;
+ GtkArgGetFunc arg_get_func;
+};
+</verb></tscreen>
+
+Los utilidad de cada campo de esta estructura se explica por su propio
+nombre. Ignoraremos por ahora los campos <tt/arg_set_func/
+y <tt/arg_get_func/: son importantes, pero todavía es raro
+utilizarlos, su papel es permitir que las opciones de los <em/wdigets/
+puedan establecerse correctamente mediante lenguajes
+interpretados. Una vez que GTK tiene una copia de esta estructura
+correctamente rellenada, sabrá como crear objetos de un tipo
+particular de <em/widget/.
+
+<!-- ----------------------------------------------------------------- -->
+<sect2> La función <tt/_class_init()/
+<p>
+La función <tt/NOMBREWIDGET_class_init()/ inicializa los campos de la
+estructura clase del <em/widget/, y establece las señales de la
+clase. Para nuestro <em/widget/ Tictactoe será una cosa así:
+
+<tscreen><verb>
+
+enum {
+ TICTACTOE_SIGNAL,
+ LAST_SIGNAL
+};
+
+static gint tictactoe_signals[LAST_SIGNAL] = { 0 };
+
+static void
+tictactoe_class_init (TictactoeClass *class)
+{
+ GtkObjectClass *object_class;
+
+ object_class = (GtkObjectClass*) class;
+
+ tictactoe_signals[TICTACTOE_SIGNAL] = gtk_signal_new ("tictactoe",
+ GTK_RUN_FIRST,
+ object_class->type,
+ GTK_SIGNAL_OFFSET (TictactoeClass, tictactoe),
+ gtk_signal_default_marshaller, GTK_TYPE_NONE, 0);
+
+
+ gtk_object_class_add_signals (object_class, tictactoe_signals, LAST_SIGNAL);
+
+ class->tictactoe = NULL;
+}
+</verb></tscreen>
+
+Nuestro <em/widget/ sólo tiene una señal, la señal <tt/tictactoe/ que
+se invoca cuando una fila, columna, o diagonal se rellena
+completamente. No todos los <em/widgets/ compuestos necesitan señales,
+por lo que si está leyendo esto por primera vez, puede que sea mejor
+que pase a la sección siguiente, ya que las cosas van a complicarse un
+poco.
+
+La función:
+
+<tscreen><verb>
+gint gtk_signal_new( const gchar *name,
+ GtkSignalRunType run_type,
+ GtkType object_type,
+ gint function_offset,
+ GtkSignalMarshaller marshaller,
+ GtkType return_val,
+ guint nparams,
+ ...);
+</verb></tscreen>
+
+crea una nueva señal. Los parámetros son:
+
+<itemize>
+<item> <tt/name/: El nombre de la señal.
+<item> <tt/run_type/: Si el manejador por defecto se ejecuta antes o
+despues del manejador de usuario. Normalmente debe ser
+<tt/GTK_RUN_FIRST/, o <tt/GTK_RUN_LAST/, aunque hay otras
+posibilidades.
+<item> <tt/object_type/: El ID del objeto al que se le aplica esta
+señal. (También se aplicará a los descendientes de los objetos)
+<item> <tt/function_offset/: El desplazamiento en la estructura de la
+clase de un puntero al manejador por defecto.
+<item> <tt/marshaller/: Una función que se utiliza para invocar al
+manejador de señal. Para los manejadores de señal que no tengan más
+argumentos que el objeto que emitió la señal podemos utilizar la
+función marshaller por defecto <tt/gtk_signal_default_marshaller/.
+<item> <tt/return_val/: El tipo del valor devuelto.
+<item> <tt/nparams/: El número de parámetros del manejador de señal
+(distintos de los dos por defecto que hemos mencionado arriba).
+<item> <tt/.../: Los tipos de los parámetros.
+</itemize>
+
+Cuando se especifican los tipos, se utilizará la enumeración
+<tt/GtkType/:
+
+<tscreen><verb>
+typedef enum
+{
+ GTK_TYPE_INVALID,
+ GTK_TYPE_NONE,
+ GTK_TYPE_CHAR,
+ GTK_TYPE_BOOL,
+ GTK_TYPE_INT,
+ GTK_TYPE_UINT,
+ GTK_TYPE_LONG,
+ GTK_TYPE_ULONG,
+ GTK_TYPE_FLOAT,
+ GTK_TYPE_DOUBLE,
+ GTK_TYPE_STRING,
+ GTK_TYPE_ENUM,
+ GTK_TYPE_FLAGS,
+ GTK_TYPE_BOXED,
+ GTK_TYPE_FOREIGN,
+ GTK_TYPE_CALLBACK,
+ GTK_TYPE_ARGS,
+
+ GTK_TYPE_POINTER,
+
+ /* it'd be great if the next two could be removed eventually */
+ GTK_TYPE_SIGNAL,
+ GTK_TYPE_C_CALLBACK,
+
+ GTK_TYPE_OBJECT
+
+} GtkFundamentalType;
+</verb></tscreen>
+
+<tt/gtk_signal_new()/ devuelve un identificador entero único para la
+señal, que almacenamos en el vector <tt/tictactoe_signals/, que
+indexaremos utilizando una enumeración. (Convencionalmente, los
+elementos de la enumeración son el nombre de la señal, en mayúsculas,
+pero aquí tendríamos un conflicto con la macro <tt/TICTACTOE()/, por
+lo que lo llamaremos <tt/TICTACTOE_SIGNAL/.
+
+Después de crear nuestras señales, necesitamos llamar a GTK para
+asociarlas con la clase Tictactoe. Hacemos esto llamando a
+<tt/gtk_object_class_add_signals()/. Entonces haremos que el puntero
+que apunta al manejador por defecto para la señal `tictactoe' sea NULL,
+indicando que no hay ninguna acción por defecto.
+
+<!-- ----------------------------------------------------------------- -->
+<sect2> La función <tt/_init()/.
+<p>
+Cada clase <em/widget/ también necesita una función para inicializar
+la estructura del objeto. Normalmente, esta función tiene el limitado
+rol de poner los distintos campos de la estructura a su valor por
+defecto. Sin embargo para los <em/widgets/ de composición, esta
+función también crea los distintos <em/widgets/ componentes.
+
+<tscreen><verb>
+static void
+tictactoe_init (Tictactoe *ttt)
+{
+ GtkWidget *table;
+ gint i,j;
+
+ table = gtk_table_new (3, 3, TRUE);
+ gtk_container_add (GTK_CONTAINER(ttt), table);
+ gtk_widget_show (table);
+
+ for (i=0;i<3; i++)
+ for (j=0;j<3; j++)
+ {
+ ttt->buttons[i][j] = gtk_toggle_button_new ();
+ gtk_table_attach_defaults (GTK_TABLE(table), ttt->buttons[i][j],
+ i, i+1, j, j+1);
+ gtk_signal_connect (GTK_OBJECT (ttt->buttons[i][j]), "toggled",
+ GTK_SIGNAL_FUNC (tictactoe_toggle), ttt);
+ gtk_widget_set_usize (ttt->buttons[i][j], 20, 20);
+ gtk_widget_show (ttt->buttons[i][j]);
+ }
+}
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect2> Y el resto...
+<p>
+Hay una función más que cada <em/widget/ (excepto los <em/widget/ muy
+básicos como GtkBin que no pueden crear objetos) tiene que
+tener - la función que el usuario llama para crear un objeto de ese
+tipo. Normalmente se llama <tt/NOMBREWIDGET_new()/. En algunos
+<em/widgets/, que no es el caso del <em/widget/ Tictactoe, esta
+función toma argumentos, y hace alguna inicialización en función de
+estos. Las otras dos funciones son específicas al <em/widget/
+Tictactoe.
+
+<tt/tictactoe_clear()/ es una función pública que reinicia todos los
+botones en el <em/widget/ a la posición alta. Observe la utilización
+de <tt/gtk_signal_handler_block_by_data()/ para hacer que no se
+ejecute nuestro manejador de señal innecesariamente por cambios en los
+botones.
+
+<tt/tictactoe_toggle()/ es el manejador de señal que se invoca cuando
+el usuario pulsa un botón. Hace una comprobación para ver si hay
+alguna combinación ganadora, y si la hay, emite la señal
+«tictactoe».
+
+<tscreen><verb>
+GtkWidget*
+tictactoe_new ()
+{
+ return GTK_WIDGET ( gtk_type_new (tictactoe_get_type ()));
+}
+
+void
+tictactoe_clear (Tictactoe *ttt)
+{
+ int i,j;
+
+ for (i=0;i<3;i++)
+ for (j=0;j<3;j++)
+ {
+ gtk_signal_handler_block_by_data (GTK_OBJECT(ttt->buttons[i][j]), ttt);
+ gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (ttt->buttons[i][j]),
+ FALSE);
+ gtk_signal_handler_unblock_by_data (GTK_OBJECT(ttt->buttons[i][j]), ttt);
+ }
+}
+
+static void
+tictactoe_toggle (GtkWidget *widget, Tictactoe *ttt)
+{
+ int i,k;
+
+ static int rwins[8][3] = { { 0, 0, 0 }, { 1, 1, 1 }, { 2, 2, 2 },
+ { 0, 1, 2 }, { 0, 1, 2 }, { 0, 1, 2 },
+ { 0, 1, 2 }, { 0, 1, 2 } };
+ static int cwins[8][3] = { { 0, 1, 2 }, { 0, 1, 2 }, { 0, 1, 2 },
+ { 0, 0, 0 }, { 1, 1, 1 }, { 2, 2, 2 },
+ { 0, 1, 2 }, { 2, 1, 0 } };
+
+ int success, found;
+
+ for (k=0; k<8; k++)
+ {
+ success = TRUE;
+ found = FALSE;
+
+ for (i=0;i<3;i++)
+ {
+ success = success &amp;&amp;
+ GTK_TOGGLE_BUTTON(ttt->buttons[rwins[k][i]][cwins[k][i]])->active;
+ found = found ||
+ ttt->buttons[rwins[k][i]][cwins[k][i]] == widget;
+ }
+
+ if (success &amp;&amp; found)
+ {
+ gtk_signal_emit (GTK_OBJECT (ttt),
+ tictactoe_signals[TICTACTOE_SIGNAL]);
+ break;
+ }
+ }
+}
+</verb></tscreen>
+
+Y finalmente, un programa ejemplo que utiliza nuestro <em/widget/
+Tictactoe:
+
+<tscreen><verb>
+#include <gtk/gtk.h>
+#include "tictactoe.h"
+
+/* Invocado cuando se completa una fila, columna o diagonal */
+void
+win (GtkWidget *widget, gpointer data)
+{
+ g_print ("Yay!\n");
+ tictactoe_clear (TICTACTOE (widget));
+}
+
+int
+main (int argc, char *argv[])
+{
+ GtkWidget *ventana;
+ GtkWidget *ttt;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ gtk_window_set_title (GTK_WINDOW (ventana), "Aspect Frame");
+
+ gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
+ GTK_SIGNAL_FUNC (gtk_exit), NULL);
+
+ gtk_container_border_width (GTK_CONTAINER (ventana), 10);
+
+ /* Create a new Tictactoe widget */
+ ttt = tictactoe_new ();
+ gtk_container_add (GTK_CONTAINER (ventana), ttt);
+ gtk_widget_show (ttt);
+
+ /* And attach to its "tictactoe" signal */
+ gtk_signal_connect (GTK_OBJECT (ttt), "tictactoe",
+ GTK_SIGNAL_FUNC (win), NULL);
+
+ gtk_widget_show (ventana);
+
+ gtk_main ();
+
+ return 0;
+}
+
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Creando un <em/widget/ desde cero.
+
+<!-- ----------------------------------------------------------------- -->
+<sect2> Introducción
+<p>
+En esta sección, averiguaremos como se dibujan los <em/widgets/ a sí
+mismos en pantalla y como interactuan con los eventos. Como ejemplo,
+crearemos un marcador analógico con un puntero que el usuario
+podrá arrastrar para hacer que el marcador tenga un valor dado.
+
+<!-- ----------------------------------------------------------------- -->
+<sect2> Mostrando un <em/widget/ en la pantalla
+<p>
+Hay varios pasos que están involucrados en el dibujado en pantalla.
+Después de que el <em/widget/ se cree con una llamada a
+<tt/NOMBREWIDGET_new()/, se necesitarán muchas más funciones:
+
+<itemize>
+<item> <tt/NOMBREWIDGET_realize()/ es la responsable de crear una
+ventana X para el <em/widget/, si tiene alguna.
+<item> <tt/NOMBREWIDGET_map()/ se invoca después de las llamadas del
+usuario
+<tt/gtk_widget_show()/. Es la responsable de asegurarse de que el
+<em/widget/ está dibujado (<em/mapeado/) en la pantalla. Para una
+clase contenedor, también deberá ocuparse de llamar a las funciones
+<tt/map()/ de cada <em/widget/ hijo.
+<item> <tt/NOMBREWIDGET_draw()/ se invoca cuando se llama a
+<tt/gtk_widget_draw()/ desde el <em/widget/ de uno de sus
+antepasados. Hace las llamadas necesarias a las funciones de dibujo
+para dibujar el <em/widget/ en la pantalla. Para los <em/widgets/
+contenedores, esta función debe llamar a las <tt/gtk_widget_draw/ de
+sus <em/widgets/ hijos.
+<item> <tt/NOMBREWIDGET_expose()/ es un manejador de los eventos
+<tt/expose/ del <em/widget/. Hace las llamadas necesarias a las
+funciones de dibujo para dibujar la parte expuesta en la
+pantalla. Para los <em/widgets/ contenedores, esta función debe
+generar los eventos <tt/expose/ de sus <em/widgets/ hijos que no
+tengan su propia ventana. (Si tuviesen su propia ventana, X generaría
+los eventos <tt/expose/ necesarios)
+</itemize>
+
+Las últimas dos funciones son bastante similares - ambas son
+responsables de dibujar el <em/widget/ en pantalla. De hecho en muchos
+<em/widgets/ realmente no importa la diferencia que hay entre ambas
+funciones. La función <em/draw()/ que hay por defecto en
+la clase <em/widget/ simplemente genera un evento <tt/expose/
+artificial de la zona a redibujar. Sin embargo, algunos tipos de
+<em/widgets/ puede ahorrarse trabajo distinguiendo entre las dos
+funciones. Por ejemplo, si un <em/widget/ tiene varias ventanas X,
+entonces, como los eventos <tt/expose/ identifican a la ventana
+expuesta, podrán redibujar sólo la ventana afectada, lo que no es
+posible con llamadas a <tt/draw()/.
+
+Los <em/widgets/ contenedores, aunque no utilicen la diferecia
+existente entre las dos funciones por sí mismos, no pueden utilizar
+simplemente las funciones <tt/draw()/ que hay por defecto ya que sus
+<em/widgets/ hijos puede que tengan que utilizar la diferencia. Sin
+embargo, sería un derroche duplicar el código de dibujado entre las
+dos funciones. Lo normal es que cada <em/widget/ tenga una función
+llamada <tt/NOMBREWIDGET_paint()/ que haga el trabajo de dibujar el
+<em/widget/, ésta función será a la que se llame por las funciones
+<tt/draw()/ y <tt/expose()/.
+
+En nuestro ejemplo, como el <em/widget/ Dial no es un <em/widget/
+contenedor, y sólo tiene una ventana, podemos tomar el camino más
+corto, utilizar la función <tt/draw()/ por defecto y sólo
+implementar la función <tt/expose()/.
+
+<!-- ----------------------------------------------------------------- -->
+<sect2> Los orígenes del <em/widget/ Dial
+<p>
+Así como todos los animales terrestes son variaciones del primer
+anfíbio que salió del barro, los <em/widgets/ Gtk tienden a nacer
+como variaciones de algún otro <em/widget/ escrito previamente. Por
+tanto, aunque esta sección se titule `Creando un <em/widget/ de la
+nada', el <em/widget/ Dial empieza realmente con el código fuente
+del <em/widget/ Range. He tomado éste como punto de arranque porque
+sería bonito que nuestro dial tuviese la misma interfaz que los
+<em/widgets/ Scale, que son sólo una especialización del <em/widget/
+Range. Por tanto, aunque el código fuente se presente más adelante en
+su forma final, no implica que fuese escrito de esta forma <em>deus ex
+machina</em>. Si todavía no está familiarizado, desde el punto de
+vista del escritor de aplicaciones, con la forma de funcionar de los
+<em/widgets/ Scale, sería una buena idea echarles un vistazo antes de
+continuar.
+
+<!-- ----------------------------------------------------------------- -->
+<sect2> Los comienzos
+<p>
+Nuestro <em/widget/ tiene un aspecto algo parecido al del <em/widget/
+Tictactoe. Primero, tenemos un fichero de cabecera:
+
+<tscreen><verb>
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __GTK_DIAL_H__
+#define __GTK_DIAL_H__
+
+#include <gdk/gdk.h>
+#include <gtk/gtkadjustment.h>
+#include <gtk/gtkwidget.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+#define GTK_DIAL(obj) GTK_CHECK_CAST (obj, gtk_dial_get_type (), GtkDial)
+#define GTK_DIAL_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, gtk_dial_get_type (), GtkDialClass)
+#define GTK_IS_DIAL(obj) GTK_CHECK_TYPE (obj, gtk_dial_get_type ())
+
+
+typedef struct _GtkDial GtkDial;
+typedef struct _GtkDialClass GtkDialClass;
+
+struct _GtkDial
+{
+ GtkWidget widget;
+
+ /* política de actualización
+ * (GTK_UPDATE_[CONTINUOUS/DELAYED/DISCONTINUOUS]) */
+ guint policy : 2;
+
+ /* Botón actualmente presionado o 0 si no hay ninguno */
+ guint8 boton;
+
+ /* Dimensión de los componendes del dial */
+ gint radius;
+ gint pointer_width;
+
+ /* ID del temporizador de actualización, o 0 si no hay ninguno */
+ guint32 timer;
+
+ /* ángulo actual */
+ gfloat angle;
+
+ /* Viejos valores almacenados del adjustment, para que así no
+ * tengamos que saber cuando cambia algo */
+ gfloat old_value;
+ gfloat old_lower;
+ gfloat old_upper;
+
+ /* El objeto adjustment que almacena los datos para este dial */
+ GtkAdjustment *adjustment;
+};
+
+struct _GtkDialClass
+{
+ GtkWidgetClass parent_class;
+};
+
+
+GtkWidget* gtk_dial_new (GtkAdjustment *adjustment);
+guint gtk_dial_get_type (void);
+GtkAdjustment* gtk_dial_get_adjustment (GtkDial *dial);
+void gtk_dial_set_update_policy (GtkDial *dial,
+ GtkUpdateType policy);
+
+void gtk_dial_set_adjustment (GtkDial *dial,
+ GtkAdjustment *adjustment);
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* __GTK_DIAL_H__ */
+</verb></tscreen>
+
+Como vamos a ir con este <em/widget/ un poco más lejos que con el
+último que creamos, ahora tenemos unos cuantos campos más en la
+estructura de datos, pero el resto de las cosas son muy parecidas.
+
+Ahora, después de incluir los ficheros de cabecera, y declarar unas
+cuantas constantes, tenemos algunas funciones que proporcionan
+información sobre el <em/widget/ y lo inicializan:
+
+<tscreen><verb>
+#include <math.h>
+#include <stdio.h>
+#include <gtk/gtkmain.h>
+#include <gtk/gtksignal.h>
+
+#include "gtkdial.h"
+
+#define SCROLL_DELAY_LENGTH 300
+#define DIAL_DEFAULT_SIZE 100
+
+/* Declaraciones de funciones */
+
+[ omitido para salvar espacio ]
+
+/* datos locales */
+
+static GtkWidgetClass *parent_class = NULL;
+
+guint
+gtk_dial_get_type ()
+{
+ static guint dial_type = 0;
+
+ if (!dial_type)
+ {
+ GtkTypeInfo dial_info =
+ {
+ "GtkDial",
+ sizeof (GtkDial),
+ sizeof (GtkDialClass),
+ (GtkClassInitFunc) gtk_dial_class_init,
+ (GtkObjectInitFunc) gtk_dial_init,
+ (GtkArgSetFunc) NULL,
+ (GtkArgGetFunc) NULL,
+ };
+
+ dial_type = gtk_type_unique (gtk_widget_get_type (), &amp;dial_info);
+ }
+
+ return dial_type;
+}
+
+static void
+gtk_dial_class_init (GtkDialClass *class)
+{
+ GtkObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+
+ object_class = (GtkObjectClass*) class;
+ widget_class = (GtkWidgetClass*) class;
+
+ parent_class = gtk_type_class (gtk_widget_get_type ());
+
+ object_class->destroy = gtk_dial_destroy;
+
+ widget_class->realize = gtk_dial_realize;
+ widget_class->expose_event = gtk_dial_expose;
+ widget_class->size_request = gtk_dial_size_request;
+ widget_class->size_allocate = gtk_dial_size_allocate;
+ widget_class->button_press_event = gtk_dial_button_press;
+ widget_class->button_release_event = gtk_dial_button_release;
+ widget_class->motion_notify_event = gtk_dial_motion_notify;
+}
+
+static void
+gtk_dial_init (GtkDial *dial)
+{
+ dial->button = 0;
+ dial->policy = GTK_UPDATE_CONTINUOUS;
+ dial->timer = 0;
+ dial->radius = 0;
+ dial->pointer_width = 0;
+ dial->angle = 0.0;
+ dial->old_value = 0.0;
+ dial->old_lower = 0.0;
+ dial->old_upper = 0.0;
+ dial->adjustment = NULL;
+}
+
+GtkWidget*
+gtk_dial_new (GtkAdjustment *adjustment)
+{
+ GtkDial *dial;
+
+ dial = gtk_type_new (gtk_dial_get_type ());
+
+ if (!adjustment)
+ adjustment = (GtkAdjustment*) gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
+
+ gtk_dial_set_adjustment (dial, adjustment);
+
+ return GTK_WIDGET (dial);
+}
+
+static void
+gtk_dial_destroy (GtkObject *object)
+{
+ GtkDial *dial;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GTK_IS_DIAL (object));
+
+ dial = GTK_DIAL (object);
+
+ if (dial->adjustment)
+ gtk_object_unref (GTK_OBJECT (dial->adjustment));
+
+ if (GTK_OBJECT_CLASS (parent_class)->destroy)
+ (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+</verb></tscreen>
+
+Observe que ésta función <tt/init()/ hace menos cosas de las que hacía
+la función <tt/init()/ que utilizamos con el <em/widget/ Tictactoe, ya
+que éste no es un <em/widget/ compuesto, y la función <tt/new()/ hace
+más cosas, ya que ahora admite un argumento. Observe también que
+cuando almacenamos un puntero en un objeto Adjustment, incrementamos
+su contador interno, (y lo decrementamos cuando ya no lo utilizamos)
+por lo que GTK puede saber cuando se puede destruir sin que se
+produzcan problemas.
+
+<p>
+Aquí tenemos unas cuantas funciones para manipular las opciones del
+<em/widget/:
+
+<tscreen><verb>
+GtkAdjustment*
+gtk_dial_get_adjustment (GtkDial *dial)
+{
+ g_return_val_if_fail (dial != NULL, NULL);
+ g_return_val_if_fail (GTK_IS_DIAL (dial), NULL);
+
+ return dial->adjustment;
+}
+
+void
+gtk_dial_set_update_policy (GtkDial *dial,
+ GtkUpdateType policy)
+{
+ g_return_if_fail (dial != NULL);
+ g_return_if_fail (GTK_IS_DIAL (dial));
+
+ dial->policy = policy;
+}
+
+void
+gtk_dial_set_adjustment (GtkDial *dial,
+ GtkAdjustment *adjustment)
+{
+ g_return_if_fail (dial != NULL);
+ g_return_if_fail (GTK_IS_DIAL (dial));
+
+ if (dial->adjustment)
+ {
+ gtk_signal_disconnect_by_data (GTK_OBJECT (dial->adjustment), (gpointer) dial);
+ gtk_object_unref (GTK_OBJECT (dial->adjustment));
+ }
+
+ dial->adjustment = adjustment;
+ gtk_object_ref (GTK_OBJECT (dial->adjustment));
+
+ gtk_signal_connect (GTK_OBJECT (adjustment), "changed",
+ (GtkSignalFunc) gtk_dial_adjustment_changed,
+ (gpointer) dial);
+ gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed",
+ (GtkSignalFunc) gtk_dial_adjustment_value_changed,
+ (gpointer) dial);
+
+ dial->old_value = adjustment->value;
+ dial->old_lower = adjustment->lower;
+ dial->old_upper = adjustment->upper;
+
+ gtk_dial_update (dial);
+}
+</verb></tscreen>
+
+<sect2> <tt/gtk_dial_realize()/
+
+<p>
+Ahora vienen algunas funciones nuevas. Primero, tenemos una función
+que hace el trabajo de crear la ventana X. A la función se le pasará
+una máscara <tt/gdk_window_new()/ que especifica que campos de la
+estructura <tt/GdkWindowAttr/ tienen datos (los campos restantes
+tendrán los valores por defecto). También es bueno fijarse en la forma
+en que se crea la máscara de eventos. Llamamos a
+<tt/gtk_widget_get_events()/ para recuperar la máscara de eventos que
+el usuario ha especificado para su <em/widget/ (con
+<tt/gtk_widget_set_events()/), y añadir nosotros mismos los eventos
+en los que estemos interesados.
+
+<p>
+Después de crear la ventana, decidiremos su estilo y su fondo, y
+pondremos un puntero al <em/widget/ en el campo de datos del usuario
+de la <tt/GdkWindow/. Este último paso le permite a GTK despachar los
+eventos que hayan para esta ventana hacia el <em/widget/ correcto.
+
+<tscreen><verb>
+static void
+gtk_dial_realize (GtkWidget *widget)
+{
+ GtkDial *dial;
+ GdkWindowAttr attributes;
+ gint attributes_mask;
+
+ g_return_if_fail (widget != NULL);
+ g_return_if_fail (GTK_IS_DIAL (widget));
+
+ GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
+ dial = GTK_DIAL (widget);
+
+ attributes.x = widget->allocation.x;
+ attributes.y = widget->allocation.y;
+ attributes.width = widget->allocation.width;
+ attributes.height = widget->allocation.height;
+ attributes.wclass = GDK_INPUT_OUTPUT;
+ attributes.window_type = GDK_WINDOW_CHILD;
+ attributes.event_mask = gtk_widget_get_events (widget) |
+ GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK |
+ GDK_POINTER_MOTION_HINT_MASK;
+ attributes.visual = gtk_widget_get_visual (widget);
+ attributes.colormap = gtk_widget_get_colormap (widget);
+
+ attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+ widget->window = gdk_window_new (widget->parent->window, &amp;attributes, attributes_mask);
+
+ widget->style = gtk_style_attach (widget->style, widget->window);
+
+ gdk_window_set_user_data (widget->window, widget);
+
+ gtk_style_set_background (widget->style, widget->window, GTK_STATE_ACTIVE);
+}
+</verb></tscreen>
+
+<sect2> Negociación del tamaño
+
+<p>
+Antes de que se muestre por primera vez la ventana conteniendo un
+<em/widget/, y cuando quiera que la capa de la ventana cambie, GTK le
+preguntara a cada <em/widget/ hijo por su tamaño deseado. Esta
+petición se controla mediante la función
+<tt/gtk_dial_size_request()/. Como nuestro <em/widget/ no es un
+<em/widget/ contenedor, y no tiene ninguna limitación en su tamaño,
+nos contentaremos con devolver un valor por defecto.
+
+<tscreen><verb>
+static void
+gtk_dial_size_request (GtkWidget *widget,
+ GtkRequisition *requisition)
+{
+ requisition->width = DIAL_DEFAULT_SIZE;
+ requisition->height = DIAL_DEFAULT_SIZE;
+}
+</verb></tscreen>
+
+<p>
+Después de que todos los <em/widgets/ hayan pedido su tamaño ideal,
+se calculará la ventana y cada <em/widget/ hijo será informado de su
+tamaño actual. Normalmente, éste será al menos tan grande como el
+pedido, pero si por ejemplo, el usuario ha redimensionado la ventana,
+entonces puede que el tamaño que se le de al <em/widget/ sea menor
+que el que pidió. La notificación del tamaño se maneja mediante la
+función <tt/gtk_dial_size_allocate()/. Fíjese que esta función calcula
+los tamaños de los diferentes elementos que componen la ventana para
+su uso futuro, así como todo el trabajo sucio que poner los
+<em/widgets/ de la ventana X en la nueva posición y con el nuevo
+tamaño.
+
+<tscreen><verb>
+static void
+gtk_dial_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation)
+{
+ GtkDial *dial;
+
+ g_return_if_fail (widget != NULL);
+ g_return_if_fail (GTK_IS_DIAL (widget));
+ g_return_if_fail (allocation != NULL);
+
+ widget->allocation = *allocation;
+ if (GTK_WIDGET_REALIZED (widget))
+ {
+ dial = GTK_DIAL (widget);
+
+ gdk_window_move_resize (widget->window,
+ allocation->x, allocation->y,
+ allocation->width, allocation->height);
+
+ dial->radius = MAX(allocation->width,allocation->height) * 0.45;
+ dial->pointer_width = dial->radius / 5;
+ }
+}
+</verb></tscreen>.
+
+<!-- ----------------------------------------------------------------- -->
+<sect2> <tt/gtk_dial_expose()/
+
+<p>
+Como se mencionó arriba, todo el dibujado de este <em/widget/ se hace
+en el manejador de los eventos <tt/expose/. No hay mucho destacable
+aquí, excepto la utilización de la función <tt/gtk_draw_polygon/ para
+dibujar el puntero con un degradado tridimensional de acuerdo con los
+colores almacenados en el estilo del <em/widget/.
+
+<tscreen><verb>
+static gint
+gtk_dial_expose (GtkWidget *widget,
+ GdkEventExpose *event)
+{
+ GtkDial *dial;
+ GdkPoint points[3];
+ gdouble s,c;
+ gdouble theta;
+ gint xc, yc;
+ gint tick_length;
+ gint i;
+
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ if (event->count > 0)
+ return FALSE;
+
+ dial = GTK_DIAL (widget);
+
+ gdk_window_clear_area (widget->window,
+ 0, 0,
+ widget->allocation.width,
+ widget->allocation.height);
+
+ xc = widget->allocation.width/2;
+ yc = widget->allocation.height/2;
+
+ /* Dibujar las rayitas */
+
+ for (i=0; i<25; i++)
+ {
+ theta = (i*M_PI/18. - M_PI/6.);
+ s = sin(theta);
+ c = cos(theta);
+
+ tick_length = (i%6 == 0) ? dial->pointer_width : dial->pointer_width/2;
+
+ gdk_draw_line (widget->window,
+ widget->style->fg_gc[widget->state],
+ xc + c*(dial->radius - tick_length),
+ yc - s*(dial->radius - tick_length),
+ xc + c*dial->radius,
+ yc - s*dial->radius);
+ }
+
+ /* Dibujar el puntero */
+
+ s = sin(dial->angle);
+ c = cos(dial->angle);
+
+
+ points[0].x = xc + s*dial->pointer_width/2;
+ points[0].y = yc + c*dial->pointer_width/2;
+ points[1].x = xc + c*dial->radius;
+ points[1].y = yc - s*dial->radius;
+ points[2].x = xc - s*dial->pointer_width/2;
+ points[2].y = yc - c*dial->pointer_width/2;
+
+ gtk_draw_polygon (widget->style,
+ widget->window,
+ GTK_STATE_NORMAL,
+ GTK_SHADOW_OUT,
+ points, 3,
+ TRUE);
+
+ return FALSE;
+}
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect2> Manejo de eventos
+
+<p>
+El resto del código del <em/widget/ controla varios tipos de eventos,
+y no es muy diferente del que podemos encontrar en muchas aplicaciones
+GTK. Pueden ocurrir dos tipos de eventos - el usuario puede pulsar en
+el <em/widget/ con el ratón y arrastrar para mover el puntero, o el
+valor del objeto Adjustement puede cambiar debido a alguna
+circunstancia externa.
+
+<p>
+Cuando el usuario pulsa en el <em/widget/, haremos una comprobación
+para ver si la pulsación se hizo lo suficientemente cerca del
+puntero, y si así fue, almacenamos el botón que pulsó el usuario en
+en el campo <tt/button/ de la estructura del <em/widget/, y grabamos
+todos los eventos del ratón con una llamada a <tt/gtk_grab_add()/. El
+movimiento del ratón hará que se recalcule el valor del control
+(mediante la función <tt/gtk_dial_update_mouse/). Dependiendo de la
+política que sigamos, o bien se generarán instantáneamente los eventos
+<tt/value_changed/ (<tt/GTK_UPDATE_CONTINUOUS/), o bien después de una
+espera del temporizador establecido mediante <tt/gtk_timeout_add()/
+(<tt/GTK_UPDATE_DELAYED/), o bien sólo cuando se levante el botón
+(<tt/GTK_UPDATE_DISCONTINUOUS/).
+
+<tscreen><verb>
+static gint
+gtk_dial_button_press (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ GtkDial *dial;
+ gint dx, dy;
+ double s, c;
+ double d_parallel;
+ double d_perpendicular;
+
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ dial = GTK_DIAL (widget);
+
+ /* Determinar si la pulsación del botón fue dentro de la región del
+ puntero - esto lo hacemos calculando la distancia x e y del punto
+ donde se pulsó el botón ratón de la línea que se ha pasado mediante el
+ puntero */
+
+ dx = event->x - widget->allocation.width / 2;
+ dy = widget->allocation.height / 2 - event->y;
+
+ s = sin(dial->angle);
+ c = cos(dial->angle);
+
+ d_parallel = s*dy + c*dx;
+ d_perpendicular = fabs(s*dx - c*dy);
+
+ if (!dial->button &&
+ (d_perpendicular < dial->pointer_width/2) &&
+ (d_parallel > - dial->pointer_width))
+ {
+ gtk_grab_add (widget);
+
+ dial->button = event->button;
+
+ gtk_dial_update_mouse (dial, event->x, event->y);
+ }
+
+ return FALSE;
+}
+
+static gint
+gtk_dial_button_release (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ GtkDial *dial;
+
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ dial = GTK_DIAL (widget);
+
+ if (dial->button == event->button)
+ {
+ gtk_grab_remove (widget);
+
+ dial->button = 0;
+
+ if (dial->policy == GTK_UPDATE_DELAYED)
+ gtk_timeout_remove (dial->timer);
+
+ if ((dial->policy != GTK_UPDATE_CONTINUOUS) &&
+ (dial->old_value != dial->adjustment->value))
+ gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
+ }
+
+ return FALSE;
+}
+
+static gint
+gtk_dial_motion_notify (GtkWidget *widget,
+ GdkEventMotion *event)
+{
+ GtkDial *dial;
+ GdkModifierType mods;
+ gint x, y, mask;
+
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ dial = GTK_DIAL (widget);
+
+ if (dial->button != 0)
+ {
+ x = event->x;
+ y = event->y;
+
+ if (event->is_hint || (event->window != widget->window))
+ gdk_window_get_pointer (widget->window, &amp;x, &amp;y, &amp;mods);
+
+ switch (dial->button)
+ {
+ case 1:
+ mask = GDK_BUTTON1_MASK;
+ break;
+ case 2:
+ mask = GDK_BUTTON2_MASK;
+ break;
+ case 3:
+ mask = GDK_BUTTON3_MASK;
+ break;
+ default:
+ mask = 0;
+ break;
+ }
+
+ if (mods & mask)
+ gtk_dial_update_mouse (dial, x,y);
+ }
+
+ return FALSE;
+}
+
+static gint
+gtk_dial_timer (GtkDial *dial)
+{
+ g_return_val_if_fail (dial != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_DIAL (dial), FALSE);
+
+ if (dial->policy == GTK_UPDATE_DELAYED)
+ gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
+
+ return FALSE;
+}
+
+static void
+gtk_dial_update_mouse (GtkDial *dial, gint x, gint y)
+{
+ gint xc, yc;
+ gfloat old_value;
+
+ g_return_if_fail (dial != NULL);
+ g_return_if_fail (GTK_IS_DIAL (dial));
+
+ xc = GTK_WIDGET(dial)->allocation.width / 2;
+ yc = GTK_WIDGET(dial)->allocation.height / 2;
+
+ old_value = dial->adjustment->value;
+ dial->angle = atan2(yc-y, x-xc);
+
+ if (dial->angle < -M_PI/2.)
+ dial->angle += 2*M_PI;
+
+ if (dial->angle < -M_PI/6)
+ dial->angle = -M_PI/6;
+
+ if (dial->angle > 7.*M_PI/6.)
+ dial->angle = 7.*M_PI/6.;
+
+ dial->adjustment->value = dial->adjustment->lower + (7.*M_PI/6 - dial->angle) *
+ (dial->adjustment->upper - dial->adjustment->lower) / (4.*M_PI/3.);
+
+ if (dial->adjustment->value != old_value)
+ {
+ if (dial->policy == GTK_UPDATE_CONTINUOUS)
+ {
+ gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
+ }
+ else
+ {
+ gtk_widget_draw (GTK_WIDGET(dial), NULL);
+
+ if (dial->policy == GTK_UPDATE_DELAYED)
+ {
+ if (dial->timer)
+ gtk_timeout_remove (dial->timer);
+
+ dial->timer = gtk_timeout_add (SCROLL_DELAY_LENGTH,
+ (GtkFunction) gtk_dial_timer,
+ (gpointer) dial);
+ }
+ }
+ }
+}
+</verb></tscreen>
+
+<p>
+Cambios en el Adjustment por motivos externos significa que se le
+comunicarán a nuestro <em/widget/ mediante las señales <tt/changed/ y
+<tt/value_changed/. Los manejadores de estas funciones llaman a
+<tt/gtk_dial_update()/ para comprobar los argumentos, calcular el
+nuevo ángulo del puntero, y redibujar el <em/widget/ (llamando a
+<tt/gtk_widget_draw()/).
+
+<tscreen><verb>
+static void
+gtk_dial_update (GtkDial *dial)
+{
+ gfloat new_value;
+
+ g_return_if_fail (dial != NULL);
+ g_return_if_fail (GTK_IS_DIAL (dial));
+
+ new_value = dial->adjustment->value;
+
+ if (new_value < dial->adjustment->lower)
+ new_value = dial->adjustment->lower;
+
+ if (new_value > dial->adjustment->upper)
+ new_value = dial->adjustment->upper;
+
+ if (new_value != dial->adjustment->value)
+ {
+ dial->adjustment->value = new_value;
+ gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
+ }
+
+ dial->angle = 7.*M_PI/6. - (new_value - dial->adjustment->lower) * 4.*M_PI/3. /
+ (dial->adjustment->upper - dial->adjustment->lower);
+
+ gtk_widget_draw (GTK_WIDGET(dial), NULL);
+}
+
+static void
+gtk_dial_adjustment_changed (GtkAdjustment *adjustment,
+ gpointer data)
+{
+ GtkDial *dial;
+
+ g_return_if_fail (adjustment != NULL);
+ g_return_if_fail (data != NULL);
+
+ dial = GTK_DIAL (data);
+
+ if ((dial->old_value != adjustment->value) ||
+ (dial->old_lower != adjustment->lower) ||
+ (dial->old_upper != adjustment->upper))
+ {
+ gtk_dial_update (dial);
+
+ dial->old_value = adjustment->value;
+ dial->old_lower = adjustment->lower;
+ dial->old_upper = adjustment->upper;
+ }
+}
+
+static void
+gtk_dial_adjustment_value_changed (GtkAdjustment *adjustment,
+ gpointer data)
+{
+ GtkDial *dial;
+
+ g_return_if_fail (adjustment != NULL);
+ g_return_if_fail (data != NULL);
+
+ dial = GTK_DIAL (data);
+
+ if (dial->old_value != adjustment->value)
+ {
+ gtk_dial_update (dial);
+
+ dial->old_value = adjustment->value;
+ }
+}
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect2> Posibles mejoras
+<p>
+
+El <em/widget/ Dial tal y como lo hemos descrito tiene unas 670
+líneas de código. Aunque pueda parecer un poco exagerado, todavía
+no hemos escrito demasiado código, ya que la mayoría de las líneas
+son de ficheros de cabecera y de adornos. Todavía se le pueden hacer
+algunas mejoras a este <em/widget/:
+
+<itemize>
+<item> Si prueba el <em/widget/, verá que el puntero cambia a
+pantallazos cuando se le arrastra. Esto es debido a que todo el
+<em/widget/ se borra cada vez que se mueve el puntero, antes de
+redibujarse. Normalmente, la mejor forma de tratar este problema es
+dibujar en un <em/pixmap/ que no represente lo que se ve directamente
+en pantalla, y copiar el resultado final en la pantalla en sólo un
+paso. (El <em/widget/ ProgressBar funciona de esta forma.)
+
+<item> El usuario debería ser capaz de utilizar las flechas de arriba
+y abajo para aumentar y decrementar el valor.
+
+<item> Sería bonito si el <em/widget/ tuviese botones para
+incrementar y decrementar el valor a saltos más o menos
+grandes. Es posible utilizar <em/widgets/ botón, aunque también
+queremos que los botones pudiesen realizar la operación de incrementar
+o decrementar varias veces, mientras se mantenga el botón pulsado, tal
+y como lo hacen las flechas en una barra de desplazamiento. La mayoría
+del código para implementar todo esto lo podemos encontrar en el
+<em/widget/ GtkRange.
+
+<item> El <em/widget/ Dial puede utilizarse en un <em/widget/
+contenedor con un simple <em/widget/ hijo colocado en la parte
+inferior entre los botones antes mencionados. El usuario puede añadir
+(según prefiera) una etiqueta o un <em/widget/ entry para mostrar el
+valor actual del marcador.
+
+</itemize>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Aprendiendo más
+
+<p>
+Sólo se han descrito una pequeña parte de los muchos detalles
+involucrados en la creación de <em/widgets/, la mejor fuente de
+ejemplos es el código mismo de GTK. Hágase algunas preguntas acerca
+del <em/widget/ que desea crear: ¿es un <em/widget/ contenedor?
+¿Debe tener su propia ventana? ¿Es una modificación de un
+<em/widget/ existente? En ese momento busque un <em/widget/ similar, y
+comience a hacer los cambios.
+¡Buena suerte!
+
+<!-- ***************************************************************** -->
+<sect>Scribble, un sencillo programa de dibujo de ejemplo
+<!-- ***************************************************************** -->
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Objetivos
+
+<p>
+En esta sección, vamos a crear un sencillo programa de dibujo. En el
+proceso, vamos a examinar como se manejan los eventos de ratón, como
+dibujar en una ventana, y como mejorar el dibujado utilizando un
+<em/pixmap/ intermedio. Después de crear el programa de dibujo, lo
+ampliaremos añadiendole la posibilidad de utilizar dispositivos
+XInput, como tabletas digitalizadoras. GTK proporciona las rutinas que
+nos darán la posibilidad de obtener información extra, como la presión
+y la inclinación, de todo tipo de dispositivos de una forma sencilla.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Manejo de eventos
+
+<p>
+Las señales GTK sobre las que ya hemos discutido son para las
+acciones de alto nivel, como cuando se selecciona un elemento de un
+menú. Sin embargo a veces es útil tratar con los acontecimientos a
+bajo nivel, como cuando se mueve el ratón, o cuando se está
+presionando una tecla. También hay señales GTK relacionadas con
+estos <em/eventos/ de bajo nivel. Los manejadores de estas señales
+tienen un parámetro extra que es un puntero a una estructura
+conteniendo información sobre el evento. Por ejemplo, a los manejadores
+de los eventos de movimiento se les pasa una estructura
+<tt/GdkEventMotion/ que es (en parte) así:
+
+<tscreen><verb>
+struct _GdkEventMotion
+{
+ GdkEventType type;
+ GdkWindow *ventana;
+ guint32 time;
+ gdouble x;
+ gdouble y;
+ ...
+ guint state;
+ ...
+};
+</verb></tscreen>
+
+<tt/type/ adquirirá su valor adecuado dependiendo del tipo de evento,
+en nuestro caso <tt/GDK_MOTION_NOTIFY/, <tt/ventana/ es la ventana en
+la que ocurre el evento. <tt/x/ e <tt/y/ dan las coordenadas del
+evento, y <tt/state/ especifica cual es la modificación que ha habido
+cuando ocurrió el evento (esto es, especifica que teclas han cambiado
+su estado y que botones del ratón se han presionado.) Es la
+operación OR (O) de algunos de los siguientes valores:
+
+<tscreen><verb>
+GDK_SHIFT_MASK
+GDK_LOCK_MASK
+GDK_CONTROL_MASK
+GDK_MOD1_MASK
+GDK_MOD2_MASK
+GDK_MOD3_MASK
+GDK_MOD4_MASK
+GDK_MOD5_MASK
+GDK_BUTTON1_MASK
+GDK_BUTTON2_MASK
+GDK_BUTTON3_MASK
+GDK_BUTTON4_MASK
+GDK_BUTTON5_MASK
+</verb></tscreen>
+
+<p>
+Como con las otras señales, para especificar que es lo que pasa cuando
+ocurre un evento, llamaremos a <tt>gtk_signal_connect()</tt>. Pero
+también necesitamos decirle a GTK sobre que eventos queremos ser
+informados. Para ello, llamaremos a la función:
+
+<tscreen><verb>
+void gtk_widget_set_events (GtkWidget *widget,
+ gint events);
+</verb></tscreen>
+
+El segundo campo especifica los eventos en los que estamos
+interesados. Es el OR (O) de las constantes que especifican los
+diferentes tipos de eventos. Por las referencias futuras que podamos
+hacer, presentamos aquí los tipos de eventos que hay disponibles:
+
+<tscreen><verb>
+GDK_EXPOSURE_MASK
+GDK_POINTER_MOTION_MASK
+GDK_POINTER_MOTION_HINT_MASK
+GDK_BUTTON_MOTION_MASK
+GDK_BUTTON1_MOTION_MASK
+GDK_BUTTON2_MOTION_MASK
+GDK_BUTTON3_MOTION_MASK
+GDK_BUTTON_PRESS_MASK
+GDK_BUTTON_RELEASE_MASK
+GDK_KEY_PRESS_MASK
+GDK_KEY_RELEASE_MASK
+GDK_ENTER_NOTIFY_MASK
+GDK_LEAVE_NOTIFY_MASK
+GDK_FOCUS_CHANGE_MASK
+GDK_STRUCTURE_MASK
+GDK_PROPERTY_CHANGE_MASK
+GDK_PROXIMITY_IN_MASK
+GDK_PROXIMITY_OUT_MASK
+</verb></tscreen>
+
+Hay unos cuantas sutilezas que debemos respetar cuando llamamos a
+<tt/gtk_widget_set_events()/. Primero, debemos llamar a esta función
+antes de que se cree la ventana X para el <em/widget/ GTK. En
+términos prácticos, significa que debemos llamarla inmediatamente
+después de crear el <em/widget/. Segundo, el <em/widget/ debe tener
+una ventana X asociado. Por motivos de eficiencia, hay muchos
+<em/widgets/ que no tienen su propia ventana, sino que dibujan en la
+de su padre. Estos <em/widgets/ son:
+
+<tscreen><verb>
+GtkAlignment
+GtkArrow
+GtkBin
+GtkBox
+GtkImage
+GtkItem
+GtkLabel
+GtkPixmap
+GtkScrolledWindow
+GtkSeparator
+GtkTable
+GtkAspectFrame
+GtkFrame
+GtkVBox
+GtkHBox
+GtkVSeparator
+GtkHSeparator
+</verb></tscreen>
+
+Para capturar eventos para estos <em/widgets/, necesita utilizar un
+<em/widget/ EventBox. Vea la sección <ref
+id="sec_The_EventBox_Widget" name="El widget EventBox"> para más
+detalles.
+
+<p>
+Para nuestro programa de dibujo, queremos saber cuando se presiona el
+botón del ratón y cuando se mueve, por lo que debemos especificar
+los eventos <tt/GDK_POINTER_MOTION_MASK/ y
+<tt/GDK_BUTTON_PRESS_MASK/. También queremos saber cuando necesitamos
+redibujar nuestra ventana, por lo que especificaremos el evento
+<tt/GDK_EXPOSURE_MASK/. Aunque queremos estar informados mediante un
+evento <tt/Configure/ cuando cambie el tamaño de nuestra ventana, no
+tenemos que especificar la correspondiente <tt/GDK_STRUCTURE_MASK/,
+porque ya está activada por defecto para todas las ventanas.
+
+<p>
+Tenemos un problema con lo que acabamos de hacer, y tiene que ver con
+la utilización de <tt/GDK_POINTER_MOTION_MASK/. Si especificamos este
+evento, el servidor añadirá un evento de movimiento a la cola de
+eventos cada vez que el usuario mueva el ratón. Imagine que nos
+cuesta 0'1 segundo tratar el evento de movimiento, pero que el
+servidor X añade a la cola un nuevo evento de moviento cada 0'05
+segundos. Pronto nos iremos quedando retrasados con respecto al resto
+de los eventos. Si el usuario dibuja durante 5 segundos, ¡nos llevará
+otros 5 segundos el cazarle después de que hay levantado el botón
+del ratón! Lo que queremos es sólo un evento de movimiento por cada
+evento que procesemos. La manera de hacerlo es especificando
+<tt/GDK_POINTER_MOTION_HINT_MASK/.
+
+<p>
+Cuando especificamos <tt/GDK_POINTER_MOTION_HINT_MASK/, el servidor
+nos envia un evento de movimiento la primera ver que el puntero se
+mueve depués de entrar en nuestra ventana, o después de que se
+apriete o se suelte un botón (y se reciba el evento
+correspondiente). Los eventos de movimiento restantes se eliminarán a
+no ser que preguntemos especificamente por la posición del puntero
+utilizando la función:
+
+<tscreen><verb>
+GdkWindow* gdk_window_get_pointer (GdkWindow *ventana,
+ gint *x,
+ gint *y,
+ GdkModifierType *mask);
+</verb></tscreen>
+
+(Hay otra función, <tt>gtk_widget_get_pointer()</tt> que tiene una
+interfaz más sencillo, pero esta simplificación le resta utilidad, ya
+que sólo devuelve la posición del ratón, y no si alguno de sus botones
+está presionado.)
+
+<p>
+El código para establecer los eventos para nuestra ventana es el
+siguiente:
+
+<tscreen><verb>
+ gtk_signal_connect (GTK_OBJECT (drawing_area), "expose_event",
+ (GtkSignalFunc) expose_event, NULL);
+ gtk_signal_connect (GTK_OBJECT(drawing_area),"configure_event",
+ (GtkSignalFunc) configure_event, NULL);
+ gtk_signal_connect (GTK_OBJECT (drawing_area), "motion_notify_event",
+ (GtkSignalFunc) motion_notify_event, NULL);
+ gtk_signal_connect (GTK_OBJECT (drawing_area), "button_press_event",
+ (GtkSignalFunc) button_press_event, NULL);
+
+ gtk_widget_set_events (drawing_area, GDK_EXPOSURE_MASK
+ | GDK_LEAVE_NOTIFY_MASK
+ | GDK_BUTTON_PRESS_MASK
+ | GDK_POINTER_MOTION_MASK
+ | GDK_POINTER_MOTION_HINT_MASK);
+</verb></tscreen>
+
+Vamos a dejar los manejadores de los eventos <tt/expose_event/ y
+<tt/configure_event/ para después. Los manejadores de
+<tt/motion_notify_event/ y de <tt/button_press_event/ son bastante
+simples:
+
+<tscreen><verb>
+static gint
+button_press_event (GtkWidget *widget, GdkEventButton *event)
+{
+ if (event->button == 1 &amp;&amp; pixmap != NULL)
+ draw_brush (widget, event->x, event->y);
+
+ return TRUE;
+}
+
+static gint
+motion_notify_event (GtkWidget *widget, GdkEventMotion *event)
+{
+ int x, y;
+ GdkModifierType state;
+
+ if (event->is_hint)
+ gdk_window_get_pointer (event->window, &amp;x, &amp;y, &amp;state);
+ else
+ {
+ x = event->x;
+ y = event->y;
+ state = event->state;
+ }
+
+ if (state &amp; GDK_BUTTON1_MASK &amp;&amp; pixmap != NULL)
+ draw_brush (widget, x, y);
+
+ return TRUE;
+}
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> El <em/widget/ DrawingArea, y dibujando
+
+<p>
+Vamos a pasar al proceso de dibujar en la pantalla. El <em/widget/ que
+utilizaremos será el DrawingArea. Un <em/widget/ DrawingArea es
+esencialmente una ventana X y nada más. Es un lienzo en blanco en
+el que podemos dibujar lo que queramos. Crearemos un área de dibujo
+utilizando la llamada:
+
+<tscreen><verb>
+GtkWidget* gtk_drawing_area_new (void);
+</verb></tscreen>
+
+Se puede especificar un tamaño por defecto para el <em/widget/
+llamando a:
+
+<tscreen><verb>
+void gtk_drawing_area_size (GtkDrawingArea *darea,
+ gint width,
+ gint height);
+</verb></tscreen>
+
+Se puede cambiar el tamaño por defecto, como para todos los
+<em/widgets/, llamando a <tt/gtk_widget_set_usize()/, y esto, además,
+puede cambiarse si el usuario cambia manualmente el tamaño de la
+ventana que contiene el área de dibujo.
+
+<p>
+Debemos hacer notar que cuando creamos un <em/widget/ DrawingArea,
+seremos <em/completamente/ responsables de dibujar su contenido. Si
+nuestra ventana se tapa y se vuelve a poner al descubierto,
+obtendremos un evento de exposición y deberemos redibujar lo que se
+había tapado.
+
+<p>
+Tener que recordar todo lo que se dibujó en la pantalla para que
+podamos redibujarla convenientemente es, por decirlo de alguna manera
+suave, una locura. Además puede quedar mal si hay que borrar partes
+de la pantalla y hay que redibujarlas paso a paso. La solución a este
+problema es utilizar un <em>pixmap</em> intermedio. En lugar de
+dibujar directamente en la pantalla, dibujaremos en una imagen que
+estará almacenada en la memoria del servidor, pero que no se mostrará,
+y cuando cambie la imagen o se muestren nuevas partes de
+la misma, copiaremos las porciones relevantes en la pantalla.
+
+<p>
+Para crear un <em/pixmap/ intermedio, llamaremos a la función:
+
+<tscreen><verb>
+GdkPixmap* gdk_pixmap_new (GdkWindow *ventana,
+ gint width,
+ gint height,
+ gint depth);
+</verb></tscreen>
+
+El parámetro <tt/widget/ especifica una ventana GDK de las que este
+<em/pixmap/ tomará algunas propiedades. <tt/width/ y <tt/height/
+especifican el tamaño del <em/pixmap/. <tt/depth/ especifica la
+<em/profundidad del color/, que es el número de bits por pixel de la
+nueva ventana. Si la profundidad que se especifica es <tt/-1/, se
+utilizará la misma profundidad de color que tenga la <tt/ventana/.
+
+<p>
+Creamos nuestro <em/pixmap/ en nuestro manejador del evento
+<tt/configure_event/. Este evento se genera cada vez que cambia el
+tamaño de la ventana, incluyendo cuando ésta se crea.
+
+<tscreen><verb>
+/* Backing pixmap for drawing area */
+static GdkPixmap *pixmap = NULL;
+
+/* Create a new backing pixmap of the appropriate size */
+static gint
+configure_event (GtkWidget *widget, GdkEventConfigure *event)
+{
+ if (pixmap)
+ gdk_pixmap_unref(pixmap);
+
+ pixmap = gdk_pixmap_new(widget->window,
+ widget->allocation.width,
+ widget->allocation.height,
+ -1);
+ gdk_draw_rectangle (pixmap,
+ widget->style->white_gc,
+ TRUE,
+ 0, 0,
+ widget->allocation.width,
+ widget->allocation.height);
+
+ return TRUE;
+}
+</verb></tscreen>
+
+La llamada a <tt/gdk_draw_rectangle()/ rellena todo el <em/pixmap/ de
+blanco. Hablaremos más de todo esto en un momento.
+
+<p>
+Nuestro manejador del evento de exposición simplemente copia la
+porción relevante del <em/pixmap/ en la pantalla (determinaremos la
+zona a redibujar utilizando el campo <tt/event->area/ del evento de
+exposición):
+
+<tscreen><verb>
+/* Redraw the screen from the backing pixmap */
+static gint
+expose_event (GtkWidget *widget, GdkEventExpose *event)
+{
+ gdk_draw_pixmap(widget->window,
+ widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
+ pixmap,
+ event->area.x, event->area.y,
+ event->area.x, event->area.y,
+ event->area.width, event->area.height);
+
+ return FALSE;
+}
+</verb></tscreen>
+
+Ahora ya sabemos como mantener la pantalla actualizada con el
+contenido de nuestro <em/pixmap/, pero ¿cómo podemos dibujar algo
+interesante en nuestro <em/pixmap/? Hay un gran número de llamadas en
+la biblioteca GDK para dibujar en los <em/dibujables/. Un dibujable es
+simplemente algo sobre lo que se puede dibujar. Puede ser una ventana,
+un <em/pixmap/, un <em/bitmap/ (una imagen en blanco y negro), etc. Ya
+hemos visto arriba dos de estas llamadas,
+<tt>gdk_draw_rectangle()</tt> y <tt>gdk_draw_pixmap()</tt>. La lista
+completa de funciones para dibujar es:
+
+<tscreen><verb>
+gdk_draw_line ()
+gdk_draw_rectangle ()
+gdk_draw_arc ()
+gdk_draw_polygon ()
+gdk_draw_string ()
+gdk_draw_text ()
+gdk_draw_pixmap ()
+gdk_draw_bitmap ()
+gdk_draw_image ()
+gdk_draw_points ()
+gdk_draw_segments ()
+</verb></tscreen>
+
+Ver la documentación de estas funciones o el fichero de cabecera
+<tt>&lt;gdk/gdk.h&gt;</tt> para obtener más detalles sobre estas
+funciones. Todas comparten los dos primeros argumentos. El primero es
+el dibujable en el que se dibujará, y el segundo argumento es un
+<em/contexto gráfico/ (GC).
+
+<p>
+Un contexto gráfico reúne la información sobre cosas como el color
+de fondo y del color de lo que se dibuja, el ancho de la línea,
+etc... GDK tiene un conjunto completo de funciones para crear y
+modificar los contextos gráficos. Cada <em/widget/ tiene un GC
+asociado. (Que puede modificarse en un fichero gtkrc, ver la sección
+«Ficheros rc de GTK».) Estos, junto con otras cosas, almacenan
+GC's. Algunos ejemplos de como acceder a estos GC's son:
+
+<tscreen><verb>
+widget->style->white_gc
+widget->style->black_gc
+widget->style->fg_gc[GTK_STATE_NORMAL]
+widget->style->bg_gc[GTK_WIDGET_STATE(widget)]
+</verb></tscreen>
+
+Los campos <tt>fg_gc</tt>, <tt>bg_gc</tt>, <tt>dark_gc</tt>, y
+<tt>light_gc</tt> se indexan con un parámetro del tipo
+<tt/GtkStateType/ que puede tomar uno de los valores:
+
+<tscreen><verb>
+GTK_STATE_NORMAL,
+GTK_STATE_ACTIVE,
+GTK_STATE_PRELIGHT,
+GTK_STATE_SELECTED,
+GTK_STATE_INSENSITIVE
+</verb></tscreen>
+
+Por ejemplo, para el <tt/GTK_STATE_SELECTED/, el color que se utiliza
+para pintar por defecto es el blanco y el color del fondo por defecto,
+es el azul oscuro.
+
+<p>
+Nuestra función <tt/draw_brush()/, que es la que dibuja en la
+pantalla, será la siguiente:
+
+<tscreen><verb>
+/* Draw a rectangle on the screen */
+static void
+draw_brush (GtkWidget *widget, gdouble x, gdouble y)
+{
+ GdkRectangle update_rect;
+
+ update_rect.x = x - 5;
+ update_rect.y = y - 5;
+ update_rect.width = 10;
+ update_rect.height = 10;
+ gdk_draw_rectangle (pixmap,
+ widget->style->black_gc,
+ TRUE,
+ update_rect.x, update_rect.y,
+ update_rect.width, update_rect.height);
+ gtk_widget_draw (widget, &amp;update_rect);
+}
+</verb></tscreen>
+
+Después de que dibujemos el rectángulo representando la brocha en el
+<em/pixmap/ llamaremos a la función:
+
+<tscreen><verb>
+void gtk_widget_draw (GtkWidget *widget,
+ GdkRectangle *area);
+</verb></tscreen>
+
+que le informa a X de que la zona dada por el parámetro <tt/area/
+necesita actualizarse. X generará un evento de exposición
+(combinando posiblemente distintas zonas pasadas mediante distintas
+llamadas a <tt/gtk_widget_draw()/) que hará que nuestro manejador de
+eventos de exposición copie las porciones relevantes en la pantalla.
+
+<p>
+Ya hemos cubierto el programa de dibujo completo, excepto unos cuantos
+detalles mundanos como crear la ventana principal. El código completo
+está disponible en el mismo lugar en el que consiguió este tutorial,
+o en:
+
+<htmlurl url="http://www.gtk.org/~otaylor/gtk/tutorial/"
+name="http://www.gtk.org/~otaylor/gtk/tutorial/">
+
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Añadiendo la capacidad de utilizar XInput
+
+<p>
+Ahora es posible comprar dispositos de entrada bastante baratos, como
+tabletas digitalizadoras, que permiten dibujar de forma artística
+mucho más fácilmente de cómo lo haríamos con un ratón. La forma
+más sencilla de utilizar estos dispositivos es simplemente
+reemplazando a los ratones, pero así perdemos muchas de las ventajas
+de este tipo de dispositivos, como por ejemplo:
+
+<itemize>
+<item> Sensibilidad a la presión
+<item> Información sobre la inclinación
+<item> Colocación subpixel
+<item> Multiples entradas (por ejemplo, un lápiz con una punta y una
+goma)
+</itemize>
+
+Para información sobre la extensión XInput, ver el <htmlurl
+url="http://www.msc.cornell.edu/~otaylor/xinput/XInput-HOWTO.html"
+name="XInput-HOWTO">.
+
+<p>
+Si examinamos la definición completa de, por ejemplo, la estructura
+<tt/GdkEventMotion/, veremos que tiene campos para almacenar la
+información de los dispositivos extendidos.
+
+<tscreen><verb>
+struct _GdkEventMotion
+{
+ GdkEventType type;
+ GdkWindow *ventana;
+ guint32 time;
+ gdouble x;
+ gdouble y;
+ gdouble pressure;
+ gdouble xtilt;
+ gdouble ytilt;
+ guint state;
+ gint16 is_hint;
+ GdkInputSource source;
+ guint32 deviceid;
+};
+</verb></tscreen>
+
+<tt/pressure/ da la presión como un número de coma flotante entre 0
+y 1. <tt/xtilt/ e <tt/ytilt/ pueden tomar valores entre -1 y 1,
+correspondiendo al grado de inclinación en cada dirección. <tt/source/
+y <tt/deviceid/ especifican el dispositivo para el que ocurre el
+evento de dos maneras diferentes. <tt/source/ da alguna información
+simple sobre el tipo de dispositivo. Puede tomar los valores de la
+enumeración siguiente:
+
+<tscreen><verb>
+GDK_SOURCE_MOUSE
+GDK_SOURCE_PEN
+GDK_SOURCE_ERASER
+GDK_SOURCE_CURSOR
+</verb></tscreen>
+
+<tt/deviceid/ especifica un número único ID para el dispositivo. Puede
+utilizarse para obtener más información sobre el dispositivo
+utilizando la función <tt/gdk_input_list_devices()/ (ver abajo). El
+valor especial <tt/GDK_CORE_POINTER/ se utiliza para el núcleo del
+dispositivo apuntador. (Normalmente el ratón.)
+
+<sect2> Activando la información del dispositivo extendido
+
+<p>
+Para informar a GTK de nuestro interés en la información sobre los
+dispositivos extendidos, sólo tenemos que añadirle una línea a
+nuestro programa:
+
+<tscreen><verb>
+gtk_widget_set_extension_events (drawing_area, GDK_EXTENSION_EVENTS_CURSOR);
+</verb></tscreen>
+
+Dando el valor <tt/GDK_EXTENSION_EVENTS_CURSOR/ decimos que estamos
+interesados en los eventos de extensión, pero sólo si no tenemos que
+dibujar nuestro propio cursor. Ver la sección <ref
+id="sec_Further_Sophistications" name="Sofisticaciones adicionales">
+más abajo para obtener más información sobre el dibujado del
+cursor. También podríamos dar los valores
+<tt/GDK_EXTENSION_EVENTS_ALL/ si queremos dibujar nuestro propio
+cursor, o <tt/GDK_EXTENSION_EVENTS_NONE/ para volver al estado
+inicial.
+
+<p>
+Todavía no hemos llegado al final de la historia. Por defecto, no hay
+ningún dispositivo extra activado. Necesitamos un mecanismo que
+permita a los usuarios activar y configurar sus dispositivos
+extra. GTK proporciona el <em/widget/ InputDialog para automatizar el
+proceso. El siguiente procedimiento utiliza el <em/widget/
+InputDialog. Crea el cuadro de diálogo si no ha sido ya creado, y lo
+pone en primer plano en caso contrario.
+
+<tscreen><verb>
+void
+input_dialog_destroy (GtkWidget *w, gpointer data)
+{
+ *((GtkWidget **)data) = NULL;
+}
+
+void
+create_input_dialog ()
+{
+ static GtkWidget *inputd = NULL;
+
+ if (!inputd)
+ {
+ inputd = gtk_input_dialog_new();
+
+ gtk_signal_connect (GTK_OBJECT(inputd), "destroy",
+ (GtkSignalFunc)input_dialog_destroy, &amp;inputd);
+ gtk_signal_connect_object (GTK_OBJECT(GTK_INPUT_DIALOG(inputd)->close_button),
+ "clicked",
+ (GtkSignalFunc)gtk_widget_hide,
+ GTK_OBJECT(inputd));
+ gtk_widget_hide ( GTK_INPUT_DIALOG(inputd)->save_button);
+
+ gtk_widget_show (inputd);
+ }
+ else
+ {
+ if (!GTK_WIDGET_MAPPED(inputd))
+ gtk_widget_show(inputd);
+ else
+ gdk_window_raise(inputd->window);
+ }
+}
+</verb></tscreen>
+
+(Tome nota de la manera en que hemos manejado el cuadro de
+diálogo. Conectando la señal <tt/destroy/, nos aseguramos de que no
+tendremos un puntero al cuadro de diálogo después de que haya sido
+destruido, lo que nos podría llevar a un segfault.)
+
+<p>
+El InputDialog tiene dos botones «Cerrar» y «Guardar», que por
+defecto no tienen ninguna acción asignada. En la función anterior
+hemos hecho que «Cerrar» oculte el cuadro de diálogo, ocultando el
+botón «Guardar», ya que no implementaremos en este programa la
+acción de guardar las opciones de XInput.
+
+<sect2> Utilizando la información de los dispositivos extras
+
+<p>
+Una vez hemos activado el dispositivo, podemos utilizar la
+información que hay respecto a los dispositivos extendidos en los
+campos extras de las estructuras de los eventos. De hecho, es bueno
+utilizar esa información ya que esos campos tienen unos valores por
+defecto razonables aún cuando no se activen los eventos extendidos.
+
+<p>
+Un cambio que tenemos que hacer es llamar a
+<tt/gdk_input_window_get_pointer()/ en vez de a
+<tt/gdk_window_get_pointer/. Esto es necesario porque
+<tt/gdk_window_get_pointer/ no devuelve la información de los
+dispositivos extra.
+
+<tscreen><verb>
+void gdk_input_window_get_pointer (GdkWindow *ventana,
+ guint32 deviceid,
+ gdouble *x,
+ gdouble *y,
+ gdouble *pressure,
+ gdouble *xtilt,
+ gdouble *ytilt,
+ GdkModifierType *mask);
+</verb></tscreen>
+
+Cuando llamamos a esta función, necesitamos especificar tanto el ID
+del dispositivo como la ventana. Normalmente, obtendremos el ID del
+dispositivo del campo <tt/deviceid/ de una estructura de evento. De
+nuevo, esta función devolverá valores razonables cuando no estén
+activados los eventos extendidos. (En ese caso, <tt/event->deviceid/
+tendrá el valor <tt/GDK_CORE_POINTER/).
+
+Por tanto la estructura básica de nuestros manejadores de los
+eventos de movimiento y de pulsación del botón del ratón no
+cambiarán mucho - sólo tenemos que añadir código para manejar la
+información extra.
+
+<tscreen><verb>
+static gint
+button_press_event (GtkWidget *widget, GdkEventButton *event)
+{
+ print_button_press (event->deviceid);
+
+ if (event->button == 1 &amp;&amp; pixmap != NULL)
+ draw_brush (widget, event->source, event->x, event->y, event->pressure);
+
+ return TRUE;
+}
+
+static gint
+motion_notify_event (GtkWidget *widget, GdkEventMotion *event)
+{
+ gdouble x, y;
+ gdouble pressure;
+ GdkModifierType state;
+
+ if (event->is_hint)
+ gdk_input_window_get_pointer (event->window, event->deviceid,
+ &amp;x, &amp;y, &amp;pressure, NULL, NULL, &amp;state);
+ else
+ {
+ x = event->x;
+ y = event->y;
+ pressure = event->pressure;
+ state = event->state;
+ }
+
+ if (state &amp; GDK_BUTTON1_MASK &amp;&amp; pixmap != NULL)
+ draw_brush (widget, event->source, x, y, pressure);
+
+ return TRUE;
+}
+</verb></tscreen>
+
+También tenemos que hacer algo con la nueva información. Nuestra
+nueva función <tt/draw_brush()/ dibuja con un color diferente
+dependiendo de <tt/event->source/ y cambia el tamaño de la brocha
+dependiendo de la presión.
+
+<tscreen><verb>
+/* Draw a rectangle on the screen, size depending on pressure,
+ and color on the type of device */
+static void
+draw_brush (GtkWidget *widget, GdkInputSource source,
+ gdouble x, gdouble y, gdouble pressure)
+{
+ GdkGC *gc;
+ GdkRectangle update_rect;
+
+ switch (source)
+ {
+ case GDK_SOURCE_MOUSE:
+ gc = widget->style->dark_gc[GTK_WIDGET_STATE (widget)];
+ break;
+ case GDK_SOURCE_PEN:
+ gc = widget->style->black_gc;
+ break;
+ case GDK_SOURCE_ERASER:
+ gc = widget->style->white_gc;
+ break;
+ default:
+ gc = widget->style->light_gc[GTK_WIDGET_STATE (widget)];
+ }
+
+ update_rect.x = x - 10 * pressure;
+ update_rect.y = y - 10 * pressure;
+ update_rect.width = 20 * pressure;
+ update_rect.height = 20 * pressure;
+ gdk_draw_rectangle (pixmap, gc, TRUE,
+ update_rect.x, update_rect.y,
+ update_rect.width, update_rect.height);
+ gtk_widget_draw (widget, &amp;update_rect);
+}
+</verb></tscreen>
+
+<sect2> Obteniendo más información de un dispositivo
+
+<p>
+Como ejemplo de como podemos obtener más información de un
+dispositivo, nuestro programa imprimirá el nombre del dispositivo que
+genera cada pulsación de botón. Para encontrar el nombre de un
+dispositivo, llamaremos a la función:
+
+<tscreen><verb>
+GList *gdk_input_list_devices (void);
+</verb></tscreen>
+
+que devuelve una GList (una lista enlazada de la biblioteca glib)
+de estructuras <tt/GdkDeviceInfo/. La estructura <tt/GdkDeviceInfo/ se
+define como:
+
+<tscreen><verb>
+struct _GdkDeviceInfo
+{
+ guint32 deviceid;
+ gchar *name;
+ GdkInputSource source;
+ GdkInputMode mode;
+ gint has_cursor;
+ gint num_axes;
+ GdkAxisUse *axes;
+ gint num_keys;
+ GdkDeviceKey *keys;
+};
+</verb></tscreen>
+
+Muchos de estos campos son información de configuración que puede
+ignorar, a menos que quiera permitir la opción de grabar la
+configuración de XInput. El campo que nos interesa ahora es <tt/name/
+que es simplemente el nombre que X le asigna al dispositivo. El otro
+campo que no tiene información sobre la configuración es
+<tt/has_cursor/. Si <tt/has_cursor/ es falso, tendremos que dibujar
+nuestro propio cursor. Pero como hemos especificado
+<tt/GDK_EXTENSION_EVENTS_CURSOR/, no tendremos que preocuparnos por
+esto.
+
+<p>
+Nuestra función <tt/print_button_press()/ simplemente recorre la
+lista devuelta hasta que encuentra una coincidencia, y entonces
+imprime el nombre del dispositivo.
+
+<tscreen><verb>
+static void
+print_button_press (guint32 deviceid)
+{
+ GList *tmp_list;
+
+ /* gdk_input_list_devices returns an internal list, so we shouldn't
+ free it afterwards */
+ tmp_list = gdk_input_list_devices();
+
+ while (tmp_list)
+ {
+ GdkDeviceInfo *info = (GdkDeviceInfo *)tmp_list->data;
+
+ if (info->deviceid == deviceid)
+ {
+ printf("Button press on device '%s'\n", info->name);
+ return;
+ }
+
+ tmp_list = tmp_list->next;
+ }
+}
+</verb></tscreen>
+
+Con esto hemos completado los cambios para `XInputizar' nuestro
+programa. Como ocurría con la primera versión, el código completo se
+encuentra disponible en el mismo sitio donde obtuvo este tutorial, o
+desde:
+
+<htmlurl url="http://www.gtk.org/~otaylor/gtk/tutorial/"
+name="http://www.gtk.org/~otaylor/gtk/tutorial/">
+
+
+<sect2> Sofisticaciones adicionales <label id="sec_Further_Sophistications">
+
+<p>
+Aunque ahora nuestro programa admite XInput bastante bien, todavía
+falla en algunas características que deberían estar disponibles en una
+aplicación bien hecha. Primero, el usuario no debería tener que
+configurar su dispositivo cada vez que ejecute el programa, por lo que
+debería estar disponible la opción de guardar la configuración del
+dispositivo. Esto se hace recorriendo el resultado de
+<tt/gdk_input_list_devices()/ y escribiendo la configuración en un
+fichero.
+
+<p>
+Para cargar la configuración del dispositivo cuando se vuelva a
+ejecutar el programa, puede utilizar las funciones que proporciona GDK
+para cambiar la configuración de los dispositivos:
+
+<tscreen><verb>
+gdk_input_set_extension_events()
+gdk_input_set_source()
+gdk_input_set_mode()
+gdk_input_set_axes()
+gdk_input_set_key()
+</verb></tscreen>
+
+(La lista devuelta por <tt/gdk_input_list_devices()/ no debería
+modificarse directamente.) Podemos encontrar un ejemplo de como debe
+utilizarse en el programa de dibujo <tt/gsumi/. (Disponible en
+<htmlurl url="http://www.msc.cornell.edu/~otaylor/gsumi/"
+name="http://www.msc.cornell.edu/~otaylor/gsumi/">) Estaría bien
+tener un procedimiento estándar para poder hacer todo esto en
+cualquier aplicaciones. Probablemente se llegue a esto en una capa
+superior a GTK, quizás en la biblioteca GNOME.
+
+<p>
+El programa tiene otra carencia importante que ya hemos mencionado más
+arriba, y es la falta del cursor. Ninguna plataforma distinta de
+XFree86 permite utilizar simultaneamente un dispositivo como puntero
+núcleo y como dispositivo directamente utilizable por una
+aplicación. Ver el <url
+url="http://www.msc.cornell.edu/~otaylor/xinput/XInput-HOWTO.html"
+name="XInput-HOWTO"> para más información sobre esto. Con esto
+queremos decir que si quiere tener la máxima audiencia necesita
+dibujar su propio cursor.
+
+<p>
+Una aplicación que dibuja su propio cursor necesita hacer dos cosas:
+determinar si el dispositivo actual necesita que se dibuje un cursor o
+no, y determinar si el dispositivo está «próximo». (Si el
+dispositivo es una tableta digitalizadora, queda muy bonito que el
+cursor desaparezca cuando el lápiz se separa de la tableta. Cuando el
+lápiz está tocando la tableta, se dice que el dispositivo está
+«próximo»). Lo primero se hace buscando la lista de dispositivos,
+tal y como hicimos para encontrar el nombre del dispositivo. Lo
+segundo se consigue seleccionando los eventos
+<em/proximity_out/. Podemos encontrar un ejemplo de como dibujar
+nuestro propio cursor en el programa `testinput' que viene con la
+distribución de GTK.
+
+<!-- ***************************************************************** -->
+<sect>Trucos para escribir aplicaciones GTK
+<!-- ***************************************************************** -->
+
+<p>
+Esta sección es sólo un compendio de sabiduria, de guías generales
+de estilo y de consejos para crear buenas aplicaciones GTK. Y es
+totalmente inútil por ahora ya que esta frase es sólo un tópico :)
+
+¡Utilice GNU autoconf y automake! Son sus amigos :) Pretendo poner
+aquí una rápida introducción a ambos.
+
+<!-- ***************************************************************** -->
+<sect>Contribuyendo <label id="sec_Contributing">
+<!-- ***************************************************************** -->
+
+<p>
+Este documento, como muchos otros grandes paquetes de programas que
+hay por ahí, fue creado de forma libre por voluntarios. Si comprende
+algo de GTK que todavía no se ha documentado, por favor piense en
+contribuir a este documento.
+
+<p>
+Si decide contribuir, por favor mande un correo-e con su texto a Tony
+Gale, <tt><htmlurl url="mailto:gale@gtk.org"
+name="gale@gtk.org"></tt>. Recuerde que todas las partes que componen
+este documento son libre, y cualquier añadido que haga debe ser
+libre. Esto es, la gente debe de poder utilizar cualquier trozo de sus
+ejemplos en sus programas, podrán distribuir copias de su documento
+como deseen, etc...
+<p>
+Gracias.
+
+<!-- ***************************************************************** -->
+<sect>Créditos
+<!-- ***************************************************************** -->
+<p>
+Quiero agradecer a las siguientes personas por sus contribuciones a
+este texto.
+
+<itemize>
+<item>Bawer Dagdeviren, <tt><htmlurl url="mailto:chamele0n@geocities.com"
+name="chamele0n@geocities.com"></tt> por el tutorial sobre los menús.
+
+<item>Raph Levien, <tt><htmlurl url="mailto:raph@acm.org"
+name="raph@acm.org"></tt> por el «hola mundo» a la GTK, el
+empaquetado de <em/widgets/, y su sabiduría general. Ha donado
+generosamente un hogar para este tutorial.
+
+<item>Peter Mattis, <tt><htmlurl url="mailto:petm@xcf.berkeley.edu"
+name="petm@xcf.berkeley.edu"></tt> por el más simple de los programas
+GTK... y por la posibilidad de hacerlo :)
+
+<item>Werner Koch <tt><htmlurl url="mailto:werner.koch@guug.de"
+name="werner.koch@guug.de"></tt> por convertir el texto original a
+SGML, y por la jerarquia de clases de <em/widgets/.
+
+<item>Mark Crichton <tt><htmlurl url="mailto:crichton@expert.cc.purdue.edu"
+name="crichton@expert.cc.purdue.edu"></tt> por el código del menú
+factory, y el tutorial sobre el empaquetamiento de las tablas.
+
+<item>Owen Taylor <tt><htmlurl url="mailto:owt1@cornell.edu"
+name="owt1@cornell.edu"></tt> por la sección sobre el <em/widget/
+EventBox (y el parche para el distro). También es el responsable
+del código de las selecciones y el tutorial, así como de la
+sección de escribiendo su propio <em/widget/ GTK, y la aplicación de
+ejemplo. ¡Muchas gracias por toda tu ayuda, Owen!
+
+<item>Mark VanderBoom <tt><htmlurl url="mailto:mvboom42@calvin.edu"
+name="mvboom42@calvin.edu"></tt> por su fantástico trabajo sobre los
+<em/widgets/ Notebook, Progress Bar, Dialog, y selección de ficheros.
+¡Muchas gracias Mark!
+Has sido de una gran ayuda.
+
+<item>Tim Janik <tt><htmlurl url="mailto:timj@psynet.net"
+name="timj@psynet.net"></tt> por su gran trabajo en el <em/widget/ List.
+Gracias Tim :)
+
+<item>Rajat Datta <tt><htmlurl url="mailto:rajat@ix.netcom.com"
+name="rajat@ix.netcom.com"</tt> por el excelente trabajo con el
+tutorial Pixmap.
+
+<item>Michael K. Johnson <tt><htmlurl url="mailto:johnsonm@redhat.com"
+name="johnsonm@redhat.com"></tt> por la información y el código de
+los menús ("popup").
+
+<item>David Huggins-Daines <tt><htmlurl url="mailto:bn711@freenet.carleton.ca"
+name="bn711@freenet.carleton.ca"></tt> por las secciones sobre los
+<em/widgets/ Range y Tree.
+
+<item>Stefan Mars <tt><htmlurl url="mailto:mars@lysator.liu.se"
+name="mars@lysator.liu.se"></tt> por la sección GtkCList
+</itemize>
+<p>
+Y a todos los que han comentado y ayudado a refinar este documento.
+<p>
+Gracias.
+
+<!-- ***************************************************************** -->
+<sect> Copyright del Tutorial y notas sobre los permisos
+<!-- ***************************************************************** -->
+
+<p>
+Esta traducción está bajo la misma licencia bajo la que está
+el documento original. A continuación se presenta la traducción
+de la licencia y la licencia en versión original. En caso de haber
+alguna discrepancia entre la traducción y la licencia original, se
+aplicará esta última.
+
+El Tutorial GTK tiene Copyright (C) 1997 Ian Main.
+
+Copyright (C) 1998 Tony Gale.
+<p>
+Se da permiso para hacer y distribuir copias idénticas de este manual
+siempre que se incluya el copyright en todas las copias.
+<p>
+Se da permiso para copiar y distribuir versiones modificadas de este
+documento bajo las mismas condiciones que para las copias idénticas,
+siempre que el copyright se incluya exactamente tal y como se
+encuentra en el original, y que el trabajo completo derivado de este
+documento se distribuya bajo los términos de un permiso idéntico a
+éste.
+<P>
+Se da permiso para copiar y distribuir traducciones de este documento
+en otro lenguaje, bajo las condiciones arriba mencionadas para las
+versiones modificadas.
+<P>
+Si se propone incluir este documento en un trabajo que vaya a ser
+impreso, por favor contacte con el encargado del mantenimiento, y
+haremos un esfuerzo para asegurarnos de que dispone de la información
+lo más actualizada posible.
+<P>
+No hay ninguna garantia de que este documento se mantenga activo lo
+suficiente como para conseguir cumplir con su propósito. Se
+proporciona como un documento libre. Como tal, los autores y
+encargados del mantenimiento de la información que se da en el
+documento no pueden dar ninguna garantia de que la misma esté al día.
+<P>
+-----------------------------
+<p>
+The GTK Tutorial is Copyright (C) 1997 Ian Main.
+
+Copyright (C) 1998 Tony Gale.
+<p>
+Permission is granted to make and distribute verbatim copies of this
+manual provided the copyright notice and this permission notice are
+preserved on all copies.
+<P>Permission is granted to copy and distribute modified versions of
+this document under the conditions for verbatim copying, provided that
+this copyright notice is included exactly as in the original,
+and that the entire resulting derived work is distributed under
+the terms of a permission notice identical to this one.
+<P>Permission is granted to copy and distribute translations of this
+document into another language, under the above conditions for modified
+versions.
+<P>If you are intending to incorporate this document into a published
+work, please contact the maintainer, and we will make an effort
+to ensure that you have the most up to date information available.
+<P>There is no guarantee that this document lives up to its intended
+purpose. This is simply provided as a free resource. As such,
+the authors and maintainers of the information provided within can
+not make any guarantee that the information is even accurate.
+
+<sect1>Acerca de la traducción
+
+<p>
+Esta traduccion tiene copyright (C) 1999 de Joaquín Cuenca Abela
+<tt><htmlurl url="mailto:e98cuenc@criens.u-psud.fr"
+name="&lt;e98cuenc@criens.u-psud.fr&gt;"></tt>
+y de Eduardo Anglada Varela
+<tt><htmlurl url="mailto:eduardo.anglada@adi.uam.es"
+name="&lt;eduardo.anglada@adi.uam.es&gt;"></tt>.
+Si tiene cualquier
+duda, sugerencia o corrección no dude en consultarnos.
+
+Gracias a Manuel de Vega Barreiro <tt><htmlurl
+url="mailto:barreiro@arrakis.es"
+name="&lt;barreiro@arrakis.es&gt;"></tt> por haber hospedado las
+versiones beta y la versión actual de este tutorial en su página
+web Linux Landia <tt><url
+url="http://www.croftj.net/~barreiro/spain/gnome/"
+name="www.croftj.net/~barreiro/spain/gnome/"></tt>.
+
+</sect1>
+
+<!-- ***************************************************************** -->
+<appendix>
+<!-- ***************************************************************** -->
+
+<!-- ***************************************************************** -->
+<sect> Señales GTK <label id="sec_GTK_Signals">
+<!-- ***************************************************************** -->
+<p>
+GTK+, al ser un conjunto de <em/widgets/ orientado al objeto, tiene
+una jerarquía de herencias. Este mecanismo de herencia se aplica a las
+señales. Por eso, debe utilizar el árbol de jerarquías de los
+<em/widgets/ cuando utilice las señales que aparecen en esta sección.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GtkObject
+<!-- ----------------------------------------------------------------- -->
+<p>
+<tscreen><verb>
+void GtkObject::destroy (GtkObject *,
+ gpointer);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GtkWidget
+<!-- ----------------------------------------------------------------- -->
+<p>
+<tscreen><verb>
+
+void GtkWidget::show (GtkWidget *,
+ gpointer);
+void GtkWidget::hide (GtkWidget *,
+ gpointer);
+void GtkWidget::map (GtkWidget *,
+ gpointer);
+void GtkWidget::unmap (GtkWidget *,
+ gpointer);
+void GtkWidget::realize (GtkWidget *,
+ gpointer);
+void GtkWidget::unrealize (GtkWidget *,
+ gpointer);
+void GtkWidget::draw (GtkWidget *,
+ ggpointer,
+ gpointer);
+void GtkWidget::draw-focus (GtkWidget *,
+ gpointer);
+void GtkWidget::draw-default (GtkWidget *,
+ gpointer);
+void GtkWidget::size-request (GtkWidget *,
+ ggpointer,
+ gpointer);
+void GtkWidget::size-allocate (GtkWidget *,
+ ggpointer,
+ gpointer);
+void GtkWidget::state-changed (GtkWidget *,
+ GtkStateType,
+ gpointer);
+void GtkWidget::parent-set (GtkWidget *,
+ GtkObject *,
+ gpointer);
+void GtkWidget::style-set (GtkWidget *,
+ GtkStyle *,
+ gpointer);
+void GtkWidget::add-accelerator (GtkWidget *,
+ gguint,
+ GtkAccelGroup *,
+ gguint,
+ GdkModifierType,
+ GtkAccelFlags,
+ gpointer);
+void GtkWidget::remove-accelerator (GtkWidget *,
+ GtkAccelGroup *,
+ gguint,
+ GdkModifierType,
+ gpointer);
+gboolean GtkWidget::event (GtkWidget *,
+ GdkEvent *,
+ gpointer);
+gboolean GtkWidget::button-press-event (GtkWidget *,
+ GdkEvent *,
+ gpointer);
+gboolean GtkWidget::button-release-event (GtkWidget *,
+ GdkEvent *,
+ gpointer);
+gboolean GtkWidget::motion-notify-event (GtkWidget *,
+ GdkEvent *,
+ gpointer);
+gboolean GtkWidget::delete-event (GtkWidget *,
+ GdkEvent *,
+ gpointer);
+gboolean GtkWidget::destroy-event (GtkWidget *,
+ GdkEvent *,
+ gpointer);
+gboolean GtkWidget::expose-event (GtkWidget *,
+ GdkEvent *,
+ gpointer);
+gboolean GtkWidget::key-press-event (GtkWidget *,
+ GdkEvent *,
+ gpointer);
+gboolean GtkWidget::key-release-event (GtkWidget *,
+ GdkEvent *,
+ gpointer);
+gboolean GtkWidget::enter-notify-event (GtkWidget *,
+ GdkEvent *,
+ gpointer);
+gboolean GtkWidget::leave-notify-event (GtkWidget *,
+ GdkEvent *,
+ gpointer);
+gboolean GtkWidget::configure-event (GtkWidget *,
+ GdkEvent *,
+ gpointer);
+gboolean GtkWidget::focus-in-event (GtkWidget *,
+ GdkEvent *,
+ gpointer);
+gboolean GtkWidget::focus-out-event (GtkWidget *,
+ GdkEvent *,
+ gpointer);
+gboolean GtkWidget::map-event (GtkWidget *,
+ GdkEvent *,
+ gpointer);
+gboolean GtkWidget::unmap-event (GtkWidget *,
+ GdkEvent *,
+ gpointer);
+gboolean GtkWidget::property-notify-event (GtkWidget *,
+ GdkEvent *,
+ gpointer);
+gboolean GtkWidget::selection-clear-event (GtkWidget *,
+ GdkEvent *,
+ gpointer);
+gboolean GtkWidget::selection-request-event (GtkWidget *,
+ GdkEvent *,
+ gpointer);
+gboolean GtkWidget::selection-notify-event (GtkWidget *,
+ GdkEvent *,
+ gpointer);
+void GtkWidget::selection-get (GtkWidget *,
+ GtkSelectionData *,
+ gguint,
+ gpointer);
+void GtkWidget::selection-received (GtkWidget *,
+ GtkSelectionData *,
+ gguint,
+ gpointer);
+gboolean GtkWidget::proximity-in-event (GtkWidget *,
+ GdkEvent *,
+ gpointer);
+gboolean GtkWidget::proximity-out-event (GtkWidget *,
+ GdkEvent *,
+ gpointer);
+void GtkWidget::drag-begin (GtkWidget *,
+ GdkDragContext *,
+ gpointer);
+void GtkWidget::drag-end (GtkWidget *,
+ GdkDragContext *,
+ gpointer);
+void GtkWidget::drag-data-delete (GtkWidget *,
+ GdkDragContext *,
+ gpointer);
+void GtkWidget::drag-leave (GtkWidget *,
+ GdkDragContext *,
+ gguint,
+ gpointer);
+gboolean GtkWidget::drag-motion (GtkWidget *,
+ GdkDragContext *,
+ ggint,
+ ggint,
+ gguint,
+ gpointer);
+gboolean GtkWidget::drag-drop (GtkWidget *,
+ GdkDragContext *,
+ ggint,
+ ggint,
+ gguint,
+ gpointer);
+void GtkWidget::drag-data-get (GtkWidget *,
+ GdkDragContext *,
+ GtkSelectionData *,
+ gguint,
+ gguint,
+ gpointer);
+void GtkWidget::drag-data-received (GtkWidget *,
+ GdkDragContext *,
+ ggint,
+ ggint,
+ GtkSelectionData *,
+ gguint,
+ gguint,
+ gpointer);
+gboolean GtkWidget::client-event (GtkWidget *,
+ GdkEvent *,
+ gpointer);
+gboolean GtkWidget::no-expose-event (GtkWidget *,
+ GdkEvent *,
+ gpointer);
+gboolean GtkWidget::visibility-notify-event (GtkWidget *,
+ GdkEvent *,
+ gpointer);
+void GtkWidget::debug-msg (GtkWidget *,
+ GtkString *,
+ gpointer);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GtkData
+<!-- ----------------------------------------------------------------- -->
+<p>
+<tscreen><verb>
+void GtkData::disconnect (GtkData *,
+ gpointer);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GtkContainer
+<!-- ----------------------------------------------------------------- -->
+<p>
+<tscreen><verb>
+void GtkContainer::add (GtkContainer *,
+ GtkWidget *,
+ gpointer);
+void GtkContainer::remove (GtkContainer *,
+ GtkWidget *,
+ gpointer);
+void GtkContainer::check-resize (GtkContainer *,
+ gpointer);
+GtkDirectionType GtkContainer::focus (GtkContainer *,
+ GtkDirectionType,
+ gpointer);
+void GtkContainer::set-focus-child (GtkContainer *,
+ GtkWidget *,
+ gpointer);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GtkCalendar
+<!-- ----------------------------------------------------------------- -->
+<p>
+<tscreen><verb>
+void GtkCalendar::month-changed (GtkCalendar *,
+ gpointer);
+void GtkCalendar::day-selected (GtkCalendar *,
+ gpointer);
+void GtkCalendar::day-selected-double-click (GtkCalendar *,
+ gpointer);
+void GtkCalendar::prev-month (GtkCalendar *,
+ gpointer);
+void GtkCalendar::next-month (GtkCalendar *,
+ gpointer);
+void GtkCalendar::prev-year (GtkCalendar *,
+ gpointer);
+void GtkCalendar::next-year (GtkCalendar *,
+ gpointer);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GtkEditable
+<!-- ----------------------------------------------------------------- -->
+<p>
+<tscreen><verb>
+void GtkEditable::changed (GtkEditable *,
+ gpointer);
+void GtkEditable::insert-text (GtkEditable *,
+ GtkString *,
+ ggint,
+ ggpointer,
+ gpointer);
+void GtkEditable::delete-text (GtkEditable *,
+ ggint,
+ ggint,
+ gpointer);
+void GtkEditable::activate (GtkEditable *,
+ gpointer);
+void GtkEditable::set-editable (GtkEditable *,
+ gboolean,
+ gpointer);
+void GtkEditable::move-cursor (GtkEditable *,
+ ggint,
+ ggint,
+ gpointer);
+void GtkEditable::move-word (GtkEditable *,
+ ggint,
+ gpointer);
+void GtkEditable::move-page (GtkEditable *,
+ ggint,
+ ggint,
+ gpointer);
+void GtkEditable::move-to-row (GtkEditable *,
+ ggint,
+ gpointer);
+void GtkEditable::move-to-column (GtkEditable *,
+ ggint,
+ gpointer);
+void GtkEditable::kill-char (GtkEditable *,
+ ggint,
+ gpointer);
+void GtkEditable::kill-word (GtkEditable *,
+ ggint,
+ gpointer);
+void GtkEditable::kill-line (GtkEditable *,
+ ggint,
+ gpointer);
+void GtkEditable::cut-clipboard (GtkEditable *,
+ gpointer);
+void GtkEditable::copy-clipboard (GtkEditable *,
+ gpointer);
+void GtkEditable::paste-clipboard (GtkEditable *,
+ gpointer);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GtkTipsQuery
+<!-- ----------------------------------------------------------------- -->
+<p>
+<tscreen><verb>
+void GtkTipsQuery::start-query (GtkTipsQuery *,
+ gpointer);
+void GtkTipsQuery::stop-query (GtkTipsQuery *,
+ gpointer);
+void GtkTipsQuery::widget-entered (GtkTipsQuery *,
+ GtkWidget *,
+ GtkString *,
+ GtkString *,
+ gpointer);
+gboolean GtkTipsQuery::widget-selected (GtkTipsQuery *,
+ GtkWidget *,
+ GtkString *,
+ GtkString *,
+ GdkEvent *,
+ gpointer);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GtkCList
+<!-- ----------------------------------------------------------------- -->
+<p>
+<tscreen><verb>
+void GtkCList::select-row (GtkCList *,
+ ggint,
+ ggint,
+ GdkEvent *,
+ gpointer);
+void GtkCList::unselect-row (GtkCList *,
+ ggint,
+ ggint,
+ GdkEvent *,
+ gpointer);
+void GtkCList::row-move (GtkCList *,
+ ggint,
+ ggint,
+ gpointer);
+void GtkCList::click-column (GtkCList *,
+ ggint,
+ gpointer);
+void GtkCList::resize-column (GtkCList *,
+ ggint,
+ ggint,
+ gpointer);
+void GtkCList::toggle-focus-row (GtkCList *,
+ gpointer);
+void GtkCList::select-all (GtkCList *,
+ gpointer);
+void GtkCList::unselect-all (GtkCList *,
+ gpointer);
+void GtkCList::undo-selection (GtkCList *,
+ gpointer);
+void GtkCList::start-selection (GtkCList *,
+ gpointer);
+void GtkCList::end-selection (GtkCList *,
+ gpointer);
+void GtkCList::toggle-add-mode (GtkCList *,
+ gpointer);
+void GtkCList::extend-selection (GtkCList *,
+ GtkScrollType,
+ ggfloat,
+ gboolean,
+ gpointer);
+void GtkCList::scroll-vertical (GtkCList *,
+ GtkScrollType,
+ ggfloat,
+ gpointer);
+void GtkCList::scroll-horizontal (GtkCList *,
+ GtkScrollType,
+ ggfloat,
+ gpointer);
+void GtkCList::abort-column-resize (GtkCList *,
+ gpointer);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GtkNotebook
+<!-- ----------------------------------------------------------------- -->
+<p>
+<tscreen><verb>
+void GtkNotebook::switch-page (GtkNotebook *,
+ ggpointer,
+ gguint,
+ gpointer);
+
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GtkList
+<!-- ----------------------------------------------------------------- -->
+<p>
+<tscreen><verb>
+void GtkList::selection-changed (GtkList *,
+ gpointer);
+void GtkList::select-child (GtkList *,
+ GtkWidget *,
+ gpointer);
+void GtkList::unselect-child (GtkList *,
+ GtkWidget *,
+ gpointer);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GtkMenuShell
+<!-- ----------------------------------------------------------------- -->
+<p>
+<tscreen><verb>
+void GtkMenuShell::deactivate (GtkMenuShell *,
+ gpointer);
+void GtkMenuShell::selection-done (GtkMenuShell *,
+ gpointer);
+void GtkMenuShell::move-current (GtkMenuShell *,
+ GtkMenuDirectionType,
+ gpointer);
+void GtkMenuShell::activate-current (GtkMenuShell *,
+ gboolean,
+ gpointer);
+void GtkMenuShell::cancel (GtkMenuShell *,
+ gpointer);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GtkToolbar
+<!-- ----------------------------------------------------------------- -->
+<p>
+<tscreen><verb>
+void GtkToolbar::orientation-changed (GtkToolbar *,
+ ggint,
+ gpointer);
+void GtkToolbar::style-changed (GtkToolbar *,
+ ggint,
+ gpointer);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GtkTree
+<!-- ----------------------------------------------------------------- -->
+<p>
+<tscreen><verb>
+void GtkTree::selection-changed (GtkTree *,
+ gpointer);
+void GtkTree::select-child (GtkTree *,
+ GtkWidget *,
+ gpointer);
+void GtkTree::unselect-child (GtkTree *,
+ GtkWidget *,
+ gpointer);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GtkButton
+<!-- ----------------------------------------------------------------- -->
+<p>
+<tscreen><verb>
+void GtkButton::pressed (GtkButton *,
+ gpointer);
+void GtkButton::released (GtkButton *,
+ gpointer);
+void GtkButton::clicked (GtkButton *,
+ gpointer);
+void GtkButton::enter (GtkButton *,
+ gpointer);
+void GtkButton::leave (GtkButton *,
+ gpointer);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GtkItem
+<!-- ----------------------------------------------------------------- -->
+<p>
+<tscreen><verb>
+void GtkItem::select (GtkItem *,
+ gpointer);
+void GtkItem::deselect (GtkItem *,
+ gpointer);
+void GtkItem::toggle (GtkItem *,
+ gpointer);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GtkWindow
+<!-- ----------------------------------------------------------------- -->
+<p>
+<tscreen><verb>
+void GtkWindow::set-focus (GtkWindow *,
+ ggpointer,
+ gpointer);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GtkHandleBox
+<!-- ----------------------------------------------------------------- -->
+<p>
+<tscreen><verb>
+void GtkHandleBox::child-attached (GtkHandleBox *,
+ GtkWidget *,
+ gpointer);
+void GtkHandleBox::child-detached (GtkHandleBox *,
+ GtkWidget *,
+ gpointer);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GtkToggleButton
+<!-- ----------------------------------------------------------------- -->
+<p>
+<tscreen><verb>
+void GtkToggleButton::toggled (GtkToggleButton *,
+ gpointer);
+
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GtkMenuItem
+<!-- ----------------------------------------------------------------- -->
+<p>
+<tscreen><verb>
+void GtkMenuItem::activate (GtkMenuItem *,
+ gpointer);
+void GtkMenuItem::activate-item (GtkMenuItem *,
+ gpointer);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GtkListItem
+<!-- ----------------------------------------------------------------- -->
+<p>
+<tscreen><verb>
+void GtkListItem::toggle-focus-row (GtkListItem *,
+ gpointer);
+void GtkListItem::select-all (GtkListItem *,
+ gpointer);
+void GtkListItem::unselect-all (GtkListItem *,
+ gpointer);
+void GtkListItem::undo-selection (GtkListItem *,
+ gpointer);
+void GtkListItem::start-selection (GtkListItem *,
+ gpointer);
+void GtkListItem::end-selection (GtkListItem *,
+ gpointer);
+void GtkListItem::toggle-add-mode (GtkListItem *,
+ gpointer);
+void GtkListItem::extend-selection (GtkListItem *,
+ GtkEnum,
+ ggfloat,
+ gboolean,
+ gpointer);
+void GtkListItem::scroll-vertical (GtkListItem *,
+ GtkEnum,
+ ggfloat,
+ gpointer);
+void GtkListItem::scroll-horizontal (GtkListItem *,
+ GtkEnum,
+ ggfloat,
+ gpointer);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GtkTreeItem
+<!-- ----------------------------------------------------------------- -->
+<p>
+<tscreen><verb>
+void GtkTreeItem::collapse (GtkTreeItem *,
+ gpointer);
+void GtkTreeItem::expand (GtkTreeItem *,
+ gpointer);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GtkCheckMenuItem
+<!-- ----------------------------------------------------------------- -->
+<p>
+<tscreen><verb>
+void GtkCheckMenuItem::toggled (GtkCheckMenuItem *,
+ gpointer);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GtkInputDialog
+<!-- ----------------------------------------------------------------- -->
+<p>
+<tscreen><verb>
+void GtkInputDialog::enable-device (GtkInputDialog *,
+ ggint,
+ gpointer);
+void GtkInputDialog::disable-device (GtkInputDialog *,
+ ggint,
+ gpointer);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GtkColorSelection
+<!-- ----------------------------------------------------------------- -->
+<p>
+<tscreen><verb>
+void GtkColorSelection::color-changed (GtkColorSelection *,
+ gpointer);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GtkStatusBar
+<!-- ----------------------------------------------------------------- -->
+<p>
+<tscreen><verb>
+void GtkStatusbar::text-pushed (GtkStatusbar *,
+ gguint,
+ GtkString *,
+ gpointer);
+void GtkStatusbar::text-popped (GtkStatusbar *,
+ gguint,
+ GtkString *,
+ gpointer);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GtkCTree
+<!-- ----------------------------------------------------------------- -->
+<p>
+<tscreen><verb>
+void GtkCTree::tree-select-row (GtkCTree *,
+ GtkCTreeNode *,
+ ggint,
+ gpointer);
+void GtkCTree::tree-unselect-row (GtkCTree *,
+ GtkCTreeNode *,
+ ggint,
+ gpointer);
+void GtkCTree::tree-expand (GtkCTree *,
+ GtkCTreeNode *,
+ gpointer);
+void GtkCTree::tree-collapse (GtkCTree *,
+ ggpointer,
+ gpointer);
+void GtkCTree::tree-move (GtkCTree *,
+ GtkCTreeNode *,
+ GtkCTreeNode *,
+ GtkCTreeNode *,
+ gpointer);
+void GtkCTree::change-focus-row-expansion (GtkCTree *,
+ GtkCTreeExpansionType,
+ gpointer);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GtkCurve
+<!-- ----------------------------------------------------------------- -->
+<p>
+<tscreen><verb>
+void GtkCurve::curve-type-changed (GtkCurve *,
+ gpointer);
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>GtkAdjustment
+<!-- ----------------------------------------------------------------- -->
+<p>
+<tscreen><verb>
+void GtkAdjustment::changed (GtkAdjustment *,
+ gpointer);
+void GtkAdjustment::value-changed (GtkAdjustment *,
+ gpointer);
+</verb></tscreen>
+
+<!-- ***************************************************************** -->
+<sect> Tipos de eventos GDK<label id="sec_GDK_Event_Types">
+<!-- ***************************************************************** -->
+<p>
+Los siguientes tipos de datos se pasan en los manejadores de los
+eventos por GTK+. Para cada tipo de dato que se muestra, se muestran
+las señales que utilizan ese tipo de dato.
+
+<itemize>
+<item> GdkEvent
+ <itemize>
+ <item>drag_end_event
+ </itemize>
+
+<item> GdkEventType
+
+<item> GdkEventAny
+ <itemize>
+ <item>delete_event
+ <item>destroy_event
+ <item>map_event
+ <item>unmap_event
+ <item>no_expose_event
+ </itemize>
+
+<item> GdkEventExpose
+ <itemize>
+ <item>expose_event
+ </itemize>
+
+<item> GdkEventNoExpose
+
+<item> GdkEventVisibility
+
+<item> GdkEventMotion
+ <itemize>
+ <item>motion_notify_event
+ </itemize>
+
+<item> GdkEventButton
+ <itemize>
+ <item>button_press_event
+ <item>button_release_event
+ </itemize>
+
+<item> GdkEventKey
+ <itemize>
+ <item>key_press_event
+ <item>key_release_event
+ </itemize>
+
+<item> GdkEventCrossing
+ <itemize>
+ <item>enter_notify_event
+ <item>leave_notify_event
+ </itemize>
+
+<item> GdkEventFocus
+ <itemize>
+ <item>focus_in_event
+ <item>focus_out_event
+ </itemize>
+
+<item> GdkEventConfigure
+ <itemize>
+ <item>configure_event
+ </itemize>
+
+<item> GdkEventProperty
+ <itemize>
+ <item>property_notify_event
+ </itemize>
+
+<item> GdkEventSelection
+ <itemize>
+ <item>selection_clear_event
+ <item>selection_request_event
+ <item>selection_notify_event
+ </itemize>
+
+<item> GdkEventProximity
+ <itemize>
+ <item>proximity_in_event
+ <item>proximity_out_event
+ </itemize>
+
+<item> GdkEventDragBegin
+ <itemize>
+ <item>drag_begin_event
+ </itemize>
+
+<item> GdkEventDragRequest
+ <itemize>
+ <item>drag_request_event
+ </itemize>
+
+<item> GdkEventDropEnter
+ <itemize>
+ <item>drop_enter_event
+ </itemize>
+
+<item> GdkEventDropLeave
+ <itemize>
+ <item>drop_leave_event
+ </itemize>
+
+<item> GdkEventDropDataAvailable
+ <itemize>
+ <item>drop_data_available_event
+ </itemize>
+
+<item> GdkEventClient
+ <itemize>
+ <item>client_event
+ </itemize>
+
+<item> GdkEventOther
+ <itemize>
+ <item>other_event
+ </itemize>
+</itemize>
+
+El tipo de dato <tt/GdkEventType/ es un tipo de dato especial que se
+utiliza por todos los otros tipos de datos como un indicador del tipo
+de dato que se le está pasando al manejador de señal. Como verá
+más adelante, cada una de estructuras de los datos de los eventos
+tienen un miembro de este tipo. Se define como la siguiente
+enumeración:
+
+<tscreen><verb>
+typedef enum
+{
+ GDK_NOTHING = -1,
+ GDK_DELETE = 0,
+ GDK_DESTROY = 1,
+ GDK_EXPOSE = 2,
+ GDK_MOTION_NOTIFY = 3,
+ GDK_BUTTON_PRESS = 4,
+ GDK_2BUTTON_PRESS = 5,
+ GDK_3BUTTON_PRESS = 6,
+ GDK_BUTTON_RELEASE = 7,
+ GDK_KEY_PRESS = 8,
+ GDK_KEY_RELEASE = 9,
+ GDK_ENTER_NOTIFY = 10,
+ GDK_LEAVE_NOTIFY = 11,
+ GDK_FOCUS_CHANGE = 12,
+ GDK_CONFIGURE = 13,
+ GDK_MAP = 14,
+ GDK_UNMAP = 15,
+ GDK_PROPERTY_NOTIFY = 16,
+ GDK_SELECTION_CLEAR = 17,
+ GDK_SELECTION_REQUEST = 18,
+ GDK_SELECTION_NOTIFY = 19,
+ GDK_PROXIMITY_IN = 20,
+ GDK_PROXIMITY_OUT = 21,
+ GDK_DRAG_BEGIN = 22,
+ GDK_DRAG_REQUEST = 23,
+ GDK_DROP_ENTER = 24,
+ GDK_DROP_LEAVE = 25,
+ GDK_DROP_DATA_AVAIL = 26,
+ GDK_CLIENT_EVENT = 27,
+ GDK_VISIBILITY_NOTIFY = 28,
+ GDK_NO_EXPOSE = 29,
+ GDK_OTHER_EVENT = 9999 /* Anacrónico, utilice en su lugar los
+ filtros */
+} GdkEventType;
+</verb></tscreen>
+
+El otro tipo de evento que es diferente del resto es el mismo
+<tt/GdkEvent/. Ésta es una unión de todos los otros tipos de
+datos, que permite que se convierta en un tipo de dato de evento
+específico con un manejador de señal.
+
+<!-- Just a big list for now, needs expanding upon - TRG -->
+Por tanto, los tipos de los datos de los eventos se definen como
+sigue:
+
+<tscreen><verb>
+struct _GdkEventAny
+{
+ GdkEventType type;
+ GdkWindow *window;
+ gint8 send_event;
+};
+
+struct _GdkEventExpose
+{
+ GdkEventType type;
+ GdkWindow *window;
+ gint8 send_event;
+ GdkRectangle area;
+ gint count; /* Si count no es, entonces es el número de eventos que
+ * siguen. */
+};
+
+struct _GdkEventNoExpose
+{
+ GdkEventType type;
+ GdkWindow *window;
+ gint8 send_event;
+ /* XXX: ¿Hay alguien que necesite los campos major_code y minor_code
+ de X ? */
+};
+
+struct _GdkEventVisibility
+{
+ GdkEventType type;
+ GdkWindow *window;
+ gint8 send_event;
+ GdkVisibilityState state;
+};
+
+struct _GdkEventMotion
+{
+ GdkEventType type;
+ GdkWindow *window;
+ gint8 send_event;
+ guint32 time;
+ gdouble x;
+ gdouble y;
+ gdouble pressure;
+ gdouble xtilt;
+ gdouble ytilt;
+ guint state;
+ gint16 is_hint;
+ GdkInputSource source;
+ guint32 deviceid;
+ gdouble x_root, y_root;
+};
+
+struct _GdkEventButton
+{
+ GdkEventType type;
+ GdkWindow *window;
+ gint8 send_event;
+ guint32 time;
+ gdouble x;
+ gdouble y;
+ gdouble pressure;
+ gdouble xtilt;
+ gdouble ytilt;
+ guint state;
+ guint button;
+ GdkInputSource source;
+ guint32 deviceid;
+ gdouble x_root, y_root;
+};
+
+struct _GdkEventKey
+{
+ GdkEventType type;
+ GdkWindow *window;
+ gint8 send_event;
+ guint32 time;
+ guint state;
+ guint keyval;
+ gint length;
+ gchar *string;
+};
+
+struct _GdkEventCrossing
+{
+ GdkEventType type;
+ GdkWindow *window;
+ gint8 send_event;
+ GdkWindow *subwindow;
+ GdkNotifyType detail;
+};
+
+struct _GdkEventFocus
+{
+ GdkEventType type;
+ GdkWindow *window;
+ gint8 send_event;
+ gint16 in;
+};
+
+struct _GdkEventConfigure
+{
+ GdkEventType type;
+ GdkWindow *window;
+ gint8 send_event;
+ gint16 x, y;
+ gint16 width;
+ gint16 height;
+};
+
+struct _GdkEventProperty
+{
+ GdkEventType type;
+ GdkWindow *window;
+ gint8 send_event;
+ GdkAtom atom;
+ guint32 time;
+ guint state;
+};
+
+struct _GdkEventSelection
+{
+ GdkEventType type;
+ GdkWindow *window;
+ gint8 send_event;
+ GdkAtom selection;
+ GdkAtom target;
+ GdkAtom property;
+ guint32 requestor;
+ guint32 time;
+};
+
+/* Este tipo de evento se utiliza muy raramente. Solamente es
+ * importante para los programas que utilizan XInput y que dibujar su
+ * propio cursor */
+
+struct _GdkEventProximity
+{
+ GdkEventType type;
+ GdkWindow *window;
+ gint8 send_event;
+ guint32 time;
+ GdkInputSource source;
+ guint32 deviceid;
+};
+
+struct _GdkEventDragRequest
+{
+ GdkEventType type;
+ GdkWindow *window;
+ gint8 send_event;
+ guint32 requestor;
+ union {
+ struct {
+ guint protocol_version:4;
+ guint sendreply:1;
+ guint willaccept:1;
+ guint delete_data:1; /* No borrar si se ha mandado un enlace,
+ sólo si se ha mandado el dato */
+ guint senddata:1;
+ guint reserved:22;
+ } flags;
+ glong allflags;
+ } u;
+ guint8 isdrop; /* Este evento gdk puede ser generado por un par de
+ eventos X - esto le permite a las aplicaciones
+ saber si ha ocurrido realmente el soltado (drop),
+ o si sólo hemos cambiado el valor de algunos datos
+ */
+
+ GdkPoint drop_coords;
+ gchar *data_type;
+ guint32 timestamp;
+};
+
+struct _GdkEventDragBegin
+{
+ GdkEventType type;
+ GdkWindow *window;
+ gint8 send_event;
+ union {
+ struct {
+ guint protocol_version:4;
+ guint reserved:28;
+ } flags;
+ glong allflags;
+ } u;
+};
+
+struct _GdkEventDropEnter
+{
+ GdkEventType type;
+ GdkWindow *window;
+ gint8 send_event;
+ guint32 requestor;
+ union {
+ struct {
+ guint protocol_version:4;
+ guint sendreply:1;
+ guint extended_typelist:1;
+ guint reserved:26;
+ } flags;
+ glong allflags;
+ } u;
+};
+
+struct _GdkEventDropLeave
+{
+ GdkEventType type;
+ GdkWindow *window;
+ gint8 send_event;
+ guint32 requestor;
+ union {
+ struct {
+ guint protocol_version:4;
+ guint reserved:28;
+ } flags;
+ glong allflags;
+ } u;
+};
+
+struct _GdkEventDropDataAvailable
+{
+ GdkEventType type;
+ GdkWindow *window;
+ gint8 send_event;
+ guint32 requestor;
+ union {
+ struct {
+ guint protocol_version:4;
+ guint isdrop:1;
+ guint reserved:25;
+ } flags;
+ glong allflags;
+ } u;
+ gchar *data_type; /* tipo MIME */
+ gulong data_numbytes;
+ gpointer data;
+ guint32 timestamp;
+ GdkPoint coords;
+};
+
+struct _GdkEventClient
+{
+ GdkEventType type;
+ GdkWindow *window;
+ gint8 send_event;
+ GdkAtom message_type;
+ gushort data_format;
+ union {
+ char b[20];
+ short s[10];
+ long l[5];
+ } data;
+};
+
+struct _GdkEventOther
+{
+ GdkEventType type;
+ GdkWindow *window;
+ gint8 send_event;
+ GdkXEvent *xevent;
+};
+</verb></tscreen>
+
+<!-- ***************************************************************** -->
+<sect> Código ejemplo
+<!-- ***************************************************************** -->
+<p>
+A continuación tenemos el código ejemplo que se ha utilizado en el
+texto anterior y que no se ha incluido al completo en otro lugar.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1>Tictactoe
+<!-- ----------------------------------------------------------------- -->
+<sect2>tictactoe.h
+<p>
+<tscreen><verb>
+/* principio del ejemplo tictactoe tictactoe.h */
+
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifndef __TICTACTOE_H__
+#define __TICTACTOE_H__
+
+
+#include <gdk/gdk.h>
+#include <gtk/gtkvbox.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define TICTACTOE(obj) GTK_CHECK_CAST (obj, tictactoe_get_type (), Tictactoe)
+#define TICTACTOE_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, tictactoe_get_type (), TictactoeClass)
+#define IS_TICTACTOE(obj) GTK_CHECK_TYPE (obj, tictactoe_get_type ())
+
+
+typedef struct _Tictactoe Tictactoe;
+typedef struct _TictactoeClass TictactoeClass;
+
+struct _Tictactoe
+{
+ GtkVBox vbox;
+
+ GtkWidget *botones[3][3];
+};
+
+struct _TictactoeClass
+{
+ GtkVBoxClass parent_class;
+
+ void (* tictactoe) (Tictactoe *ttt);
+};
+
+guint tictactoe_get_type (void);
+GtkWidget* tictactoe_new (void);
+void tictactoe_clear (Tictactoe *ttt);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __TICTACTOE_H__ */
+
+/* fin del ejemplo */
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect2>tictactoe.c
+<p>
+<tscreen><verb>
+/* principio del ejemplo tictactoe tictactoe.c */
+
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#include "gtk/gtksignal.h"
+#include "gtk/gtktable.h"
+#include "gtk/gtktogglebutton.h"
+#include "tictactoe.h"
+
+enum {
+ TICTACTOE_SIGNAL,
+ LAST_SIGNAL
+};
+
+static void tictactoe_class_init (TictactoeClass *klass);
+static void tictactoe_init (Tictactoe *ttt);
+static void tictactoe_toggle (GtkWidget *widget, Tictactoe *ttt);
+
+static gint tictactoe_signals[LAST_SIGNAL] = { 0 };
+
+guint
+tictactoe_get_type ()
+{
+ static guint ttt_type = 0;
+
+ if (!ttt_type)
+ {
+ GtkTypeInfo ttt_info =
+ {
+ "Tictactoe",
+ sizeof (Tictactoe),
+ sizeof (TictactoeClass),
+ (GtkClassInitFunc) tictactoe_class_init,
+ (GtkObjectInitFunc) tictactoe_init,
+ (GtkArgSetFunc) NULL,
+ (GtkArgGetFunc) NULL
+ };
+
+ ttt_type = gtk_type_unique (gtk_vbox_get_type (), &amp;ttt_info);
+ }
+
+ return ttt_type;
+}
+
+static void
+tictactoe_class_init (TictactoeClass *class)
+{
+ GtkObjectClass *object_class;
+
+ object_class = (GtkObjectClass*) class;
+
+ tictactoe_signals[TICTACTOE_SIGNAL] = gtk_signal_new ("tictactoe",
+ GTK_RUN_FIRST,
+ object_class->type,
+ GTK_SIGNAL_OFFSET (TictactoeClass, tictactoe),
+ gtk_signal_default_marshaller, GTK_TYPE_NONE, 0);
+
+
+ gtk_object_class_add_signals (object_class, tictactoe_signals, LAST_SIGNAL);
+
+ class->tictactoe = NULL;
+}
+
+static void
+tictactoe_init (Tictactoe *ttt)
+{
+ GtkWidget *table;
+ gint i,j;
+
+ table = gtk_table_new (3, 3, TRUE);
+ gtk_container_add (GTK_CONTAINER(ttt), table);
+ gtk_widget_show (table);
+
+ for (i=0;i<3; i++)
+ for (j=0;j<3; j++)
+ {
+ ttt->buttons[i][j] = gtk_toggle_button_new ();
+ gtk_table_attach_defaults (GTK_TABLE(table), ttt->buttons[i][j],
+ i, i+1, j, j+1);
+ gtk_signal_connect (GTK_OBJECT (ttt->buttons[i][j]), "toggled",
+ GTK_SIGNAL_FUNC (tictactoe_toggle), ttt);
+ gtk_widget_set_usize (ttt->buttons[i][j], 20, 20);
+ gtk_widget_show (ttt->buttons[i][j]);
+ }
+}
+
+GtkWidget*
+tictactoe_new ()
+{
+ return GTK_WIDGET ( gtk_type_new (tictactoe_get_type ()));
+}
+
+void
+tictactoe_clear (Tictactoe *ttt)
+{
+ int i,j;
+
+ for (i=0;i<3;i++)
+ for (j=0;j<3;j++)
+ {
+ gtk_signal_handler_block_by_data (GTK_OBJECT(ttt->buttons[i][j]), ttt);
+ gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (ttt->buttons[i][j]),
+ FALSE);
+ gtk_signal_handler_unblock_by_data (GTK_OBJECT(ttt->buttons[i][j]), ttt);
+ }
+}
+
+static void
+tictactoe_toggle (GtkWidget *widget, Tictactoe *ttt)
+{
+ int i,k;
+
+ static int rwins[8][3] = { { 0, 0, 0 }, { 1, 1, 1 }, { 2, 2, 2 },
+ { 0, 1, 2 }, { 0, 1, 2 }, { 0, 1, 2 },
+ { 0, 1, 2 }, { 0, 1, 2 } };
+ static int cwins[8][3] = { { 0, 1, 2 }, { 0, 1, 2 }, { 0, 1, 2 },
+ { 0, 0, 0 }, { 1, 1, 1 }, { 2, 2, 2 },
+ { 0, 1, 2 }, { 2, 1, 0 } };
+
+ int success, found;
+
+ for (k=0; k<8; k++)
+ {
+ success = TRUE;
+ found = FALSE;
+
+ for (i=0;i<3;i++)
+ {
+ success = success &amp;&amp;
+ GTK_TOGGLE_BUTTON(ttt->buttons[rwins[k][i]][cwins[k][i]])->active;
+ found = found ||
+ ttt->buttons[rwins[k][i]][cwins[k][i]] == widget;
+ }
+
+ if (success &amp;&amp; found)
+ {
+ gtk_signal_emit (GTK_OBJECT (ttt),
+ tictactoe_signals[TICTACTOE_SIGNAL]);
+ break;
+ }
+ }
+}
+
+/* fin del ejemplo */
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect2>ttt_test.c
+<p>
+<tscreen><verb>
+/* principio del ejemplo tictactoe ttt_test.c */
+
+#include <gtk/gtk.h>
+#include "tictactoe.h"
+
+void
+win (GtkWidget *widget, gpointer data)
+{
+ g_print ("Yay!\n");
+ tictactoe_clear (TICTACTOE (widget));
+}
+
+int
+main (int argc, char *argv[])
+{
+ GtkWidget *ventana;
+ GtkWidget *ttt;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ gtk_window_set_title (GTK_WINDOW (ventana), "Aspect Frame");
+
+ gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
+ GTK_SIGNAL_FUNC (gtk_exit), NULL);
+
+ gtk_container_border_width (GTK_CONTAINER (ventana), 10);
+
+ ttt = tictactoe_new ();
+
+ gtk_container_add (GTK_CONTAINER (ventana), ttt);
+ gtk_widget_show (ttt);
+
+ gtk_signal_connect (GTK_OBJECT (ttt), "tictactoe",
+ GTK_SIGNAL_FUNC (win), NULL);
+
+ gtk_widget_show (ventana);
+
+ gtk_main ();
+
+ return 0;
+}
+
+/* fin del ejemplo */
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> GtkDial
+
+<!-- ----------------------------------------------------------------- -->
+<sect2> gtkdial.h
+<p>
+<tscreen><verb>
+/* principio del ejmplo gtkdial gtkdial.h */
+
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifndef __GTK_DIAL_H__
+#define __GTK_DIAL_H__
+
+
+#include <gdk/gdk.h>
+#include <gtk/gtkadjustment.h>
+#include <gtk/gtkwidget.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+#define GTK_DIAL(obj) GTK_CHECK_CAST (obj, gtk_dial_get_type (), GtkDial)
+#define GTK_DIAL_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, gtk_dial_get_type (), GtkDialClass)
+#define GTK_IS_DIAL(obj) GTK_CHECK_TYPE (obj, gtk_dial_get_type ())
+
+
+typedef struct _GtkDial GtkDial;
+typedef struct _GtkDialClass GtkDialClass;
+
+struct _GtkDial
+{
+ GtkWidget widget;
+
+ /* política de actualización
+ * (GTK_UPDATE_[CONTINUOUS/DELAYED/DISCONTINUOUS]) */
+ guint policy : 2;
+
+ /* Botón actualmente presionado o 0 si no hay ninguno */
+ guint8 boton;
+
+ /* Dimensión de los componendes del dial */
+ gint radius;
+ gint pointer_width;
+
+ /* ID del temporizador de actualización, o 0 si no hay ninguno */
+ guint32 timer;
+
+ /* ángulo actual */
+ gfloat angle;
+
+ /* Viejos valores almacenados del adjustment, para que así no
+ * tengamos que saber cuando cambia algo */
+ gfloat old_value;
+ gfloat old_lower;
+ gfloat old_upper;
+
+ /* El objeto adjustment que almacena los datos para este dial */
+ GtkAdjustment *adjustment;
+};
+
+struct _GtkDialClass
+{
+ GtkWidgetClass parent_class;
+};
+
+
+GtkWidget* gtk_dial_new (GtkAdjustment *adjustment);
+guint gtk_dial_get_type (void);
+GtkAdjustment* gtk_dial_get_adjustment (GtkDial *dial);
+void gtk_dial_set_update_policy (GtkDial *dial,
+ GtkUpdateType policy);
+
+void gtk_dial_set_adjustment (GtkDial *dial,
+ GtkAdjustment *adjustment);
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* __GTK_DIAL_H__ */
+/* fin del ejemplo */
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect2> gtkdial.c
+<p>
+<tscreen><verb>
+/* principio del ejemplo gtkdial gtkdial.c */
+
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#include <math.h>
+#include <stdio.h>
+#include <gtk/gtkmain.h>
+#include <gtk/gtksignal.h>
+
+#include "gtkdial.h"
+
+#define SCROLL_DELAY_LENGTH 300
+#define DIAL_DEFAULT_SIZE 100
+
+/* declaraciones de funciones */
+
+static void gtk_dial_class_init (GtkDialClass *klass);
+static void gtk_dial_init (GtkDial *dial);
+static void gtk_dial_destroy (GtkObject *object);
+static void gtk_dial_realize (GtkWidget *widget);
+static void gtk_dial_size_request (GtkWidget *widget,
+ GtkRequisition *requisition);
+static void gtk_dial_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation);
+static gint gtk_dial_expose (GtkWidget *widget,
+ GdkEventExpose *event);
+static gint gtk_dial_button_press (GtkWidget *widget,
+ GdkEventButton *event);
+static gint gtk_dial_button_release (GtkWidget *widget,
+ GdkEventButton *event);
+static gint gtk_dial_motion_notify (GtkWidget *widget,
+ GdkEventMotion *event);
+static gint gtk_dial_timer (GtkDial *dial);
+
+static void gtk_dial_update_mouse (GtkDial *dial, gint x, gint y);
+static void gtk_dial_update (GtkDial *dial);
+static void gtk_dial_adjustment_changed (GtkAdjustment *adjustment,
+ gpointer data);
+static void gtk_dial_adjustment_value_changed (GtkAdjustment *adjustment,
+ gpointer data);
+
+/* datos locales */
+
+static GtkWidgetClass *parent_class = NULL;
+
+guint
+gtk_dial_get_type ()
+{
+ static guint dial_type = 0;
+
+ if (!dial_type)
+ {
+ GtkTypeInfo dial_info =
+ {
+ "GtkDial",
+ sizeof (GtkDial),
+ sizeof (GtkDialClass),
+ (GtkClassInitFunc) gtk_dial_class_init,
+ (GtkObjectInitFunc) gtk_dial_init,
+ (GtkArgSetFunc) NULL,
+ (GtkArgGetFunc) NULL,
+ };
+
+ dial_type = gtk_type_unique (gtk_widget_get_type (), &amp;dial_info);
+ }
+
+ return dial_type;
+}
+
+static void
+gtk_dial_class_init (GtkDialClass *class)
+{
+ GtkObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+
+ object_class = (GtkObjectClass*) class;
+ widget_class = (GtkWidgetClass*) class;
+
+ parent_class = gtk_type_class (gtk_widget_get_type ());
+
+ object_class->destroy = gtk_dial_destroy;
+
+ widget_class->realize = gtk_dial_realize;
+ widget_class->expose_event = gtk_dial_expose;
+ widget_class->size_request = gtk_dial_size_request;
+ widget_class->size_allocate = gtk_dial_size_allocate;
+ widget_class->button_press_event = gtk_dial_button_press;
+ widget_class->button_release_event = gtk_dial_button_release;
+ widget_class->motion_notify_event = gtk_dial_motion_notify;
+}
+
+static void
+gtk_dial_init (GtkDial *dial)
+{
+ dial->button = 0;
+ dial->policy = GTK_UPDATE_CONTINUOUS;
+ dial->timer = 0;
+ dial->radius = 0;
+ dial->pointer_width = 0;
+ dial->angle = 0.0;
+ dial->old_value = 0.0;
+ dial->old_lower = 0.0;
+ dial->old_upper = 0.0;
+ dial->adjustment = NULL;
+}
+
+GtkWidget*
+gtk_dial_new (GtkAdjustment *adjustment)
+{
+ GtkDial *dial;
+
+ dial = gtk_type_new (gtk_dial_get_type ());
+
+ if (!adjustment)
+ adjustment = (GtkAdjustment*) gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
+
+ gtk_dial_set_adjustment (dial, adjustment);
+
+ return GTK_WIDGET (dial);
+}
+
+static void
+gtk_dial_destroy (GtkObject *object)
+{
+ GtkDial *dial;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GTK_IS_DIAL (object));
+
+ dial = GTK_DIAL (object);
+
+ if (dial->adjustment)
+ gtk_object_unref (GTK_OBJECT (dial->adjustment));
+
+ if (GTK_OBJECT_CLASS (parent_class)->destroy)
+ (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+GtkAdjustment*
+gtk_dial_get_adjustment (GtkDial *dial)
+{
+ g_return_val_if_fail (dial != NULL, NULL);
+ g_return_val_if_fail (GTK_IS_DIAL (dial), NULL);
+
+ return dial->adjustment;
+}
+
+void
+gtk_dial_set_update_policy (GtkDial *dial,
+ GtkUpdateType policy)
+{
+ g_return_if_fail (dial != NULL);
+ g_return_if_fail (GTK_IS_DIAL (dial));
+
+ dial->policy = policy;
+}
+
+void
+gtk_dial_set_adjustment (GtkDial *dial,
+ GtkAdjustment *adjustment)
+{
+ g_return_if_fail (dial != NULL);
+ g_return_if_fail (GTK_IS_DIAL (dial));
+
+ if (dial->adjustment)
+ {
+ gtk_signal_disconnect_by_data (GTK_OBJECT (dial->adjustment), (gpointer) dial);
+ gtk_object_unref (GTK_OBJECT (dial->adjustment));
+ }
+
+ dial->adjustment = adjustment;
+ gtk_object_ref (GTK_OBJECT (dial->adjustment));
+
+ gtk_signal_connect (GTK_OBJECT (adjustment), "changed",
+ (GtkSignalFunc) gtk_dial_adjustment_changed,
+ (gpointer) dial);
+ gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed",
+ (GtkSignalFunc) gtk_dial_adjustment_value_changed,
+ (gpointer) dial);
+
+ dial->old_value = adjustment->value;
+ dial->old_lower = adjustment->lower;
+ dial->old_upper = adjustment->upper;
+
+ gtk_dial_update (dial);
+}
+
+static void
+gtk_dial_realize (GtkWidget *widget)
+{
+ GtkDial *dial;
+ GdkWindowAttr attributes;
+ gint attributes_mask;
+
+ g_return_if_fail (widget != NULL);
+ g_return_if_fail (GTK_IS_DIAL (widget));
+
+ GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
+ dial = GTK_DIAL (widget);
+
+ attributes.x = widget->allocation.x;
+ attributes.y = widget->allocation.y;
+ attributes.width = widget->allocation.width;
+ attributes.height = widget->allocation.height;
+ attributes.wclass = GDK_INPUT_OUTPUT;
+ attributes.window_type = GDK_WINDOW_CHILD;
+ attributes.event_mask = gtk_widget_get_events (widget) |
+ GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK |
+ GDK_POINTER_MOTION_HINT_MASK;
+ attributes.visual = gtk_widget_get_visual (widget);
+ attributes.colormap = gtk_widget_get_colormap (widget);
+
+ attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+ widget->window = gdk_window_new (widget->parent->window, &amp;attributes, attributes_mask);
+
+ widget->style = gtk_style_attach (widget->style, widget->window);
+
+ gdk_window_set_user_data (widget->window, widget);
+
+ gtk_style_set_background (widget->style, widget->window, GTK_STATE_ACTIVE);
+}
+
+static void
+gtk_dial_size_request (GtkWidget *widget,
+ GtkRequisition *requisition)
+{
+ requisition->width = DIAL_DEFAULT_SIZE;
+ requisition->height = DIAL_DEFAULT_SIZE;
+}
+
+static void
+gtk_dial_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation)
+{
+ GtkDial *dial;
+
+ g_return_if_fail (widget != NULL);
+ g_return_if_fail (GTK_IS_DIAL (widget));
+ g_return_if_fail (allocation != NULL);
+
+ widget->allocation = *allocation;
+ dial = GTK_DIAL (widget);
+
+ if (GTK_WIDGET_REALIZED (widget))
+ {
+
+ gdk_window_move_resize (widget->window,
+ allocation->x, allocation->y,
+ allocation->width, allocation->height);
+
+ }
+ dial->radius = MIN(allocation->width,allocation->height) * 0.45;
+ dial->pointer_width = dial->radius / 5;
+}
+
+static gint
+gtk_dial_expose (GtkWidget *widget,
+ GdkEventExpose *event)
+{
+ GtkDial *dial;
+ GdkPoint points[3];
+ gdouble s,c;
+ gdouble theta;
+ gint xc, yc;
+ gint tick_length;
+ gint i;
+
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ if (event->count > 0)
+ return FALSE;
+
+ dial = GTK_DIAL (widget);
+
+ gdk_window_clear_area (widget->window,
+ 0, 0,
+ widget->allocation.width,
+ widget->allocation.height);
+
+ xc = widget->allocation.width/2;
+ yc = widget->allocation.height/2;
+
+ /* Dibuja las rayitas */
+
+ for (i=0; i<25; i++)
+ {
+ theta = (i*M_PI/18. - M_PI/6.);
+ s = sin(theta);
+ c = cos(theta);
+
+ tick_length = (i%6 == 0) ? dial->pointer_width : dial->pointer_width/2;
+
+ gdk_draw_line (widget->window,
+ widget->style->fg_gc[widget->state],
+ xc + c*(dial->radius - tick_length),
+ yc - s*(dial->radius - tick_length),
+ xc + c*dial->radius,
+ yc - s*dial->radius);
+ }
+
+ /* Dibuja el puntero */
+
+ s = sin(dial->angle);
+ c = cos(dial->angle);
+
+
+ points[0].x = xc + s*dial->pointer_width/2;
+ points[0].y = yc + c*dial->pointer_width/2;
+ points[1].x = xc + c*dial->radius;
+ points[1].y = yc - s*dial->radius;
+ points[2].x = xc - s*dial->pointer_width/2;
+ points[2].y = yc - c*dial->pointer_width/2;
+
+ gtk_draw_polygon (widget->style,
+ widget->window,
+ GTK_STATE_NORMAL,
+ GTK_SHADOW_OUT,
+ points, 3,
+ TRUE);
+
+ return FALSE;
+}
+
+static gint
+gtk_dial_button_press (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ GtkDial *dial;
+ gint dx, dy;
+ double s, c;
+ double d_parallel;
+ double d_perpendicular;
+
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ dial = GTK_DIAL (widget);
+
+
+ /* Determinar si la pulsación del botón fue dentro de la región del
+ puntero - esto lo hacemos calculando la distancia x e y del punto
+ donde se pulsó el botón ratón de la línea que se ha pasado mediante el
+ puntero */
+
+ dx = event->x - widget->allocation.width / 2;
+ dy = widget->allocation.height / 2 - event->y;
+
+ s = sin(dial->angle);
+ c = cos(dial->angle);
+
+ d_parallel = s*dy + c*dx;
+ d_perpendicular = fabs(s*dx - c*dy);
+
+ if (!dial->button &amp;&amp;
+ (d_perpendicular < dial->pointer_width/2) &amp;&amp;
+ (d_parallel > - dial->pointer_width))
+ {
+ gtk_grab_add (widget);
+
+ dial->button = event->button;
+
+ gtk_dial_update_mouse (dial, event->x, event->y);
+ }
+
+ return FALSE;
+}
+
+static gint
+gtk_dial_button_release (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ GtkDial *dial;
+
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ dial = GTK_DIAL (widget);
+
+ if (dial->button == event->button)
+ {
+ gtk_grab_remove (widget);
+
+ dial->button = 0;
+
+ if (dial->policy == GTK_UPDATE_DELAYED)
+ gtk_timeout_remove (dial->timer);
+
+ if ((dial->policy != GTK_UPDATE_CONTINUOUS) &amp;&amp;
+ (dial->old_value != dial->adjustment->value))
+ gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
+ }
+
+ return FALSE;
+}
+
+static gint
+gtk_dial_motion_notify (GtkWidget *widget,
+ GdkEventMotion *event)
+{
+ GtkDial *dial;
+ GdkModifierType mods;
+ gint x, y, mask;
+
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ dial = GTK_DIAL (widget);
+
+ if (dial->button != 0)
+ {
+ x = event->x;
+ y = event->y;
+
+ if (event->is_hint || (event->window != widget->window))
+ gdk_window_get_pointer (widget->window, &amp;x, &amp;y, &amp;mods);
+
+ switch (dial->button)
+ {
+ case 1:
+ mask = GDK_BUTTON1_MASK;
+ break;
+ case 2:
+ mask = GDK_BUTTON2_MASK;
+ break;
+ case 3:
+ mask = GDK_BUTTON3_MASK;
+ break;
+ default:
+ mask = 0;
+ break;
+ }
+
+ if (mods &amp; mask)
+ gtk_dial_update_mouse (dial, x,y);
+ }
+
+ return FALSE;
+}
+
+static gint
+gtk_dial_timer (GtkDial *dial)
+{
+ g_return_val_if_fail (dial != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_DIAL (dial), FALSE);
+
+ if (dial->policy == GTK_UPDATE_DELAYED)
+ gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
+
+ return FALSE;
+}
+
+static void
+gtk_dial_update_mouse (GtkDial *dial, gint x, gint y)
+{
+ gint xc, yc;
+ gfloat old_value;
+
+ g_return_if_fail (dial != NULL);
+ g_return_if_fail (GTK_IS_DIAL (dial));
+
+ xc = GTK_WIDGET(dial)->allocation.width / 2;
+ yc = GTK_WIDGET(dial)->allocation.height / 2;
+
+ old_value = dial->adjustment->value;
+ dial->angle = atan2(yc-y, x-xc);
+
+ if (dial->angle < -M_PI/2.)
+ dial->angle += 2*M_PI;
+
+ if (dial->angle < -M_PI/6)
+ dial->angle = -M_PI/6;
+
+ if (dial->angle > 7.*M_PI/6.)
+ dial->angle = 7.*M_PI/6.;
+
+ dial->adjustment->value = dial->adjustment->lower + (7.*M_PI/6 - dial->angle) *
+ (dial->adjustment->upper - dial->adjustment->lower) / (4.*M_PI/3.);
+
+ if (dial->adjustment->value != old_value)
+ {
+ if (dial->policy == GTK_UPDATE_CONTINUOUS)
+ {
+ gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
+ }
+ else
+ {
+ gtk_widget_draw (GTK_WIDGET(dial), NULL);
+
+ if (dial->policy == GTK_UPDATE_DELAYED)
+ {
+ if (dial->timer)
+ gtk_timeout_remove (dial->timer);
+
+ dial->timer = gtk_timeout_add (SCROLL_DELAY_LENGTH,
+ (GtkFunction) gtk_dial_timer,
+ (gpointer) dial);
+ }
+ }
+ }
+}
+
+static void
+gtk_dial_update (GtkDial *dial)
+{
+ gfloat new_value;
+
+ g_return_if_fail (dial != NULL);
+ g_return_if_fail (GTK_IS_DIAL (dial));
+
+ new_value = dial->adjustment->value;
+
+ if (new_value < dial->adjustment->lower)
+ new_value = dial->adjustment->lower;
+
+ if (new_value > dial->adjustment->upper)
+ new_value = dial->adjustment->upper;
+
+ if (new_value != dial->adjustment->value)
+ {
+ dial->adjustment->value = new_value;
+ gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
+ }
+
+ dial->angle = 7.*M_PI/6. - (new_value - dial->adjustment->lower) * 4.*M_PI/3. /
+ (dial->adjustment->upper - dial->adjustment->lower);
+
+ gtk_widget_draw (GTK_WIDGET(dial), NULL);
+}
+
+static void
+gtk_dial_adjustment_changed (GtkAdjustment *adjustment,
+ gpointer data)
+{
+ GtkDial *dial;
+
+ g_return_if_fail (adjustment != NULL);
+ g_return_if_fail (data != NULL);
+
+ dial = GTK_DIAL (data);
+
+ if ((dial->old_value != adjustment->value) ||
+ (dial->old_lower != adjustment->lower) ||
+ (dial->old_upper != adjustment->upper))
+ {
+ gtk_dial_update (dial);
+
+ dial->old_value = adjustment->value;
+ dial->old_lower = adjustment->lower;
+ dial->old_upper = adjustment->upper;
+ }
+}
+
+static void
+gtk_dial_adjustment_value_changed (GtkAdjustment *adjustment,
+ gpointer data)
+{
+ GtkDial *dial;
+
+ g_return_if_fail (adjustment != NULL);
+ g_return_if_fail (data != NULL);
+
+ dial = GTK_DIAL (data);
+
+ if (dial->old_value != adjustment->value)
+ {
+ gtk_dial_update (dial);
+
+ dial->old_value = adjustment->value;
+ }
+}
+/* fin del ejemplo */
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Scribble
+<p>
+<tscreen><verb>
+/* principio del ejemplo scribble-simple scribble-simple.c */
+
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <gtk/gtk.h>
+
+/* Creamos un backing pixmap para la zona donde dibujamos */
+static GdkPixmap *pixmap = NULL;
+
+/* Creamos un nuevo backing pixmap del tamaño apropiado */
+static gint
+configure_event (GtkWidget *widget, GdkEventConfigure *event)
+{
+ if (pixmap)
+ gdk_pixmap_unref(pixmap);
+
+ pixmap = gdk_pixmap_new(widget->window,
+ widget->allocation.width,
+ widget->allocation.height,
+ -1);
+ gdk_draw_rectangle (pixmap,
+ widget->style->white_gc,
+ TRUE,
+ 0, 0,
+ widget->allocation.width,
+ widget->allocation.height);
+
+ return TRUE;
+}
+
+/* Redibujamos la pantalla con el backing pixmap */
+static gint
+expose_event (GtkWidget *widget, GdkEventExpose *event)
+{
+ gdk_draw_pixmap(widget->window,
+ widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
+ pixmap,
+ event->area.x, event->area.y,
+ event->area.x, event->area.y,
+ event->area.width, event->area.height);
+
+ return FALSE;
+}
+
+/* Dibujamos un rectángulo en la pantalla */
+static void
+draw_brush (GtkWidget *widget, gdouble x, gdouble y)
+{
+ GdkRectangle update_rect;
+
+ update_rect.x = x - 5;
+ update_rect.y = y - 5;
+ update_rect.width = 10;
+ update_rect.height = 10;
+ gdk_draw_rectangle (pixmap,
+ widget->style->black_gc,
+ TRUE,
+ update_rect.x, update_rect.y,
+ update_rect.width, update_rect.height);
+ gtk_widget_draw (widget, &amp;update_rect);
+}
+
+static gint
+button_press_event (GtkWidget *widget, GdkEventButton *event)
+{
+ if (event->button == 1 &amp;&amp; pixmap != NULL)
+ draw_brush (widget, event->x, event->y);
+
+ return TRUE;
+}
+
+static gint
+motion_notify_event (GtkWidget *widget, GdkEventMotion *event)
+{
+ int x, y;
+ GdkModifierType state;
+
+ if (event->is_hint)
+ gdk_window_get_pointer (event->window, &amp;x, &amp;y, &amp;state);
+ else
+ {
+ x = event->x;
+ y = event->y;
+ state = event->state;
+ }
+
+ if (state &amp; GDK_BUTTON1_MASK &amp;&amp; pixmap != NULL)
+ draw_brush (widget, x, y);
+
+ return TRUE;
+}
+
+void
+quit ()
+{
+ gtk_exit (0);
+}
+
+int
+main (int argc, char *argv[])
+{
+ GtkWidget *ventana;
+ GtkWidget *drawing_area;
+ GtkWidget *vbox;
+
+ GtkWidget *boton;
+
+ gtk_init (&amp;argc, &amp;argv);
+
+ ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_widget_set_name (ventana, "Test Input");
+
+ vbox = gtk_vbox_new (FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (ventana), vbox);
+ gtk_widget_show (vbox);
+
+ gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
+ GTK_SIGNAL_FUNC (quit), NULL);
+
+ /* Crear la zona de dibujado */
+
+ drawing_area = gtk_drawing_area_new ();
+ gtk_drawing_area_size (GTK_DRAWING_AREA (drawing_area), 200, 200);
+ gtk_box_pack_start (GTK_BOX (vbox), drawing_area, TRUE, TRUE, 0);
+
+ gtk_widget_show (drawing_area);
+
+ /* Las señales utilizadas para manejar el backing pixmap */
+
+ gtk_signal_connect (GTK_OBJECT (drawing_area), "expose_event",
+ (GtkSignalFunc) expose_event, NULL);
+ gtk_signal_connect (GTK_OBJECT(drawing_area),"configure_event",
+ (GtkSignalFunc) configure_event, NULL);
+
+ /* Señales evento */
+
+ gtk_signal_connect (GTK_OBJECT (drawing_area), "motion_notify_event",
+ (GtkSignalFunc) motion_notify_event, NULL);
+ gtk_signal_connect (GTK_OBJECT (drawing_area), "button_press_event",
+ (GtkSignalFunc) button_press_event, NULL);
+
+ gtk_widget_set_events (drawing_area, GDK_EXPOSURE_MASK
+ | GDK_LEAVE_NOTIFY_MASK
+ | GDK_BUTTON_PRESS_MASK
+ | GDK_POINTER_MOTION_MASK
+ | GDK_POINTER_MOTION_HINT_MASK);
+
+ /* .. Y un botón para salir */
+ boton = gtk_button_new_with_label ("Quit");
+ gtk_box_pack_start (GTK_BOX (vbox), boton, FALSE, FALSE, 0);
+
+ gtk_signal_connect_object (GTK_OBJECT (boton), "clicked",
+ GTK_SIGNAL_FUNC (gtk_widget_destroy),
+ GTK_OBJECT (ventana));
+ gtk_widget_show (boton);
+
+ gtk_widget_show (ventana);
+
+ gtk_main ();
+
+ return 0;
+}
+/* fin del ejemplo */
+</verb></tscreen>
+
+<!-- ***************************************************************** -->
+<sect> El <em>widget</em> lista
+<!-- ***************************************************************** -->
+<p>
+ATENCIÓN: El <em>widget</em> GtkList ha sido reemplazado por el
+<em>widget</em> GtkCList.
+
+El <em>widget</em> GtkList está diseñado para actuar como un
+contenedor vertical de <em>widgets</em> que deben ser del tipo
+GtkListItem.
+
+Un <em>widget</em> GtkList tiene su propia ventana para recibir
+eventos y su propio color de fondo, que normalmente es blanco.
+Como es un objeto derivado directamente de GtkContainer puede tratarse
+utilizando la macro GTK_CONTAINER(List), ver el <em>widget</em>
+GtkContainer para obtener más información.
+
+Debería familiarizarse con la utilización de un GList y con sus
+funciones relacionadas <tt/g_list_*()/ para ser capaz de explotar el
+<em>widget</em> GtkList hasta su límite.
+
+Sólo hay un campo dentro de la definición de la estructura del
+<em>widget</em> GtkList que nos es de interés, y es:
+
+<tscreen><verb>
+struct _GtkList
+{
+ ...
+ GList *selection;
+ guint selection_mode;
+ ...
+};
+</verb></tscreen>
+
+El campo <tt/selection/ de un GtkList apunta a una lista enlazada de
+todos los elementos que están actualmente seleccionados, o NULL si la
+selección está vacia. Por lo tanto para saber quien es la actual
+selección debemos leer el campo <tt/GTK_LIST()->selection/, pero no
+modificarlo ya que los campos de los que está constituido GtkList
+están controlados por las funciones gtk_list_*().
+
+El <tt/selection_mode/ de la GtkList determina las posibilidades de
+selección de una GtkList y por tanto los contenidos del campo
+<tt/GTK_LIST()->selection/. El <tt/selection_mode/ puede tener uno de
+los valores siguientes:
+
+<itemize>
+<item> GTK_SELECTION_SINGLE - La selección es o NULL o contiene un
+puntero a un GList con un solo elemento seleccionado.
+
+<item> GTK_SELECTION_BROWSE - La selección es NULL si la lista no
+contiene <em>widgets</em> o si los que contiene no son sensibles, en
+cualquier otro caso contiene un puntero GList a una estructura GList,
+y contendrá por tanto un solo elemento.
+
+<item> GTK_SELECTION_MULTIPLE - La selección es NULL si no hay
+elementos seleccionados o un puntero GList hacia el primer elemento
+seleccionado. ("That in turn") apunta a una estructura GList para
+el segundo elemento seleccionado y así.
+
+<item> GTK_SELECTION_EXTENDED - La selección siempre es NULL.
+</itemize>
+
+El valor por defecto es GTK_SELECTION_MULTIPLE.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Señales
+<p>
+<tscreen><verb>
+void selection_changed( GtkList *list );
+</verb></tscreen>
+
+Se invocará esta señal cuando cambie el campo <tt/selection/ de un
+GtkList. Es decir, cuando un hijo de una GtkList se selecciona o
+deselecciona.
+
+<tscreen><verb>
+void select_child( GtkList *list,
+ GtkWidget *hijo);
+</verb></tscreen>
+
+Se invoca esta señal cuando un hijo de la GtkList está siendo
+seleccionado. Esto ocurre principalmente en llamadas a
+<tt/gtk_list_select_item()/, a <tt/gtk_list_select_child()/, cuando se
+pulsa algún botón y a veces se lanza indirectamente cuando se añade o
+se elimina un hijo del GtkList.
+
+<tscreen><verb>
+void unselect_child( GtkList *list,
+ GtkWidget *hijo );
+</verb></tscreen>
+
+Se invoca esta señal cuando un hijo del GtkList está siendo
+deseleccionado. Esto ocurre principalmente cuando ocurre una llamada a
+<tt/gtk_list_unselect_item()/, <tt/gtk_list_unselect_item()/,
+pulsaciones de botón y a veces se lanza indirectamente cuando se añade
+o se elimina algún hijo de la GtkList.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Funciones
+<p>
+<tscreen><verb>
+guint gtk_list_get_type( void );
+</verb></tscreen>
+
+Devuelve el identificador de tipo `GtkList'.
+
+<tscreen><verb>
+GtkWidget *gtk_list_new( void );
+</verb></tscreen>
+
+Crea un nuevo objeto GtkList. Se devuelve el nuevo <em/widget/ como un
+puntero a un objeto GtkWidget. Se devuelve NULL en caso de producirse
+algún fallo.
+
+<tscreen><verb>
+void gtk_list_insert_items( GtkList *list,
+ GList *items,
+ gint posicion );
+</verb></tscreen>
+
+Introduce elementos en la lista, comenzando en la posición
+<tt/posicion/. <tt/items/ es una lista doblemente enlazada donde cada
+puntero de datos de cada nodo se supone que apunta a una nueva
+GtkListItem (recien creada). Los nodos GList de <tt/items/ son
+controlados por la lista.
+
+<tscreen><verb>
+void gtk_list_append_items( GtkList *list,
+ GList *items);
+</verb></tscreen>
+
+Introduce elementos tal y como lo hace <tt/gtk_list_insert_items()/,
+pero los mete en el final de la lista. Los nodos GList de <tt/items/
+son controlados por la lista.
+
+<tscreen><verb>
+void gtk_list_prepend_items( GtkList *list,
+ GList *items);
+</verb></tscreen>
+
+Introduce elementos tal y como lo hace <tt/gtk_list_insert_items()/,
+pero los mete al principio de la lista. Los nodos GList de <tt/items/
+son controlados por la lista.
+
+<tscreen><verb>
+void gtk_list_remove_items( GtkList *list,
+ GList *items);
+</verb></tscreen>
+
+Elimina elementos de la lista. <tt/items/ es una lista doblemente
+enlazada donde cada puntero de datos de cada nodo se supone que apunta
+a un hijo directo de la lista. El ejecutar o no
+<tt/g_list_free(items)/ cuando la función termine de ejecutarse es
+responsabilidad del que llama a la misma. Está bajo su responsabilidad
+la destrucción de los elementos de la lista.
+
+<tscreen><verb>
+void gtk_list_clear_items( GtkList *list,
+ gint start,
+ gint end );
+</verb></tscreen>
+
+Elimina y destruye los elementos de la lista. Esta operación afectará
+a todos los <em/widgets/ que se encuentren en la lista y en el rango
+especificado por <tt/start/ y <tt/end/.
+
+<tscreen><verb>
+void gtk_list_select_item( GtkList *list,
+ gint item );
+</verb></tscreen>
+
+Invoca la señal <tt/select_child/ para el elemento especificado
+mediante su posición actual en la lista.
+
+<tscreen><verb>
+void gtk_list_unselect_item( GtkList *list,
+ gint item);
+</verb></tscreen>
+
+Invoca la señal <tt/unselect_child/ para un elemento especificado
+mediante su posición actual en la lista.
+
+<tscreen><verb>
+void gtk_list_select_child( GtkList *list,
+ GtkWidget *hijo);
+</verb></tscreen>
+
+Invoca la señal <tt/select_child/ para el hijo especificado.
+
+<tscreen><verb>
+void gtk_list_unselect_child( GtkList *list,
+ GtkWidget *hijo);
+</verb></tscreen>
+
+Invoca la señal <tt/unselect_child/ para el hijo especificado.
+
+<tscreen><verb>
+gint gtk_list_child_position( GtkList *list,
+ GtkWidget *hijo);
+</verb></tscreen>
+
+Devuelve la posición de <tt/hijo/ en la lista. Se devuelve «-1» en
+caso de producirse algún error.
+
+<tscreen><verb>
+void gtk_list_set_selection_mode( GtkList *list,
+ GtkSelectionMode mode );
+</verb></tscreen>
+
+Pone el modo de selección, que puede ser <tt/GTK_SELECTION_SINGLE/,
+<tt/GTK_SELECTION_BROWSE/, <tt/GTK_SELECTION_MULTIPLE/ o
+<tt/GTK_SELECTION_EXTENDED/.
+
+<tscreen><verb>
+GtkList *GTK_LIST( gpointer obj );
+</verb></tscreen>
+
+Convierte un puntero general en `GtkList *'. Para más información *Note
+Standard Macros::.
+
+<tscreen><verb>
+GtkListClass *GTK_LIST_CLASS( gpointer class);
+</verb></tscreen>
+
+Convierte un puntero general en `GtkListClass *'. Para más información
+*Note Standard Macros::.
+
+<tscreen><verb>
+gint GTK_IS_LIST( gpointer obj);
+</verb></tscreen>
+
+Determina si un puntero general se refiere a un objeto `GtkList'. Para
+más información, *Note Standard Macros::.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Ejemplo
+<p>
+A continuación tenemos un programa ejemplo que muestra los cambios de
+la selección de un GtkList, y le deja «arrestar» elementos de la
+lista en una prisión, seleccionándolos con el botón derecho del ratón.
+
+<tscreen><verb>
+/* principio del ejemplo list list.c */
+
+/* incluye los ficheros de cabecera de gtk+
+ * incluye stdio.h, que necesitamos para la función printf()
+ */
+#include <gtk/gtk.h>
+#include <stdio.h>
+
+/* ésta es nuestra cadena de identificación para almacenar datos en la
+ * lista de elementos
+ */
+const gchar *list_item_data_key="list_item_data";
+
+
+/* prototipos para los manejadores de señal que vamos a conectar con
+ * el widget GtkList
+ */
+static void sigh_print_selection (GtkWidget *gtklist,
+ gpointer func_data);
+static void sigh_button_event (GtkWidget *gtklist,
+ GdkEventButton *event,
+ GtkWidget *frame);
+
+
+/* función principal donde se establece el interface con el usuario */
+
+gint main (int argc, gchar *argv[])
+{
+ GtkWidget *separator;
+ GtkWidget *ventana;
+ GtkWidget *vbox;
+ GtkWidget *scrolled_window;
+ GtkWidget *frame;
+ GtkWidget *gtklist;
+ GtkWidget *boton;
+ GtkWidget *list_item;
+ GList *dlist;
+ guint i;
+ gchar buffer[64];
+
+
+ /* inicializar gtk+ (y consecuentemente gdk) */
+
+ gtk_init(&amp;argc, &amp;argv);
+
+
+ /* crear una ventana donde meter todos los widgets y conectar
+ * gtk_main_quit() con el evento "destroy" de la ventana para
+ * poder controlar los eventos de cerrado de ventana del
+ * administrador de ventanas
+ */
+ ventana=gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title(GTK_WINDOW(ventana), "GtkList Example");
+ gtk_signal_connect(GTK_OBJECT(ventana),
+ "destroy",
+ GTK_SIGNAL_FUNC(gtk_main_quit),
+ NULL);
+
+
+ /* dentro de la ventana necesitamos una caja para alinear los
+ * widgets verticalmente */
+ vbox=gtk_vbox_new(FALSE, 5);
+ gtk_container_border_width(GTK_CONTAINER(vbox), 5);
+ gtk_container_add(GTK_CONTAINER(ventana), vbox);
+ gtk_widget_show(vbox);
+
+ /* Ésta es la ventana con barras de desplazamiento donde meteremos
+ * el widget GtkList */
+ scrolled_window=gtk_scrolled_window_new(NULL, NULL);
+ gtk_widget_set_usize(scrolled_window, 250, 150);
+ gtk_container_add(GTK_CONTAINER(vbox), scrolled_window);
+ gtk_widget_show(scrolled_window);
+
+ /* crear el widget GtkList
+ * conectar la función manipuladora de señal
+ * sigh_print_selection() a la señal "selection_changed" del
+ * GtkList para imprimir los elementos seleccionados cada vez que
+ * cambie la selección */
+ gtklist=gtk_list_new();
+ gtk_container_add(GTK_CONTAINER(scrolled_window), gtklist);
+ gtk_widget_show(gtklist);
+ gtk_signal_connect(GTK_OBJECT(gtklist),
+ "selection_changed",
+ GTK_SIGNAL_FUNC(sigh_print_selection),
+ NULL);
+
+ /* creamos una "Prisión" donde meteremos una lista de elementos ;)
+ */
+ frame=gtk_frame_new("Prison");
+ gtk_widget_set_usize(frame, 200, 50);
+ gtk_container_border_width(GTK_CONTAINER(frame), 5);
+ gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);
+ gtk_container_add(GTK_CONTAINER(vbox), frame);
+ gtk_widget_show(frame);
+
+ /* conectamos el manipulador de señal sigh_button_event() al
+ * GtkList que manejará la lista de elementos "arrestados"
+ */
+ gtk_signal_connect(GTK_OBJECT(gtklist),
+ "button_release_event",
+ GTK_SIGNAL_FUNC(sigh_button_event),
+ frame);
+
+ /* crear un separador
+ */
+ separator=gtk_hseparator_new();
+ gtk_container_add(GTK_CONTAINER(vbox), separator);
+ gtk_widget_show(separator);
+
+ /* crear finalmente un botón y conectar su señal "clicked" con la
+ * destrucción de la ventana
+ */
+ boton=gtk_button_new_with_label("Close");
+ gtk_container_add(GTK_CONTAINER(vbox), boton);
+ gtk_widget_show(boton);
+ gtk_signal_connect_object(GTK_OBJECT(boton),
+ "clicked",
+ GTK_SIGNAL_FUNC(gtk_widget_destroy),
+ GTK_OBJECT(ventana));
+
+
+ /* ahora creamos 5 elementos de lista, teniendo cada uno su propia
+ * etiqueta y añadiéndolos a la GtkList mediante
+ * gtk_container_add() también consultaremos la cadena de texto de
+ * la etiqueta y la asociaremos con la list_item_data_key para
+ * cada elemento de la lista
+ */
+ for (i=0; i<5; i++) {
+ GtkWidget *etiqueta;
+ gchar *string;
+
+ sprintf(buffer, "ListItemContainer with Label #%d", i);
+ etiqueta=gtk_label_new(buffer);
+ list_item=gtk_list_item_new();
+ gtk_container_add(GTK_CONTAINER(list_item), etiqueta);
+ gtk_widget_show(etiqueta);
+ gtk_container_add(GTK_CONTAINER(gtklist), list_item);
+ gtk_widget_show(list_item);
+ gtk_label_get(GTK_LABEL(etiqueta), &amp;string);
+ gtk_object_set_data(GTK_OBJECT(list_item),
+ list_item_data_key,
+ string);
+ }
+ /* aquí, estamos creando otras 5 etiquetas, esta vez utilizaremos
+ * gtk_list_item_new_with_label() para la creación
+ * no podemos consultar la cadena de texto de la etiqueta ya que
+ * no tenemos el puntero de etiquetas y por tanto lo único que
+ * haremos será asociar el list_item_data_key de cada elemento de
+ * la lista con la misma cadena de texto. Para añadirlo a la lista
+ * de elementos los pondremos en lista doblemente enlazada
+ * (GList), y entonces los añadimos mediante una simple llamada a
+ * gtk_list_append_items()
+ * como utilizamos g_list_prepend() para poner los elementos en la
+ * lista doblemente enlazada, su orden será descendente (en vez de
+ * ascendente como cuando utilizamos g_list_append())
+ */
+ dlist=NULL;
+ for (; i<10; i++) {
+ sprintf(buffer, "List Item with Label %d", i);
+ list_item=gtk_list_item_new_with_label(buffer);
+ dlist=g_list_prepend(dlist, list_item);
+ gtk_widget_show(list_item);
+ gtk_object_set_data(GTK_OBJECT(list_item),
+ list_item_data_key,
+ "ListItem with integrated Label");
+ }
+ gtk_list_append_items(GTK_LIST(gtklist), dlist);
+
+ /* finalmente queremos ver la ventana, ¿verdad? ;)
+ */
+ gtk_widget_show(ventana);
+
+ /* y nos metemos en el bucle de eventos de gtk
+ */
+ gtk_main();
+
+ /* llegaremos aquí después de que se llame a gtk_main_quit(), lo
+ * que ocurre si se destruye la ventana
+ */
+ return 0;
+}
+
+/* éste es el manejador de señal que se conectó a los eventos de
+ * pulsar/soltar de los botones de la GtkList
+ */
+void
+sigh_button_event (GtkWidget *gtklist,
+ GdkEventButton *event,
+ GtkWidget *frame)
+{
+ /* sólo hacemos algo si el tercer botón (el botón derecho) se
+ * levanta
+ */
+ if (event->type==GDK_BUTTON_RELEASE &amp;&amp;
+ event->button==3) {
+ GList *dlist, *free_list;
+ GtkWidget *new_prisoner;
+
+ /* sacar la lista de elementos que están actualmente
+ * seleccionados y que serán nuestro próximos prisioneros ;)
+ */
+ dlist=GTK_LIST(gtklist)->selection;
+ if (dlist)
+ new_prisoner=GTK_WIDGET(dlist->data);
+ else
+ new_prisoner=NULL;
+
+ /* buscar por elementos de la lista ya encarcelados, los
+ * volveremos a poner en la lista, recordar que hay que
+ * eliminar la lista doblemente enlazada que devuelve
+ * gtk_container_children()
+ */
+ dlist=gtk_container_children(GTK_CONTAINER(frame));
+ free_list=dlist;
+ while (dlist) {
+ GtkWidget *list_item;
+
+ list_item=dlist->data;
+
+ gtk_widget_reparent(list_item, gtklist);
+
+ dlist=dlist->next;
+ }
+ g_list_free(free_list);
+
+ /* si tenemos un nuevo prisionero, lo eliminamos de la GtkList
+ * y lo ponemos en el marco "Prisión". Primero tenemos que
+ * deseleccionarlo
+ */
+ if (new_prisoner) {
+ GList static_dlist;
+
+ static_dlist.data=new_prisoner;
+ static_dlist.next=NULL;
+ static_dlist.prev=NULL;
+
+ gtk_list_unselect_child(GTK_LIST(gtklist),
+ new_prisoner);
+ gtk_widget_reparent(new_prisoner, frame);
+ }
+ }
+}
+
+/* éste es el manipulador de señal que se llama si GtkList emite la
+ * señal "selection_changed"
+ */
+void
+sigh_print_selection (GtkWidget *gtklist,
+ gpointer func_data)
+{
+ GList *dlist;
+
+ /* sacar la lista doblemente enlazada de los elementos
+ * seleccionados en GtkList, ¡recuerde que hay que tratarla como
+ * de solo lectura!
+ */
+ dlist=GTK_LIST(gtklist)->selection;
+
+ /* si no hay elementos seleccionados no queda nada por hacer
+ * excepto informar al usuario
+ */
+ if (!dlist) {
+ g_print("Selection cleared\n");
+ return;
+ }
+ /* Bien, conseguimos una selección y la imprimimos
+ */
+ g_print("The selection is a ");
+
+ /* obtenemos la lista de elementos de la lista doblemente enlazada
+ * y entonces consultamos los datos asociados con la
+ * list_item_data_key que acabamos de imprimir
+ */
+ while (dlist) {
+ GtkObject *list_item;
+ gchar *item_data_string;
+
+ list_item=GTK_OBJECT(dlist->data);
+ item_data_string=gtk_object_get_data(list_item,
+ list_item_data_key);
+ g_print("%s ", item_data_string);
+
+ dlist=dlist->next;
+ }
+ g_print("\n");
+}
+/* fin del ejemplo */
+</verb></tscreen>
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> El <em/widget/ GtkListItem
+<p>
+El <em/widget/ GtkListItem está diseñado para comportarse como un
+contenedor que tiene un hijo, proporcionando funciones para la
+selección/deselección justo como las necesitan los hijos del
+<em/widget/ GtkList.
+
+Un GtkListItem tiene su propia ventana para recibir eventos y tiene su
+propio color de fondo, que normalmente es blanco.
+
+Como está derivado directamente de un GtkItem, puede tratarse como tal
+utilizando la macro GTK_ITEM(ListItem), ver el <em/widget/ GtkItem
+para más detalles. Normalmente un GtkListItem sólo tiene una etiqueta
+para identificar, por ejemplo, el nombre de un fichero dentro de una
+GtkList -- por lo tanto se proporciona la función
+<tt/gtk_list_item_new_with_label()/. Se puede conseguir el mismo
+efecto creando un GtkLabel, poniendo su alineación a <tt/xalign=0/ e
+<tt/yalign=0.5/ y seguido de una adición al contenedor GtkListItem.
+
+Nadie le obliga a meter un GtkLabel en un GtkListItem, puede meter un
+GtkVBox o un GtkArrow, etc...
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Señales
+<p>
+Un GtkListItem no crea por sí misma nuevas señales, pero hereda las
+señales de un GtkItem. Para más información *Note GtkItem::.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Funciones
+<p>
+<tscreen><verb>
+guint gtk_list_item_get_type( void );
+</verb></tscreen>
+
+Devuelve el identificador de tipo `GtkListItem'.
+
+<tscreen><verb>
+GtkWidget *gtk_list_item_new( void );
+</verb></tscreen>
+
+Crea un nuevo objeto GtkListItem. Se devuelve el nuevo <em/widget/
+como un puntero a un objeto GtkWidget. Se devuelve NULL en caso de
+producirse algún error.
+
+<tscreen><verb>
+GtkWidget *gtk_list_item_new_with_label( gchar *etiqueta );
+</verb></tscreen>
+
+Crea un nuevo objeto GtkListItem, con una sola GtkLabel como único
+hijo. Se devuelve el nuevo <em/widget/ como un puntero a un objeto
+GtkWidget. Se devuelve NULL en caso de producirse algún error.
+
+<tscreen><verb>
+void gtk_list_item_select( GtkListItem *list_item );
+</verb></tscreen>
+
+Esta función es, básicamente, un recubrimiento de una llamada a
+<tt/gtk_item_select (GTK_ITEM (list_item))/, y emitirá la señal
+<tt/select/. Para más información *Note GtkItem::.
+
+<tscreen><verb>
+void gtk_list_item_deselect( GtkListItem *list_item );
+</verb></tscreen>
+
+Esta función es, básicamente, un recubrimiento de una llamada a
+<tt/gtk_item_deselect (GTK_ITEM (list_item))/, y emitirá la señal
+<tt/deselect/. Para más información *Note GtkItem::.
+
+<tscreen><verb>
+GtkListItem *GTK_LIST_ITEM( gpointer obj );
+</verb></tscreen>
+
+Convierte un puntero general a `GtkListItem *'. Para más información
+*Note Standard Macros::.
+
+<tscreen><verb>
+GtkListItemClass *GTK_LIST_ITEM_CLASS( gpointer class );
+</verb></tscreen>
+
+Convierte un puntero general a `GtkListItemClass *'. Para más
+información *Note Standard Macros::.
+
+<tscreen><verb>
+gint GTK_IS_LIST_ITEM( gpointer obj );
+</verb></tscreen>
+
+Determina si un puntero general se refiere a un puntero
+`GtkListItem'. Para más información *Note Standard Macros::.
+
+<!-- ----------------------------------------------------------------- -->
+<sect1> Ejemplo
+<p>
+Para ver un ejemplo de todo esto, mire el de GtkList, que también
+cubre la utilización un GtkListItem.
+
+</article>