gtk/docs/tutorial/gtk_tut_12.es.sgml

17639 lines
590 KiB
Plaintext
Raw Normal View History

2000-04-23 09:17:36 +00:00
<!doctype linuxdoc system>
<!--
Traducci<63>n realizada por:
Joaqu<71>n Cuenca Abela (e98cuenc@criens.u-psud.fr)
Eduardo Anglada Varela (eduardo.anglada@adi.uam.es)
Versi<73>n beta 2 de la traducci<63>n del GTK+
Tutorial 1.2. Cualquier sugerencia ser<65> bienvenida.
Los cambios m<>s significativos de esta versi<73>n son la traducci<63>n
de las variables de los programas y XXX.
Si quiere obtener este documento puede encontrarlo en Linux Landia:
http://www.croftj.net/~barreiro/spanish/gnome-es/
-->
<article>
<title>GTK Tutorial v1.2
<author>Tony Gale <tt><htmlurl url="mailto:gale@gtk.org"
name="&lt;gale@gtk.org&gt;"></tt>,
Ian Main <tt><htmlurl url="mailto:imain@gtk.org"
name="&lt;imain@gtk.org&gt;"></tt>
<date>21 de Febrero de 1999
<abstract>
Este documento es un tutorial sobre como utilizar GTK (el GIMP
Toolkit) en C
</abstract>
<toc>
<!-- ***************************************************************** -->
<sect>Introducci<63>n
<!-- ***************************************************************** -->
<p>
GTK (GIMP Toolkit) es una biblioteca para crear interfaces gr<67>ficas
de usuario. Su licencia es la LGPL, as<61> que mediante GTK podr<64>
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<63>n, incluyendo el
proyecto GNU Network Object Model Environment (GNOME). GTK est<73>
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<63>n de
aplicaciones orientadas al objeto (API). Aunque est<73> 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<73>ndar, as<61> 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<73>n disponibles o
no son est<73>ndar en otros unixs, como por ejemplo
g_strerror(). Algunas otras contienen mejoras a la versi<73>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<EFBFBD>n. En algunos casos la documentaci<63>n puede describir
alg<EFBFBD>n convenio importante (que deber<65>a conocer de antemano) y
despu<EFBFBD>s puede volver a este tutorial. Tambi<62>n hay alg<6C>n API
multiplataforma (como wxWindows y V) que utilizan GTK como una de sus
plataformas destino; de nuevo, consulte primero la documentaci<63>n
que viene con estos paquetes.
Si est<73> desarrollando su aplicaci<63>n GTK en C++, hay algunas
cosas que deber<65>a saber. Hay un recubriento a GTK para C++ llamado
GTK--, que proporciona una interfaz C++ a GTK; probablemente
deber<EFBFBD>a empezar mirando ah<61>. Si no le gusta esa aproximaci<63>n
al problema, por los motivos que sean, tiene dos
alternativas. Primero, puede ce<63>irse al subconjunto C de C++ cuando
realice alguna llamada a GTK a trav<61>s de su interfaz en C. Segundo,
puede utilizar GTK y C++ al mismo tiempo declarando todas las
funciones respuesta como funciones est<73>ticas en clases C++, y de
nuevo, llamar a GTK utilizando su interfaz C. Si elige esta <20>ltima
forma de actuar, puede incluir como dato de la funci<63>n respuesta un
puntero al objeto a manipular (el tambi<62>n llamado valor
<EFBFBD>this<EFBFBD>). La elecci<63>n de una u otra opci<63>n es cuesti<74>n de
gustos personales, ya que de las tres maneras conseguir<69> utilizar
GTK en C++. Ninguna de estas aproximaciones requiere el uso de un
preprocesador especializado, por lo que sin importar la opci<63>n que
escoja podr<64> utilizar C++ est<73>ndar en C++.
Este tutorial es un intento de documentar GTK tanto como sea posible,
pero no est<73> completo. Este tutorial asume un buen conocimiento de
C y de como crear programas bajo este lenguaje. Se ver<65> beneficiado
si tiene un conocimiento previo de la programaci<63>n en X, pero no
deber<EFBFBD>a ser necesario. Si est<73> aprendiendo GTK y es el primer
conjunto de <em/widgets/ que utiliza, por favor env<6E>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<61>a escuchar cualquier problema que le surja mientras
aprende GTK siguiendo este documento, y apreciar<61> cualquier
informaci<EFBFBD>n sobre como mejorarlo. Por favor, vea la secci<63>n <ref
id="sec_Contributing" name="Contribuyendo"> para encontrar m<>s
informaci<EFBFBD>n.
<!-- ***************************************************************** -->
<sect>Comenzando
<!-- ***************************************************************** -->
<p>
Por supuesto lo primero que hay que hacer es descargar las fuentes de
GTK e instalarlas. La <20>ltima versi<73>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<EFBFBD>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<65>s contiene las fuentes completas de todos
los ejemplos usados en este manual, as<61> como los makefiles para
compilarlos.
Para comenzar nuestra introducci<63>n a GTK vamos a empezar con el
programa m<>s sencillo posible. Con <20>l vamos a crear una ventana de
200x200 <em/pixels/ que s<>lo se puede destruir desde el shell.
<tscreen><verb>
/* principio del ejemplo base base.c */
#include <gtk/gtk.h>
int main (int argc, char *argv[])
{
GtkWidget *ventana;
gtk_init (&amp;argc, &amp;argv);
ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_widget_show (ventana);
gtk_main ();
return 0;
}
/* final del ejemplo */
</verb></tscreen>
Puede compilar el programa anterior con gcc tecleando:
<tscreen><verb>
gcc base.c -o base `gtk-config --cflags --libs`
</verb></tscreen>
El significado de la extra<72>a opci<63>n de compilaci<63>n se explica
m<EFBFBD>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<65>n
usadas en el programa.
La siguiente l<>nea:
<tscreen><verb>
gtk_init (&amp;argc, &amp;argv);
</verb></tscreen>
Llama a la funci<63>n gtk_init (gint *argc, gchar *** argv) responsable
de `arrancar' la biblioteca y de establecer algunos par<61>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<73>ales y comprueba los
argumentos pasados a la aplicaci<63>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<41> 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<69>e la ventana. En lugar de crear una ventana
de tama<6D>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<63>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 <20>ltima l<>nea comienza el proceso del bucle principal de GTK.
<tscreen><verb>
gtk_main ();
</verb></tscreen>
Otra llamada que siempre est<73> presente en cualquier aplicaci<63>n es
gtk_main(). Cuando el control llega a ella, GTK se queda dormido
esperando a que suceda alg<6C>n tipo de evento de las X (como puede ser
pulsar un bot<6F>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<65>n ignorados.
<!-- ----------------------------------------------------------------- -->
<sect1>Programa <20>Hola Mundo<64> en GTK
<p>
El siguiente ejemplo es un programa con un <em/widget/ (un
bot<EFBFBD>n). Simplemente es la versi<73>n de GTK del cl<63>sico <20>hola mundo<64>.
<tscreen><verb>
/* comienzo del ejemplo holamundo */
#include <gtk/gtk.h>
/* <20>sta es una funci<63>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<69> la se<73>al de destrucci<63>n
* "destroy". Esto es <20>til para di<64>logos emergentes del
* tipo: <20>Seguro que desea salir?
g_print ("Ha ocurrido un evento delete\n");
/* Cambiando TRUE por FALSE la ventana se destruir<69> 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<63>n hay que realizar la siguiente
* llamada. Los argumentos son tomados de la l<>nea de comandos
* y devueltos a la aplicaci<63>n. */
gtk_init (&amp;argc, &amp;argv);
/* creamos una ventana nueva */
ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
/* Cuando la ventana recibe la se<73>al "delete_event" (emitida
* por el gestor de ventanas, normalmente mediante la opci<63>n
* 'close', o en la barra del t<>tulo) hacemos que llame a la
* funci<63>n delete_event() tal y como ya hemos visto. Los datos
* pasados a la funci<63>n de respuesta son NULL, y ser<65>n ignorados. */
gtk_signal_connect (GTK_OBJECT (ventana), "delete_event",
GTK_SIGNAL_FUNC (delete_event), NULL);
/* Aqu<71> conectamos el evento "destroy" con el administrador de
* se<73>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<6F>n nuevo con la etiqueta "Hola mundo" */
boton = gtk_button_new_with_label ("Hola mundo");
/* Cuando el bot<6F>n recibe la se<73>al "clicked" llama a la
* funci<63>n hello() pas<61>ndole NULL como argumento. (La
* funci<63>n ya ha sido definida arriba). */
gtk_signal_connect (GTK_OBJECT (boton), "clicked",
GTK_SIGNAL_FUNC (hello), NULL);
/* Esto har<61> que la ventana sea destruida llamando a
* gtk_widget_destroy(ventana) cuando se produzca "clicked". Una
* vez mas la se<73>al de destrucci<63>n puede provenir del gestor
* de ventanas o de aqu<71>. */
gtk_signal_connect_object (GTK_OBJECT (boton), "clicked",
GTK_SIGNAL_FUNC (gtk_widget_destroy),
GTK_OBJECT (ventana));
/* Ahora empaquetamos el bot<6F>n en la ventana (usamos un gtk
* container ). */
gtk_container_add (GTK_CONTAINER (ventana), boton);
/* El <20>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<71> y debe
* esperar a que suceda alg<6C>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 <20>til porque `conoce' que opciones son
necesarias para compilar programas que usen gtk. <tt>gtk-config
--cflags</tt> dar<61> una lista con los directorios donde el
compilador debe buscar ficheros <20>include<64>. A su vez <tt>gtk-config
--libs</tt> nos permite saber las bibliotecas que el compilador
intentar<EFBFBD> enlazar y d<>nde buscarlas.
Hay que destacar que las comillas simples en la orden de
compilaci<EFBFBD>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<73> construida
encima de glib por lo que simpre se usar<61>. Vea la secci<63>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<65>tica (-lm). Es usada por GTK para
diferentes cosas.
</itemize>
<!-- ----------------------------------------------------------------- -->
<sect1>Teor<6F>a de se<73>ales y respuestas
<p>
Antes de profundizar en <tt/holamundo/ vamos a discutir las
se<EFBFBD>ales y las respuestas. GTK es un toolkit (conjunto de
herramientas) gestionadas mediante eventos. Esto quiere decir que GTK
<EFBFBD>duerme<EFBFBD> en gtk_main hasta que se recibe un evento, momento en el
cual se transfiere el control a la funci<63>n adecuada.
El control se transfiere mediante <20>se<73>ales<65>. (Conviene destacar
que las se<73>ales de GTK no son iguales que las de los sistemas
UNIX, aunque la terminolog<6F>a es la misma.) Cuando sucede un evento,
como por ejemplo la pulsaci<63>n de un bot<6F>n, se <20>emitir<69><72> la
se<EFBFBD>al apropiada por el <em/widget/ pulsado. As<41> es como GTK
proporciona la mayor parte de su utilidad. Hay un conjunto de
se<EFBFBD>ales que todos los <em/widgets/ heredan, como por ejemplo
<EFBFBD>destroy<EFBFBD> y hay se<73>ales que son espec<65>ficas de cada
<em/widget/, como por ejemplo la se<73>al <20>toggled<65> de un bot<6F>n
de selecci<63>n (bot<6F>n <em/toggle/).
Para que un bot<6F>n haga algo crearemos un controlador que se encarga de
recoger las se<73>ales y llamar a la funci<63>n apropiada. Esto se hace
usando una funci<63>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<73>al, el
segundo el nombre de la se<73>al que queremos `cazar', el tercero es
la funci<63>n a la que queremos que se llame cuando se `cace' la
se<EFBFBD>al y el cuarto los datos que queremos pasarle a esta funci<63>n.
La funci<63>n especificada en el tercer argumento se denomina <20>funci<63>n
de respuesta<74> y debe tener la forma siguiente:
<tscreen><verb>
void callback_func( GtkWidget *widget,
gpointer datos_respuesta );
</verb></tscreen>
Donde el primer argumento ser<65> un puntero al <em/widget/ que emiti<74> la
se<EFBFBD>al, y el segundo un puntero a los datos pasados a la funci<63>n tal y
como hemos visto en el <20>ltimo argumento a gtk_signal_connect().
Conviene destacar que la declaraci<63>n de la funci<63>n de respuesta debe
servir s<>lo como gu<67>a general, ya que algunas se<73>ales espec<65>ficas
pueden generar diferentes par<61>metros de llamada. Por ejemplo, la se<73>al
de GtkCList <20>select_row<6F> proporciona los par<61>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<69>ntica a gtk_signal_connect() excepto
en que la funci<63>n de llamada s<>lo usa un argumento, un puntero a un
objeto GTK. Por tanto cuando usemos esta funci<63>n para conectar
se<EFBFBD>ales, la funci<63>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<63>n de GTK que acepte un <em/widget/
o un objeto como un argumento, tal y como se vio en el ejemplo hola
mundo.
<EFBFBD>Para qu<71> sirve tener dos funciones para conectar se<73>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<64> que usar
gtk_signal_connect_object() con estas funciones, mientras que
probablemente tenga que suministrarle informaci<63>n adicional a sus
funciones.
<!-- XXX Completamente revisado hasta aqu<71> -------------------------- -->
<sect1>Eventos
<p>
Adem<EFBFBD>s del mecanismo de se<73>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<63>n de respuesta a alguno de los eventos
anteriores debe usar la funci<63>n gtk_signal_connect, tal y como se
descrivi<EFBFBD> anteriormente, utilizando en el par<61>metro <tt/name/ uno de
los nombres de los eventos que se acaban de mencionar. La funci<63>n de
respuesta para los eventos tiene un forma ligeramente diferente de la
que tiene para las se<73>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<61>metro
<tt/type/ que refleja cual es el evento en cuesti<74>n. Los otros
componentes de la estructura depender<65>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<63>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<61>n se encuentre sobre el
bot<EFBFBD>n y <20>ste sea presionado, se llamar<61> a la funci<63>n
<tt/button_press_callback/. Esta funci<63>n puede declararse as<61>:
<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<69>
cuando se llame a la funci<63>n.
El valor devuelto por esta funci<63>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<EFBFBD>n. Por contra si devuelve FALSE se continua con la gesti<74>n
normal del evento. Para m<>s detalles se recomienda leer la secci<63>n
donde se aclara como se produce el proceso de propagaci<63>n.
Para m<>s detalles acerca de los tipos de informaci<63>n GdkEvent
consultar el ap<61>ndice <ref id="sec_GDK_Event_Types" name="Tipos de
eventos GDK">.
<!-- ----------------------------------------------------------------- -->
<sect1>Aclaraci<63>n de Hello World
<p>
Ahora que conocemos la teor<6F>a vamos a aclarar las ideas estudiando
en detalle el programa <tt/helloworld/.
<EFBFBD>sta es la funci<63>n respuesta a la que se llamar<61> cuando se
pulse el bot<6F>n. En el ejemplo ignoramos tanto el <em/widget/ como
la informaci<63>n, pero no es dif<69>cil usarlos. El siguiente ejemplo
usar<EFBFBD> la informaci<63>n que recibe como argumento para decirnos que
bot<EFBFBD>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 <20>delete_event<6E> ocurre
cuando el gestor de ventanas env<6E>a este evento a la aplicaci<63>n. Aqu<71>
podemos decidir que hacemos con estos eventos. Los podemos ignorar,
dar alg<6C>n tipo de respuesta, o simplemente terminar la aplicaci<63>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<73>al <20>destroy<6F> y por lo tanto queremos que nuestra
aplicaci<EFBFBD>n siga ejecut<75>ndose. Si devolvemos FALSE, decimos que
se emita <20>destroy<6F>, lo que har<61> que se ejecute nuestro manejador
de se<73>al de <20>destroy<6F>.
<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<63>n de respuesta que hace
que el programa salga llamando a gtk_main_quit(). Con esta funci<63>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<63>n debe tener una
funci<EFBFBD>n main(), y una aplicaci<63>n GTK no va a ser menos. Todas las
aplicaciones GTK tambi<62>n tienen una funci<63>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<61>n m<>s adelante para crear una ventana y un
bot<EFBFBD>n.
<tscreen><verb>
GtkWidget *ventana;
GtkWidget *boton;
</verb></tscreen>
Aqu<EFBFBD> tenemos otra vez a gtk_init. Como antes arranca el conjunto de
herramientas y filtra las opciones introducidas en la l<>nea de
<EFBFBD>rdenes. Cualquier argumento que sea reconocido ser<65> borrado de la
lista de argumentos, de modo que la aplicaci<63>n recibir<69> el resto.
<tscreen><verb>
gtk_init (&amp;argc, &amp;argv);
</verb></tscreen>
Ahora vamos a crear una ventana. Simplemente reservamos memoria para
la estructura GtkWindow *ventana, con lo que ya tenemos una nueva
ventana, ventana que no se mostrar<61> 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<EFBFBD> tenemos un ejemplo de como conectar un manejador de se<73>al a un
objeto, en este caso, la ventana. La se<73>al a cazar ser<65>
<EFBFBD>destroy<EFBFBD>. Esta se<73>al se emite cuando utilizamos el administrador de
ventanas para matar la ventana (y devolvemos TRUE en el manejador
<EFBFBD>delete_event<EFBFBD>), o cuando usamos llamamos a gtk_widget_destroy()
pas<EFBFBD>ndole el <em/widget/ que representa la ventana como argumento.
As<EFBFBD> conseguimos manejar los dos casos con una simple llamada a la
funci<EFBFBD>n destroy () (definida arriba) pas<61>ndole NULL como argumento y
ella acabar<61> con la aplicaci<63>n por nosotros.
GTK_OBJECT y GTK_SIGNAL_FUNC son macros que realizan la comprobaci<63>n y
transformaci<EFBFBD>n de tipos por nosotros. Tambi<62>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<63>n establece un atributo a un objeto contenedor
(discutidos luego). En este caso le pone a la ventana un <20>rea
negra de 10 <em/pixels/ de ancho donde no habr<62>n <em/widgets/. Hay
funciones similares que ser<65>n tratadas con m<>s detalle en la secci<63>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<73>n
entre tipos
<tscreen><verb>
gtk_container_border_width (GTK_CONTAINER (ventana), 10);
</verb></tscreen>
La siguiente llamada crea un nuevo bot<6F>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<EFBFBD>: "Hola mundo".
<tscreen><verb>
boton = gtk_button_new_with_label ("Hola mundo");
</verb></tscreen>
Ahora hacemos que el bot<6F>n sea <20>til, para ello enlazamos el bot<6F>n con
el manejador de se<73>ales para que cuando emita la se<73>al <20>clicked<65>, se
llame a nuestra funci<63>n hola(). Los datos adicionales ser<65>n
ignorados, por lo que simplemente le pasaremos NULL a la funci<63>n
respuesta. Obviamente se emitir<69> la se<73>al <20>clicked<65> cuando pulsemos
en el bot<6F>n con el rat<61>n.
<tscreen><verb>
gtk_signal_connect (GTK_OBJECT (boton), "clicked",
GTK_SIGNAL_FUNC (hola), NULL);
</verb></tscreen>
XXX
Ahora vamos a usar el bot<6F>n para terminar nuestro programa. As<41>
aclararemos c<>mo es posible que la se<73>al <20>destroy<6F> sea emitida tanto
por el gestor de ventanas como por nuestro programa. Cuando el bot<6F>n
es pulsado, al igual que arriba, se llama a la primera funci<63>n
respuesta hello() y despu<70>s se llamar<61> a esta funci<63>n. Las funciones
respuesta ser<65>n ejecutadas en el orden en que sean conectadas. Como la
funci<EFBFBD>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<6F>n debe estar en la ventana d<>nde
ser<EFBFBD> mostrado. Conviene destacar que un contenedor GTK s<>lo puede
contener un <em/widget/. Existen otros <em/widgets/ (descritos
despu<EFBFBD>s) que sirven para contener y establecer la disposici<63>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<73>ales ya est<73>n en su sitio, y el bot<6F>n est<73> situado en la
ventana donde queremos que est<73>, s<>lo nos queda pedirle a GTK que
muestre todos los <em/widgets/ en pantalla. El <em/widget/ ventana ser<65>
el <20>ltimo en mostrarse queremos que aparezca todo de golpe, en vez de
ver aparecer la ventana, y despu<70>s ver aparecer el bot<6F>n. De todas
formas con un ejemplo tan simple nunca se notar<61>a cual es el orden de
aparici<EFBFBD>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<6C>n evento para emitir las se<73>ales apropiadas.
<tscreen><verb>
gtk_main ();
</verb></tscreen>
Por <20>ltimo el `return' final que devuelve el control cuando gtk_quit()
sea invocada.
<tscreen><verb>
return 0;
</verb></tscreen>
Cuando pulsemos el bot<6F>n del rat<61>n el <em/widget/ emite la se<73>al
correspondiente <20>clicked<65>. Para que podamos usar la informaci<63>n el
programa activa el gestor de eventos que al recibir la se<73>al llama a
la funci<63>n que hemos elegido. En nuestro ejemplo cuando pulsamos el
bot<EFBFBD>n se llama a la funci<63>n hello() con NULL como argumento y adem<65>s
se invoca al siguiente manipulador de se<73>al. As<41> conseguimos que se
llame a la funci<63>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<73>al <20>destroy<6F>, que es cazada, y que llama
a nuestra funci<63>n respuesta destroy(), que simplemente sale de GTK.
Otra posibilidad es usar el gestor de ventanas para acabar con la
aplicaci<EFBFBD>n. Esto emitir<69> <20>delete_event<6E> que har<61> que se
llame a nuestra funci<63>n manejadora correspondiente. Si en la
funci<EFBFBD>n manejadora <20>delete_event<6E> devolvemos TRUE la ventana se
quedar<EFBFBD> como si nada hubiese ocurrido, pero si devolvemos FALSE GTK
emitir<EFBFBD> la se<73>al <20>destroy<6F> que, por supuesto, llamar<61> a la
funci<EFBFBD>n respuesta <20>destroy<6F>, que saldr<64> 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<61> son typedefs a int y
a char respectivamente. Sirven para que no haya que tener en cuenta el
tama<EFBFBD>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<63>n llama a un GtkObject. Esto es debido a que GTK
est<EFBFBD> orienta a objetos y un <em/widget/ es un GtkObject.
<!-- ----------------------------------------------------------------- -->
<sect1>M<>s sobre el manejo de se<73>ales
<p>
Si estudiamos en mayor profundidad la declaraci<63>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<63>n de respuesta. Tal
y como ya vimos podemos tener tantas funciones de respuesta por
se<EFBFBD>al y objeto como sean necesarias, y cada una de ellas se
ejecutar<EFBFBD> en el mismo orden en el que fueron enlazadas.
Esta etiqueta nos permite eliminar la funci<63>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<73>al pas<61>ndole
a la funci<63>n anterior el <em/widget/ del que queremos desconectar y
la etiqueta o id devuelta por una de las funciones signal_connect.
Otra funci<63>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<73>ales del objeto que pasamos como primer argumento.
<!-- ----------------------------------------------------------------- -->
<sect1>Un Hello World mejorado.
<p>
Vamos a mejorar el ejemplo para obtener una visi<73>n m<>s amplia
sobre el manejo de se<73>ales y respuestas. Tambi<62>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<63>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<73> presente en todas las aplicaciones basadas
* en GTK. Los argumentos introducidos a la aplicaci<63>n*/
gtk_init (&amp;argc, &amp;argv);
/* creamos una nueva ventana*/
ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
/* Esta funci<63>n es nueva, pone como t<>tulo de la ventana
* "<22>Hola botones!"*/
gtk_window_set_title (GTK_WINDOW (ventana), "<22>Hola botones!");
/* Establecemos el controlador para la llamada delete_event que
* termina la aplicaci<63>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<63>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<6F>n con la etiqueta "Bot<6F>n 1". */
boton = gtk_button_new_with_label ("Bot<6F>n 1");
/* Cada vez que el bot<6F>n sea pulsado llamamos a la funci<63>n
* "callback" con un puntero a "bot<6F>n 1" como argumento. */
gtk_signal_connect (GTK_OBJECT (boton), "clicked",
GTK_SIGNAL_FUNC (callback), (gpointer) "bot<6F>n 1");
/* En lugar de gtk_container_add empaquetamos el bot<6F>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<6F>n ya se han finalizado y que
* por tanto puede ser mostrado. */
gtk_widget_show(boton);
/* hacemos lo mismo para crear un segundo bot<6F>n. */
boton = gtk_button_new_with_label ("Bot<6F>n 2");
/* Llamamos a la misma funci<63>n de respuesta pero con diferente
* argumento: un puntero a "bot<6F>n 2". */
gtk_signal_connect (GTK_OBJECT (boton), "clicked",
GTK_SIGNAL_FUNC (callback), (gpointer) "bot<6F>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 <20>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<63>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<62> 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<6F>n que termine el
programa. Tambi<62>n puede resultar interesante probar las diferentes
opciones de gtk_box_pack_start() mientras lee la siguiente
secci<EFBFBD>n. Intente cambiar el tama<6D>o de la ventana y observe el
comportamiento.
Como <20>ltima nota, existe otra definici<63>n bastante <20>til:
gtk_widow_new() - GTK_WINDOW_DIALOG. Su comportamiento es un poco
diferente y debe ser usado para ventanas intermedias (cuadros de
di<EFBFBD>logo).
<!-- ***************************************************************** -->
<sect><em/Widgets/ usados para empaquetar
<!-- ***************************************************************** -->
<p>
Al crear una aplicaci<63>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<75>bamos la funci<63>n gtk_container_add
para <20>empaquetar<61> el <em/widget/ en la ventana. Pero cuando cuando
se quiere poner m<>s de un <em/widget/ en una ventana, <20>C<EFBFBD>mo
podemos controlar donde aparecer<65> el <em/widget/?. Aqu<71> es donde
entra el empaquetamiento.
<!-- ----------------------------------------------------------------- -->
<sect1>Empaquetamiento usando cajas
<p>
Normalmente para empaquetar se usan cajas, tal y como ya hemos
visto. <20>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<65>s (dependiendo de que
llamada se use). Lo mismo ocurre en los verticales (de arriba a bajo o
al rev<65>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<61> de arriba a abajo o de izquierda a derecha. La
segunda lo har<61> al rev<65>s.
Usando estas funciones podemos ir metiendo <em/widgets/ con una
justificaci<EFBFBD>n a la izquierda o a la derecha y adem<65>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<6F>n (button) (aunque normalmente lo
<EFBFBD>nico que meteremos dentro ser<65> una etiqueta de texto).
Mediante el uso de estas funciones le decimos a GTK d<>nde queremos
situar nuestros widgets, y GTK podr<64>, por ejemplo, cambiarles el
tama<EFBFBD>o de forma autom<6F>tica y hacer otras cosas de
utilidad. Tambi<62>n hay unas cuantas opciones que tienen que ver con
la forma en la que los <em/widgets/ ser<65>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<70>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<63>n de la funci<63>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<65> un bot<6F>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<63>n a la derecha o a la
izquierda. En caso contrario, los <em/widgets/ se expandir<69>n para
llenar toda la caja, y podemos conseguir el mismo efecto utilizando
s<EFBFBD>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<73>n tambi<62>n es TRUE.
Al crear una nueva ventana la funci<63>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<EFBFBD>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<61>
activado.
Puede que el lector se est<73> haciendo la siguiente pregunta:
<EFBFBD>C<EFBFBD>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<EFBFBD>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<69>genes
anteriores. Con los comentarios no deber<65>a de haber ning<6E>n
problema para entenderlo.
<!-- ----------------------------------------------------------------- -->
<sect1>Programa demostraci<63>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<6F>n. Los argumentos
* para las variables que estamos interesados son pasados a esta
* funci<63>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<6F>n llevar<61> 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;
/* <20>No olvidar la siguiente llamada! */
gtk_init (&amp;argc, &amp;argv);
if (argc != 2) {
fprintf (stderr, "usage: packbox num, where num is 1, 2, or 3.\n");
/* hacemos limpieza en GTK y devolvemos el valor de 1 */
gtk_exit (1);
}
which = atoi (argv[1]);
/* Creamos la ventana */
ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
/* Siempre hay que conectar la se<73>al de destrucci<63>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<41> 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<69>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<73> funci<63>n
* ser<65> discutida en detalle en la secci<63>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<65>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<63>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<63>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<65>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 <20>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 <20>ltima etiqueta*/
etiqueta = gtk_label_new ("end");
/* la empaquetamos usando gtk_box_pack_end(), por lo que se
* sit<69>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<41> se determina el tama<6D>o del separador a 400 pixels
* de largo por 5 de alto. La hbox tambi<62>n tendr<64> 400
* pixels de largo y la etiqueta "end" estar<61> separada de
* las dem<65>s etiquetas en la hbox. Si no establecemos estos
* par<61>metros todos los widgets en la hbox ser<65>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<6F>n de salida. */
boton = gtk_button_new_with_label ("Quit");
/* Establecemos la se<73>al de destrucci<63>n de la ventana.
* Recuerde que emitir<69> la se<73>al de "destroy" que a su vez
* ser<65> procesada por el controlador de se<73>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<6F>n en la caja de salida (quitbox).
* los tres <20>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 <20>ltimo todo aparece de golpe. */
gtk_widget_show (ventana);
/* por supuesto tenemos una funci<63>n main. */
gtk_main ();
/* El programa llega aqu<71> 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 <20>tiles.
Usando tablas creamos una cuadr<64>cula donde podemos poner los
widgets. Estos pueden ocupar tanto espacio como queramos.
La primera funci<63>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<6D>o de las celdas de la tabla. Si es TRUE
se fuerza a que el tama<6D>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<65>
la de la celda m<>s alta de esa fila.
El n<>mero de filas y columnas var<61>a entre 0 y n, donde n es el
n<EFBFBD>mero especificado en la llamada a gtk_table_new. As<41> si se
especifica columnas = 2 y filas = 2 la apariencia ser<65> parecida a:
<tscreen><verb>
0 1 2
0+----------+----------+
| | |
1+----------+----------+
| | |
2+----------+----------+
</verb></tscreen>
Conviene destacar que el origen de coordenadas se sit<69>a en la esquina superior izquierda. Para
situar un widget en una ventana se usa la siguiente funci<63>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<6F>n que s<>lo ocupe la esquina inferior
izquierda en nuestra tabla 2x2. Los valores ser<65>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<EFBFBD>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<69> ocupando todo el
espacio disponible.
<item>GTK_SHRINK - En el caso de que hayamos dejado espacio sin usar
cuando el usuario reajuste el tama<6D>o de la ventana los <em/widgets/
normalmente ser<65>n empujados al fondo de la ventana y
desaparecer<EFBFBD>n. Si especifica GTK_SHRINK los widgets se reducir<69>n
con la tabla.
<item>GTK_EXPAND - Mediante esta opci<63>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<EFBFBD>a alrededor del widget (el tama<6D>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<63>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<69>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 <20>ltimas fila y columna no estar<61>n
espaciadas.
<!-- ----------------------------------------------------------------- -->
<sect1>Ejemplo de empaquetamiento mediante tablas.
<p>
Haremos una ventana con tres botones en una tabla 2x2. Los dos
primeros botones ocupar<61>n la fila de arriba, mientras que el
tercero (de salida) ocupar<61> 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<65>s se imprime en stdout. */
void callback (GtkWidget *widget, gpointer data)
{
g_print ("Hello again - %s was pressed\n", (char *) data);
}
/* Con esta otra respuesta terminamos el programa. */
void delete_event (GtkWidget *widget, GdkEvent *event, gpointer data)
{
gtk_main_quit ();
}
int main (int argc, char *argv[])
{
GtkWidget *ventana;
GtkWidget *boton;
GtkWidget *table;
gtk_init (&amp;argc, &amp;argv);
ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (ventana), "Table");
gtk_signal_connect (GTK_OBJECT (ventana), "delete_event",
GTK_SIGNAL_FUNC (delete_event), NULL);
gtk_container_border_width (GTK_CONTAINER (ventana), 20);
table = gtk_table_new (2, 2, TRUE);
gtk_container_add (GTK_CONTAINER (ventana), table);
boton = gtk_button_new_with_label ("bot<6F>n 1");
gtk_signal_connect (GTK_OBJECT (boton), "clicked",
GTK_SIGNAL_FUNC (callback), (gpointer) "bot<6F>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<6F>n 2");
gtk_signal_connect (GTK_OBJECT (boton), "clicked",
GTK_SIGNAL_FUNC (callback), (gpointer) "bot<6F>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<65>n explicadas en esta secci<63>n).
<item> Connectar todas las se<73>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 <20>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<73>n de tipos
<p>
GTK usa un sistema de conversi<73>n de tipos mediante macros que
comprueban si se puede realizar la conversi<73>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<63>n.
Aparecer<EFBFBD>n mucho en los ejemplos, para usarlas s<>lo hay que mirar la
declaraci<EFBFBD>n de la funci<63>n.
Tal y como se puede ver en el <20>rbol de clases (situado un poco
m<EFBFBD>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<63>n (que acepte un objeto, claro)
realizando la conversi<73>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<6F>n pase a ser un objeto y que se cambie el
puntero a la funci<63>n a una funci<63>n respuesta.
Muchos <em/widgets/ son contenedores, por lo que unos pueden derivar
de otros (la mayor<6F>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<70>ctica es posible aprender a manejar un
<em/widget/ leyendo las declaraciones de las funciones.
<!-- ----------------------------------------------------------------- -->
<sect1><3E>rbol formado por los <em/widgets/
<p>
A continuaci<63>n se detallan todas las ramas del <20>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<64> que utilizar GtkEventBox. En la
secci<EFBFBD>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<63>n describiendo cada uno de los
<em/widgets/ mediante ejemplos. Tambi<62>n se puede consultar el
programa testgtk.c (Se encuentra en gtk/testgtk.c).
<!-- ***************************************************************** -->
<sect>El <em/widget/ Bot<6F>n
<!-- ***************************************************************** -->
<!-- ----------------------------------------------------------------- -->
<sect1>Botones normales <label id="sec_Radio_Buttons">
<p>
Ya hemos visto pr<70>cticamente todo lo que hay que saber a cerca de
este <em/widget/. Existen dos formas diferentes de crear un
bot<EFBFBD>n. Se puede usar gtk_button_new_with_label() para conseguir un
bot<EFBFBD>n con etiqueta o simplemente gtk_button_new(). Si se quiere se
puede a<>adir una etiqueta a este <20>ltimo empaquet<65>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<6F>n mediante gtk_container_add.
Estudiemos un ejemplo de gtk_button_new para crear un bot<6F>n con una
imagen y una etiqueta. El c<>digo est<73> 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<6F>n (probablemente para el color
* de fondo, pero no estoy seguro) */
style = gtk_widget_get_style(parent);
/* cargamos el pixmap. Hay una secci<63>n que describe el proceso
* en detalle */
pixmap = gdk_pixmap_create_from_xpm (parent->window, &amp;mask,
&amp;style->bg[GTK_STATE_NORMAL],
xpm_filename);
pixmapwid = gtk_pixmap_new (pixmap, mask);
etiqueta = gtk_label_new (label_text);
gtk_box_pack_start (GTK_BOX (caja1),
pixmapwid, FALSE, FALSE, 3);
gtk_box_pack_start (GTK_BOX (caja1), etiqueta, FALSE, FALSE, 3);
gtk_widget_show(pixmapwid);
gtk_widget_show(etiqueta);
return (caja1);
}
/* respuesta */
void callback (GtkWidget *widget, gpointer data)
{
g_print ("Hola de nuevo. Se ha pulsado %s\n", (char *) data);
}
int main (int argc, char *argv[])
{
GtkWidget *ventana;
GtkWidget *boton;
GtkWidget *caja1;
gtk_init (&amp;argc, &amp;argv);
ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (ventana), "Botones con dibujos");
/* It's a good idea to do this for all windows. */
gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
GTK_SIGNAL_FUNC (gtk_exit), NULL);
gtk_signal_connect (GTK_OBJECT (ventana), "delete_event",
GTK_SIGNAL_FUNC (gtk_exit), NULL);
gtk_container_border_width (GTK_CONTAINER (ventana), 10);
gtk_widget_realize(ventana);
boton = gtk_button_new ();
gtk_signal_connect (GTK_OBJECT (boton), "clicked",
GTK_SIGNAL_FUNC (callback),
(gpointer) "bot<6F>n divertido");
caja1 = xpm_label_box(ventana, "info.xpm", "bot<6F>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<63>n xpm_label_box puede ser usada para empaquetar xpm y
etiquetas en cualquier widget que pueda ser un contenedor.
El bot<6F>n puede responder a las siguientes se<73>ales:
<itemize>
<item> pressed
<item> released
<item> clicked
<item> enter
<item> leave
</itemize>
<!-- ----------------------------------------------------------------- -->
<sect1> Botones de selecci<63>n
<p>
Estos botones son muy similares a los normales. La <20>nica diferencia
es que s<>lo pueden estar en dos posiciones diferentes alternadas
mediante pulsaciones del rat<61>n.
Los botones de selecci<63>n son la base de otros tipos: los de
comprobaci<EFBFBD>n y los circulares. Por lo tanto muchas de sus llamadas
seran heredadas por estos.
Creamos un nuevo bot<6F>n de selecci<63>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<EFBFBD>n normal. La primera crea un bot<6F>n, mientras que la segunda
crea un bot<6F>n con una etiqueta.
Para saber cual es el estado de un bot<6F>n de selecci<63>n,
comprobaci<EFBFBD>n o circular se usa una de las macros del ejemplo
siguiente. En <20>stas se comprueba el estado del bot<6F>n mediante
una respuesta. La se<73>al que queremos recibir es
<EFBFBD>toggled<EFBFBD>. Generalmente para comprobar el estado de una se<73>al se
establece un controlador de se<73>ales y luego se usa la siguiente
macro. La funci<63>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<71> el bot<6F>n est<73> pulsado */
} else {
/* El bot<6F>n no est<73> 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<EFBFBD>n de selecci<63>n (o de cualquiera de sus hijos: el circular o
el de comprobaci<63>n). El primer argumento es el bot<6F>n, el segundo
TRUE cuando queremos que el bot<6F>n no est<73> pulsado o FALSE para
cuando lo est<73>. Por defecto se establece FALSE.
Hay que destacar que cuando se usa gtk_toggle_button_set_state() y se
cambia el estado del bot<6F>n este emite la se<73>al <20>clicked<65>.
<tscreen><verb>
void gtk_toggle_button_toggled (GtkToggleButton *toggle_button);
</verb></tscreen>
Cambia el estado del bot<6F>n emitiendo la se<73>al <20>toggled<65>.
<!-- ----------------------------------------------------------------- -->
<sect1> Botones de comprobaci<63>n
<p>
Los botones de comprobaci<63>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<75>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<63>n new_with_label crea un bot<6F>n de comprobaci<63>n con
una etiqueta dentro.
El proceso para comprobar el estado de un bot<6F>n de este tipo es
igual al de los de comprobaci<63>n.
<!-- ----------------------------------------------------------------- -->
<sect1> Botones circulares
<p>
Estos botones son similares a los de selecci<63>n con la salvedad de
que est<73>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<6F>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<63>n de <20>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<6F>n a un grupo hay que usar
gtk_radio_button_group con el anterior bot<6F>n como argumento. El
resultado se le pasa a gtk_radio_button_new o a
gtk_radio_button_new_with_label. As<41> se consigue enlazar una cadena
de botones. (El ejemplo siguiente sirve para aclarar el proceso)
Tambi<EFBFBD>n se puede establecer c<>al es el bot<6F>n pulsado por
defecto:
<tscreen><verb>
void gtk_toggle_button_set_state( GtkToggleButton *toggle_button,
gint state );
</verb></tscreen>
El siguiente ejemplo crea un grupo de tres botones:
<tscreen><verb>
/* Principio del ejemplo radiobuttons.c */
#include <gtk/gtk.h>
#include <glib.h>
void close_application( GtkWidget *widget, GdkEvent *event, gpointer data ) {
gtk_main_quit();
}
main(int argc,char *argv[])
{
static GtkWidget *ventana = NULL;
GtkWidget *caja1;
GtkWidget *caja2;
GtkWidget *boton;
GtkWidget *separator;
GSList *group;
gtk_init(&amp;argc,&amp;argv);
ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_signal_connect (GTK_OBJECT (ventana), "delete_event",
GTK_SIGNAL_FUNC(close_application),
NULL);
gtk_window_set_title (GTK_WINDOW (ventana), "radio buttons");
gtk_container_border_width (GTK_CONTAINER (ventana), 0);
caja1 = gtk_vbox_new (FALSE, 0);
gtk_container_add (GTK_CONTAINER (ventana), caja1);
gtk_widget_show (caja1);
caja2 = gtk_vbox_new (FALSE, 10);
gtk_container_border_width (GTK_CONTAINER (caja2), 10);
gtk_box_pack_start (GTK_BOX (caja1), caja2, TRUE, TRUE, 0);
gtk_widget_show (caja2);
boton = gtk_radio_button_new_with_label (NULL, "bot<6F>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<6F>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<6F>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<61>n o el teclado. Un
ejemplo son los <em/widgets/ de selecci<63>n descritos en la
secci<EFBFBD>n <ref id="sec_Range_Widgets" name="Widgets de selecci<63>n
de rango">. Tambi<62>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<63>n de
rango. Una forma de hacer que el programa reaccione ser<65>a tener
cada <em/widget/ emitiendo su propio tipo de se<73>al cuando cambie el
ajuste, y bien pasar el nuevo valor al manejador de se<73>al o bien
obligarle a que mire dentro de la estructura de datos del <em/widget/
para conocer este valor. Pero tambi<62>n puede ser que quiera conectar
los ajustes de varios <em/widgets/, para que as<61> cuando se ajuste
uno, los dem<65>s se ajusten autom<6F>ticamente. El ejemplo m<>s
obvio es conectar una barra de desplazamiento a una regi<67>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<73>ales para traducir el
resultado de la se<73>al producida por un <em/widget/ como el
argumento de una funci<63>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<63>n de una forma
abstracta y flexible. El uso m<>s obvio es el de almacenes de
p<EFBFBD>rametros para <em/widgets/ de escala (barras deslizantes y
escalas). Como los GtkAdjustment derivan de GtkObject poseen
cualidades intr<74>nsecas que les permiten ser algo m<>s que simples
estructuras de datos. Lo m<>s importante es que pueden emitir
se<EFBFBD>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<EFBFBD>s arriba y a la izquierda de un <em/widget/ ajustable. El argumento
<tt/lower/ especifica los valores m<>s peque<75>os que el ajuste
puede contener. A su vez con <tt/step_increment/ se especifica el
valor m<>s peque<75>o en el que se puede variar la magnitud en
cuesti<EFBFBD>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<73>bles se pueden dividir en dos categorias
diferentes, aquellos que necesitan saber las unidades de la cantidad
almacenada y los que no. Este <20>ltimo grupo incluye los <em/widgets/
de tama<6D>o (barras deslizantes, escalas, barras de estado, o botones
giratorios). Normalmente estos <em/widgets/ son ajustados
<EFBFBD>directamente<EFBFBD> por el usuario. Los argumentos <tt/lower/ y
<tt/upper/ ser<65>n los limites dentro de los cuales el usuario puede
manipular los ajustes. Por defecto s<>lo se modificar<61> 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
<EFBFBD>indirectamente<EFBFBD> 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<64>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<64> que leer el c<>digo fuente para saber que
pasa con cada widget).
Probablemente ya se habr<62> 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<61> 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<63>n detallada de los ajustes
<p>
Puede que se est<73> 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<63>n
de acceso que permita obtener el <tt/value/ de un GtkAdjustment, por
lo que tendr<64> que hacerlo usted mismo. Tampoco se preocupe mucho
porque la macro <tt>GTK_ADJUSTMENT (Object)</tt> comprueba los tipos
durante el proceso de ejecuci<63>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<63>n especial:
<tscreen><verb>
void gtk_adjustment_set_value( GtkAdjustment *adjustment,
gfloat value );
</verb></tscreen>
Tal y como se mencion<6F> antes GtkAdjustment es una subclase de GtkObject
y por tanto puede emitir se<73>ales. As<41> 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<73>ales a sus se<73>ales del tipo <tt/value_changed/. Esta es la
definici<EFBFBD>n de la se<73>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<EFBFBD>al cuando cambie el valor de alg<6C>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<62>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>
<EFBFBD>Qu<EFBFBD> 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<73>al <tt/changed/, que debe ser
parecida a:
<tscreen><verb>
void (* changed) (GtkAdjustment *adjustment);
</verb></tscreen>
Los <em/widgets/ de tama<6D>o normalmente conectan un controlador a
esta se<73>al, que cambia el aspecto de <20>ste para reflejar el
cambio. Por ejemplo el tama<6D>o de la gu<67>a en una barra deslizante
que se alarga o encoge seg<65>n la inversa de la diferencia de los
valores <tt/lower/ y <tt/upper/.
Probablemente nunca tenga que conectar un controlador a esta se<73>al
a no ser que est<73> 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<73>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<63>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<63>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<63>n comparten elementos
gr<EFBFBD>ficos, cada uno de los cuales tiene su propia ventana X window y
recibe eventos. Todos contienen una gu<67>a y un rect<63>ngulo para
determinar la posici<63>n dentro de la gu<67>a (en una procesador de
textos con entorno gr<67>fico se encuentra situado a la derecha del
texto y sirve para situarnos en las diferentes partes del texto). Con
el rat<61>n podemos subir o bajar el rect<63>ngulo, mientras que si
hacemos `click' dentro de la gu<67>a, pero no sobre el rect<63>ngulo,
este se mueve hacia donde hemos hecho el click. Dependiendo del
bot<EFBFBD>n pulsado el rect<63>ngulo se mover<65> hasta la posici<63>n
del click o una cantidad prefijada de ante mano.
Tal y como se mencion<6F> 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<63>n. Cuando el usuario manipula la
barra de desplazamiento el widget cambiar<61> 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<73>n (y en muchos es m<>s f<>cil utilizar
el <em/widget/ scrolled window). Para el resto de los casos, deber<65>a
utilizar los <em/widgets/ de escala, ya son m<>s sencillos de usar y
m<EFBFBD>s potentes.
Hay dos tipos separados de barras de desplazamiento, seg<65>n sea
horizontal o vertical. Realmente no hay mucho que a<>adir. Puede
crear estos <em/widgets/ utilizar las funciones siguientes, definidas
en <tt>&lt;gtk/gtkhscrollbar.h&gt;</tt> y
<tt>&lt;gtk/gtkvscrollbar.h&gt;</tt>:
<tscreen><verb>
GtkWidget* gtk_hscrollbar_new( GtkAdjustment *adjustment );
GtkWidget* gtk_vscrollbar_new( GtkAdjustment *adjustment );
</verb></tscreen>
y esto es todo lo que hay (si no me cree, <20>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<61>
uno. Es <20>til especificar NULL si quiere pasar el ajuste reci<63>n
creado a la funci<63>n constructora de alg<6C>n otro <em/widget/ (como
por ejemplo el <em/widget/ texto) que se ocupar<61> 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<61> 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<61>n y no mediante un n<>mero concreto.
<!-- ----------------------------------------------------------------- -->
<sect2>Creaci<63>n de un <em/widget/ de escala
<p>
Existen dos tipos de <em/widgets/ de escala: GtkHScale (que es
horizontal) y GtkVscale (vertical). Como funcionan de la misma manera
los vamos a describir a la vez. Las funciones definidas en
<tt>&lt;gtk/gtkvscale.h&gt;</tt> y <tt>&lt;gtk/gtkhscale.h&gt;</tt>,
crean <em/widgets/ de escala verticales y horizontales
respectivamente.
<tscreen><verb>
GtkWidget* gtk_vscale_new( GtkAdjustment *adjustment );
GtkWidget* gtk_hscale_new( GtkAdjustment *adjustment );
</verb></tscreen>
El <tt/ajuste/ (adjustment) puede ser tanto un ajuste creado
mediante <tt/gtk_adjustment_new()/ como <tt/NULL/. En este
<EFBFBD>ltimo caso se crea un GtkAdjustment an<61>nimo con todos sus
valores iguales a <tt/0.0/. Si no ha quedado claro el uso de esta
funci<EFBFBD>n consulte la secci<63>n <ref id="sec_Adjustment"
name="Ajustes"> para una discusi<73>n m<>s detallada.
<!-- ----------------------------------------------------------------- -->
<sect2> Funciones y se<73>ales
<p>
Los <em/widgets/ de escala pueden indicar su valor actual como un
n<EFBFBD>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<70>ctica s<>lo se mostrar<61>n 13 como m<>ximo.
Por <20>ltimo, el valor se puede dibujar en diferentes posiciones con
respecto a la posici<63>n del rectangulo que hay dentro de la gu<67>a:
<tscreen>
<verb>
void gtk_scale_set_value_pos( GtkScale *scale,
GtkPositionType pos );
</verb>
</tscreen>
Si ha leido la secci<63>n acerca del <em/widget/ libro de notas
entonces ya conoce cuales son los valores posibles de <tt/pos/. Estan
definidos en <tt>&lt;gtk/gtkscale.h&gt;</tt> como <tt/enum GtkPositionType/
y son auto explicatorios. Si se escoge un lateral de la gu<67>a,
entonces seguir<69> al rect<63>ngulo a lo largo de la gu<67>a.
Todas las funcioenes precedentes se encuentran definidas en:
<tt>&lt;gtk/gtkscale.h&gt;</tt>.
</sect2>
</sect1>
<!-- ----------------------------------------------------------------- -->
<sect1> Funciones comunes <label id="sec_funciones_range">
<p>
La descripci<63>n interna de la clase GtkRange es bastante complicada,
pero al igual que con el resto de las <20>clases base<73> s<>lo es
interesante si se quiere <20>hackear<61>. Casi todas las se<73>ales y
funciones s<>lo son <20>tiles para desarrollar derivados. Para un
usuario normal las funciones interesantes son aquellas definidas en:
<tt>&lt;gtk/gtkrange.h&gt;</tt> y funcionan igual en todos los
<em/widgets/ de rango.
<!-- ----------------------------------------------------------------- -->
<sect2> Estableciendo cada c<>anto se actualizan
<p>
La pol<6F>tica de actualizaci<63>n de un <em/widget/ define en que
puntos de la interacci<63>n con el usuario debe cambiar el valor
<tt/value/ en su GtkAdjustment y emitir la se<73>al
<EFBFBD>value_changed<EFBFBD>. Las actualizaciones definidas en
<tt>&lt;gtk/gtkenums.h&gt;</tt> como <tt>enum GtkUpdateType</tt>, son:
<itemize>
<item>GTK_UPDATE_POLICY_CONTINUOUS - Este es el valor por defecto.La
se<EFBFBD>al <20>value_changed<65> se emite continuamente, por ejemplo cuando
la barra deslizante se mueve incluso aunque sea un poquito.
</item>
<item>GTK_UPDATE_POLICY_DISCONTINUOUS - La se<73>al <20>value_changed<65>
s<EFBFBD>lo se emite cuando se ha parado de mover la barra y el usuario ha
soltado el bot<6F>n del rat<61>n.
</item>
<item>GTK_UPDATE_POLICY_DELAYED - La se<73>al s<>lo se emite cuando
el usuario suelta el bot<6F>n del rat<61>n o si la barra no se mueve
durante un periodo largo de tiempo.
</item>
</itemize>
Para establecer la pol<6F>tica de actualizaci<63>n se usa la
conversi<EFBFBD>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<63>n <tt/gtk_range_get_adjustment()/ devuelve un puntero al
ajuste al que <tt/range/ est<73> conectado.
La funci<63>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<6C>n valor). En el caso de que
sea un ajuste nuevo (GtkAdjustment) dejar<61> de usar el antiguo
(probablemente lo destruir<69>) y conectar<61> las se<73>ales
apropiadas al nuevo. A continuaci<63>n llamar<61> a la funci<63>n
<tt/gtk_range_adjustment_changed()/ que en teor<6F>a recalcular<61> el
tama<EFBFBD>o y/o la posici<63>n de la barra, redibuj<75>ndola en caso de
que sea necesario. Tal y como se mencion<6F> en la secci<63>n de los
ajustes si se quiere reusar el mismo GtkAdjustment cuando se modifican
sus valores se debe emitir la se<73>al <20>changed<65>. 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<61>n
<p>
Todos los <em/widgets/ de rango reaccionan m<>s o menos de la misma
manera a las pulsaciones del rat<61>n. Al pulsar el bot<6F>n 1 sobre
el rect<63>ngulo de la barra el <tt/value/ del ajuste aumentar<61> o
disminuir<EFBFBD> seg<65>n <tt/page_increment/. Con el bot<6F>n 2 la barra
se desplazar<61> al punto en el que el bot<6F>n fue pulsado. Con cada
pulsaci<EFBFBD>n de cualquier bot<6F>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<63>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<63>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<63>n (focus)) se comportan de manera
diferente para los <em/widgets/ de rango horizontales que para los
verticales. Tambi<62>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<63>an sobre la misma <20>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<65>n <tt/page_increment/.
El usuario tambi<62>n puede mover la barra de un extremo al otro de la
gu<EFBFBD>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<62> 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<67>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<73>n modificada del test <20>range controls<6C>
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<61>metros
ya mencionados. As<41> se consigue ver como funcionan estos
<em/widgets/ al ser manipulados por el usuario.
<tscreen><verb>
/* principio del ejemplo widgets de selecci<63>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<6F>tica de actualizaci<63>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<61> 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<6D>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<73>al "changed" para reconfigurar todos los
* widgets que est<73>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<6F>n de comprobaci<63>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<63>n est<73>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<64> ser<65> (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<6F>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<63>n en el men<65> para cambiar la posici<63>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<63>n de men<65>, esta vez para la pol<6F>tica
* de actualizaci<63>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 <20>ltimo widget GtkHScale para ajustar el tama<6D>o de la
* p<>gina de la barra de desplazamiento. */
etiqueta = gtk_label_new ("Scrollbar Page Size:");
gtk_box_pack_start (GTK_BOX (caja2), etiqueta, FALSE, FALSE, 0);
gtk_widget_show (etiqueta);
adj2 = gtk_adjustment_new (1.0, 1.0, 101.0, 1.0, 1.0, 0.0);
gtk_signal_connect (GTK_OBJECT (adj2), "value_changed",
GTK_SIGNAL_FUNC (cb_page_size), adj1);
scale = gtk_hscale_new (GTK_ADJUSTMENT (adj2));
gtk_scale_set_digits (GTK_SCALE (scale), 0);
gtk_box_pack_start (GTK_BOX (caja2), scale, TRUE, TRUE, 0);
gtk_widget_show (scale);
gtk_box_pack_start (GTK_BOX (caja1), caja2, TRUE, TRUE, 0);
gtk_widget_show (caja2);
separator = gtk_hseparator_new ();
gtk_box_pack_start (GTK_BOX (caja1), separator, FALSE, TRUE, 0);
gtk_widget_show (separator);
caja2 = gtk_vbox_new (FALSE, 10);
gtk_container_border_width (GTK_CONTAINER (caja2), 10);
gtk_box_pack_start (GTK_BOX (caja1), caja2, FALSE, TRUE, 0);
gtk_widget_show (caja2);
boton = gtk_button_new_with_label ("Quit");
gtk_signal_connect_object (GTK_OBJECT (boton), "clicked",
GTK_SIGNAL_FUNC(gtk_main_quit),
NULL);
gtk_box_pack_start (GTK_BOX (caja2), boton, TRUE, TRUE, 0);
GTK_WIDGET_SET_FLAGS (boton, GTK_CAN_DEFAULT);
gtk_widget_grab_default (boton);
gtk_widget_show (boton);
gtk_widget_show (ventana);
}
int main( int argc,
char *argv[] )
{
gtk_init(&amp;argc, &amp;argv);
create_range_controls();
gtk_main();
return(0);
}
/* fin del ejemplo */
</verb></tscreen>
Observe que el programa no llama a <tt/gtk_signal_connect/ para
conectar el <20>delete_event<6E>, y que s<>lo conecta la se<73>al
<EFBFBD>destroy<EFBFBD>. Con esto seguimos realizando la funci<63>n deseada, ya que
un <20>delete_event<6E> no manejado desenboca en una se<73>al <20>destroy<6F>
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<73>ales ya que no tienen ventanas X window
asociadas. Si se desea capturar se<73>ales se debe usar el <em/widget/
EventBox o un <em/widget/ bot<6F>n.
Para crear una nueva etiqueta se usa:
<tscreen><verb>
GtkWidget *gtk_label_new( char *str );
</verb></tscreen>
El <20>nico argumento es la cadena de texto que se quiere mostrar.
Para cambiarla despu<70>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<61>
autom<EFBFBD>ticamente, si es necesario.
Para obtener el estado de la cadena en un momento dado existe la
funci<EFBFBD>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<62>n es capaz de separar el texto de forma
autom<EFBFBD>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<61>cteres
de subrayado. Por ejemplo, la cadena <tt/"__ __"/ debe hacer que
se subrayen los dos primeros y el octavo y el noveno car<61>cter.
A continuaci<63>n tenemos un peque<75>o ejemplo que ilustra el uso de estas
funciones. Este ejemplo utiliza el <em/widget/ marco (<em/frame/) para
hacer una mejor demostraci<63>n de los estilos de la etiqueta. Por ahora
puede ignorarlo, ya que el <em/widget/ <ref id="sec_Frames"
name="Frame"> se explicar<61> m<>s tarde.
<tscreen><verb>
/* principio del ejemplo label label.c */
#include <gtk/gtk.h>
int main( int argc,
char *argv[] )
{
static GtkWidget *ventana = NULL;
GtkWidget *hbox;
GtkWidget *vbox;
GtkWidget *frame;
GtkWidget *etiqueta;
/* Inicializa GTK */
gtk_init(&amp;argc, &amp;argv);
ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
GTK_SIGNAL_FUNC(gtk_main_quit),
NULL);
gtk_window_set_title (GTK_WINDOW (ventana), "Etiqueta");
vbox = gtk_vbox_new (FALSE, 5);
hbox = gtk_hbox_new (FALSE, 5);
gtk_container_add (GTK_CONTAINER (ventana), hbox);
gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
gtk_container_set_border_width (GTK_CONTAINER (ventana), 5);
frame = gtk_frame_new ("Normal Label");
etiqueta = gtk_label_new ("This is a Normal label");
gtk_container_add (GTK_CONTAINER (frame), etiqueta);
gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
frame = gtk_frame_new ("Multi-line Label");
etiqueta = gtk_label_new ("This is a Multi-line label.\nSecond line\n" \
"Third line");
gtk_container_add (GTK_CONTAINER (frame), etiqueta);
gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
frame = gtk_frame_new ("Left Justified Label");
etiqueta = gtk_label_new ("This is a Left-Justified\n" \
"Multi-line label.\nThird line");
gtk_label_set_justify (GTK_LABEL (etiqueta), GTK_JUSTIFY_LEFT);
gtk_container_add (GTK_CONTAINER (frame), etiqueta);
gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
frame = gtk_frame_new ("Right Justified Label");
etiqueta = gtk_label_new ("This is a Right-Justified\nMulti-line label.\n" \
"Fourth line, (j/k)");
gtk_label_set_justify (GTK_LABEL (etiqueta), GTK_JUSTIFY_RIGHT);
gtk_container_add (GTK_CONTAINER (frame), etiqueta);
gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
vbox = gtk_vbox_new (FALSE, 5);
gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
frame = gtk_frame_new ("Line wrapped label");
etiqueta = gtk_label_new ("This is an example of a line-wrapped label. It " \
"should not be taking up the entire " /* big space to test spacing */\
"width allocated to it, but automatically " \
"wraps the words to fit. " \
"The time has come, for all good men, to come to " \
"the aid of their party. " \
"The sixth sheik's six sheep's sick.\n" \
" It supports multiple paragraphs correctly, " \
"and correctly adds "\
"many extra spaces. ");
gtk_label_set_line_wrap (GTK_LABEL (etiqueta), TRUE);
gtk_container_add (GTK_CONTAINER (frame), etiqueta);
gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
frame = gtk_frame_new ("Filled, wrapped label");
etiqueta = gtk_label_new ("This is an example of a line-wrapped, filled label. " \
"It should be taking "\
"up the entire width allocated to it. " \
"Here is a seneance to prove "\
"my point. Here is another sentence. "\
"Here comes the sun, do de do de do.\n"\
" This is a new paragraph.\n"\
" This is another newer, longer, better " \
"paragraph. It is coming to an end, "\
"unfortunately.");
gtk_label_set_justify (GTK_LABEL (etiqueta), GTK_JUSTIFY_FILL);
gtk_label_set_line_wrap (GTK_LABEL (etiqueta), TRUE);
gtk_container_add (GTK_CONTAINER (frame), etiqueta);
gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
frame = gtk_frame_new ("Underlined label");
etiqueta = gtk_label_new ("This label is underlined!\n"
"This one is underlined in quite a funky fashion");
gtk_label_set_justify (GTK_LABEL (etiqueta), GTK_JUSTIFY_LEFT);
gtk_label_set_pattern (GTK_LABEL (etiqueta),
"_________________________ _ _________ _ ______ __ _______ ___");
gtk_container_add (GTK_CONTAINER (frame), etiqueta);
gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
gtk_widget_show_all (ventana);
gtk_main ();
return(0);
}
/* fin del ejemplo */
</verb></tscreen>
<!-- ----------------------------------------------------------------- -->
<sect1> Flechas
<p>
En <em/widget/ flecha (<em/arrow/) dibuja la punta de una flecha,
con un estilo y hacia una direcci<63>n a escoger. Puede ser muy <20>til
en muchas aplicaciones cuando se coloca en un bot<6F>n.
S<EFBFBD>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<63>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<EFBFBD> tenemos un peque<75>o ejemplo para ilustrar la utilizaci<63>n de la
flecha.
<tscreen><verb>
/* principio del ejemplo arrow arrow.c */
#include <gtk/gtk.h>
/* Crea un widget flecha con los par<61>metros especificados
* y lo empaqueta en un bot<6F>n */
GtkWidget *create_arrow_button( GtkArrowType arrow_type,
GtkShadowType shadow_type )
{
GtkWidget *boton;
GtkWidget *arrow;
boton = gtk_button_new();
arrow = gtk_arrow_new (arrow_type, shadow_type);
gtk_container_add (GTK_CONTAINER (boton), arrow);
gtk_widget_show(boton);
gtk_widget_show(arrow);
return(boton);
}
int main( int argc,
char *argv[] )
{
/* GtkWidget es el tipo utilizado para los widgets */
GtkWidget *ventana;
GtkWidget *boton;
GtkWidget *box;
/* Inicializa el toolkit */
gtk_init (&amp;argc, &amp;argv);
/* Crea una nueva ventana */
ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (ventana), "Arrow Buttons");
/* Es una buena idea hacer esto con todas las ventanas. */
gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
/* Establece el ancho del borde de la ventana. */
gtk_container_set_border_width (GTK_CONTAINER (ventana), 10);
/* Crea una caja para almacenar las flechas/botones */
box = gtk_hbox_new (FALSE, 0);
gtk_container_set_border_width (GTK_CONTAINER (box), 2);
gtk_container_add (GTK_CONTAINER (ventana), box);
/* Empaqueta y muestra todos nuestros widgets */
gtk_widget_show(box);
boton = create_arrow_button(GTK_ARROW_UP, GTK_SHADOW_IN);
gtk_box_pack_start (GTK_BOX (box), boton, FALSE, FALSE, 3);
boton = create_arrow_button(GTK_ARROW_DOWN, GTK_SHADOW_OUT);
gtk_box_pack_start (GTK_BOX (box), boton, FALSE, FALSE, 3);
boton = create_arrow_button(GTK_ARROW_LEFT, GTK_SHADOW_ETCHED_IN);
gtk_box_pack_start (GTK_BOX (box), boton, FALSE, FALSE, 3);
boton = create_arrow_button(GTK_ARROW_RIGHT, GTK_SHADOW_ETCHED_OUT);
gtk_box_pack_start (GTK_BOX (box), boton, FALSE, FALSE, 3);
gtk_widget_show (ventana);
/* Nos quedamos en gtk_main y <20>esperamos que empiece la diversi<73>n! */
gtk_main ();
return(0);
}
/* fin del ejemplo */
</verb></tscreen>
<!-- ----------------------------------------------------------------- -->
<sect1>El <em/widget/ de informaci<63>n r<>pida (<em/tooltip/)
<p>
Estos <em/widgets/ son las peque<75>as etiquetas que texto que
aparecen cuando se sit<69>a el puntero del rat<61>n sobre un bot<6F>n
u otro <em/widget/ durante algunos segundos. Son bastante f<>ciles
de usar, as<61> que no se dar<61> ning<6E>n ejemplo. Si quiere ver
alg<EFBFBD>n ejemplo se recomienda leer el programa testgtk.c que
acompa<EFBFBD>a a GTK.
Algunos <em/widgets/ (como la etiqueta) no pueden llevar asociado un
<em/tooltip/.
Para cada funci<63>n s<>lo hay que hacer una llamada para conseguir
un <em/tooltip/. El objeto <tt/GtkTooltip/ que devuelve la siguiente
funci<EFBFBD>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 <20>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<6F>n 1");
...
gtk_tooltips_set_tip (tooltips, boton, "Este es el bot<6F>n 1", NULL);
</verb></tscreen>
Existen otras funciones que pueden ser usadas con los <em/tooltips/.
Solamente vamos a enumerlarlas a<>adiendo una peque<75>a descripci<63>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<63>n. Son
bastante sencillas de utilizar, tal y como se ver<65> 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<61> 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<61>metros para la
barra de progreso.
El ajuste de una barra de progreso se puede cambiar de forma din<69>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<65>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<63>n
<tscreen><verb>
void gtk_progress_bar_set_orientation( GtkProgressBar *pbar,
GtkProgressBarOrientation orientation );
</verb></tscreen>
Donde el argumento <tt/orientaci<63>n/ puede tomar uno de los valores que
vienen a continuaci<63>n para indicar la direcci<63>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<EFBFBD>mero de bloques tambi<62>n es configurable.
Se puede configurar el estilo de la barra de progreso utilizando la
siguiente funci<63>n:
<tscreen><verb>
void gtk_progress_bar_set_bar_style( GtkProgressBar *pbar,
GtkProgressBarStyle style );
</verb></tscreen>
El par<61>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<62>n se puede utilizar, a parte de para
indicar lo <20>avanzado<64> de una tarea, para indicar que hay alg<6C>n tipo
de actividad. Esto puede ser <20>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<EFBFBD>n:
<tscreen><verb>
void gtk_progress_set_activity_mode( GtkProgress *progress,
guint activity_mode );
</verb></tscreen>
El tama<6D>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<63>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<63>n de la cadena de texto dentro de la
barra. Si ponemos 0.0 en los dos sitios la cadena de texto aparecer<65>
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<61>metros actuales del texto de un objeto barra
de progreso utilizando las dos funciones que se muestran a
continuaci<EFBFBD>n. La cadena de car<61>cteres devuelta por estas funciones
debe liberarse en la aplicaci<63>n (utilizando la funci<63>n
g_free()). Estas funciones devuelven el texto formateado que se
mostrar<EFBFBD> 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<63>n:
<tscreen><verb>
void gtk_progress_configure( GtkProgress *progress,
gfloat value,
gfloat min,
gfloat max );
</verb></tscreen>
Esta funci<63>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 <20>ltima funci<63>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<63>n <ref id="sec_timeouts"
name="Tiempos de espera, E/S (I/O) y funciones ociosas (idle)">) para
crear la ilusi<73>n de la multitarea. Todas usan la funci<63>n
gtk_progress_bar_update de la misma manera.
Estudiemos un ejemplo de barras de progreso actualizada usando
tiempos de espera. Tambi<62>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<6C>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<63>n de espera, devolvemos TRUE
* para que continue siendo llamada */
return(TRUE);
}
/* Funci<63>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<63>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<63>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<63>n de llamada que activa/desactiva el modo discreto
* de la barra de progreso */
void set_discrete_mode( GtkWidget *widget,
ProgressData *pdata )
{
gtk_progress_bar_set_bar_style (GTK_PROGRESS_BAR (pdata->pbar),
GTK_PROGRESS_DISCRETE);
}
/* Libera la memoria y elimina el temporizador */
void destroy_progress( GtkWidget *widget,
ProgressData *pdata)
{
gtk_timeout_remove (pdata->timer);
pdata->timer = 0;
pdata->ventana = NULL;
g_free(pdata);
gtk_main_quit();
}
int main( int argc,
char *argv[])
{
ProgressData *pdata;
GtkWidget *align;
GtkWidget *separator;
GtkWidget *table;
GtkAdjustment *adj;
GtkWidget *boton;
GtkWidget *check;
GtkWidget *vbox;
gtk_init (&amp;argc, &amp;argv);
/* Reserva memoria para los datos que se le pasan a las funciones
* de llamada */
pdata = g_malloc( sizeof(ProgressData) );
pdata->ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_policy (GTK_WINDOW (pdata->ventana), FALSE, FALSE, TRUE);
gtk_signal_connect (GTK_OBJECT (pdata->ventana), "destroy",
GTK_SIGNAL_FUNC (destroy_progress),
pdata);
gtk_window_set_title (GTK_WINDOW (pdata->ventana), "GtkProgressBar");
gtk_container_set_border_width (GTK_CONTAINER (pdata->ventana), 0);
vbox = gtk_vbox_new (FALSE, 5);
gtk_container_set_border_width (GTK_CONTAINER (vbox), 10);
gtk_container_add (GTK_CONTAINER (pdata->ventana), vbox);
gtk_widget_show(vbox);
/* Crea un objeto de alineamiento centrado */
align = gtk_alignment_new (0.5, 0.5, 0, 0);
gtk_box_pack_start (GTK_BOX (vbox), align, FALSE, FALSE, 5);
gtk_widget_show(align);
/* Crea un objeto GtkAdjusment para albergar el rango de la barra
* de progreso */
adj = (GtkAdjustment *) gtk_adjustment_new (0, 1, 150, 0, 0, 0);
/* Crea la GtkProgressBar utilizando el ajuste */
pdata->pbar = gtk_progress_bar_new_with_adjustment (adj);
/* Establece el formato de la cadena de texto que puede mostrarse
* en la barra de progreso:
* %p - porcentaje
* %v - valor
* %l - valor inferior del rango
* %u - valor superior del rango */
gtk_progress_set_format_string (GTK_PROGRESS (pdata->pbar),
"%v from [%l-%u] (=%p%%)");
gtk_container_add (GTK_CONTAINER (align), pdata->pbar);
gtk_widget_show(pdata->pbar);
/* A<>ade un temporizador para la actualizaci<63>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<6F>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<6F>n de comprobaci<63>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<6F>n de comprobaci<63>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<6F>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<6F>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<6F>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<6F>n sea el bot<6F>n pueda utilizarse por
* defecto defecto. */
GTK_WIDGET_SET_FLAGS (boton, GTK_CAN_DEFAULT);
/* Esto marca este bot<6F>n para que sea el bot<6F>n por
* defecto. Simplemente utilizando la tecla "Intro" haremos que se
* active este bot<6F>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<64>logo
<p>
El <em/widget/ del cuadro de di<64>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 <20>action_area<65>.
Este tipo de <em/widgets/ pueden ser usados como mensages <em/pop-up/
(peque<75>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<63>n:
<tscreen><verb>
GtkWidget *gtk_dialog_new( void );
</verb></tscreen>
Para crear un nuevo cuadro de di<64>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<6F>n en la action_area escribir<69>amos
algo as<61>:
<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<EFBFBD>ndose en el ordenador que est<73> interactuando con el
usuario. Las distintas aplicaciones, llamadas <20>clientes<65>, comunican
con un programa que muestra los gr<67>ficos y que controla el tecledo y
el rat<61>n. Este programa que interactua directamente con el usuario se
llama un <20><em/display server/<2F> o <20>servidor X<>. Como la
comunicaci<EFBFBD>n entre el servidor y el cliente puede llevarse a cabo
mediante una red, es importante mantener alguna informaci<63>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<74>ndose por la
red; en su lugar lo <20>nico que hay que enviar es una orden del estilo
<EFBFBD>mostrar <em/pixmap/ n<>mero XYZ aqu<71><75>. Incluso si no est<73> utilizando
X-windows con GTK, al utilizar construcciones como los <em/pixmaps/
conseguir<EFBFBD> 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<63>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<EFBFBD>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<69>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<65>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<69>genes peque<75>as dentro de un programa en
formato XPM. Un <em/pixmap/ se crea usando esta informaci<63>n, en
lugar de leerla de un archivo. Un ejemplo ser<65>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<63>n gtk_pixmap_set se usa para cambiar los datos del
<em/pixmap/ que el <em/widget/ est<73> manejando en ese
momento. <tt/val/ es el <em/pixmap/ creado usando GDK.
El ejemplo siguiente usa un <em/pixmap/ en un bot<6F>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<63>n (usando signal delete_event) se
* termina la aplicaci<63>n*/
void close_application( GtkWidget *widget, GdkEvent *event, gpointer data ) {
gtk_main_quit();
}
/* Al presionar el bot<6F>n aparece el mensaje */
void button_clicked( GtkWidget *widget, gpointer data ) {
printf( "bot<6F>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<73>al
* delete_event con acabar el programa.*/
gtk_init( &amp;argc, &amp;argv );
ventana = gtk_window_new( GTK_WINDOW_TOPLEVEL );
gtk_signal_connect( GTK_OBJECT (ventana), "delete_event",
GTK_SIGNAL_FUNC (close_application), NULL );
gtk_container_border_width( GTK_CONTAINER (ventana), 10 );
gtk_widget_show( ventana );
/* Ahora para el pixmap de gdk */
style = gtk_widget_get_style( ventana );
pixmap = gdk_pixmap_create_from_xpm_d( ventana->window, &amp;mask,
&amp;style->bg[GTK_STATE_NORMAL],
(gchar **)xpm_data );
/* Un pixmap widget que contendr<64> al pixmap */
pixmapwid = gtk_pixmap_new( pixmap, mask );
gtk_widget_show( pixmapwid );
/* Un bot<6F>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<63>n XPM (que
se encuentra en en directorio actual) habr<62>amos usado:
<tscreen><verb>
/* cargar un pixmap desde un fichero */
pixmap = gdk_pixmap_create_from_xpm( ventana->window, &amp;mask,
&amp;style->bg[GTK_STATE_NORMAL],
"./icon0.xpm" );
pixmapwid = gtk_pixmap_new( pixmap, mask );
gtk_widget_show( pixmapwid );
gtk_container_add( GTK_CONTAINER(ventana), pixmapwid );
</verb></tscreen>
Una desventaja de los <em/pixmaps/ es que la imagen mostrada siempre
es rectangular (independientemente de como sea la imagen en s<>). Si
queremos usar im<69>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<41> cuando la imagen del fondo tiene muchos colores
no los sobreescribimos con el borde de nuestro icono. El ejemplo
siguiente muestra la imagen de una carretilla en el escritorio.
<tscreen><verb>
/* comienzo del ejemplo carretilla wheelbarrow.c */
#include <gtk/gtk.h>
/* XPM */
static char * WheelbarrowFull_xpm[] = {
"48 48 64 1",
" c None",
". c #DF7DCF3CC71B",
"X c #965875D669A6",
"o c #71C671C671C6",
"O c #A699A289A699",
"+ c #965892489658",
"@ c #8E38410330C2",
"# c #D75C7DF769A6",
"$ c #F7DECF3CC71B",
"% c #96588A288E38",
"&amp; c #A69992489E79",
"* c #8E3886178E38",
"= c #104008200820",
"- c #596510401040",
"; c #C71B30C230C2",
": c #C71B9A699658",
"> c #618561856185",
", c #20811C712081",
"< c #104000000000",
"1 c #861720812081",
"2 c #DF7D4D344103",
"3 c #79E769A671C6",
"4 c #861782078617",
"5 c #41033CF34103",
"6 c #000000000000",
"7 c #49241C711040",
"8 c #492445144924",
"9 c #082008200820",
"0 c #69A618611861",
"q c #B6DA71C65144",
"w c #410330C238E3",
"e c #CF3CBAEAB6DA",
"r c #71C6451430C2",
"t c #EFBEDB6CD75C",
"y c #28A208200820",
"u c #186110401040",
"i c #596528A21861",
"p c #71C661855965",
"a c #A69996589658",
"s c #30C228A230C2",
"d c #BEFBA289AEBA",
"f c #596545145144",
"g c #30C230C230C2",
"h c #8E3882078617",
"j c #208118612081",
"k c #38E30C300820",
"l c #30C2208128A2",
"z c #38E328A238E3",
"x c #514438E34924",
"c c #618555555965",
"v c #30C2208130C2",
"b c #38E328A230C2",
"n c #28A228A228A2",
"m c #41032CB228A2",
"M c #104010401040",
"N c #492438E34103",
"B c #28A2208128A2",
"V c #A699596538E3",
"C c #30C21C711040",
"Z c #30C218611040",
"A c #965865955965",
"S c #618534D32081",
"D c #38E31C711040",
"F c #082000000820",
" ",
" .XoO ",
" +@#$%o&amp; ",
" *=-;#::o+ ",
" >,<12#:34 ",
" 45671#:X3 ",
" +89<02qwo ",
"e* >,67;ro ",
"ty> 459@>+&amp;&amp; ",
"$2u+ ><ipas8* ",
"%$;=* *3:.Xa.dfg> ",
"Oh$;ya *3d.a8j,Xe.d3g8+ ",
" Oh$;ka *3d$a8lz,,xxc:.e3g54 ",
" Oh$;kO *pd$%svbzz,sxxxxfX..&amp;wn> ",
" Oh$@mO *3dthwlsslszjzxxxxxxx3:td8M4 ",
" Oh$@g&amp; *3d$XNlvvvlllm,mNwxxxxxxxfa.:,B* ",
" Oh$@,Od.czlllllzlmmqV@V#V@fxxxxxxxf:%j5&amp; ",
" Oh$1hd5lllslllCCZrV#r#:#2AxxxxxxxxxcdwM* ",
" OXq6c.%8vvvllZZiqqApA:mq:Xxcpcxxxxxfdc9* ",
" 2r<6gde3bllZZrVi7S@SV77A::qApxxxxxxfdcM ",
" :,q-6MN.dfmZZrrSS:#riirDSAX@Af5xxxxxfevo",
" +A26jguXtAZZZC7iDiCCrVVii7Cmmmxxxxxx%3g",
" *#16jszN..3DZZZZrCVSA2rZrV7Dmmwxxxx&amp;en",
" p2yFvzssXe:fCZZCiiD7iiZDiDSSZwwxx8e*>",
" OA1<jzxwwc:$d%NDZZZZCCCZCCZZCmxxfd.B ",
" 3206Bwxxszx%et.eaAp77m77mmmf3&amp;eeeg* ",
" @26MvzxNzvlbwfpdettttttttttt.c,n&amp; ",
" *;16=lsNwwNwgsvslbwwvccc3pcfu<o ",
" p;<69BvwwsszslllbBlllllllu<5+ ",
" OS0y6FBlvvvzvzss,u=Blllj=54 ",
" c1-699Blvlllllu7k96MMMg4 ",
" *10y8n6FjvllllB<166668 ",
" S-kg+>666<M<996-y6n<8* ",
" p71=4 m69996kD8Z-66698&amp;&amp; ",
" &amp;i0ycm6n4 ogk17,0<6666g ",
" N-k-<> >=01-kuu666> ",
" ,6ky&amp; &amp;46-10ul,66, ",
" Ou0<> o66y<ulw<66&amp; ",
" *kk5 >66By7=xu664 ",
" <<M4 466lj<Mxu66o ",
" *>> +66uv,zN666* ",
" 566,xxj669 ",
" 4666FF666> ",
" >966666M ",
" oM6668+ ",
" *4 ",
" ",
" "};
void close_application( GtkWidget *widget, GdkEvent *event, gpointer data ) {
gtk_main_quit();
}
int main (int argc, char *argv[])
{
GtkWidget *ventana, *pixmap, *fixed;
GdkPixmap *gdk_pixmap;
GdkBitmap *mask;
GtkStyle *style;
GdkGC *gc;
/* Creamos la ventana principal y relacionamos la se<73>al
* delete_event para terminar la aplicaci<63>n. Conviene destacar
* que la ventana no tendr<64> t<>tulo puesto que es popup.*/
gtk_init (&amp;argc, &amp;argv);
ventana = gtk_window_new( GTK_WINDOW_POPUP );
gtk_signal_connect (GTK_OBJECT (ventana), "delete_event",
GTK_SIGNAL_FUNC (close_application), NULL);
gtk_widget_show (ventana);
style = gtk_widget_get_default_style();
gc = style->black_gc;
gdk_pixmap = gdk_pixmap_create_from_xpm_d( ventana->window, &amp;mask,
&amp;style->bg[GTK_STATE_NORMAL],
WheelbarrowFull_xpm );
pixmap = gtk_pixmap_new( gdk_pixmap, mask );
gtk_widget_show( pixmap );
fixed = gtk_fixed_new();
gtk_widget_set_usize( fixed, 200, 200 );
gtk_fixed_put( GTK_FIXED(fixed), pixmap, 0, 0 );
gtk_container_add( GTK_CONTAINER(ventana), fixed );
gtk_widget_show( fixed );
/* Con esto cubrimos todo menos la imagen */
gtk_widget_shape_combine_mask( ventana, mask, 0, 0 );
/* mostramos la ventana */
gtk_widget_set_uposition( ventana, 20, 400 );
gtk_widget_show( ventana );
gtk_main ();
return 0;
}
/* final del ejemplo */
</verb></tscreen>
Para que la carretilla sea m<>s realista podr<64>amos relacionar la
pulsaci<EFBFBD>n del bot<6F>n con que haga algo. Con las l<>neas
siguientes la pulsaci<63>n del bot<6F>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<63>n del puntero del
rat<EFBFBD>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<75>o indicador triangular muestra la relaci<63>n entre
el puntero del rat<61>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<6E>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<65>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<73>n de la regla. El argumento
<tt/max_size/ es el n<>mero m<>s alto que ser<65> mostrado. Como
es l<>gico <tt/posicion/ define la posici<63>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<61>n entre 0 y 800 con una
periodicidad de 100. Si queremos que var<61>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<75>o tri<72>ngulo que se<73>ala la
posici<EFBFBD>n del puntero con relaci<63>n a la regla. Si la regla debe
seguir al puntero del rat<61>n la se<73>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<6D>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<69>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<6F>n close
*/
void close_application( GtkWidget *widget, GdkEvent *event, gpointer data ) {
gtk_main_quit();
}
int main( int argc, char *argv[] ) {
GtkWidget *ventana, *table, *area, *hrule, *vrule;
gtk_init( &amp;argc, &amp;argv );
ventana = gtk_window_new( GTK_WINDOW_TOPLEVEL );
gtk_signal_connect (GTK_OBJECT (ventana), "delete_event",
GTK_SIGNAL_FUNC( close_application ), NULL);
gtk_container_border_width (GTK_CONTAINER (ventana), 10);
/* creaci<63>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<73> arriba. Cuando el rat<61>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 <20>ltimo mensaje.
Para permitir que diferentes partes del programa usen la misma barra
de estado <20>stas usan Identificadores por Contexto (Context
Identifiers) para identificar a los `usuarios'. El mensaje que est<73>
en lo alto de la pila ser<65> el siguiente en mostrarse, sin importar
el contexto en el que se est<73>. Los mensajes se almacenan en el
orden el <20>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<75>a
descripci<EFBFBD>n textual del contexto y una llamada a la funci<63>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<63>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<63>n gtk_statusbar_pop elimina el mensaje que se encuentra
m<EFBFBD>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 <20>ltimo elemento
introducido.
<tscreen><verb>
/* Principio del ejemplo de barras de estado statusbar.c */
#include <gtk/gtk.h>
#include <glib.h>
GtkWidget *status_bar;
void push_item (GtkWidget *widget, gpointer data)
{
static int count = 1;
char buff[20];
g_snprintf(buff, 20, "Item %d", count++);
gtk_statusbar_push( GTK_STATUSBAR(status_bar), GPOINTER_TO_INT(data), buff);
return;
}
void pop_item (GtkWidget *widget, gpointer data)
{
gtk_statusbar_pop( GTK_STATUSBAR(status_bar), GPOINTER_TO_INT(data) );
return;
}
int main (int argc, char *argv[])
{
GtkWidget *ventana;
GtkWidget *vbox;
GtkWidget *boton;
int context_id;
gtk_init (&amp;argc, &amp;argv);
/* crear una nueva ventana */
ventana = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_widget_set_usize( GTK_WIDGET (ventana), 200, 100);
gtk_window_set_title(GTK_WINDOW (ventana), "GTK Statusbar Example");
gtk_signal_connect(GTK_OBJECT (ventana), "delete_event",
(GtkSignalFunc) gtk_exit, NULL);
vbox = gtk_vbox_new(FALSE, 1);
gtk_container_add(GTK_CONTAINER(ventana), vbox);
gtk_widget_show(vbox);
status_bar = gtk_statusbar_new();
gtk_box_pack_start (GTK_BOX (vbox), status_bar, TRUE, TRUE, 0);
gtk_widget_show (status_bar);
context_id = gtk_statusbar_get_context_id(
GTK_STATUSBAR(status_bar), "Statusbar example");
boton = gtk_button_new_with_label("push item");
gtk_signal_connect(GTK_OBJECT(boton), "clicked",
GTK_SIGNAL_FUNC (push_item), &amp;context_id);
gtk_box_pack_start(GTK_BOX(vbox), boton, TRUE, TRUE, 2);
gtk_widget_show(boton);
boton = gtk_button_new_with_label("pop last item");
gtk_signal_connect(GTK_OBJECT(boton), "clicked",
GTK_SIGNAL_FUNC (pop_item), &amp;context_id);
gtk_box_pack_start(GTK_BOX(vbox), boton, TRUE, TRUE, 2);
gtk_widget_show(boton);
/* siempre mostramos la ventana en el <20>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<64>logo. El texto se puede poner con llamadas a funciones
que permiten reemplazar, prea<65>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<65>s establece un l<>mite en la longitud
del texto que ir<69> en el mismo.
hay varias funciones que sirven para alterar el que texto que se est<73>
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<63>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<65>adir texto.
Las funci<63>n siguientes permiten decir donde poner el punto de inserci<63>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<63>n
que se describe a continuaci<63>n. Obtener los contenidos del <em/widget/
puede ser <20>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 <20>l, utilice la funci<63>n
<tscreen><verb>
void gtk_entry_set_editable( GtkEntry *entry,
gboolean editable );
</verb></tscreen>
Esta funci<63>n permite camiar el estado de edici<63>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<63>n siguiente, que tambi<62>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<67>n del texto utilizando la siguiente funci<63>n.
Esta funci<63>n se puede utilizar despu<70>s de poner alg<6C>n texto por defecto en
el <em/widget/, haci<63>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<73>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<6C>n car<61>cter.
<tscreen><verb>
/* Principio del ejemplo entry entry.c */
#include <gtk/gtk.h>
void enter_callback(GtkWidget *widget, GtkWidget *entry)
{
gchar *entry_text;
entry_text = gtk_entry_get_text(GTK_ENTRY(entry));
printf("Entry contents: %s\n", entry_text);
}
void entry_toggle_editable (GtkWidget *checkbutton,
GtkWidget *entry)
{
gtk_entry_set_editable(GTK_ENTRY(entry),
GTK_TOGGLE_BUTTON(checkbutton)->active);
}
void entry_toggle_visibility (GtkWidget *checkbutton,
GtkWidget *entry)
{
gtk_entry_set_visibility(GTK_ENTRY(entry),
GTK_TOGGLE_BUTTON(checkbutton)->active);
}
int main (int argc, char *argv[])
{
GtkWidget *ventana;
GtkWidget *vbox, *hbox;
GtkWidget *entry;
GtkWidget *boton;
GtkWidget *check;
gtk_init (&amp;argc, &amp;argv);
/* crear una nueva ventana */
ventana = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_widget_set_usize( GTK_WIDGET (ventana), 200, 100);
gtk_window_set_title(GTK_WINDOW (ventana), "GTK Entry");
gtk_signal_connect(GTK_OBJECT (ventana), "delete_event",
(GtkSignalFunc) gtk_exit, NULL);
vbox = gtk_vbox_new (FALSE, 0);
gtk_container_add (GTK_CONTAINER (ventana), vbox);
gtk_widget_show (vbox);
entry = gtk_entry_new_with_max_length (50);
gtk_signal_connect(GTK_OBJECT(entry), "activate",
GTK_SIGNAL_FUNC(enter_callback),
entry);
gtk_entry_set_text (GTK_ENTRY (entry), "hello");
gtk_entry_append_text (GTK_ENTRY (entry), " world");
gtk_entry_select_region (GTK_ENTRY (entry),
0, GTK_ENTRY(entry)->text_length);
gtk_box_pack_start (GTK_BOX (vbox), entry, TRUE, TRUE, 0);
gtk_widget_show (entry);
hbox = gtk_hbox_new (FALSE, 0);
gtk_container_add (GTK_CONTAINER (vbox), hbox);
gtk_widget_show (hbox);
check = gtk_check_button_new_with_label("Editable");
gtk_box_pack_start (GTK_BOX (hbox), check, TRUE, TRUE, 0);
gtk_signal_connect (GTK_OBJECT(check), "toggled",
GTK_SIGNAL_FUNC(entry_toggle_editable), entry);
gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(check), TRUE);
gtk_widget_show (check);
check = gtk_check_button_new_with_label("Visible");
gtk_box_pack_start (GTK_BOX (hbox), check, TRUE, TRUE, 0);
gtk_signal_connect (GTK_OBJECT(check), "toggled",
GTK_SIGNAL_FUNC(entry_toggle_visibility), entry);
gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(check), TRUE);
gtk_widget_show (check);
boton = gtk_button_new_with_label ("Close");
gtk_signal_connect_object (GTK_OBJECT (boton), "clicked",
GTK_SIGNAL_FUNC(gtk_exit),
GTK_OBJECT (ventana));
gtk_box_pack_start (GTK_BOX (vbox), boton, TRUE, TRUE, 0);
GTK_WIDGET_SET_FLAGS (boton, GTK_CAN_DEFAULT);
gtk_widget_grab_default (boton);
gtk_widget_show (boton);
gtk_widget_show(ventana);
gtk_main();
return(0);
}
/* fin del ejemplo */
</verb></tscreen>
<!-- ----------------------------------------------------------------- -->
<sect1>Botones <em/spin/
<p>
El <em/widget/ bot<6F>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<62>n podemos introducir directamente un valor espec<65>fico
(utilizando la caja de texto).
El <em/widget/ bot<6F>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<63>n de
matener pulsado uno de los botones puede resultar (es opcional) en una
aceleraci<EFBFBD>n del cambio en el valor de acuerdo con el tiempo que se
mantenga pulsado.
El bot<6F>n <em/spin/ utiliza un objeto <ref id="sec_Adjustment"
name="Ajuste"> para conservar la informaci<63>n referente al rango de
valores que puede tomar el bot<6F>n <em/spin/. Esto hace que el
<em/widget/ bot<6F>n <em/spin/ sea muy poderoso.
Recuerde que un <em/widget/ ajuste puede crearse con la siguiente
funci<EFBFBD>n, que ilustra la informaci<63>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<6F>n <em/spin/ de la
forma siguiente:
<itemize>
<item> <tt/valor/: valor inicial del bot<6F>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<EFBFBD>n 1 en una flecha
<item> <tt/incremento_pagina/: valor a incrementar/decrementar cuando
pulsemos el bot<6F>n 2 en una flecha
<item> <tt/tamano_pagina/: no se utiliza
</itemize>
Adem<EFBFBD>s, se puede utilizar el bot<6F>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<6F>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<63>n que tendr<64> el bot<6F>n <em/spin/. El argumento
<tt/digitos/ especifica el n<>mero de cifras decimales con que se
mostrar<EFBFBD> el valor.
Se puede reconfigurar un bot<6F>n <em/spin/ despu<70>s de su creaci<63>n
utilizando la funci<63>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<6F>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<62>n puede alterarse utilizando:
<tscreen><verb>
void gtk_spin_button_set_digits( GtkSpinButton *boton_spin,
guint digitos) ;
</verb></tscreen>
El valor que un bot<6F>n <em/spin/ est<73> 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<6F>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<63>n:
<tscreen><verb>
void gtk_spin_button_spin( GtkSpinButton *boton_spin,
GtkSpinType direccion,
gfloat incremento );
</verb></tscreen>
El par<61>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<EFBFBD> de explicar todas las posibilidades que ofrece esta
funci<EFBFBD>n. Algunos de los valores que puede utilizar <tt/direccion/
hacen que se utilicen valores que est<73>n almacenados en el objeto
Ajuste que est<73> asociado con el bot<6F>n <em/spin/.
GTK_SPIN_STEP_FORWARD y GTK_SPIN_STEP_BACKWARD aumentan o disminuyen
(respectivamente) el valor del bot<6F>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<61> o disminuir<69> 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<6F>n <em/spin/ por la cantidad <tt/incremento/.
GTK_SPIN_HOME hace que el bot<6F>n <em/spin/ tenga el mismo valor que el
valor inferior del rango Ajuste.
GTK_SPIN_END hace que el bot<6F>n <em/spin/ tenga el mismo valor que el
valor superior del rango Ajuste.
GTK_SPIN_USER_DEFINED cambia el valor del bot<6F>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<6F>n <em/spin/, y vamos a pasar a las
funciones que afectan a la apariencia y al comportamiento del
<em/widget/ bot<6F>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<6F>n <em/spin/ a un valor num<75>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<6F>n <em/spin/ pasar<61> del l<>mite superior al
inferior utilizando la siguiente funci<63>n:
<tscreen><verb>
void gtk_spin_button_set_wrap( GtkSpinButton *boton_spin,
gboolean wrap );
</verb></tscreen>
Puede hacer que un bot<6F>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<EFBFBD>n <em/spin/. Para hacer que redondee tenemos que utilizar la
funci<EFBFBD>n siguiente:
<tscreen><verb>
void gtk_spin_button_set_snap_to_ticks( GtkSpinButton *boton_spin,
gboolean redondear );
</verb></tscreen>
Para pol<6F>tica de actualizaci<63>n de un bot<6F>n <em/spin/ puede cambiarse
con la siguiente funci<63>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<6F>ticas afectan al comportamiento de un bot<6F>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<6F>n <em/spin/
cambiar<EFBFBD> si el texto introducido es un valor num<75>rico contenido dentro
del rango especificado por el Ajuste. En caso contrario el texto
introducido se convierte al valor del bot<6F>n <em/spin/.
En caso de utilizar GTK_UPDATE_ALWAYS se ignorar<61>n los errores que
puedan ocurrir en la conversi<73>n del texto en un valor num<75>rico.
El aspecto de los botones utilizados en un bot<6F>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<70>cita que un bot<6F>n <em/spin/ se
actualice a s<> mismo:
<tscreen><verb>
void gtk_spin_button_update( GtkSpinButton *boton_spin );
</verb></tscreen>
Es hora de un nuevo ejemplo.
<tscreen><verb>
/* principio del ejemplo spinbutton spinbutton.c */
#include <gtk/gtk.h>
static GtkWidget *spinner1;
void toggle_snap( GtkWidget *widget,
GtkSpinButton *spin )
{
gtk_spin_button_set_snap_to_ticks (spin, GTK_TOGGLE_BUTTON (widget)->active);
}
void toggle_numeric( GtkWidget *widget,
GtkSpinButton *spin )
{
gtk_spin_button_set_numeric (spin, GTK_TOGGLE_BUTTON (widget)->active);
}
void change_digits( GtkWidget *widget,
GtkSpinButton *spin )
{
gtk_spin_button_set_digits (GTK_SPIN_BUTTON (spinner1),
gtk_spin_button_get_value_as_int (spin));
}
void get_value( GtkWidget *widget,
gpointer data )
{
gchar buf[32];
GtkLabel *etiqueta;
GtkSpinButton *spin;
spin = GTK_SPIN_BUTTON (spinner1);
etiqueta = GTK_LABEL (gtk_object_get_user_data (GTK_OBJECT (widget)));
if (GPOINTER_TO_INT (data) == 1)
sprintf (buf, "%d", gtk_spin_button_get_value_as_int (spin));
else
sprintf (buf, "%0.*f", spin->digits,
gtk_spin_button_get_value_as_float (spin));
gtk_label_set_text (etiqueta, buf);
}
int main( int argc,
char *argv[] )
{
GtkWidget *ventana;
GtkWidget *frame;
GtkWidget *hbox;
GtkWidget *main_vbox;
GtkWidget *vbox;
GtkWidget *vbox2;
GtkWidget *spinner2;
GtkWidget *spinner;
GtkWidget *boton;
GtkWidget *etiqueta;
GtkWidget *val_label;
GtkAdjustment *adj;
/* Inicializar GTK */
gtk_init(&amp;argc, &amp;argv);
ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
GTK_SIGNAL_FUNC (gtk_main_quit),
NULL);
gtk_window_set_title (GTK_WINDOW (ventana), "Spin Button");
main_vbox = gtk_vbox_new (FALSE, 5);
gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 10);
gtk_container_add (GTK_CONTAINER (ventana), main_vbox);
frame = gtk_frame_new ("Not accelerated");
gtk_box_pack_start (GTK_BOX (main_vbox), frame, TRUE, TRUE, 0);
vbox = gtk_vbox_new (FALSE, 0);
gtk_container_set_border_width (GTK_CONTAINER (vbox), 5);
gtk_container_add (GTK_CONTAINER (frame), vbox);
/* spin del d<>a, mes y a<>o */
hbox = gtk_hbox_new (FALSE, 0);
gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 5);
vbox2 = gtk_vbox_new (FALSE, 0);
gtk_box_pack_start (GTK_BOX (hbox), vbox2, TRUE, TRUE, 5);
etiqueta = gtk_label_new ("Day :");
gtk_misc_set_alignment (GTK_MISC (etiqueta), 0, 0.5);
gtk_box_pack_start (GTK_BOX (vbox2), etiqueta, FALSE, TRUE, 0);
adj = (GtkAdjustment *) gtk_adjustment_new (1.0, 1.0, 31.0, 1.0,
5.0, 0.0);
spinner = gtk_spin_button_new (adj, 0, 0);
gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (spinner), TRUE);
gtk_spin_button_set_shadow_type (GTK_SPIN_BUTTON (spinner),
GTK_SHADOW_OUT);
gtk_box_pack_start (GTK_BOX (vbox2), spinner, FALSE, TRUE, 0);
vbox2 = gtk_vbox_new (FALSE, 0);
gtk_box_pack_start (GTK_BOX (hbox), vbox2, TRUE, TRUE, 5);
etiqueta = gtk_label_new ("Month :");
gtk_misc_set_alignment (GTK_MISC (etiqueta), 0, 0.5);
gtk_box_pack_start (GTK_BOX (vbox2), etiqueta, FALSE, TRUE, 0);
adj = (GtkAdjustment *) gtk_adjustment_new (1.0, 1.0, 12.0, 1.0,
5.0, 0.0);
spinner = gtk_spin_button_new (adj, 0, 0);
gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (spinner), TRUE);
gtk_spin_button_set_shadow_type (GTK_SPIN_BUTTON (spinner),
GTK_SHADOW_ETCHED_IN);
gtk_box_pack_start (GTK_BOX (vbox2), spinner, FALSE, TRUE, 0);
vbox2 = gtk_vbox_new (FALSE, 0);
gtk_box_pack_start (GTK_BOX (hbox), vbox2, TRUE, TRUE, 5);
etiqueta = gtk_label_new ("Year :");
gtk_misc_set_alignment (GTK_MISC (etiqueta), 0, 0.5);
gtk_box_pack_start (GTK_BOX (vbox2), etiqueta, FALSE, TRUE, 0);
adj = (GtkAdjustment *) gtk_adjustment_new (1998.0, 0.0, 2100.0,
1.0, 100.0, 0.0);
spinner = gtk_spin_button_new (adj, 0, 0);
gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (spinner), FALSE);
gtk_spin_button_set_shadow_type (GTK_SPIN_BUTTON (spinner),
GTK_SHADOW_IN);
gtk_widget_set_usize (spinner, 55, 0);
gtk_box_pack_start (GTK_BOX (vbox2), spinner, FALSE, TRUE, 0);
frame = gtk_frame_new ("Accelerated");
gtk_box_pack_start (GTK_BOX (main_vbox), frame, TRUE, TRUE, 0);
vbox = gtk_vbox_new (FALSE, 0);
gtk_container_set_border_width (GTK_CONTAINER (vbox), 5);
gtk_container_add (GTK_CONTAINER (frame), vbox);
hbox = gtk_hbox_new (FALSE, 0);
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 5);
vbox2 = gtk_vbox_new (FALSE, 0);
gtk_box_pack_start (GTK_BOX (hbox), vbox2, TRUE, TRUE, 5);
etiqueta = gtk_label_new ("Value :");
gtk_misc_set_alignment (GTK_MISC (etiqueta), 0, 0.5);
gtk_box_pack_start (GTK_BOX (vbox2), etiqueta, FALSE, TRUE, 0);
adj = (GtkAdjustment *) gtk_adjustment_new (0.0, -10000.0, 10000.0,
0.5, 100.0, 0.0);
spinner1 = gtk_spin_button_new (adj, 1.0, 2);
gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (spinner1), TRUE);
gtk_widget_set_usize (spinner1, 100, 0);
gtk_box_pack_start (GTK_BOX (vbox2), spinner1, FALSE, TRUE, 0);
vbox2 = gtk_vbox_new (FALSE, 0);
gtk_box_pack_start (GTK_BOX (hbox), vbox2, TRUE, TRUE, 5);
etiqueta = gtk_label_new ("Digits :");
gtk_misc_set_alignment (GTK_MISC (etiqueta), 0, 0.5);
gtk_box_pack_start (GTK_BOX (vbox2), etiqueta, FALSE, TRUE, 0);
adj = (GtkAdjustment *) gtk_adjustment_new (2, 1, 5, 1, 1, 0);
spinner2 = gtk_spin_button_new (adj, 0.0, 0);
gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (spinner2), TRUE);
gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
GTK_SIGNAL_FUNC (change_digits),
(gpointer) spinner2);
gtk_box_pack_start (GTK_BOX (vbox2), spinner2, FALSE, TRUE, 0);
hbox = gtk_hbox_new (FALSE, 0);
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 5);
boton = gtk_check_button_new_with_label ("Snap to 0.5-ticks");
gtk_signal_connect (GTK_OBJECT (boton), "clicked",
GTK_SIGNAL_FUNC (toggle_snap),
spinner1);
gtk_box_pack_start (GTK_BOX (vbox), boton, TRUE, TRUE, 0);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (boton), TRUE);
boton = gtk_check_button_new_with_label ("Numeric only input mode");
gtk_signal_connect (GTK_OBJECT (boton), "clicked",
GTK_SIGNAL_FUNC (toggle_numeric),
spinner1);
gtk_box_pack_start (GTK_BOX (vbox), boton, TRUE, TRUE, 0);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (boton), TRUE);
val_label = gtk_label_new ("");
hbox = gtk_hbox_new (FALSE, 0);
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 5);
boton = gtk_button_new_with_label ("Value as Int");
gtk_object_set_user_data (GTK_OBJECT (boton), val_label);
gtk_signal_connect (GTK_OBJECT (boton), "clicked",
GTK_SIGNAL_FUNC (get_value),
GINT_TO_POINTER (1));
gtk_box_pack_start (GTK_BOX (hbox), boton, TRUE, TRUE, 5);
boton = gtk_button_new_with_label ("Value as Float");
gtk_object_set_user_data (GTK_OBJECT (boton), val_label);
gtk_signal_connect (GTK_OBJECT (boton), "clicked",
GTK_SIGNAL_FUNC (get_value),
GINT_TO_POINTER (2));
gtk_box_pack_start (GTK_BOX (hbox), boton, TRUE, TRUE, 5);
gtk_box_pack_start (GTK_BOX (vbox), val_label, TRUE, TRUE, 0);
gtk_label_set_text (GTK_LABEL (val_label), "0");
hbox = gtk_hbox_new (FALSE, 0);
gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, TRUE, 0);
boton = gtk_button_new_with_label ("Close");
gtk_signal_connect_object (GTK_OBJECT (boton), "clicked",
GTK_SIGNAL_FUNC (gtk_widget_destroy),
GTK_OBJECT (ventana));
gtk_box_pack_start (GTK_BOX (hbox), boton, TRUE, TRUE, 5);
gtk_widget_show_all (ventana);
/* Entramos dentro del bucle de eventos */
gtk_main ();
return(0);
}
/* fin del ejemplo */
</verb></tscreen>
<!-- ----------------------------------------------------------------- -->
<sect1>Caja combinada (<em/Combo Box/)
<p>
La caja combinada o <em/combo box/ es otro sencillo <em/widget/
exclusivamente compuesto por otros <em/widgets/. Desde el punto de
vista del usuario, el <em/widget/ consiste en una caja para la
introducci<EFBFBD>n de texto y un men<65> desplegable desde el que el usuario
puede seleccionar una de un conjunto predefinido de entradas. De forma
alternativa, el usuario puede introducir una opci<63>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<63>n
entry del combo box, podr<64> 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<EFBFBD>n:
<tscreen><verb>
void gtk_combo_set_popdown_strings( GtkCombo *combo,
GList *cadenas );
</verb></tscreen>
Antes de llamar a esta funci<63>n, tiene que ensamblar una GList con las
cadenas que quiere. GList es una implementaci<63>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<63>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<63>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<63>n g_list_append debe
utilizarse como el nuevo puntero a la GList.
Aqu<EFBFBD> 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<64> 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<61> el texto actual
del combo box con el siguiente elemento de la lista (superior o
inferior, seg<65>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<65>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<65> el TAB quien se ocupe. Cuando el
elemento actual sea el <20>ltimo de la lista y presione la flecha abajo
se cambiar<61> 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<73> en la lista,
entonces se desactiva la funci<63>n de <tt/gtk_combo_set_use_arrows()/.
<tt/gtk_combo_set_use_arrows_always()/ igualmente permite la
utilizaci<EFBFBD>n de las flechas arriba/abajo para cambiar el elemento
seleccionado por el siguiente/anterior de la lista, pero adem<65>s trata
la lista como si fuese circular (ie. pasa del <20>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<61>sculas y min<69>sculas y una b<>squeda que
no discrimine. Se utiliza cuando se quiere que el <em/widget/ combo
complete el valor que se est<73> introduciendo con un valor de la
lista. Dependiendo de esta funci<63>n, se completar<61> distinguiendo entre
may<EFBFBD>sculas y min<69>sculas o no. El <em/widget/ combo completar<61> la
entrada actual si el usuario presiona la combinaci<63>n de teclas MOD-1 y
`Tab'. MOD-1 normalmente es la tecla `Alt'. Hay algunos
administradores de ventanas que tambi<62>n utilizan esta combinaci<63>n de
teclas, con lo que perderemos su posible utilizaci<63>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<6F>a
del tiempo, de lo <20>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<73>al <tt/activate/, que
indica que el usuario ha presionado la tecla <20>Intro<72>, y leer el
texto. Lo primero podemos hacerlo utilizando algo as<61>:
<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<63>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<63>n,
<tscreen><verb>
void gtk_combo_disable_activate(GtkCombo *combo);
</verb></tscreen>
que permite desactivar la se<73>al <tt/activate/ en el <em/widget/ entry
dentro del combo box. Personalmente no se me ocurre ning<6E>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<63>n de Color
<p>
El <em/widget/ selecci<63>n de color, nos permite (<28>sorpresa!) la selecci<63>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<63>n, valor). Para conseguirlo puede ajustar cada variable
mediante las regletas o introduciendo directamente el valor deseado.
Tambi<EFBFBD>n puede pinchar en la rueda de colores y seleccionar as<61> el color
deseado. Tambi<62>n se puede establecer, opcionalmente, la transparencia
del color.
El <em/widget/ de selecci<63>n de color emite (por ahora) s<>lo una se<73>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<63>n <tt/gtk_color_selection_set_color()/.
Ech<EFBFBD>mosle un vistazo a lo que nos ofrece el <em/widget/ de selecci<63>n de color.
El <em/widget/ tiene dos <20>sabores<65> diferentes; <tt/gtk_color_selection/
y <tt/gtk_color_selection_dialog/:
<tscreen><verb>
GtkWidget *gtk_color_selection_new( void );
</verb></tscreen>
Probablemente no utilizar<61> este constructor directamente. Crea un <em/widget/
GtkColorSelection hu<68>rfano al que le tendr<64> que asignarle un padre. El
<em/widget/ GtkColorSelection est<73> heredado del <em/widget/ GtkVBox.
<tscreen><verb>
GtkWidget *gtk_color_selection_dialog_new( const gchar *title );
</verb></tscreen>
<EFBFBD>ste es el constructor del cuadro de selecci<63>n de color m<>s com<6F>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, <20>Aceptar<61>, <20>Cancelar<61> y <20>Ayuda<64>.
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<63>n se utiliza para indicar la pol<6F>tica de actuaci<63>n. La pol<6F>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<61>n un color de la rueda de colores. Si tiene problemas
de rendimiento, puede poner la pol<6F>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<63>n de color admite el ajuste de la transparencia
de un color (tambi<62>n conocido como el canal alfa). Esta opci<63>n est<73>
desactivada por defecto. Si se llama a esta funci<63>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<63>n con
un puntero a un vector de colores (de tipo <tt/gdouble/). La longitud del
vector depende de si est<73> activada la transparencia. La posici<63>n 0 contiene
la componente roja del color, la 1 contiene la verde, la 2 la azul y la
transparencia est<73> en la posici<63>n 3 (solamente si est<73> 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<73>al <tt/color_changed/, utilice esta funci<63>n. <tt/color/
es un puntero al vector de colores que se rellenar<61>. Ver la descripci<63>n
de la funci<63>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<EFBFBD> tenemos un peque<75>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<64>logo de
selecci<EFBFBD>n del color, y cambiando el color en el cuadro de di<64>logo se
cambia el color de fondo de la zona de dibujo.
<tscreen><verb>
/* principio del ejemplo colorsel colorsel.c */
#include <glib.h>
#include <gdk/gdk.h>
#include <gtk/gtk.h>
GtkWidget *colorseldlg = NULL;
GtkWidget *drawingarea = NULL;
/* Manejador del cambio de color */
void color_changed_cb (GtkWidget *widget, GtkColorSelection *colorsel)
{
gdouble color[3];
GdkColor gdk_color;
GdkColormap *colormap;
/* Obtener el mapa de colores de la zona de dibujo */
colormap = gdk_window_get_colormap (drawingarea->window);
/* Obtener el color actual */
gtk_color_selection_get_color (colorsel,color);
/* Meterlo en un entero sin signo de 16 bits (0..65535) e insertarlo
en la estructura GdkColor */
gdk_color.red = (guint16)(color[0]*65535.0);
gdk_color.green = (guint16)(color[1]*65535.0);
gdk_color.blue = (guint16)(color[2]*65535.0);
/* Pedir memoria para el color */
gdk_color_alloc (colormap, &amp;gdk_color);
/* Poner el color de fondo de la ventana */
gdk_window_set_background (drawingarea->window, &amp;gdk_color);
/* Limpiar la ventana */
gdk_window_clear (drawingarea->window);
}
/* Manejador del evento Drawingarea */
gint area_event (GtkWidget *widget, GdkEvent *event, gpointer client_data)
{
gint handled = FALSE;
GtkWidget *colorsel;
/* Comprobar si hemos recibido un evento de pulsaci<63>n de bot<6F>n */
if (event->type == GDK_BUTTON_PRESS &amp;&amp; colorseldlg == NULL)
{
/* S<>, <20>tenemos un evento y todav<61>a no est<73> el colorseldlg! */
handled = TRUE;
/* Crear el cuadro de di<64>logo de selecci<63>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<73>al <20>color_changed<65>, 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<64>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 <20>rdenes */
gtk_init (&amp;argc,&amp;argv);
/* Crea la ventana de m<>s alto nivel, le da t<>tulo y la pol<6F>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 <20>delete<74> y <20>destroy<6F>, 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<6D>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<71>) */
gtk_main ();
/* Para satisfacer a los compiladores pijos */
return 0;
}
/* final del ejemplo */
</verb></tscreen>
<!-- ----------------------------------------------------------------- -->
<sect1> Selecci<63>n de ficheros
<p>
El <em/widget/ de selecci<63>n de ficheros nos proporciona una forma
r<EFBFBD>pida y sencilla de mostrar un cuadro de di<64>logo para la selecci<63>n de
un fichero. Ya viene con los botones Aceptar, Cancelar y Ayuda. Una
magnifica ayuda para acortar el tiempo de programaci<63>n.
Para crear un nuevo cuadro de di<64>logo de selecci<63>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<64>logo, por
ejemplo para poder utilizar un directorio o un fichero por defecto,
utilice la funci<63>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<63>n:
<tscreen><verb>
gchar *gtk_file_selection_get_filename( GtkFileSelection *filesel );
</verb></tscreen>
Tambi<EFBFBD>n hay punteros a los <em/widgets/ que contiene el cuadro de
di<EFBFBD>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<EFBFBD> inclu<6C>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<63>n de
ficheros. Aunque aparezca el bot<6F>n de ayuda en la pantalla, no hace
nada y no tiene ninguna se<73>al conectada.
<tscreen><verb>
/* principio del ejemplo filesel filesel.c */
#include <gtk/gtk.h>
/* Obtener el nombre del fichero e imprimirlo en la consola */
void file_ok_sel (GtkWidget *w, GtkFileSelection *fs)
{
g_print ("%s\n", gtk_file_selection_get_filename (GTK_FILE_SELECTION (fs)));
}
void destroy (GtkWidget *widget, gpointer data)
{
gtk_main_quit ();
}
int main (int argc, char *argv[])
{
GtkWidget *filew;
gtk_init (&amp;argc, &amp;argv);
/* Crear un nuevo widget de selecci<63>n de ficheros */
filew = gtk_file_selection_new ("File selection");
gtk_signal_connect (GTK_OBJECT (filew), "destroy",
(GtkSignalFunc) destroy, &amp;filew);
/* Conectar el ok_button con la funci<63>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<63>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<64>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<EFBFBD>lo pueden dibujar en la de su padre. Debido a esto, no pueden
recibir ning<6E>n evento y si tienen un tama<6D>o incorrecto, no se
recortar<EFBFBD>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<69>til. No dibuja nada en la pantalla y no responde
a ning<6E>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<61> ning<6E>n recorte en sus contenidos. Aunque el
nombre <em/EventBox/ enfatiza su funci<63>n de manejador de eventos, el
<em/widget/ tambi<62>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<61>:
<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<75>a caja, y hace
que una pulsaci<63>n del rat<61>n en la misma finalice el programa.
<tscreen><verb>
/* principio del ejemplo eventbox eventbox.c */
#include <gtk/gtk.h>
int
main (int argc, char *argv[])
{
GtkWidget *ventana;
GtkWidget *event_box;
GtkWidget *etiqueta;
gtk_init (&amp;argc, &amp;argv);
ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (ventana), "Event Box");
gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
GTK_SIGNAL_FUNC (gtk_exit), NULL);
gtk_container_border_width (GTK_CONTAINER (ventana), 10);
/* Crea un EventBox y lo a<>ade a nuestra ventana superior */
event_box = gtk_event_box_new ();
gtk_container_add (GTK_CONTAINER(ventana), event_box);
gtk_widget_show (event_box);
/* Crea una larga etiqueta */
etiqueta = gtk_label_new ("Click here to quit, quit, quit, quit, quit");
gtk_container_add (GTK_CONTAINER (event_box), etiqueta);
gtk_widget_show (etiqueta);
/* La recortamos. */
gtk_widget_set_usize (etiqueta, 110, 20);
/* Y enlazamos una acci<63>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<69> colocar un
<em/widget/ dentro de su ventana utilizando una posici<63>n y un tama<6D>o
relativos al mismo <em/widget/ de alineamiento. Por ejemplo, puede ser
muy <20>til para centrar un <em/widget/ en la ventana.
S<EFBFBD>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<63>n crea un nuevo <em/widget/ alineamiento con los
par<EFBFBD>metros especificados. La segunda funci<63>n permite alterar los
par<EFBFBD>metros de un <em/widget/ alineamiento ya existente.
Los cuatro par<61>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<63>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<63>n del <em/widget/ alineamiento,
dir<EFBFBD>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<63>n fija
dentro de su ventana, relativa a la esquina superior izquierda. La
posici<EFBFBD>n de los <em/widgets/ puede cambiarse din<69>micamente.
S<EFBFBD>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<63>n <tt/gtk_fixed_new/ permite la creaci<63>n de un nuevo
contenedor fijo.
<tt/gtk_fixed_put/ situa <tt/widget/ dentro del contenedor <tt/fixed/
en la posici<63>n especificada por <tt/x/ e <tt/y/.
<tt/gtk_fixed_move/ permite que mover hacia una nuevo posici<63>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<63>n del widget que
* hay dentro del contenedor */
gint x=50;
gint y=50;
/* Esta funci<63>n de llamada mueve el bot<6F>n a una nueva
* posici<63>n dentro del contenedor fijo. */
void move_button( GtkWidget *widget,
GtkWidget *fixed )
{
x = (x+30)%300;
y = (y+50)%300;
gtk_fixed_move( GTK_FIXED(fixed), widget, x, y);
}
int main( int argc,
char *argv[] )
{
/* GtkWidget es el tipo de almacenamiento para los widgets */
GtkWidget *ventana;
GtkWidget *fixed;
GtkWidget *boton;
gint i;
/* Inicializa GTK */
gtk_init(&amp;argc, &amp;argv);
/* Crear una nueva ventana */
ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(ventana), "Fixed Container");
/* Aqu<71> conectamos el evento "destroy" al manejador de la se<73>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<6F>n con la etiqueta "Press me" */
boton = gtk_button_new_with_label ("Press me");
/* Cuando el bot<6F>n reciba la se<73>al "pulsado", llamar<61> a la funci<63>n
* move_button() pas<61>ndole el contenedor fijo como argumento. */
gtk_signal_connect (GTK_OBJECT (boton), "clicked",
GTK_SIGNAL_FUNC (move_button), fixed);
/* Esto mete el bot<6F>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<63>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<63>n con una ex<65>tica combinaci<63>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<EFBFBD> crear un contenedor capa utilizando:
<tscreen><verb>
GtkWidget *gtk_layout_new( GtkAdjustment *hadjustment,
GtkAdjustment *vadjustment );
</verb></tscreen>
Como puede observar, podr<64> especificar (de forma opcional) los objetos
de ajuste que utilizar<61> 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<6D>o del contenedor capa se puede establecer utilizando la
siguiente funci<63>n:
<tscreen><verb>
void gtk_layout_set_size( GtkLayout *layout,
guint width,
guint height );
</verb></tscreen>
Los contenedores capa son uno de los poqu<71>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<63>n a la cola que ser<65> procesada cuando
se devuelva el control a la funci<63>n <tt/gtk_main()/).
Cuando quiera hacer una gran cantidad de cambios dentro del contenedor
capa, podr<64> utilizar las dos funciones siguientes para desactivar y
reactivar la caracter<65>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<63>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<63>n de la etiqueta y el estilo de la caja pueden
modificarse.
Se puede crear un marco con la siguiente funci<63>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<61> ninguna
etiqueta. Puede cambiarse el texto de la etiqueta utilizando la
funci<EFBFBD>n siguiente.
<tscreen><verb>
void gtk_frame_set_label( GtkFrame *frame,
const gchar *etiqueta );
</verb></tscreen>
La posici<63>n de la etiqueta se puede cambiar utilizado la funci<63>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<63>n altera el estilo de la caja que se utiliza para
se<EFBFBD>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<63>n del <em/widget/ marco.
<tscreen><verb>
/* principio del ejemplo frame frame.c */
#include <gtk/gtk.h>
int main( int argc,
char *argv[] )
{
/* GtkWidget es el tipo de almacenamiento para los widgets */
GtkWidget *ventana;
GtkWidget *frame;
GtkWidget *boton;
gint i;
/* Inicializa GTK */
gtk_init(&amp;argc, &amp;argv);
/* Crea una nueva ventana */
ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(ventana), "Frame Example");
/* Aqu<71> conectamos el evento "destroy"al manejador de se<73>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<EFBFBD>n entre el ancho y el alto) del <em/widget/ hijo, a<>adiendo
espacio extra en caso de ser necesario. Esto es <20>til, por ejemplo, si
quiere hacer una vista previa de una gran imagen. El tama<6D>o de la
vista previa deber<65>a variar cuando el usuario redimensione la ventana,
pero la proporci<63>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<63>n exactamente igual que
con los <em/widgets Alignment/. Si <tt/obey_child/ es TRUE, la
proporci<EFBFBD>n de un <em/widget/ hijo ser<65> la misma que la proporci<63>n del
tama<EFBFBD>o ideal que <20>ste pida. En caso contrario, vendr<64> 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<65> de
2:1, no importa como el usuario redimensione la ventana.
<tscreen><verb>
/* principio del ejemplo aspectframe aspectframe.c */
#include <gtk/gtk.h>
int
main (int argc, char *argv[])
{
GtkWidget *ventana;
GtkWidget *aspect_frame;
GtkWidget *drawing_area;
gtk_init (&amp;argc, &amp;argv);
ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (ventana), "Aspect Frame");
gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
gtk_container_border_width (GTK_CONTAINER (ventana), 10);
/* Crear un aspect_frame y a<>adirlo a nuestra ventana superior */
aspect_frame = gtk_aspect_frame_new ("2x1", /* etiqueta */
0.5, /* centro x */
0.5, /* centro y */
2, /* tama<6D>ox/tama<6D>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<61> una ventana de 200x100, ya que tenemos una
* relaci<63>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 <20>til para cuando se quiere dividir
una zona en dos partes, con un tama<6D>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<6D>o
de cada zona. La divisi<73>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<EFBFBD>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<EFBFBD>n de una t<>cnica alternativa, para a<>adir el texto
conectaremos un manipulador a la se<73>al <20>realize<7A>. Y tenemos que
a<EFBFBD>adir la opci<63>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<61>
cuando la porci<63>n de abajo se haga m<>s peque<75>a, se encoger<65>
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<6C>n texto a nuestro widget de texto - esta funci<63>n se
invoca cada vez que se produce una se<73>al realize en la
ventana. Podemos forzar esta se<73>al mediante gtk_widget_realize, pero
primero tiene que formar parte de una jerarqu<71>a */
void
realize_text (GtkWidget *text, gpointer data)
{
gtk_text_freeze (GTK_TEXT (text));
gtk_text_insert (GTK_TEXT (text), NULL, &amp;text->style->black, NULL,
"From: pathfinder@nasa.gov\n"
"To: mom@nasa.gov\n"
"Subject: Made it!\n"
"\n"
"We just got in this morning. The weather has been\n"
"great - clear but cold, and there are lots of fun sights.\n"
"Sojourner says hi. See you soon.\n"
" -Path\n", -1);
gtk_text_thaw (GTK_TEXT (text));
}
/* Creamos una zona con texto que muestra un "message" */
GtkWidget *
create_text (void)
{
GtkWidget *table;
GtkWidget *text;
GtkWidget *hscrollbar;
GtkWidget *vscrollbar;
/* Crea una tabla para contener el widget de texto y las barras de
desplazamiento */
table = gtk_table_new (2, 2, FALSE);
/* Pone un widget de texto en la esquina superior izquierda.
Observe la utilizaci<63>n de GTK_SHRINK en la direcci<63>n y */
text = gtk_text_new (NULL, NULL);
gtk_table_attach (GTK_TABLE (table), text, 0, 1, 0, 1,
GTK_FILL | GTK_EXPAND,
GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 0);
gtk_widget_show (text);
/* Pone una HScrollbar en la esquina inferior izquierda */
hscrollbar = gtk_hscrollbar_new (GTK_TEXT (text)->hadj);
gtk_table_attach (GTK_TABLE (table), hscrollbar, 0, 1, 1, 2,
GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
gtk_widget_show (hscrollbar);
/* Y una VScrollbar en la esquina superior derecha */
vscrollbar = gtk_vscrollbar_new (GTK_TEXT (text)->vadj);
gtk_table_attach (GTK_TABLE (table), vscrollbar, 1, 2, 0, 1,
GTK_FILL, GTK_EXPAND | GTK_FILL | GTK_SHRINK, 0, 0);
gtk_widget_show (vscrollbar);
/* Y un manejador para poner un mensaje en el widget de texto
cuando reciba realize */
gtk_signal_connect (GTK_OBJECT (text), "realize",
GTK_SIGNAL_FUNC (realize_text), NULL);
return table;
}
int
main (int argc, char *argv[])
{
GtkWidget *ventana;
GtkWidget *vpaned;
GtkWidget *list;
GtkWidget *text;
gtk_init (&amp;argc, &amp;argv);
ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (ventana), "Paned Windows");
gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
gtk_container_border_width (GTK_CONTAINER (ventana), 10);
/* crea un widget vpaned y lo a<>ade a nuestra ventana superior */
vpaned = gtk_vpaned_new ();
gtk_container_add (GTK_CONTAINER(ventana), vpaned);
gtk_widget_show (vpaned);
/* Ahora crea los contenidos de las dos mitades de la ventana */
list = create_list ();
gtk_paned_add1 (GTK_PANED(vpaned), list);
gtk_widget_show (list);
text = create_text ();
gtk_paned_add2 (GTK_PANED(vpaned), text);
gtk_widget_show (text);
gtk_widget_show (ventana);
gtk_main ();
return 0;
}
/* fin del ejemplo */
</verb></tscreen>
<!-- XXX -->
<!-- ----------------------------------------------------------------- -->
<sect1> <em/Viewports/ <label id="sec_Viewports">
<p>
Probablemente nunca le llegue a hacer falta utilizar el <em/widget/
Viewport directamente. Ser<65> 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<65> una parte del mismo. Utiliza
<ref id="sec_Adjustment" name="ajustes"> para definir la zona que se
est<EFBFBD> viendo actualmente.
Para crear un <em/viewport/ hay que utilizar la funci<63>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<EFBFBD>n. El <em/widget/ crear<61> sus propios ajustes en caso de que
reciba como argumento un valor NULL.
Puede obtener y establecer los ajustes despu<70>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 <20>nica funci<63>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<64> utilizarlo sin importar su tama<6D>o gracias a
las barras de desplazamiento.
La funci<63>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<63>n horizontal, y
el segundo es el ajuste para la direcci<63>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<63>n establece la pol<6F>tica que se utilizar<61> 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<6F>tica para la barra de desplazamiento horizontal, y el tercero
la pol<6F>tica para la barra de desplazamiento vertical.
La pol<6F>tica puede ser GTK_POLICY_AUTOMATIC, o
GTK_POLICY_ALWAYS. GTK_POLICY_AUTOMATIC decidir<69> autom<6F>ticamente si
necesita barras de desplazamiento, mientras que GTK_POLICY_ALWAYS pondr<64>
siempre las barras de desplazamiento.
Aqu<EFBFBD> tenemos un ejemplo sencillo que empaqueta 100 botones de
selecci<EFBFBD>n en una ventana con barras de desplazamiento. Solamente he
comentado las partes que deber<65>a ser nuevas para usted.
<tscreen><verb>
/* principio del ejemplo scrolledwin scrolledwin.c */
#include <gtk/gtk.h>
void destroy(GtkWidget *widget, gpointer data)
{
gtk_main_quit();
}
int main (int argc, char *argv[])
{
static GtkWidget *ventana;
GtkWidget *scrolled_window;
GtkWidget *table;
GtkWidget *boton;
char buffer[32];
int i, j;
gtk_init (&amp;argc, &amp;argv);
/* Crea un nuevo cuadro de di<64>logo para que la ventana con barras de
* desplazamiento se meta dentro. Un cuadro de di<64>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<64>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<6F>tica es GTK_POLICY_AUTOMATIC, o GTK_POLICY_ALWAYS.
* GTK_POLICY_AUTOMATIC decidir<69> autom<6F>ticamente si necesita
* barras de desplazamiento, mientras que GTK_POLICY_ALWAYS pondr<64>
* 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<64>logo se crea con una vbox dentro de <20>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<63>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<6F>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<6F>n "close" en la parte de abajo del cuadro de
* di<64>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<6F>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<6F>n sea el elegido por defecto. Con pulsar la
* tecla "Enter" se activar<61> este bot<6F>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<62>n puede utilizar la funci<63>n
<tt/gtk_widget_set_usize()/ para poner el tama<6D>o por defecto de la
ventana o de cualquier otro <em/widget/.
<!-- XXX -->
<!-- ----------------------------------------------------------------- -->
<sect1>Cajas de botones
<p>
Las cajas de botones son <20>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 <20>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<63>n:
<tscreen><verb>
gtk_container_add( GTK_CONTAINER(button_box), child_widget );
</verb></tscreen>
Aqu<EFBFBD> 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<61>metros
* especificados */
GtkWidget *create_bbox (gint horizontal,
char* title,
gint spacing,
gint child_w,
gint child_h,
gint layout)
{
GtkWidget *frame;
GtkWidget *bbox;
GtkWidget *boton;
frame = gtk_frame_new (title);
if (horizontal)
bbox = gtk_hbutton_box_new ();
else
bbox = gtk_vbutton_box_new ();
gtk_container_set_border_width (GTK_CONTAINER (bbox), 5);
gtk_container_add (GTK_CONTAINER (frame), bbox);
/* Establece la apariencia de la Caja de Botones */
gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), layout);
gtk_button_box_set_spacing (GTK_BUTTON_BOX (bbox), spacing);
gtk_button_box_set_child_size (GTK_BUTTON_BOX (bbox), child_w, child_h);
boton = gtk_button_new_with_label ("OK");
gtk_container_add (GTK_CONTAINER (bbox), boton);
boton = gtk_button_new_with_label ("Cancel");
gtk_container_add (GTK_CONTAINER (bbox), boton);
boton = gtk_button_new_with_label ("Help");
gtk_container_add (GTK_CONTAINER (bbox), boton);
return(frame);
}
int main( int argc,
char *argv[] )
{
static GtkWidget* ventana = NULL;
GtkWidget *main_vbox;
GtkWidget *vbox;
GtkWidget *hbox;
GtkWidget *frame_horz;
GtkWidget *frame_vert;
/* Inicializa GTK */
gtk_init( &amp;argc, &amp;argv );
ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (ventana), "Button Boxes");
gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
GTK_SIGNAL_FUNC(gtk_main_quit),
NULL);
gtk_container_set_border_width (GTK_CONTAINER (ventana), 10);
main_vbox = gtk_vbox_new (FALSE, 0);
gtk_container_add (GTK_CONTAINER (ventana), main_vbox);
frame_horz = gtk_frame_new ("Horizontal Button Boxes");
gtk_box_pack_start (GTK_BOX (main_vbox), frame_horz, TRUE, TRUE, 10);
vbox = gtk_vbox_new (FALSE, 0);
gtk_container_set_border_width (GTK_CONTAINER (vbox), 10);
gtk_container_add (GTK_CONTAINER (frame_horz), vbox);
gtk_box_pack_start (GTK_BOX (vbox),
create_bbox (TRUE, "Spread (spacing 40)", 40, 85, 20, GTK_BUTTONBOX_SPREAD),
TRUE, TRUE, 0);
gtk_box_pack_start (GTK_BOX (vbox),
create_bbox (TRUE, "Edge (spacing 30)", 30, 85, 20, GTK_BUTTONBOX_EDGE),
TRUE, TRUE, 5);
gtk_box_pack_start (GTK_BOX (vbox),
create_bbox (TRUE, "Start (spacing 20)", 20, 85, 20, GTK_BUTTONBOX_START),
TRUE, TRUE, 5);
gtk_box_pack_start (GTK_BOX (vbox),
create_bbox (TRUE, "End (spacing 10)", 10, 85, 20, GTK_BUTTONBOX_END),
TRUE, TRUE, 5);
frame_vert = gtk_frame_new ("Vertical Button Boxes");
gtk_box_pack_start (GTK_BOX (main_vbox), frame_vert, TRUE, TRUE, 10);
hbox = gtk_hbox_new (FALSE, 0);
gtk_container_set_border_width (GTK_CONTAINER (hbox), 10);
gtk_container_add (GTK_CONTAINER (frame_vert), hbox);
gtk_box_pack_start (GTK_BOX (hbox),
create_bbox (FALSE, "Spread (spacing 5)", 5, 85, 20, GTK_BUTTONBOX_SPREAD),
TRUE, TRUE, 0);
gtk_box_pack_start (GTK_BOX (hbox),
create_bbox (FALSE, "Edge (spacing 30)", 30, 85, 20, GTK_BUTTONBOX_EDGE),
TRUE, TRUE, 5);
gtk_box_pack_start (GTK_BOX (hbox),
create_bbox (FALSE, "Start (spacing 20)", 20, 85, 20, GTK_BUTTONBOX_START),
TRUE, TRUE, 5);
gtk_box_pack_start (GTK_BOX (hbox),
create_bbox (FALSE, "End (spacing 20)", 20, 85, 20, GTK_BUTTONBOX_END),
TRUE, TRUE, 5);
gtk_widget_show_all (ventana);
/* Entra dentro del bucle de eventos */
gtk_main ();
return(0);
}
/* fin del ejemplo */
</verb></tscreen>
<!-- ----------------------------------------------------------------- -->
<sect1>Barras de herramientas
<p>
Las barras de herramientas acostumbran a agrupar un conjunto de
<em>widgets</em> para hacer m<>s sencilla la personalizaci<63>n
de su aspecto y composici<63>n. T<>picamente una barra de herramientas
consiste en botones con iconos, etiquetas y <em/tips/ para los iconos
(peque<75>o texto descriptivo que aparece cuando se mantiene el rat<61>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<63>n de una barra de herramientas se hace (como puede que ya
haya sospechado) mediante la funci<63>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<EFBFBD>s de crear una barra de herramientas, se pueden a<>adir,
prea<EFBFBD>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<EFBFBD>n y una funci<63>n de llamada para el mismo. Por ejemplo, para a<>adir
un elemento puede utilizar la siguiente funci<63>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 <20>nico par<61>metro
adicional que deber<65>a especificar es la posici<63>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<63>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<6D>o del espacio en blanco puede establecerse globalmente
para toda una barra de herramientas con la funci<63>n:
<tscreen><verb>
void gtk_toolbar_set_space_size( GtkToolbar *toolbar,
gint space_size) ;
</verb></tscreen>
Si tiene que establecer la orientaci<63>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<63>n adicional):
<tscreen><verb>
#include <gtk/gtk.h>
#include "gtk.xpm"
/* Esta funci<63>n est<73> conectada al bot<6F>n Close o a la acci<63>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<65>a de sonarle familiar, a no ser que <20>ste sea
su primer programa GTK. En nuestro programa no habr<62> ninguna novedad,
salvo un bonito dibujo XPM que utilizaremos como icono para todos los
botones.
<tscreen><verb>
GtkWidget* close_button; // este bot<6F>n emitir<69> la se<73>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<73> activo y hacer que el estilo
* de la barra de herramientas est<73> acorde con la elecci<63>n
* ATENCI<43>N: <20>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<61>a m<>s f<>cil, s<>lo hay que comprobar el bot<6F>n de selecci<63>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<61>n cuando
se presione uno de los botones de la barra de herramientas. Todo esto
ya deber<65>a resultarle familiar si ha utilizado alguna vez los botones
de selecci<63>n (o los botones circulares)
<tscreen><verb>
int main (int argc, char *argv[])
{
/* Aqu<71> est<73> nuestra ventana principal (un cuadro de di<64>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<6F>n) */
GtkWidget * toolbar;
GdkPixmap * icon;
GdkBitmap * mask;
GtkWidget * iconw;
/* a esta funci<63>n se le llama en todas las aplicaci<63>n GTK */
gtk_init (&amp;argc, &amp;argv);
/* crear una ventana nueva con un t<>tulo y el tama<6D>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<73>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<61> 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<65>a ser parecido en cualquier aplicaci<63>n GTK. S<>lo
est<EFBFBD> la inicializaci<63>n de GTK, la creaci<63>n de la ventana, etc...
Solamente hay una cosa que probablemente necesite una explicaci<63>n:
una barra de herramientas flotante. Una barra de herramientas flotante
s<EFBFBD>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<63>ngulo muy peque<75>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<65> horizontal, con iconos y texto, y
* con un espacio de 5pxl entre elementos y finalmente, la ponemos en
* nuestra caja flotante */
toolbar = gtk_toolbar_new ( GTK_ORIENTATION_HORIZONTAL,
GTK_TOOLBAR_BOTH );
gtk_container_border_width ( GTK_CONTAINER ( toolbar ) , 5 );
gtk_toolbar_set_space_size ( GTK_TOOLBAR ( toolbar ), 5 );
gtk_container_add ( GTK_CONTAINER ( handlebox ) , toolbar );
/* ahora creamos el icono con la m<>scara: utilizaremos el widget
* icon con todos los elementos de la barra de herramientas */
icon = gdk_pixmap_create_from_xpm_d ( dialog->window, &amp;mask,
&amp;dialog->style->white, gtk_xpm );
</verb></tscreen>
Bien, lo que acabamos de escribir es la inicializaci<63>n del
<em>widget</em> de la barra de herramientas y la creaci<63>n de un
<em>pixmap</em> GDK con su m<>scara. Si quiere saber algo m<>s sobre la
utilizaci<EFBFBD>n de <em>pixmaps</em>, vea la documentaci<63>n de GDK o la
secci<EFBFBD>n <ref id="sec_Pixmaps" name="Pixmaps"> en este tutorial.
<tscreen><verb>
/* nuestro primer elemento es el bot<6F>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<6F>n
"Closes this app", // tooltip para el bot<6F>n
"Private", // cadena privada del tooltip
iconw, // widget del icono
GTK_SIGNAL_FUNC (delete_event), // una se<73>al
NULL );
gtk_toolbar_append_space ( GTK_TOOLBAR ( toolbar ) ); // espacio despu<70>s del elemento
</verb></tscreen>
En el trozo de c<>digo de arriba puede ver como se hace la acci<63>n m<>s
simple: a<>adir un bot<6F>n a la barra de herramientas. Justo antes de
a<EFBFBD>adir un nuevo elemento, tenemos que construir un <em>widget
pixmap</em> para que sirva como icono para este elemento; este paso
tendr<EFBFBD> que repetirse para cada nuevo elemento. Despu<70>s del elemento
a<EFBFBD>adiremos un espacio en blanco en la barra de herramientas, para que
los elementos que a<>adamos a continuaci<63>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<6F>n recien creado, por
lo que podremos trabajar con <20>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<73>al
toolbar); // dato para la se<73>al
gtk_toolbar_append_space ( GTK_TOOLBAR ( toolbar ) );
</verb></tscreen>
Aqu<EFBFBD> empezamos creando un grupo de botones circulares. Para hacerlo
hemos utilizado <tt/gtk_toolbar_append_element/. De hecho, utilizando
esta funci<63>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<6F>n anterior del grupo, mediante el que
podremos construir f<>cilmente una lista de botones (ver la secci<63>n
<ref id="sec_Radio_Buttons" name="Botones circulares"> que se encuentra
m<EFBFBD>s adelante en este tutorial).
<tscreen><verb>
/* los botones circulares que vienen a continuaci<63>n est<73>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<65>an todos en estado activo,
impidi<EFBFBD>ndonos poder cambiar de uno a otro).
<tscreen><verb>
/* aqu<71> tenemos un sencillo bot<6F>n de selecci<63>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<6F>n de selecci<63>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<61> 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 <20>nico que debe recordar es que este
<em>widget</em> debe mostrarse manualmente (al contrario que los dem<65>s
elementos que se mostrar<61>n junto con la barra de herramientas).
<tscreen><verb>
/* <20> Eso es ! mostremos algo. */
gtk_widget_show ( toolbar );
gtk_widget_show (handlebox);
gtk_widget_show ( dialog );
/* qued<65>monos en gtk_main y <20>esperemos a que empiece la diversi<73>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<65>s
del c<>digo este precioso icono XPM que le mostramos a continuaci<63>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<63>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<6F>n <20>ltimamente en la
programaci<EFBFBD>n de interfaces gr<67>ficos de usuario (GUI en ingl<67>s), y es
una buena forma de mostrar bloques de informaci<63>n similar que
necesitan aparecer de forma separada.
La primera funci<63>n que necesita conocer, como probablemente ya habr<62>
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 <20>l. Ech<63>mosles un vistazo una a una.
La primera que estudiaremos ser<65> la que nos permita establecer la
posici<EFBFBD>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<EFBFBD> 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<72>ndolas desde
el fondo del libro (a<>adi<64>ndolas), o desde parte superior del libro
(prea<65>adi<64>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<63>n que queda que sirve para a<>adir una p<>gina contiene todas las
propiedades de las anteriores, pero adem<65>s permite especificar en que
posici<EFBFBD>n quiere que est<73> 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<61>metros son los mismos que hab<61>an en las funciones _append_ y
_prepend_ excepto que hay uno m<>s que antes, <tt/posicion/. Este
par<EFBFBD>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<63>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<EFBFBD>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<73> en la <20>ltima p<>gina y se llama a
<tt/gtk_notebook_next_page/, se pasar<61> a la primera p<>gina. Sin
embargo, si el libro de notas est<73> en la primera p<>gina, y se llama a
<tt/gtk_notebook_prev_page/, no se pasar<61> a la <20>ltima p<>gina.
<tscreen><verb>
void gtk_notebook_next_page( GtkNoteBook *notebook );
void gtk_notebook_prev_page( GtkNoteBook *notebook );
</verb></tscreen>
La siguiente funci<63>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<63>n. Si no utiliza esta funci<63>n el libro de notas empezar<61>
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<EFBFBD>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<63>mosle un vistaza a un ejemplo, sacado del c<>digo de
<tt/testgtk.c/ que viene con la distribuci<63>n de GTK, y que muestra
la utilizaci<63>n de las 13 funciones. Este peque<75>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<65>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<EFBFBD>s, y salir del programa.
<tscreen><verb>
/* principio del ejemplo notebook notebook.c */
#include <gtk/gtk.h>
/* Esta funci<63>n rota la posici<63>n de los indicadores */
void rotate_book (GtkButton *boton, GtkNotebook *notebook)
{
gtk_notebook_set_tab_pos (notebook, (notebook->tab_pos +1) %4);
}
/* A<>ade/Elimina los indicadores de la p<>gina y los bordes */
void tabsborder_book (GtkButton *boton, GtkNotebook *notebook)
{
gint tval = FALSE;
gint bval = FALSE;
if (notebook->show_tabs == 0)
tval = TRUE;
if (notebook->show_border == 0)
bval = TRUE;
gtk_notebook_set_show_tabs (notebook, tval);
gtk_notebook_set_show_border (notebook, bval);
}
/* Elimina una p<>gina del libro de notas */
void remove_book (GtkButton *boton, GtkNotebook *notebook)
{
gint page;
page = gtk_notebook_current_page(notebook);
gtk_notebook_remove_page (notebook, page);
/* Hay que redibujar el widget --
Esto fuerza que el widget se autoredibuje */
gtk_widget_draw(GTK_WIDGET(notebook), NULL);
}
void delete (GtkWidget *widget, GtkWidget *event, gpointer data)
{
gtk_main_quit ();
}
int main (int argc, char *argv[])
{
GtkWidget *ventana;
GtkWidget *boton;
GtkWidget *table;
GtkWidget *notebook;
GtkWidget *frame;
GtkWidget *etiqueta;
GtkWidget *checkbutton;
int i;
char bufferf[32];
char bufferl[32];
gtk_init (&amp;argc, &amp;argv);
ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_signal_connect (GTK_OBJECT (ventana), "delete_event",
GTK_SIGNAL_FUNC (delete), NULL);
gtk_container_border_width (GTK_CONTAINER (ventana), 10);
table = gtk_table_new(2,6,TRUE);
gtk_container_add (GTK_CONTAINER (ventana), table);
/* Crea un nuevo libro de notas, indicando la posici<63>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<6E>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<65>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<65>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<6E>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<63>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<EFBFBD>n. Cada columna puede tener (opcionalmente) un t<>tulo, que
puede estar activado (opcionalmente), permiti<74>ndonos enlazar una
funci<EFBFBD>n con la selecci<63>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<73>ndar de crearlo, la forma f<>cil y la forma dif<69>cil. Pero
antes de crear una GtkCList, hay una cosa que debemos saber: <20>Cu<43>ntas
columnas va a tener?
No todas las columnas tienen que ser visibles y pueden utilizarse para
almacenar datos que est<73>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<63>n al problema es muy sencilla, pero la segunda
requerir<EFBFBD> alguna explicaci<63>n adicional. Cada columna puede tener un
t<EFBFBD>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<63>n y a<>adir m<>s tarde los t<>tulos
de forma manual.
<!-- ----------------------------------------------------------------- -->
<sect1>Modos de operaci<63>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<63>n de la
lista GtkCList. El primer argumento es el <em>widget</em> GtkCList, y el
segundo especifica el modo de selecci<63>n de la celda (est<73>n definidos
en <tt/gtkenums.h/). En el momento de escribir esto, estaban
disponibles los siguientes modos:
<itemize>
<item> GTK_SELECTION_SINGLE - La selecci<63>n o es NULL o contiene un
puntero GList a un elemento seleccionado.
<item> GTK_SELECTION_BROWSE - La selecci<63>n es NULL si la lista no
contiene <em>widgets</em> o si los que contiene son insensibles, en
caso contrario contendr<64> un puntero GList hacia una estructura GList,
y por tanto con exactamente un elemento.
<item> GTK_SELECTION_MULTIPLE - La selecci<63>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 -->
<EFBFBD>ste apunta de nuevo a una estructura GList para el segundo elemento
seleccionado y continua as<61>. <20>ste es, actualmente, el modo por
<bf>defecto</bf> para el <em>widget</em> GtkCList.
<item> GTK_SELECTION_EXTENDED - La selecci<63>n siempre es NULL.
</itemize>
Puede que se a<>adan otros modos en versiones posteriores de GTK.
Tambi<EFBFBD>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<73> ah<61>.
<item> GTK_POLICY_AUTOMATIC - La barra de desplazamiento estar<61> ah<61> 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<EFBFBD>n podemos definir como deber<65>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<62>n obtendr<64>
autom<EFBFBD>ticamente un conjunto de botones t<>tulo. Vivir<69>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<65>n nada m<>s que un t<>tulo. Hay cuatro llamadas diferentes que
nos ayudar<61>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<6F>n normal, y uno pasivo
es s<>lo una etiqueta. Las primeras dos llamadas de arriba
activar<EFBFBD>n/desactivar<61>n el bot<6F>n t<>tulo correspondiente a la columna
<tt/column/, mientras que las dos llamadas siguientes
activar<EFBFBD>n/desactivar<61>n todos los botones t<>tulo que hayan en el
<em>widget</em> <tt/clist/ que se le proporcione a la funci<63>n.
Pero, por supuesto, habr<62> casos en el que no querremos utilizar los
botones t<>tulo, as<61> que tambi<62>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 <20>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<61>
tiempo de programaci<63>n, y har<61> su programa m<>s peque<75>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<65>a necesitar de explicaciones adicionales.
<!-- ----------------------------------------------------------------- -->
<sect1>Manipulando la lista en s<>.
<p>
Es posible cambiar la justificaci<63>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<61> desde el lado
izquierdo.
<item>GTK_JUSTIFY_RIGHT - El texto en la columna empezar<61> desde el
lado derecho.
<item>GTK_JUSTIFY_CENTER - El texto se colocar<61> en el centro de la
columna.
<item>GTK_JUSTIFY_FILL - El texto utilizar<61> 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<63>n es muy importante, y deber<65>a ser un est<73>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 <20>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<63>tico
para la aplicaci<63>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<EFBFBD>n podemos ir hacia un elemento sin la intervenci<63>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<65> en la parte de
abajo. El resto de valores entre 0.0 y 1.0 son v<>lidos y har<61>n que la
fila aparezca entre la parte superior y la inferior. El <20>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<63>n, puede que no tengamos
que hacer un desplazamiento hacia un elemento que ya sea visible. Por
tanto, <20>c<EFBFBD>mo podemos saber si ya es visible? Como siempre, hay una funci<63>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<63>n parcial, porque si el valor devuelto es
GTK_VISIBILITY_PARTIAL, entonces es que alguna parte est<73> oculta,
pero no sabemos si es la fila que est<73> cortada por la parte de abajo
de la lista, o si la fila tiene columnas que est<73>n fuera.
Tambi<EFBFBD>n podemos cambiar el color del primer y del segundo plano de una
fila en particular. Esto es <20>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<EFBFBD>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<62> texto en las columnas
de la fila. Esto ser<65>a <20>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<62>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<65>a la <20>nica alternativa.
<tscreen><verb>
void gtk_clist_clear( GtkCList *clist );
</verb></tscreen>
Tambi<EFBFBD>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<61> 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<EFBFBD>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<64>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<65>amos hacer lo siguiente, suponiendo que
<tt/clist/, <tt/row/ y <tt/column/ ya existan:
<tscreen><verb>
gchar *mytext;
gtk_clist_get_pixtext(clist, row, column, &amp;mytext, NULL, NULL, NULL);
</verb></tscreen>
Hay una rutina m<>s que est<73> relacionada con lo que est<73> 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<EFBFBD>n hay una funci<63>n que nos permite especificar la indentaci<63>n de
un celda (horizontal o vertical). El valor de la indentaci<63>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<65> visible al usuario, pero puede serle <20>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<63>n
<p>
Tambi<EFBFBD>n hay funciones que nos permiten forzar la (de)selecci<63>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<62>n una funci<63>n que tomar<61> las coordenadas x e y (por ejemplo,
recibidas del rat<61>n), mirar<61> en la lista y devolver<65> 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<EFBFBD>n, o una pulsaci<63>n en cualquier lugar de la lista, podemos leer
las coordenadas del rat<61>n y encontrar en que elemento de la lista se
encuentra. <20>Engorroso? Afortunadamente, hay una forma m<>s sencilla de
hacer las cosas...
<!-- ----------------------------------------------------------------- -->
<sect1>Las se<73>ales que lo hacen todo
<p>
Como con el resto de <em>widgets</em>, hay unas cuantas se<73>ales que
podemos utilizar. El <em>widget</em> GtkCList est<73> derivado del
<em>widget</em> GtkContainer, y por tanto tiene las mismas
se<EFBFBD>ales que <20>ste, pero adem<65>s a<>ade las siguientes:
<itemize>
<item><tt/select_row/ - Esta se<73>al enviar<61> la siguiente informaci<63>n,
en este orden: GtkCList *clist, gint row, gint column, GtkEventButton
*event
<item><tt/unselect_row/ - Cuando el usuario deselecciona una fila, se
activar<EFBFBD> esta se<73>al. Envia la misma informaci<63>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<65> declarar como
<tscreen><verb>
void select_row_callback(GtkWidget *widget,
gint row,
gint column,
GdkEventButton *event,
gpointer data);
</verb></tscreen>
La llamada se conectar<61>, 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<71> tenemos algunos prototipos de las funciones de llamada */
void button_add_clicked( GtkWidget *boton, gpointer data);
void button_clear_clicked( GtkWidget *boton, gpointer data);
void button_hide_show_clicked( GtkWidget *boton, gpointer data);
void selection_made( GtkWidget *clist, gint row, gint column,
GdkEventButton *event, gpointer data);
gint main (int argc, gchar *argv[])
{
GtkWidget *ventana;
GtkWidget *vbox, *hbox;
GtkWidget *clist;
GtkWidget *button_add, *button_clear, *button_hide_show;
gchar *titles[2] = {"Ingredients","Amount"};
gtk_init(&amp;argc, &amp;argv);
ventana=gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_widget_set_usize(GTK_WIDGET(ventana), 300, 150);
gtk_window_set_title(GTK_WINDOW(ventana), "GtkCList Example");
gtk_signal_connect(GTK_OBJECT(ventana),
"destroy",
GTK_SIGNAL_FUNC(gtk_main_quit),
NULL);
vbox=gtk_vbox_new(FALSE, 5);
gtk_container_border_width(GTK_CONTAINER(vbox), 5);
gtk_container_add(GTK_CONTAINER(ventana), vbox);
gtk_widget_show(vbox);
/* Crear el GtkCList. Para este ejemplo utilizaremos 2 columnas */
clist = gtk_clist_new_with_titles( 2, titles);
/* Cuando se hace una selecci<63>n, queremos saber algo acerca de
* ella. La funci<63>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<64>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<6C> el bot<6F>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<71> hacemos la adici<63>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<6C> el bot<6F>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<6C> el bot<6F>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<71>, entonces el usuario ha seleccionado una fila de
* la lista.
*/
void selection_made( GtkWidget *clist, gint row, gint column,
GdkEventButton *event, gpointer data)
{
gchar *text;
/* Obtiene el texto que se ha almacenado en la fila y columna
* sobre las que se ha pulsado. Lo recibiremos como un puntero en
* el argumento text.
*/
gtk_clist_get_text(GTK_CLIST(clist), row, column, &amp;text);
/* Imprime alguna informaci<63>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> <20>rbol<label id="sec_Tree_Widgets">
<!-- ***************************************************************** -->
<p>
El prop<6F>sito del <em>widget</em> GtkTree es mostrar datos organizados
de forma jer<65>rquica. El <em>widget</em> GtkTree en s<> es un contenedor
vertical para los <em>widgets</em> del tipo GtkTreeItem. GtkTree en
s<EFBFBD> mismo no es muy diferente de GtkList - ambos est<73>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<6F>a de los
m<EFBFBD>todos de GtkTree funcionan igual que sus correspondientes de
GtkList. Sin embargo, GtkTree no est<73> derivado de GtkList, por lo que
no puede intercambiarlos.
<sect1> Creando un <20>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<65> cuando le a<>adan
elementos o cuando crezca alguno de sus sub<75>rboles. Por esta raz<61>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 <20>rbol, ya que el valor por defecto de GtkScrolledWindow
es bastante peque<75>o.
Ahora que ya sabemos como crear un <20>rbol, probablemente quiera
a<EFBFBD>adirle algunos elementos. <ref id="sec_Tree_Item_Widget" name="El
widget elemento de <20>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 <20>rbol utilizando una de las siguientes funciones
(ver <ref id="sec_GtkTree_Functions" name="Funciones y macros">
m<EFBFBD>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<75>rbol
<p>
Un sub<75>rbol se crea como cualquier otro <em>widget</em> GtkTree. Un
sub<EFBFBD>rbol se a<>ade a otro <20>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<75>rbol ni antes ni
despu<EFBFBD>s de a<>adirlo a GtkTreeItem. Sin embargo, <em>deber<65></em> haber
a<EFBFBD>adido el GtkTreeItem en cuesti<74>n a un <20>rbol padre antes de llamar a
<em/gtk_tree_item_set_subtree()/. Esto se debe a que, t<>cnicamente,
el padre del sub<75>rbol <em>no</em> es el GtkTreeItem <20>propietario<69>,
sino el GtkTree que contiene al GtkTreeItem.
Cuando le a<>ade un sub<75>rbol a un GtkTreeItem, aparece el signo de un
m<EFBFBD>s o de un menos a su lado, donde puede pinchar el usuario para
<EFBFBD>expandirlo<EFBFBD> u <20>contraerlo<6C>, o sea, para mostrar u ocultar su
sub<EFBFBD>rbol. Los GtkTreeItems est<73>n contra<72>dos por defecto. Observe que
cuando contrae un GtkTreeItem, cualquier elemento seleccionado en el
sub<EFBFBD>rbol permanece seleccionado, que puede no coincidir con lo que el
usuario espera.
<sect1> Manejando la lista de selecci<63>n
<p>
Como con GtkList, GtkTree tiene un campo <tt>selection</tt>, y
es posible controlar el comportamiento del <20>rbol (de alguna manera)
estableciendo el tipo de selecci<63>n, utilizando:
<tscreen><verb>
void gtk_tree_set_selection_mode( GtkTree *arbol,
GtkSelectionMode mode );
</verb></tscreen>
La sem<65>ntica asociada con los distintos modos de selecci<63>n est<73>
descrita en la secci<63>n del <em>widget</em> GtkList. Como ocurr<72>a con
el <em>widget</em> GtkList, se enviar<61>n las se<73>ales <tt/select_child/,
<tt/unselect_child/ (realmente no - ver <ref id="sec_GtkTree_Signals"
name="Se<53>ales"> m<>s adelante para una explicaci<63>n), y
<tt/selection_changed/ cuando los elementos de la lista sean
seleccionados o deseleccionados. Sin embargo, para aprovechar estas
se<EFBFBD>ales, necesita conocer por medio <em>de que</em> <em>widget</em>
GtkTree ser<65>n emitidas, y donde encontrar una lista con los elementos
seleccionados.
Todo esto es una potencial fuente de confusi<73>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<61>n (<28>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<65>fica al <em>widget</em>
GtkTree superior de la jerarquia, conocido como el <20><>rbol ra<72>z<EFBFBD>.
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 <20>rbol ra<72>z. En vez de eso, utilice la
macro GTK_TREE_SELECTION (arbol), que da la lista selecci<63>n del <20>rbol
ra<EFBFBD>z como un puntero <tt/GList/. Por supuesto, esta lista puede
incluir elementos que no est<73>n en el sub<75>rbol en cuesti<74>n si el tipo
de selecci<63>n es GTK_SELECTION_MULTIPLE.
Para terminar, las se<73>ales <tt/select_child/ (y tt/unselect_child/, en
teor<EFBFBD>a) son emitidas por todos los <20>rboles, pero la se<73>al
<em/selection_changed/ es emitida s<>lo por el <20>rbol ra<72>z. En
consecuencia, si quiere manipular la se<73>al <tt/select_child/ de un
<EFBFBD>rbol y todos sus sub<75>rboles, tendr<64> que llamar a
<tt/gtk_signal_connect()/ una vez por cada sub<75>rbol.
<sect1> Estructura interna del <em>widget</em> <20>rbol
<p>
La definici<63>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<63>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 <20>rbol es <20>rbol ra<72>z de una jerarquia
GtkTree, mientras que GTK_TREE_ROOT_TREE (arbol) devuelve el <20>rbol
ra<EFBFBD>z, un objeto de tipo GtkTree (recuerde transformarlo utilizando
GTK_WIDGET (arbol) si quiere utilizar con <20>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<61>rselo a la funci<63>n
<tt/gtk_container_children()/. Con esto crearemos un duplicado de la
lista original, por lo que deber<65> eliminarlo de la memoria utilizando
<tt/g_list_free()/ despu<70>s haber hecho con <20>l lo que tenga que hacer,
o bien crear un bucle que lo vaya destruyendo de elemento en elemento,
como por ejemplo as<61>:
<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<73> definido en sub<75>rboles, donde
apunta al <em>widget</em> GtkTreeItem que contiene al <20>rbol en
cuesti<EFBFBD>n. El campo <tt>level</tt> indica el nivel de profundidad de un
<EFBFBD>rbol en particular; los <20>rboles ra<72>z tienen un nivel 0, y cada nivel
sucesivo de sub<75>rboles tiene un nivel superior al del padre. S<>lo se
puede asegurar que este campo contiene un valor correcto despu<70>s de
que el <em>widget</em> GtkTree se dibuje en la pantalla.
<sect2> Se<53>ales<label id="sec_GtkTree_Signals">
<p>
<tscreen><verb>
void selection_changed( GtkTree *arbol );
</verb></tscreen>
Esta se<73>al se emitir<69> 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<73>al se emite cuando se est<73> 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<EFBFBD>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<73>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<69>s
en otras ocasiones, pero <em>no</em> cuando la pulsaci<63>n de un bot<6F>n
deselecciona un hijo, y tampoco por la emisi<73>n de la se<73>al <20>toggle<6C>
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<65> NULL si se produce alg<6C>n
error.
<tscreen><verb>
void gtk_tree_append( GtkTree *arbol,
GtkWidget *elemento_arbol );
</verb></tscreen>
A<EFBFBD>ade un <20>rbol a un GtkTree.
<tscreen><verb>
void gtk_tree_prepend( GtkTree *arbol,
GtkWidget *elemento_arbol );
</verb></tscreen>
Prea<EFBFBD>ade un <20>rbol a un GtkTree.
<tscreen><verb>
void gtk_tree_insert( GtkTree *arbol,
GtkWidget *elemento_arbol,
gint posicion );
</verb></tscreen>
Inserta un <20>rbol en un GtkTree en la posici<63>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 <20>rbol lo dereferencia (y por tanto
normalmente) lo destruye (""), a <20>l <em>y</em> a su sub<75>rbol, de
haberlo, <em>y</em> a todos los sub<75>rboles que contenga ese
sub<EFBFBD>rbol. Si quiere eliminar s<>lo un elemento, deber<65> 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<63>n <tt>start</tt>
hasta la posici<63>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<73>al <tt/select_item/ para el hijo que se encuentra en la
posici<EFBFBD>n <tt>item</tt>, y por tanto selecciona a ese hijo (a menos que
lo deseleccione en un manejador de se<73>al...)
<tscreen><verb>
void gtk_tree_unselect_item( GtkTree *arbol,
gint item );
</verb></tscreen>
Emite la se<73>al <tt/unselect_item/ para el hijo en la posici<63>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<73>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<73>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<63>n en el <20>rbol de <tt>child</tt>, a menos que
<tt>child</tt> no est<73> en el <20>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<63>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<73>
definido para los <20>rboles ra<72>z, que es donde tiene sentido, ya que el
<EFBFBD>rbol ra<72>z es el <20>propietario<69> de la selecci<63>n. Establecer este
valor en un sub<75>rbol no tiene ning<6E>n efecto en absoluto; el valor
simplemente ser<65> ignorado.
<tscreen><verb>
void gtk_tree_set_view_mode( GtkTree *arbol,
GtkTreeViewMode mode );
</verb></tscreen>
Establece el <20>modo de visi<73>n<EFBFBD>, que puede ser o GTK_TREE_VIEW_LINE
(por defecto) o GTK_TREE_VIEW_ITEM. El modo de visi<73>n se propaga
de un <20>rbol a sus sub<75>rboles, y no puede establecerse en exclusiva
para un sub<75>rbol (esto no es exacto del todo - vea los comentarios en el
c<EFBFBD>digo de ejemplo).
El termino <20>modo de visi<73>n<EFBFBD> es algo ambiguo - b<>sicamente, controla
la forma en que se resalta a uno de los hijos del <20>rbol cuando es
seleccionado. Si es GTK_TREE_VIEW_LINE, se resaltar<61> el
<em>widget</em> GtkTreeItem completo, mientras que si es
GTK_TREE_VIEW_ITEM, s<>lo se resaltar<61> 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<61>n las l<>neas de conexi<78>n entre los elementos
del <20>rbol. <tt>flag</tt> es o TRUE, en cuyo caso se dibujar<61>n, o
FALSE, en cuyo caso no se dibujar<61>n.
<tscreen><verb>
GtkTree *GTK_TREE (gpointer obj);
</verb></tscreen>
Convierte un puntero gen<65>rico a `GtkTree *'.
<tscreen><verb>
GtkTreeClass *GTK_TREE_CLASS (gpointer class);
</verb></tscreen>
Convierte un puntero gen<65>rico a `GtkTreeClass *'.
<tscreen><verb>
gint GTK_IS_TREE (gpointer obj);
</verb></tscreen>
Determina si un puntero gen<65>rico se refiere a un objeto `GtkTree'.
<tscreen><verb>
gint GTK_IS_ROOT_TREE (gpointer obj)
</verb></tscreen>
Determina si un puntero gen<65>rico se refiere a un objeto `GtkTree'
<em>y</em> es un <20>rbol ra<72>z. Aunque la funci<63>n acepta cualquier
puntero, los resultados de pasarle un puntero que no se refiera
a un GtkTree no est<73>n definidos y probablemente no tengan ning<6E>n
sentido.
<tscreen><verb>
GtkTree *GTK_TREE_ROOT_TREE (gpointer obj)
</verb></tscreen>
Devuelve el <20>rbol ra<72>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<63>n del <20>rbol ra<72>z de un objeto
`GtkTree'. Seguimos con el mismo problema que antes.
<sect1> El <em>widget</em> elemento de <20>rbol<label id="sec_Tree_Item_Widget">
<p>
El <em>widget</em> GtkTreeItem, c<>mo el GtkListItem, est<73> derivado
de GtkItem, que de nuevo, est<73> derivado de GtkBin. Sin embargo, el
elemento en s<> mismo es un contenedor gen<65>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 <20>nico que nos
interesa ahora es el campo <em>sub<75>rbol</em>.
La definici<63>n de la estructura GtkTreeItem es as<61>:
<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<73>n y contracci<63>n. El
campo <tt>pixmaps</tt> apunta a una estructura de datos interna. Ya que
siempre puede obtener el sub<75>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<73> haciendo.
Ya que est<73> 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<63>n la
funci<EFBFBD>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<63>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<EFBFBD>mo no es obligatorio a<>adir una GtkLabel a un GtkTreeItem, puede
tambi<EFBFBD>n a<>adirle un GtkHBox o una GtkArrow, o hasta un GtkNotebook
(aunque en esos casos su aplicaci<63>n no ser<65> muy popular).
Si elimina todos los elementos de un sub<75>rbol, ser<65> destruido
y se eliminar<61> la informaci<63>n sobre su padre, a menos que lo
referencie de antemano, adem<65>s el GtkTreeItem que sea su propietario
se colapsar<61>. Por lo tanto, si quiere que se mantenga el sub<75>rbol
tendr<EFBFBD> que hacer algo as<61>:
<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<63>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<65>s de haber sido a<>adido a
GtkTree, sino que adem<65>s cada su <em>widget</em> padre tiene a su vez
un padre, y as<61> hasta llegar al nivel m<>s alto o ventana de di<64>logo,
cuando llamamos a <tt/gtk_widget_dnd_drag_set()/ o
<tt/gtk_widget_dnd_drop_set()/. En caso contrario, podr<64>an ocurrir
cosas extra<72>as.
<sect2> Se<53>ales
<p>
GtkTreeItem hereda las se<73>ales <tt/select/, <tt/deselect/, y
<tt/toggle/ de GtkItem. Adem<65>s, a<>ade dos se<73>ales propias, <tt/expand/
y <tt/collapse/.
<tscreen><verb>
void select( GtkItem *elemento_arbol );
</verb></tscreen>
Esta se<73>al se emite cuando un elemento est<73> siendo seleccionado,
o bien despu<70>s de que el usuario pinche en <20>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<73>al se emite cuando un elemento est<73> siendo deseleccionado,
o bien despu<70>s de que el usuario pinche en <20>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<62>n se
emitir<EFBFBD> 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<73>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 <20>rbol padre del elemento, si el
elemento tiene un <20>rbol padre. Si no lo tiene, entonces se cambiar<61> el
resaltado del elemento.
<tscreen><verb>
void expand( GtkTreeItem *elemento_arbol );
</verb></tscreen>
Esta se<73>al se emite cuando se est<73> expandiendo el sub<75>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<73>al se emite cuando se est<73> contrayendo el sub<75>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<65> NULL si hay alg<6C>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 <20>nico hijo. El nuevo <em>widget</em> se devolver<65> como
un puntero a un objeto GtkWidget. Se devolver<65> NULL en caso
de haber alg<6C>n fallo.
<tscreen><verb>
void gtk_tree_item_select( GtkTreeItem *elemento_arbol );
</verb></tscreen>
Esta funci<63>n es b<>sicamente un recubrimiento de una llamada a
gtk_item_select (GTK_ITEM (elemento_arbol)) que emitir<69> la
se<EFBFBD>al select.
<tscreen><verb>
void gtk_tree_item_deselect( GtkTreeItem *elemento_arbol );
</verb></tscreen>
Esta funci<63>n es b<>sicamente un recubrimiento de una llamada a
gtk_item_deselect (GTK_ITEM (elemento_arbol)) que emitir<69> la
se<EFBFBD>al deselect.
<tscreen><verb>
void gtk_tree_item_set_subtree( GtkTreeItem *elemento_arbol,
GtkWidget *subarbol );
</verb></tscreen>
Esta funci<63>n a<>ade <tt/subarbol/ a <tt/elemento_arbol/, mostr<74>ndolo si
<tt/elemento_arbol/ est<73> expandido, u ocult<6C>ndolo si <tt/elemento_arbol/ est<73>
contra<EFBFBD>do. De nuevo, recuerde que el <tt/elemento_arbol/ ya debe de haber
sido a<>adido a un <20>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<75>rboles del <tt/elemento_arbol/
(esto es, dereferencia y destruye a los sub<75>rboles hijos, y a los
hijos de los hijos y...), entonces elimina el sub<75>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<73>al <20>expand<6E> para el <tt/elemento_arbol/, que lo
expande.
<tscreen><verb>
void gtk_tree_item_collapse( GtkTreeItem *elemento_arbol );
</verb></tscreen>
Esto emite la se<73>al <20>collapse<73> en el <tt/elemento_arbol/, que lo
contrae.
<tscreen><verb>
GtkTreeItem *GTK_TREE_ITEM (gpointer obj)
</verb></tscreen>
Convierte un puntero gen<65>rico en un `GtkTreeItem *'.
<tscreen><verb>
GtkTreeItemClass *GTK_TREE_ITEM_CLASS (gpointer obj)
</verb></tscreen>
Convierte un puntero gen<65>rico en un `GtkTreeItemClass'.
<tscreen><verb>
gint GTK_IS_TREE_ITEM (gpointer obj)
</verb></tscreen>
Determina si un puntero gen<65>rico se refiere a un objeto `GtkTreeItem'.
<tscreen><verb>
GtkWidget GTK_TREE_ITEM_SUBTREE (gpointer obj)
</verb></tscreen>
Devuelve un sub<75>rbol del elemento (<tt/obj/ deber<65>a apuntar a un
objeto `GtkTreeItem').
<sect1> <20>rbol ejemplo
<p>
Este ejemplo es muy parecido al <20>rbol ejemplo que hay en
<tt/testgtk.c/, pero mucho menos completo (aunque mucho mejor
comentado). Pone una ventana con un <20>rbol, y conecta todas las se<73>ales
de los objetos relevantes, con lo que podr<64> 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<73>ales GtkItem:: y GtkTreeItem:: */
static void cb_itemsignal (GtkWidget *item, gchar *signame)
{
gchar *name;
GtkLabel *etiqueta;
/* Es un GtkBin, por lo que tiene un hijo, que sabemos que es una
* etiqueta, por lo que la cogemos */
etiqueta = GTK_LABEL (GTK_BIN (item)->child);
/* Conseguimos el texto de la etiqueta */
gtk_label_get (etiqueta, &amp;name);
/* Conseguimos el nivel del <20>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<61> a esta funci<63>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<61> a esta funci<63>n cada vez que el usuario pulse en un
* elemento, est<73> o no seleccionado. */
whether it is already selected or not. */
static void cb_select_child (GtkWidget *arbol_raiz, GtkWidget *hijo,
GtkWidget *subarbol)
{
g_print ("select_child called for root tree %p, subtree %p, child %p\n",
arbol_raiz, subarbol, hijo);
}
static void cb_selection_changed (GtkWidget *arbol)
{
GList *i;
g_print ("selection_change called for tree %p\n", arbol);
g_print ("selected objects are:\n");
i = GTK_TREE_SELECTION(arbol);
while (i){
gchar *name;
GtkLabel *etiqueta;
GtkWidget *item;
/* Get a GtkWidget pointer from the list node */
item = GTK_WIDGET (i->data);
etiqueta = GTK_LABEL (GTK_BIN (item)->child);
gtk_label_get (etiqueta, &amp;name);
g_print ("\t%s on level %d\n", name, GTK_TREE
(item->parent)->level);
i = i->next;
}
}
int main (int argc, char *argv[])
{
GtkWidget *ventana, *scrolled_win, *arbol;
static gchar *itemnames[] = {"Foo", "Bar", "Baz", "Quux",
"Maurice"};
gint i;
gtk_init (&amp;argc, &amp;argv);
/* una ventana general */
ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_signal_connect (GTK_OBJECT(ventana), "delete_event",
GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
gtk_container_border_width (GTK_CONTAINER(ventana), 5);
/* una ventana con barras de desplazamiento */
scrolled_win = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
GTK_POLICY_AUTOMATIC,
GTK_POLICY_AUTOMATIC);
gtk_widget_set_usize (scrolled_win, 150, 200);
gtk_container_add (GTK_CONTAINER(ventana), scrolled_win);
gtk_widget_show (scrolled_win);
/* Crear el <20>rbol ra<72>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<63>n */
gtk_tree_set_selection_mode (GTK_TREE(arbol),
GTK_SELECTION_MULTIPLE);
/* mostrar el <20>rbol */
gtk_widget_show (arbol);
for (i = 0; i < 5; i++){
GtkWidget *subarbol, *item;
gint j;
/* Crear un elemento del <20>rbol */
item = gtk_tree_item_new_with_label (itemnames[i]);
/* Conectar todas las se<73>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 <20>rbol padre */
gtk_tree_append (GTK_TREE(arbol), item);
/* Mostrarlo - esto se puede hacer en cualquier momento */
gtk_widget_show (item);
/* Crear el sub<75>rbol de este elemento */
subarbol = gtk_tree_new();
g_print ("-> item %s->%p, subtree %p\n", itemnames[i], item,
subarbol);
/* Esto todav<61>a es necesario si quiere que se llamen a est<73>n
* se<73>ales en el sub<75>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<6E>n efecto, ya que se ignora
* completamente en los sub<75>rboles */
gtk_tree_set_selection_mode (GTK_TREE(subarbol),
GTK_SELECTION_SINGLE);
/* Esto tampoco hace nada, pero por una raz<61>n diferente - los
* valores view_mode y view_line de un <20>rbol se propagan a los
* sub<75>rboles cuando son mapeados. Por tanto, establecer los
* valores despu<70>s actualmente tendr<64>a (alg<6C>n impredecible) efecto
*/
gtk_tree_set_view_mode (GTK_TREE(subarbol), GTK_TREE_VIEW_ITEM);
/* Establecer este sub<75>rbol del elemento - <20>Recuerde que no puede
* hacerlo hasta que se haya a<>adido a su <20>rbol padre! */
gtk_tree_item_set_subtree (GTK_TREE_ITEM(item), subarbol);
for (j = 0; j < 5; j++){
GtkWidget *subitem;
/* Crea un elemento sub<75>rbol, m<>s o menos lo mismo de antes */
subitem = gtk_tree_item_new_with_label (itemnames[j]);
/* Conectar todas las se<73>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 <20>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<65>
<!-- ***************************************************************** -->
<p>
Hay dos formas de crear men<65>s, la f<>cil, y la dif<69>cil. Ambas tienen su
utilidad, aunque lo m<>s probable es que normalmente utilice la
menufactory (la forma f<>cil). La forma <20>dif<69>cil<69> consiste en crear
todos los men<65>s utilizando las llamadas directamente. La forma f<>cil
consiste en utilizar las llamadas de <tt/gtk_item_factory/. Es mucho
m<EFBFBD>s f<>cil, pero aun as<61> cada aproximaci<63>n tiene sus ventajas y sus
inconvenientes.
La menufactory es mucho m<>s f<>cil de utilizar, y tamb<6D>en es m<>s f<>cil
a<EFBFBD>adir nuevos men<65>s, aunque a larga, escribiendo unas cu<63>ntas
funciones de recubrimiento para crear men<65>s utilizando el m<>todo
manual puede acabar siendo m<>s <20>til. Con la itemfactory, no es posible
a<EFBFBD>adir im<69>genes o el car<61>cter `/' a los men<65>s.
<!-- ----------------------------------------------------------------- -->
<sect1>Creaci<63>n manual de men<65>s
<p>
Siguiendo la aut<75>ntica tradici<63>n de la ense<73>anza, vamos a ense<73>arle
primero la forma dif<69>cil. <tt>:)</tt>
Se utilizan tres <em>widgets</em> para hacer una barra de men<65>s y
submen<EFBFBD>s:
<itemize>
<item>un elemento del men<65>, que es lo que el usuario quiere seleccionar,
p.e. 'Guardar'
<item>un men<65>, que actua como un contenedor para los elementos del men<65>, y
<item>una barra de men<65>, que es un contenedor para cada uno de los men<65>s,
</itemize>
Todo esto se complica ligeramente por el hecho de que los
<em>widgets</em> de los elementos del men<65> se utilizan para dos cosas
diferentes. Est<73>n los <em>widgets</em> que se empaquetan en el men<65>, y
los que se empaquetan en una barra de men<65>s, que cuando se selecciona,
activa el men<65>.
Vamos a ver las funciones que se utilizan para crear men<65>s y barras
de men<65>s. <20>sta primera funci<63>n se utiliza para crear una barra de men<65>s.
<tscreen><verb>
GtkWidget *gtk_menu_bar_new( void );
</verb></tscreen>
Como el propio nombre indica, esta funci<63>n crea una nueva barra de
men<EFBFBD>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<63>n devuelve un puntero a un nuevo men<65>, que no se debe
mostrar nunca (no hace falta utilizar <tt/gtk_widget_show/), es s<>lo
un contenedor para los elementos del men<65>. 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<65>
que se empaquetar<61>n en el men<65> (y en la barra de men<65>).
<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<65> que
van a mostrarse. Recuerde que hay que distinguir entre un <20>men<65><6E>
creado con <tt/gtk_menu_new/ y un <20>elemento del men<65><6E> creado con las
funciones <tt/gtk_menu_item_new/. El elemento de men<65> ser<65> un bot<6F>n
con una acci<63>n asociada, y un men<65> ser<65> un contenedor con los
elementos del men<65>.
Las funciones <tt/gtk_menu_new_with_label/ y <tt/gtk_menu_new/ son
s<EFBFBD>lo lo que espera que sean despu<70>s de leer lo de los botones. Una
crea un nuevo elemento del men<65> con una etiqueta ya dentro, y la otra
crea un elemento del men<65> en blanco.
Una vez ha creado un elemento del men<65> tiene que ponerlo en un men<65>.
Esto se hace utilizando la funci<63>n <tt/gtk_menu_append/. Para capturar
el momento en el que el elemento se selecciona por el usuario,
necesitamos conectar con la se<73>al <tt/activate/ de la forma usual. Por
tanto, si quiere crear un men<65> est<73>ndar <tt/File/, con las opciones
<tt/Open/, <tt/Save/ y <tt/Quit/ el c<>digo deber<65>a ser algo como
<tscreen><verb>
file_menu = gtk_menu_new(); /* No hay que mostrar men<65>s */
/* Crear los elementos del men<65> */
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<65> */
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<63>n de llamada a la se<73>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<65> Quit con nuestra funci<63>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<65> */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<65>. Ahora necesitamos crear una
barra de men<65>s y un elemento de men<65> para el elemento <tt/File/, que
vamos a a<>adir a nuestro men<65>. 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<65> con <tt/file_item/. Esto se hace con
la funci<63>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<65> a la barra de men<65>s, que
se hace mediante la funci<63>n
<tscreen>
void gtk_menu_bar_append( GtkMenuBar *menu_bar, GtkWidget *menu_item);
</tscreen>
que en nuestro caso habr<62> que utilizar as<61>:
<tscreen><verb>
gtk_menu_bar_append( GTK_MENU_BAR (menu_bar), file_item );
</verb></tscreen>
Si queremos que el men<65> est<73> alineado a la derecha en la barra de
men<EFBFBD>s, como suele estar la opci<63>n de ayuda, podemos utilizar la
funci<EFBFBD>n siguiente (otra vez en <tt/file_item/ en el ejemplo actual)
antes de enlazarla en la barra de men<65>.
<tscreen><verb>
void gtk_menu_item_right_justify( GtkMenuItem *menu_item );
</verb></tscreen>
Aqu<EFBFBD> hay un resumen de los pasos que son necesarios para crear una
barra de men<65>s con los men<65>s correspondientes ya enlazados:
<itemize>
<item> Crear un nuevo men<65> utilizando <tt/gtk_menu_new()/
<item> Utilizar multiples llamadas a <tt/gtk_menu_item_new()/ para
cada elemento que desee tener en su men<65>. Y utilizar
<tt/gtk_menu_append()/ para poner cada uno de esos nuevos elementos en
el men<65>.
<item> Crear un elemento de men<65> utilizando
<tt/gtk_menu_item_new()/. <20>sta ser<65> la ra<72>z del men<65>, el texto que
aparezca aqu<71> estar<61> en la barra de men<65>s.
<item> Utilizar <tt/gtk_menu_item_set_submenu()/ para enlazar el men<65>
al elemento del men<65> ra<72>z (el creado en el paso anterior).
<item> Crear una nueva barra de men<65>s utilizando
<tt/gtk_menu_bar_new/. Este paso solo necesita hacerse una vez cuando
se crea una serie de men<65>s en una barra de men<65>s.
<item> Utilizar <tt/gtk_menu_bar_append/ para poner el men<65> ra<72>z en la
barra de men<65>s.
</itemize>
Para hacer un men<65> desplegable hay que seguir pr<70>cticamente los mismos
pasos. La <20>nica diferencia es que el men<65> no estar<61> conectado
`autom<6F>ticamente' a una barra de men<65>, sino que para que aparezca
deber<EFBFBD> llamarse expl<70>citamente a la funci<63>n <tt/gtk_menu_popup()/
utilizando, por ejemplo, un evento de pulsaci<63>n de bot<6F>n. Siga los
pasos siguientes:
<itemize>
<item>Cree una funci<63>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<65>.
<item>En el manejador de eventos, si el evento es una pulsaci<63>n de un
bot<EFBFBD>n del rat<61>n, tratar <tt>event</tt> como un evento de bot<6F>n
(que lo es) y utilizarlo como se indica en el c<>digo ejemplo para
pasarle informaci<63>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<63>n manejadora, y <tt>menu</tt> es un men<65>
creado con <tt/gtk_menu_new()/. <20>ste puede ser un men<65> que est<73>
contenido en una barra de men<65>s, como se puede ver en el c<>digo de
ejemplo.
</itemize>
<!-- ----------------------------------------------------------------- -->
<sect1>Ejemplo de la creaci<63>n manual de un men<65>
<p>
Esto deber<65>a funcionar. <20>chele un vistazo al ejemplo para aclarar los
conceptos.
<tscreen><verb>
/* principio del ejemplo menu menu.c */
#include <gtk/gtk.h>
static gint button_press (GtkWidget *, GdkEvent *);
static void menuitem_response (gchar *);
int main (int argc, char *argv[])
{
GtkWidget *ventana;
GtkWidget *menu;
GtkWidget *menu_bar;
GtkWidget *root_menu;
GtkWidget *menu_items;
GtkWidget *vbox;
GtkWidget *boton;
char buf[128];
int i;
gtk_init (&amp;argc, &amp;argv);
/* crear una nueva ventana */
ventana = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_widget_set_usize( GTK_WIDGET (ventana), 200, 100);
gtk_window_set_title(GTK_WINDOW (ventana), "GTK Menu Test");
gtk_signal_connect(GTK_OBJECT (ventana), "delete_event",
(GtkSignalFunc) gtk_main_quit, NULL);
/* Inicializar el widget-menu, y recuerde -- <20><>Nunca haga
* gtk_show_widget() con el widget menu!!
* <20>ste es el men<65> que contiene todos los elementos del men<65>, el
* que se desplegar<61> cuando pulse en el "Root Menu" en la
* aplicaci<63>n
*/
menu = gtk_menu_new();
/* Ahora hacemos un peque<75>o bucle que crea tres elementos de men<65>
* para "test-menu". Recuerde llamar a gtk_menu_append. Aqu<71>
* estamos a<>adiendo una lista de elementos de men<65> a nuestro
* men<65>. Normalmente tendr<64>amos que cazar aqu<71> la se<73>al "clicked"
* de cada uno de los elementos del men<65> y le deber<65>amos dar una
* funci<63>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<65> con un nombre... */
menu_items = gtk_menu_item_new_with_label(buf);
/* ...y lo a<>ade al men<65>. */
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);
}
/* <20>sta es el men<65> ra<72>z, y ser<65> la etiqueta mostrada en la
* barra de men<65>s. No habr<62> ning<6E>n manejador de se<73>al conectado, ya que
* lo <20>nico que hace es desplegar el resto del men<65>. */
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<65> para el "root menu" */
gtk_menu_item_set_submenu(GTK_MENU_ITEM (root_menu), menu);
/* Un vbox para poner dentro un men<65> y un bot<6F>n */
vbox = gtk_vbox_new(FALSE, 0);
gtk_container_add(GTK_CONTAINER(ventana), vbox);
gtk_widget_show(vbox);
/* Crear una barra de men<65> para que contenga al men<65> 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<6F>n al que atar los men<65>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<65> y la barra de men<65> --
* <20>ste es el elemento de men<65> "ra<72>z" sobre el que he estado
* delirando =) */
gtk_menu_bar_append(GTK_MENU_BAR (menu_bar), root_menu);
/* siempre mostramos la ventana como <20>ltimo paso para que todo se
* pongo en pantalla a la vez. */
gtk_widget_show(ventana);
gtk_main ();
return 0;
}
/* Responde a una pulsaci<63>n del bot<6F>n enviando un men<65> como un widget
* Recuerde que el argumento "widget" es el men<65> que se est<73> enviando,
* NO el bot<6F>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<61> a la rutina que hemos manejado el
* evento; la historia termina aqu<71>. */
return TRUE;
}
/* Le dice al que llam<61> a la rutina que no hemos manejado el
* evento. */
return FALSE;
}
/* Imprime una cadena cuando se selecciona un elemento del men<65> */
static void menuitem_response (gchar *string)
{
printf("%s\n", string);
}
/* final del ejemplo */
</verb></tscreen>
Tambi<EFBFBD>n puede hacer que un elemento del men<65> sea insensible y, utilizando
una tabla de teclas aceleradoras, conectar las teclas con las funciones
del men<65>.
<!-- XXX Las dos sect1 que vienen han cambiado -->
<!-- ----------------------------------------------------------------- -->
<sect1>Utilizando GtkItemFactory
<p>
Ahora que le hemos ense<73>ado la forma dif<69>cil, le mostraremos como
utilizar las llamadas <tt/gtk_item_factory/.
<!-- ----------------------------------------------------------------- -->
<sect1>Ejemplo de la f<>brica de elementos
<p>
Aqu<EFBFBD> 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<63>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<65>es.
This is the GtkItemFactoryEntry structure used to generate new menus.
Elemento 1: La direcci<63>n del men<65>. La letra que hay
despu<70>s del subrayado indica una tecla aceleradora
una vez que el men<65> est<73> abierto.
Elemento 2: La tecla aceleradora para la entrada del men<65>.
Elemento 3: La funci<63>n de llamada.
Elemento 4: La acci<63>n de llamada. Cambia los par<61>metros que
se le pasan a la funci<63>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<63>n
"<ToggleItem>" -> crea un elemento de selecci<63>n
"<RadioItem>" -> crea un elemento circular
<path> -> direcci<63>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<63>n inicializa la f<>brica de elementos
Param 1: El tipo de men<65> - puede ser GTK_TYPE_MENU_BAR,
GTK_TYPE_MENU, o GTK_TYPE_OPTION_MENU.
Param 2: La direcci<63>n del men<65>.
Param 3: Un puntero a un gtk_accel_group. La f<>brica de
elementos actualiza la tabla de teclas aceleradoras
mientras genera los men<65>es.
*/
item_factory = gtk_item_factory_new (GTK_TYPE_MENU_BAR, "<main>",
accel_group);
/* Esta funci<63>n genera los elementos de men<65>. 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<65>. */
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<65> creada por la
* f<>brica de elementos. */
*menubar = gtk_item_factory_get_widget (item_factory, "<main>");
}
int main( int argc,
char *argv[] )
{
GtkWidget *ventana;
GtkWidget *main_vbox;
GtkWidget *menubar;
gtk_init (&amp;argc, &amp;argv);
ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
GTK_SIGNAL_FUNC (gtk_main_quit),
"WM destroy");
gtk_window_set_title (GTK_WINDOW(ventana), "Item Factory");
gtk_widget_set_usize (GTK_WIDGET(ventana), 300, 200);
main_vbox = gtk_vbox_new (FALSE, 1);
gtk_container_border_width (GTK_CONTAINER (main_vbox), 1);
gtk_container_add (GTK_CONTAINER (ventana), main_vbox);
gtk_widget_show (main_vbox);
get_main_menu (ventana, &amp;menubar);
gtk_box_pack_start (GTK_BOX (main_vbox), menubar, FALSE, TRUE, 0);
gtk_widget_show (menubar);
gtk_widget_show (ventana);
gtk_main ();
return(0);
}
/* fin del ejemplo */
</verb></tscreen>
Por ahora, s<>lo est<73> este ejemplo. Ya llegar<61> una
explicaci<EFBFBD>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<62>n hay un
gran n<>mero de teclas para la edici<63>n de textos, que son compatibles
con Emacs.
El <em>widget</em> texto admite copiar-y-pegar, incluyendo la
utilizaci<EFBFBD>n de doble y triple-click para seleccionar una palabra y una
l<EFBFBD>nea completa, respectivamente.
<!-- ----------------------------------------------------------------- -->
<sect1>Creando y configurando un cuadro de texto
<p>
S<EFBFBD>lo hay una funci<63>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<69>n dar al <em>widget</em> texto punteros a
<tt/GtkAdjustement/ que pueden ser utilizados para controlar la visi<73>n
de la posici<63>n del <em>widget</em>. Si le ponemos un valor NULL en
cualquiera de los dos argumentos (o en los dos), la funci<63>n
<tt/gtk_text_new/ crear<61> su propio ajuste.
<tscreen><verb>
void gtk_text_set_adjustments( GtkText *text,
GtkAdjustment *hadj,
GtkAdjustment *vadj );
</verb></tscreen>
La funci<63>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<6F>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<EFBFBD>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<74>ndonos
mostrar varias l<>neas de texto al usuario. Para cambiar entre estos
dos modos de operaci<63>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<61> un cursor
en la posici<63>n actual de inserci<63>n.
Sin embargo la utilizaci<63>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<63>n:
<tscreen><verb>
void gtk_text_set_word_wrap( GtkText *text,
gint word_wrap );
</verb></tscreen>
Utilizando esta funci<63>n podremos especificar que el <em>widget</em>
texto deber<65>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<63>n de texto
<P>
El punto actual de inserci<63>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<63>n en la que poner el punto de inserci<63>n.
An<EFBFBD>logamente tenemos la funci<63>n para obtener la posici<63>n del punto
de inserci<63>n:
<tscreen><verb>
guint gtk_text_get_point( GtkText *text );
</verb></tscreen>
Una funci<63>n que es <20>til en combinaci<63>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<63>n actual del cursor, tendr<64> que
utilizar la funci<63>n <tt/gtk_text_insert/, que nos permitir<69>
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<61> que
se utilicen los valores que indiquen el estilo del <em>widget</em>.
Utilizar un valor de <tt/-1/ para el par<61>metro <tt/length/ har<61>
que se inserte todo el texto.
El <em/widget/ texto es uno de los pocos de GTK que se redibuja
a s<> mismo din<69>micamente, fuera de la funci<63>n <tt/gtk_main/. Esto
significa que todos los cambios en el contenido del <em/widget/ texto
se manifestar<61>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<61> que pare
momentaneamente de redibujarse a s<> mismo cada vez que haya alg<6C>n
cambio. Podemos descongelarlo cuando hayamos acabado con nuestras
actualizaciones.
Las siguientes dos funciones realizar<61>n la acci<63>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<EFBFBD>n del <em/widget/ de texto mediante dos funciones. El valor
devuelto es TRUE o FALSE en funci<63>n del <20>xito de la operaci<63>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<69> obtener el
car<EFBFBD>cter que se encuentra en la posici<63>n <tt/index/ del <em/widget/
de texto <tt/t/.
Para obtener mayores bloques de texto, podemos utilizar la funci<63>n
<tscreen><verb>
gchar *gtk_editable_get_chars( GtkEditable *editable,
gint start_pos,
gint end_pos );
</verb></tscreen>
Esta es una funci<63>n de la clase padre del <em/widget/ texto. Un valor
de -1 en <tt/end_pos/ significa el final del texto. El <20>ndice del
texto empieza en 0.
La funci<63>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<63>n est<73>ndar, movimiento y selecci<63>n. Pueden
utilizarse mediante combinaciones de las teclas Control y Alt.
Adem<EFBFBD>s, si se mantiene apretada la tecla de Control y se utilizan las
teclas de movimiento, el cursor se mover<65> por palabras en lugar de por
caracteres. Manteniendo apretada la tecla Shift, las teclas de movimiento
har<EFBFBD>n que se extienda la selecci<63>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<61>cter
<item> Ctrl-F Adelantarse un car<61>cter
<item> Alt-B Retrasarse una palabra
<item> Alt-F Adelantarse una palabra
</itemize>
<sect2>Atajos para la edici<63>n
<p>
<itemize>
<item> Ctrl-H Borrar el car<61>cter anterior (Backspace)
<item> Ctrl-D Borrar el car<61>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<63>n
<p>
<itemize>
<item> Ctrl-X Cortar al portapapeles
<item> Ctrl-C Copiar al portapapeles
<item> Ctrl-V Pegar desde el portapapeles
</itemize>
<!-- ----------------------------------------------------------------- -->
<sect1>Un ejemplo de GtkText
<p>
<tscreen><verb>
/* principio del ejemplo text text.c */
/* text.c */
#include <stdio.h>
#include <gtk/gtk.h>
void text_toggle_editable (GtkWidget *checkbutton,
GtkWidget *text)
{
gtk_text_set_editable(GTK_TEXT(text),
GTK_TOGGLE_BUTTON(checkbutton)->active);
}
void text_toggle_word_wrap (GtkWidget *checkbutton,
GtkWidget *text)
{
gtk_text_set_word_wrap(GTK_TEXT(text),
GTK_TOGGLE_BUTTON(checkbutton)->active);
}
void close_application( GtkWidget *widget, gpointer data )
{
gtk_main_quit();
}
int main (int argc, char *argv[])
{
GtkWidget *ventana;
GtkWidget *caja1;
GtkWidget *caja2;
GtkWidget *hbox;
GtkWidget *boton;
GtkWidget *check;
GtkWidget *separator;
GtkWidget *table;
GtkWidget *vscrollbar;
GtkWidget *text;
GdkColormap *cmap;
GdkColor colour;
GdkFont *fixed_font;
FILE *infile;
gtk_init (&amp;argc, &amp;argv);
ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_widget_set_usize (ventana, 600, 500);
gtk_window_set_policy (GTK_WINDOW(ventana), TRUE, TRUE, FALSE);
gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
GTK_SIGNAL_FUNC(close_application),
NULL);
gtk_window_set_title (GTK_WINDOW (ventana), "Text Widget Example");
gtk_container_border_width (GTK_CONTAINER (ventana), 0);
caja1 = gtk_vbox_new (FALSE, 0);
gtk_container_add (GTK_CONTAINER (ventana), caja1);
gtk_widget_show (caja1);
caja2 = gtk_vbox_new (FALSE, 10);
gtk_container_border_width (GTK_CONTAINER (caja2), 10);
gtk_box_pack_start (GTK_BOX (caja1), caja2, TRUE, TRUE, 0);
gtk_widget_show (caja2);
table = gtk_table_new (2, 2, FALSE);
gtk_table_set_row_spacing (GTK_TABLE (table), 0, 2);
gtk_table_set_col_spacing (GTK_TABLE (table), 0, 2);
gtk_box_pack_start (GTK_BOX (caja2), table, TRUE, TRUE, 0);
gtk_widget_show (table);
/* Crear el widget GtkText */
text = gtk_text_new (NULL, NULL);
gtk_text_set_editable (GTK_TEXT (text), TRUE);
gtk_table_attach (GTK_TABLE (table), text, 0, 1, 0, 1,
GTK_EXPAND | GTK_SHRINK | GTK_FILL,
GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
gtk_widget_show (text);
/* A<>adir una barra de desplazamiento vertical al widget GtkText */
vscrollbar = gtk_vscrollbar_new (GTK_TEXT (text)->vadj);
gtk_table_attach (GTK_TABLE (table), vscrollbar, 1, 2, 0, 1,
GTK_FILL, GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
gtk_widget_show (vscrollbar);
/* Obtener el mapa de colores del sistema y conseguir el color rojo */
cmap = gdk_colormap_get_system();
colour.red = 0xffff;
colour.green = 0;
colour.blue = 0;
if (!gdk_color_alloc(cmap, &amp;colour)) {
g_error("couldn't allocate colour");
}
/* Cargar un fuente de tama<6D>o fijo */
fixed_font = gdk_font_load ("-misc-fixed-medium-r-*-*-*-140-*-*-*-*-*-*");
/* Al enviar la se<73>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<6C>n texto coloreado */
gtk_text_insert (GTK_TEXT (text), NULL, &amp;text->style->black, NULL,
"Supports ", -1);
gtk_text_insert (GTK_TEXT (text), NULL, &amp;colour, NULL,
"colored ", -1);
gtk_text_insert (GTK_TEXT (text), NULL, &amp;text->style->black, NULL,
"text and different ", -1);
gtk_text_insert (GTK_TEXT (text), fixed_font, &amp;text->style->black, NULL,
"fonts\n\n", -1);
/* Cargamos el fichero text.c en la ventana de texto */
infile = fopen("text.c", "r");
if (infile) {
char buffer[1024];
int nchars;
while (1)
{
nchars = fread(buffer, 1, 1024, infile);
gtk_text_insert (GTK_TEXT (text), fixed_font, NULL,
NULL, buffer, nchars);
if (nchars < 1024)
break;
}
fclose (infile);
}
/* Descongelamos el widget text, permiti<74>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>
<EFBFBD>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<63>n GTK. Los nombre de las
funciones GTK son muy descriptivos. Una vez haya comprendido como
funcionan las cosas, no le ser<65> dif<69>cil ver como hay que utilizar un
<em/widget/ simplemente mirando su declaraci<63>n de funciones. Con esto,
y unos cu<63>ntos ejemplos del c<>digo de otros, no deber<65>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 <20>l, para que as<61> otros se puedan beneficiar del tiempo que usted
gast<EFBFBD>.
<!-- ----------------------------------------------------------------- -->
<sect1> Calendar
<p>
<!-- ----------------------------------------------------------------- -->
<sect1> CTree
<p>
<!-- ----------------------------------------------------------------- -->
<sect1> Curves
<p>
<!-- ----------------------------------------------------------------- -->
<sect1> Drawing Area
<p>
<!-- ----------------------------------------------------------------- -->
<sect1> Font Selection Dialog
<p>
<!-- ----------------------------------------------------------------- -->
<sect1> Gamma Curve
<p>
<!-- ----------------------------------------------------------------- -->
<sect1> Image
<p>
<!-- ----------------------------------------------------------------- -->
<sect1> Packer
<p>
<!-- ----------------------------------------------------------------- -->
<sect1> Plugs and Sockets
<p>
<!-- ----------------------------------------------------------------- -->
<sect1> Preview
<p>
<!--
(This may need to be rewritten to follow the style of the rest of the tutorial)
<tscreen><verb>
Previews serve a number of purposes in GIMP/GTK. The most important one is
this. High quality images may take up to tens of megabytes of memory - easy!
Any operation on an image that big is bound to take a long time. If it takes
you 5-10 trial-and-errors (i.e. 10-20 steps, since you have to revert after
you make an error) to choose the desired modification, it make take you
literally hours to make the right one - if you don't run out of memory
first. People who have spent hours in color darkrooms know the feeling.
Previews to the rescue!
But the annoyance of the delay is not the only issue. Oftentimes it is
helpful to compare the Before and After versions side-by-side or at least
back-to-back. If you're working with big images and 10 second delays,
obtaining the Before and After impressions is, to say the least, difficult.
For 30M images (4"x6", 600dpi, 24 bit) the side-by-side comparison is right
out for most people, while back-to-back is more like back-to-1001, 1002,
..., 1010-back! Previews to the rescue!
But there's more. Previews allow for side-by-side pre-previews. In other
words, you write a plug-in (e.g. the filterpack simulation) which would have
a number of here's-what-it-would-look-like-if-you-were-to-do-this previews.
An approach like this acts as a sort of a preview palette and is very
effective for subtle changes. Let's go previews!
There's more. For certain plug-ins real-time image-specific human
intervention maybe necessary. In the SuperNova plug-in, for example, the
user is asked to enter the coordinates of the center of the future
supernova. The easiest way to do this, really, is to present the user with a
preview and ask him to interactively select the spot. Let's go previews!
Finally, a couple of misc uses. One can use previews even when not working
with big images. For example, they are useful when rendering complicated
patterns. (Just check out the venerable Diffraction plug-in + many other
ones!) As another example, take a look at the colormap rotation plug-in
(work in progress). You can also use previews for little logos inside you
plug-ins and even for an image of yourself, The Author. Let's go previews!
When Not to Use Previews
Don't use previews for graphs, drawing etc. GDK is much faster for that. Use
previews only for rendered images!
Let's go previews!
You can stick a preview into just about anything. In a vbox, an hbox, a
table, a button, etc. But they look their best in tight frames around them.
Previews by themselves do not have borders and look flat without them. (Of
course, if the flat look is what you want...) Tight frames provide the
necessary borders.
[Image][Image]
Previews in many ways are like any other widgets in GTK (whatever that
means) except they possess an additional feature: they need to be filled with
some sort of an image! First, we will deal exclusively with the GTK aspect
of previews and then we'll discuss how to fill them.
GtkWidget *preview!
Without any ado:
/* Create a preview widget,
set its size, an show it */
GtkWidget *preview;
preview=gtk_preview_new(GTK_PREVIEW_COLOR)
/*Other option:
GTK_PREVIEW_GRAYSCALE);*/
gtk_preview_size (GTK_PREVIEW (preview), WIDTH, HEIGHT);
gtk_widget_show(preview);
my_preview_rendering_function(preview);
Oh yeah, like I said, previews look good inside frames, so how about:
GtkWidget *create_a_preview(int Width,
int Height,
int Colorfulness)
{
GtkWidget *preview;
GtkWidget *frame;
frame = gtk_frame_new(NULL);
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
gtk_container_set_border_width (GTK_CONTAINER(frame),0);
gtk_widget_show(frame);
preview=gtk_preview_new (Colorfulness?GTK_PREVIEW_COLOR
:GTK_PREVIEW_GRAYSCALE);
gtk_preview_size (GTK_PREVIEW (preview), Width, Height);
gtk_container_add(GTK_CONTAINER(frame),preview);
gtk_widget_show(preview);
my_preview_rendering_function(preview);
return frame;
}
That's my basic preview. This routine returns the "parent" frame so you can
place it somewhere else in your interface. Of course, you can pass the
parent frame to this routine as a parameter. In many situations, however,
the contents of the preview are changed continually by your application. In
this case you may want to pass a pointer to the preview to a
"create_a_preview()" and thus have control of it later.
One more important note that may one day save you a lot of time. Sometimes
it is desirable to label you preview. For example, you may label the preview
containing the original image as "Original" and the one containing the
modified image as "Less Original". It might occur to you to pack the
preview along with the appropriate label into a vbox. The unexpected caveat
is that if the label is wider than the preview (which may happen for a
variety of reasons unforseeable to you, from the dynamic decision on the
size of the preview to the size of the font) the frame expands and no longer
fits tightly over the preview. The same problem can probably arise in other
situations as well.
[Image]
The solution is to place the preview and the label into a 2x1 table and by
attaching them with the following parameters (this is one possible variations
of course. The key is no GTK_FILL in the second attachment):
gtk_table_attach(GTK_TABLE(table),label,0,1,0,1,
0,
GTK_EXPAND|GTK_FILL,
0,0);
gtk_table_attach(GTK_TABLE(table),frame,0,1,1,2,
GTK_EXPAND,
GTK_EXPAND,
0,0);
And here's the result:
[Image]
Misc
Making a preview clickable is achieved most easily by placing it in a
boton. It also adds a nice border around the preview and you may not even
need to place it in a frame. See the Filter Pack Simulation plug-in for an
example.
This is pretty much it as far as GTK is concerned.
Filling In a Preview
In order to familiarize ourselves with the basics of filling in previews,
let's create the following pattern (contrived by trial and error):
[Image]
void
my_preview_rendering_function(GtkWidget *preview)
{
#define SIZE 100
#define HALF (SIZE/2)
guchar *row=(guchar *) malloc(3*SIZE); /* 3 bits per dot */
gint i, j; /* Coordinates */
double r, alpha, x, y;
if (preview==NULL) return; /* I usually add this when I want */
/* to avoid silly crashes. You */
/* should probably make sure that */
/* everything has been nicely */
/* initialized! */
for (j=0; j < ABS(cos(2*alpha)) ) { /* Are we inside the shape? */
/* glib.h contains ABS(x). */
row[i*3+0] = sqrt(1-r)*255; /* Define Red */
row[i*3+1] = 128; /* Define Green */
row[i*3+2] = 224; /* Define Blue */
} /* "+0" is for alignment! */
else {
row[i*3+0] = r*255;
row[i*3+1] = ABS(sin((float)i/SIZE*2*PI))*255;
row[i*3+2] = ABS(sin((float)j/SIZE*2*PI))*255;
}
}
gtk_preview_draw_row( GTK_PREVIEW(preview),row,0,j,SIZE);
/* Insert "row" into "preview" starting at the point with */
/* coordinates (0,j) first column, j_th row extending SIZE */
/* pixels to the right */
}
free(row); /* save some space */
gtk_widget_draw(preview,NULL); /* what does this do? */
gdk_flush(); /* or this? */
}
Non-GIMP users can have probably seen enough to do a lot of things already.
For the GIMP users I have a few pointers to add.
Image Preview
It is probably wise to keep a reduced version of the image around with just
enough pixels to fill the preview. This is done by selecting every n'th
pixel where n is the ratio of the size of the image to the size of the
preview. All further operations (including filling in the previews) are then
performed on the reduced number of pixels only. The following is my
implementation of reducing the image. (Keep in mind that I've had only basic
C!)
(UNTESTED CODE ALERT!!!)
typedef struct {
gint width;
gint height;
gint bbp;
guchar *rgb;
guchar *mask;
} ReducedImage;
enum {
SELECTION_ONLY,
SELECTION_IN_CONTEXT,
ENTIRE_IMAGE
};
ReducedImage *Reduce_The_Image(GDrawable *drawable,
GDrawable *mask,
gint LongerSize,
gint Selection)
{
/* This function reduced the image down to the the selected preview size */
/* The preview size is determine by LongerSize, i.e. the greater of the */
/* two dimensions. Works for RGB images only! */
gint RH, RW; /* Reduced height and reduced width */
gint width, height; /* Width and Height of the area being reduced */
gint bytes=drawable->bpp;
ReducedImage *temp=(ReducedImage *)malloc(sizeof(ReducedImage));
guchar *tempRGB, *src_row, *tempmask, *src_mask_row,R,G,B;
gint i, j, whichcol, whichrow, x1, x2, y1, y2;
GPixelRgn srcPR, srcMask;
gint NoSelectionMade=TRUE; /* Assume that we're dealing with the entire */
/* image. */
gimp_drawable_mask_bounds (drawable->id, &amp;x1, &amp;y1, &amp;x2, &amp;y2);
width = x2-x1;
height = y2-y1;
/* If there's a SELECTION, we got its bounds!)
if (width != drawable->width &amp;&amp; height != drawable->height)
NoSelectionMade=FALSE;
/* Become aware of whether the user has made an active selection */
/* This will become important later, when creating a reduced mask. */
/* If we want to preview the entire image, overrule the above! */
/* Of course, if no selection has been made, this does nothing! */
if (Selection==ENTIRE_IMAGE) {
x1=0;
x2=drawable->width;
y1=0;
y2=drawable->height;
}
/* If we want to preview a selection with some surrounding area we */
/* have to expand it a little bit. Consider it a bit of a riddle. */
if (Selection==SELECTION_IN_CONTEXT) {
x1=MAX(0, x1-width/2.0);
x2=MIN(drawable->width, x2+width/2.0);
y1=MAX(0, y1-height/2.0);
y2=MIN(drawable->height, y2+height/2.0);
}
/* How we can determine the width and the height of the area being */
/* reduced. */
width = x2-x1;
height = y2-y1;
/* The lines below determine which dimension is to be the longer */
/* side. The idea borrowed from the supernova plug-in. I suspect I */
/* could've thought of it myself, but the truth must be told. */
/* Plagiarism stinks! */
if (width>height) {
RW=LongerSize;
RH=(float) height * (float) LongerSize/ (float) width;
}
else {
RH=LongerSize;
RW=(float)width * (float) LongerSize/ (float) height;
}
/* The entire image is stretched into a string! */
tempRGB = (guchar *) malloc(RW*RH*bytes);
tempmask = (guchar *) malloc(RW*RH);
gimp_pixel_rgn_init (&amp;srcPR, drawable, x1, y1, width, height,
FALSE, FALSE);
gimp_pixel_rgn_init (&amp;srcMask, mask, x1, y1, width, height,
FALSE, FALSE);
/* Grab enough to save a row of image and a row of mask. */
src_row = (guchar *) malloc (width*bytes);
src_mask_row = (guchar *) malloc (width);
for (i=0; i < RH; i++) {
whichrow=(float)i*(float)height/(float)RH;
gimp_pixel_rgn_get_row (&amp;srcPR, src_row, x1, y1+whichrow, width);
gimp_pixel_rgn_get_row (&amp;srcMask, src_mask_row, x1, y1+whichrow, width);
for (j=0; j < RW; j++) {
whichcol=(float)j*(float)width/(float)RW;
/* No selection made = each point is completely selected! */
if (NoSelectionMade)
tempmask[i*RW+j]=255;
else
tempmask[i*RW+j]=src_mask_row[whichcol];
/* Add the row to the one long string which now contains the image! */
tempRGB[i*RW*bytes+j*bytes+0]=src_row[whichcol*bytes+0];
tempRGB[i*RW*bytes+j*bytes+1]=src_row[whichcol*bytes+1];
tempRGB[i*RW*bytes+j*bytes+2]=src_row[whichcol*bytes+2];
/* Hold on to the alpha as well */
if (bytes==4)
tempRGB[i*RW*bytes+j*bytes+3]=src_row[whichcol*bytes+3];
}
}
temp->bpp=bytes;
temp->width=RW;
temp->height=RH;
temp->rgb=tempRGB;
temp->mask=tempmask;
return temp;
}
The following is a preview function which used the same ReducedImage type!
Note that it uses fakes transparency (if one is present by means of
fake_transparency which is defined as follows:
gint fake_transparency(gint i, gint j)
{
if ( ((i%20)- 10) * ((j%20)- 10)>0 )
return 64;
else
return 196;
}
Now here's the preview function:
void
my_preview_render_function(GtkWidget *preview,
gint changewhat,
gint changewhich)
{
gint Inten, bytes=drawable->bpp;
gint i, j, k;
float partial;
gint RW=reduced->width;
gint RH=reduced->height;
guchar *row=malloc(bytes*RW);;
for (i=0; i < RH; i++) {
for (j=0; j < RW; j++) {
row[j*3+0] = reduced->rgb[i*RW*bytes + j*bytes + 0];
row[j*3+1] = reduced->rgb[i*RW*bytes + j*bytes + 1];
row[j*3+2] = reduced->rgb[i*RW*bytes + j*bytes + 2];
if (bytes==4)
for (k=0; k<3; k++) {
float transp=reduced->rgb[i*RW*bytes+j*bytes+3]/255.0;
row[3*j+k]=transp*a[3*j+k]+(1-transp)*fake_transparency(i,j);
}
}
gtk_preview_draw_row( GTK_PREVIEW(preview),row,0,i,RW);
}
free(a);
gtk_widget_draw(preview,NULL);
gdk_flush();
}
Applicable Routines
guint gtk_preview_get_type (void);
/* No idea */
void gtk_preview_uninit (void);
/* No idea */
GtkWidget* gtk_preview_new (GtkPreviewType type);
/* Described above */
void gtk_preview_size (GtkPreview *preview,
gint width,
gint height);
/* Allows you to resize an existing preview. */
/* Apparently there's a bug in GTK which makes */
/* this process messy. A way to clean up a mess */
/* is to manually resize the window containing */
/* the preview after resizing the preview. */
void gtk_preview_put (GtkPreview *preview,
GdkWindow *ventana,
GdkGC *gc,
gint srcx,
gint srcy,
gint destx,
gint desty,
gint width,
gint height);
/* No idea */
void gtk_preview_put_row (GtkPreview *preview,
guchar *src,
guchar *dest,
gint x,
gint y,
gint w);
/* No idea */
void gtk_preview_draw_row (GtkPreview *preview,
guchar *data,
gint x,
gint y,
gint w);
/* Described in the text */
void gtk_preview_set_expand (GtkPreview *preview,
gint expand);
/* No idea */
/* No clue for any of the below but */
/* should be standard for most widgets */
void gtk_preview_set_gamma (double gamma);
void gtk_preview_set_color_cube (guint nred_shades,
guint ngreen_shades,
guint nblue_shades,
guint ngray_shades);
void gtk_preview_set_install_cmap (gint install_cmap);
void gtk_preview_set_reserved (gint nreserved);
GdkVisual* gtk_preview_get_visual (void);
GdkColormap* gtk_preview_get_cmap (void);
GtkPreviewInfo* gtk_preview_get_info (void);
That's all, folks!
</verb></tscreen>
-->
<!-- ***************************************************************** -->
<sect>Estableciendo los atributos de un <em/widget/<label id="sec_setting_widget_attributes">
<!-- ***************************************************************** -->
<p>
En este cap<61>tulo se describen las funciones utilizadas para manejar los
<em/widgets/. Pueden utilizarse para establecer el estilo, relleno,
tama<EFBFBD>o, etc...
(Puede que deba hacer una secci<63>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<73> preguntando como hacer que GTK haga algo <20>til
cuando se encuentra en <tt/gtk_main/. Bien, tiene varias
opciones. Utilizando las rutinas siguientes puede crear una funci<63>n
a la que se llamar<61> 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<62> entre dos
llamadas a su funci<63>n. El segundo argumento es la funci<63>n a la que
desea llamar, y el tercero, los datos que le pasar<61> a <20>sta funci<63>n.
El valor devuelto es un <20>identificador<6F> (un valor entero) que puede
utilizar para detener las llamadas haciendo:
<tscreen><verb>
void gtk_timeout_remove( gint tag );
</verb></tscreen>
Tambi<EFBFBD>n puede hacer que cesen las llamadas a la funci<63>n haciendo que
la misma devuelva cero o FALSE. Obviamente esto significa que si
quiere que se continue llamando a su funci<63>n, deber<65> devolver un valor
distinto de cero, es decir TRUE.
La declaraci<63>n de su funci<63>n deber<65>a ser algo como:
<tscreen><verb>
gint timeout_callback( gpointer data );
</verb></tscreen>
<!-- ----------------------------------------------------------------- -->
<sect1>Monitorizando la ES
<p>
Otra caracter<65>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
<EFBFBD>til para las aplicaciones de red. La funci<63>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<63>n cuando hay datos listos para
leerse del fichero.
<item>GDK_INPUT_WRITE - Llama a su funci<63>n cuando el descriptor del
fichero est<73> listo para la escritura.
</itemize>
Tal y como se habr<62> imaginado, el tercer argumento es la funci<63>n a la
que desea que se llame cuando se den las condiciones anteriores, y el
cuarto son los datos que se le pasar<61>n a <20>sta funci<63>n.
El valor devuelto es un identificador que puede utilizarse para que GDK
pare de vigilar ese fichero, utilizando la funci<63>n
<tscreen><verb>
void gdk_input_remove( gint tag );
</verb></tscreen>
La funci<63>n a la que quiere que se llame deber<65> declararse as<61>:
<tscreen><verb>
void input_callback( gpointer data,
gint source,
GdkInputCondition condition );
</verb></tscreen>
Donde <tt/source/ y <tt/condition/ est<73>n especificados m<>s arriba.
<!-- ----------------------------------------------------------------- -->
<sect1>Funciones ociosas
<p>
<!-- Need to check on idle priorities - TRG -->
<EFBFBD>Qu<EFBFBD> le parece si tuviese una funci<63>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<63>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<63>n a la que se apunta
mediante el primer argumento de <tt/gtk_idle_add/ ser<65> a la que se
llame cuando llegue el momento. Como antes, si devuelve FALSE har<61> que
cese de llamarse a la funci<63>n.
<!-- ***************************************************************** -->
<sect>Manejo avanzado de eventos y se<73>ales<label id="sec_Adv_Events_and_Signals">
<!-- ***************************************************************** -->
<!-- ----------------------------------------------------------------- -->
<sect1>Funciones se<73>al
<!-- ----------------------------------------------------------------- -->
<sect2>Conectando y desconectando los manejadores de se<73>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<73>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<73>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<73>n y propagaci<63>n de se<73>ales
<p>
La emisi<73>n de se<73>ales es el proceso mediante el cual GTK+ ejecuta
todos los manejadores de un objeto y una se<73>al en especial.
Primero, observe que el valor devuelto por la emisi<73>n de una
se<EFBFBD>al es el mismo que el valor devuelto por el <em><3E>ltimo</em>
manipulador ejecutado. Ya que las se<73>ales de los eventos son todas
del tipo GTK_RUN_LAST, el manejador por defecto (proporcionado por
GTK+) ser<65> 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<72> el evento.
<item>Emite la se<73>al gen<65>rica <tt/event/. Si esta se<73>al devuelve un
valor TRUE, detiene todo el proceso.
<item>En caso contrario, emite una se<73>al especifica,
<EFBFBD>button_press_event<EFBFBD> en nuestro caso. Si <20>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<6C>n manejador de se<73>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<64> ning<6E>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<73>al se propaga, y no s<>lo por el hecho de emitirse.
</itemize>
<!-- ***************************************************************** -->
<sect>Manejando selecciones
<!-- ***************************************************************** -->
<!-- ----------------------------------------------------------------- -->
<sect1> Contenido
<p>
Un tipo de comunicaci<63>n entre procesos que se puede utilizar con GTK
son las <em/selecciones/. Una selecci<63>n identifica un conjunto de
datos, por ejemplo, un trozo de texto, seleccionado por el usuario de
alguna manera, por ejemplo, cogi<67>ndolo con el rat<61>n. S<>lo una
aplicaci<EFBFBD>n en un <em/display/ (la <em>propietaria</em>) puede tener
una selecci<63>n en particular en un momento dado, por lo que cuando una
aplicaci<EFBFBD>n pide una selecci<63>n, el propietario previo debe indicar al
usuario que la selecci<63>n ya no es v<>lida. Otras aplicaciones pueden
pedir el contenido de la selecci<63>n de diferentes formas, llamadas
<em/objetivos/. Puede haber cualquier n<>mero de selecciones, pero la
mayor<EFBFBD>a de las aplicacion X s<>lo pueden manejar una, la <em/selecci<63>n
primaria/.
En muchos casos, no es necesario para una aplicaci<63>n GTK tratar por
s<EFBFBD> misma con las selecciones. Los <em/widgets/ est<73>ndar, como el
<em/widget/ Entry, ya tienen la posibilidad de crear la selecci<63>n
cuando sea necesario (p.e., cuando el usuario pase el rat<61>n sobre el
texto manteniendo el bot<6F>n derecho del rat<61>n pulsado), y de recoger
los contenidos de la selecci<63>n propiedad de otro <em/widget/, o de
otra aplicaci<63>n (p.e., cuando el usuario pulsa el segundo bot<6F>n del
rat<EFBFBD>n). Sin embargo, pueden haber casos en los que quiera darle a
otros <em/widgets/ la posibilidad de proporcionar la selecci<63>n, o
puede que quiera recuperar objetivos que no est<73>n admitidos por
defecto.
Un concepto fundamental que es necesario para comprender el manejo de
la selecci<63>n es el de <em><3E>tomo</em>. Un <20>tomo es un entero que
identifica de una manera un<75>voca una cadena (en un cierto
<em/display/). Ciertos <20>tomos est<73>n predefinidos por el servidor X, y
en algunos casos hay constantes en <tt>gtk.h</tt> que corresponden a
estos <20>tomos. Por ejemplo la constante <tt>GDK_PRIMARY_SELECTION</tt>
corresponde a la cadena <20>PRIMARY<52>. En otros casos, deber<65>a utilizar
las funciones <tt>gdk_atom_intern()</tt>, para obtener el <20>tomo
correspondiente a una cadena, y <tt>gdk_atom_name()</tt>, para obtener
el nombre de un <20>tomo. Ambas, selecciones y objetivos, est<73>n
identificados por <20>tomos.
<!-- ----------------------------------------------------------------- -->
<sect1> Recuperando la selecci<63>n
<p>
Recuperar la selecci<63>n es un proceso as<61>ncrono. Para comenzar el
proceso, deber<65> llamar a:
<tscreen><verb>
gint gtk_selection_convert( GtkWidget *widget,
GdkAtom selection,
GdkAtom target,
guint32 time );
</verb</tscreen>
Este proceso <em/convierte/ la selecci<63>n en la forma especificada por
<tt/target/. Si es posible, el campo <tt/time/ debe ser el tiempo
desde que el evento lanz<6E> la selecci<63>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<73> disponible (por ejemplo, si se empez<65> la
conversi<EFBFBD>n por una se<73>al de <20>pulsaci<63>n<EFBFBD>), entonces puede utilizar la
constante <tt>GDK_CURRENT_TIME</tt>.
Cuando el propietario de la selecci<63>n responda a la petici<63>n, se
enviar<EFBFBD> una se<73>al <20>selection_received<65> a su aplicaci<63>n. El manejador
de esta se<73>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<64> en su
llamada a <tt>gtk_selection_convert()</tt>. <tt>type</tt> es un <20>tomo
que identifica el tipo de datos devueltos por el propietario de la
selecci<EFBFBD>n. Algunos valores posibles son <20>STRING<4E>, un cadena de
caracteres latin-1, <20>ATOM<4F>, una serie de <20>tomos, <20>INTEGER<45>, 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<63>n. Esto podr<64>a ocurrir si no hay ninguna
aplicaci<EFBFBD>n que sea la propietaria de la selecci<63>n, o si pide un
objetivo que la aplicaci<63>n no admite. Actualmente se garantiza que el
b<EFBFBD>fer tendr<64> un byte m<>s que <tt/length/; el byte extra siempre ser<65>
cero, por lo que no es necesario hacer una copia de las cadenas s<>lo
para a<>adirles un car<61>cter nulo al final.
En el siguiente ejemplo, recuperamos el objetivo especial <20>TARGETS<54>,
que es una lista de todos los objetivos en los que se puede convertir
la selecci<63>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<73>al invocado cuando el usuario pulsa en el bot<6F>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<63>n primaria */
gtk_selection_convert (widget, GDK_SELECTION_PRIMARY, targets_atom,
GDK_CURRENT_TIME);
}
/* Manipulador de se<73>al llamado cuando el propietario de la se<73>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<63>n de los
* datos */
if (selection_data->length < 0)
{
g_print ("Selection retrieval failed\n");
return;
}
/* Asegurarse de que obtenemos los datos de la forma esperada */
if (selection_data->type != GDK_SELECTION_TYPE_ATOM)
{
g_print ("Selection \"TARGETS\" was not returned as atoms!\n");
return;
}
/* Imprimir los atoms que hemos recibido */
atoms = (GdkAtom *)selection_data->data;
item_list = NULL;
for (i=0; i<selection_data->length/sizeof(GdkAtom); i++)
{
char *name;
name = gdk_atom_name (atoms[i]);
if (name != NULL)
g_print ("%s\n",name);
else
g_print ("(bad atom)\n");
}
return;
}
int
main (int argc, char *argv[])
{
GtkWidget *ventana;
GtkWidget *boton;
gtk_init (&amp;argc, &amp;argv);
/* Crear la ventana superior */
ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (ventana), "Event Box");
gtk_container_border_width (GTK_CONTAINER (ventana), 10);
gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
GTK_SIGNAL_FUNC (gtk_exit), NULL);
/* Crear un bot<6F>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<63>n
<p>
Proporcionar la selecci<63>n es un poco m<>s complicado. Debe registrar
los manejadores a los que se llamar<61>n cuando se le pida la
selecci<EFBFBD>n. Por cada par selecci<63>n/objetivo que quiera manejar, deber<65>
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<61> cuando se elimine el manejador de la se<73>al. Esto es <20>til,
por ejemplo, para los lenguajes interpretados que necesitan mantener
una memoria de las referencias a <tt/data/.
La funci<63>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<65> 8 - es decir un car<61>cter - o 32 - es decir un
entero.) Esto se hace llamando a la funci<63>n:
<tscreen><verb>
void gtk_selection_data_set( GtkSelectionData *selection_data,
GdkAtom type,
gint format,
guchar *data,
gint length );
</verb></tscreen>
Esta funci<63>n tiene la responsabilidad de hacer una copia de los datos
para que no tenga que preocuparse de ir guard<72>ndolos. (No deber<65>a
rellenar los campos de la estructura <tt/GtkSelectionData/ a mano.)
Cuando haga falta, puede pedir el propietario de la selecci<63>n llamando
a:
<tscreen><verb>
gint gtk_selection_owner_set( GtkWidget *widget,
GdkAtom selection,
guint32 time );
</verb></tscreen>
Si otra aplicaci<63>n pide el propietario de la selecci<63>n, recibira un
<EFBFBD>selection_clear_event<EFBFBD>.
Como ejemplo de proporciar la selecci<63>n, el programa siguiente le a<>ade
la posibilidad de selecci<63>n a un bot<6F>n de comprobaci<63>n. Cuando se presione
el bot<6F>n de comprobaci<63>n, el programa pedir<69> la selecci<63>n primaria. El
<EFBFBD>nico objetivo que admite es un objetivo <20>STRING<4E> (aparte de ciertos
objetivos como "TARGETS", proporcionados por GTK). Cuando se pida este
objetivo, se devolver<65> una representaci<63>n del tiempo.
<tscreen><verb>
/* principio del ejemplo selection setselection.c */
#include <gtk/gtk.h>
#include <time.h>
/* Funci<63>n de llamada para cuando el usuario cambia la selecci<63>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<63>n ha fallado, ponemos el bot<6F>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<63>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<63>n pide la selecci<63>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<63>n. */
void
selection_handle (GtkWidget *widget,
GtkSelectionData *selection_data,
gpointer data)
{
gchar *timestr;
time_t current_time;
current_time = time (NULL);
timestr = asctime (localtime(&amp;current_time));
/* Cuando devolvemos una cadena, no debe terminar en NULL. La
* funci<63>n lo har<61> por nosotros */
gtk_selection_data_set (selection_data, GDK_SELECTION_TYPE_STRING,
8, timestr, strlen(timestr));
}
int
main (int argc, char *argv[])
{
GtkWidget *ventana;
GtkWidget *selection_button;
static int have_selection = FALSE;
gtk_init (&amp;argc, &amp;argv);
/* Crear la ventana superior */
ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (ventana), "Event Box");
gtk_container_border_width (GTK_CONTAINER (ventana), 10);
gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
GTK_SIGNAL_FUNC (gtk_exit), NULL);
/* Crear un bot<6F>n de selecci<63>n para que actue como la selecci<63>n */
selection_button = gtk_toggle_button_new_with_label ("Claim Selection");
gtk_container_add (GTK_CONTAINER (ventana), selection_button);
gtk_widget_show (selection_button);
gtk_signal_connect (GTK_OBJECT(selection_button), "toggled",
GTK_SIGNAL_FUNC (selection_toggled), &amp;have_selection);
gtk_signal_connect (GTK_OBJECT(selection_button), "selection_clear_event",
GTK_SIGNAL_FUNC (selection_clear), &amp;have_selection);
gtk_selection_add_handler (selection_button, GDK_SELECTION_PRIMARY,
GDK_SELECTION_TYPE_STRING,
selection_handle, NULL);
gtk_widget_show (selection_button);
gtk_widget_show (ventana);
gtk_main ();
return 0;
}
/* fin del ejemplo */
</verb></tscreen>
<!-- ***************************************************************** -->
<sect>Glib<label id="sec_glib">
<!-- ***************************************************************** -->
<p>
Glib proporciona muchas definiciones y funciones <20>tiles disponibles
para su utilizaci<63>n cuando se crean aplicaciones GDK y GTK. Har<61> una
lista con todas ellas incluyendo una peque<75>a explicaci<63>n. Muchas no
son m<>s que duplicados de funciones est<73>ndar de libc por lo que no
entrar<EFBFBD> en detalle en la explicaci<63>n de las mismas. Esta secci<63>n est<73>
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<73>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<62>n, los siguientes <tt/typedefs/. Cuando no se especifica el
tipo que deber<65>a aparecer a la izquierda significa que el mismo se
establecer<EFBFBD> din<69>micamente en funci<63>n de la arquitectura. <20>Recuerde
evitar los calculos relativos al tama<6D>o de un puntero si quiere que
su aplicaci<63>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<64> que sabe lo que son las listas
enlazadas, ya que explicarlas va m<>s all<6C> 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<69>nticas a las
de m<>s arriba. Aqu<71> 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<63>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<73> utilizando, pero
tiene que a<>adir <tt/#define MEM_PROFILE/ en lo alto de
<tt>glib/gmem.c</tt> y tendr<64> que hacer un make y un make install.
<tscreen><verb>
void g_mem_check( gpointer mem );
</verb></tscreen>
Comprueba que una direcci<63>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<70>ado de funciones para manejar cadenas de texto. Parecen muy
interesantes, y probablemente sean mejores en muchos aspectos que las
funciones est<73>ndar de C, pero necesitan documentaci<63>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<63>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<63>n para todos los mensages de error. Es
mucho m<>s bonita, y m<>s portable que <tt/perror()/ y dem<65>s funciones
cl<EFBFBD>sicas. La salida es normalmente de la forma:
<tscreen><verb>
nombre del programa:funci<63>n que fall<6C>:fichero o descripci<63>n adicional:strerror
</verb></tscreen>
Aqu<EFBFBD> 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 <20>ltima funci<63>n:
<tscreen><verb>
gchar *g_strsignal( gint signum );
</verb></tscreen>
Imprime el nombre de la se<73>al del sistema Unix que corresponde con el
n<EFBFBD>mero <tt/signum/. <20>til para las funciones gen<65>ricas de manejo de se<73>al.
Todo lo anterior est<73> m<>s o menos robado de <tt/glib.h/. Si alguien
quiere documentar una funci<63>n, <20>s<EFBFBD>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<EFBFBD>n, y es utilizando los ficheros <tt/rc/. Pueden ser
utilizados para poner los colores de cualquier <em/widget/, y tambi<62>n
pueden utilizarse para poner im<69>genes como fondos de algunos <em/widgets/.
<!-- ----------------------------------------------------------------- -->
<sect1>Funciones para los ficheros <tt/rc/
<p>
Cuando empiece su aplicaci<63>n, deber<65>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<61> que GTK analice
este fichero, y utilice el estilo para los <em/widgets/ que se definan
ah<EFBFBD>.
Si desea tener un conjunto especial de <em/widgets/ con un estilo
diferente de los otros, o realizar cualquier otra divisi<73>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<EFBFBD>ndole su nuevo <em/widget/ como primer argumento, y el nombre que
desea darle como el segundo. Mediante este nombre podr<64> cambiar los
atributos de ese <em/widget/.
Si hacemos algo as<61>:
<tscreen><verb>
boton = gtk_button_new_with_label ("Bot<6F>n especial");
gtk_widget_set_name (boton, "bot<6F>n especial");
</verb></tscreen>
El bot<6F>n tendr<64> el nombre <20>bot<6F>n especial<61> y podr<64>a hacersele
referencia en el fichero <tt/rc/ como <20>bot<6F>n especial.GtkButton<6F>.
[<--- <20>Verificadme! ]
El fichero de ejemplo <tt/rc/ que mostramos a continuaci<63>n, establece las
propiedades de la ventana principal, y deja que todos los hijos de la
ventana principal hereden el estilo descrito por <20>main button<6F>. El
c<EFBFBD>digo utilizado en la aplicaci<63>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<EFBFBD> hace que todos los <em/widgets/ GtkButton de la <20>main window<6F>
(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<EFBFBD>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. <20>ste es el fichero <tt/testgtkrc/ de la distribuci<63>n GTK, pero he
a<EFBFBD>adido unos cuantos comentarios y alguna cosilla. Puede que quiera
incluir esta explicaci<63>n en su aplicaci<63>n para permitir al usuario
personalizar su aplicaci<63>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<69> de fondo al
<em/widget/ (como mosaico).
<item>font - Establece el tipo de letra que se utilizar<61> con el
<em/widget/.
</itemize>
Adem<EFBFBD>s de esto, hay varios estados en el que puede estar un
<em/widget/, y puede especificar diferentes colores, im<69>genes y tipos
de letra para cada estado. Estos estados son:
<itemize>
<item>NORMAL - El estado normal de un <em/widget/, sin el rat<61>n sobre
<EFBFBD>l, y no siendo presionado, etc...
<item>PRELIGHT - Cuando el rat<61>n est<73> sobre este <em/widget/ se
utilizar<EFBFBD>n los colores definidos para este estado.
<item>ACTIVE - Cuando se presiona o se pulsa sobre el <em/widget/,
estar<EFBFBD> activo, y los atributos asignados por est<73> etiqueta ser<65>n
utilizados.
<item>INSENSITIVE - Cuando un <em/widget/ es insensible, y no se puede
activar, tomar<61> estos atributos.
<item>SELECTED - Cuando se seleccione un objeto, tomar<61> estos atributos.
</itemize>
Cuando se utilizan las directivas <20>fg<66> y <20>bg<62> para poner los colores de
los <em/widgets/, se utilizar<61> 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<65>n un 0, por lo que "1" no funcionar<61>,
debe ser "1.0". Un "0" est<73> bien ya que es lo mismo si no se
reconoce. Los valores no reconocidos se pondr<64>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
<EFBFBD>:<3A>. Estos caminos se utilizar<61>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 <20>nico dif<69>cil es saber la cadena del tipo de letra a
elegir. Utilizar <tt/xfontsel/ o un programa similar deber<65>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<71>a de clases.
La directiva <tt/widget/ hace que un conjunto espec<65>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<63>n utilizando una
llamada a <tt/gtk_widget_set_name()/. Esto le permitir<69> especificar
los atributos de un <em/widget/ uno a uno, en vez de establecer los
atributos de toda una clase <em/widget/. Deber<65> 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<61> los atributos de su padre en la aplicaci<63>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 <20>button<6F>, y crea un nuevo estilo
<EFBFBD>main_button<EFBFBD> cambiando simplemente el tipo de letra y cambiando el
color de fondo cuando el <em/widget/ est<73> en estado <tt/PRELIGHT/.
Por supuesto, muchos de estos atributos no se aplican a todos los
<em/widgets/. Realmente es una cuesti<74>n de sentido com<6F>n. Se utilizar<61>
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<73>n general
<p>
Aunque la distribuci<63>n de GTK viene con muchos tipos de <em/widgets/
que deber<65>a cubrir todas la mayor<6F>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<65>rese primero de que no hay nadie que ya haya
hecho otro parecido. As<41> evitar<61> la duplicaci<63>n de esfuerzo y
mantendr<EFBFBD> el n<>mero de <em/widgets/ GTK en su valor m<>nimo, lo que
ayudar<EFBFBD> a que el c<>digo y la interfaz de las diferentes aplicaciones
sea consistente. Por otra parte, cuando haya acabado su <em/widget/,
an<EFBFBD>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<73>n disponibles
en el mismo lugar en el que consigui<75> este tutorial, o en:
<htmlurl url="http://www.gtk.org/~otaylor/gtk/tutorial/"
name="http://www.gtk.org/~otaylor/gtk/tutorial/">
<!-- ----------------------------------------------------------------- -->
<sect1> La anatom<6F>a de un <em/widget/
<p>
Para crear un nuevo <em/widget/, es importante conocer como funcionan
los objetos de GTK. Esta secci<63>n es s<>lo un breve resumen. Ver la
documentaci<EFBFBD>n a la que se hace referencia para obtener m<>s detalles.
Los widgets GTK est<73>n implementados siguiendo una orientaci<63>n a
objetos. Sin embargo, est<73>n implementados en C est<73>ndar. De esta forma
se mejora enormemente la portabilidad y la estabilidad con respecto a
la actual generaci<63>n de compiladores C++; sin embargo, con todo esto
no queremos decir que el creador de <em/widgets/ tenga que prestar
atenci<EFBFBD>n a ninguno de los detalles de implementaci<63>n. La informaci<63>n
que es com<6F>n a todos los <em/widgets/ de una clase de <em/widgets/
(p.e., a todos los <em/widgets/ bot<6F>n) se almacena en la
<em>estructura de clase</em>. S<>lo hay una copia de <20>sta en la que se
almacena informaci<63>n sobre las se<73>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<63>n de la estructura de la clase de
GtkButton debe ser algo as<61>:
<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<6F>n se trata como un contenedor (por ejemplo, cuando se le
cambia el tama<6D>o), su estructura de clase puede convertirse a
GtkContainerClass, y los campos relevantes se utilizar<61>n para manejar
las se<73>ales.
Tambi<EFBFBD>n hay una estructura que se crea para cada <em/widget/. Esta
estructura tiene campos para almacenar la informaci<63>n que es diferente
para cada copia del <em/widget/. Nosotros llamaremos a esta estructura
la <em>estructura objeto</em>. Para la clase bot<6F>n, es as<61>:
<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<63>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<63>n. Los
<em/widgets/ <tt/FileSelection/ y <tt/ColorSelection/ incluidos en la
distribuci<EFBFBD>n est<73>ndar son ejemplos de este tipo de <em/widgets/.
El <em/widget/ ejemplo que hemos creado en esta secci<63>n es el
<em/widget/ Tictactoe, una matriz de 3x3 de botones de selecci<63>n que
lanza una se<73>al cuando est<73>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<EFBFBD>n en una tabla, parece natural hacer que nuestra clase padre
sea la clase <tt/GtkTable/. Desafortunadamente, esto no
funcionar<EFBFBD>a. La creaci<63>n de un <em/widget/ se divide en dos funciones
- una funci<63>n <tt/NOMBREWIDGET_new()/ que utilizar<61> el usuario, y una
funci<EFBFBD>n <tt/NOMBREWIDGET_init()/ que har<61> el trabajo b<>sico de
inicializar el <em/widget/ que es independiente de los argumentos que
se le pasen a la funci<63>n <tt/_new()/. Los <em/widgets/ derivados s<>lo
llaman a la funci<63>n <tt/_init/ de su <em/widget/ padre. Pero esta
divisi<EFBFBD>n del trabajo no funciona bien con las tablas, que necesitan
saber en el momento de su creaci<63>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<61>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<61> como las
funciones p<>blicas. Un par de caracter<65>sticas que merecen dejarse
aparte. Para evitar la duplicaci<63>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<73>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<EFBFBD> est<73> 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<63>n <tt/_get_type()/.
<p>
Ahora continuaremos con la implementaci<63>n de nuestro <em/widget/. Una
funci<EFBFBD>n del n<>cleo de todo <em/widget/ es
<tt/NOMBREWIDGET_get_type()/. Cuando se llame a esta funci<63>n por
vez primera, le informar<61> a GTK sobre la clase del <em/widget/, y
devolver<EFBFBD> un ID que identificar<61> un<75>vocamente la clase <em/widget/. En
las llamadas siguientes, lo <20>nico que har<61> ser<65> devolver el ID.
<tscreen><verb>
guint
tictactoe_get_type ()
{
static guint ttt_type = 0;
if (!ttt_type)
{
GtkTypeInfo ttt_info =
{
"Tictactoe",
sizeof (Tictactoe),
sizeof (TictactoeClass),
(GtkClassInitFunc) tictactoe_class_init,
(GtkObjectInitFunc) tictactoe_init,
(GtkArgSetFunc) NULL,
(GtkArgGetFunc) NULL
};
ttt_type = gtk_type_unique (gtk_vbox_get_type (), &amp;ttt_info);
}
return ttt_type;
}
</verb></tscreen>
La estructura GtkTypeInfo tiene la definici<63>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<61>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<62> como crear objetos de un tipo
particular de <em/widget/.
<!-- ----------------------------------------------------------------- -->
<sect2> La funci<63>n <tt/_class_init()/
<p>
La funci<63>n <tt/NOMBREWIDGET_class_init()/ inicializa los campos de la
estructura clase del <em/widget/, y establece las se<73>ales de la
clase. Para nuestro <em/widget/ Tictactoe ser<65> una cosa as<61>:
<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<73>al, la se<73>al <tt/tictactoe/ que
se invoca cuando una fila, columna, o diagonal se rellena
completamente. No todos los <em/widgets/ compuestos necesitan se<73>ales,
por lo que si est<73> leyendo esto por primera vez, puede que sea mejor
que pase a la secci<63>n siguiente, ya que las cosas van a complicarse un
poco.
La funci<63>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<73>al. Los par<61>metros son:
<itemize>
<item> <tt/name/: El nombre de la se<73>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<EFBFBD>al. (Tambi<62>n se aplicar<61> 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<63>n que se utiliza para invocar al
manejador de se<73>al. Para los manejadores de se<73>al que no tengan m<>s
argumentos que el objeto que emiti<74> la se<73>al podemos utilizar la
funci<EFBFBD>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<61>metros del manejador de se<73>al
(distintos de los dos por defecto que hemos mencionado arriba).
<item> <tt/.../: Los tipos de los par<61>metros.
</itemize>
Cuando se especifican los tipos, se utilizar<61> la enumeraci<63>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 <20>nico para la
se<EFBFBD>al, que almacenamos en el vector <tt/tictactoe_signals/, que
indexaremos utilizando una enumeraci<63>n. (Convencionalmente, los
elementos de la enumeraci<63>n son el nombre de la se<73>al, en may<61>sculas,
pero aqu<71> tendr<64>amos un conflicto con la macro <tt/TICTACTOE()/, por
lo que lo llamaremos <tt/TICTACTOE_SIGNAL/.
Despu<EFBFBD>s de crear nuestras se<73>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<73>al `tictactoe' sea NULL,
indicando que no hay ninguna acci<63>n por defecto.
<!-- ----------------------------------------------------------------- -->
<sect2> La funci<63>n <tt/_init()/.
<p>
Cada clase <em/widget/ tambi<62>n necesita una funci<63>n para inicializar
la estructura del objeto. Normalmente, esta funci<63>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<63>n, esta
funci<EFBFBD>n tambi<62>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<63>n m<>s que cada <em/widget/ (excepto los <em/widget/ muy
b<EFBFBD>sicos como GtkBin que no pueden crear objetos) tiene que
tener - la funci<63>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<EFBFBD>n toma argumentos, y hace alguna inicializaci<63>n en funci<63>n de
estos. Las otras dos funciones son espec<65>ficas al <em/widget/
Tictactoe.
<tt/tictactoe_clear()/ es una funci<63>n p<>blica que reinicia todos los
botones en el <em/widget/ a la posici<63>n alta. Observe la utilizaci<63>n
de <tt/gtk_signal_handler_block_by_data()/ para hacer que no se
ejecute nuestro manejador de se<73>al innecesariamente por cambios en los
botones.
<tt/tictactoe_toggle()/ es el manejador de se<73>al que se invoca cuando
el usuario pulsa un bot<6F>n. Hace una comprobaci<63>n para ver si hay
alguna combinaci<63>n ganadora, y si la hay, emite la se<73>al
<EFBFBD>tictactoe<EFBFBD>.
<tscreen><verb>
GtkWidget*
tictactoe_new ()
{
return GTK_WIDGET ( gtk_type_new (tictactoe_get_type ()));
}
void
tictactoe_clear (Tictactoe *ttt)
{
int i,j;
for (i=0;i<3;i++)
for (j=0;j<3;j++)
{
gtk_signal_handler_block_by_data (GTK_OBJECT(ttt->buttons[i][j]), ttt);
gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (ttt->buttons[i][j]),
FALSE);
gtk_signal_handler_unblock_by_data (GTK_OBJECT(ttt->buttons[i][j]), ttt);
}
}
static void
tictactoe_toggle (GtkWidget *widget, Tictactoe *ttt)
{
int i,k;
static int rwins[8][3] = { { 0, 0, 0 }, { 1, 1, 1 }, { 2, 2, 2 },
{ 0, 1, 2 }, { 0, 1, 2 }, { 0, 1, 2 },
{ 0, 1, 2 }, { 0, 1, 2 } };
static int cwins[8][3] = { { 0, 1, 2 }, { 0, 1, 2 }, { 0, 1, 2 },
{ 0, 0, 0 }, { 1, 1, 1 }, { 2, 2, 2 },
{ 0, 1, 2 }, { 2, 1, 0 } };
int success, found;
for (k=0; k<8; k++)
{
success = TRUE;
found = FALSE;
for (i=0;i<3;i++)
{
success = success &amp;&amp;
GTK_TOGGLE_BUTTON(ttt->buttons[rwins[k][i]][cwins[k][i]])->active;
found = found ||
ttt->buttons[rwins[k][i]][cwins[k][i]] == widget;
}
if (success &amp;&amp; found)
{
gtk_signal_emit (GTK_OBJECT (ttt),
tictactoe_signals[TICTACTOE_SIGNAL]);
break;
}
}
}
</verb></tscreen>
Y finalmente, un programa ejemplo que utiliza nuestro <em/widget/
Tictactoe:
<tscreen><verb>
#include <gtk/gtk.h>
#include "tictactoe.h"
/* Invocado cuando se completa una fila, columna o diagonal */
void
win (GtkWidget *widget, gpointer data)
{
g_print ("Yay!\n");
tictactoe_clear (TICTACTOE (widget));
}
int
main (int argc, char *argv[])
{
GtkWidget *ventana;
GtkWidget *ttt;
gtk_init (&amp;argc, &amp;argv);
ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (ventana), "Aspect Frame");
gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
GTK_SIGNAL_FUNC (gtk_exit), NULL);
gtk_container_border_width (GTK_CONTAINER (ventana), 10);
/* Create a new Tictactoe widget */
ttt = tictactoe_new ();
gtk_container_add (GTK_CONTAINER (ventana), ttt);
gtk_widget_show (ttt);
/* And attach to its "tictactoe" signal */
gtk_signal_connect (GTK_OBJECT (ttt), "tictactoe",
GTK_SIGNAL_FUNC (win), NULL);
gtk_widget_show (ventana);
gtk_main ();
return 0;
}
</verb></tscreen>
<!-- ----------------------------------------------------------------- -->
<sect1> Creando un <em/widget/ desde cero.
<!-- ----------------------------------------------------------------- -->
<sect2> Introducci<63>n
<p>
En esta secci<63>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<61>gico con un puntero que el usuario
podr<EFBFBD> arrastrar para hacer que el marcador tenga un valor dado.
<!-- ----------------------------------------------------------------- -->
<sect2> Mostrando un <em/widget/ en la pantalla
<p>
Hay varios pasos que est<73>n involucrados en el dibujado en pantalla.
Despu<EFBFBD>s de que el <em/widget/ se cree con una llamada a
<tt/NOMBREWIDGET_new()/, se necesitar<61>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<70>s de las llamadas del
usuario
<tt/gtk_widget_show()/. Es la responsable de asegurarse de que el
<em/widget/ est<73> dibujado (<em/mapeado/) en la pantalla. Para una
clase contenedor, tambi<62>n deber<65> 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<63>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<63>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<61>a
los eventos <tt/expose/ necesarios)
</itemize>
Las <20>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<63>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<64>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<65>a un derroche duplicar el c<>digo de dibujado entre las
dos funciones. Lo normal es que cada <em/widget/ tenga una funci<63>n
llamada <tt/NOMBREWIDGET_paint()/ que haga el trabajo de dibujar el
<em/widget/, <20>sta funci<63>n ser<65> 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<63>n <tt/draw()/ por defecto y s<>lo
implementar la funci<63>n <tt/expose()/.
<!-- ----------------------------------------------------------------- -->
<sect2> Los or<6F>genes del <em/widget/ Dial
<p>
As<EFBFBD> como todos los animales terrestes son variaciones del primer
anf<EFBFBD>bio que sali<6C> del barro, los <em/widgets/ Gtk tienden a nacer
como variaciones de alg<6C>n otro <em/widget/ escrito previamente. Por
tanto, aunque esta secci<63>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 <20>ste como punto de arranque porque
ser<EFBFBD>a bonito que nuestro dial tuviese la misma interfaz que los
<em/widgets/ Scale, que son s<>lo una especializaci<63>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<61>a no est<73> familiarizado, desde el punto de
vista del escritor de aplicaciones, con la forma de funcionar de los
<em/widgets/ Scale, ser<65>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<6F>tica de actualizaci<63>n
* (GTK_UPDATE_[CONTINUOUS/DELAYED/DISCONTINUOUS]) */
guint policy : 2;
/* Bot<6F>n actualmente presionado o 0 si no hay ninguno */
guint8 boton;
/* Dimensi<73>n de los componendes del dial */
gint radius;
gint pointer_width;
/* ID del temporizador de actualizaci<63>n, o 0 si no hay ninguno */
guint32 timer;
/* <20>ngulo actual */
gfloat angle;
/* Viejos valores almacenados del adjustment, para que as<61> 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
<EFBFBD>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<70>s de incluir los ficheros de cabecera, y declarar unas
cuantas constantes, tenemos algunas funciones que proporcionan
informaci<EFBFBD>n sobre el <em/widget/ y lo inicializan:
<tscreen><verb>
#include <math.h>
#include <stdio.h>
#include <gtk/gtkmain.h>
#include <gtk/gtksignal.h>
#include "gtkdial.h"
#define SCROLL_DELAY_LENGTH 300
#define DIAL_DEFAULT_SIZE 100
/* Declaraciones de funciones */
[ omitido para salvar espacio ]
/* datos locales */
static GtkWidgetClass *parent_class = NULL;
guint
gtk_dial_get_type ()
{
static guint dial_type = 0;
if (!dial_type)
{
GtkTypeInfo dial_info =
{
"GtkDial",
sizeof (GtkDial),
sizeof (GtkDialClass),
(GtkClassInitFunc) gtk_dial_class_init,
(GtkObjectInitFunc) gtk_dial_init,
(GtkArgSetFunc) NULL,
(GtkArgGetFunc) NULL,
};
dial_type = gtk_type_unique (gtk_widget_get_type (), &amp;dial_info);
}
return dial_type;
}
static void
gtk_dial_class_init (GtkDialClass *class)
{
GtkObjectClass *object_class;
GtkWidgetClass *widget_class;
object_class = (GtkObjectClass*) class;
widget_class = (GtkWidgetClass*) class;
parent_class = gtk_type_class (gtk_widget_get_type ());
object_class->destroy = gtk_dial_destroy;
widget_class->realize = gtk_dial_realize;
widget_class->expose_event = gtk_dial_expose;
widget_class->size_request = gtk_dial_size_request;
widget_class->size_allocate = gtk_dial_size_allocate;
widget_class->button_press_event = gtk_dial_button_press;
widget_class->button_release_event = gtk_dial_button_release;
widget_class->motion_notify_event = gtk_dial_motion_notify;
}
static void
gtk_dial_init (GtkDial *dial)
{
dial->button = 0;
dial->policy = GTK_UPDATE_CONTINUOUS;
dial->timer = 0;
dial->radius = 0;
dial->pointer_width = 0;
dial->angle = 0.0;
dial->old_value = 0.0;
dial->old_lower = 0.0;
dial->old_upper = 0.0;
dial->adjustment = NULL;
}
GtkWidget*
gtk_dial_new (GtkAdjustment *adjustment)
{
GtkDial *dial;
dial = gtk_type_new (gtk_dial_get_type ());
if (!adjustment)
adjustment = (GtkAdjustment*) gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
gtk_dial_set_adjustment (dial, adjustment);
return GTK_WIDGET (dial);
}
static void
gtk_dial_destroy (GtkObject *object)
{
GtkDial *dial;
g_return_if_fail (object != NULL);
g_return_if_fail (GTK_IS_DIAL (object));
dial = GTK_DIAL (object);
if (dial->adjustment)
gtk_object_unref (GTK_OBJECT (dial->adjustment));
if (GTK_OBJECT_CLASS (parent_class)->destroy)
(* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
}
</verb></tscreen>
Observe que <20>sta funci<63>n <tt/init()/ hace menos cosas de las que hac<61>a
la funci<63>n <tt/init()/ que utilizamos con el <em/widget/ Tictactoe, ya
que <20>ste no es un <em/widget/ compuesto, y la funci<63>n <tt/new()/ hace
m<EFBFBD>s cosas, ya que ahora admite un argumento. Observe tambi<62>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<EFBFBD> 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<63>n
que hace el trabajo de crear la ventana X. A la funci<63>n se le pasar<61>
una m<>scara <tt/gdk_window_new()/ que especifica que campos de la
estructura <tt/GdkWindowAttr/ tienen datos (los campos restantes
tendr<EFBFBD>n los valores por defecto). Tambi<62>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<EFBFBD>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 <20>ltimo paso le permite a GTK despachar los
eventos que hayan para esta ventana hacia el <em/widget/ correcto.
<tscreen><verb>
static void
gtk_dial_realize (GtkWidget *widget)
{
GtkDial *dial;
GdkWindowAttr attributes;
gint attributes_mask;
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_DIAL (widget));
GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
dial = GTK_DIAL (widget);
attributes.x = widget->allocation.x;
attributes.y = widget->allocation.y;
attributes.width = widget->allocation.width;
attributes.height = widget->allocation.height;
attributes.wclass = GDK_INPUT_OUTPUT;
attributes.window_type = GDK_WINDOW_CHILD;
attributes.event_mask = gtk_widget_get_events (widget) |
GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK |
GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK |
GDK_POINTER_MOTION_HINT_MASK;
attributes.visual = gtk_widget_get_visual (widget);
attributes.colormap = gtk_widget_get_colormap (widget);
attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
widget->window = gdk_window_new (widget->parent->window, &amp;attributes, attributes_mask);
widget->style = gtk_style_attach (widget->style, widget->window);
gdk_window_set_user_data (widget->window, widget);
gtk_style_set_background (widget->style, widget->window, GTK_STATE_ACTIVE);
}
</verb></tscreen>
<sect2> Negociaci<63>n del tama<6D>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<6D>o deseado. Esta
petici<EFBFBD>n se controla mediante la funci<63>n
<tt/gtk_dial_size_request()/. Como nuestro <em/widget/ no es un
<em/widget/ contenedor, y no tiene ninguna limitaci<63>n en su tama<6D>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<EFBFBD>s de que todos los <em/widgets/ hayan pedido su tama<6D>o ideal,
se calcular<61> la ventana y cada <em/widget/ hijo ser<65> informado de su
tama<EFBFBD>o actual. Normalmente, <20>ste ser<65> al menos tan grande como el
pedido, pero si por ejemplo, el usuario ha redimensionado la ventana,
entonces puede que el tama<6D>o que se le de al <em/widget/ sea menor
que el que pidi<64>. La notificaci<63>n del tama<6D>o se maneja mediante la
funci<EFBFBD>n <tt/gtk_dial_size_allocate()/. F<>jese que esta funci<63>n calcula
los tama<6D>os de los diferentes elementos que componen la ventana para
su uso futuro, as<61> como todo el trabajo sucio que poner los
<em/widgets/ de la ventana X en la nueva posici<63>n y con el nuevo
tama<EFBFBD>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<6F> arriba, todo el dibujado de este <em/widget/ se hace
en el manejador de los eventos <tt/expose/. No hay mucho destacable
aqu<EFBFBD>, excepto la utilizaci<63>n de la funci<63>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<61>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<63>n
para ver si la pulsaci<63>n se hizo lo suficientemente cerca del
puntero, y si as<61> fue, almacenamos el bot<6F>n que puls<6C> el usuario en
en el campo <tt/button/ de la estructura del <em/widget/, y grabamos
todos los eventos del rat<61>n con una llamada a <tt/gtk_grab_add()/. El
movimiento del rat<61>n har<61> que se recalcule el valor del control
(mediante la funci<63>n <tt/gtk_dial_update_mouse/). Dependiendo de la
pol<EFBFBD>tica que sigamos, o bien se generar<61>n instant<6E>neamente los eventos
<tt/value_changed/ (<tt/GTK_UPDATE_CONTINUOUS/), o bien despu<70>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<6F>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<63>n del bot<6F>n fue dentro de la regi<67>n del
puntero - esto lo hacemos calculando la distancia x e y del punto
donde se puls<6C> el bot<6F>n rat<61>n de la l<>nea que se ha pasado mediante el
puntero */
dx = event->x - widget->allocation.width / 2;
dy = widget->allocation.height / 2 - event->y;
s = sin(dial->angle);
c = cos(dial->angle);
d_parallel = s*dy + c*dx;
d_perpendicular = fabs(s*dx - c*dy);
if (!dial->button &&
(d_perpendicular < dial->pointer_width/2) &&
(d_parallel > - dial->pointer_width))
{
gtk_grab_add (widget);
dial->button = event->button;
gtk_dial_update_mouse (dial, event->x, event->y);
}
return FALSE;
}
static gint
gtk_dial_button_release (GtkWidget *widget,
GdkEventButton *event)
{
GtkDial *dial;
g_return_val_if_fail (widget != NULL, FALSE);
g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
g_return_val_if_fail (event != NULL, FALSE);
dial = GTK_DIAL (widget);
if (dial->button == event->button)
{
gtk_grab_remove (widget);
dial->button = 0;
if (dial->policy == GTK_UPDATE_DELAYED)
gtk_timeout_remove (dial->timer);
if ((dial->policy != GTK_UPDATE_CONTINUOUS) &&
(dial->old_value != dial->adjustment->value))
gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
}
return FALSE;
}
static gint
gtk_dial_motion_notify (GtkWidget *widget,
GdkEventMotion *event)
{
GtkDial *dial;
GdkModifierType mods;
gint x, y, mask;
g_return_val_if_fail (widget != NULL, FALSE);
g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
g_return_val_if_fail (event != NULL, FALSE);
dial = GTK_DIAL (widget);
if (dial->button != 0)
{
x = event->x;
y = event->y;
if (event->is_hint || (event->window != widget->window))
gdk_window_get_pointer (widget->window, &amp;x, &amp;y, &amp;mods);
switch (dial->button)
{
case 1:
mask = GDK_BUTTON1_MASK;
break;
case 2:
mask = GDK_BUTTON2_MASK;
break;
case 3:
mask = GDK_BUTTON3_MASK;
break;
default:
mask = 0;
break;
}
if (mods & mask)
gtk_dial_update_mouse (dial, x,y);
}
return FALSE;
}
static gint
gtk_dial_timer (GtkDial *dial)
{
g_return_val_if_fail (dial != NULL, FALSE);
g_return_val_if_fail (GTK_IS_DIAL (dial), FALSE);
if (dial->policy == GTK_UPDATE_DELAYED)
gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
return FALSE;
}
static void
gtk_dial_update_mouse (GtkDial *dial, gint x, gint y)
{
gint xc, yc;
gfloat old_value;
g_return_if_fail (dial != NULL);
g_return_if_fail (GTK_IS_DIAL (dial));
xc = GTK_WIDGET(dial)->allocation.width / 2;
yc = GTK_WIDGET(dial)->allocation.height / 2;
old_value = dial->adjustment->value;
dial->angle = atan2(yc-y, x-xc);
if (dial->angle < -M_PI/2.)
dial->angle += 2*M_PI;
if (dial->angle < -M_PI/6)
dial->angle = -M_PI/6;
if (dial->angle > 7.*M_PI/6.)
dial->angle = 7.*M_PI/6.;
dial->adjustment->value = dial->adjustment->lower + (7.*M_PI/6 - dial->angle) *
(dial->adjustment->upper - dial->adjustment->lower) / (4.*M_PI/3.);
if (dial->adjustment->value != old_value)
{
if (dial->policy == GTK_UPDATE_CONTINUOUS)
{
gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
}
else
{
gtk_widget_draw (GTK_WIDGET(dial), NULL);
if (dial->policy == GTK_UPDATE_DELAYED)
{
if (dial->timer)
gtk_timeout_remove (dial->timer);
dial->timer = gtk_timeout_add (SCROLL_DELAY_LENGTH,
(GtkFunction) gtk_dial_timer,
(gpointer) dial);
}
}
}
}
</verb></tscreen>
<p>
Cambios en el Adjustment por motivos externos significa que se le
comunicar<EFBFBD>n a nuestro <em/widget/ mediante las se<73>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 <20>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<EFBFBD>neas de c<>digo. Aunque pueda parecer un poco exagerado, todav<61>a
no hemos escrito demasiado c<>digo, ya que la mayor<6F>a de las l<>neas
son de ficheros de cabecera y de adornos. Todav<61>a se le pueden hacer
algunas mejoras a este <em/widget/:
<itemize>
<item> Si prueba el <em/widget/, ver<65> 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<65>a ser capaz de utilizar las flechas de arriba
y abajo para aumentar y decrementar el valor.
<item> Ser<65>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<6F>n, aunque tambi<62>n
queremos que los botones pudiesen realizar la operaci<63>n de incrementar
o decrementar varias veces, mientras se mantenga el bot<6F>n pulsado, tal
y como lo hacen las flechas en una barra de desplazamiento. La mayor<6F>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<65>n prefiera) una etiqueta o un <em/widget/ entry para mostrar el
valor actual del marcador.
</itemize>
<!-- ----------------------------------------------------------------- -->
<sect1> Aprendiendo m<>s
<p>
S<EFBFBD>lo se han descrito una peque<75>a parte de los muchos detalles
involucrados en la creaci<63>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: <20>es un <em/widget/ contenedor?
<EFBFBD>Debe tener su propia ventana? <20>Es una modificaci<63>n de un
<em/widget/ existente? En ese momento busque un <em/widget/ similar, y
comience a hacer los cambios.
<EFBFBD>Buena suerte!
<!-- ***************************************************************** -->
<sect>Scribble, un sencillo programa de dibujo de ejemplo
<!-- ***************************************************************** -->
<!-- ----------------------------------------------------------------- -->
<sect1> Objetivos
<p>
En esta secci<63>n, vamos a crear un sencillo programa de dibujo. En el
proceso, vamos a examinar como se manejan los eventos de rat<61>n, como
dibujar en una ventana, y como mejorar el dibujado utilizando un
<em/pixmap/ intermedio. Despu<70>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<61>n la posibilidad de obtener informaci<63>n extra, como la presi<73>n
y la inclinaci<63>n, de todo tipo de dispositivos de una forma sencilla.
<!-- ----------------------------------------------------------------- -->
<sect1> Manejo de eventos
<p>
Las se<73>ales GTK sobre las que ya hemos discutido son para las
acciones de alto nivel, como cuando se selecciona un elemento de un
men<EFBFBD>. Sin embargo a veces es <20>til tratar con los acontecimientos a
bajo nivel, como cuando se mueve el rat<61>n, o cuando se est<73>
presionando una tecla. Tambi<62>n hay se<73>ales GTK relacionadas con
estos <em/eventos/ de bajo nivel. Los manejadores de estas se<73>ales
tienen un par<61>metro extra que es un puntero a una estructura
conteniendo informaci<63>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<61>:
<tscreen><verb>
struct _GdkEventMotion
{
GdkEventType type;
GdkWindow *ventana;
guint32 time;
gdouble x;
gdouble y;
...
guint state;
...
};
</verb></tscreen>
<tt/type/ adquirir<69> 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<63>n que ha habido
cuando ocurri<72> el evento (esto es, especifica que teclas han cambiado
su estado y que botones del rat<61>n se han presionado.) Es la
operaci<EFBFBD>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<73>ales, para especificar que es lo que pasa cuando
ocurre un evento, llamaremos a <tt>gtk_signal_connect()</tt>. Pero
tambi<EFBFBD>n necesitamos decirle a GTK sobre que eventos queremos ser
informados. Para ello, llamaremos a la funci<63>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<71> 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<63>n
antes de que se cree la ventana X para el <em/widget/ GTK. En
t<EFBFBD>rminos pr<70>cticos, significa que debemos llamarla inmediatamente
despu<EFBFBD>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<63>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<EFBFBD>n del rat<61>n y cuando se mueve, por lo que debemos especificar
los eventos <tt/GDK_POINTER_MOTION_MASK/ y
<tt/GDK_BUTTON_PRESS_MASK/. Tambi<62>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<6D>o de nuestra ventana, no
tenemos que especificar la correspondiente <tt/GDK_STRUCTURE_MASK/,
porque ya est<73> activada por defecto para todas las ventanas.
<p>
Tenemos un problema con lo que acabamos de hacer, y tiene que ver con
la utilizaci<63>n de <tt/GDK_POINTER_MOTION_MASK/. Si especificamos este
evento, el servidor a<>adir<69> un evento de movimiento a la cola de
eventos cada vez que el usuario mueva el rat<61>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, <20>nos llevar<61>
otros 5 segundos el cazarle despu<70>s de que hay levantado el bot<6F>n
del rat<61>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<70>s de entrar en nuestra ventana, o despu<70>s de que se
apriete o se suelte un bot<6F>n (y se reciba el evento
correspondiente). Los eventos de movimiento restantes se eliminar<61>n a
no ser que preguntemos especificamente por la posici<63>n del puntero
utilizando la funci<63>n:
<tscreen><verb>
GdkWindow* gdk_window_get_pointer (GdkWindow *ventana,
gint *x,
gint *y,
GdkModifierType *mask);
</verb></tscreen>
(Hay otra funci<63>n, <tt>gtk_widget_get_pointer()</tt> que tiene una
interfaz m<>s sencillo, pero esta simplificaci<63>n le resta utilidad, ya
que s<>lo devuelve la posici<63>n del rat<61>n, y no si alguno de sus botones
est<EFBFBD> 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<70>s. Los manejadores de
<tt/motion_notify_event/ y de <tt/button_press_event/ son bastante
simples:
<tscreen><verb>
static gint
button_press_event (GtkWidget *widget, GdkEventButton *event)
{
if (event->button == 1 &amp;&amp; pixmap != NULL)
draw_brush (widget, event->x, event->y);
return TRUE;
}
static gint
motion_notify_event (GtkWidget *widget, GdkEventMotion *event)
{
int x, y;
GdkModifierType state;
if (event->is_hint)
gdk_window_get_pointer (event->window, &amp;x, &amp;y, &amp;state);
else
{
x = event->x;
y = event->y;
state = event->state;
}
if (state &amp; GDK_BUTTON1_MASK &amp;&amp; pixmap != NULL)
draw_brush (widget, x, y);
return TRUE;
}
</verb></tscreen>
<!-- ----------------------------------------------------------------- -->
<sect1> El <em/widget/ DrawingArea, y dibujando
<p>
Vamos a pasar al proceso de dibujar en la pantalla. El <em/widget/ que
utilizaremos ser<65> 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 <20>rea de dibujo
utilizando la llamada:
<tscreen><verb>
GtkWidget* gtk_drawing_area_new (void);
</verb></tscreen>
Se puede especificar un tama<6D>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<6D>o por defecto, como para todos los
<em/widgets/, llamando a <tt/gtk_widget_set_usize()/, y esto, adem<65>s,
puede cambiarse si el usuario cambia manualmente el tama<6D>o de la
ventana que contiene el <20>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<63>n y deberemos redibujar lo que se
hab<EFBFBD>a tapado.
<p>
Tener que recordar todo lo que se dibuj<75> en la pantalla para que
podamos redibujarla convenientemente es, por decirlo de alguna manera
suave, una locura. Adem<65>s puede quedar mal si hay que borrar partes
de la pantalla y hay que redibujarlas paso a paso. La soluci<63>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<EFBFBD> almacenada en la memoria del servidor, pero que no se mostrar<61>,
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<63>n:
<tscreen><verb>
GdkPixmap* gdk_pixmap_new (GdkWindow *ventana,
gint width,
gint height,
gint depth);
</verb></tscreen>
El par<61>metro <tt/widget/ especifica una ventana GDK de las que este
<em/pixmap/ tomar<61> algunas propiedades. <tt/width/ y <tt/height/
especifican el tama<6D>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<EFBFBD> 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<EFBFBD>o de la ventana, incluyendo cuando <20>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<63>n simplemente copia la
porci<EFBFBD>n relevante del <em/pixmap/ en la pantalla (determinaremos la
zona a redibujar utilizando el campo <tt/event->area/ del evento de
exposici<EFBFBD>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 <20>c<EFBFBD>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<63>n de estas funciones o el fichero de cabecera
<tt>&lt;gdk/gdk.h&gt;</tt> para obtener m<>s detalles sobre estas
funciones. Todas comparten los dos primeros argumentos. El primero es
el dibujable en el que se dibujar<61>, y el segundo argumento es un
<em/contexto gr<67>fico/ (GC).
<p>
Un contexto gr<67>fico re<72>ne la informaci<63>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<67>ficos. Cada <em/widget/ tiene un GC
asociado. (Que puede modificarse en un fichero gtkrc, ver la secci<63>n
<EFBFBD>Ficheros rc de GTK<54>.) 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<61>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<63>n <tt/draw_brush()/, que es la que dibuja en la
pantalla, ser<65> la siguiente:
<tscreen><verb>
/* Draw a rectangle on the screen */
static void
draw_brush (GtkWidget *widget, gdouble x, gdouble y)
{
GdkRectangle update_rect;
update_rect.x = x - 5;
update_rect.y = y - 5;
update_rect.width = 10;
update_rect.height = 10;
gdk_draw_rectangle (pixmap,
widget->style->black_gc,
TRUE,
update_rect.x, update_rect.y,
update_rect.width, update_rect.height);
gtk_widget_draw (widget, &amp;update_rect);
}
</verb></tscreen>
Despu<EFBFBD>s de que dibujemos el rect<63>ngulo representando la brocha en el
<em/pixmap/ llamaremos a la funci<63>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<61>metro <tt/area/
necesita actualizarse. X generar<61> un evento de exposici<63>n
(combinando posiblemente distintas zonas pasadas mediante distintas
llamadas a <tt/gtk_widget_draw()/) que har<61> que nuestro manejador de
eventos de exposici<63>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<EFBFBD> disponible en el mismo lugar en el que consigui<75> 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<72>stica
mucho m<>s f<>cilmente de c<>mo lo har<61>amos con un rat<61>n. La forma
m<EFBFBD>s sencilla de utilizar estos dispositivos es simplemente
reemplazando a los ratones, pero as<61> perdemos muchas de las ventajas
de este tipo de dispositivos, como por ejemplo:
<itemize>
<item> Sensibilidad a la presi<73>n
<item> Informaci<63>n sobre la inclinaci<63>n
<item> Colocaci<63>n subpixel
<item> Multiples entradas (por ejemplo, un l<>piz con una punta y una
goma)
</itemize>
Para informaci<63>n sobre la extensi<73>n XInput, ver el <htmlurl
url="http://www.msc.cornell.edu/~otaylor/xinput/XInput-HOWTO.html"
name="XInput-HOWTO">.
<p>
Si examinamos la definici<63>n completa de, por ejemplo, la estructura
<tt/GdkEventMotion/, veremos que tiene campos para almacenar la
informaci<EFBFBD>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<73>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<63>n en cada direcci<63>n. <tt/source/
y <tt/deviceid/ especifican el dispositivo para el que ocurre el
evento de dos maneras diferentes. <tt/source/ da alguna informaci<63>n
simple sobre el tipo de dispositivo. Puede tomar los valores de la
enumeraci<EFBFBD>n siguiente:
<tscreen><verb>
GDK_SOURCE_MOUSE
GDK_SOURCE_PEN
GDK_SOURCE_ERASER
GDK_SOURCE_CURSOR
</verb></tscreen>
<tt/deviceid/ especifica un n<>mero <20>nico ID para el dispositivo. Puede
utilizarse para obtener m<>s informaci<63>n sobre el dispositivo
utilizando la funci<63>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<61>n.)
<sect2> Activando la informaci<63>n del dispositivo extendido
<p>
Para informar a GTK de nuestro inter<65>s en la informaci<63>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<73>n, pero s<>lo si no tenemos que
dibujar nuestro propio cursor. Ver la secci<63>n <ref
id="sec_Further_Sophistications" name="Sofisticaciones adicionales">
m<EFBFBD>s abajo para obtener m<>s informaci<63>n sobre el dibujado del
cursor. Tambi<62>n podr<64>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<EFBFBD>a no hemos llegado al final de la historia. Por defecto, no hay
ning<EFBFBD>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<64>logo si no ha sido ya creado, y lo
pone en primer plano en caso contrario.
<tscreen><verb>
void
input_dialog_destroy (GtkWidget *w, gpointer data)
{
*((GtkWidget **)data) = NULL;
}
void
create_input_dialog ()
{
static GtkWidget *inputd = NULL;
if (!inputd)
{
inputd = gtk_input_dialog_new();
gtk_signal_connect (GTK_OBJECT(inputd), "destroy",
(GtkSignalFunc)input_dialog_destroy, &amp;inputd);
gtk_signal_connect_object (GTK_OBJECT(GTK_INPUT_DIALOG(inputd)->close_button),
"clicked",
(GtkSignalFunc)gtk_widget_hide,
GTK_OBJECT(inputd));
gtk_widget_hide ( GTK_INPUT_DIALOG(inputd)->save_button);
gtk_widget_show (inputd);
}
else
{
if (!GTK_WIDGET_MAPPED(inputd))
gtk_widget_show(inputd);
else
gdk_window_raise(inputd->window);
}
}
</verb></tscreen>
(Tome nota de la manera en que hemos manejado el cuadro de
di<EFBFBD>logo. Conectando la se<73>al <tt/destroy/, nos aseguramos de que no
tendremos un puntero al cuadro de di<64>logo despu<70>s de que haya sido
destruido, lo que nos podr<64>a llevar a un segfault.)
<p>
El InputDialog tiene dos botones <20>Cerrar<61> y <20>Guardar<61>, que por
defecto no tienen ninguna acci<63>n asignada. En la funci<63>n anterior
hemos hecho que <20>Cerrar<61> oculte el cuadro de di<64>logo, ocultando el
bot<EFBFBD>n <20>Guardar<61>, ya que no implementaremos en este programa la
acci<EFBFBD>n de guardar las opciones de XInput.
<sect2> Utilizando la informaci<63>n de los dispositivos extras
<p>
Una vez hemos activado el dispositivo, podemos utilizar la
informaci<EFBFBD>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<63>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<63>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<63>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<63>n devolver<65> valores razonables cuando no est<73>n
activados los eventos extendidos. (En ese caso, <tt/event->deviceid/
tendr<EFBFBD> el valor <tt/GDK_CORE_POINTER/).
Por tanto la estructura b<>sica de nuestros manejadores de los
eventos de movimiento y de pulsaci<63>n del bot<6F>n del rat<61>n no
cambiar<EFBFBD>n mucho - s<>lo tenemos que a<>adir c<>digo para manejar la
informaci<EFBFBD>n extra.
<tscreen><verb>
static gint
button_press_event (GtkWidget *widget, GdkEventButton *event)
{
print_button_press (event->deviceid);
if (event->button == 1 &amp;&amp; pixmap != NULL)
draw_brush (widget, event->source, event->x, event->y, event->pressure);
return TRUE;
}
static gint
motion_notify_event (GtkWidget *widget, GdkEventMotion *event)
{
gdouble x, y;
gdouble pressure;
GdkModifierType state;
if (event->is_hint)
gdk_input_window_get_pointer (event->window, event->deviceid,
&amp;x, &amp;y, &amp;pressure, NULL, NULL, &amp;state);
else
{
x = event->x;
y = event->y;
pressure = event->pressure;
state = event->state;
}
if (state &amp; GDK_BUTTON1_MASK &amp;&amp; pixmap != NULL)
draw_brush (widget, event->source, x, y, pressure);
return TRUE;
}
</verb></tscreen>
Tambi<EFBFBD>n tenemos que hacer algo con la nueva informaci<63>n. Nuestra
nueva funci<63>n <tt/draw_brush()/ dibuja con un color diferente
dependiendo de <tt/event->source/ y cambia el tama<6D>o de la brocha
dependiendo de la presi<73>n.
<tscreen><verb>
/* Draw a rectangle on the screen, size depending on pressure,
and color on the type of device */
static void
draw_brush (GtkWidget *widget, GdkInputSource source,
gdouble x, gdouble y, gdouble pressure)
{
GdkGC *gc;
GdkRectangle update_rect;
switch (source)
{
case GDK_SOURCE_MOUSE:
gc = widget->style->dark_gc[GTK_WIDGET_STATE (widget)];
break;
case GDK_SOURCE_PEN:
gc = widget->style->black_gc;
break;
case GDK_SOURCE_ERASER:
gc = widget->style->white_gc;
break;
default:
gc = widget->style->light_gc[GTK_WIDGET_STATE (widget)];
}
update_rect.x = x - 10 * pressure;
update_rect.y = y - 10 * pressure;
update_rect.width = 20 * pressure;
update_rect.height = 20 * pressure;
gdk_draw_rectangle (pixmap, gc, TRUE,
update_rect.x, update_rect.y,
update_rect.width, update_rect.height);
gtk_widget_draw (widget, &amp;update_rect);
}
</verb></tscreen>
<sect2> Obteniendo m<>s informaci<63>n de un dispositivo
<p>
Como ejemplo de como podemos obtener m<>s informaci<63>n de un
dispositivo, nuestro programa imprimir<69> el nombre del dispositivo que
genera cada pulsaci<63>n de bot<6F>n. Para encontrar el nombre de un
dispositivo, llamaremos a la funci<63>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<63>n de configuraci<63>n que puede
ignorar, a menos que quiera permitir la opci<63>n de grabar la
configuraci<EFBFBD>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<63>n sobre la configuraci<63>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<63>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<72>a con la primera versi<73>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<61>a
falla en algunas caracter<65>sticas que deber<65>an estar disponibles en una
aplicaci<EFBFBD>n bien hecha. Primero, el usuario no deber<65>a tener que
configurar su dispositivo cada vez que ejecute el programa, por lo que
deber<EFBFBD>a estar disponible la opci<63>n de guardar la configuraci<63>n del
dispositivo. Esto se hace recorriendo el resultado de
<tt/gdk_input_list_devices()/ y escribiendo la configuraci<63>n en un
fichero.
<p>
Para cargar la configuraci<63>n del dispositivo cuando se vuelva a
ejecutar el programa, puede utilizar las funciones que proporciona GDK
para cambiar la configuraci<63>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<65>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<61>a bien
tener un procedimiento est<73>ndar para poder hacer todo esto en
cualquier aplicaciones. Probablemente se llegue a esto en una capa
superior a GTK, quiz<69>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<EFBFBD>cleo y como dispositivo directamente utilizable por una
aplicaci<EFBFBD>n. Ver el <url
url="http://www.msc.cornell.edu/~otaylor/xinput/XInput-HOWTO.html"
name="XInput-HOWTO"> para m<>s informaci<63>n sobre esto. Con esto
queremos decir que si quiere tener la m<>xima audiencia necesita
dibujar su propio cursor.
<p>
Una aplicaci<63>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<73> <20>pr<70>ximo<6D>. (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<EFBFBD>piz est<73> tocando la tableta, se dice que el dispositivo est<73>
<EFBFBD>pr<EFBFBD>ximo<EFBFBD>). 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<EFBFBD>n de GTK.
<!-- ***************************************************************** -->
<sect>Trucos para escribir aplicaciones GTK
<!-- ***************************************************************** -->
<p>
Esta secci<63>n es s<>lo un compendio de sabiduria, de gu<67>as generales
de estilo y de consejos para crear buenas aplicaciones GTK. Y es
totalmente in<69>til por ahora ya que esta frase es s<>lo un t<>pico :)
<EFBFBD>Utilice GNU autoconf y automake! Son sus amigos :) Pretendo poner
aqu<EFBFBD> una r<>pida introducci<63>n a ambos.
<!-- ***************************************************************** -->
<sect>Contribuyendo <label id="sec_Contributing">
<!-- ***************************************************************** -->
<p>
Este documento, como muchos otros grandes paquetes de programas que
hay por ah<61>, fue creado de forma libre por voluntarios. Si comprende
algo de GTK que todav<61>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<64>n distribuir copias de su documento
como deseen, etc...
<p>
Gracias.
<!-- ***************************************************************** -->
<sect>Cr<43>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<65>s.
<item>Raph Levien, <tt><htmlurl url="mailto:raph@acm.org"
name="raph@acm.org"></tt> por el <20>hola mundo<64> a la GTK, el
empaquetado de <em/widgets/, y su sabidur<75>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<65>
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<63>n sobre el <em/widget/
EventBox (y el parche para el distro). Tambi<62>n es el responsable
del c<>digo de las selecciones y el tutorial, as<61> como de la
secci<EFBFBD>n de escribiendo su propio <em/widget/ GTK, y la aplicaci<63>n de
ejemplo. <20>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<6E>stico trabajo sobre los
<em/widgets/ Notebook, Progress Bar, Dialog, y selecci<63>n de ficheros.
<EFBFBD>Muchas gracias Mark!
Has sido de una gran ayuda.
<item>Tim Janik <tt><htmlurl url="mailto:timj@gtk.org"
name="timj@gtk.org"></tt> por su gran trabajo en el <em/widget/ List.
2000-04-23 09:17:36 +00:00
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<63>n y el c<>digo de
los men<65>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<63>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<63>n est<73> bajo la misma licencia bajo la que est<73>
el documento original. A continuaci<63>n se presenta la traducci<63>n
de la licencia y la licencia en versi<73>n original. En caso de haber
alguna discrepancia entre la traducci<63>n y la licencia original, se
aplicar<EFBFBD> esta <20>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<69>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<69>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<69>ntico a
<EFBFBD>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<63>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<6F>sito. Se
proporciona como un documento libre. Como tal, los autores y
encargados del mantenimiento de la informaci<63>n que se da en el
documento no pueden dar ninguna garantia de que la misma est<73> 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<63>n
<p>
Esta traduccion tiene copyright (C) 1999 de Joaqu<71>n Cuenca Abela
<tt><htmlurl url="mailto:e98cuenc@criens.u-psud.fr"
name="&lt;e98cuenc@criens.u-psud.fr&gt;"></tt>
y de Eduardo Anglada Varela
<tt><htmlurl url="mailto:eduardo.anglada@adi.uam.es"
name="&lt;eduardo.anglada@adi.uam.es&gt;"></tt>.
Si tiene cualquier
duda, sugerencia o correcci<63>n no dude en consultarnos.
Gracias a Manuel de Vega Barreiro <tt><htmlurl
url="mailto:barreiro@arrakis.es"
name="&lt;barreiro@arrakis.es&gt;"></tt> por haber hospedado las
versiones beta y la versi<73>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<53>ales GTK <label id="sec_GTK_Signals">
<!-- ***************************************************************** -->
<p>
GTK+, al ser un conjunto de <em/widgets/ orientado al objeto, tiene
una jerarqu<71>a de herencias. Este mecanismo de herencia se aplica a las
se<EFBFBD>ales. Por eso, debe utilizar el <20>rbol de jerarqu<71>as de los
<em/widgets/ cuando utilice las se<73>ales que aparecen en esta secci<63>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<73>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<73> pasando al manejador de se<73>al. Como ver<65>
m<EFBFBD>s adelante, cada una de estructuras de los datos de los eventos
tienen un miembro de este tipo. Se define como la siguiente
enumeraci<EFBFBD>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<63>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/. <20>sta es una uni<6E>n de todos los otros tipos de
datos, que permite que se convierta en un tipo de dato de evento
espec<EFBFBD>fico con un manejador de se<73>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: <20>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<63>n tenemos el c<>digo ejemplo que se ha utilizado en el
texto anterior y que no se ha incluido al completo en otro lugar.
<!-- ----------------------------------------------------------------- -->
<sect1>Tictactoe
<!-- ----------------------------------------------------------------- -->
<sect2>tictactoe.h
<p>
<tscreen><verb>
/* principio del ejemplo tictactoe tictactoe.h */
/* GTK - The GIMP Toolkit
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __TICTACTOE_H__
#define __TICTACTOE_H__
#include <gdk/gdk.h>
#include <gtk/gtkvbox.h>
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#define TICTACTOE(obj) GTK_CHECK_CAST (obj, tictactoe_get_type (), Tictactoe)
#define TICTACTOE_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, tictactoe_get_type (), TictactoeClass)
#define IS_TICTACTOE(obj) GTK_CHECK_TYPE (obj, tictactoe_get_type ())
typedef struct _Tictactoe Tictactoe;
typedef struct _TictactoeClass TictactoeClass;
struct _Tictactoe
{
GtkVBox vbox;
GtkWidget *botones[3][3];
};
struct _TictactoeClass
{
GtkVBoxClass parent_class;
void (* tictactoe) (Tictactoe *ttt);
};
guint tictactoe_get_type (void);
GtkWidget* tictactoe_new (void);
void tictactoe_clear (Tictactoe *ttt);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* __TICTACTOE_H__ */
/* fin del ejemplo */
</verb></tscreen>
<!-- ----------------------------------------------------------------- -->
<sect2>tictactoe.c
<p>
<tscreen><verb>
/* principio del ejemplo tictactoe tictactoe.c */
/* GTK - The GIMP Toolkit
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include "gtk/gtksignal.h"
#include "gtk/gtktable.h"
#include "gtk/gtktogglebutton.h"
#include "tictactoe.h"
enum {
TICTACTOE_SIGNAL,
LAST_SIGNAL
};
static void tictactoe_class_init (TictactoeClass *klass);
static void tictactoe_init (Tictactoe *ttt);
static void tictactoe_toggle (GtkWidget *widget, Tictactoe *ttt);
static gint tictactoe_signals[LAST_SIGNAL] = { 0 };
guint
tictactoe_get_type ()
{
static guint ttt_type = 0;
if (!ttt_type)
{
GtkTypeInfo ttt_info =
{
"Tictactoe",
sizeof (Tictactoe),
sizeof (TictactoeClass),
(GtkClassInitFunc) tictactoe_class_init,
(GtkObjectInitFunc) tictactoe_init,
(GtkArgSetFunc) NULL,
(GtkArgGetFunc) NULL
};
ttt_type = gtk_type_unique (gtk_vbox_get_type (), &amp;ttt_info);
}
return ttt_type;
}
static void
tictactoe_class_init (TictactoeClass *class)
{
GtkObjectClass *object_class;
object_class = (GtkObjectClass*) class;
tictactoe_signals[TICTACTOE_SIGNAL] = gtk_signal_new ("tictactoe",
GTK_RUN_FIRST,
object_class->type,
GTK_SIGNAL_OFFSET (TictactoeClass, tictactoe),
gtk_signal_default_marshaller, GTK_TYPE_NONE, 0);
gtk_object_class_add_signals (object_class, tictactoe_signals, LAST_SIGNAL);
class->tictactoe = NULL;
}
static void
tictactoe_init (Tictactoe *ttt)
{
GtkWidget *table;
gint i,j;
table = gtk_table_new (3, 3, TRUE);
gtk_container_add (GTK_CONTAINER(ttt), table);
gtk_widget_show (table);
for (i=0;i<3; i++)
for (j=0;j<3; j++)
{
ttt->buttons[i][j] = gtk_toggle_button_new ();
gtk_table_attach_defaults (GTK_TABLE(table), ttt->buttons[i][j],
i, i+1, j, j+1);
gtk_signal_connect (GTK_OBJECT (ttt->buttons[i][j]), "toggled",
GTK_SIGNAL_FUNC (tictactoe_toggle), ttt);
gtk_widget_set_usize (ttt->buttons[i][j], 20, 20);
gtk_widget_show (ttt->buttons[i][j]);
}
}
GtkWidget*
tictactoe_new ()
{
return GTK_WIDGET ( gtk_type_new (tictactoe_get_type ()));
}
void
tictactoe_clear (Tictactoe *ttt)
{
int i,j;
for (i=0;i<3;i++)
for (j=0;j<3;j++)
{
gtk_signal_handler_block_by_data (GTK_OBJECT(ttt->buttons[i][j]), ttt);
gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (ttt->buttons[i][j]),
FALSE);
gtk_signal_handler_unblock_by_data (GTK_OBJECT(ttt->buttons[i][j]), ttt);
}
}
static void
tictactoe_toggle (GtkWidget *widget, Tictactoe *ttt)
{
int i,k;
static int rwins[8][3] = { { 0, 0, 0 }, { 1, 1, 1 }, { 2, 2, 2 },
{ 0, 1, 2 }, { 0, 1, 2 }, { 0, 1, 2 },
{ 0, 1, 2 }, { 0, 1, 2 } };
static int cwins[8][3] = { { 0, 1, 2 }, { 0, 1, 2 }, { 0, 1, 2 },
{ 0, 0, 0 }, { 1, 1, 1 }, { 2, 2, 2 },
{ 0, 1, 2 }, { 2, 1, 0 } };
int success, found;
for (k=0; k<8; k++)
{
success = TRUE;
found = FALSE;
for (i=0;i<3;i++)
{
success = success &amp;&amp;
GTK_TOGGLE_BUTTON(ttt->buttons[rwins[k][i]][cwins[k][i]])->active;
found = found ||
ttt->buttons[rwins[k][i]][cwins[k][i]] == widget;
}
if (success &amp;&amp; found)
{
gtk_signal_emit (GTK_OBJECT (ttt),
tictactoe_signals[TICTACTOE_SIGNAL]);
break;
}
}
}
/* fin del ejemplo */
</verb></tscreen>
<!-- ----------------------------------------------------------------- -->
<sect2>ttt_test.c
<p>
<tscreen><verb>
/* principio del ejemplo tictactoe ttt_test.c */
#include <gtk/gtk.h>
#include "tictactoe.h"
void
win (GtkWidget *widget, gpointer data)
{
g_print ("Yay!\n");
tictactoe_clear (TICTACTOE (widget));
}
int
main (int argc, char *argv[])
{
GtkWidget *ventana;
GtkWidget *ttt;
gtk_init (&amp;argc, &amp;argv);
ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (ventana), "Aspect Frame");
gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
GTK_SIGNAL_FUNC (gtk_exit), NULL);
gtk_container_border_width (GTK_CONTAINER (ventana), 10);
ttt = tictactoe_new ();
gtk_container_add (GTK_CONTAINER (ventana), ttt);
gtk_widget_show (ttt);
gtk_signal_connect (GTK_OBJECT (ttt), "tictactoe",
GTK_SIGNAL_FUNC (win), NULL);
gtk_widget_show (ventana);
gtk_main ();
return 0;
}
/* fin del ejemplo */
</verb></tscreen>
<!-- ----------------------------------------------------------------- -->
<sect1> GtkDial
<!-- ----------------------------------------------------------------- -->
<sect2> gtkdial.h
<p>
<tscreen><verb>
/* principio del ejmplo gtkdial gtkdial.h */
/* GTK - The GIMP Toolkit
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __GTK_DIAL_H__
#define __GTK_DIAL_H__
#include <gdk/gdk.h>
#include <gtk/gtkadjustment.h>
#include <gtk/gtkwidget.h>
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#define GTK_DIAL(obj) GTK_CHECK_CAST (obj, gtk_dial_get_type (), GtkDial)
#define GTK_DIAL_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, gtk_dial_get_type (), GtkDialClass)
#define GTK_IS_DIAL(obj) GTK_CHECK_TYPE (obj, gtk_dial_get_type ())
typedef struct _GtkDial GtkDial;
typedef struct _GtkDialClass GtkDialClass;
struct _GtkDial
{
GtkWidget widget;
/* pol<6F>tica de actualizaci<63>n
* (GTK_UPDATE_[CONTINUOUS/DELAYED/DISCONTINUOUS]) */
guint policy : 2;
/* Bot<6F>n actualmente presionado o 0 si no hay ninguno */
guint8 boton;
/* Dimensi<73>n de los componendes del dial */
gint radius;
gint pointer_width;
/* ID del temporizador de actualizaci<63>n, o 0 si no hay ninguno */
guint32 timer;
/* <20>ngulo actual */
gfloat angle;
/* Viejos valores almacenados del adjustment, para que as<61> no
* tengamos que saber cuando cambia algo */
gfloat old_value;
gfloat old_lower;
gfloat old_upper;
/* El objeto adjustment que almacena los datos para este dial */
GtkAdjustment *adjustment;
};
struct _GtkDialClass
{
GtkWidgetClass parent_class;
};
GtkWidget* gtk_dial_new (GtkAdjustment *adjustment);
guint gtk_dial_get_type (void);
GtkAdjustment* gtk_dial_get_adjustment (GtkDial *dial);
void gtk_dial_set_update_policy (GtkDial *dial,
GtkUpdateType policy);
void gtk_dial_set_adjustment (GtkDial *dial,
GtkAdjustment *adjustment);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* __GTK_DIAL_H__ */
/* fin del ejemplo */
</verb></tscreen>
<!-- ----------------------------------------------------------------- -->
<sect2> gtkdial.c
<p>
<tscreen><verb>
/* principio del ejemplo gtkdial gtkdial.c */
/* GTK - The GIMP Toolkit
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include <math.h>
#include <stdio.h>
#include <gtk/gtkmain.h>
#include <gtk/gtksignal.h>
#include "gtkdial.h"
#define SCROLL_DELAY_LENGTH 300
#define DIAL_DEFAULT_SIZE 100
/* declaraciones de funciones */
static void gtk_dial_class_init (GtkDialClass *klass);
static void gtk_dial_init (GtkDial *dial);
static void gtk_dial_destroy (GtkObject *object);
static void gtk_dial_realize (GtkWidget *widget);
static void gtk_dial_size_request (GtkWidget *widget,
GtkRequisition *requisition);
static void gtk_dial_size_allocate (GtkWidget *widget,
GtkAllocation *allocation);
static gint gtk_dial_expose (GtkWidget *widget,
GdkEventExpose *event);
static gint gtk_dial_button_press (GtkWidget *widget,
GdkEventButton *event);
static gint gtk_dial_button_release (GtkWidget *widget,
GdkEventButton *event);
static gint gtk_dial_motion_notify (GtkWidget *widget,
GdkEventMotion *event);
static gint gtk_dial_timer (GtkDial *dial);
static void gtk_dial_update_mouse (GtkDial *dial, gint x, gint y);
static void gtk_dial_update (GtkDial *dial);
static void gtk_dial_adjustment_changed (GtkAdjustment *adjustment,
gpointer data);
static void gtk_dial_adjustment_value_changed (GtkAdjustment *adjustment,
gpointer data);
/* datos locales */
static GtkWidgetClass *parent_class = NULL;
guint
gtk_dial_get_type ()
{
static guint dial_type = 0;
if (!dial_type)
{
GtkTypeInfo dial_info =
{
"GtkDial",
sizeof (GtkDial),
sizeof (GtkDialClass),
(GtkClassInitFunc) gtk_dial_class_init,
(GtkObjectInitFunc) gtk_dial_init,
(GtkArgSetFunc) NULL,
(GtkArgGetFunc) NULL,
};
dial_type = gtk_type_unique (gtk_widget_get_type (), &amp;dial_info);
}
return dial_type;
}
static void
gtk_dial_class_init (GtkDialClass *class)
{
GtkObjectClass *object_class;
GtkWidgetClass *widget_class;
object_class = (GtkObjectClass*) class;
widget_class = (GtkWidgetClass*) class;
parent_class = gtk_type_class (gtk_widget_get_type ());
object_class->destroy = gtk_dial_destroy;
widget_class->realize = gtk_dial_realize;
widget_class->expose_event = gtk_dial_expose;
widget_class->size_request = gtk_dial_size_request;
widget_class->size_allocate = gtk_dial_size_allocate;
widget_class->button_press_event = gtk_dial_button_press;
widget_class->button_release_event = gtk_dial_button_release;
widget_class->motion_notify_event = gtk_dial_motion_notify;
}
static void
gtk_dial_init (GtkDial *dial)
{
dial->button = 0;
dial->policy = GTK_UPDATE_CONTINUOUS;
dial->timer = 0;
dial->radius = 0;
dial->pointer_width = 0;
dial->angle = 0.0;
dial->old_value = 0.0;
dial->old_lower = 0.0;
dial->old_upper = 0.0;
dial->adjustment = NULL;
}
GtkWidget*
gtk_dial_new (GtkAdjustment *adjustment)
{
GtkDial *dial;
dial = gtk_type_new (gtk_dial_get_type ());
if (!adjustment)
adjustment = (GtkAdjustment*) gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
gtk_dial_set_adjustment (dial, adjustment);
return GTK_WIDGET (dial);
}
static void
gtk_dial_destroy (GtkObject *object)
{
GtkDial *dial;
g_return_if_fail (object != NULL);
g_return_if_fail (GTK_IS_DIAL (object));
dial = GTK_DIAL (object);
if (dial->adjustment)
gtk_object_unref (GTK_OBJECT (dial->adjustment));
if (GTK_OBJECT_CLASS (parent_class)->destroy)
(* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
}
GtkAdjustment*
gtk_dial_get_adjustment (GtkDial *dial)
{
g_return_val_if_fail (dial != NULL, NULL);
g_return_val_if_fail (GTK_IS_DIAL (dial), NULL);
return dial->adjustment;
}
void
gtk_dial_set_update_policy (GtkDial *dial,
GtkUpdateType policy)
{
g_return_if_fail (dial != NULL);
g_return_if_fail (GTK_IS_DIAL (dial));
dial->policy = policy;
}
void
gtk_dial_set_adjustment (GtkDial *dial,
GtkAdjustment *adjustment)
{
g_return_if_fail (dial != NULL);
g_return_if_fail (GTK_IS_DIAL (dial));
if (dial->adjustment)
{
gtk_signal_disconnect_by_data (GTK_OBJECT (dial->adjustment), (gpointer) dial);
gtk_object_unref (GTK_OBJECT (dial->adjustment));
}
dial->adjustment = adjustment;
gtk_object_ref (GTK_OBJECT (dial->adjustment));
gtk_signal_connect (GTK_OBJECT (adjustment), "changed",
(GtkSignalFunc) gtk_dial_adjustment_changed,
(gpointer) dial);
gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed",
(GtkSignalFunc) gtk_dial_adjustment_value_changed,
(gpointer) dial);
dial->old_value = adjustment->value;
dial->old_lower = adjustment->lower;
dial->old_upper = adjustment->upper;
gtk_dial_update (dial);
}
static void
gtk_dial_realize (GtkWidget *widget)
{
GtkDial *dial;
GdkWindowAttr attributes;
gint attributes_mask;
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_DIAL (widget));
GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
dial = GTK_DIAL (widget);
attributes.x = widget->allocation.x;
attributes.y = widget->allocation.y;
attributes.width = widget->allocation.width;
attributes.height = widget->allocation.height;
attributes.wclass = GDK_INPUT_OUTPUT;
attributes.window_type = GDK_WINDOW_CHILD;
attributes.event_mask = gtk_widget_get_events (widget) |
GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK |
GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK |
GDK_POINTER_MOTION_HINT_MASK;
attributes.visual = gtk_widget_get_visual (widget);
attributes.colormap = gtk_widget_get_colormap (widget);
attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
widget->window = gdk_window_new (widget->parent->window, &amp;attributes, attributes_mask);
widget->style = gtk_style_attach (widget->style, widget->window);
gdk_window_set_user_data (widget->window, widget);
gtk_style_set_background (widget->style, widget->window, GTK_STATE_ACTIVE);
}
static void
gtk_dial_size_request (GtkWidget *widget,
GtkRequisition *requisition)
{
requisition->width = DIAL_DEFAULT_SIZE;
requisition->height = DIAL_DEFAULT_SIZE;
}
static void
gtk_dial_size_allocate (GtkWidget *widget,
GtkAllocation *allocation)
{
GtkDial *dial;
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_DIAL (widget));
g_return_if_fail (allocation != NULL);
widget->allocation = *allocation;
dial = GTK_DIAL (widget);
if (GTK_WIDGET_REALIZED (widget))
{
gdk_window_move_resize (widget->window,
allocation->x, allocation->y,
allocation->width, allocation->height);
}
dial->radius = MIN(allocation->width,allocation->height) * 0.45;
dial->pointer_width = dial->radius / 5;
}
static gint
gtk_dial_expose (GtkWidget *widget,
GdkEventExpose *event)
{
GtkDial *dial;
GdkPoint points[3];
gdouble s,c;
gdouble theta;
gint xc, yc;
gint tick_length;
gint i;
g_return_val_if_fail (widget != NULL, FALSE);
g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
g_return_val_if_fail (event != NULL, FALSE);
if (event->count > 0)
return FALSE;
dial = GTK_DIAL (widget);
gdk_window_clear_area (widget->window,
0, 0,
widget->allocation.width,
widget->allocation.height);
xc = widget->allocation.width/2;
yc = widget->allocation.height/2;
/* Dibuja las rayitas */
for (i=0; i<25; i++)
{
theta = (i*M_PI/18. - M_PI/6.);
s = sin(theta);
c = cos(theta);
tick_length = (i%6 == 0) ? dial->pointer_width : dial->pointer_width/2;
gdk_draw_line (widget->window,
widget->style->fg_gc[widget->state],
xc + c*(dial->radius - tick_length),
yc - s*(dial->radius - tick_length),
xc + c*dial->radius,
yc - s*dial->radius);
}
/* Dibuja el puntero */
s = sin(dial->angle);
c = cos(dial->angle);
points[0].x = xc + s*dial->pointer_width/2;
points[0].y = yc + c*dial->pointer_width/2;
points[1].x = xc + c*dial->radius;
points[1].y = yc - s*dial->radius;
points[2].x = xc - s*dial->pointer_width/2;
points[2].y = yc - c*dial->pointer_width/2;
gtk_draw_polygon (widget->style,
widget->window,
GTK_STATE_NORMAL,
GTK_SHADOW_OUT,
points, 3,
TRUE);
return FALSE;
}
static gint
gtk_dial_button_press (GtkWidget *widget,
GdkEventButton *event)
{
GtkDial *dial;
gint dx, dy;
double s, c;
double d_parallel;
double d_perpendicular;
g_return_val_if_fail (widget != NULL, FALSE);
g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
g_return_val_if_fail (event != NULL, FALSE);
dial = GTK_DIAL (widget);
/* Determinar si la pulsaci<63>n del bot<6F>n fue dentro de la regi<67>n del
puntero - esto lo hacemos calculando la distancia x e y del punto
donde se puls<6C> el bot<6F>n rat<61>n de la l<>nea que se ha pasado mediante el
puntero */
dx = event->x - widget->allocation.width / 2;
dy = widget->allocation.height / 2 - event->y;
s = sin(dial->angle);
c = cos(dial->angle);
d_parallel = s*dy + c*dx;
d_perpendicular = fabs(s*dx - c*dy);
if (!dial->button &amp;&amp;
(d_perpendicular < dial->pointer_width/2) &amp;&amp;
(d_parallel > - dial->pointer_width))
{
gtk_grab_add (widget);
dial->button = event->button;
gtk_dial_update_mouse (dial, event->x, event->y);
}
return FALSE;
}
static gint
gtk_dial_button_release (GtkWidget *widget,
GdkEventButton *event)
{
GtkDial *dial;
g_return_val_if_fail (widget != NULL, FALSE);
g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
g_return_val_if_fail (event != NULL, FALSE);
dial = GTK_DIAL (widget);
if (dial->button == event->button)
{
gtk_grab_remove (widget);
dial->button = 0;
if (dial->policy == GTK_UPDATE_DELAYED)
gtk_timeout_remove (dial->timer);
if ((dial->policy != GTK_UPDATE_CONTINUOUS) &amp;&amp;
(dial->old_value != dial->adjustment->value))
gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
}
return FALSE;
}
static gint
gtk_dial_motion_notify (GtkWidget *widget,
GdkEventMotion *event)
{
GtkDial *dial;
GdkModifierType mods;
gint x, y, mask;
g_return_val_if_fail (widget != NULL, FALSE);
g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
g_return_val_if_fail (event != NULL, FALSE);
dial = GTK_DIAL (widget);
if (dial->button != 0)
{
x = event->x;
y = event->y;
if (event->is_hint || (event->window != widget->window))
gdk_window_get_pointer (widget->window, &amp;x, &amp;y, &amp;mods);
switch (dial->button)
{
case 1:
mask = GDK_BUTTON1_MASK;
break;
case 2:
mask = GDK_BUTTON2_MASK;
break;
case 3:
mask = GDK_BUTTON3_MASK;
break;
default:
mask = 0;
break;
}
if (mods &amp; mask)
gtk_dial_update_mouse (dial, x,y);
}
return FALSE;
}
static gint
gtk_dial_timer (GtkDial *dial)
{
g_return_val_if_fail (dial != NULL, FALSE);
g_return_val_if_fail (GTK_IS_DIAL (dial), FALSE);
if (dial->policy == GTK_UPDATE_DELAYED)
gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
return FALSE;
}
static void
gtk_dial_update_mouse (GtkDial *dial, gint x, gint y)
{
gint xc, yc;
gfloat old_value;
g_return_if_fail (dial != NULL);
g_return_if_fail (GTK_IS_DIAL (dial));
xc = GTK_WIDGET(dial)->allocation.width / 2;
yc = GTK_WIDGET(dial)->allocation.height / 2;
old_value = dial->adjustment->value;
dial->angle = atan2(yc-y, x-xc);
if (dial->angle < -M_PI/2.)
dial->angle += 2*M_PI;
if (dial->angle < -M_PI/6)
dial->angle = -M_PI/6;
if (dial->angle > 7.*M_PI/6.)
dial->angle = 7.*M_PI/6.;
dial->adjustment->value = dial->adjustment->lower + (7.*M_PI/6 - dial->angle) *
(dial->adjustment->upper - dial->adjustment->lower) / (4.*M_PI/3.);
if (dial->adjustment->value != old_value)
{
if (dial->policy == GTK_UPDATE_CONTINUOUS)
{
gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
}
else
{
gtk_widget_draw (GTK_WIDGET(dial), NULL);
if (dial->policy == GTK_UPDATE_DELAYED)
{
if (dial->timer)
gtk_timeout_remove (dial->timer);
dial->timer = gtk_timeout_add (SCROLL_DELAY_LENGTH,
(GtkFunction) gtk_dial_timer,
(gpointer) dial);
}
}
}
}
static void
gtk_dial_update (GtkDial *dial)
{
gfloat new_value;
g_return_if_fail (dial != NULL);
g_return_if_fail (GTK_IS_DIAL (dial));
new_value = dial->adjustment->value;
if (new_value < dial->adjustment->lower)
new_value = dial->adjustment->lower;
if (new_value > dial->adjustment->upper)
new_value = dial->adjustment->upper;
if (new_value != dial->adjustment->value)
{
dial->adjustment->value = new_value;
gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
}
dial->angle = 7.*M_PI/6. - (new_value - dial->adjustment->lower) * 4.*M_PI/3. /
(dial->adjustment->upper - dial->adjustment->lower);
gtk_widget_draw (GTK_WIDGET(dial), NULL);
}
static void
gtk_dial_adjustment_changed (GtkAdjustment *adjustment,
gpointer data)
{
GtkDial *dial;
g_return_if_fail (adjustment != NULL);
g_return_if_fail (data != NULL);
dial = GTK_DIAL (data);
if ((dial->old_value != adjustment->value) ||
(dial->old_lower != adjustment->lower) ||
(dial->old_upper != adjustment->upper))
{
gtk_dial_update (dial);
dial->old_value = adjustment->value;
dial->old_lower = adjustment->lower;
dial->old_upper = adjustment->upper;
}
}
static void
gtk_dial_adjustment_value_changed (GtkAdjustment *adjustment,
gpointer data)
{
GtkDial *dial;
g_return_if_fail (adjustment != NULL);
g_return_if_fail (data != NULL);
dial = GTK_DIAL (data);
if (dial->old_value != adjustment->value)
{
gtk_dial_update (dial);
dial->old_value = adjustment->value;
}
}
/* fin del ejemplo */
</verb></tscreen>
<!-- ----------------------------------------------------------------- -->
<sect1> Scribble
<p>
<tscreen><verb>
/* principio del ejemplo scribble-simple scribble-simple.c */
/* GTK - The GIMP Toolkit
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include <gtk/gtk.h>
/* Creamos un backing pixmap para la zona donde dibujamos */
static GdkPixmap *pixmap = NULL;
/* Creamos un nuevo backing pixmap del tama<6D>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<63>ngulo en la pantalla */
static void
draw_brush (GtkWidget *widget, gdouble x, gdouble y)
{
GdkRectangle update_rect;
update_rect.x = x - 5;
update_rect.y = y - 5;
update_rect.width = 10;
update_rect.height = 10;
gdk_draw_rectangle (pixmap,
widget->style->black_gc,
TRUE,
update_rect.x, update_rect.y,
update_rect.width, update_rect.height);
gtk_widget_draw (widget, &amp;update_rect);
}
static gint
button_press_event (GtkWidget *widget, GdkEventButton *event)
{
if (event->button == 1 &amp;&amp; pixmap != NULL)
draw_brush (widget, event->x, event->y);
return TRUE;
}
static gint
motion_notify_event (GtkWidget *widget, GdkEventMotion *event)
{
int x, y;
GdkModifierType state;
if (event->is_hint)
gdk_window_get_pointer (event->window, &amp;x, &amp;y, &amp;state);
else
{
x = event->x;
y = event->y;
state = event->state;
}
if (state &amp; GDK_BUTTON1_MASK &amp;&amp; pixmap != NULL)
draw_brush (widget, x, y);
return TRUE;
}
void
quit ()
{
gtk_exit (0);
}
int
main (int argc, char *argv[])
{
GtkWidget *ventana;
GtkWidget *drawing_area;
GtkWidget *vbox;
GtkWidget *boton;
gtk_init (&amp;argc, &amp;argv);
ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_widget_set_name (ventana, "Test Input");
vbox = gtk_vbox_new (FALSE, 0);
gtk_container_add (GTK_CONTAINER (ventana), vbox);
gtk_widget_show (vbox);
gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
GTK_SIGNAL_FUNC (quit), NULL);
/* Crear la zona de dibujado */
drawing_area = gtk_drawing_area_new ();
gtk_drawing_area_size (GTK_DRAWING_AREA (drawing_area), 200, 200);
gtk_box_pack_start (GTK_BOX (vbox), drawing_area, TRUE, TRUE, 0);
gtk_widget_show (drawing_area);
/* Las se<73>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<53>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<6F>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<EFBFBD>N: El <em>widget</em> GtkList ha sido reemplazado por el
<em>widget</em> GtkCList.
El <em>widget</em> GtkList est<73> dise<73>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<63>n.
Deber<EFBFBD>a familiarizarse con la utilizaci<63>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<EFBFBD>lo hay un campo dentro de la definici<63>n de la estructura del
<em>widget</em> GtkList que nos es de inter<65>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<73>n actualmente seleccionados, o NULL si la
selecci<EFBFBD>n est<73> vacia. Por lo tanto para saber quien es la actual
selecci<EFBFBD>n debemos leer el campo <tt/GTK_LIST()->selection/, pero no
modificarlo ya que los campos de los que est<73> constituido GtkList
est<EFBFBD>n controlados por las funciones gtk_list_*().
El <tt/selection_mode/ de la GtkList determina las posibilidades de
selecci<EFBFBD>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<63>n es o NULL o contiene un
puntero a un GList con un solo elemento seleccionado.
<item> GTK_SELECTION_BROWSE - La selecci<63>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<64> por tanto un solo elemento.
<item> GTK_SELECTION_MULTIPLE - La selecci<63>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<61>.
<item> GTK_SELECTION_EXTENDED - La selecci<63>n siempre es NULL.
</itemize>
El valor por defecto es GTK_SELECTION_MULTIPLE.
<!-- ----------------------------------------------------------------- -->
<sect1> Se<53>ales
<p>
<tscreen><verb>
void selection_changed( GtkList *list );
</verb></tscreen>
Se invocar<61> esta se<73>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<73>al cuando un hijo de la GtkList est<73> siendo
seleccionado. Esto ocurre principalmente en llamadas a
<tt/gtk_list_select_item()/, a <tt/gtk_list_select_child()/, cuando se
pulsa alg<6C>n bot<6F>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<73>al cuando un hijo del GtkList est<73> siendo
deseleccionado. Esto ocurre principalmente cuando ocurre una llamada a
<tt/gtk_list_unselect_item()/, <tt/gtk_list_unselect_item()/,
pulsaciones de bot<6F>n y a veces se lanza indirectamente cuando se a<>ade
o se elimina alg<6C>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<EFBFBD>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<63>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<63>n termine de ejecutarse es
responsabilidad del que llama a la misma. Est<73> bajo su responsabilidad
la destrucci<63>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<63>n afectar<61>
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<73>al <tt/select_child/ para el elemento especificado
mediante su posici<63>n actual en la lista.
<tscreen><verb>
void gtk_list_unselect_item( GtkList *list,
gint item);
</verb></tscreen>
Invoca la se<73>al <tt/unselect_child/ para un elemento especificado
mediante su posici<63>n actual en la lista.
<tscreen><verb>
void gtk_list_select_child( GtkList *list,
GtkWidget *hijo);
</verb></tscreen>
Invoca la se<73>al <tt/select_child/ para el hijo especificado.
<tscreen><verb>
void gtk_list_unselect_child( GtkList *list,
GtkWidget *hijo);
</verb></tscreen>
Invoca la se<73>al <tt/unselect_child/ para el hijo especificado.
<tscreen><verb>
gint gtk_list_child_position( GtkList *list,
GtkWidget *hijo);
</verb></tscreen>
Devuelve la posici<63>n de <tt/hijo/ en la lista. Se devuelve <20>-1<> en
caso de producirse alg<6C>n error.
<tscreen><verb>
void gtk_list_set_selection_mode( GtkList *list,
GtkSelectionMode mode );
</verb></tscreen>
Pone el modo de selecci<63>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<63>n *Note
Standard Macros::.
<tscreen><verb>
GtkListClass *GTK_LIST_CLASS( gpointer class);
</verb></tscreen>
Convierte un puntero general en `GtkListClass *'. Para m<>s informaci<63>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<EFBFBD>s informaci<63>n, *Note Standard Macros::.
<!-- ----------------------------------------------------------------- -->
<sect1> Ejemplo
<p>
A continuaci<63>n tenemos un programa ejemplo que muestra los cambios de
la selecci<63>n de un GtkList, y le deja <20>arrestar<61> elementos de la
lista en una prisi<73>n, seleccion<6F>ndolos con el bot<6F>n derecho del rat<61>n.
<tscreen><verb>
/* principio del ejemplo list list.c */
/* incluye los ficheros de cabecera de gtk+
* incluye stdio.h, que necesitamos para la funci<63>n printf()
*/
#include <gtk/gtk.h>
#include <stdio.h>
/* <20>sta es nuestra cadena de identificaci<63>n para almacenar datos en la
* lista de elementos
*/
const gchar *list_item_data_key="list_item_data";
/* prototipos para los manejadores de se<73>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<63>n principal donde se establece el interface con el usuario */
gint main (int argc, gchar *argv[])
{
GtkWidget *separator;
GtkWidget *ventana;
GtkWidget *vbox;
GtkWidget *scrolled_window;
GtkWidget *frame;
GtkWidget *gtklist;
GtkWidget *boton;
GtkWidget *list_item;
GList *dlist;
guint i;
gchar buffer[64];
/* inicializar gtk+ (y consecuentemente gdk) */
gtk_init(&amp;argc, &amp;argv);
/* crear una ventana donde meter todos los widgets y conectar
* gtk_main_quit() con el evento "destroy" de la ventana para
* poder controlar los eventos de cerrado de ventana del
* administrador de ventanas
*/
ventana=gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(ventana), "GtkList Example");
gtk_signal_connect(GTK_OBJECT(ventana),
"destroy",
GTK_SIGNAL_FUNC(gtk_main_quit),
NULL);
/* dentro de la ventana necesitamos una caja para alinear los
* widgets verticalmente */
vbox=gtk_vbox_new(FALSE, 5);
gtk_container_border_width(GTK_CONTAINER(vbox), 5);
gtk_container_add(GTK_CONTAINER(ventana), vbox);
gtk_widget_show(vbox);
/* <20>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<63>n manipuladora de se<73>al
* sigh_print_selection() a la se<73>al "selection_changed" del
* GtkList para imprimir los elementos seleccionados cada vez que
* cambie la selecci<63>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<73>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<73>al sigh_button_event() al
* GtkList que manejar<61> 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<6F>n y conectar su se<73>al "clicked" con la
* destrucci<63>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<64>ndolos a la GtkList mediante
* gtk_container_add() tambi<62>n consultaremos la cadena de texto de
* la etiqueta y la asociaremos con la list_item_data_key para
* cada elemento de la lista
*/
for (i=0; i<5; i++) {
GtkWidget *etiqueta;
gchar *string;
sprintf(buffer, "ListItemContainer with Label #%d", i);
etiqueta=gtk_label_new(buffer);
list_item=gtk_list_item_new();
gtk_container_add(GTK_CONTAINER(list_item), etiqueta);
gtk_widget_show(etiqueta);
gtk_container_add(GTK_CONTAINER(gtklist), list_item);
gtk_widget_show(list_item);
gtk_label_get(GTK_LABEL(etiqueta), &amp;string);
gtk_object_set_data(GTK_OBJECT(list_item),
list_item_data_key,
string);
}
/* aqu<71>, estamos creando otras 5 etiquetas, esta vez utilizaremos
* gtk_list_item_new_with_label() para la creaci<63>n
* no podemos consultar la cadena de texto de la etiqueta ya que
* no tenemos el puntero de etiquetas y por tanto lo <20>nico que
* haremos ser<65> 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<65> 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, <20>verdad? ;)
*/
gtk_widget_show(ventana);
/* y nos metemos en el bucle de eventos de gtk
*/
gtk_main();
/* llegaremos aqu<71> despu<70>s de que se llame a gtk_main_quit(), lo
* que ocurre si se destruye la ventana
*/
return 0;
}
/* <20>ste es el manejador de se<73>al que se conect<63> 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<6F>n (el bot<6F>n derecho) se
* levanta
*/
if (event->type==GDK_BUTTON_RELEASE &amp;&amp;
event->button==3) {
GList *dlist, *free_list;
GtkWidget *new_prisoner;
/* sacar la lista de elementos que est<73>n actualmente
* seleccionados y que ser<65>n nuestro pr<70>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<73>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);
}
}
}
/* <20>ste es el manipulador de se<73>al que se llama si GtkList emite la
* se<73>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, <20>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<63>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<73> dise<73>ado para comportarse como un
contenedor que tiene un hijo, proporcionando funciones para la
selecci<EFBFBD>n/deselecci<63>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<73> 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<63>n
<tt/gtk_list_item_new_with_label()/. Se puede conseguir el mismo
efecto creando un GtkLabel, poniendo su alineaci<63>n a <tt/xalign=0/ e
<tt/yalign=0.5/ y seguido de una adici<63>n al contenedor GtkListItem.
Nadie le obliga a meter un GtkLabel en un GtkListItem, puede meter un
GtkVBox o un GtkArrow, etc...
<!-- ----------------------------------------------------------------- -->
<sect1> Se<53>ales
<p>
Un GtkListItem no crea por s<> misma nuevas se<73>ales, pero hereda las
se<EFBFBD>ales de un GtkItem. Para m<>s informaci<63>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<6C>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 <20>nico
hijo. Se devuelve el nuevo <em/widget/ como un puntero a un objeto
GtkWidget. Se devuelve NULL en caso de producirse alg<6C>n error.
<tscreen><verb>
void gtk_list_item_select( GtkListItem *list_item );
</verb></tscreen>
Esta funci<63>n es, b<>sicamente, un recubrimiento de una llamada a
<tt/gtk_item_select (GTK_ITEM (list_item))/, y emitir<69> la se<73>al
<tt/select/. Para m<>s informaci<63>n *Note GtkItem::.
<tscreen><verb>
void gtk_list_item_deselect( GtkListItem *list_item );
</verb></tscreen>
Esta funci<63>n es, b<>sicamente, un recubrimiento de una llamada a
<tt/gtk_item_deselect (GTK_ITEM (list_item))/, y emitir<69> la se<73>al
<tt/deselect/. Para m<>s informaci<63>n *Note GtkItem::.
<tscreen><verb>
GtkListItem *GTK_LIST_ITEM( gpointer obj );
</verb></tscreen>
Convierte un puntero general a `GtkListItem *'. Para m<>s informaci<63>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<EFBFBD>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<63>n *Note Standard Macros::.
<!-- ----------------------------------------------------------------- -->
<sect1> Ejemplo
<p>
Para ver un ejemplo de todo esto, mire el de GtkList, que tambi<62>n
cubre la utilizaci<63>n un GtkListItem.
</article>