diff --git a/docs/gtk_tut_12.es.sgml b/docs/gtk_tut_12.es.sgml new file mode 100755 index 0000000000..38449c8edc --- /dev/null +++ b/docs/gtk_tut_12.es.sgml @@ -0,0 +1,17638 @@ + + + + +
+GTK Tutorial v1.2 +<author>Tony Gale <tt><htmlurl url="mailto:gale@gtk.org" + name="<gale@gtk.org>"></tt>, +Ian Main <tt><htmlurl url="mailto:imain@gtk.org" + name="<imain@gtk.org>"></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 (&argc, &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 (&argc, &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 (&argc, &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 (&argc, &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 (&argc, &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 (&argc, &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 (&argc, &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, &mask, + &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 (&argc, &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(&argc,&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><gtk/gtkhscrollbar.h></tt> y +<tt><gtk/gtkvscrollbar.h></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><gtk/gtkvscale.h></tt> y <tt><gtk/gtkhscale.h></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><gtk/gtkscale.h></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><gtk/gtkscale.h></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><gtk/gtkrange.h></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><gtk/gtkenums.h></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(&argc, &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(&argc, &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 (&argc, &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 (&argc, &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( &argc, &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, &mask, + &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, &mask, + &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", +"& 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& ", +" *=-;#::o+ ", +" >,<12#:34 ", +" 45671#:X3 ", +" +89<02qwo ", +"e* >,67;ro ", +"ty> 459@>+&& ", +"$2u+ ><ipas8* ", +"%$;=* *3:.Xa.dfg> ", +"Oh$;ya *3d.a8j,Xe.d3g8+ ", +" Oh$;ka *3d$a8lz,,xxc:.e3g54 ", +" Oh$;kO *pd$%svbzz,sxxxxfX..&wn> ", +" Oh$@mO *3dthwlsslszjzxxxxxxx3:td8M4 ", +" Oh$@g& *3d$XNlvvvlllm,mNwxxxxxxxfa.:,B* ", +" Oh$@,Od.czlllllzlmmqV@V#V@fxxxxxxxf:%j5& ", +" Oh$1hd5lllslllCCZrV#r#:#2AxxxxxxxxxcdwM* ", +" OXq6c.%8vvvllZZiqqApA:mq:Xxcpcxxxxxfdc9* ", +" 2r<6gde3bllZZrVi7S@SV77A::qApxxxxxxfdcM ", +" :,q-6MN.dfmZZrrSS:#riirDSAX@Af5xxxxxfevo", +" +A26jguXtAZZZC7iDiCCrVVii7Cmmmxxxxxx%3g", +" *#16jszN..3DZZZZrCVSA2rZrV7Dmmwxxxx&en", +" p2yFvzssXe:fCZZCiiD7iiZDiDSSZwwxx8e*>", +" OA1<jzxwwc:$d%NDZZZZCCCZCCZZCmxxfd.B ", +" 3206Bwxxszx%et.eaAp77m77mmmf3&eeeg* ", +" @26MvzxNzvlbwfpdettttttttttt.c,n& ", +" *;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&& ", +" &i0ycm6n4 ogk17,0<6666g ", +" N-k-<> >=01-kuu666> ", +" ,6ky& &46-10ul,66, ", +" Ou0<> o66y<ulw<66& ", +" *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 (&argc, &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, &mask, + &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( &argc, &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 (&argc, &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), &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), &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 (&argc, &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(&argc, &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, &gdk_color); + + /* Poner el color de fondo de la ventana */ + + gdk_window_set_background (drawingarea->window, &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 && 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 (&argc,&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 (&argc, &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, &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 (&argc, &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(&argc, &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(&argc, &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 (&argc, &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, &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 (&argc, &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 (&argc, &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( &argc, &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 (&argc, &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, &mask, + &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 (&argc, &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, &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(&argc, &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, &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, &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, &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 (&argc, &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 (&argc, &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 (&argc, &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, &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 (&argc, &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, &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, &text->style->black, NULL, + "Supports ", -1); + gtk_text_insert (GTK_TEXT (text), NULL, &colour, NULL, + "colored ", -1); + gtk_text_insert (GTK_TEXT (text), NULL, &text->style->black, NULL, + "text and different ", -1); + gtk_text_insert (GTK_TEXT (text), fixed_font, &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, &x1, &y1, &x2, &y2); + width = x2-x1; + height = y2-y1; + /* If there's a SELECTION, we got its bounds!) + + if (width != drawable->width && 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 (&srcPR, drawable, x1, y1, width, height, + FALSE, FALSE); + gimp_pixel_rgn_init (&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 (&srcPR, src_row, x1, y1+whichrow, width); + gimp_pixel_rgn_get_row (&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 (&argc, &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(&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 (&argc, &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), &have_selection); + gtk_signal_connect (GTK_OBJECT(selection_button), "selection_clear_event", + GTK_SIGNAL_FUNC (selection_clear), &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 (), &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 && + 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 && 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 (&argc, &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 (), &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, &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, &x, &y, &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 && 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, &x, &y, &state); + else + { + x = event->x; + y = event->y; + state = event->state; + } + + if (state & GDK_BUTTON1_MASK && 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><gdk/gdk.h></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, &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, &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 && 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, + &x, &y, &pressure, NULL, NULL, &state); + else + { + x = event->x; + y = event->y; + pressure = event->pressure; + state = event->state; + } + + if (state & GDK_BUTTON1_MASK && 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, &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="<e98cuenc@criens.u-psud.fr>"></tt> +y de Eduardo Anglada Varela +<tt><htmlurl url="mailto:eduardo.anglada@adi.uam.es" +name="<eduardo.anglada@adi.uam.es>"></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="<barreiro@arrakis.es>"></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 (), &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 && + 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 && 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 (&argc, &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 (), &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, &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 && + (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, &x, &y, &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); + } + } + } +} + +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, &update_rect); +} + +static gint +button_press_event (GtkWidget *widget, GdkEventButton *event) +{ + if (event->button == 1 && 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, &x, &y, &state); + else + { + x = event->x; + y = event->y; + state = event->state; + } + + if (state & GDK_BUTTON1_MASK && 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 (&argc, &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(&argc, &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), &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 && + 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 0000000000..38449c8edc --- /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="<gale@gtk.org>"></tt>, +Ian Main <tt><htmlurl url="mailto:imain@gtk.org" + name="<imain@gtk.org>"></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 (&argc, &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 (&argc, &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 (&argc, &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 (&argc, &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 (&argc, &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 (&argc, &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 (&argc, &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, &mask, + &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 (&argc, &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(&argc,&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><gtk/gtkhscrollbar.h></tt> y +<tt><gtk/gtkvscrollbar.h></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><gtk/gtkvscale.h></tt> y <tt><gtk/gtkhscale.h></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><gtk/gtkscale.h></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><gtk/gtkscale.h></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><gtk/gtkrange.h></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><gtk/gtkenums.h></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(&argc, &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(&argc, &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 (&argc, &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 (&argc, &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( &argc, &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, &mask, + &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, &mask, + &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", +"& 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& ", +" *=-;#::o+ ", +" >,<12#:34 ", +" 45671#:X3 ", +" +89<02qwo ", +"e* >,67;ro ", +"ty> 459@>+&& ", +"$2u+ ><ipas8* ", +"%$;=* *3:.Xa.dfg> ", +"Oh$;ya *3d.a8j,Xe.d3g8+ ", +" Oh$;ka *3d$a8lz,,xxc:.e3g54 ", +" Oh$;kO *pd$%svbzz,sxxxxfX..&wn> ", +" Oh$@mO *3dthwlsslszjzxxxxxxx3:td8M4 ", +" Oh$@g& *3d$XNlvvvlllm,mNwxxxxxxxfa.:,B* ", +" Oh$@,Od.czlllllzlmmqV@V#V@fxxxxxxxf:%j5& ", +" Oh$1hd5lllslllCCZrV#r#:#2AxxxxxxxxxcdwM* ", +" OXq6c.%8vvvllZZiqqApA:mq:Xxcpcxxxxxfdc9* ", +" 2r<6gde3bllZZrVi7S@SV77A::qApxxxxxxfdcM ", +" :,q-6MN.dfmZZrrSS:#riirDSAX@Af5xxxxxfevo", +" +A26jguXtAZZZC7iDiCCrVVii7Cmmmxxxxxx%3g", +" *#16jszN..3DZZZZrCVSA2rZrV7Dmmwxxxx&en", +" p2yFvzssXe:fCZZCiiD7iiZDiDSSZwwxx8e*>", +" OA1<jzxwwc:$d%NDZZZZCCCZCCZZCmxxfd.B ", +" 3206Bwxxszx%et.eaAp77m77mmmf3&eeeg* ", +" @26MvzxNzvlbwfpdettttttttttt.c,n& ", +" *;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&& ", +" &i0ycm6n4 ogk17,0<6666g ", +" N-k-<> >=01-kuu666> ", +" ,6ky& &46-10ul,66, ", +" Ou0<> o66y<ulw<66& ", +" *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 (&argc, &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, &mask, + &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( &argc, &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 (&argc, &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), &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), &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 (&argc, &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(&argc, &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, &gdk_color); + + /* Poner el color de fondo de la ventana */ + + gdk_window_set_background (drawingarea->window, &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 && 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 (&argc,&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 (&argc, &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, &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 (&argc, &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(&argc, &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(&argc, &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 (&argc, &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, &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 (&argc, &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 (&argc, &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( &argc, &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 (&argc, &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, &mask, + &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 (&argc, &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, &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(&argc, &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, &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, &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, &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 (&argc, &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 (&argc, &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 (&argc, &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, &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 (&argc, &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, &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, &text->style->black, NULL, + "Supports ", -1); + gtk_text_insert (GTK_TEXT (text), NULL, &colour, NULL, + "colored ", -1); + gtk_text_insert (GTK_TEXT (text), NULL, &text->style->black, NULL, + "text and different ", -1); + gtk_text_insert (GTK_TEXT (text), fixed_font, &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, &x1, &y1, &x2, &y2); + width = x2-x1; + height = y2-y1; + /* If there's a SELECTION, we got its bounds!) + + if (width != drawable->width && 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 (&srcPR, drawable, x1, y1, width, height, + FALSE, FALSE); + gimp_pixel_rgn_init (&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 (&srcPR, src_row, x1, y1+whichrow, width); + gimp_pixel_rgn_get_row (&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 (&argc, &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(&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 (&argc, &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), &have_selection); + gtk_signal_connect (GTK_OBJECT(selection_button), "selection_clear_event", + GTK_SIGNAL_FUNC (selection_clear), &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 (), &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 && + 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 && 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 (&argc, &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 (), &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, &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, &x, &y, &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 && 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, &x, &y, &state); + else + { + x = event->x; + y = event->y; + state = event->state; + } + + if (state & GDK_BUTTON1_MASK && 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><gdk/gdk.h></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, &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, &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 && 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, + &x, &y, &pressure, NULL, NULL, &state); + else + { + x = event->x; + y = event->y; + pressure = event->pressure; + state = event->state; + } + + if (state & GDK_BUTTON1_MASK && 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, &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="<e98cuenc@criens.u-psud.fr>"></tt> +y de Eduardo Anglada Varela +<tt><htmlurl url="mailto:eduardo.anglada@adi.uam.es" +name="<eduardo.anglada@adi.uam.es>"></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="<barreiro@arrakis.es>"></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 (), &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 && + 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 && 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 (&argc, &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 (), &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, &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 && + (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, &x, &y, &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); + } + } + } +} + +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, &update_rect); +} + +static gint +button_press_event (GtkWidget *widget, GdkEventButton *event) +{ + if (event->button == 1 && 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, &x, &y, &state); + else + { + x = event->x; + y = event->y; + state = event->state; + } + + if (state & GDK_BUTTON1_MASK && 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 (&argc, &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(&argc, &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), &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 && + 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>