mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-12-26 13:41:07 +00:00
5fc106081f
Tue Jul 11 20:59:35 2000 Tim Janik <timj@gtk.org> * docs/tutorial/gtk_tut_12.es.sgml (name): * docs/tutorial/gtk_tut_it.sgml (name): * docs/tutorial/gtk_tut_fr.sgml (name): * docs/tutorial/gtk_tut.sgml (name): scratched notion of completely outdated email adress: s/timj@psynet.net/timj@gtk.org/.
17639 lines
590 KiB
Plaintext
Executable File
17639 lines
590 KiB
Plaintext
Executable File
<!doctype linuxdoc system>
|
|
|
|
<!--
|
|
Traducción realizada por:
|
|
Joaquín Cuenca Abela (e98cuenc@criens.u-psud.fr)
|
|
Eduardo Anglada Varela (eduardo.anglada@adi.uam.es)
|
|
|
|
Versión beta 2 de la traducción del GTK+
|
|
Tutorial 1.2. Cualquier sugerencia será bienvenida.
|
|
Los cambios más significativos de esta versión son la traducción
|
|
de las variables de los programas y XXX.
|
|
|
|
Si quiere obtener este documento puede encontrarlo en Linux Landia:
|
|
http://www.croftj.net/~barreiro/spanish/gnome-es/
|
|
-->
|
|
|
|
<article>
|
|
<title>GTK Tutorial v1.2
|
|
<author>Tony Gale <tt><htmlurl url="mailto:gale@gtk.org"
|
|
name="<gale@gtk.org>"></tt>,
|
|
Ian Main <tt><htmlurl url="mailto:imain@gtk.org"
|
|
name="<imain@gtk.org>"></tt>
|
|
<date>21 de Febrero de 1999
|
|
<abstract>
|
|
Este documento es un tutorial sobre como utilizar GTK (el GIMP
|
|
Toolkit) en C
|
|
</abstract>
|
|
|
|
<toc>
|
|
|
|
<!-- ***************************************************************** -->
|
|
<sect>Introducción
|
|
<!-- ***************************************************************** -->
|
|
<p>
|
|
GTK (GIMP Toolkit) es una biblioteca para crear interfaces gráficas
|
|
de usuario. Su licencia es la LGPL, así que mediante GTK podrá
|
|
desarrollar programas con licencias abiertas, gratuitas, libres, y
|
|
hasta licencias comerciales no libres sin mayores problemas.
|
|
|
|
Se llama el GIMP toolkit porque fue escrito para el desarrollo del
|
|
General Image Manipulation Program (GIMP), pero ahora GTK se utiliza
|
|
en un gran número de proyectos de programación, incluyendo el
|
|
proyecto GNU Network Object Model Environment (GNOME). GTK está
|
|
construido encima de GDK (GIMP Drawing Kit) que básicamente es un
|
|
recubrimiento de las funciones de bajo nivel que deben haber para
|
|
acceder al sistema de ventanas sobre el que se programe (Xlib en el
|
|
caso de X windows). Los principales autores de GTK son:
|
|
|
|
<itemize>
|
|
<item> Peter Mattis <tt><htmlurl url="mailto:petm@xcf.berkeley.edu"
|
|
name="petm@xcf.berkeley.edu"></tt>
|
|
<item> Spencer Kimball <tt><htmlurl url="mailto:spencer@xcf.berkeley.edu"
|
|
name="spencer@xcf.berkeley.edu"></tt>
|
|
<item> Josh MacDonald <tt><htmlurl url="mailto:jmacd@xcf.berkeley.edu"
|
|
name="jmacd@xcf.berkeley.edu"></tt>
|
|
</itemize>
|
|
|
|
GTK es esencialmente una interfaz para la programación de
|
|
aplicaciones orientadas al objeto (API). Aunque está completamente
|
|
escrito en C, esta implementado haciendo uso de la idea de clases y de
|
|
funciones respuesta o de <em/callback/ (punteros o funciones).
|
|
|
|
Tenemos un tercer componente llamado glib, que contiene unas cuantas
|
|
funciones para reemplazar algunas llamadas estándar, así como
|
|
funciones adicionales para manejar listas enlazadas, etc... Se
|
|
reemplazan algunas funciones para aumentar la portabilidad de GTK, ya
|
|
que algunas de las funciones implementadas no están disponibles o
|
|
no son estándar en otros unixs, como por ejemplo
|
|
g_strerror(). Algunas otras contienen mejoras a la versión de libc,
|
|
como g_malloc que mejora las posibilidades de encontrar errores.
|
|
|
|
Este tutorial describe la interfaz C de GTK. Hay recubrimientos GTK
|
|
para muchos otros lenguajes, incluyendo C++, Guile, Perl, Python, TOM,
|
|
Ada95, Objective C, Free Pascal, y Eiffel. Si va a utilizar el
|
|
recubrimiento para alguno de estos lenguajes, mire primero su
|
|
documentación. En algunos casos la documentación puede describir
|
|
algún convenio importante (que debería conocer de antemano) y
|
|
después puede volver a este tutorial. También hay algún API
|
|
multiplataforma (como wxWindows y V) que utilizan GTK como una de sus
|
|
plataformas destino; de nuevo, consulte primero la documentación
|
|
que viene con estos paquetes.
|
|
|
|
Si está desarrollando su aplicación GTK en C++, hay algunas
|
|
cosas que debería saber. Hay un recubriento a GTK para C++ llamado
|
|
GTK--, que proporciona una interfaz C++ a GTK; probablemente
|
|
debería empezar mirando ahí. Si no le gusta esa aproximación
|
|
al problema, por los motivos que sean, tiene dos
|
|
alternativas. Primero, puede ceñirse al subconjunto C de C++ cuando
|
|
realice alguna llamada a GTK a través de su interfaz en C. Segundo,
|
|
puede utilizar GTK y C++ al mismo tiempo declarando todas las
|
|
funciones respuesta como funciones estáticas en clases C++, y de
|
|
nuevo, llamar a GTK utilizando su interfaz C. Si elige esta última
|
|
forma de actuar, puede incluir como dato de la función respuesta un
|
|
puntero al objeto a manipular (el también llamado valor
|
|
«this»). La elección de una u otra opción es cuestión de
|
|
gustos personales, ya que de las tres maneras conseguirá utilizar
|
|
GTK en C++. Ninguna de estas aproximaciones requiere el uso de un
|
|
preprocesador especializado, por lo que sin importar la opción que
|
|
escoja podrá utilizar C++ estándar en C++.
|
|
|
|
Este tutorial es un intento de documentar GTK tanto como sea posible,
|
|
pero no está completo. Este tutorial asume un buen conocimiento de
|
|
C y de como crear programas bajo este lenguaje. Se verá beneficiado
|
|
si tiene un conocimiento previo de la programación en X, pero no
|
|
debería ser necesario. Si está aprendiendo GTK y es el primer
|
|
conjunto de <em/widgets/ que utiliza, por favor envíenos sus
|
|
comentarios sobre este tutorial y los problemas que ha
|
|
encontrado.
|
|
|
|
Este documento es un `trabajo pendiente de finalizar'. Para encontrar
|
|
actualizaciones mire en http://www.gtk.org/ <htmlurl
|
|
url="http://www.gtk.org/" name="http://www.gtk.org/">.
|
|
|
|
Me gustaría escuchar cualquier problema que le surja mientras
|
|
aprende GTK siguiendo este documento, y apreciaré cualquier
|
|
información sobre como mejorarlo. Por favor, vea la sección <ref
|
|
id="sec_Contributing" name="Contribuyendo"> para encontrar más
|
|
información.
|
|
|
|
<!-- ***************************************************************** -->
|
|
<sect>Comenzando
|
|
<!-- ***************************************************************** -->
|
|
|
|
<p>
|
|
Por supuesto lo primero que hay que hacer es descargar las fuentes de
|
|
GTK e instalarlas. La última versión siempre se puede obtener de
|
|
ftp.gtk.org (en el directorio /pub/gtk). En <htmlurl
|
|
url="http://www.gtk.org/" name="http://www.gtk.org/"> hay más
|
|
información sobre GTK. Para configurar GTK hay que usar GNU
|
|
autoconf. Una vez descomprimido se pueden obtener las opciones usando
|
|
<tt>./configure --help</tt>.
|
|
|
|
El código de GTK además contiene las fuentes completas de todos
|
|
los ejemplos usados en este manual, así como los makefiles para
|
|
compilarlos.
|
|
|
|
Para comenzar nuestra introducción a GTK vamos a empezar con el
|
|
programa más sencillo posible. Con él vamos a crear una ventana de
|
|
200x200 <em/pixels/ que sólo se puede destruir desde el shell.
|
|
|
|
<tscreen><verb>
|
|
/* principio del ejemplo base base.c */
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
int main (int argc, char *argv[])
|
|
{
|
|
GtkWidget *ventana;
|
|
|
|
gtk_init (&argc, &argv);
|
|
|
|
ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
|
gtk_widget_show (ventana);
|
|
|
|
gtk_main ();
|
|
|
|
return 0;
|
|
}
|
|
/* final del ejemplo */
|
|
</verb></tscreen>
|
|
|
|
Puede compilar el programa anterior con gcc tecleando:
|
|
<tscreen><verb>
|
|
gcc base.c -o base `gtk-config --cflags --libs`
|
|
</verb></tscreen>
|
|
|
|
El significado de la extraña opción de compilación se explica
|
|
más adelante.
|
|
|
|
Todo programa que use GTK debe llamar a <tt>gtk/gtk.h</tt> donde se
|
|
declaran todas las variables, funciones, estructuras, etc. que serán
|
|
usadas en el programa.
|
|
|
|
La siguiente línea:
|
|
|
|
<tscreen><verb>
|
|
gtk_init (&argc, &argv);
|
|
</verb></tscreen>
|
|
|
|
Llama a la función gtk_init (gint *argc, gchar *** argv) responsable
|
|
de `arrancar' la biblioteca y de establecer algunos parámetros (como son
|
|
los colores y los visuales por defecto), llama a gdk_init (gint *argc,
|
|
gchar *** argv) que inicializa la biblioteca para que pueda
|
|
utilizarse, establece los controladores de las señales y comprueba los
|
|
argumentos pasados a la aplicación desde la línea de comandos,
|
|
buscando alguno de los siguientes:
|
|
|
|
<itemize>
|
|
<item> <tt/--gtk-module/
|
|
<item> <tt/--g-fatal-warnings/
|
|
<item> <tt/--gtk-debug/
|
|
<item> <tt/--gtk-no-debug/
|
|
<item> <tt/--gdk-debug/
|
|
<item> <tt/--gdk-no-debug/
|
|
<item> <tt/--display/
|
|
<item> <tt/--sync/
|
|
<item> <tt/--no-xshm/
|
|
<item> <tt/--name/
|
|
<item> <tt/--class/
|
|
</itemize>
|
|
|
|
En el caso de que encuentre alguno lo quita de la lista, dejando todo
|
|
aquello que no reconozca para que el programa lo utilice o lo
|
|
ignore. Así se consigue crear un conjunto de argumentos que son
|
|
comunes a todas las aplicaciones basadas en GTK.
|
|
|
|
Las dos líneas de código siguientes crean y muestran una ventana.
|
|
|
|
<tscreen><verb>
|
|
ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
|
gtk_widget_show (ventana);
|
|
</verb></tscreen>
|
|
|
|
El argumento GTK_WINDOW_TOPLEVEL especifica que queremos que el gestor
|
|
de ventanas decore y sitúe la ventana. En lugar de crear una ventana
|
|
de tamaño 0 x 0 toda ventana sin hijos por defecto es de 200 x 200, con
|
|
lo que se consigue que pueda ser manipulada.
|
|
|
|
La función gtk_widget_show() le comunica a GTK que hemos acabado de
|
|
especificar los atributos del <em/widget/, y que por tanto puede
|
|
mostrarlo.
|
|
|
|
La última línea comienza el proceso del bucle principal de GTK.
|
|
|
|
<tscreen><verb>
|
|
gtk_main ();
|
|
</verb></tscreen>
|
|
|
|
Otra llamada que siempre está presente en cualquier aplicación es
|
|
gtk_main(). Cuando el control llega a ella, GTK se queda dormido
|
|
esperando a que suceda algún tipo de evento de las X (como puede ser
|
|
pulsar un botón), que pase el tiempo necesario para que el usuario
|
|
haga algo, o que se produzcan notificaciones de IO de archivos. En
|
|
nuestro caso concreto todos los eventos serán ignorados.
|
|
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Programa «Hola Mundo» en GTK
|
|
<p>
|
|
El siguiente ejemplo es un programa con un <em/widget/ (un
|
|
botón). Simplemente es la versión de GTK del clásico «hola mundo».
|
|
|
|
<tscreen><verb>
|
|
/* comienzo del ejemplo holamundo */
|
|
#include <gtk/gtk.h>
|
|
|
|
/* Ésta es una función respuesta (callback). Sus argumentos
|
|
son ignorados por en este ejemplo */
|
|
void hello (GtkWidget *widget, gpointer data)
|
|
{
|
|
g_print ("Hola mundo\n");
|
|
}
|
|
|
|
gint delete_event(GtkWidget *widget, GdkEvent *event, gpointer data)
|
|
{
|
|
/* si se devuelve FALSE al administrador de llamadas
|
|
* "delete_event", GTK emitirá la señal de destrucción
|
|
* "destroy". Esto es útil para diálogos emergentes del
|
|
* tipo: ¿Seguro que desea salir?
|
|
|
|
g_print ("Ha ocurrido un evento delete\n");
|
|
|
|
/* Cambiando TRUE por FALSE la ventana se destruirá con
|
|
* "delete_event"*/
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
/* otra respuesta */
|
|
void destroy (GtkWidget *widget, gpointer data)
|
|
{
|
|
gtk_main_quit ();
|
|
}
|
|
|
|
int main (int argc, char *argv[])
|
|
{
|
|
|
|
/* GtkWidget es el tipo de almacenamiento usado para los
|
|
* widgets */
|
|
GtkWidget *ventana;
|
|
GtkWidget *boton;
|
|
|
|
/* En cualquier aplicación hay que realizar la siguiente
|
|
* llamada. Los argumentos son tomados de la línea de comandos
|
|
* y devueltos a la aplicación. */
|
|
|
|
gtk_init (&argc, &argv);
|
|
|
|
/* creamos una ventana nueva */
|
|
ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
|
|
|
/* Cuando la ventana recibe la señal "delete_event" (emitida
|
|
* por el gestor de ventanas, normalmente mediante la opción
|
|
* 'close', o en la barra del título) hacemos que llame a la
|
|
* función delete_event() tal y como ya hemos visto. Los datos
|
|
* pasados a la función de respuesta son NULL, y serán ignorados. */
|
|
gtk_signal_connect (GTK_OBJECT (ventana), "delete_event",
|
|
GTK_SIGNAL_FUNC (delete_event), NULL);
|
|
|
|
/* Aquí conectamos el evento "destroy" con el administrador de
|
|
* señales. El evento se produce cuando llamamos a
|
|
* gtk_widget_destroy() desde la ventana o si devolvemos 'FALSE'
|
|
* en la respuesta "delete_event". */
|
|
gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
|
|
GTK_SIGNAL_FUNC (destroy), NULL);
|
|
|
|
/* establecemos el ancho del borde de la ventana. */
|
|
gtk_container_border_width (GTK_CONTAINER (ventana), 10);
|
|
|
|
/* creamos un botón nuevo con la etiqueta "Hola mundo" */
|
|
boton = gtk_button_new_with_label ("Hola mundo");
|
|
|
|
/* Cuando el botón recibe la señal "clicked" llama a la
|
|
* función hello() pasándole NULL como argumento. (La
|
|
* función ya ha sido definida arriba). */
|
|
gtk_signal_connect (GTK_OBJECT (boton), "clicked",
|
|
GTK_SIGNAL_FUNC (hello), NULL);
|
|
|
|
/* Esto hará que la ventana sea destruida llamando a
|
|
* gtk_widget_destroy(ventana) cuando se produzca "clicked". Una
|
|
* vez mas la señal de destrucción puede provenir del gestor
|
|
* de ventanas o de aquí. */
|
|
gtk_signal_connect_object (GTK_OBJECT (boton), "clicked",
|
|
GTK_SIGNAL_FUNC (gtk_widget_destroy),
|
|
GTK_OBJECT (ventana));
|
|
|
|
/* Ahora empaquetamos el botón en la ventana (usamos un gtk
|
|
* container ). */
|
|
gtk_container_add (GTK_CONTAINER (ventana), boton);
|
|
|
|
/* El último paso es representar el nuevo widget... */
|
|
gtk_widget_show (boton);
|
|
|
|
/* y la ventana */
|
|
gtk_widget_show (ventana);
|
|
|
|
/* Todas las aplicaciones basadas en GTK deben tener una llamada
|
|
* gtk_main() ya que el control termina justo aquí y debe
|
|
* esperar a que suceda algún evento */
|
|
|
|
gtk_main ();
|
|
|
|
return 0;
|
|
}
|
|
/* final del ejemplo*/
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Compilando Hello World
|
|
<p>
|
|
Para compilar el ejemplo hay que usar:
|
|
|
|
<tscreen><verb>
|
|
gcc -Wall -g helloworld.c -o hello_world `gtk-config --cflags` \
|
|
`gtk-config --libs`
|
|
</verb></tscreen>
|
|
|
|
Usamos el programa <tt>gtk-config</>, que ya viene (y se instala) con
|
|
la biblioteca. Es muy útil porque `conoce' que opciones son
|
|
necesarias para compilar programas que usen gtk. <tt>gtk-config
|
|
--cflags</tt> dará una lista con los directorios donde el
|
|
compilador debe buscar ficheros «include». A su vez <tt>gtk-config
|
|
--libs</tt> nos permite saber las bibliotecas que el compilador
|
|
intentará enlazar y dónde buscarlas.
|
|
|
|
Hay que destacar que las comillas simples en la orden de
|
|
compilación son absolutamente necesarias.
|
|
|
|
Las bibliotecas que se enlazan normalmente son:
|
|
|
|
<itemize>
|
|
|
|
<item>La biblioteca GTK (-lgtk), la biblioteca de <em/widgets/ que se
|
|
encuentra encima de GDK.
|
|
|
|
<item>La biblioteca GDK (-lgdk), el wrapper de Xlib.
|
|
|
|
<item>La biblioteca glib (-lglib), que contiene diversas funciones. En
|
|
nuestro ejemplo sólo hemos usado g_print(). GTK está construida
|
|
encima de glib por lo que simpre se usará. Vea la sección <ref
|
|
id="sec_glib" name="glib"> para más detalles.
|
|
|
|
<item>La biblioteca Xlib (-lX11) que es usada por GDK.
|
|
|
|
<item> La biblioteca Xext (-lXext) contiene código para
|
|
<em/pixmaps/ de memoria compartida y otras extensiones.
|
|
|
|
<item>La biblioteca matemática (-lm). Es usada por GTK para
|
|
diferentes cosas.
|
|
|
|
</itemize>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Teoría de señales y respuestas
|
|
<p>
|
|
Antes de profundizar en <tt/holamundo/ vamos a discutir las
|
|
señales y las respuestas. GTK es un toolkit (conjunto de
|
|
herramientas) gestionadas mediante eventos. Esto quiere decir que GTK
|
|
«duerme» en gtk_main hasta que se recibe un evento, momento en el
|
|
cual se transfiere el control a la función adecuada.
|
|
|
|
El control se transfiere mediante «señales». (Conviene destacar
|
|
que las señales de GTK no son iguales que las de los sistemas
|
|
UNIX, aunque la terminología es la misma.) Cuando sucede un evento,
|
|
como por ejemplo la pulsación de un botón, se «emitirá» la
|
|
señal apropiada por el <em/widget/ pulsado. Así es como GTK
|
|
proporciona la mayor parte de su utilidad. Hay un conjunto de
|
|
señales que todos los <em/widgets/ heredan, como por ejemplo
|
|
«destroy» y hay señales que son específicas de cada
|
|
<em/widget/, como por ejemplo la señal «toggled» de un botón
|
|
de selección (botón <em/toggle/).
|
|
|
|
Para que un botón haga algo crearemos un controlador que se encarga de
|
|
recoger las señales y llamar a la función apropiada. Esto se hace
|
|
usando una función como:
|
|
|
|
<tscreen><verb>
|
|
gint gtk_signal_connect( GtkObject *objeto,
|
|
gchar *nombre,
|
|
GtkSignalFunc func,
|
|
gpointer datos_func );
|
|
</verb></tscreen>
|
|
|
|
Donde el primer argumento es el <em/widget/ que emite la señal, el
|
|
segundo el nombre de la señal que queremos `cazar', el tercero es
|
|
la función a la que queremos que se llame cuando se `cace' la
|
|
señal y el cuarto los datos que queremos pasarle a esta función.
|
|
|
|
La función especificada en el tercer argumento se denomina «función
|
|
de respuesta» y debe tener la forma siguiente:
|
|
|
|
<tscreen><verb>
|
|
void callback_func( GtkWidget *widget,
|
|
gpointer datos_respuesta );
|
|
</verb></tscreen>
|
|
|
|
Donde el primer argumento será un puntero al <em/widget/ que emitió la
|
|
señal, y el segundo un puntero a los datos pasados a la función tal y
|
|
como hemos visto en el último argumento a gtk_signal_connect().
|
|
|
|
Conviene destacar que la declaración de la función de respuesta debe
|
|
servir sólo como guía general, ya que algunas señales específicas
|
|
pueden generar diferentes parámetros de llamada. Por ejemplo, la señal
|
|
de GtkCList «select_row» proporciona los parámetros fila y columna.
|
|
|
|
Otra llamada usada en el ejemplo del hola mundo es:
|
|
|
|
<tscreen><verb>
|
|
gint gtk_signal_connect_object( GtkObject *objeto,
|
|
gchar *nombre,
|
|
GtkSignalFunc func,
|
|
GtkObject *slot_object );
|
|
</verb></tscreen>
|
|
|
|
gtk_signal_connect_object() es idéntica a gtk_signal_connect() excepto
|
|
en que la función de llamada sólo usa un argumento, un puntero a un
|
|
objeto GTK. Por tanto cuando usemos esta función para conectar
|
|
señales, la función de respuesta debe ser de la forma:
|
|
|
|
<tscreen><verb>
|
|
void callback_func( GtkObject *object );
|
|
</verb></tscreen>
|
|
|
|
Donde, por regla general, el objeto es un <em/widget/. Sin embargo no
|
|
es normal establecer una respuesta para gtk_signal_connect_object. En
|
|
lugar de ello llamamos a una función de GTK que acepte un <em/widget/
|
|
o un objeto como un argumento, tal y como se vio en el ejemplo hola
|
|
mundo.
|
|
|
|
¿Para qué sirve tener dos funciones para conectar señales? Simplemente
|
|
para permitir que las funciones de respuesta puedan tener un número
|
|
diferente de argumentos. Muchas funciones de GTK sólo aceptan un
|
|
puntero a un GtkWidget como argumento, por lo que tendrá que usar
|
|
gtk_signal_connect_object() con estas funciones, mientras que
|
|
probablemente tenga que suministrarle información adicional a sus
|
|
funciones.
|
|
|
|
<!-- XXX Completamente revisado hasta aquí -------------------------- -->
|
|
<sect1>Eventos
|
|
<p>
|
|
Además del mecanismo de señales descrito arriba existe otro conjunto
|
|
de <em>eventos</em> que reflejan como las X manejan los eventos. Se
|
|
pueden asignar funciones de respuesta a estos eventos. Los eventos
|
|
son:
|
|
|
|
<itemize>
|
|
<item> event
|
|
<item> button_press_event
|
|
<item> button_release_event
|
|
<item> motion_notify_event
|
|
<item> delete_event
|
|
<item> destroy_event
|
|
<item> expose_event
|
|
<item> key_press_event
|
|
<item> key_release_event
|
|
<item> enter_notify_event
|
|
<item> leave_notify_event
|
|
<item> configure_event
|
|
<item> focus_in_event
|
|
<item> focus_out_event
|
|
<item> map_event
|
|
<item> unmap_event
|
|
<item> property_notify_event
|
|
<item> selection_clear_event
|
|
<item> selection_request_event
|
|
<item> selection_notify_event
|
|
<item> proximity_in_event
|
|
<item> proximity_out_event
|
|
<item> drag_begin_event
|
|
<item> drag_request_event
|
|
<item> drag_end_event
|
|
<item> drop_enter_event
|
|
<item> drop_leave_event
|
|
<item> drop_data_available_event
|
|
<item> other_event
|
|
</itemize>
|
|
|
|
Para conectar una función de respuesta a alguno de los eventos
|
|
anteriores debe usar la función gtk_signal_connect, tal y como se
|
|
descrivió anteriormente, utilizando en el parámetro <tt/name/ uno de
|
|
los nombres de los eventos que se acaban de mencionar. La función de
|
|
respuesta para los eventos tiene un forma ligeramente diferente de la
|
|
que tiene para las señales:
|
|
|
|
<tscreen><verb>
|
|
void callback_func( GtkWidget *widget,
|
|
GdkEvent *event,
|
|
gpointer callback_data );
|
|
</verb></tscreen>
|
|
|
|
GdkEvent es una estructura <tt/union/ cuyo tipo depende de cual de los
|
|
eventos anteriores haya ocurrido. Para que podamos decir que evento se
|
|
ha lanzado cada una de las posibles alternativas posee un parámetro
|
|
<tt/type/ que refleja cual es el evento en cuestión. Los otros
|
|
componentes de la estructura dependerán del tipo de evento. Algunos
|
|
valores posibles son:
|
|
|
|
<tscreen><verb>
|
|
GDK_NOTHING
|
|
GDK_DELETE
|
|
GDK_DESTROY
|
|
GDK_EXPOSE
|
|
GDK_MOTION_NOTIFY
|
|
GDK_BUTTON_PRESS
|
|
GDK_2BUTTON_PRESS
|
|
GDK_3BUTTON_PRESS
|
|
GDK_BUTTON_RELEASE
|
|
GDK_KEY_PRESS
|
|
GDK_KEY_RELEASE
|
|
GDK_ENTER_NOTIFY
|
|
GDK_LEAVE_NOTIFY
|
|
GDK_FOCUS_CHANGE
|
|
GDK_CONFIGURE
|
|
GDK_MAP
|
|
GDK_UNMAP
|
|
GDK_PROPERTY_NOTIFY
|
|
GDK_SELECTION_CLEAR
|
|
GDK_SELECTION_REQUEST
|
|
GDK_SELECTION_NOTIFY
|
|
GDK_PROXIMITY_IN
|
|
GDK_PROXIMITY_OUT
|
|
GDK_DRAG_BEGIN
|
|
GDK_DRAG_REQUEST
|
|
GDK_DROP_ENTER
|
|
GDK_DROP_LEAVE
|
|
GDK_DROP_DATA_AVAIL
|
|
GDK_CLIENT_EVENT
|
|
GDK_VISIBILITY_NOTIFY
|
|
GDK_NO_EXPOSE
|
|
GDK_OTHER_EVENT /* En desuso, usar filtros en lugar de ella */
|
|
</verb></tscreen>
|
|
|
|
Por lo tanto para conectar una función de respuesta a uno de estos
|
|
eventos debemos usar algo como:
|
|
|
|
<tscreen><verb>
|
|
gtk_signal_connect( GTK_OBJECT(boton), "button_press_event",
|
|
GTK_SIGNAL_FUNC(button_press_callback),
|
|
NULL);
|
|
</verb></tscreen>
|
|
|
|
Por supuesto se asume que <tt/boton/ es un <em/widget/
|
|
GtkButton. Cada vez que el puntero del ratón se encuentre sobre el
|
|
botón y éste sea presionado, se llamará a la función
|
|
<tt/button_press_callback/. Esta función puede declararse así:
|
|
|
|
<tscreen><verb>
|
|
static gint button_press_event (GtkWidget *widget,
|
|
GdkEventButton *event,
|
|
gpointer data);
|
|
</verb></tscreen>
|
|
|
|
Conviene destacar que se puede declarar el segundo argumento como
|
|
<tt/GdkEventButton/ porque sabemos que este tipo de evento ocurrirá
|
|
cuando se llame a la función.
|
|
|
|
El valor devuelto por esta función es usado para saber si el evento
|
|
debe ser propagado a un nivel más profundo dentro del mecanismo de
|
|
GTK para gestionar los eventos. Si devuelve TRUE el evento ya ha sido
|
|
gestionado y por tanto no tiene que ser tratado por el mecanismo de
|
|
gestión. Por contra si devuelve FALSE se continua con la gestión
|
|
normal del evento. Para más detalles se recomienda leer la sección
|
|
donde se aclara como se produce el proceso de propagación.
|
|
|
|
Para más detalles acerca de los tipos de información GdkEvent
|
|
consultar el apéndice <ref id="sec_GDK_Event_Types" name="Tipos de
|
|
eventos GDK">.
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Aclaración de Hello World
|
|
<p>
|
|
Ahora que conocemos la teoría vamos a aclarar las ideas estudiando
|
|
en detalle el programa <tt/helloworld/.
|
|
|
|
Ésta es la función respuesta a la que se llamará cuando se
|
|
pulse el botón. En el ejemplo ignoramos tanto el <em/widget/ como
|
|
la información, pero no es difícil usarlos. El siguiente ejemplo
|
|
usará la información que recibe como argumento para decirnos que
|
|
botón fue presionado.
|
|
|
|
<tscreen><verb>
|
|
void hello (GtkWidget *widget, gpointer data)
|
|
{
|
|
g_print ("Hello World\n");
|
|
}
|
|
</verb></tscreen>
|
|
|
|
La siguiente respuesta es un poco especial, el «delete_event» ocurre
|
|
cuando el gestor de ventanas envía este evento a la aplicación. Aquí
|
|
podemos decidir que hacemos con estos eventos. Los podemos ignorar,
|
|
dar algún tipo de respuesta, o simplemente terminar la aplicación.
|
|
|
|
El valor devuelto en esta respuesta le permite a GTK saber que tiene
|
|
que hacer. Si devolvemos TRUE, estamos diciendo que no queremos que se
|
|
emita la señal «destroy» y por lo tanto queremos que nuestra
|
|
aplicación siga ejecutándose. Si devolvemos FALSE, decimos que
|
|
se emita «destroy», lo que hará que se ejecute nuestro manejador
|
|
de señal de «destroy».
|
|
|
|
<tscreen><verb>
|
|
gint delete_event(GtkWidget *widget, GdkEvent *event, gpointer data)
|
|
{
|
|
g_print ("delete event occured\n");
|
|
|
|
return (TRUE);
|
|
}
|
|
</verb></tscreen>
|
|
|
|
Con el siguiente ejemplo presentamos otra función de respuesta que hace
|
|
que el programa salga llamando a gtk_main_quit(). Con esta función le
|
|
decimos a GTK que salga de la rutina gtk_main() cuando vuelva a estar
|
|
en ella.
|
|
|
|
<tscreen><verb>
|
|
void destroy (GtkWidget *widget, gpointer data)
|
|
{
|
|
gtk_main_quit ();
|
|
}
|
|
</verb></tscreen>
|
|
|
|
Como el lector probablemente ya sabe toda aplicación debe tener una
|
|
función main(), y una aplicación GTK no va a ser menos. Todas las
|
|
aplicaciones GTK también tienen una función de este tipo.
|
|
|
|
<tscreen><verb>
|
|
int main (int argc, char *argv[])
|
|
</verb></tscreen>
|
|
|
|
Las líneas siguientes declaran un puntero a una estructura del tipo
|
|
GtkWidget, que se utilizarán más adelante para crear una ventana y un
|
|
botón.
|
|
|
|
<tscreen><verb>
|
|
GtkWidget *ventana;
|
|
GtkWidget *boton;
|
|
</verb></tscreen>
|
|
|
|
Aquí tenemos otra vez a gtk_init. Como antes arranca el conjunto de
|
|
herramientas y filtra las opciones introducidas en la línea de
|
|
órdenes. Cualquier argumento que sea reconocido será borrado de la
|
|
lista de argumentos, de modo que la aplicación recibirá el resto.
|
|
|
|
<tscreen><verb>
|
|
gtk_init (&argc, &argv);
|
|
</verb></tscreen>
|
|
|
|
Ahora vamos a crear una ventana. Simplemente reservamos memoria para
|
|
la estructura GtkWindow *ventana, con lo que ya tenemos una nueva
|
|
ventana, ventana que no se mostrará hasta que llamemos a
|
|
gtk_widget_show (ventana) hacia el final del programa.
|
|
|
|
<tscreen><verb>
|
|
ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
|
</verb></tscreen>
|
|
|
|
Aquí tenemos un ejemplo de como conectar un manejador de señal a un
|
|
objeto, en este caso, la ventana. La señal a cazar será
|
|
«destroy». Esta señal se emite cuando utilizamos el administrador de
|
|
ventanas para matar la ventana (y devolvemos TRUE en el manejador
|
|
«delete_event»), o cuando usamos llamamos a gtk_widget_destroy()
|
|
pasándole el <em/widget/ que representa la ventana como argumento.
|
|
Así conseguimos manejar los dos casos con una simple llamada a la
|
|
función destroy () (definida arriba) pasándole NULL como argumento y
|
|
ella acabará con la aplicación por nosotros.
|
|
|
|
GTK_OBJECT y GTK_SIGNAL_FUNC son macros que realizan la comprobación y
|
|
transformación de tipos por nosotros. También aumentan la legibilidad
|
|
del código.
|
|
|
|
<tscreen><verb>
|
|
gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
|
|
GTK_SIGNAL_FUNC (destroy), NULL);
|
|
</verb></tscreen>
|
|
|
|
La siguiente función establece un atributo a un objeto contenedor
|
|
(discutidos luego). En este caso le pone a la ventana un área
|
|
negra de 10 <em/pixels/ de ancho donde no habrán <em/widgets/. Hay
|
|
funciones similares que serán tratadas con más detalle en la sección
|
|
<ref id="sec_setting_widget_attributes" name="Estableciendo los
|
|
atributos de los <em/widgets/">
|
|
|
|
De nuevo, GTK_CONTAINER es una macro que se encarga de la conversión
|
|
entre tipos
|
|
|
|
<tscreen><verb>
|
|
gtk_container_border_width (GTK_CONTAINER (ventana), 10);
|
|
</verb></tscreen>
|
|
|
|
La siguiente llamada crea un nuevo botón. Reserva espacio en la
|
|
memoria para una nueva estructura del tipo GtkWidget, la inicializa
|
|
y hace que el puntero <tt/boton/ apunte a esta estructura. Su etiqueta
|
|
será: "Hola mundo".
|
|
|
|
<tscreen><verb>
|
|
boton = gtk_button_new_with_label ("Hola mundo");
|
|
</verb></tscreen>
|
|
|
|
Ahora hacemos que el botón sea útil, para ello enlazamos el botón con
|
|
el manejador de señales para que cuando emita la señal «clicked», se
|
|
llame a nuestra función hola(). Los datos adicionales serán
|
|
ignorados, por lo que simplemente le pasaremos NULL a la función
|
|
respuesta. Obviamente se emitirá la señal «clicked» cuando pulsemos
|
|
en el botón con el ratón.
|
|
|
|
<tscreen><verb>
|
|
gtk_signal_connect (GTK_OBJECT (boton), "clicked",
|
|
GTK_SIGNAL_FUNC (hola), NULL);
|
|
</verb></tscreen>
|
|
XXX
|
|
Ahora vamos a usar el botón para terminar nuestro programa. Así
|
|
aclararemos cómo es posible que la señal «destroy» sea emitida tanto
|
|
por el gestor de ventanas como por nuestro programa. Cuando el botón
|
|
es pulsado, al igual que arriba, se llama a la primera función
|
|
respuesta hello() y después se llamará a esta función. Las funciones
|
|
respuesta serán ejecutadas en el orden en que sean conectadas. Como la
|
|
función gtk_widget_destroy() sólo acepta un GtkWidget como argumento,
|
|
utilizaremos gtk_signal_connect_object() en lugar de
|
|
gtk_signal_connect().
|
|
|
|
<tscreen><verb>
|
|
gtk_signal_connect_object (GTK_OBJECT (boton), "clicked",
|
|
GTK_SIGNAL_FUNC (gtk_widget_destroy),
|
|
GTK_OBJECT (ventana));
|
|
</verb></tscreen>
|
|
|
|
La siguiente llamada sirve para empaquetar (más detalles luego). Se
|
|
usa para decirle a GTK que el botón debe estar en la ventana dónde
|
|
será mostrado. Conviene destacar que un contenedor GTK sólo puede
|
|
contener un <em/widget/. Existen otros <em/widgets/ (descritos
|
|
después) que sirven para contener y establecer la disposición de
|
|
varios <em/widgets/ de diferentes formas.
|
|
|
|
<tscreen><verb>
|
|
gtk_container_add (GTK_CONTAINER (ventana), boton);
|
|
</verb></tscreen>
|
|
|
|
Ahora ya tenemos todo bien organizado. Como todos los controladores de
|
|
las señales ya están en su sitio, y el botón está situado en la
|
|
ventana donde queremos que esté, sólo nos queda pedirle a GTK que
|
|
muestre todos los <em/widgets/ en pantalla. El <em/widget/ ventana será
|
|
el último en mostrarse queremos que aparezca todo de golpe, en vez de
|
|
ver aparecer la ventana, y después ver aparecer el botón. De todas
|
|
formas con un ejemplo tan simple nunca se notaría cual es el orden de
|
|
aparición.
|
|
|
|
<tscreen><verb>
|
|
gtk_widget_show (boton);
|
|
|
|
gtk_widget_show (ventana);
|
|
</verb></tscreen>
|
|
|
|
Llamamos a gtk_main() que espera hasta que el servidor X le comunique
|
|
que se ha producido algún evento para emitir las señales apropiadas.
|
|
|
|
<tscreen><verb>
|
|
gtk_main ();
|
|
</verb></tscreen>
|
|
|
|
Por último el `return' final que devuelve el control cuando gtk_quit()
|
|
sea invocada.
|
|
|
|
<tscreen><verb>
|
|
return 0;
|
|
</verb></tscreen>
|
|
|
|
Cuando pulsemos el botón del ratón el <em/widget/ emite la señal
|
|
correspondiente «clicked». Para que podamos usar la información el
|
|
programa activa el gestor de eventos que al recibir la señal llama a
|
|
la función que hemos elegido. En nuestro ejemplo cuando pulsamos el
|
|
botón se llama a la función hello() con NULL como argumento y además
|
|
se invoca al siguiente manipulador de señal. Así conseguimos que se
|
|
llame a la función gtk_widget_destroy() con el <em/widget/ asociado a
|
|
la ventana como argumento, lo que destruye al <em/widget/. Esto hace
|
|
que la ventana emita la señal «destroy», que es cazada, y que llama
|
|
a nuestra función respuesta destroy(), que simplemente sale de GTK.
|
|
|
|
Otra posibilidad es usar el gestor de ventanas para acabar con la
|
|
aplicación. Esto emitirá «delete_event» que hará que se
|
|
llame a nuestra función manejadora correspondiente. Si en la
|
|
función manejadora «delete_event» devolvemos TRUE la ventana se
|
|
quedará como si nada hubiese ocurrido, pero si devolvemos FALSE GTK
|
|
emitirá la señal «destroy» que, por supuesto, llamará a la
|
|
función respuesta «destroy», que saldrá de GTK.
|
|
|
|
<!-- ***************************************************************** -->
|
|
<sect>Avanzando
|
|
<!-- ***************************************************************** -->
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Tipos de datos
|
|
<p>
|
|
Existen algunos detalles de los ejemplos anteriores que hay que aclarar.
|
|
Los tipos gint, gchar, etc. que puede ver por ahí son typedefs a int y
|
|
a char respectivamente. Sirven para que no haya que tener en cuenta el
|
|
tamaño de cada uno de ellos a la hora de hacer cálculos.
|
|
|
|
Un buen ejemplo es <tt/gint32/ que es un entero de 32 bits independientemente
|
|
de la plataforma, bien sea un Alpha de 64 bits o un i386 de 32. Todas las
|
|
definiciones son muy intuitivas y se encuentran definidas en glib/glib.h
|
|
(que se incluye desde gtk.h).
|
|
|
|
Probablemente el lector se haya dado cuenta de que se puede usar GtkWidget
|
|
cuando la función llama a un GtkObject. Esto es debido a que GTK
|
|
está orienta a objetos y un <em/widget/ es un GtkObject.
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Más sobre el manejo de señales
|
|
<p>
|
|
Si estudiamos en mayor profundidad la declaración de
|
|
gtk_signal_connect:
|
|
|
|
<tscreen><verb>
|
|
gint gtk_signal_connect( GtkObject *object,
|
|
gchar *name,
|
|
GtkSignalFunc func,
|
|
gpointer func_data );
|
|
</verb></tscreen>
|
|
|
|
Podemos darnos cuenta de que el valor devuelto es del tipo gint. Este
|
|
valor es una etiqueta que identifica a la función de respuesta. Tal
|
|
y como ya vimos podemos tener tantas funciones de respuesta por
|
|
seÑal y objeto como sean necesarias, y cada una de ellas se
|
|
ejecutará en el mismo orden en el que fueron enlazadas.
|
|
|
|
Esta etiqueta nos permite eliminar la función respuesta de la lista
|
|
usando:
|
|
|
|
<tscreen><verb>
|
|
void gtk_signal_disconnect( GtkObject *object,
|
|
gint id );
|
|
</verb></tscreen>
|
|
|
|
Por lo tanto podemos desconectar un manejador de señal pasándole
|
|
a la función anterior el <em/widget/ del que queremos desconectar y
|
|
la etiqueta o id devuelta por una de las funciones signal_connect.
|
|
|
|
Otra función que se usa para quitar desconectar todos los
|
|
controladores de un objeto es:
|
|
|
|
<tscreen><verb>
|
|
void gtk_signal_handlers_destroy( GtkObject *object );
|
|
</verb></tscreen>
|
|
|
|
Esta llamada es bastante auto explicativa. Simplemente quitamos todos los
|
|
controladores de señales del objeto que pasamos como primer argumento.
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Un Hello World mejorado.
|
|
<p>
|
|
Vamos a mejorar el ejemplo para obtener una visión más amplia
|
|
sobre el manejo de señales y respuestas. También introduciremos
|
|
los <em/widgets/ usados para empaquetar.
|
|
|
|
<tscreen><verb>
|
|
/* principio del ejemplo helloworld2 */
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
/* Nuestra respuesta mejorada. Los argumentos de la función se
|
|
* imprimen en el stdout.*/
|
|
void callback (GtkWidget *widget, gpointer data)
|
|
{
|
|
g_print ("Hello again - %s was pressed\n", (char *) data);
|
|
}
|
|
|
|
/* otra respuesta*/
|
|
void delete_event (GtkWidget *widget, GdkEvent *event, gpointer data)
|
|
{
|
|
gtk_main_quit ();
|
|
}
|
|
|
|
int main (int argc, char *argv[])
|
|
{
|
|
/* GtkWidget es el tipo de almacenamiento usado para los wigtes*/
|
|
GtkWidget *ventana;
|
|
GtkWidget *boton;
|
|
GtkWidget *caja1;
|
|
|
|
/* Esta llamada está presente en todas las aplicaciones basadas
|
|
* en GTK. Los argumentos introducidos a la aplicación*/
|
|
gtk_init (&argc, &argv);
|
|
|
|
/* creamos una nueva ventana*/
|
|
ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
|
|
|
/* Esta función es nueva, pone como título de la ventana
|
|
* "¡Hola botones!"*/
|
|
|
|
gtk_window_set_title (GTK_WINDOW (ventana), "¡Hola botones!");
|
|
|
|
/* Establecemos el controlador para la llamada delete_event que
|
|
* termina la aplicación inmediatamente. */
|
|
|
|
gtk_signal_connect (GTK_OBJECT (ventana), "delete_event",
|
|
GTK_SIGNAL_FUNC (delete_event), NULL);
|
|
|
|
|
|
/* Establecemos el ancho del borde de la ventana.*/
|
|
gtk_container_border_width (GTK_CONTAINER (ventana), 10);
|
|
|
|
/* Creamos una caja donde empaquetaremos los widgets. El
|
|
* procedimiento de empaquetamiento se describe en detalle en la
|
|
* sección correspondiente. La caja no se ve realmente, sólo
|
|
* sirve para introducir los widgets. */
|
|
caja1 = gtk_hbox_new(FALSE, 0);
|
|
|
|
/* ponemos la caja en la ventana principal */
|
|
gtk_container_add (GTK_CONTAINER (ventana), caja1);
|
|
|
|
/* Creamos un nuevo botón con la etiqueta "Botón 1". */
|
|
boton = gtk_button_new_with_label ("Botón 1");
|
|
|
|
/* Cada vez que el botón sea pulsado llamamos a la función
|
|
* "callback" con un puntero a "botón 1" como argumento. */
|
|
gtk_signal_connect (GTK_OBJECT (boton), "clicked",
|
|
GTK_SIGNAL_FUNC (callback), (gpointer) "botón 1");
|
|
|
|
/* En lugar de gtk_container_add empaquetamos el botón en la
|
|
* caja invisible, que a su vez ha sido empaquetado en la
|
|
* ventana. */
|
|
gtk_box_pack_start(GTK_BOX(caja1), boton, TRUE, TRUE, 0);
|
|
|
|
/* Siempre se debe realizar este paso. Sirve para decirle a GTK
|
|
* que los preparativos del botón ya se han finalizado y que
|
|
* por tanto puede ser mostrado. */
|
|
gtk_widget_show(boton);
|
|
|
|
/* hacemos lo mismo para crear un segundo botón. */
|
|
boton = gtk_button_new_with_label ("Botón 2");
|
|
|
|
/* Llamamos a la misma función de respuesta pero con diferente
|
|
* argumento: un puntero a "botón 2". */
|
|
gtk_signal_connect (GTK_OBJECT (boton), "clicked",
|
|
GTK_SIGNAL_FUNC (callback), (gpointer) "botón 2");
|
|
|
|
gtk_box_pack_start(GTK_BOX(caja1), boton, TRUE, TRUE, 0);
|
|
|
|
/* El orden en que mostramos los botones no es realmente
|
|
* importante, pero se recomienda mostrar la ventana la última
|
|
* para que todo aparezca de golpe. */
|
|
gtk_widget_show(boton);
|
|
|
|
gtk_widget_show(caja1);
|
|
|
|
gtk_widget_show (ventana);
|
|
|
|
/* Esperamos en gtk_main a que comience el espectáculo.*/
|
|
gtk_main ();
|
|
|
|
return 0;
|
|
}
|
|
/* final del ejemplo*/
|
|
</verb></tscreen>
|
|
|
|
Compile el programa usando los mismos argumentos que en el ejemplo
|
|
anterior. Probablemente ya se habrá dado cuenta de que no hay una
|
|
forma sencilla para terminar el programa, se debe usar el gestor de
|
|
ventanas o la línea de comandos para ello. Un buen ejercicio para
|
|
el lector es introducir un tercer botón que termine el
|
|
programa. También puede resultar interesante probar las diferentes
|
|
opciones de gtk_box_pack_start() mientras lee la siguiente
|
|
sección. Intente cambiar el tamaño de la ventana y observe el
|
|
comportamiento.
|
|
|
|
Como última nota, existe otra definición bastante útil:
|
|
gtk_widow_new() - GTK_WINDOW_DIALOG. Su comportamiento es un poco
|
|
diferente y debe ser usado para ventanas intermedias (cuadros de
|
|
diálogo).
|
|
|
|
<!-- ***************************************************************** -->
|
|
<sect><em/Widgets/ usados para empaquetar
|
|
<!-- ***************************************************************** -->
|
|
<p>
|
|
Al crear una aplicación normalmente se quiere que haya más de un
|
|
<em/widget/ por ventana. Nuestro primer ejemplo sólo usaba un
|
|
<em/widget/ por lo que usábamos la función gtk_container_add
|
|
para «empaquetar» el <em/widget/ en la ventana. Pero cuando cuando
|
|
se quiere poner más de un <em/widget/ en una ventana, ¿Cómo
|
|
podemos controlar donde aparecerá el <em/widget/?. Aquí es donde
|
|
entra el empaquetamiento.
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Empaquetamiento usando cajas
|
|
<p>
|
|
Normalmente para empaquetar se usan cajas, tal y como ya hemos
|
|
visto. Éstas son <em/widgets/ invisibles que pueden contener
|
|
nuestros <em/widgets/ de dos formas diferentes, horizontal o
|
|
verticalmente. Al hacerlo de la primera forma los objetos son
|
|
insertados de izquierda a derecha o al revés (dependiendo de que
|
|
llamada se use). Lo mismo ocurre en los verticales (de arriba a bajo o
|
|
al revés). Se pueden usar tantas cajas como se quieran para
|
|
conseguir cualquier tipo de efecto.
|
|
|
|
Para crear una caja horizontal llamamos a gtk_hbox_new() y para las
|
|
verticales gtk_vbox_new(). Las funciones usadas para introducir
|
|
objetos dentro son gtk_box_pack_start() y gtk_box_pack_end(). La
|
|
primera llenará de arriba a abajo o de izquierda a derecha. La
|
|
segunda lo hará al revés.
|
|
Usando estas funciones podemos ir metiendo <em/widgets/ con una
|
|
justificación a la izquierda o a la derecha y además podemos
|
|
mezclarlas de cualquier manera para conseguir el efecto
|
|
deseado. Nosotros usaremos gtk_box_pack_start() en la mayoria de
|
|
nuestros ejemplos. Un objeto puede ser otro contenedor o un
|
|
<em/widget/. De hecho, muchos <em/widgets/ son contenedores,
|
|
incluyendo el <em/widget/ botón (button) (aunque normalmente lo
|
|
único que meteremos dentro será una etiqueta de texto).
|
|
|
|
Mediante el uso de estas funciones le decimos a GTK dónde queremos
|
|
situar nuestros widgets, y GTK podrá, por ejemplo, cambiarles el
|
|
tamaño de forma automática y hacer otras cosas de
|
|
utilidad. También hay unas cuantas opciones que tienen que ver con
|
|
la forma en la que los <em/widgets/ serán empaquetados. Como puede
|
|
imaginarse, este método nos da una gran flexibilidad a la hora de
|
|
colocar y crear <em/widgets/.
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Detalles de la cajas.
|
|
<p>
|
|
Debido a esta flexibilidad el empaquetamiento puede ser confuso al
|
|
principio. Hay muchas opciones y no es obvio como encajan unas con
|
|
otras. Pero en la práctica sólo hay cinco estilos diferentes.
|
|
|
|
<? <CENTER> >
|
|
<?
|
|
<IMG SRC="gtk_tut_packbox1.gif" VSPACE="15" HSPACE="10" WIDTH="528"
|
|
HEIGHT="235" ALT="Imagen de ejemplo sobre el empaquetado con cajas">
|
|
>
|
|
<? </CENTER> >
|
|
|
|
Cada línea contiene una caja horizontal (hbox) con diferentes
|
|
botones. La llamada a gtk_box_pack es una manera de conseguir
|
|
empaquetar cada uno de los botones dentro de la caja. Eso sí, cada uno
|
|
de ellos se empaqueta de la misma forma que el resto (se llama con los
|
|
mismos argumentos a gtk_box_pack_start()).
|
|
|
|
Esta es la declaración de la función gtk_box_pack_start:
|
|
|
|
<tscreen><verb>
|
|
void gtk_box_pack_start( GtkBox *box,
|
|
GtkWidget *hijo,
|
|
gint expand,
|
|
gint fill,
|
|
gint padding );
|
|
</verb></tscreen>
|
|
|
|
El primer argumento es la caja dónde se empaqueta, el segundo el
|
|
objeto. Por ahora el objeto será un botón, ya que estamos
|
|
empaquetando botones dentro de las cajas.
|
|
|
|
El argumento <tt/expand/ de gtk_box_pack_start() y de
|
|
gtk_box_pack_end() controla si los <em/widgets/ son expandidos en la
|
|
caja para rellenar todo el espacio de la misma (TRUE) o si por el
|
|
contrario no se usa el espacio extra dentro de la caja
|
|
(FALSE). Poniendo FALSE en <em/expand/ podremos hacer que nuestros
|
|
<em/widgets/ tengan una justaficación a la derecha o a la
|
|
izquierda. En caso contrario, los <em/widgets/ se expandirán para
|
|
llenar toda la caja, y podemos conseguir el mismo efecto utilizando
|
|
sólo una de las funciones gtk_box_pack_start o pack_end.
|
|
|
|
El argumento <tt/fill/ de gtk_box controla si el espacio extra se mete
|
|
dentro de los objetos (TRUE) o como relleno extra (FALSE). Sólo
|
|
tiene efecto si el argumento de expansión también es TRUE.
|
|
|
|
Al crear una nueva ventana la función debe ser parecida a esta:
|
|
|
|
<tscreen><verb>
|
|
GtkWidget *gtk_hbox_new (gint homogeneous,
|
|
gint spacing);
|
|
</verb></tscreen>
|
|
|
|
El argumento <tt/homogeneous/ (tanto para gtk_hbox_new como para
|
|
gtk_vbox_new) controla si cada objeto en la caja tiene el mismo
|
|
tamaño (anchura en una hbox o altura en una vbox). Si se activa, el
|
|
argumento <tt/expand/ de las rutinas gtk_box_pack siempre estará
|
|
activado.
|
|
|
|
Puede que el lector se esté haciendo la siguiente pregunta:
|
|
¿Cúal es la diferencia entre espaciar (establecido cuando se
|
|
crea la caja) y rellenar (determinado cuando se empaquetan los
|
|
elementos)? El espaciado se añade entre objetos, y el rellenado se
|
|
hace en cada parte de cada objeto. La siguiente figura debe aclarar la
|
|
cuestión.
|
|
|
|
<? <CENTER> >
|
|
<?
|
|
<IMG ALIGN="center" SRC="gtk_tut_packbox2.gif" WIDTH="509" HEIGHT="213"
|
|
VSPACE="15" HSPACE="10" ALT="Imagen de ejemplo sobre el empaquetado con cajas">
|
|
>
|
|
<? </CENTER> >
|
|
|
|
Estudiemos el código usado para crear las imágenes
|
|
anteriores. Con los comentarios no debería de haber ningún
|
|
problema para entenderlo.
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Programa demostración de empaquetamiento
|
|
<p>
|
|
<tscreen><verb>
|
|
/* principio del ejemplo packbox packbox.c */
|
|
|
|
#include <stdio.h>
|
|
#include "gtk/gtk.h"
|
|
|
|
void
|
|
delete_event (GtkWidget *widget, GdkEvent *event, gpointer data)
|
|
{
|
|
gtk_main_quit ();
|
|
}
|
|
|
|
/* Hacemos una hbox llena de etiquetas de botón. Los argumentos
|
|
* para las variables que estamos interesados son pasados a esta
|
|
* función. No mostramos la caja, pero hacemos todo lo que
|
|
* queremos. */
|
|
GtkWidget *make_box (gint homogeneous, gint spacing,
|
|
gint expand, gint fill, gint padding)
|
|
{
|
|
GtkWidget *box;
|
|
GtkWidget *boton;
|
|
char padstr[80];
|
|
|
|
/* creamos una nueva caja con los argumentos homogeneous y
|
|
* spacing */
|
|
box = gtk_hbox_new (homogeneous, spacing);
|
|
|
|
/* crear una serie de botones */
|
|
boton = gtk_button_new_with_label ("gtk_box_pack");
|
|
gtk_box_pack_start (GTK_BOX (box), boton, expand, fill, padding);
|
|
gtk_widget_show (boton);
|
|
|
|
boton = gtk_button_new_with_label ("(box,");
|
|
gtk_box_pack_start (GTK_BOX (box), boton, expand, fill, padding);
|
|
gtk_widget_show (boton);
|
|
|
|
boton = gtk_button_new_with_label ("boton,");
|
|
gtk_box_pack_start (GTK_BOX (box), boton, expand, fill, padding);
|
|
gtk_widget_show (boton);
|
|
|
|
/* Este botón llevará por etiqueta el valor de expand */
|
|
if (expand == TRUE)
|
|
boton = gtk_button_new_with_label ("TRUE,");
|
|
else
|
|
boton = gtk_button_new_with_label ("FALSE,");
|
|
|
|
gtk_box_pack_start (GTK_BOX (box), boton, expand, fill, padding);
|
|
gtk_widget_show (boton);
|
|
|
|
/* Este es el mismo caso que el de arriba, pero más compacto */
|
|
boton = gtk_button_new_with_label (fill ? "TRUE," : "FALSE,");
|
|
gtk_box_pack_start (GTK_BOX (box), boton, expand, fill, padding);
|
|
gtk_widget_show (boton);
|
|
|
|
sprintf (padstr, "%d);", padding);
|
|
|
|
boton = gtk_button_new_with_label (padstr);
|
|
gtk_box_pack_start (GTK_BOX (box), boton, expand, fill, padding);
|
|
gtk_widget_show (boton);
|
|
|
|
return box;
|
|
}
|
|
|
|
int
|
|
main (int argc, char *argv[])
|
|
{
|
|
GtkWidget *ventana;
|
|
GtkWidget *boton;
|
|
GtkWidget *caja1;
|
|
GtkWidget *caja2;
|
|
GtkWidget *separator;
|
|
GtkWidget *etiqueta;
|
|
GtkWidget *quitbox;
|
|
int which;
|
|
|
|
/* ¡No olvidar la siguiente llamada! */
|
|
gtk_init (&argc, &argv);
|
|
|
|
if (argc != 2) {
|
|
fprintf (stderr, "usage: packbox num, where num is 1, 2, or 3.\n");
|
|
/* hacemos limpieza en GTK y devolvemos el valor de 1 */
|
|
gtk_exit (1);
|
|
}
|
|
|
|
which = atoi (argv[1]);
|
|
|
|
/* Creamos la ventana */
|
|
ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
|
|
|
/* Siempre hay que conectar la señal de destrucción con la
|
|
* ventana principal. Esto es muy importante para que el
|
|
* comportamiento de la ventana sea intuitivo. */
|
|
gtk_signal_connect (GTK_OBJECT (ventana), "delete_event",
|
|
GTK_SIGNAL_FUNC (delete_event), NULL);
|
|
gtk_container_border_width (GTK_CONTAINER (ventana), 10);
|
|
|
|
/* Creamos una caja vertical donde empaquetaremos las cajas
|
|
* horizontales. Así podemos apilar las cajas horizontales
|
|
* llenas con botones una encima de las otras. */
|
|
caja1 = gtk_vbox_new (FALSE, 0);
|
|
|
|
/* Aclaramos cúal es el ejemplo a mostrar. Se corresponde con
|
|
* las imágenes anteriores. */
|
|
switch (which) {
|
|
case 1:
|
|
/* creamos una nueva etiqueta. */
|
|
etiqueta = gtk_label_new ("gtk_hbox_new (FALSE, 0);");
|
|
|
|
/* Alineamos la etiqueta a la izquierda. Está función
|
|
* será discutida en detalle en la sección de los
|
|
* atributos de los widgets. */
|
|
gtk_misc_set_alignment (GTK_MISC (etiqueta), 0, 0);
|
|
|
|
/* Empaquetamos la etiqueta en la caja vertical (vbox
|
|
* caja1). Siempre hay que recordar que los widgets añadidos a
|
|
* una vbox serán empaquetados uno encimo de otro. */
|
|
gtk_box_pack_start (GTK_BOX (caja1), etiqueta, FALSE, FALSE, 0);
|
|
|
|
/* mostramos la etiqueta. */
|
|
gtk_widget_show (etiqueta);
|
|
|
|
/* llamada a la función que hace las cajas. Los argumentos
|
|
* son homogenous = FALSE, expand = FALSE, fill = FALSE,
|
|
* padding = 0 */
|
|
caja2 = make_box (FALSE, 0, FALSE, FALSE, 0);
|
|
gtk_box_pack_start (GTK_BOX (caja1), caja2, FALSE, FALSE, 0);
|
|
gtk_widget_show (caja2);
|
|
|
|
/* Llamad a la función para hacer cajas -
|
|
* homogeneous = FALSE, spacing = 0, expand = FALSE,
|
|
* fill = FALSE, padding = 0 */
|
|
caja2 = make_box (FALSE, 0, TRUE, FALSE, 0);
|
|
gtk_box_pack_start (GTK_BOX (caja1), caja2, FALSE, FALSE, 0);
|
|
gtk_widget_show (caja2);
|
|
|
|
/* Los argumentos son: homogeneous, spacing, expand, fill,
|
|
* padding */
|
|
caja2 = make_box (FALSE, 0, TRUE, TRUE, 0);
|
|
gtk_box_pack_start (GTK_BOX (caja1), caja2, FALSE, FALSE, 0);
|
|
gtk_widget_show (caja2);
|
|
|
|
|
|
/* creamos un separador. Más tarde aprenderemos más cosas
|
|
* sobre ellos, pero son bastante sencillos. */
|
|
separator = gtk_hseparator_new ();
|
|
|
|
/* empaquetamos el separador el la vbox. Los widgets serán
|
|
* apilados verticalmente. */
|
|
gtk_box_pack_start (GTK_BOX (caja1), separator, FALSE, TRUE, 5);
|
|
gtk_widget_show (separator);
|
|
|
|
/* creamos una nueva etiqueta y la mostramos */
|
|
etiqueta = gtk_label_new ("gtk_hbox_new (TRUE, 0);");
|
|
gtk_misc_set_alignment (GTK_MISC (etiqueta), 0, 0);
|
|
gtk_box_pack_start (GTK_BOX (caja1), etiqueta, FALSE, FALSE, 0);
|
|
gtk_widget_show (etiqueta);
|
|
|
|
/* Los argumentos son: homogeneous, spacing, expand, fill,
|
|
* padding */
|
|
caja2 = make_box (TRUE, 0, TRUE, FALSE, 0);
|
|
gtk_box_pack_start (GTK_BOX (caja1), caja2, FALSE, FALSE, 0);
|
|
gtk_widget_show (caja2);
|
|
|
|
/* Los argumentos son: homogeneous, spacing, expand, fill,
|
|
* padding */
|
|
caja2 = make_box (TRUE, 0, TRUE, TRUE, 0);
|
|
gtk_box_pack_start (GTK_BOX (caja1), caja2, FALSE, FALSE, 0);
|
|
gtk_widget_show (caja2);
|
|
|
|
/* un nuevo separador */
|
|
separator = gtk_hseparator_new ();
|
|
/* Los tres últimos argumentos son: expand, fill, padding. */
|
|
gtk_box_pack_start (GTK_BOX (caja1), separator, FALSE, TRUE, 5);
|
|
gtk_widget_show (separator);
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
/* Nueva etiqueta */
|
|
etiqueta = gtk_label_new ("gtk_hbox_new (FALSE, 10);");
|
|
gtk_misc_set_alignment (GTK_MISC (etiqueta), 0, 0);
|
|
gtk_box_pack_start (GTK_BOX (caja1), etiqueta, FALSE, FALSE, 0);
|
|
gtk_widget_show (etiqueta);
|
|
|
|
/* Los argumentos son: homogeneous, spacing, expand, fill,
|
|
* padding */
|
|
caja2 = make_box (FALSE, 10, TRUE, FALSE, 0);
|
|
gtk_box_pack_start (GTK_BOX (caja1), caja2, FALSE, FALSE, 0);
|
|
gtk_widget_show (caja2);
|
|
|
|
/* Los argumentos son: homogeneous, spacing, expand, fill,
|
|
* padding */
|
|
caja2 = make_box (FALSE, 10, TRUE, TRUE, 0);
|
|
gtk_box_pack_start (GTK_BOX (caja1), caja2, FALSE, FALSE, 0);
|
|
gtk_widget_show (caja2);
|
|
|
|
separator = gtk_hseparator_new ();
|
|
/* Los argumentos son: expand, fill, padding. */
|
|
gtk_box_pack_start (GTK_BOX (caja1), separator, FALSE, TRUE, 5);
|
|
gtk_widget_show (separator);
|
|
|
|
etiqueta = gtk_label_new ("gtk_hbox_new (FALSE, 0);");
|
|
gtk_misc_set_alignment (GTK_MISC (etiqueta), 0, 0);
|
|
gtk_box_pack_start (GTK_BOX (caja1), etiqueta, FALSE, FALSE, 0);
|
|
gtk_widget_show (etiqueta);
|
|
|
|
/* Los argumentos son: homogeneous, spacing, expand, fill,
|
|
* padding */
|
|
caja2 = make_box (FALSE, 0, TRUE, FALSE, 10);
|
|
gtk_box_pack_start (GTK_BOX (caja1), caja2, FALSE, FALSE, 0);
|
|
gtk_widget_show (caja2);
|
|
|
|
/* Los argumentos son: homogeneous, spacing, expand, fill,
|
|
* padding */
|
|
caja2 = make_box (FALSE, 0, TRUE, TRUE, 10);
|
|
gtk_box_pack_start (GTK_BOX (caja1), caja2, FALSE, FALSE, 0);
|
|
gtk_widget_show (caja2);
|
|
|
|
separator = gtk_hseparator_new ();
|
|
/* Los argumentos son: expand, fill, padding. */
|
|
gtk_box_pack_start (GTK_BOX (caja1), separator, FALSE, TRUE, 5);
|
|
gtk_widget_show (separator);
|
|
break;
|
|
|
|
case 3:
|
|
|
|
/* Con esto demostramos como hay que usar gtk_box_pack_end ()
|
|
* para conseguir que los
|
|
widgets esten alineados a la izquierda. */
|
|
caja2 = make_box (FALSE, 0, FALSE, FALSE, 0);
|
|
|
|
/* la última etiqueta*/
|
|
etiqueta = gtk_label_new ("end");
|
|
|
|
/* la empaquetamos usando gtk_box_pack_end(), por lo que se
|
|
* sitúa en el lado derecho de la hbox.*/
|
|
gtk_box_pack_end (GTK_BOX (caja2), etiqueta, FALSE, FALSE, 0);
|
|
|
|
/* mostrar la etiqueta */
|
|
gtk_widget_show (etiqueta);
|
|
|
|
|
|
/* empaquetamos caja2 en caja1 */
|
|
gtk_box_pack_start (GTK_BOX (caja1), caja2, FALSE, FALSE, 0);
|
|
gtk_widget_show (caja2);
|
|
|
|
|
|
/* el separador para la parte de abajo. */
|
|
separator = gtk_hseparator_new ();
|
|
|
|
/* Así se determina el tamaño del separador a 400 pixels
|
|
* de largo por 5 de alto. La hbox también tendrá 400
|
|
* pixels de largo y la etiqueta "end" estará separada de
|
|
* las demás etiquetas en la hbox. Si no establecemos estos
|
|
* parámetros todos los widgets en la hbox serán
|
|
* empaquetados tan juntos como se pueda.*/
|
|
gtk_widget_set_usize (separator, 400, 5);
|
|
|
|
/* Empaquetamos el separador creado al principio de main() en
|
|
* la vbox (caja1). */
|
|
gtk_box_pack_start (GTK_BOX (caja1), separator, FALSE, TRUE, 5);
|
|
gtk_widget_show (separator);
|
|
}
|
|
|
|
/* Creamos otra hbox... recordar que podemos crear tantas como
|
|
* queramos. */
|
|
quitbox = gtk_hbox_new (FALSE, 0);
|
|
|
|
/* El botón de salida. */
|
|
boton = gtk_button_new_with_label ("Quit");
|
|
|
|
/* Establecemos la señal de destrucción de la ventana.
|
|
* Recuerde que emitirá la señal de "destroy" que a su vez
|
|
* será procesada por el controlador de señales, tal y como
|
|
* ya hemos visto. */
|
|
|
|
gtk_signal_connect_object (GTK_OBJECT (boton), "clicked",
|
|
GTK_SIGNAL_FUNC (gtk_main_quit),
|
|
GTK_OBJECT (ventana));
|
|
/* Empaquetamos el botón en la caja de salida (quitbox).
|
|
* los tres últimos argumentos de gtk_box_pack_start
|
|
* son:expand, fill, padding. */
|
|
gtk_box_pack_start (GTK_BOX (caja1), quitbox, FALSE, FALSE, 0);
|
|
|
|
/* empaquetamos la vbox (caja1) que ya contiene todos los widgets
|
|
* en la ventana principal. */
|
|
gtk_container_add (GTK_CONTAINER (ventana), caja1);
|
|
|
|
/* mostramos todo aquello que faltaba por mostrar */
|
|
gtk_widget_show (boton);
|
|
gtk_widget_show (quitbox);
|
|
|
|
gtk_widget_show (caja1);
|
|
|
|
/* Si mostramos la ventana lo último todo aparece de golpe. */
|
|
gtk_widget_show (ventana);
|
|
|
|
/* por supuesto tenemos una función main. */
|
|
gtk_main ();
|
|
|
|
/* El programa llega aquí cuando se llama a gtk_main_quit(),
|
|
* pero no cuando se llama a gtk_exit(). */
|
|
return 0;
|
|
}
|
|
/* final del ejemplo*/
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Empaquetamiento usando tablas
|
|
<p>
|
|
Existe otra forma de empaquetar: usando tablas. Estas pueden llegar a
|
|
ser extremadamente útiles.
|
|
|
|
Usando tablas creamos una cuadrícula donde podemos poner los
|
|
widgets. Estos pueden ocupar tanto espacio como queramos.
|
|
|
|
La primera función que conviene estudiar es gtk_table_new:
|
|
|
|
<tscreen><verb>
|
|
GtkWidget *gtk_table_new( gint rows,
|
|
gint columns,
|
|
gint homogeneous );
|
|
</verb></tscreen>
|
|
|
|
Como es lógico el primer argumento es el número de filas y el
|
|
segundo el de columnas.
|
|
|
|
El tercero establece el tamaño de las celdas de la tabla. Si es TRUE
|
|
se fuerza a que el tamaño de las celdas sea igual al de la celda
|
|
mayor. Con FALSE se establece el ancho de toda una columna igual al de
|
|
la celda más ancha de esa columna, y la altura de una fila será
|
|
la de la celda más alta de esa fila.
|
|
|
|
El número de filas y columnas varía entre 0 y n, donde n es el
|
|
número especificado en la llamada a gtk_table_new. Así si se
|
|
especifica columnas = 2 y filas = 2 la apariencia será parecida a:
|
|
|
|
<tscreen><verb>
|
|
0 1 2
|
|
0+----------+----------+
|
|
| | |
|
|
1+----------+----------+
|
|
| | |
|
|
2+----------+----------+
|
|
</verb></tscreen>
|
|
|
|
Conviene destacar que el origen de coordenadas se sitúa en la esquina superior izquierda. Para
|
|
situar un widget en una ventana se usa la siguiente función:
|
|
|
|
<tscreen><verb>
|
|
void gtk_table_attach( GtkTable *table,
|
|
GtkWidget *hijo,
|
|
gint left_attach,
|
|
gint right_attach,
|
|
gint top_attach,
|
|
gint bottom_attach,
|
|
gint xoptions,
|
|
gint yoptions,
|
|
gint xpadding,
|
|
gint ypadding );
|
|
</verb></tscreen>
|
|
|
|
El primer argumento (<tt/table/) es el nombre de la tabla y el segundo
|
|
(<tt/hijo/) el <em/widget/ que quiere poner en la tabla.
|
|
|
|
Los argumentos <tt/left_attach/, <tt/right_attach/ especifican donde
|
|
se pone el widget y cuantas cajas se usan. Por ejemplo, supongamos que
|
|
queremos poner un botón que sólo ocupe la esquina inferior
|
|
izquierda en nuestra tabla 2x2. Los valores serán left_attach = 1,
|
|
right_attach = 2, top_attach = 2, top_attach = 1, bottom_attach = 2.
|
|
|
|
Supongamos que queremos ocupar toda la fila de nuestra tabla 2x2,
|
|
usaríamos left_attach = 0, right_attach = 2, top_attach = 0,
|
|
bottom_attach = 1.
|
|
|
|
Las opciones <tt/xoptions/ e <tt/yoptions/ son usadas para especificar
|
|
como queremos el empaquetamiento y podemos utilizar multiples
|
|
opciones simultaneamente con OR.
|
|
|
|
Las opciones son:
|
|
|
|
<itemize>
|
|
<item>GTK_FILL - Si el relleno es más grande que el widget, y se
|
|
especifica GTK_FILL, el <em/widget/ se expandirá ocupando todo el
|
|
espacio disponible.
|
|
|
|
<item>GTK_SHRINK - En el caso de que hayamos dejado espacio sin usar
|
|
cuando el usuario reajuste el tamaño de la ventana los <em/widgets/
|
|
normalmente serán empujados al fondo de la ventana y
|
|
desaparecerán. Si especifica GTK_SHRINK los widgets se reducirán
|
|
con la tabla.
|
|
|
|
<item>GTK_EXPAND - Mediante esta opción la tabla se expande usando
|
|
todo el espacio libre de la ventana.
|
|
</itemize>
|
|
|
|
El relleno es igual que con las cajas. Simplemente se crea una zona
|
|
vacía alrededor del widget (el tamaño se especifica en pixels).
|
|
|
|
gtk_table_attach() tiene MUCHAS opciones. Asi que hay un atajo:
|
|
|
|
<tscreen><verb>
|
|
void gtk_table_attach_defaults( GtkTable *table,
|
|
GtkWidget *widget,
|
|
gint left_attach,
|
|
gint right_attach,
|
|
gint top_attach,
|
|
gint bottom_attach );
|
|
</verb></tscreen>
|
|
|
|
Las opciones X e Y se ponen por defecto a GTK_FILL | GTK_EXPAND, y el
|
|
relleno X e Y se pone a 0. El resto de los argumentos son identicos a
|
|
la función anterior.
|
|
|
|
Existen otras funciones como gtk_table_set_row_spacing() y
|
|
gtk_table_set_col_spacing(), que sirven para especificar el espaciado
|
|
entre las columnas/filas en la columna/fila que queramos.
|
|
|
|
<tscreen><verb>
|
|
void gtk_table_set_row_spacing( GtkTable *table,
|
|
gint row,
|
|
gint spacing );
|
|
</verb></tscreen>
|
|
|
|
y
|
|
|
|
<tscreen><verb>
|
|
void gtk_table_set_col_spacing ( GtkTable *table,
|
|
gint column,
|
|
gint spacing );
|
|
</verb></tscreen>
|
|
|
|
Conviene destacar que el espaciado se sitúa a la derecha de la
|
|
columna y debajo de la fila.
|
|
|
|
Tambien se puede forzar que el espaciado sea el mismo para las filas
|
|
y/o las columnas:
|
|
|
|
<tscreen><verb>
|
|
void gtk_table_set_row_spacings( GtkTable *table,
|
|
gint spacing );
|
|
</verb></tscreen>
|
|
|
|
y
|
|
|
|
<tscreen><verb>
|
|
void gtk_table_set_col_spacings( GtkTable *table,
|
|
gint spacing );
|
|
</verb></tscreen>
|
|
|
|
Usando estas funciones las últimas fila y columna no estarán
|
|
espaciadas.
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Ejemplo de empaquetamiento mediante tablas.
|
|
<p>
|
|
Haremos una ventana con tres botones en una tabla 2x2. Los dos
|
|
primeros botones ocuparán la fila de arriba, mientras que el
|
|
tercero (de salida) ocupará toda la fila de abajo. El resultado es
|
|
el siguiente:
|
|
|
|
<? <CENTER> >
|
|
<?
|
|
<IMG SRC="gtk_tut_table.gif" VSPACE="15" HSPACE="10"
|
|
ALT="Imagen de ejemplo sobre el empaquetado mediante tablas" WIDTH="180" HEIGHT="120">
|
|
>
|
|
<? </CENTER> >
|
|
|
|
Este es el código:
|
|
<tscreen><verb>
|
|
/* principio del ejemplo table table.c */
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
/* La respuesta, que además se imprime en stdout. */
|
|
void callback (GtkWidget *widget, gpointer data)
|
|
{
|
|
g_print ("Hello again - %s was pressed\n", (char *) data);
|
|
}
|
|
|
|
/* Con esta otra respuesta terminamos el programa. */
|
|
void delete_event (GtkWidget *widget, GdkEvent *event, gpointer data)
|
|
{
|
|
gtk_main_quit ();
|
|
}
|
|
|
|
int main (int argc, char *argv[])
|
|
{
|
|
GtkWidget *ventana;
|
|
GtkWidget *boton;
|
|
GtkWidget *table;
|
|
|
|
gtk_init (&argc, &argv);
|
|
|
|
ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
|
|
|
gtk_window_set_title (GTK_WINDOW (ventana), "Table");
|
|
|
|
gtk_signal_connect (GTK_OBJECT (ventana), "delete_event",
|
|
GTK_SIGNAL_FUNC (delete_event), NULL);
|
|
|
|
gtk_container_border_width (GTK_CONTAINER (ventana), 20);
|
|
|
|
table = gtk_table_new (2, 2, TRUE);
|
|
|
|
gtk_container_add (GTK_CONTAINER (ventana), table);
|
|
|
|
boton = gtk_button_new_with_label ("botón 1");
|
|
|
|
gtk_signal_connect (GTK_OBJECT (boton), "clicked",
|
|
GTK_SIGNAL_FUNC (callback), (gpointer) "botón 1");
|
|
|
|
gtk_table_attach_defaults (GTK_TABLE(table), boton, 0, 1, 0, 1);
|
|
|
|
gtk_widget_show (boton);
|
|
|
|
boton = gtk_button_new_with_label ("botón 2");
|
|
|
|
gtk_signal_connect (GTK_OBJECT (boton), "clicked",
|
|
GTK_SIGNAL_FUNC (callback), (gpointer) "botón 2");
|
|
gtk_table_attach_defaults (GTK_TABLE(table), boton, 1, 2, 0, 1);
|
|
|
|
gtk_widget_show (boton);
|
|
|
|
boton = gtk_button_new_with_label ("Quit");
|
|
|
|
gtk_signal_connect (GTK_OBJECT (boton), "clicked",
|
|
GTK_SIGNAL_FUNC (delete_event), NULL);
|
|
gtk_table_attach_defaults (GTK_TABLE(table), boton, 0, 2, 1, 2);
|
|
|
|
gtk_widget_show (boton);
|
|
|
|
gtk_widget_show (table);
|
|
gtk_widget_show (ventana);
|
|
|
|
gtk_main ();
|
|
|
|
return 0;
|
|
}
|
|
/* final del ejemplo */
|
|
</verb></tscreen>
|
|
|
|
<!-- ***************************************************************** -->
|
|
<sect>Estudio general de los <em/widgets/
|
|
<!-- ***************************************************************** -->
|
|
<p>
|
|
Los pasos generales a la hora de crear un <em/widget/ son:
|
|
|
|
<enum>
|
|
|
|
<item> Usar gtk_*_new - Una de las diferentes formas de crear un
|
|
<em/widget/. (Todas serán explicadas en esta sección).
|
|
|
|
<item> Connectar todas las señales y los eventos a los
|
|
controladores apropiados.
|
|
|
|
<item> Establecer los atributos del <em/widget/.
|
|
|
|
<item> Empaquetar el <em/widget/ en un contenedor usando las llamadas
|
|
apropiadas, como gtk_container_add() o gtk_box_pack_start().
|
|
|
|
<item> Mostrar el <em/widget/ usando gtk_widget_show().
|
|
|
|
</enum>
|
|
|
|
Mediante esta última llamada GTK `sabe' que hemos acabado de
|
|
establecer los atributos del <em/widget/, y que por lo tanto puede
|
|
mostrarse. Se puede usar gtk_widget_hide para hacer que desaparezca.
|
|
El orden en el que se muestran los <em/widgets/ no es importante, pero
|
|
se recomienda mostrar al final la ventana para que todo aparezca de
|
|
golpe. El hijo de un <em/widget/ no se muestra hasta que lo hace la
|
|
propia ventana (que en este caso es un <em/widget/ padre) mediante
|
|
gtk_widget_show().
|
|
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1> Conversión de tipos
|
|
<p>
|
|
GTK usa un sistema de conversión de tipos mediante macros que
|
|
comprueban si se puede realizar la conversión y en caso
|
|
afirmativo la hacen. Las más comunes son:
|
|
|
|
<itemize>
|
|
<item> GTK_WIDGET(widget)
|
|
<item> GTK_OBJECT(object)
|
|
<item> GTK_SIGNAL_FUNC(function)
|
|
<item> GTK_CONTAINER(container)
|
|
<item> GTK_WINDOW(ventana)
|
|
<item> GTK_BOX(box)
|
|
</itemize>
|
|
|
|
Todas son usadas para cambiar de tipo los argumentos de una función.
|
|
Aparecerán mucho en los ejemplos, para usarlas sólo hay que mirar la
|
|
declaración de la función.
|
|
|
|
Tal y como se puede ver en el árbol de clases (situado un poco
|
|
más adelante) todos los <em/widgets/ derivan de la clase base
|
|
GtkObject. Esto significa que siempre se puede usar un <em/widget/
|
|
como argumento de una función (que acepte un objeto, claro)
|
|
realizando la conversión de tipo GTK_OBJECT().
|
|
|
|
Por ejemplo:
|
|
|
|
<tscreen><verb>
|
|
gtk_signal_connect( GTK_OBJECT(boton), "clicked",
|
|
GTK_SIGNAL_FUNC(callback_function), callback_data);
|
|
</verb></tscreen>
|
|
|
|
Hemos hecho que el botón pase a ser un objeto y que se cambie el
|
|
puntero a la función a una función respuesta.
|
|
|
|
Muchos <em/widgets/ son contenedores, por lo que unos pueden derivar
|
|
de otros (la mayoría lo hace de GtkContainer). Cualquiera puede ser
|
|
usado junto con la macro GTK_CONTAINER como argumento a funciones en
|
|
forma de puntero.
|
|
|
|
Desgraciadamente estas macros no son descritas en detalle en el
|
|
tutorial, por lo que se recomienda echar un vistazo a los archivos de
|
|
cabecera de GTK. En la práctica es posible aprender a manejar un
|
|
<em/widget/ leyendo las declaraciones de las funciones.
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Árbol formado por los <em/widgets/
|
|
<p>
|
|
A continuación se detallan todas las ramas del árbol que forman
|
|
los <em/widgets/.
|
|
|
|
<tscreen><verb>
|
|
GtkObject
|
|
+GtkWidget
|
|
| +GtkMisc
|
|
| | +GtkLabel
|
|
| | | +GtkAccelLabel
|
|
| | | `GtkTipsQuery
|
|
| | +GtkArrow
|
|
| | +GtkImage
|
|
| | `GtkPixmap
|
|
| +GtkContainer
|
|
| | +GtkBin
|
|
| | | +GtkAlignment
|
|
| | | +GtkFrame
|
|
| | | | `GtkAspectFrame
|
|
| | | +GtkButton
|
|
| | | | +GtkToggleButton
|
|
| | | | | `GtkCheckButton
|
|
| | | | | `GtkRadioButton
|
|
| | | | `GtkOptionMenu
|
|
| | | +GtkItem
|
|
| | | | +GtkMenuItem
|
|
| | | | | +GtkCheckMenuItem
|
|
| | | | | | `GtkRadioMenuItem
|
|
| | | | | `GtkTearoffMenuItem
|
|
| | | | +GtkListItem
|
|
| | | | `GtkTreeItem
|
|
| | | +GtkWindow
|
|
| | | | +GtkColorSelectionDialog
|
|
| | | | +GtkDialog
|
|
| | | | | `GtkInputDialog
|
|
| | | | +GtkDrawWindow
|
|
| | | | +GtkFileSelection
|
|
| | | | +GtkFontSelectionDialog
|
|
| | | | `GtkPlug
|
|
| | | +GtkEventBox
|
|
| | | +GtkHandleBox
|
|
| | | +GtkScrolledWindow
|
|
| | | `GtkViewport
|
|
| | +GtkBox
|
|
| | | +GtkButtonBox
|
|
| | | | +GtkHButtonBox
|
|
| | | | `GtkVButtonBox
|
|
| | | +GtkVBox
|
|
| | | | +GtkColorSelection
|
|
| | | | `GtkGammaCurve
|
|
| | | `GtkHBox
|
|
| | | +GtkCombo
|
|
| | | `GtkStatusbar
|
|
| | +GtkCList
|
|
| | | `GtkCTree
|
|
| | +GtkFixed
|
|
| | +GtkNotebook
|
|
| | | `GtkFontSelection
|
|
| | +GtkPaned
|
|
| | | +GtkHPaned
|
|
| | | `GtkVPaned
|
|
| | +GtkLayout
|
|
| | +GtkList
|
|
| | +GtkMenuShell
|
|
| | | +GtkMenuBar
|
|
| | | `GtkMenu
|
|
| | +GtkPacker
|
|
| | +GtkSocket
|
|
| | +GtkTable
|
|
| | +GtkToolbar
|
|
| | `GtkTree
|
|
| +GtkCalendar
|
|
| +GtkDrawingArea
|
|
| | `GtkCurve
|
|
| +GtkEditable
|
|
| | +GtkEntry
|
|
| | | `GtkSpinButton
|
|
| | `GtkText
|
|
| +GtkRuler
|
|
| | +GtkHRuler
|
|
| | `GtkVRuler
|
|
| +GtkRange
|
|
| | +GtkScale
|
|
| | | +GtkHScale
|
|
| | | `GtkVScale
|
|
| | `GtkScrollbar
|
|
| | +GtkHScrollbar
|
|
| | `GtkVScrollbar
|
|
| +GtkSeparator
|
|
| | +GtkHSeparator
|
|
| | `GtkVSeparator
|
|
| +GtkPreview
|
|
| `GtkProgress
|
|
| `GtkProgressBar
|
|
+GtkData
|
|
| +GtkAdjustment
|
|
| `GtkTooltips
|
|
`GtkItemFactory
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1><em/Widgets/ sin ventanas
|
|
<p>
|
|
Los siguientes <em/widgets/ no tienen ventanas asociadas. Si se
|
|
quieren capturar eventos se tendrá que utilizar GtkEventBox. En la
|
|
sección <ref id="sec_The_EventBox_Widget" name="El widget
|
|
EventBox"> se pueden encontrar más detalles sobre su uso.
|
|
|
|
<tscreen><verb>
|
|
GtkAlignment
|
|
GtkArrow
|
|
GtkBin
|
|
GtkBox
|
|
GtkImage
|
|
GtkItem
|
|
GtkLabel
|
|
GtkPixmap
|
|
GtkScrolledWindow
|
|
GtkSeparator
|
|
GtkTable
|
|
GtkAspectFrame
|
|
GtkFrame
|
|
GtkVBox
|
|
GtkHBox
|
|
GtkVSeparator
|
|
GtkHSeparator
|
|
</verb></tscreen>
|
|
|
|
Vamos a continuar la explicación describiendo cada uno de los
|
|
<em/widgets/ mediante ejemplos. También se puede consultar el
|
|
programa testgtk.c (Se encuentra en gtk/testgtk.c).
|
|
|
|
<!-- ***************************************************************** -->
|
|
<sect>El <em/widget/ Botón
|
|
<!-- ***************************************************************** -->
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Botones normales <label id="sec_Radio_Buttons">
|
|
<p>
|
|
Ya hemos visto prácticamente todo lo que hay que saber a cerca de
|
|
este <em/widget/. Existen dos formas diferentes de crear un
|
|
botón. Se puede usar gtk_button_new_with_label() para conseguir un
|
|
botón con etiqueta o simplemente gtk_button_new(). Si se quiere se
|
|
puede añadir una etiqueta a este último empaquetándola,
|
|
primero se crea una nueva caja y luego se empaquetan los objetos que
|
|
se quieran mediante gtk_box_pack_start. Una vez finalizado esto se
|
|
relaciona la caja con el botón mediante gtk_container_add.
|
|
|
|
Estudiemos un ejemplo de gtk_button_new para crear un botón con una
|
|
imagen y una etiqueta. El código está dividido en dos para que
|
|
pueda ser reusado.
|
|
|
|
<tscreen><verb>
|
|
/* principio del ejemplo buttons buttons.c */
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
/* Creamos la caja con una imagen y una etiqueta empaquetadas. Se
|
|
* devuelve la caja. */
|
|
GtkWidget *xpm_label_box (GtkWidget *parent, gchar *xpm_filename, gchar *label_text)
|
|
{
|
|
GtkWidget *caja1;
|
|
GtkWidget *etiqueta;
|
|
GtkWidget *pixmapwid;
|
|
GdkPixmap *pixmap;
|
|
GdkBitmap *mask;
|
|
GtkStyle *style;
|
|
|
|
/* create box for xpm and etiqueta */
|
|
caja1 = gtk_hbox_new (FALSE, 0);
|
|
gtk_container_border_width (GTK_CONTAINER (caja1), 2);
|
|
|
|
/* obtenemos el estilo del botón (probablemente para el color
|
|
* de fondo, pero no estoy seguro) */
|
|
style = gtk_widget_get_style(parent);
|
|
|
|
/* cargamos el pixmap. Hay una sección que describe el proceso
|
|
* en detalle */
|
|
pixmap = gdk_pixmap_create_from_xpm (parent->window, &mask,
|
|
&style->bg[GTK_STATE_NORMAL],
|
|
xpm_filename);
|
|
pixmapwid = gtk_pixmap_new (pixmap, mask);
|
|
|
|
etiqueta = gtk_label_new (label_text);
|
|
|
|
gtk_box_pack_start (GTK_BOX (caja1),
|
|
pixmapwid, FALSE, FALSE, 3);
|
|
|
|
gtk_box_pack_start (GTK_BOX (caja1), etiqueta, FALSE, FALSE, 3);
|
|
|
|
gtk_widget_show(pixmapwid);
|
|
gtk_widget_show(etiqueta);
|
|
|
|
return (caja1);
|
|
}
|
|
|
|
/* respuesta */
|
|
void callback (GtkWidget *widget, gpointer data)
|
|
{
|
|
g_print ("Hola de nuevo. Se ha pulsado %s\n", (char *) data);
|
|
}
|
|
|
|
|
|
int main (int argc, char *argv[])
|
|
{
|
|
|
|
GtkWidget *ventana;
|
|
GtkWidget *boton;
|
|
GtkWidget *caja1;
|
|
|
|
gtk_init (&argc, &argv);
|
|
|
|
ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
|
|
|
gtk_window_set_title (GTK_WINDOW (ventana), "Botones con dibujos");
|
|
|
|
/* It's a good idea to do this for all windows. */
|
|
gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
|
|
GTK_SIGNAL_FUNC (gtk_exit), NULL);
|
|
|
|
gtk_signal_connect (GTK_OBJECT (ventana), "delete_event",
|
|
GTK_SIGNAL_FUNC (gtk_exit), NULL);
|
|
|
|
gtk_container_border_width (GTK_CONTAINER (ventana), 10);
|
|
gtk_widget_realize(ventana);
|
|
|
|
boton = gtk_button_new ();
|
|
|
|
gtk_signal_connect (GTK_OBJECT (boton), "clicked",
|
|
GTK_SIGNAL_FUNC (callback),
|
|
(gpointer) "botón divertido");
|
|
|
|
caja1 = xpm_label_box(ventana, "info.xpm", "botón divertido");
|
|
|
|
gtk_widget_show(caja1);
|
|
|
|
gtk_container_add (GTK_CONTAINER (boton), caja1);
|
|
|
|
gtk_widget_show(boton);
|
|
|
|
gtk_container_add (GTK_CONTAINER (ventana), boton);
|
|
|
|
gtk_widget_show (ventana);
|
|
|
|
gtk_main ();
|
|
|
|
return 0;
|
|
}
|
|
/* final del ejemplo */
|
|
</verb></tscreen>
|
|
|
|
La función xpm_label_box puede ser usada para empaquetar xpm y
|
|
etiquetas en cualquier widget que pueda ser un contenedor.
|
|
|
|
El botón puede responder a las siguientes señales:
|
|
|
|
<itemize>
|
|
<item> pressed
|
|
<item> released
|
|
<item> clicked
|
|
<item> enter
|
|
<item> leave
|
|
</itemize>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1> Botones de selección
|
|
<p>
|
|
Estos botones son muy similares a los normales. La única diferencia
|
|
es que sólo pueden estar en dos posiciones diferentes alternadas
|
|
mediante pulsaciones del ratón.
|
|
|
|
Los botones de selección son la base de otros tipos: los de
|
|
comprobación y los circulares. Por lo tanto muchas de sus llamadas
|
|
seran heredadas por estos.
|
|
|
|
Creamos un nuevo botón de selección:
|
|
|
|
<tscreen><verb>
|
|
GtkWidget *gtk_toggle_button_new( void );
|
|
|
|
GtkWidget *gtk_toggle_button_new_with_label( gchar *etiqueta );
|
|
</verb></tscreen>
|
|
|
|
Como se ha podido imaginar estas funciones son iguales a las de un
|
|
botón normal. La primera crea un botón, mientras que la segunda
|
|
crea un botón con una etiqueta.
|
|
|
|
Para saber cual es el estado de un botón de selección,
|
|
comprobación o circular se usa una de las macros del ejemplo
|
|
siguiente. En éstas se comprueba el estado del botón mediante
|
|
una respuesta. La señal que queremos recibir es
|
|
«toggled». Generalmente para comprobar el estado de una señal se
|
|
establece un controlador de señales y luego se usa la siguiente
|
|
macro. La función de respuesta debe ser de la forma:
|
|
|
|
<tscreen><verb>
|
|
void toggle_button_callback (GtkWidget *widget, gpointer data)
|
|
{
|
|
if (GTK_TOGGLE_BUTTON (widget)->active)
|
|
{
|
|
/* Si el control llega aquí el botón está pulsado */
|
|
|
|
} else {
|
|
|
|
/* El botón no está pulsado (sobresale) */
|
|
}
|
|
}
|
|
</verb></tscreen>
|
|
|
|
<tscreen><verb>
|
|
void gtk_toggle_button_set_state( GtkToggleButton *toggle_button,
|
|
gint state );
|
|
</verb></tscreen>
|
|
|
|
La llamada de arriba puede ser usada para establecer el estado de un
|
|
botón de selección (o de cualquiera de sus hijos: el circular o
|
|
el de comprobación). El primer argumento es el botón, el segundo
|
|
TRUE cuando queremos que el botón no esté pulsado o FALSE para
|
|
cuando lo esté. Por defecto se establece FALSE.
|
|
|
|
Hay que destacar que cuando se usa gtk_toggle_button_set_state() y se
|
|
cambia el estado del botón este emite la señal «clicked».
|
|
|
|
<tscreen><verb>
|
|
void gtk_toggle_button_toggled (GtkToggleButton *toggle_button);
|
|
</verb></tscreen>
|
|
|
|
Cambia el estado del botón emitiendo la señal «toggled».
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1> Botones de comprobación
|
|
<p>
|
|
Los botones de comprobación son un poco diferentes a los anteriores, aunque
|
|
sus propiedades y funciones son bastante similares. En lugar de ser botones
|
|
con texto en su interior son pequeños cuadrados con texto a su derecha.
|
|
Normalmente son usados para (des)seleccionar opciones.
|
|
|
|
Las dos funciones que los crean son muy similares a las de los botones
|
|
normales.
|
|
|
|
<tscreen><verb>
|
|
GtkWidget *gtk_check_button_new( void );
|
|
|
|
GtkWidget *gtk_check_button_new_with_label ( gchar *etiqueta );
|
|
</verb></tscreen>
|
|
|
|
La función new_with_label crea un botón de comprobación con
|
|
una etiqueta dentro.
|
|
|
|
El proceso para comprobar el estado de un botón de este tipo es
|
|
igual al de los de comprobación.
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1> Botones circulares
|
|
<p>
|
|
Estos botones son similares a los de selección con la salvedad de
|
|
que están agrupados, de modo que sólo uno puede estar
|
|
seleccionado. Por tanto son usados para permitir al usuario
|
|
seleccionar algo de una lista de opciones mutuamente excluyentes.
|
|
|
|
Las llamadas para crear un botón circular son:
|
|
<tscreen><verb>
|
|
GtkWidget *gtk_radio_button_new( GSList *group );
|
|
|
|
GtkWidget *gtk_radio_button_new_with_label( GSList *group,
|
|
gchar *etiqueta );
|
|
</verb></tscreen>
|
|
|
|
El nuevo argumento sirve para especificar el grupo al que
|
|
pertenecen. La primera llamada debe pasar NULL como primer
|
|
argumento. A continuación de ésta se puede crear el grupo
|
|
usando:
|
|
|
|
<tscreen><verb>
|
|
GSList *gtk_radio_button_group( GtkRadioButton *radio_button );
|
|
</verb></tscreen>
|
|
|
|
Para añadir un nuevo botón a un grupo hay que usar
|
|
gtk_radio_button_group con el anterior botón como argumento. El
|
|
resultado se le pasa a gtk_radio_button_new o a
|
|
gtk_radio_button_new_with_label. Así se consigue enlazar una cadena
|
|
de botones. (El ejemplo siguiente sirve para aclarar el proceso)
|
|
|
|
También se puede establecer cúal es el botón pulsado por
|
|
defecto:
|
|
|
|
<tscreen><verb>
|
|
void gtk_toggle_button_set_state( GtkToggleButton *toggle_button,
|
|
gint state );
|
|
</verb></tscreen>
|
|
El siguiente ejemplo crea un grupo de tres botones:
|
|
|
|
<tscreen><verb>
|
|
/* Principio del ejemplo radiobuttons.c */
|
|
|
|
#include <gtk/gtk.h>
|
|
#include <glib.h>
|
|
|
|
void close_application( GtkWidget *widget, GdkEvent *event, gpointer data ) {
|
|
gtk_main_quit();
|
|
}
|
|
|
|
main(int argc,char *argv[])
|
|
{
|
|
static GtkWidget *ventana = NULL;
|
|
GtkWidget *caja1;
|
|
GtkWidget *caja2;
|
|
GtkWidget *boton;
|
|
GtkWidget *separator;
|
|
GSList *group;
|
|
|
|
gtk_init(&argc,&argv);
|
|
ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
|
|
|
gtk_signal_connect (GTK_OBJECT (ventana), "delete_event",
|
|
GTK_SIGNAL_FUNC(close_application),
|
|
NULL);
|
|
|
|
gtk_window_set_title (GTK_WINDOW (ventana), "radio buttons");
|
|
gtk_container_border_width (GTK_CONTAINER (ventana), 0);
|
|
|
|
caja1 = gtk_vbox_new (FALSE, 0);
|
|
gtk_container_add (GTK_CONTAINER (ventana), caja1);
|
|
gtk_widget_show (caja1);
|
|
|
|
caja2 = gtk_vbox_new (FALSE, 10);
|
|
gtk_container_border_width (GTK_CONTAINER (caja2), 10);
|
|
gtk_box_pack_start (GTK_BOX (caja1), caja2, TRUE, TRUE, 0);
|
|
gtk_widget_show (caja2);
|
|
|
|
boton = gtk_radio_button_new_with_label (NULL, "botón1");
|
|
gtk_box_pack_start (GTK_BOX (caja2), boton, TRUE, TRUE, 0);
|
|
gtk_widget_show (boton);
|
|
|
|
group = gtk_radio_button_group (GTK_RADIO_BUTTON (boton));
|
|
boton = gtk_radio_button_new_with_label(group, "botón2");
|
|
gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (boton), TRUE);
|
|
gtk_box_pack_start (GTK_BOX (caja2), boton, TRUE, TRUE, 0);
|
|
gtk_widget_show (boton);
|
|
|
|
group = gtk_radio_button_group (GTK_RADIO_BUTTON (boton));
|
|
boton = gtk_radio_button_new_with_label(group, "botón3");
|
|
gtk_box_pack_start (GTK_BOX (caja2), boton, TRUE, TRUE, 0);
|
|
gtk_widget_show (boton);
|
|
|
|
separator = gtk_hseparator_new ();
|
|
gtk_box_pack_start (GTK_BOX (caja1), separator, FALSE, TRUE, 0);
|
|
gtk_widget_show (separator);
|
|
|
|
caja2 = gtk_vbox_new (FALSE, 10);
|
|
gtk_container_border_width (GTK_CONTAINER (caja2), 10);
|
|
gtk_box_pack_start (GTK_BOX (caja1), caja2, FALSE, TRUE, 0);
|
|
gtk_widget_show (caja2);
|
|
|
|
boton = gtk_button_new_with_label ("close");
|
|
gtk_signal_connect_object (GTK_OBJECT (boton), "clicked",
|
|
GTK_SIGNAL_FUNC(close_application),
|
|
GTK_OBJECT (ventana));
|
|
gtk_box_pack_start (GTK_BOX (caja2), boton, TRUE, TRUE, 0);
|
|
GTK_WIDGET_SET_FLAGS (boton, GTK_CAN_DEFAULT);
|
|
gtk_widget_grab_default (boton);
|
|
gtk_widget_show (boton);
|
|
gtk_widget_show (ventana);
|
|
|
|
gtk_main();
|
|
return(0);
|
|
}
|
|
/* final del ejemplo */
|
|
</verb></tscreen>
|
|
|
|
<!-- TODO: checout out gtk_radio_button_new_from_widget function - TRG -->
|
|
|
|
<!-- ***************************************************************** -->
|
|
<sect>Ajustes (<em/Adjustment/) <label id="sec_Adjustment">
|
|
<!-- ***************************************************************** -->
|
|
<p>
|
|
Existen diferentes <em/widgets/ en GTK+ que pueden ser ajustados
|
|
visualmente por el usuario mediante el ratón o el teclado. Un
|
|
ejemplo son los <em/widgets/ de selección descritos en la
|
|
sección <ref id="sec_Range_Widgets" name="Widgets de selección
|
|
de rango">. También hay otros widgets que pueden ser ajustados
|
|
parcialmente, por ejemplo el <em/widget/ de texto o el <em/viewport/.
|
|
|
|
Como es lógico el programa tiene que poder reaccionar a los
|
|
cambios que el usuario realiza en los <em/widgets/ de selección de
|
|
rango. Una forma de hacer que el programa reaccione sería tener
|
|
cada <em/widget/ emitiendo su propio tipo de señal cuando cambie el
|
|
ajuste, y bien pasar el nuevo valor al manejador de señal o bien
|
|
obligarle a que mire dentro de la estructura de datos del <em/widget/
|
|
para conocer este valor. Pero también puede ser que quiera conectar
|
|
los ajustes de varios <em/widgets/, para que así cuando se ajuste
|
|
uno, los demás se ajusten automáticamente. El ejemplo más
|
|
obvio es conectar una barra de desplazamiento a una región con
|
|
texto. Si cada <em/widget/ posee su propia forma de establecer u
|
|
obtener sus valores de ajuste el programador puede que tenga que
|
|
escribir sus propios controladores de señales para traducir el
|
|
resultado de la señal producida por un <em/widget/ como el
|
|
argumento de una función usada para determinar valores en otro
|
|
<em/widget/.
|
|
|
|
Para resolver este problema GTK+ usa objetos del tipo GtkAdjustment.
|
|
Con ellos se consigue almacenar y traspasar información de una forma
|
|
abstracta y flexible. El uso más obvio es el de almacenes de
|
|
párametros para <em/widgets/ de escala (barras deslizantes y
|
|
escalas). Como los GtkAdjustment derivan de GtkObject poseen
|
|
cualidades intrínsecas que les permiten ser algo más que simples
|
|
estructuras de datos. Lo más importante es que pueden emitir
|
|
señales que a su vez pueden ser usadas tanto para reaccionar frente
|
|
al cambio de datos introducidos por el usuario como para transferir
|
|
los nuevos valores de forma transparente entre <em/widgets/ ajustables.
|
|
|
|
<sect1>Creando un ajuste
|
|
<p>
|
|
Los ajustes se pueden crear usando:
|
|
|
|
<tscreen><verb>
|
|
GtkObject *gtk_adjustment_new( gfloat value,
|
|
gfloat lower,
|
|
gfloat upper,
|
|
gfloat step_increment,
|
|
gfloat page_increment,
|
|
gfloat page_size );
|
|
</verb></tscreen>
|
|
|
|
El argumento <tt/value/ es el valor inicial que le queremos dar
|
|
al ajuste. Normalmente se corresponde con las posiciones situadas
|
|
más arriba y a la izquierda de un <em/widget/ ajustable. El argumento
|
|
<tt/lower/ especifica los valores más pequeños que el ajuste
|
|
puede contener. A su vez con <tt/step_increment/ se especifica el
|
|
valor más pequeño en el que se puede variar la magnitud en
|
|
cuestión (valor de paso asociado), mientras que <tt/page_increment/
|
|
es el mayor. Con <tt/page_size/ se determina el valor visible de un
|
|
<em/widget/.
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1> Forma sencilla de usar los ajustes
|
|
<p>
|
|
Los <em/widgets/ ajustábles se pueden dividir en dos categorias
|
|
diferentes, aquellos que necesitan saber las unidades de la cantidad
|
|
almacenada y los que no. Este último grupo incluye los <em/widgets/
|
|
de tamaño (barras deslizantes, escalas, barras de estado, o botones
|
|
giratorios). Normalmente estos <em/widgets/ son ajustados
|
|
«directamente» por el usuario. Los argumentos <tt/lower/ y
|
|
<tt/upper/ serán los limites dentro de los cuales el usuario puede
|
|
manipular los ajustes. Por defecto sólo se modificará el
|
|
<tt/value/ (valor) de un ajuste.
|
|
|
|
El otro grupo incluye los <em/widgets/ de texto, la lista compuesta o
|
|
la ventana con barra deslizante. Estos <em/widgets/ usan valores en
|
|
pixels para sus ajustes, y normalmente son ajustados
|
|
«indirectamente» mediante barras deslizantes. Aunque todos los
|
|
<em/widgets/ pueden crear sus propios ajustes o usar otros creados por
|
|
el programador con el segundo grupo suele ser conveniente dejarles que
|
|
creen sus propios ajustes. Normalmente no tendrán en cuenta ninguno
|
|
de los valores de un ajuste proporcionado por el programador, excepto
|
|
<tt/value/, pero los resultados son, en general, indefinidos
|
|
(entiendase que tendrá que leer el código fuente para saber que
|
|
pasa con cada widget).
|
|
|
|
Probablemente ya se habrá dado cuenta de que como los <em/widgets/
|
|
de texto (y todos los <em/widgets/ del segundo grupo), insisten en
|
|
establecer todos los valores excepto <tt/value/, mientras que las
|
|
barras deslizantes sólo modifican <tt/value/, si se comparte un
|
|
objeto de ajuste entre una barra deslizante y un <em/widget/ de texto
|
|
al manipular la barra se modificará el <em/widget/ de texto. Ahora
|
|
queda completamente demostrada la utilidad de los ajustes. Veamos un
|
|
ejemplo:
|
|
|
|
<tscreen><verb>
|
|
/* creamos un ajuste */
|
|
text = gtk_text_new (NULL, NULL);
|
|
/* lo usamos con la barra deslizante */
|
|
vscrollbar = gtk_vscrollbar_new (GTK_TEXT(text)->vadj);
|
|
</verb></tscreen>
|
|
|
|
</sect1>
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1> Descripción detallada de los ajustes
|
|
<p>
|
|
Puede que se esté preguntando cómo es posible crear sus propios
|
|
controladores para responder a las modificaciones producidas por
|
|
el usuario y cómo obtener el valor del ajuste hecho por este.
|
|
Para aclarar esto y otras cosas vamos a estudiar la estructura
|
|
del ajuste
|
|
|
|
<tscreen><verb>
|
|
struct _GtkAdjustment
|
|
{
|
|
GtkData data;
|
|
|
|
gfloat lower;
|
|
gfloat upper;
|
|
gfloat value;
|
|
gfloat step_increment;
|
|
gfloat page_increment;
|
|
gfloat page_size;
|
|
};
|
|
</verb></tscreen>
|
|
|
|
Lo primero que hay que aclarar es que no hay ninguna macro o función
|
|
de acceso que permita obtener el <tt/value/ de un GtkAdjustment, por
|
|
lo que tendrá que hacerlo usted mismo. Tampoco se preocupe mucho
|
|
porque la macro <tt>GTK_ADJUSTMENT (Object)</tt> comprueba los tipos
|
|
durante el proceso de ejecución (como hacen todas las macros de GTK+
|
|
que sirven para comprobar los tipos).
|
|
|
|
Cuando se establece el <tt/value/ de un ajuste normalmente se quiere
|
|
que cualquier <em/widget/ se entere del cambio producido. Para ello
|
|
GTK+ posee una función especial:
|
|
|
|
<tscreen><verb>
|
|
void gtk_adjustment_set_value( GtkAdjustment *adjustment,
|
|
gfloat value );
|
|
</verb></tscreen>
|
|
|
|
Tal y como se mencionó antes GtkAdjustment es una subclase de GtkObject
|
|
y por tanto puede emitir señales. Así se consigue que se actualicen
|
|
los valores de los ajustes cuando se comparten entre varios <em/widgets/.
|
|
Por tanto todos los <em/widgets/ ajustables deben conectar controladores
|
|
de señales a sus señales del tipo <tt/value_changed/. Esta es la
|
|
definición de la señal como viene en <tt/struct _GtkAdjustmentClass/
|
|
|
|
<tscreen><verb>
|
|
void (* value_changed) (GtkAdjustment *adjustment);
|
|
</verb></tscreen>
|
|
|
|
Todos los <em/widgets/ que usan GtkAdjustment deben emitir esta
|
|
señal cuando cambie el valor de algún ajuste. Esto sucede cuando
|
|
el usuario cambia algo o el programa modifica los ajustes
|
|
mediante. Por ejemplo si queremos que rote una figura cuando
|
|
modificamos un <em/widget/ de escala habría que usar una respuesta
|
|
como esta:
|
|
|
|
<tscreen><verb>
|
|
void cb_rotate_picture (GtkAdjustment *adj, GtkWidget *picture)
|
|
{
|
|
set_picture_rotation (picture, adj->value);
|
|
...
|
|
</verb></tscreen>
|
|
|
|
y conectarla con el ajuste del <em/widget/ de escala mediante:
|
|
|
|
<tscreen><verb>
|
|
gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
|
|
GTK_SIGNAL_FUNC (cb_rotate_picture), picture);
|
|
</verb></tscreen>
|
|
¿Qué pasa cuando un <em/widget/ reconfigura los valores
|
|
<tt/upper/ o <tt/lower/ (por ejemplo cuando se añade más texto)?
|
|
Simplemente que se emite la señal <tt/changed/, que debe ser
|
|
parecida a:
|
|
|
|
<tscreen><verb>
|
|
void (* changed) (GtkAdjustment *adjustment);
|
|
</verb></tscreen>
|
|
|
|
Los <em/widgets/ de tamaño normalmente conectan un controlador a
|
|
esta señal, que cambia el aspecto de éste para reflejar el
|
|
cambio. Por ejemplo el tamaño de la guía en una barra deslizante
|
|
que se alarga o encoge según la inversa de la diferencia de los
|
|
valores <tt/lower/ y <tt/upper/.
|
|
|
|
Probablemente nunca tenga que conectar un controlador a esta señal
|
|
a no ser que esté escribiendo un nuevo tipo de <em/widget/. Pero si
|
|
cambia directamente alguno de los valores de GtkAdjustment debe hacer
|
|
que se emita la siguiente señal para reconfigurar todos aquellos
|
|
<em/widgets/ que usen ese ajuste:
|
|
|
|
<tscreen><verb>
|
|
gtk_signal_emit_by_name (GTK_OBJECT (adjustment), "changed");
|
|
</verb></tscreen>
|
|
|
|
</sect1>
|
|
</sect>
|
|
<!-- ***************************************************************** -->
|
|
<sect>Los <em/widgets/ de selección de rango <label id="sec_Range_Widgets">
|
|
<!-- ***************************************************************** -->
|
|
<p>
|
|
Este tipo de <em/widgets/ incluye a las barras de desplazamiento
|
|
(<em>scroollbar</em>) y la menos conocida escala
|
|
(<em/scale</em>). Ambos pueden ser usados para muchas cosas, pero como
|
|
sus funciones y su implementación son muy parecidas los describimos
|
|
al mismo tiempo. Principalmente se utilizan para permitirle al usuario
|
|
escoger un valor dentro de un rango ya prefijado.
|
|
|
|
Todos los <em/widgets/ de selección comparten elementos
|
|
gráficos, cada uno de los cuales tiene su propia ventana X window y
|
|
recibe eventos. Todos contienen una guía y un rectángulo para
|
|
determinar la posición dentro de la guía (en una procesador de
|
|
textos con entorno gráfico se encuentra situado a la derecha del
|
|
texto y sirve para situarnos en las diferentes partes del texto). Con
|
|
el ratón podemos subir o bajar el rectángulo, mientras que si
|
|
hacemos `click' dentro de la guía, pero no sobre el rectángulo,
|
|
este se mueve hacia donde hemos hecho el click. Dependiendo del
|
|
botón pulsado el rectángulo se moverá hasta la posición
|
|
del click o una cantidad prefijada de ante mano.
|
|
|
|
Tal y como se mencionó en <ref id="sec_Adjustment" name="Ajustes">
|
|
todos los <em/widgets/ usados para seleccionar un rango estan
|
|
asociados con un objeto de ajuste, a partir del cual calculan la
|
|
longitud de la barra y su posición. Cuando el usuario manipula la
|
|
barra de desplazamiento el widget cambiará el valor del ajuste.
|
|
|
|
<sect1>El <em/widget/ barra de desplazamiento
|
|
<p>
|
|
El <em/widget/ barra de desplazamiento solamente debe utilizarse para
|
|
hacer <em/scroll/ sobre otro <em/widget/, como una lista, una caja de
|
|
texto, o un puerto de visión (y en muchos es más fácil utilizar
|
|
el <em/widget/ scrolled window). Para el resto de los casos, debería
|
|
utilizar los <em/widgets/ de escala, ya son más sencillos de usar y
|
|
más potentes.
|
|
|
|
Hay dos tipos separados de barras de desplazamiento, según sea
|
|
horizontal o vertical. Realmente no hay mucho que añadir. Puede
|
|
crear estos <em/widgets/ utilizar las funciones siguientes, definidas
|
|
en <tt><gtk/gtkhscrollbar.h></tt> y
|
|
<tt><gtk/gtkvscrollbar.h></tt>:
|
|
|
|
<tscreen><verb>
|
|
GtkWidget* gtk_hscrollbar_new( GtkAdjustment *adjustment );
|
|
|
|
GtkWidget* gtk_vscrollbar_new( GtkAdjustment *adjustment );
|
|
</verb></tscreen>
|
|
|
|
y esto es todo lo que hay (si no me cree, ¡mire los ficheros de
|
|
cabecera!). El argumento <tt/adjustment/ puede ser un puntero a un
|
|
ajuste ya existente, o puede ser NULL, en cuyo caso se creará
|
|
uno. Es útil especificar NULL si quiere pasar el ajuste recién
|
|
creado a la función constructora de algún otro <em/widget/ (como
|
|
por ejemplo el <em/widget/ texto) que se ocupará de configurarlo
|
|
correctamente por usted.
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1><em/Widgets/ de escala
|
|
<p>
|
|
Los <em/widgets/ de escala se usan para determinar el valor de una
|
|
cantidad que se puede interpretar visualmente. El usuario
|
|
probablemente fijará el valor a ojo. Por ejemplo el <em/widget/
|
|
GtkColorSelection contiene <em/widgets/ de escala que controlan las
|
|
componentes del color a seleccionar. Normalmente el valor preciso es
|
|
menos importante que el efecto visual, por lo que el color se
|
|
selecciona con el ratón y no mediante un número concreto.
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect2>Creación de un <em/widget/ de escala
|
|
<p>
|
|
Existen dos tipos de <em/widgets/ de escala: GtkHScale (que es
|
|
horizontal) y GtkVscale (vertical). Como funcionan de la misma manera
|
|
los vamos a describir a la vez. Las funciones definidas en
|
|
<tt><gtk/gtkvscale.h></tt> y <tt><gtk/gtkhscale.h></tt>,
|
|
crean <em/widgets/ de escala verticales y horizontales
|
|
respectivamente.
|
|
|
|
<tscreen><verb>
|
|
GtkWidget* gtk_vscale_new( GtkAdjustment *adjustment );
|
|
|
|
GtkWidget* gtk_hscale_new( GtkAdjustment *adjustment );
|
|
</verb></tscreen>
|
|
|
|
El <tt/ajuste/ (adjustment) puede ser tanto un ajuste creado
|
|
mediante <tt/gtk_adjustment_new()/ como <tt/NULL/. En este
|
|
último caso se crea un GtkAdjustment anónimo con todos sus
|
|
valores iguales a <tt/0.0/. Si no ha quedado claro el uso de esta
|
|
función consulte la sección <ref id="sec_Adjustment"
|
|
name="Ajustes"> para una discusión más detallada.
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect2> Funciones y señales
|
|
<p>
|
|
Los <em/widgets/ de escala pueden indicar su valor actual como un
|
|
número. Su comportamiento por defecto es mostrar este valor, pero
|
|
se puede modificar usando:
|
|
|
|
<tscreen><verb>
|
|
void gtk_scale_set_draw_value( GtkScale *scale,
|
|
gint draw_value );
|
|
</verb></tscreen>
|
|
|
|
Los valores posibles de <tt/draw_value son/ son <tt/TRUE/ o <tt/FALSE/.
|
|
Con el primero se muestra el valor y con el segundo no.
|
|
|
|
El valor mostrado por un <em/widget/ de escala por defecto se redondea
|
|
a un valor decimal (igual que con <tt/value/ en un GtkAdjustment). Se
|
|
puede cambiar con:
|
|
|
|
<tscreen>
|
|
<verb>
|
|
void gtk_scale_set_digits( GtkScale *scale,
|
|
gint digits );
|
|
</verb>
|
|
</tscreen>
|
|
|
|
donde <tt/digits/ es el número de posiciones decimales que se
|
|
quiera. En la práctica sólo se mostrarán 13 como máximo.
|
|
|
|
Por último, el valor se puede dibujar en diferentes posiciones con
|
|
respecto a la posición del rectangulo que hay dentro de la guía:
|
|
|
|
<tscreen>
|
|
<verb>
|
|
void gtk_scale_set_value_pos( GtkScale *scale,
|
|
GtkPositionType pos );
|
|
</verb>
|
|
</tscreen>
|
|
|
|
Si ha leido la sección acerca del <em/widget/ libro de notas
|
|
entonces ya conoce cuales son los valores posibles de <tt/pos/. Estan
|
|
definidos en <tt><gtk/gtkscale.h></tt> como <tt/enum GtkPositionType/
|
|
y son auto explicatorios. Si se escoge un lateral de la guía,
|
|
entonces seguirá al rectángulo a lo largo de la guía.
|
|
|
|
Todas las funcioenes precedentes se encuentran definidas en:
|
|
<tt><gtk/gtkscale.h></tt>.
|
|
</sect2>
|
|
</sect1>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1> Funciones comunes <label id="sec_funciones_range">
|
|
<p>
|
|
La descripción interna de la clase GtkRange es bastante complicada,
|
|
pero al igual que con el resto de las «clases base» sólo es
|
|
interesante si se quiere «hackear». Casi todas las señales y
|
|
funciones sólo son útiles para desarrollar derivados. Para un
|
|
usuario normal las funciones interesantes son aquellas definidas en:
|
|
<tt><gtk/gtkrange.h></tt> y funcionan igual en todos los
|
|
<em/widgets/ de rango.
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect2> Estableciendo cada cúanto se actualizan
|
|
<p>
|
|
La política de actualización de un <em/widget/ define en que
|
|
puntos de la interacción con el usuario debe cambiar el valor
|
|
<tt/value/ en su GtkAdjustment y emitir la señal
|
|
«value_changed». Las actualizaciones definidas en
|
|
<tt><gtk/gtkenums.h></tt> como <tt>enum GtkUpdateType</tt>, son:
|
|
|
|
<itemize>
|
|
<item>GTK_UPDATE_POLICY_CONTINUOUS - Este es el valor por defecto.La
|
|
señal «value_changed» se emite continuamente, por ejemplo cuando
|
|
la barra deslizante se mueve incluso aunque sea un poquito.
|
|
</item>
|
|
<item>GTK_UPDATE_POLICY_DISCONTINUOUS - La señal «value_changed»
|
|
sólo se emite cuando se ha parado de mover la barra y el usuario ha
|
|
soltado el botón del ratón.
|
|
</item>
|
|
<item>GTK_UPDATE_POLICY_DELAYED - La señal sólo se emite cuando
|
|
el usuario suelta el botón del ratón o si la barra no se mueve
|
|
durante un periodo largo de tiempo.
|
|
</item>
|
|
</itemize>
|
|
|
|
Para establecer la política de actualización se usa la
|
|
conversión definida en la macro
|
|
|
|
<tscreen><verb>
|
|
void gtk_range_set_update_policy( GtkRange *range,
|
|
GtkUpdateType policy) ;
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect2>Obteniendo y estableciendo Ajustes
|
|
<p>
|
|
Para obtener o establecer el ajuste de un <em/widget/ de rango se usa:
|
|
|
|
<tscreen><verb>
|
|
GtkAdjustment* gtk_range_get_adjustment( GtkRange *range );
|
|
|
|
void gtk_range_set_adjustment( GtkRange *range,
|
|
GtkAdjustment *adjustment );
|
|
</verb></tscreen>
|
|
|
|
La función <tt/gtk_range_get_adjustment()/ devuelve un puntero al
|
|
ajuste al que <tt/range/ esté conectado.
|
|
|
|
La función <tt/gtk_range_set_adjustment()/ no hace nada si se le
|
|
pasa como argumento el valor <tt/range/ del ajuste que esta siendo
|
|
usado (aunque se haya modificado algún valor). En el caso de que
|
|
sea un ajuste nuevo (GtkAdjustment) dejará de usar el antiguo
|
|
(probablemente lo destruirá) y conectará las señales
|
|
apropiadas al nuevo. A continuación llamará a la función
|
|
<tt/gtk_range_adjustment_changed()/ que en teoría recalculará el
|
|
tamaño y/o la posición de la barra, redibujándola en caso de
|
|
que sea necesario. Tal y como se mencionó en la sección de los
|
|
ajustes si se quiere reusar el mismo GtkAdjustment cuando se modifican
|
|
sus valores se debe emitir la señal «changed». Por ejemplo:
|
|
|
|
<tscreen><verb>
|
|
gtk_signal_emit_by_name (GTK_OBJECT (adjustment), "changed");
|
|
</verb></tscreen>
|
|
</sect2>
|
|
</sect1>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1> Enlaces con el teclado y el ratón
|
|
<p>
|
|
Todos los <em/widgets/ de rango reaccionan más o menos de la misma
|
|
manera a las pulsaciones del ratón. Al pulsar el botón 1 sobre
|
|
el rectángulo de la barra el <tt/value/ del ajuste aumentará o
|
|
disminuirá según <tt/page_increment/. Con el botón 2 la barra
|
|
se desplazará al punto en el que el botón fue pulsado. Con cada
|
|
pulsación de cualquier botón sobre las flechas el valor del
|
|
ajuste se modifica una cantidad igual a <tt/step_increment/.
|
|
|
|
|
|
Acostumbrarse a que tanto las barras deslizantes como los <em/widgets/ de
|
|
escala puedan tomar la atención del teclado puede ser un proceso largo.
|
|
Si que se cree que los usuarios no lo van a entender se puede anular
|
|
mediante la función GTK_WIDGET_UNSET_FLAGS y con GTK_CAN_FOCUS como
|
|
argumento:
|
|
|
|
<tscreen><verb>
|
|
GTK_WIDGET_UNSET_FLAGS (scrollbar, GTK_CAN_FOCUS);
|
|
</verb></tscreen>
|
|
|
|
Los enlaces entre teclas (que sólo estan activos cuando el
|
|
<em/widget/ tiene la atención (focus)) se comportan de manera
|
|
diferente para los <em/widgets/ de rango horizontales que para los
|
|
verticales. También son diferentes para los <em/widgets/ de escala
|
|
y para las barras deslizantes. (Simplemente para evitar confusiones
|
|
entre las teclas de las barras deslizantes horizontales y verticales,
|
|
ya que ambas actúan sobre la misma área)
|
|
|
|
<sect2><em/Widgets/ de rango vertical
|
|
<p>
|
|
Todos los <em/widgets/ de rango pueden ser manipulados con las teclas
|
|
arriba, abajo, <tt/Re Pág/, <tt/ Av Pág/. Las flechas mueven las
|
|
barras la cantidad fijada mediante <tt/step_increment/, mientras que
|
|
<tt/Re Pág/ y <tt/Av Pag/ lo hacen según <tt/page_increment/.
|
|
|
|
El usuario también puede mover la barra de un extremo al otro de la
|
|
guía mediante el teclado. Con el <em/widget/ GtkVScale podemos ir a
|
|
los extremos utilizando las teclas <tt/Inicio/ y <tt/Final/ mientras
|
|
que con el <em/widget/ GtkVScrollbar habrá que utilizar
|
|
<tt/Control-Re Pág/ y <tt/Control-Av Pág/.
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect2><em/Widgets/ de rango horizontal
|
|
<p>
|
|
Las teclas izquierda y derecha funcionan tal y como espera que
|
|
funcionen en estos <em/widgets/: mueven la barra una cantidad dada por
|
|
<tt/step_increment/. A su vez <tt/Inicio/ y <tt/Final/ sirven para
|
|
pasar de un extremo al otro de la guía. Para el <em/widget/
|
|
GtkHScale el mover la barra una cantidad dada por <tt/page_increment/
|
|
se consigue mediante <tt>Control-Izquierda</tt> y
|
|
<tt>Control-derecha</tt>, mientras que para el <em/widget/
|
|
GtkHScrollbar se consigue con <tt/Control-Inicio/ y
|
|
<tt/Control-Final/.
|
|
</sect2>
|
|
</sect1>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1> Ejemplo <label id="sec_Ejemplo_Rango">
|
|
<p>
|
|
Este ejemplo es una versión modificada del test «range controls»
|
|
que a su vez forma parte de <tt/testgtk.c/. Simplemente dibuja una
|
|
ventana con tres <em/widgets/ de rango conectados al mismo ajuste, y
|
|
un conjunto de controles para ajustar algunos de los parámetros
|
|
ya mencionados. Así se consigue ver como funcionan estos
|
|
<em/widgets/ al ser manipulados por el usuario.
|
|
|
|
<tscreen><verb>
|
|
/* principio del ejemplo widgets de selección de rango rangewidgets.c */
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
GtkWidget *hscale, *vscale;
|
|
|
|
void cb_pos_menu_select( GtkWidget *item,
|
|
GtkPositionType pos )
|
|
{
|
|
/* Establece el valor position en los widgets de escala */
|
|
gtk_scale_set_value_pos (GTK_SCALE (hscale), pos);
|
|
gtk_scale_set_value_pos (GTK_SCALE (vscale), pos);
|
|
}
|
|
|
|
void cb_update_menu_select( GtkWidget *item,
|
|
GtkUpdateType policy )
|
|
{
|
|
/* Establece la política de actualización para los widgets
|
|
* de escala */
|
|
gtk_range_set_update_policy (GTK_RANGE (hscale), policy);
|
|
gtk_range_set_update_policy (GTK_RANGE (vscale), policy);
|
|
}
|
|
|
|
void cb_digits_scale( GtkAdjustment *adj )
|
|
{
|
|
/* Establece el número de cifras decimales a las que se
|
|
* redondeará adj->value */
|
|
gtk_scale_set_digits (GTK_SCALE (hscale), (gint) adj->value);
|
|
gtk_scale_set_digits (GTK_SCALE (vscale), (gint) adj->value);
|
|
}
|
|
|
|
void cb_page_size( GtkAdjustment *get,
|
|
GtkAdjustment *set )
|
|
{
|
|
/* Establece el tamaño de la página y el incremento del
|
|
* ajuste al valor especificado en la escala "Page Size" */
|
|
set->page_size = get->value;
|
|
set->page_increment = get->value;
|
|
/* Ahora emite la señal "changed" para reconfigurar todos los
|
|
* widgets que están enlazados a este ajuste */
|
|
gtk_signal_emit_by_name (GTK_OBJECT (set), "changed");
|
|
}
|
|
|
|
void cb_draw_value( GtkToggleButton *boton )
|
|
{
|
|
/* Activa o desactiva el valor display en los widgets de escala
|
|
* dependiendo del estado del botón de comprobación */
|
|
gtk_scale_set_draw_value (GTK_SCALE (hscale), boton->active);
|
|
gtk_scale_set_draw_value (GTK_SCALE (vscale), boton->active);
|
|
}
|
|
|
|
/* Funciones varias */
|
|
|
|
GtkWidget *make_menu_item( gchar *name,
|
|
GtkSignalFunc callback,
|
|
gpointer data )
|
|
{
|
|
GtkWidget *item;
|
|
|
|
item = gtk_menu_item_new_with_label (name);
|
|
gtk_signal_connect (GTK_OBJECT (item), "activate",
|
|
callback, data);
|
|
gtk_widget_show (item);
|
|
|
|
return(item);
|
|
}
|
|
|
|
void scale_set_default_values( GtkScale *scale )
|
|
{
|
|
gtk_range_set_update_policy (GTK_RANGE (scale),
|
|
GTK_UPDATE_CONTINUOUS);
|
|
gtk_scale_set_digits (scale, 1);
|
|
gtk_scale_set_value_pos (scale, GTK_POS_TOP);
|
|
gtk_scale_set_draw_value (scale, TRUE);
|
|
}
|
|
|
|
/* crea la ventana principal */
|
|
|
|
void create_range_controls( void )
|
|
{
|
|
GtkWidget *ventana;
|
|
GtkWidget *caja1, *caja2, *caja3;
|
|
GtkWidget *boton;
|
|
GtkWidget *scrollbar;
|
|
GtkWidget *separator;
|
|
GtkWidget *opt, *menu, *item;
|
|
GtkWidget *etiqueta;
|
|
GtkWidget *scale;
|
|
GtkObject *adj1, *adj2;
|
|
|
|
/* creación estándar de una ventana */
|
|
ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
|
gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
|
|
GTK_SIGNAL_FUNC(gtk_main_quit),
|
|
NULL);
|
|
gtk_window_set_title (GTK_WINDOW (ventana), "range controls");
|
|
|
|
caja1 = gtk_vbox_new (FALSE, 0);
|
|
gtk_container_add (GTK_CONTAINER (ventana), caja1);
|
|
gtk_widget_show (caja1);
|
|
|
|
caja2 = gtk_hbox_new (FALSE, 10);
|
|
gtk_container_border_width (GTK_CONTAINER (caja2), 10);
|
|
gtk_box_pack_start (GTK_BOX (caja1), caja2, TRUE, TRUE, 0);
|
|
gtk_widget_show (caja2);
|
|
|
|
/* value, lower, upper, step_increment, page_increment, page_size */
|
|
/* Observe que el valor de page_size solo sirve para los widgets
|
|
* barras de desplazamiento (scrollbar), y que el valor más
|
|
* alto que obtendrá será (upper - page_size). */
|
|
adj1 = gtk_adjustment_new (0.0, 0.0, 101.0, 0.1, 1.0, 1.0);
|
|
|
|
vscale = gtk_vscale_new (GTK_ADJUSTMENT (adj1));
|
|
scale_set_default_values (GTK_SCALE (vscale));
|
|
gtk_box_pack_start (GTK_BOX (caja2), vscale, TRUE, TRUE, 0);
|
|
gtk_widget_show (vscale);
|
|
|
|
caja3 = gtk_vbox_new (FALSE, 10);
|
|
gtk_box_pack_start (GTK_BOX (caja2), caja3, TRUE, TRUE, 0);
|
|
gtk_widget_show (caja3);
|
|
|
|
/* Reutilizamos el mismo ajuste */
|
|
hscale = gtk_hscale_new (GTK_ADJUSTMENT (adj1));
|
|
gtk_widget_set_usize (GTK_WIDGET (hscale), 200, 30);
|
|
scale_set_default_values (GTK_SCALE (hscale));
|
|
gtk_box_pack_start (GTK_BOX (caja3), hscale, TRUE, TRUE, 0);
|
|
gtk_widget_show (hscale);
|
|
|
|
/* Reutilizamos de nuevo el mismo ajuste */
|
|
scrollbar = gtk_hscrollbar_new (GTK_ADJUSTMENT (adj1));
|
|
/* Observe que con esto conseguimos que la escala siempre se
|
|
* actualice de una forma continua cuando se mueva la barra de
|
|
* desplazamiento */
|
|
gtk_range_set_update_policy (GTK_RANGE (scrollbar),
|
|
GTK_UPDATE_CONTINUOUS);
|
|
gtk_box_pack_start (GTK_BOX (caja3), scrollbar, TRUE, TRUE, 0);
|
|
gtk_widget_show (scrollbar);
|
|
|
|
caja2 = gtk_hbox_new (FALSE, 10);
|
|
gtk_container_border_width (GTK_CONTAINER (caja2), 10);
|
|
gtk_box_pack_start (GTK_BOX (caja1), caja2, TRUE, TRUE, 0);
|
|
gtk_widget_show (caja2);
|
|
|
|
/* Un botón para comprobar si el valor se muestra o no*/
|
|
boton = gtk_check_button_new_with_label("Display value on scale widgets");
|
|
gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (boton), TRUE);
|
|
gtk_signal_connect (GTK_OBJECT (boton), "toggled",
|
|
GTK_SIGNAL_FUNC(cb_draw_value), NULL);
|
|
gtk_box_pack_start (GTK_BOX (caja2), boton, TRUE, TRUE, 0);
|
|
gtk_widget_show (boton);
|
|
|
|
caja2 = gtk_hbox_new (FALSE, 10);
|
|
gtk_container_border_width (GTK_CONTAINER (caja2), 10);
|
|
|
|
/* Una opción en el menú para cambiar la posición del
|
|
* valor */
|
|
etiqueta = gtk_label_new ("Scale Value Position:");
|
|
gtk_box_pack_start (GTK_BOX (caja2), etiqueta, FALSE, FALSE, 0);
|
|
gtk_widget_show (etiqueta);
|
|
|
|
opt = gtk_option_menu_new();
|
|
menu = gtk_menu_new();
|
|
|
|
item = make_menu_item ("Top",
|
|
GTK_SIGNAL_FUNC(cb_pos_menu_select),
|
|
GINT_TO_POINTER (GTK_POS_TOP));
|
|
gtk_menu_append (GTK_MENU (menu), item);
|
|
|
|
item = make_menu_item ("Bottom", GTK_SIGNAL_FUNC (cb_pos_menu_select),
|
|
GINT_TO_POINTER (GTK_POS_BOTTOM));
|
|
gtk_menu_append (GTK_MENU (menu), item);
|
|
|
|
item = make_menu_item ("Left", GTK_SIGNAL_FUNC (cb_pos_menu_select),
|
|
GINT_TO_POINTER (GTK_POS_LEFT));
|
|
gtk_menu_append (GTK_MENU (menu), item);
|
|
|
|
item = make_menu_item ("Right", GTK_SIGNAL_FUNC (cb_pos_menu_select),
|
|
GINT_TO_POINTER (GTK_POS_RIGHT));
|
|
gtk_menu_append (GTK_MENU (menu), item);
|
|
|
|
gtk_option_menu_set_menu (GTK_OPTION_MENU (opt), menu);
|
|
gtk_box_pack_start (GTK_BOX (caja2), opt, TRUE, TRUE, 0);
|
|
gtk_widget_show (opt);
|
|
|
|
gtk_box_pack_start (GTK_BOX (caja1), caja2, TRUE, TRUE, 0);
|
|
gtk_widget_show (caja2);
|
|
|
|
caja2 = gtk_hbox_new (FALSE, 10);
|
|
gtk_container_border_width (GTK_CONTAINER (caja2), 10);
|
|
|
|
/* Sí, otra opción de menú, esta vez para la política
|
|
* de actualización de los widgets */
|
|
etiqueta = gtk_label_new ("Scale Update Policy:");
|
|
gtk_box_pack_start (GTK_BOX (caja2), etiqueta, FALSE, FALSE, 0);
|
|
gtk_widget_show (etiqueta);
|
|
|
|
opt = gtk_option_menu_new();
|
|
menu = gtk_menu_new();
|
|
|
|
item = make_menu_item ("Continuous",
|
|
GTK_SIGNAL_FUNC (cb_update_menu_select),
|
|
GINT_TO_POINTER (GTK_UPDATE_CONTINUOUS));
|
|
gtk_menu_append (GTK_MENU (menu), item);
|
|
|
|
item = make_menu_item ("Discontinuous",
|
|
GTK_SIGNAL_FUNC (cb_update_menu_select),
|
|
GINT_TO_POINTER (GTK_UPDATE_DISCONTINUOUS));
|
|
gtk_menu_append (GTK_MENU (menu), item);
|
|
|
|
item = make_menu_item ("Delayed",
|
|
GTK_SIGNAL_FUNC (cb_update_menu_select),
|
|
GINT_TO_POINTER (GTK_UPDATE_DELAYED));
|
|
gtk_menu_append (GTK_MENU (menu), item);
|
|
|
|
gtk_option_menu_set_menu (GTK_OPTION_MENU (opt), menu);
|
|
gtk_box_pack_start (GTK_BOX (caja2), opt, TRUE, TRUE, 0);
|
|
gtk_widget_show (opt);
|
|
|
|
gtk_box_pack_start (GTK_BOX (caja1), caja2, TRUE, TRUE, 0);
|
|
gtk_widget_show (caja2);
|
|
|
|
caja2 = gtk_hbox_new (FALSE, 10);
|
|
gtk_container_border_width (GTK_CONTAINER (caja2), 10);
|
|
|
|
/* Un widget GtkHScale para ajustar el número de dígitos en
|
|
* la escala. */
|
|
etiqueta = gtk_label_new ("Scale Digits:");
|
|
gtk_box_pack_start (GTK_BOX (caja2), etiqueta, FALSE, FALSE, 0);
|
|
gtk_widget_show (etiqueta);
|
|
|
|
adj2 = gtk_adjustment_new (1.0, 0.0, 5.0, 1.0, 1.0, 0.0);
|
|
gtk_signal_connect (GTK_OBJECT (adj2), "value_changed",
|
|
GTK_SIGNAL_FUNC (cb_digits_scale), NULL);
|
|
scale = gtk_hscale_new (GTK_ADJUSTMENT (adj2));
|
|
gtk_scale_set_digits (GTK_SCALE (scale), 0);
|
|
gtk_box_pack_start (GTK_BOX (caja2), scale, TRUE, TRUE, 0);
|
|
gtk_widget_show (scale);
|
|
|
|
gtk_box_pack_start (GTK_BOX (caja1), caja2, TRUE, TRUE, 0);
|
|
gtk_widget_show (caja2);
|
|
|
|
caja2 = gtk_hbox_new (FALSE, 10);
|
|
gtk_container_border_width (GTK_CONTAINER (caja2), 10);
|
|
|
|
/* Y un último widget GtkHScale para ajustar el tamaño de la
|
|
* página de la barra de desplazamiento. */
|
|
etiqueta = gtk_label_new ("Scrollbar Page Size:");
|
|
gtk_box_pack_start (GTK_BOX (caja2), etiqueta, FALSE, FALSE, 0);
|
|
gtk_widget_show (etiqueta);
|
|
|
|
adj2 = gtk_adjustment_new (1.0, 1.0, 101.0, 1.0, 1.0, 0.0);
|
|
gtk_signal_connect (GTK_OBJECT (adj2), "value_changed",
|
|
GTK_SIGNAL_FUNC (cb_page_size), adj1);
|
|
scale = gtk_hscale_new (GTK_ADJUSTMENT (adj2));
|
|
gtk_scale_set_digits (GTK_SCALE (scale), 0);
|
|
gtk_box_pack_start (GTK_BOX (caja2), scale, TRUE, TRUE, 0);
|
|
gtk_widget_show (scale);
|
|
|
|
gtk_box_pack_start (GTK_BOX (caja1), caja2, TRUE, TRUE, 0);
|
|
gtk_widget_show (caja2);
|
|
|
|
separator = gtk_hseparator_new ();
|
|
gtk_box_pack_start (GTK_BOX (caja1), separator, FALSE, TRUE, 0);
|
|
gtk_widget_show (separator);
|
|
|
|
caja2 = gtk_vbox_new (FALSE, 10);
|
|
gtk_container_border_width (GTK_CONTAINER (caja2), 10);
|
|
gtk_box_pack_start (GTK_BOX (caja1), caja2, FALSE, TRUE, 0);
|
|
gtk_widget_show (caja2);
|
|
|
|
boton = gtk_button_new_with_label ("Quit");
|
|
gtk_signal_connect_object (GTK_OBJECT (boton), "clicked",
|
|
GTK_SIGNAL_FUNC(gtk_main_quit),
|
|
NULL);
|
|
gtk_box_pack_start (GTK_BOX (caja2), boton, TRUE, TRUE, 0);
|
|
GTK_WIDGET_SET_FLAGS (boton, GTK_CAN_DEFAULT);
|
|
gtk_widget_grab_default (boton);
|
|
gtk_widget_show (boton);
|
|
|
|
gtk_widget_show (ventana);
|
|
}
|
|
|
|
int main( int argc,
|
|
char *argv[] )
|
|
{
|
|
gtk_init(&argc, &argv);
|
|
|
|
create_range_controls();
|
|
|
|
gtk_main();
|
|
|
|
return(0);
|
|
}
|
|
|
|
/* fin del ejemplo */
|
|
</verb></tscreen>
|
|
|
|
Observe que el programa no llama a <tt/gtk_signal_connect/ para
|
|
conectar el «delete_event», y que sólo conecta la señal
|
|
«destroy». Con esto seguimos realizando la función deseada, ya que
|
|
un «delete_event» no manejado desenboca en una señal «destroy»
|
|
para la ventana.
|
|
</sect1>
|
|
</sect>
|
|
|
|
<!-- ***************************************************************** -->
|
|
<sect><em/Widgets/ varios
|
|
<!-- ***************************************************************** -->
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1> Etiquetas
|
|
<p>
|
|
Las etiquetas se usan mucho en GTK y son bastante simples de manejar.
|
|
No pueden emitir señales ya que no tienen ventanas X window
|
|
asociadas. Si se desea capturar señales se debe usar el <em/widget/
|
|
EventBox o un <em/widget/ botón.
|
|
|
|
Para crear una nueva etiqueta se usa:
|
|
|
|
<tscreen><verb>
|
|
GtkWidget *gtk_label_new( char *str );
|
|
</verb></tscreen>
|
|
|
|
El único argumento es la cadena de texto que se quiere mostrar.
|
|
|
|
Para cambiarla después de que haya sido creada se usa:
|
|
|
|
<tscreen><verb>
|
|
void gtk_label_set( GtkLabel *etiqueta,
|
|
char *str );
|
|
</verb></tscreen>
|
|
|
|
En este caso el primer argumento es la etiqueta ya creada (cambiado su
|
|
tipo mediante la macro <tt/GTK_LABEL()/) y el segundo es la nueva cadena.
|
|
El espacio que necesite la nueva etiqueta se ajustará
|
|
automáticamente, si es necesario.
|
|
|
|
Para obtener el estado de la cadena en un momento dado existe la
|
|
función:
|
|
|
|
<tscreen><verb>
|
|
void gtk_label_get( GtkLabel *etiqueta,
|
|
char **str );
|
|
</verb></tscreen>
|
|
El primer argumento es la etiqueta, mientras que el segundo es el
|
|
valor devuelto para la cadena. No libere la memoria de la cadena
|
|
devuelta, ya que se utiliza internamente por GTK.
|
|
|
|
El texto de la etiqueta se puede justificar utilizando:
|
|
|
|
<tscreen><verb>
|
|
void gtk_label_set_justify( GtkLabel *etiqueta,
|
|
GtkJustification jtype );
|
|
</verb></tscreen>
|
|
|
|
Los valores posibles para <tt/jtype/ son:
|
|
<itemize>
|
|
<item> GTK_JUSTIFY_LEFT
|
|
<item> GTK_JUSTIFY_RIGHT
|
|
<item> GTK_JUSTIFY_CENTER (the default)
|
|
<item> GTK_JUSTIFY_FILL
|
|
</itemize>
|
|
|
|
El <em/widget/ etiqueta también es capaz de separar el texto de forma
|
|
automática cuando se llega al final de una linea. Esto se puede
|
|
conseguir utilizando:
|
|
|
|
<tscreen><verb>
|
|
void gtk_label_set_line_wrap (GtkLabel *etiqueta,
|
|
gboolean wrap);
|
|
</verb></tscreen>
|
|
|
|
El argumento <tt/wrap/ toma el valor TRUE o FALSE.
|
|
|
|
Si quiere que su etiqueta salga subrayada, puede especificar un motivo
|
|
para el subrayado con:
|
|
|
|
<tscreen><verb>
|
|
void gtk_label_set_pattern (GtkLabel *etiqueta,
|
|
const gchar *pattern);
|
|
</verb></tscreen>
|
|
|
|
El argumento <tt/pattern/ indica cual debe ser el aspecto del
|
|
subrayado. Consiste en una cadena de espacios en blanco y carácteres
|
|
de subrayado. Por ejemplo, la cadena <tt/"__ __"/ debe hacer que
|
|
se subrayen los dos primeros y el octavo y el noveno carácter.
|
|
|
|
A continuación tenemos un pequeño ejemplo que ilustra el uso de estas
|
|
funciones. Este ejemplo utiliza el <em/widget/ marco (<em/frame/) para
|
|
hacer una mejor demostración de los estilos de la etiqueta. Por ahora
|
|
puede ignorarlo, ya que el <em/widget/ <ref id="sec_Frames"
|
|
name="Frame"> se explicará más tarde.
|
|
|
|
<tscreen><verb>
|
|
/* principio del ejemplo label label.c */
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
int main( int argc,
|
|
char *argv[] )
|
|
{
|
|
static GtkWidget *ventana = NULL;
|
|
GtkWidget *hbox;
|
|
GtkWidget *vbox;
|
|
GtkWidget *frame;
|
|
GtkWidget *etiqueta;
|
|
|
|
/* Inicializa GTK */
|
|
gtk_init(&argc, &argv);
|
|
|
|
ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
|
gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
|
|
GTK_SIGNAL_FUNC(gtk_main_quit),
|
|
NULL);
|
|
|
|
gtk_window_set_title (GTK_WINDOW (ventana), "Etiqueta");
|
|
vbox = gtk_vbox_new (FALSE, 5);
|
|
hbox = gtk_hbox_new (FALSE, 5);
|
|
gtk_container_add (GTK_CONTAINER (ventana), hbox);
|
|
gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
|
|
gtk_container_set_border_width (GTK_CONTAINER (ventana), 5);
|
|
|
|
frame = gtk_frame_new ("Normal Label");
|
|
etiqueta = gtk_label_new ("This is a Normal label");
|
|
gtk_container_add (GTK_CONTAINER (frame), etiqueta);
|
|
gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
|
|
|
|
frame = gtk_frame_new ("Multi-line Label");
|
|
etiqueta = gtk_label_new ("This is a Multi-line label.\nSecond line\n" \
|
|
"Third line");
|
|
gtk_container_add (GTK_CONTAINER (frame), etiqueta);
|
|
gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
|
|
|
|
frame = gtk_frame_new ("Left Justified Label");
|
|
etiqueta = gtk_label_new ("This is a Left-Justified\n" \
|
|
"Multi-line label.\nThird line");
|
|
gtk_label_set_justify (GTK_LABEL (etiqueta), GTK_JUSTIFY_LEFT);
|
|
gtk_container_add (GTK_CONTAINER (frame), etiqueta);
|
|
gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
|
|
|
|
frame = gtk_frame_new ("Right Justified Label");
|
|
etiqueta = gtk_label_new ("This is a Right-Justified\nMulti-line label.\n" \
|
|
"Fourth line, (j/k)");
|
|
gtk_label_set_justify (GTK_LABEL (etiqueta), GTK_JUSTIFY_RIGHT);
|
|
gtk_container_add (GTK_CONTAINER (frame), etiqueta);
|
|
gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
|
|
|
|
vbox = gtk_vbox_new (FALSE, 5);
|
|
gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
|
|
frame = gtk_frame_new ("Line wrapped label");
|
|
etiqueta = gtk_label_new ("This is an example of a line-wrapped label. It " \
|
|
"should not be taking up the entire " /* big space to test spacing */\
|
|
"width allocated to it, but automatically " \
|
|
"wraps the words to fit. " \
|
|
"The time has come, for all good men, to come to " \
|
|
"the aid of their party. " \
|
|
"The sixth sheik's six sheep's sick.\n" \
|
|
" It supports multiple paragraphs correctly, " \
|
|
"and correctly adds "\
|
|
"many extra spaces. ");
|
|
gtk_label_set_line_wrap (GTK_LABEL (etiqueta), TRUE);
|
|
gtk_container_add (GTK_CONTAINER (frame), etiqueta);
|
|
gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
|
|
|
|
frame = gtk_frame_new ("Filled, wrapped label");
|
|
etiqueta = gtk_label_new ("This is an example of a line-wrapped, filled label. " \
|
|
"It should be taking "\
|
|
"up the entire width allocated to it. " \
|
|
"Here is a seneance to prove "\
|
|
"my point. Here is another sentence. "\
|
|
"Here comes the sun, do de do de do.\n"\
|
|
" This is a new paragraph.\n"\
|
|
" This is another newer, longer, better " \
|
|
"paragraph. It is coming to an end, "\
|
|
"unfortunately.");
|
|
gtk_label_set_justify (GTK_LABEL (etiqueta), GTK_JUSTIFY_FILL);
|
|
gtk_label_set_line_wrap (GTK_LABEL (etiqueta), TRUE);
|
|
gtk_container_add (GTK_CONTAINER (frame), etiqueta);
|
|
gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
|
|
|
|
frame = gtk_frame_new ("Underlined label");
|
|
etiqueta = gtk_label_new ("This label is underlined!\n"
|
|
"This one is underlined in quite a funky fashion");
|
|
gtk_label_set_justify (GTK_LABEL (etiqueta), GTK_JUSTIFY_LEFT);
|
|
gtk_label_set_pattern (GTK_LABEL (etiqueta),
|
|
"_________________________ _ _________ _ ______ __ _______ ___");
|
|
gtk_container_add (GTK_CONTAINER (frame), etiqueta);
|
|
gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
|
|
|
|
gtk_widget_show_all (ventana);
|
|
|
|
gtk_main ();
|
|
|
|
return(0);
|
|
}
|
|
/* fin del ejemplo */
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1> Flechas
|
|
<p>
|
|
En <em/widget/ flecha (<em/arrow/) dibuja la punta de una flecha,
|
|
con un estilo y hacia una dirección a escoger. Puede ser muy útil
|
|
en muchas aplicaciones cuando se coloca en un botón.
|
|
|
|
Sólo hay dos funciones para manipular el <em/widget/ flecha:
|
|
|
|
<tscreen><verb>
|
|
GtkWidget *gtk_arrow_new( GtkArrowType arrow_type,
|
|
GtkShadowType shadow_type );
|
|
|
|
void gtk_arrow_set( GtkArrow *arrow,
|
|
GtkArrowType arrow_type,
|
|
GtkShadowType shadow_type );
|
|
</verb></tscreen>
|
|
|
|
La primera crea un nuevo <em/widget/ flecha del tipo y apariencia
|
|
indicados. La segunda permite alterar posteriormente estos valores. El
|
|
argumento <tt/arrow_type/ puede tomar uno de los valores siguientes:
|
|
|
|
<itemize>
|
|
<item> GTK_ARROW_UP
|
|
<item> GTK_ARROW_DOWN
|
|
<item> GTK_ARROW_LEFT
|
|
<item> GTK_ARROW_RIGHT
|
|
</itemize>
|
|
|
|
Naturalmente, estos valores indican la dirección a la que debe apuntar
|
|
la flecha. El argumento <tt/shadow_type/ puede tomar uno de los
|
|
valores siguientes:
|
|
|
|
<itemize>
|
|
<item> GTK_SHADOW_IN
|
|
<item> GTK_SHADOW_OUT (por defecto)
|
|
<item> GTK_SHADOW_ETCHED_IN
|
|
<item> GTK_SHADOW_ETCHED_OUT
|
|
</itemize>
|
|
|
|
Aquí tenemos un pequeño ejemplo para ilustrar la utilización de la
|
|
flecha.
|
|
|
|
<tscreen><verb>
|
|
/* principio del ejemplo arrow arrow.c */
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
/* Crea un widget flecha con los parámetros especificados
|
|
* y lo empaqueta en un botón */
|
|
GtkWidget *create_arrow_button( GtkArrowType arrow_type,
|
|
GtkShadowType shadow_type )
|
|
{
|
|
GtkWidget *boton;
|
|
GtkWidget *arrow;
|
|
|
|
boton = gtk_button_new();
|
|
arrow = gtk_arrow_new (arrow_type, shadow_type);
|
|
|
|
gtk_container_add (GTK_CONTAINER (boton), arrow);
|
|
|
|
gtk_widget_show(boton);
|
|
gtk_widget_show(arrow);
|
|
|
|
return(boton);
|
|
}
|
|
|
|
int main( int argc,
|
|
char *argv[] )
|
|
{
|
|
/* GtkWidget es el tipo utilizado para los widgets */
|
|
GtkWidget *ventana;
|
|
GtkWidget *boton;
|
|
GtkWidget *box;
|
|
|
|
/* Inicializa el toolkit */
|
|
gtk_init (&argc, &argv);
|
|
|
|
/* Crea una nueva ventana */
|
|
ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
|
|
|
gtk_window_set_title (GTK_WINDOW (ventana), "Arrow Buttons");
|
|
|
|
/* Es una buena idea hacer esto con todas las ventanas. */
|
|
gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
|
|
GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
|
|
|
|
/* Establece el ancho del borde de la ventana. */
|
|
gtk_container_set_border_width (GTK_CONTAINER (ventana), 10);
|
|
|
|
/* Crea una caja para almacenar las flechas/botones */
|
|
box = gtk_hbox_new (FALSE, 0);
|
|
gtk_container_set_border_width (GTK_CONTAINER (box), 2);
|
|
gtk_container_add (GTK_CONTAINER (ventana), box);
|
|
|
|
/* Empaqueta y muestra todos nuestros widgets */
|
|
gtk_widget_show(box);
|
|
|
|
boton = create_arrow_button(GTK_ARROW_UP, GTK_SHADOW_IN);
|
|
gtk_box_pack_start (GTK_BOX (box), boton, FALSE, FALSE, 3);
|
|
|
|
boton = create_arrow_button(GTK_ARROW_DOWN, GTK_SHADOW_OUT);
|
|
gtk_box_pack_start (GTK_BOX (box), boton, FALSE, FALSE, 3);
|
|
|
|
boton = create_arrow_button(GTK_ARROW_LEFT, GTK_SHADOW_ETCHED_IN);
|
|
gtk_box_pack_start (GTK_BOX (box), boton, FALSE, FALSE, 3);
|
|
|
|
boton = create_arrow_button(GTK_ARROW_RIGHT, GTK_SHADOW_ETCHED_OUT);
|
|
gtk_box_pack_start (GTK_BOX (box), boton, FALSE, FALSE, 3);
|
|
|
|
gtk_widget_show (ventana);
|
|
|
|
/* Nos quedamos en gtk_main y ¡esperamos que empiece la diversión! */
|
|
gtk_main ();
|
|
|
|
return(0);
|
|
}
|
|
/* fin del ejemplo */
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>El <em/widget/ de información rápida (<em/tooltip/)
|
|
<p>
|
|
Estos <em/widgets/ son las pequeñas etiquetas que texto que
|
|
aparecen cuando se sitúa el puntero del ratón sobre un botón
|
|
u otro <em/widget/ durante algunos segundos. Son bastante fáciles
|
|
de usar, así que no se dará ningún ejemplo. Si quiere ver
|
|
algún ejemplo se recomienda leer el programa testgtk.c que
|
|
acompaña a GTK.
|
|
|
|
Algunos <em/widgets/ (como la etiqueta) no pueden llevar asociado un
|
|
<em/tooltip/.
|
|
|
|
Para cada función sólo hay que hacer una llamada para conseguir
|
|
un <em/tooltip/. El objeto <tt/GtkTooltip/ que devuelve la siguiente
|
|
función puede ser usado para crear múltiples <em/widgets/.
|
|
|
|
<tscreen><verb>
|
|
GtkTooltips *gtk_tooltips_new( void );
|
|
</verb></tscreen>
|
|
|
|
Una vez que el <em/tooltip/ ha sido creado (y el <em/widget/ sobre el
|
|
que se quiere usar) simplemente hay que usar la siguiente llamada para
|
|
pegarlo:
|
|
|
|
<tscreen><verb>
|
|
void gtk_tooltips_set_tip( GtkTooltips *tooltips,
|
|
GtkWidget *widget,
|
|
const gchar *tip_text,
|
|
const gchar *tip_private );
|
|
</verb></tscreen>
|
|
|
|
El primer argumento es el <em/tooltip/ que ya ha creado, seguido del
|
|
<em/widget/ al que se desea asociar el <em/tooltip/, el tercero es el
|
|
texto que se quiere que aparezca y el último es una cadena de texto
|
|
que puede ser usada como un identificador cuando se usa GtkTipsQuery
|
|
para desarollar ayuda sensible al contexto. Por ahora conviene dejarlo
|
|
como NULL.
|
|
|
|
<!-- TODO: sort out what how to do the context sensitive help -->
|
|
|
|
Veamos un ejemplo:
|
|
|
|
<tscreen><verb>
|
|
GtkTooltips *tooltips;
|
|
GtkWidget *boton;
|
|
...
|
|
tooltips = gtk_tooltips_new ();
|
|
boton = gtk_button_new_with_label ("botón 1");
|
|
...
|
|
gtk_tooltips_set_tip (tooltips, boton, "Este es el botón 1", NULL);
|
|
</verb></tscreen>
|
|
|
|
Existen otras funciones que pueden ser usadas con los <em/tooltips/.
|
|
Solamente vamos a enumerlarlas añadiendo una pequeña descripción
|
|
de que hace cada una.
|
|
|
|
<tscreen><verb>
|
|
void gtk_tooltips_enable( GtkTooltips *tooltips );
|
|
</verb></tscreen>
|
|
|
|
Permite que funcionen un conjunto de <em/tooltips/
|
|
|
|
<tscreen><verb>
|
|
void gtk_tooltips_disable( GtkTooltips *tooltips );
|
|
</verb></tscreen>
|
|
|
|
Oculta un conjunto de <em/tooltips/ para que no pueda ser mostrado.
|
|
|
|
<tscreen><verb>
|
|
void gtk_tooltips_set_delay( GtkTooltips *tooltips,
|
|
gint delay );
|
|
|
|
</verb></tscreen>
|
|
|
|
Establece cuantos milisegundos tiene que estar el puntero sobre el
|
|
<em/widget/ para que aparezca el <em/tooltip/. Por defecto se usan 1000
|
|
milisegundos (1 segundo).
|
|
|
|
<tscreen><verb>
|
|
void gtk_tooltips_set_colors( GtkTooltips *tooltips,
|
|
GdkColor *background,
|
|
GdkColor *foreground );
|
|
</verb></tscreen>
|
|
|
|
Establece el color del texto y del fondo del <em/tooltip/. No se como
|
|
se especifica el color.
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1> Barras de progreso <label id="sec_ProgressBar">
|
|
<p>
|
|
Estas barras se usan para mostrar el estado de una operación. Son
|
|
bastante sencillas de utilizar, tal y como se verá en los ejemplos
|
|
siguientes. Pero primero vamos a ver cuales son las funciones que hay
|
|
que utilizar para crear una nueva barra de progreso.
|
|
|
|
Hay dos formas de crear una nueva barra de progreso, la sencilla no
|
|
necesita de argumentos, y la otra recibe un objeto GtkAdjustment. Si
|
|
se utiliza la primera forma, la barra de progreso creará su propio
|
|
GtkAdjustment.
|
|
|
|
<tscreen><verb>
|
|
GtkWidget *gtk_progress_bar_new( void );
|
|
|
|
GtkWidget *gtk_progress_bar_new_with_adjustment( GtkAdjustment *adjustment );
|
|
</verb></tscreen>
|
|
|
|
El segundo método tiene la ventaja de que podemos utilizar el objeto
|
|
adjustment para especificar nuestro propio rango de parámetros para la
|
|
barra de progreso.
|
|
|
|
El ajuste de una barra de progreso se puede cambiar de forma dinámica
|
|
utilizando:
|
|
|
|
<tscreen><verb>
|
|
void gtk_progress_set_adjustment( GtkProgress *progress,
|
|
GtkAdjustment *adjustment );
|
|
</verb></tscreen>
|
|
|
|
Ahora que hemos creado la barra de progreso ya podemos utilizarla.
|
|
|
|
<tscreen><verb>
|
|
void gtk_progress_bar_update( GtkProgressBar *pbar,
|
|
gfloat percentage );
|
|
</verb></tscreen>
|
|
|
|
El primer argumento es la barra que se quiere manejar, el segundo es
|
|
tanto por ciento que ha sido `completado' (indica cuanto ha sido
|
|
llenada la barra y oscila entre 0-100%). El valor que se le tiene que
|
|
pasar oscila entre 0 y 1.
|
|
|
|
GTK+ v1.2 ha añadido una nueva característica a la barra de progreso,
|
|
y es que ahora permite mostrar su valor de varias maneras distintas, e
|
|
informar al usuario del valor y rango actual.
|
|
|
|
Una barra de progreso puede mostrarse con distintas orientaciones
|
|
utilizando la función
|
|
|
|
<tscreen><verb>
|
|
void gtk_progress_bar_set_orientation( GtkProgressBar *pbar,
|
|
GtkProgressBarOrientation orientation );
|
|
</verb></tscreen>
|
|
|
|
Donde el argumento <tt/orientación/ puede tomar uno de los valores que
|
|
vienen a continuación para indicar la dirección en la que se mueve la
|
|
barra de progreso:
|
|
|
|
<itemize>
|
|
<item> GTK_PROGRESS_LEFT_TO_RIGHT
|
|
<item> GTK_PROGRESS_RIGHT_TO_LEFT
|
|
<item> GTK_PROGRESS_BOTTOM_TO_TOP
|
|
<item> GTK_PROGRESS_TOP_TO_BOTTOM
|
|
</itemize>
|
|
|
|
Cuando se utiliza como una medida de cuanto se ha completado de un
|
|
proceso, la barra de progreso puede configurarse para que muestre su
|
|
valor de una forma continua o discreta. En modo continuo, la barra de
|
|
progreso se actualiza mediante un número discreto de bloques, el
|
|
número de bloques también es configurable.
|
|
|
|
Se puede configurar el estilo de la barra de progreso utilizando la
|
|
siguiente función:
|
|
|
|
<tscreen><verb>
|
|
void gtk_progress_bar_set_bar_style( GtkProgressBar *pbar,
|
|
GtkProgressBarStyle style );
|
|
</verb></tscreen>
|
|
|
|
El parámetro <tt/style/ puede tomar uno de los dos valores siguientes:
|
|
|
|
<itemize>
|
|
<item>GTK_PROGRESS_CONTINUOUS
|
|
<item>GTK_PROGRESS_DISCRETE
|
|
</itemize>
|
|
|
|
El número de bloques se puede establecer utilizando
|
|
|
|
<tscreen><verb>
|
|
void gtk_progress_bar_set_discrete_blocks( GtkProgressBar *pbar,
|
|
guint blocks );
|
|
</verb></tscreen>
|
|
|
|
La barra de progreso también se puede utilizar, a parte de para
|
|
indicar lo «avanzado» de una tarea, para indicar que hay algún tipo
|
|
de actividad. Esto puede ser útil en situaciones donde no se pueda
|
|
medir el progreso de una tarea con un rango de valores. Para el modo
|
|
actividad, no sirve el estilo de barra que se ha descrito más
|
|
arriba. Este modo hay que seleccionarlo utilizando la siguiente
|
|
función:
|
|
|
|
<tscreen><verb>
|
|
void gtk_progress_set_activity_mode( GtkProgress *progress,
|
|
guint activity_mode );
|
|
</verb></tscreen>
|
|
|
|
El tamaño del paso del indicador de actividad, y el número de bloques
|
|
se indican usando las siguientes funciones:
|
|
|
|
<tscreen><verb>
|
|
void gtk_progress_bar_set_activity_step( GtkProgressBar *pbar,
|
|
guint step );
|
|
|
|
void gtk_progress_bar_set_activity_blocks( GtkProgressBar *pbar,
|
|
guint blocks );
|
|
</verb></tscreen>
|
|
|
|
Cuando estamos en modo continuo, la barra de progreso puede mostrar un
|
|
texto configurable dentro la barra misma, utilizando la función
|
|
siguiente:
|
|
|
|
<tscreen><verb>
|
|
void gtk_progress_set_format_string( GtkProgress *progress,
|
|
gchar *format);
|
|
</verb></tscreen>
|
|
|
|
El argumento <tt/format/ es parecido al que se utiliza en una orden
|
|
<tt/printf/ de C. Se pueden utilizar las siguientes opciones para el
|
|
formateado de la cadena:
|
|
|
|
<itemize>
|
|
<item> %p - porcentaje
|
|
<item> %v - valor
|
|
<item> %l - valor inferior del rango
|
|
<item> %u - valor superior del rango
|
|
</itemize>
|
|
|
|
Puede activar o desactivar el texto utilizando:
|
|
|
|
<tscreen><verb>
|
|
void gtk_progress_set_show_text( GtkProgress *progress,
|
|
gint show_text );
|
|
</verb></tscreen>
|
|
|
|
El argumento <tt/show_text/ es un valor booleano TRUE/FALSE. La
|
|
apariencia del texto puede modificarse utilizando:
|
|
|
|
<tscreen><verb>
|
|
void gtk_progress_set_text_alignment( GtkProgress *progress,
|
|
gfloat x_align,
|
|
gfloat y_align );
|
|
</verb></tscreen>
|
|
|
|
Los argumentos <tt/x_align/ y <tt/y_align/ toman un valor entre 0.0 y
|
|
1.0. Este valor indica la posición de la cadena de texto dentro de la
|
|
barra. Si ponemos 0.0 en los dos sitios la cadena de texto aparecerá
|
|
en la esquina superior izquierda; un valor de 0.5 (el que se utiliza
|
|
por defecto) centra el texto, y un valor de 1.0 coloca el texto en la
|
|
esquina inferior derecha.
|
|
|
|
Se pueden leer los parámetros actuales del texto de un objeto barra
|
|
de progreso utilizando las dos funciones que se muestran a
|
|
continuación. La cadena de carácteres devuelta por estas funciones
|
|
debe liberarse en la aplicación (utilizando la función
|
|
g_free()). Estas funciones devuelven el texto formateado que se
|
|
mostrará en la barra.
|
|
|
|
<tscreen><verb>
|
|
gchar *gtk_progress_get_current_text( GtkProgress *progress );
|
|
|
|
gchar *gtk_progress_get_text_from_value( GtkProgress *progress,
|
|
gfloat value );
|
|
</verb></tscreen>
|
|
|
|
Hay otra forma de cambiar el rango y el valor de un objeto barra de
|
|
progreso utilizando la función:
|
|
|
|
<tscreen><verb>
|
|
void gtk_progress_configure( GtkProgress *progress,
|
|
gfloat value,
|
|
gfloat min,
|
|
gfloat max );
|
|
</verb></tscreen>
|
|
|
|
Esta función proporciona una interfaz sencilla al rango y valor de una
|
|
barra de progreso.
|
|
|
|
Las funciones restantes se pueden utilizar para obtener y establecer
|
|
el valor actual de una barra de progreso utilizando distintos tipos y
|
|
formatos para el valor.
|
|
|
|
<tscreen><verb>
|
|
void gtk_progress_set_percentage( GtkProgress *progress,
|
|
gfloat percentage );
|
|
|
|
void gtk_progress_set_value( GtkProgress *progress,
|
|
gfloat value );
|
|
|
|
gfloat gtk_progress_get_value( GtkProgress *progress );
|
|
|
|
gfloat gtk_progress_get_current_percentage( GtkProgress *progress );
|
|
|
|
gfloat gtk_progress_get_percentage_from_value( GtkProgress *progress,
|
|
gfloat value );
|
|
</verb></tscreen>
|
|
|
|
Estas funciones son autoexplicatorias. La última función utiliza el
|
|
ajuste de la barra de progreso especificada para calcular el
|
|
porcentaje dentro del rango de valores de la barra.
|
|
|
|
Las barras de progreso se usan con otras funciones como los tiempos de
|
|
espera (<em/timeouts/), sección <ref id="sec_timeouts"
|
|
name="Tiempos de espera, E/S (I/O) y funciones ociosas (idle)">) para
|
|
crear la ilusión de la multitarea. Todas usan la función
|
|
gtk_progress_bar_update de la misma manera.
|
|
|
|
Estudiemos un ejemplo de barras de progreso actualizada usando
|
|
tiempos de espera. También se muestra como se debe reestablecer una
|
|
barra.
|
|
|
|
<tscreen><verb>
|
|
/* comienzo del programa-ejemplo progressbar.c */
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
typedef struct _ProgressData {
|
|
GtkWidget *ventana;
|
|
GtkWidget *pbar;
|
|
int timer;
|
|
} ProgressData;
|
|
|
|
/* Actualiza el valor de la barra de progreso para que
|
|
* podamos ver algún movimiento */
|
|
gint progress_timeout( gpointer data )
|
|
{
|
|
gfloat new_val;
|
|
GtkAdjustment *adj;
|
|
|
|
/* Calcula el valor de la barra de progreso utilizando
|
|
* el rango de valores establecido en el ajuste de la
|
|
* barra */
|
|
|
|
new_val = gtk_progress_get_value( GTK_PROGRESS(data) ) + 1;
|
|
|
|
adj = GTK_PROGRESS (data)->adjustment;
|
|
if (new_val > adj->upper)
|
|
new_val = adj->lower;
|
|
|
|
/* Establece el nuevo valor */
|
|
|
|
gtk_progress_set_value (GTK_PROGRESS (data), new_val);
|
|
|
|
/* Como esta es una función de espera, devolvemos TRUE
|
|
* para que continue siendo llamada */
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
/* Función de llamada que activa/desactiva el texto de dentro
|
|
* de la barra de progreso */
|
|
void toggle_show_text( GtkWidget *widget,
|
|
ProgressData *pdata )
|
|
{
|
|
gtk_progress_set_show_text (GTK_PROGRESS (pdata->pbar),
|
|
GTK_TOGGLE_BUTTON (widget)->active);
|
|
}
|
|
|
|
/* Función de llamada que activa/desactiva el modo actividad
|
|
* de la barra de progreso */
|
|
void toggle_activity_mode( GtkWidget *widget,
|
|
ProgressData *pdata )
|
|
{
|
|
gtk_progress_set_activity_mode (GTK_PROGRESS (pdata->pbar),
|
|
GTK_TOGGLE_BUTTON (widget)->active);
|
|
}
|
|
|
|
/* Función de llamada que activa/desactiva el modo continuo
|
|
* de la barra de progreso */
|
|
void set_continuous_mode( GtkWidget *widget,
|
|
ProgressData *pdata )
|
|
{
|
|
gtk_progress_bar_set_bar_style (GTK_PROGRESS_BAR (pdata->pbar),
|
|
GTK_PROGRESS_CONTINUOUS);
|
|
}
|
|
|
|
/* Función de llamada que activa/desactiva el modo discreto
|
|
* de la barra de progreso */
|
|
void set_discrete_mode( GtkWidget *widget,
|
|
ProgressData *pdata )
|
|
{
|
|
gtk_progress_bar_set_bar_style (GTK_PROGRESS_BAR (pdata->pbar),
|
|
GTK_PROGRESS_DISCRETE);
|
|
}
|
|
|
|
/* Libera la memoria y elimina el temporizador */
|
|
void destroy_progress( GtkWidget *widget,
|
|
ProgressData *pdata)
|
|
{
|
|
gtk_timeout_remove (pdata->timer);
|
|
pdata->timer = 0;
|
|
pdata->ventana = NULL;
|
|
g_free(pdata);
|
|
gtk_main_quit();
|
|
}
|
|
|
|
int main( int argc,
|
|
char *argv[])
|
|
{
|
|
ProgressData *pdata;
|
|
GtkWidget *align;
|
|
GtkWidget *separator;
|
|
GtkWidget *table;
|
|
GtkAdjustment *adj;
|
|
GtkWidget *boton;
|
|
GtkWidget *check;
|
|
GtkWidget *vbox;
|
|
|
|
gtk_init (&argc, &argv);
|
|
|
|
/* Reserva memoria para los datos que se le pasan a las funciones
|
|
* de llamada */
|
|
pdata = g_malloc( sizeof(ProgressData) );
|
|
|
|
pdata->ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
|
gtk_window_set_policy (GTK_WINDOW (pdata->ventana), FALSE, FALSE, TRUE);
|
|
|
|
gtk_signal_connect (GTK_OBJECT (pdata->ventana), "destroy",
|
|
GTK_SIGNAL_FUNC (destroy_progress),
|
|
pdata);
|
|
gtk_window_set_title (GTK_WINDOW (pdata->ventana), "GtkProgressBar");
|
|
gtk_container_set_border_width (GTK_CONTAINER (pdata->ventana), 0);
|
|
|
|
vbox = gtk_vbox_new (FALSE, 5);
|
|
gtk_container_set_border_width (GTK_CONTAINER (vbox), 10);
|
|
gtk_container_add (GTK_CONTAINER (pdata->ventana), vbox);
|
|
gtk_widget_show(vbox);
|
|
|
|
/* Crea un objeto de alineamiento centrado */
|
|
align = gtk_alignment_new (0.5, 0.5, 0, 0);
|
|
gtk_box_pack_start (GTK_BOX (vbox), align, FALSE, FALSE, 5);
|
|
gtk_widget_show(align);
|
|
|
|
/* Crea un objeto GtkAdjusment para albergar el rango de la barra
|
|
* de progreso */
|
|
adj = (GtkAdjustment *) gtk_adjustment_new (0, 1, 150, 0, 0, 0);
|
|
|
|
/* Crea la GtkProgressBar utilizando el ajuste */
|
|
pdata->pbar = gtk_progress_bar_new_with_adjustment (adj);
|
|
|
|
/* Establece el formato de la cadena de texto que puede mostrarse
|
|
* en la barra de progreso:
|
|
* %p - porcentaje
|
|
* %v - valor
|
|
* %l - valor inferior del rango
|
|
* %u - valor superior del rango */
|
|
gtk_progress_set_format_string (GTK_PROGRESS (pdata->pbar),
|
|
"%v from [%l-%u] (=%p%%)");
|
|
gtk_container_add (GTK_CONTAINER (align), pdata->pbar);
|
|
gtk_widget_show(pdata->pbar);
|
|
|
|
/* Añade un temporizador para la actualización del valor de la
|
|
* barra de progreso */
|
|
pdata->timer = gtk_timeout_add (100, progress_timeout, pdata->pbar);
|
|
|
|
separator = gtk_hseparator_new ();
|
|
gtk_box_pack_start (GTK_BOX (vbox), separator, FALSE, FALSE, 0);
|
|
gtk_widget_show(separator);
|
|
|
|
/* filas, columnas, homogéneo */
|
|
table = gtk_table_new (2, 3, FALSE);
|
|
gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, TRUE, 0);
|
|
gtk_widget_show(table);
|
|
|
|
/* Añade un botón de comprobación para seleccionar si se debe
|
|
* mostrar el texto dentro de la barra */
|
|
check = gtk_check_button_new_with_label ("Show text");
|
|
gtk_table_attach (GTK_TABLE (table), check, 0, 1, 0, 1,
|
|
GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL,
|
|
5, 5);
|
|
gtk_signal_connect (GTK_OBJECT (check), "clicked",
|
|
GTK_SIGNAL_FUNC (toggle_show_text),
|
|
pdata);
|
|
gtk_widget_show(check);
|
|
|
|
/* Añade un botón de comprobación para activar/desactivar el modo
|
|
* actividad */
|
|
check = gtk_check_button_new_with_label ("Activity mode");
|
|
gtk_table_attach (GTK_TABLE (table), check, 0, 1, 1, 2,
|
|
GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL,
|
|
5, 5);
|
|
gtk_signal_connect (GTK_OBJECT (check), "clicked",
|
|
GTK_SIGNAL_FUNC (toggle_activity_mode),
|
|
pdata);
|
|
gtk_widget_show(check);
|
|
|
|
separator = gtk_vseparator_new ();
|
|
gtk_table_attach (GTK_TABLE (table), separator, 1, 2, 0, 2,
|
|
GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL,
|
|
5, 5);
|
|
gtk_widget_show(separator);
|
|
|
|
/* Añade un botón circular para seleccionar el modo continuo */
|
|
boton = gtk_radio_button_new_with_label (NULL, "Continuous");
|
|
gtk_table_attach (GTK_TABLE (table), boton, 2, 3, 0, 1,
|
|
GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL,
|
|
5, 5);
|
|
gtk_signal_connect (GTK_OBJECT (boton), "clicked",
|
|
GTK_SIGNAL_FUNC (set_continuous_mode),
|
|
pdata);
|
|
gtk_widget_show (boton);
|
|
|
|
/* Añade un botón circular para seleccionar el modo discreto */
|
|
boton = gtk_radio_button_new_with_label(
|
|
gtk_radio_button_group (GTK_RADIO_BUTTON (boton)),
|
|
"Discrete");
|
|
gtk_table_attach (GTK_TABLE (table), boton, 2, 3, 1, 2,
|
|
GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL,
|
|
5, 5);
|
|
gtk_signal_connect (GTK_OBJECT (boton), "clicked",
|
|
GTK_SIGNAL_FUNC (set_discrete_mode),
|
|
pdata);
|
|
gtk_widget_show (boton);
|
|
|
|
separator = gtk_hseparator_new ();
|
|
gtk_box_pack_start (GTK_BOX (vbox), separator, FALSE, FALSE, 0);
|
|
gtk_widget_show(separator);
|
|
|
|
/* Añade un botón para salir del programa */
|
|
boton = gtk_button_new_with_label ("close");
|
|
gtk_signal_connect_object (GTK_OBJECT (boton), "clicked",
|
|
(GtkSignalFunc) gtk_widget_destroy,
|
|
GTK_OBJECT (pdata->ventana));
|
|
gtk_box_pack_start (GTK_BOX (vbox), boton, FALSE, FALSE, 0);
|
|
|
|
/* Esto hace que este botón sea el botón pueda utilizarse por
|
|
* defecto defecto. */
|
|
GTK_WIDGET_SET_FLAGS (boton, GTK_CAN_DEFAULT);
|
|
|
|
/* Esto marca este botón para que sea el botón por
|
|
* defecto. Simplemente utilizando la tecla "Intro" haremos que se
|
|
* active este botón. */
|
|
gtk_widget_grab_default (boton);
|
|
gtk_widget_show(boton);
|
|
|
|
gtk_widget_show (pdata->ventana);
|
|
|
|
gtk_main ();
|
|
|
|
return(0);
|
|
}
|
|
/* final del ejemplo */
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Cuadros de diálogo
|
|
<p>
|
|
El <em/widget/ del cuadro de diálogo es bastante simple, sólo es una
|
|
ventana con algunas cosas ya preempaquetadas. Su estructura es la
|
|
siguiente:
|
|
|
|
<tscreen><verb>
|
|
struct GtkDialog
|
|
{
|
|
GtkWindow ventana;
|
|
|
|
GtkWidget *vbox;
|
|
GtkWidget *action_area;
|
|
};
|
|
</verb></tscreen>
|
|
|
|
Simplemente se crea una ventana en la cual se empaqueta una vbox, un
|
|
separador y una hbox llamada «action_area».
|
|
|
|
Este tipo de <em/widgets/ pueden ser usados como mensages <em/pop-up/
|
|
(pequeñas ventanas con texto en su interior que aparecen cuando el
|
|
usuario hace algo y queremos informarle de alguna cosa) y otras cosas
|
|
parecidas. Su manejo desde el punto de vista del programador
|
|
es bastante fácil, sólo hay que usar una función:
|
|
|
|
<tscreen><verb>
|
|
GtkWidget *gtk_dialog_new( void );
|
|
</verb></tscreen>
|
|
|
|
Para crear un nuevo cuadro de diálogo hay que llamar a:
|
|
|
|
<tscreen><verb>
|
|
GtkWidget *ventana;
|
|
ventana = gtk_dialog_new ();
|
|
</verb></tscreen>
|
|
|
|
Una vez que el cuadro ha sido creado sólo hay que usarlo. Por
|
|
ejemplo para empaquetar un botón en la action_area escribiríamos
|
|
algo así:
|
|
|
|
<tscreen><verb>
|
|
boton = ...
|
|
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (ventana)->action_area), boton,
|
|
TRUE, TRUE, 0);
|
|
gtk_widget_show (boton);
|
|
</verb></tscreen>
|
|
|
|
Otra cosa que nos puede interesar es empaquetar una etiqueta en la
|
|
vbox:
|
|
|
|
<tscreen><verb>
|
|
etiqueta = gtk_label_new ("Dialogs are groovy");
|
|
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (ventana)->vbox), etiqueta, TRUE,
|
|
TRUE, 0);
|
|
gtk_widget_show (etiqueta);
|
|
</verb></tscreen>
|
|
|
|
Otros ejemplo posible es poner dos botones en el action_area (uno
|
|
para cancelar y el otro para permitir algo) junto con una etiqueta en
|
|
la vbox el usuario puede seleccionar lo que quiera.
|
|
|
|
Si se precisa algo más complejo siempre se puede empaquetar otro
|
|
<em/widget/ en cualquiera de las cajas (p.j. una tabla en una vbox).
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1> <em/Pixmaps/ <label id="sec_Pixmaps">
|
|
<p>
|
|
Los <em/pixmaps/ son estructuras de datos que contienen dibujos. Estos
|
|
pueden ser usados en diferentes lugares, pero los iconos y los
|
|
cursores son los más comunes.
|
|
|
|
Un <em/bitmap/ es un <em/pixmap/ que sólo tiene dos colores, y hay
|
|
unas cuantas rutinas especiales para controlar este caso particular.
|
|
|
|
Para comprender los <em/pixmaps/, puede ayudar entender como funciona
|
|
X-windows. Bajo X-windows, las aplicaciones no tienen porque estar
|
|
ejecutándose en el ordenador que está interactuando con el
|
|
usuario. Las distintas aplicaciones, llamadas «clientes», comunican
|
|
con un programa que muestra los gráficos y que controla el tecledo y
|
|
el ratón. Este programa que interactua directamente con el usuario se
|
|
llama un «<em/display server/» o «servidor X». Como la
|
|
comunicación entre el servidor y el cliente puede llevarse a cabo
|
|
mediante una red, es importante mantener alguna información en el
|
|
servidor X. Los <em/pixmaps/ por ejemplo, se almacenan en la memoria
|
|
del servidor X. Esto significa que una vez que se establecen los
|
|
valores del <em/pixmap/, no tienen que estar transmitiéndose por la
|
|
red; en su lugar lo único que hay que enviar es una orden del estilo
|
|
«mostrar <em/pixmap/ número XYZ aquí». Incluso si no está utilizando
|
|
X-windows con GTK, al utilizar construcciones como los <em/pixmaps/
|
|
conseguirá que sus programas funciones de forma aceptable bajo
|
|
X-windows.
|
|
|
|
Para usar un <em/pixmap/ en GTK primero tiene que construir una
|
|
estructura del tipo GdkPixmap usando rutinas de GDK. Los <em/pixmaps/
|
|
se pueden crear usando datos que se encuentren en la memoria o en un
|
|
archivo. Veremos con detalle cada una de las dos posibilidades.
|
|
|
|
<tscreen><verb>
|
|
GdkPixmap *gdk_bitmap_create_from_data( GdkWindow *ventana,
|
|
gchar *data,
|
|
gint width,
|
|
gint height );
|
|
</verb></tscreen>
|
|
|
|
Esta rutina se utiliza para crear un <em/bitmap/ a partir de datos
|
|
almacenados en la memoria. Cada bit de información indica si el
|
|
<em/pixel/ luce o no. Tanto la altura como la anchura estan expresadas
|
|
en <em/pixels/. El puntero del tipo GdkWindow indica la ventana en
|
|
cuestión, ya que los <em/pixmaps/ sólo tienen sentido dentro de
|
|
la pantalla en la que van a ser mostrados.
|
|
|
|
<tscreen><verb>
|
|
GdkPixmap *gdk_pixmap_create_from_data( GdkWindow *ventana,
|
|
gchar *data,
|
|
gint width,
|
|
gint height,
|
|
gint depth,
|
|
GdkColor *fg,
|
|
GdkColor *bg );
|
|
</verb></tscreen>
|
|
|
|
Con esto creamos un <em/pixmap/ con la profundidad (número de
|
|
colores) especificada en los datos del <em/bitmap/. Los valores
|
|
<tt/fg/ y <tt/bg/ son los colores del frente y del fondo
|
|
respectivamente.
|
|
|
|
<tscreen><verb>
|
|
GdkPixmap *gdk_pixmap_create_from_xpm( GdkWindow *ventana,
|
|
GdkBitmap **mask,
|
|
GdkColor *transparent_color,
|
|
const gchar *filename );
|
|
</verb></tscreen>
|
|
|
|
El formato XPM es una representacion de los <em/pixmaps/ para el
|
|
sistema X Window. Es bastante popular y existen muchos programas para
|
|
crear imágenes en este formato. El archivo especificado mediante
|
|
<tt/filename/ debe contener una imagen en ese formato para que sea
|
|
cargada en la estructura. La máscara especifica que bits son
|
|
opacos. Todos los demás bits se colorean usando el color
|
|
especificado en <tt/transparent_color/. Más adelante veremos un
|
|
ejemplo.
|
|
|
|
<tscreen><verb>
|
|
GdkPixmap *gdk_pixmap_create_from_xpm_d( GdkWindow *ventana,
|
|
GdkBitmap **mask,
|
|
GdkColor *transparent_color,
|
|
gchar **data );
|
|
</verb></tscreen>
|
|
|
|
Se pueden incorporar imágenes pequeñas dentro de un programa en
|
|
formato XPM. Un <em/pixmap/ se crea usando esta información, en
|
|
lugar de leerla de un archivo. Un ejemplo sería:
|
|
|
|
<tscreen><verb>
|
|
/* XPM */
|
|
static const char * xpm_data[] = {
|
|
"16 16 3 1",
|
|
" c None",
|
|
". c #000000000000",
|
|
"X c #FFFFFFFFFFFF",
|
|
" ",
|
|
" ...... ",
|
|
" .XXX.X. ",
|
|
" .XXX.XX. ",
|
|
" .XXX.XXX. ",
|
|
" .XXX..... ",
|
|
" .XXXXXXX. ",
|
|
" .XXXXXXX. ",
|
|
" .XXXXXXX. ",
|
|
" .XXXXXXX. ",
|
|
" .XXXXXXX. ",
|
|
" .XXXXXXX. ",
|
|
" .XXXXXXX. ",
|
|
" ......... ",
|
|
" ",
|
|
" "};
|
|
</verb></tscreen>
|
|
|
|
Cuando hayamos acabado de usar un <em/pixmap/ y no lo vayamos a usar
|
|
durante un tiempo suele ser conveniente liberar el recurso mediante
|
|
gdk_pixmap_unref(). (Los <em/pixmaps/ deben ser considerados recursos
|
|
preciosos).
|
|
|
|
Una vez que hemos creado el <em/pixmap/ lo podemos mostrar como un
|
|
<em/widget/ GTK. Primero tenemos que crear un <em/widget pixmap/ que
|
|
contenga un <em/pixmap/ GDK. Esto se hace usando:
|
|
|
|
<tscreen><verb>
|
|
GtkWidget *gtk_pixmap_new( GdkPixmap *pixmap,
|
|
GdkBitmap *mask );
|
|
</verb></tscreen>
|
|
|
|
Las otras funciones del <em/widget pixmap/ son:
|
|
|
|
<tscreen><verb>
|
|
guint gtk_pixmap_get_type( void );
|
|
|
|
void gtk_pixmap_set( GtkPixmap *pixmap,
|
|
GdkPixmap *val,
|
|
GdkBitmap *mask );
|
|
|
|
void gtk_pixmap_get( GtkPixmap *pixmap,
|
|
GdkPixmap **val,
|
|
GdkBitmap **mask);
|
|
</verb></tscreen>
|
|
|
|
La función gtk_pixmap_set se usa para cambiar los datos del
|
|
<em/pixmap/ que el <em/widget/ está manejando en ese
|
|
momento. <tt/val/ es el <em/pixmap/ creado usando GDK.
|
|
|
|
El ejemplo siguiente usa un <em/pixmap/ en un botón:
|
|
|
|
<tscreen><verb>
|
|
/* comienzo del ejemplo pixmap.c */
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
|
|
/* Datos en formato XPM del icono de apertura de archivo */
|
|
static const char * xpm_data[] = {
|
|
"16 16 3 1",
|
|
" c None",
|
|
". c #000000000000",
|
|
"X c #FFFFFFFFFFFF",
|
|
" ",
|
|
" ...... ",
|
|
" .XXX.X. ",
|
|
" .XXX.XX. ",
|
|
" .XXX.XXX. ",
|
|
" .XXX..... ",
|
|
" .XXXXXXX. ",
|
|
" .XXXXXXX. ",
|
|
" .XXXXXXX. ",
|
|
" .XXXXXXX. ",
|
|
" .XXXXXXX. ",
|
|
" .XXXXXXX. ",
|
|
" .XXXXXXX. ",
|
|
" ......... ",
|
|
" ",
|
|
" "};
|
|
|
|
/* Cuando se llama a esta función (usando signal delete_event) se
|
|
* termina la aplicación*/
|
|
|
|
void close_application( GtkWidget *widget, GdkEvent *event, gpointer data ) {
|
|
gtk_main_quit();
|
|
}
|
|
|
|
|
|
/* Al presionar el botón aparece el mensaje */
|
|
void button_clicked( GtkWidget *widget, gpointer data ) {
|
|
printf( "botón pulsado\n" );
|
|
}
|
|
|
|
int main( int argc, char *argv[] )
|
|
{
|
|
|
|
GtkWidget *ventana, *pixmapwid, *boton;
|
|
GdkPixmap *pixmap;
|
|
GdkBitmap *mask;
|
|
GtkStyle *style;
|
|
|
|
/* Creamos la ventana principal y relacionamos la señal
|
|
* delete_event con acabar el programa.*/
|
|
gtk_init( &argc, &argv );
|
|
ventana = gtk_window_new( GTK_WINDOW_TOPLEVEL );
|
|
gtk_signal_connect( GTK_OBJECT (ventana), "delete_event",
|
|
GTK_SIGNAL_FUNC (close_application), NULL );
|
|
gtk_container_border_width( GTK_CONTAINER (ventana), 10 );
|
|
gtk_widget_show( ventana );
|
|
|
|
/* Ahora para el pixmap de gdk */
|
|
style = gtk_widget_get_style( ventana );
|
|
pixmap = gdk_pixmap_create_from_xpm_d( ventana->window, &mask,
|
|
&style->bg[GTK_STATE_NORMAL],
|
|
(gchar **)xpm_data );
|
|
|
|
/* Un pixmap widget que contendrá al pixmap */
|
|
pixmapwid = gtk_pixmap_new( pixmap, mask );
|
|
gtk_widget_show( pixmapwid );
|
|
|
|
/* Un botón para contener al pixmap */
|
|
boton = gtk_button_new();
|
|
gtk_container_add( GTK_CONTAINER(boton), pixmapwid );
|
|
gtk_container_add( GTK_CONTAINER(ventana), boton );
|
|
gtk_widget_show( boton );
|
|
|
|
gtk_signal_connect( GTK_OBJECT(boton), "clicked",
|
|
GTK_SIGNAL_FUNC(button_clicked), NULL );
|
|
|
|
/* mostramos la ventana */
|
|
gtk_main ();
|
|
|
|
return 0;
|
|
}
|
|
/* final del ejemplo */
|
|
</verb></tscreen>
|
|
|
|
Para cargar un archivo llamado icon0.xpm con la información XPM (que
|
|
se encuentra en en directorio actual) habríamos usado:
|
|
|
|
<tscreen><verb>
|
|
/* cargar un pixmap desde un fichero */
|
|
pixmap = gdk_pixmap_create_from_xpm( ventana->window, &mask,
|
|
&style->bg[GTK_STATE_NORMAL],
|
|
"./icon0.xpm" );
|
|
pixmapwid = gtk_pixmap_new( pixmap, mask );
|
|
gtk_widget_show( pixmapwid );
|
|
gtk_container_add( GTK_CONTAINER(ventana), pixmapwid );
|
|
</verb></tscreen>
|
|
|
|
Una desventaja de los <em/pixmaps/ es que la imagen mostrada siempre
|
|
es rectangular (independientemente de como sea la imagen en sí). Si
|
|
queremos usar imágenes con otras formas debemos usar ventanas con
|
|
forma (<em/shaped windows/).
|
|
|
|
Este tipo de ventanas son pixmaps en los que el fondo es
|
|
transparente. Así cuando la imagen del fondo tiene muchos colores
|
|
no los sobreescribimos con el borde de nuestro icono. El ejemplo
|
|
siguiente muestra la imagen de una carretilla en el escritorio.
|
|
|
|
<tscreen><verb>
|
|
/* comienzo del ejemplo carretilla wheelbarrow.c */
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
/* XPM */
|
|
static char * WheelbarrowFull_xpm[] = {
|
|
"48 48 64 1",
|
|
" c None",
|
|
". c #DF7DCF3CC71B",
|
|
"X c #965875D669A6",
|
|
"o c #71C671C671C6",
|
|
"O c #A699A289A699",
|
|
"+ c #965892489658",
|
|
"@ c #8E38410330C2",
|
|
"# c #D75C7DF769A6",
|
|
"$ c #F7DECF3CC71B",
|
|
"% c #96588A288E38",
|
|
"& c #A69992489E79",
|
|
"* c #8E3886178E38",
|
|
"= c #104008200820",
|
|
"- c #596510401040",
|
|
"; c #C71B30C230C2",
|
|
": c #C71B9A699658",
|
|
"> c #618561856185",
|
|
", c #20811C712081",
|
|
"< c #104000000000",
|
|
"1 c #861720812081",
|
|
"2 c #DF7D4D344103",
|
|
"3 c #79E769A671C6",
|
|
"4 c #861782078617",
|
|
"5 c #41033CF34103",
|
|
"6 c #000000000000",
|
|
"7 c #49241C711040",
|
|
"8 c #492445144924",
|
|
"9 c #082008200820",
|
|
"0 c #69A618611861",
|
|
"q c #B6DA71C65144",
|
|
"w c #410330C238E3",
|
|
"e c #CF3CBAEAB6DA",
|
|
"r c #71C6451430C2",
|
|
"t c #EFBEDB6CD75C",
|
|
"y c #28A208200820",
|
|
"u c #186110401040",
|
|
"i c #596528A21861",
|
|
"p c #71C661855965",
|
|
"a c #A69996589658",
|
|
"s c #30C228A230C2",
|
|
"d c #BEFBA289AEBA",
|
|
"f c #596545145144",
|
|
"g c #30C230C230C2",
|
|
"h c #8E3882078617",
|
|
"j c #208118612081",
|
|
"k c #38E30C300820",
|
|
"l c #30C2208128A2",
|
|
"z c #38E328A238E3",
|
|
"x c #514438E34924",
|
|
"c c #618555555965",
|
|
"v c #30C2208130C2",
|
|
"b c #38E328A230C2",
|
|
"n c #28A228A228A2",
|
|
"m c #41032CB228A2",
|
|
"M c #104010401040",
|
|
"N c #492438E34103",
|
|
"B c #28A2208128A2",
|
|
"V c #A699596538E3",
|
|
"C c #30C21C711040",
|
|
"Z c #30C218611040",
|
|
"A c #965865955965",
|
|
"S c #618534D32081",
|
|
"D c #38E31C711040",
|
|
"F c #082000000820",
|
|
" ",
|
|
" .XoO ",
|
|
" +@#$%o& ",
|
|
" *=-;#::o+ ",
|
|
" >,<12#:34 ",
|
|
" 45671#:X3 ",
|
|
" +89<02qwo ",
|
|
"e* >,67;ro ",
|
|
"ty> 459@>+&& ",
|
|
"$2u+ ><ipas8* ",
|
|
"%$;=* *3:.Xa.dfg> ",
|
|
"Oh$;ya *3d.a8j,Xe.d3g8+ ",
|
|
" Oh$;ka *3d$a8lz,,xxc:.e3g54 ",
|
|
" Oh$;kO *pd$%svbzz,sxxxxfX..&wn> ",
|
|
" Oh$@mO *3dthwlsslszjzxxxxxxx3:td8M4 ",
|
|
" Oh$@g& *3d$XNlvvvlllm,mNwxxxxxxxfa.:,B* ",
|
|
" Oh$@,Od.czlllllzlmmqV@V#V@fxxxxxxxf:%j5& ",
|
|
" Oh$1hd5lllslllCCZrV#r#:#2AxxxxxxxxxcdwM* ",
|
|
" OXq6c.%8vvvllZZiqqApA:mq:Xxcpcxxxxxfdc9* ",
|
|
" 2r<6gde3bllZZrVi7S@SV77A::qApxxxxxxfdcM ",
|
|
" :,q-6MN.dfmZZrrSS:#riirDSAX@Af5xxxxxfevo",
|
|
" +A26jguXtAZZZC7iDiCCrVVii7Cmmmxxxxxx%3g",
|
|
" *#16jszN..3DZZZZrCVSA2rZrV7Dmmwxxxx&en",
|
|
" p2yFvzssXe:fCZZCiiD7iiZDiDSSZwwxx8e*>",
|
|
" OA1<jzxwwc:$d%NDZZZZCCCZCCZZCmxxfd.B ",
|
|
" 3206Bwxxszx%et.eaAp77m77mmmf3&eeeg* ",
|
|
" @26MvzxNzvlbwfpdettttttttttt.c,n& ",
|
|
" *;16=lsNwwNwgsvslbwwvccc3pcfu<o ",
|
|
" p;<69BvwwsszslllbBlllllllu<5+ ",
|
|
" OS0y6FBlvvvzvzss,u=Blllj=54 ",
|
|
" c1-699Blvlllllu7k96MMMg4 ",
|
|
" *10y8n6FjvllllB<166668 ",
|
|
" S-kg+>666<M<996-y6n<8* ",
|
|
" p71=4 m69996kD8Z-66698&& ",
|
|
" &i0ycm6n4 ogk17,0<6666g ",
|
|
" N-k-<> >=01-kuu666> ",
|
|
" ,6ky& &46-10ul,66, ",
|
|
" Ou0<> o66y<ulw<66& ",
|
|
" *kk5 >66By7=xu664 ",
|
|
" <<M4 466lj<Mxu66o ",
|
|
" *>> +66uv,zN666* ",
|
|
" 566,xxj669 ",
|
|
" 4666FF666> ",
|
|
" >966666M ",
|
|
" oM6668+ ",
|
|
" *4 ",
|
|
" ",
|
|
" "};
|
|
|
|
|
|
|
|
void close_application( GtkWidget *widget, GdkEvent *event, gpointer data ) {
|
|
gtk_main_quit();
|
|
}
|
|
|
|
int main (int argc, char *argv[])
|
|
{
|
|
|
|
GtkWidget *ventana, *pixmap, *fixed;
|
|
GdkPixmap *gdk_pixmap;
|
|
GdkBitmap *mask;
|
|
GtkStyle *style;
|
|
GdkGC *gc;
|
|
|
|
/* Creamos la ventana principal y relacionamos la señal
|
|
* delete_event para terminar la aplicación. Conviene destacar
|
|
* que la ventana no tendrá título puesto que es popup.*/
|
|
gtk_init (&argc, &argv);
|
|
ventana = gtk_window_new( GTK_WINDOW_POPUP );
|
|
gtk_signal_connect (GTK_OBJECT (ventana), "delete_event",
|
|
GTK_SIGNAL_FUNC (close_application), NULL);
|
|
gtk_widget_show (ventana);
|
|
|
|
style = gtk_widget_get_default_style();
|
|
gc = style->black_gc;
|
|
gdk_pixmap = gdk_pixmap_create_from_xpm_d( ventana->window, &mask,
|
|
&style->bg[GTK_STATE_NORMAL],
|
|
WheelbarrowFull_xpm );
|
|
pixmap = gtk_pixmap_new( gdk_pixmap, mask );
|
|
gtk_widget_show( pixmap );
|
|
|
|
|
|
fixed = gtk_fixed_new();
|
|
gtk_widget_set_usize( fixed, 200, 200 );
|
|
gtk_fixed_put( GTK_FIXED(fixed), pixmap, 0, 0 );
|
|
gtk_container_add( GTK_CONTAINER(ventana), fixed );
|
|
gtk_widget_show( fixed );
|
|
|
|
/* Con esto cubrimos todo menos la imagen */
|
|
gtk_widget_shape_combine_mask( ventana, mask, 0, 0 );
|
|
|
|
/* mostramos la ventana */
|
|
gtk_widget_set_uposition( ventana, 20, 400 );
|
|
gtk_widget_show( ventana );
|
|
gtk_main ();
|
|
|
|
return 0;
|
|
}
|
|
/* final del ejemplo */
|
|
</verb></tscreen>
|
|
|
|
Para que la carretilla sea más realista podríamos relacionar la
|
|
pulsación del botón con que haga algo. Con las líneas
|
|
siguientes la pulsación del botón hace que se acabe el programa.
|
|
|
|
<tscreen><verb>
|
|
gtk_widget_set_events( ventana,
|
|
gtk_widget_get_events( ventana ) |
|
|
GDK_BUTTON_PRESS_MASK );
|
|
|
|
gtk_signal_connect( GTK_OBJECT(ventana), "button_press_event",
|
|
GTK_SIGNAL_FUNC(close_application), NULL );
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Reglas
|
|
<p>
|
|
|
|
Las reglas son usadas para indicar la posición del puntero del
|
|
ratón en una ventana dada. Una ventana puede tener una regla
|
|
vertical a lo largo de su alto y una horizontal a lo largo de su
|
|
ancho. Un pequeño indicador triangular muestra la relación entre
|
|
el puntero del ratón y la regla.
|
|
|
|
Las reglas (horizontales y verticales) se crean usando:
|
|
|
|
<tscreen><verb>
|
|
GtkWidget *gtk_hruler_new( void ); /* horizontal */
|
|
GtkWidget *gtk_vruler_new( void ); /* vertical */
|
|
</verb></tscreen>
|
|
|
|
Las unidades de la regla pueden ser pixels, pulgadas o centímetros
|
|
(GKD_PIXELS, GDK_INCHES, GDK_CENTIMETRES). Esto se hace usando:
|
|
|
|
<tscreen><verb>
|
|
void gtk_ruler_set_metric( GtkRuler *ruler,
|
|
GtkMetricType metric );
|
|
</verb></tscreen>
|
|
|
|
El valor por defecto es GTK_PIXELS.
|
|
|
|
<tscreen><verb>
|
|
gtk_ruler_set_metric( GTK_RULER(ruler), GTK_PIXELS );
|
|
</verb></tscreen>
|
|
|
|
Otra característica importante de las reglas es cómo mostrar las
|
|
unidades de escala y la posicion inicial dónde se situa el indicador.
|
|
Todo esto se consigue mediante:
|
|
|
|
<tscreen><verb>
|
|
void gtk_ruler_set_range( GtkRuler *ruler,
|
|
gfloat lower,
|
|
gfloat upper,
|
|
gfloat posicion,
|
|
gfloat max_size );
|
|
</verb></tscreen>
|
|
|
|
Los argumentos <tt/lower/ (valor más bajo) y <tt/upper/ (más
|
|
alto) delimitan la extensión de la regla. El argumento
|
|
<tt/max_size/ es el número más alto que será mostrado. Como
|
|
es lógico <tt/posicion/ define la posición inicial del indicador
|
|
dentro de la regla.
|
|
|
|
Una regla vertical puede puede llegar a ser de 800 pixels:
|
|
|
|
<tscreen><verb>
|
|
gtk_ruler_set_range( GTK_RULER(vruler), 0, 800, 0, 800);
|
|
</verb></tscreen>
|
|
|
|
Las marcas dentro de la regla oscilarán entre 0 y 800 con una
|
|
periodicidad de 100. Si queremos que varíe entre 7 y 16
|
|
debemos usar:
|
|
|
|
<tscreen><verb>
|
|
gtk_ruler_set_range( GTK_RULER(vruler), 7, 16, 0, 20);
|
|
</verb></tscreen>
|
|
|
|
El indicador de la regla es un pequeño triángulo que señala la
|
|
posición del puntero con relación a la regla. Si la regla debe
|
|
seguir al puntero del ratón la señal motion_notify_event debe estar
|
|
conectada con el motion_notify_event de la regla. Para seguir todos
|
|
los movimientos dentro de una ventana conviene usar:
|
|
|
|
<tscreen><verb>
|
|
#define EVENT_METHOD(i, x) GTK_WIDGET_CLASS(GTK_OBJECT(i)->klass)->x
|
|
|
|
gtk_signal_connect_object( GTK_OBJECT(area), "motion_notify_event",
|
|
(GtkSignalFunc)EVENT_METHOD(ruler, motion_notify_event),
|
|
GTK_OBJECT(ruler) );
|
|
</verb></tscreen>
|
|
|
|
El siguiente ejemplo crea una zona de dibujo con una regla horizontal
|
|
y otra vertical. El tamaño de la zona de dibujo es de 600 x 400
|
|
<em/pixels/. La regla horizontal oscila entre 7 y 13 con marcas cada
|
|
100 <em/pixels/, mientras que la vertical va desde 0 a 400 con
|
|
separaciones cada 100. La zona de dibujo y las reglas se sitúan
|
|
usando una tabla.
|
|
|
|
<tscreen><verb>
|
|
/* comienzo del ejemplo rulers.c */
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
#define EVENT_METHOD(i, x) GTK_WIDGET_CLASS(GTK_OBJECT(i)->klass)->x
|
|
|
|
#define XSIZE 600
|
|
#define YSIZE 400
|
|
|
|
/* Esta rutina toma el control cuando se pulsa el botón close
|
|
*/
|
|
void close_application( GtkWidget *widget, GdkEvent *event, gpointer data ) {
|
|
gtk_main_quit();
|
|
}
|
|
|
|
int main( int argc, char *argv[] ) {
|
|
GtkWidget *ventana, *table, *area, *hrule, *vrule;
|
|
|
|
|
|
gtk_init( &argc, &argv );
|
|
|
|
ventana = gtk_window_new( GTK_WINDOW_TOPLEVEL );
|
|
gtk_signal_connect (GTK_OBJECT (ventana), "delete_event",
|
|
GTK_SIGNAL_FUNC( close_application ), NULL);
|
|
gtk_container_border_width (GTK_CONTAINER (ventana), 10);
|
|
|
|
/* creación de la tabla donde pondremos las reglas y la zona de
|
|
* dibujo */
|
|
table = gtk_table_new( 3, 2, FALSE );
|
|
gtk_container_add( GTK_CONTAINER(ventana), table );
|
|
|
|
area = gtk_drawing_area_new();
|
|
gtk_drawing_area_size( (GtkDrawingArea *)area, XSIZE, YSIZE );
|
|
gtk_table_attach( GTK_TABLE(table), area, 1, 2, 1, 2,
|
|
GTK_EXPAND|GTK_FILL, GTK_FILL, 0, 0 );
|
|
gtk_widget_set_events( area, GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK );
|
|
|
|
/* La regla horizontal está arriba. Cuando el ratón se mueve
|
|
* a lo largo de la zona de dibujo el controlador de eventos de la
|
|
* regla recibe motion_notify_event. */
|
|
hrule = gtk_hruler_new();
|
|
gtk_ruler_set_metric( GTK_RULER(hrule), GTK_PIXELS );
|
|
gtk_ruler_set_range( GTK_RULER(hrule), 7, 13, 0, 20 );
|
|
gtk_signal_connect_object( GTK_OBJECT(area), "motion_notify_event",
|
|
(GtkSignalFunc)EVENT_METHOD(hrule,
|
|
motion_notify_event),
|
|
GTK_OBJECT(hrule) );
|
|
/* GTK_WIDGET_CLASS(GTK_OBJECT(hrule)->klass)->motion_notify_event, */
|
|
gtk_table_attach( GTK_TABLE(table), hrule, 1, 2, 0, 1,
|
|
GTK_EXPAND|GTK_SHRINK|GTK_FILL, GTK_FILL, 0, 0 );
|
|
|
|
|
|
/* la zona de dibujo el controlador de eventos de la regla recibe
|
|
* motion_notify_event. */
|
|
vrule = gtk_vruler_new();
|
|
gtk_ruler_set_metric( GTK_RULER(vrule), GTK_PIXELS );
|
|
gtk_ruler_set_range( GTK_RULER(vrule), 0, YSIZE, 10, YSIZE );
|
|
gtk_signal_connect_object( GTK_OBJECT(area), "motion_notify_event",
|
|
(GtkSignalFunc)
|
|
GTK_WIDGET_CLASS(GTK_OBJECT(vrule)->klass)->
|
|
motion_notify_event,
|
|
GTK_OBJECT(vrule) );
|
|
gtk_table_attach( GTK_TABLE(table), vrule, 0, 1, 1, 2,
|
|
GTK_FILL, GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0 );
|
|
|
|
|
|
/* mostramos todo */
|
|
gtk_widget_show( area );
|
|
gtk_widget_show( hrule );
|
|
gtk_widget_show( vrule );
|
|
gtk_widget_show( table );
|
|
gtk_widget_show( ventana );
|
|
gtk_main();
|
|
|
|
return 0;
|
|
}
|
|
/* final del ejemplo */
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Barras de estado
|
|
<p>
|
|
Las barras de estado son widgets usados para mostrar un mensaje. Todo
|
|
aquello que haya sido mostrado se guarda en una pila, con lo que es
|
|
muy fácil repetir el último mensaje.
|
|
|
|
Para permitir que diferentes partes del programa usen la misma barra
|
|
de estado éstas usan Identificadores por Contexto (Context
|
|
Identifiers) para identificar a los `usuarios'. El mensaje que está
|
|
en lo alto de la pila será el siguiente en mostrarse, sin importar
|
|
el contexto en el que se esté. Los mensajes se almacenan en el
|
|
orden el último en entrar es el primero en salir, y el
|
|
Identificador por Contexto no influye en este orden.
|
|
|
|
Las barras de estado se crean con una llamada a:
|
|
|
|
<tscreen><verb>
|
|
GtkWidget *gtk_statusbar_new( void );
|
|
</verb></tscreen>
|
|
|
|
Se pide un nuevo Identificador por Contexto con una pequeña
|
|
descripción textual del contexto y una llamada a la función:
|
|
|
|
<tscreen><verb>
|
|
guint gtk_statusbar_get_context_id( GtkStatusbar *statusbar,
|
|
const gchar *context_description );
|
|
</verb></tscreen>
|
|
|
|
Hay tres funciones que pueden manipular las barras de estado:
|
|
|
|
<tscreen><verb>
|
|
guint gtk_statusbar_push( GtkStatusbar *statusbar,
|
|
guint context_id,
|
|
gchar *text );
|
|
|
|
void gtk_statusbar_pop( GtkStatusbar *statusbar)
|
|
guint context_id );
|
|
|
|
void gtk_statusbar_remove( GtkStatusbar *statusbar,
|
|
guint context_id,
|
|
guint message_id );
|
|
</verb></tscreen>
|
|
|
|
La primera, gtk_statusbar_push, se utiliza para añadir un nuevo
|
|
mensaje a la barra de estado. Devuelve un Identificador de Mensaje,
|
|
que podemos pasarle más tarde a la función gtk_statusbar_remove para
|
|
eliminar el mensaje con los Identificadores de Contexto y de Mensaje
|
|
que hay en la pila de barras de estado.
|
|
|
|
La función gtk_statusbar_pop elimina el mensaje que se encuentra
|
|
más alto en pila y que contiene el Identificador por Contexto
|
|
especificado.
|
|
|
|
El ejemplo siguiente crea una barra de estado y dos botones, uno para
|
|
meter un elemento en la barra y el otro para sacar el último elemento
|
|
introducido.
|
|
|
|
<tscreen><verb>
|
|
/* Principio del ejemplo de barras de estado statusbar.c */
|
|
|
|
#include <gtk/gtk.h>
|
|
#include <glib.h>
|
|
|
|
GtkWidget *status_bar;
|
|
|
|
void push_item (GtkWidget *widget, gpointer data)
|
|
{
|
|
static int count = 1;
|
|
char buff[20];
|
|
|
|
g_snprintf(buff, 20, "Item %d", count++);
|
|
gtk_statusbar_push( GTK_STATUSBAR(status_bar), GPOINTER_TO_INT(data), buff);
|
|
|
|
return;
|
|
}
|
|
|
|
void pop_item (GtkWidget *widget, gpointer data)
|
|
{
|
|
gtk_statusbar_pop( GTK_STATUSBAR(status_bar), GPOINTER_TO_INT(data) );
|
|
return;
|
|
}
|
|
|
|
int main (int argc, char *argv[])
|
|
{
|
|
|
|
GtkWidget *ventana;
|
|
GtkWidget *vbox;
|
|
GtkWidget *boton;
|
|
|
|
int context_id;
|
|
|
|
gtk_init (&argc, &argv);
|
|
|
|
/* crear una nueva ventana */
|
|
ventana = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
|
gtk_widget_set_usize( GTK_WIDGET (ventana), 200, 100);
|
|
gtk_window_set_title(GTK_WINDOW (ventana), "GTK Statusbar Example");
|
|
gtk_signal_connect(GTK_OBJECT (ventana), "delete_event",
|
|
(GtkSignalFunc) gtk_exit, NULL);
|
|
|
|
vbox = gtk_vbox_new(FALSE, 1);
|
|
gtk_container_add(GTK_CONTAINER(ventana), vbox);
|
|
gtk_widget_show(vbox);
|
|
|
|
status_bar = gtk_statusbar_new();
|
|
gtk_box_pack_start (GTK_BOX (vbox), status_bar, TRUE, TRUE, 0);
|
|
gtk_widget_show (status_bar);
|
|
|
|
context_id = gtk_statusbar_get_context_id(
|
|
GTK_STATUSBAR(status_bar), "Statusbar example");
|
|
|
|
boton = gtk_button_new_with_label("push item");
|
|
gtk_signal_connect(GTK_OBJECT(boton), "clicked",
|
|
GTK_SIGNAL_FUNC (push_item), &context_id);
|
|
gtk_box_pack_start(GTK_BOX(vbox), boton, TRUE, TRUE, 2);
|
|
gtk_widget_show(boton);
|
|
|
|
boton = gtk_button_new_with_label("pop last item");
|
|
gtk_signal_connect(GTK_OBJECT(boton), "clicked",
|
|
GTK_SIGNAL_FUNC (pop_item), &context_id);
|
|
gtk_box_pack_start(GTK_BOX(vbox), boton, TRUE, TRUE, 2);
|
|
gtk_widget_show(boton);
|
|
|
|
/* siempre mostramos la ventana en el último paso para que todo se
|
|
* dibuje en la pantalla de un golpe. */
|
|
gtk_widget_show(ventana);
|
|
|
|
gtk_main ();
|
|
|
|
return 0;
|
|
}
|
|
/* Final del ejemplo */
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Entrada de texto
|
|
<p>
|
|
El <em/widget/ Entry permite mostrar e introducir texto en una línea
|
|
de un cuadro de diálogo. El texto se puede poner con llamadas a funciones
|
|
que permiten reemplazar, preañadir o añadir el texto al contenido
|
|
actual del <em/widget/ Entry.
|
|
|
|
Hay dos funciones para crear un <em/widget/ Entry:
|
|
|
|
<tscreen><verb>
|
|
GtkWidget *gtk_entry_new( void );
|
|
|
|
GtkWidget *gtk_entry_new_with_max_length( guint16 max );
|
|
</verb></tscreen>
|
|
|
|
La primera sirve para crear un nuevo <em/widget/ Entry, mientras que la
|
|
segunda crea el <em/widget/ y además establece un límite en la longitud
|
|
del texto que irá en el mismo.
|
|
|
|
hay varias funciones que sirven para alterar el que texto que se está
|
|
en el <em/widget/ Entry.
|
|
|
|
<tscreen><verb>
|
|
void gtk_entry_set_text( GtkEntry *entry,
|
|
const gchar *text );
|
|
|
|
void gtk_entry_append_text( GtkEntry *entry,
|
|
const gchar *text );
|
|
|
|
void gtk_entry_prepend_text( GtkEntry *entry,
|
|
const gchar *text );
|
|
</verb></tscreen>
|
|
|
|
La función <tt/gtk_entry_set_text/ cambia el contenido actual del
|
|
<em/widget/ Entry. Las funciones <tt/gtk_entry_append_text/ y
|
|
<tt/gtk_entry_prepend_text/ permiten añadir o preañadir texto.
|
|
|
|
Las función siguientes permiten decir donde poner el punto de inserción.
|
|
|
|
<tscreen><verb>
|
|
void gtk_entry_set_position( GtkEntry *entry,
|
|
gint posicion );
|
|
</verb></tscreen>
|
|
|
|
Se pueden obtener los contenidos del <em/widget/ llamando a la función
|
|
que se describe a continuación. Obtener los contenidos del <em/widget/
|
|
puede ser útil en las funciones de llamada descritas más adelante.
|
|
|
|
<tscreen><verb>
|
|
gchar *gtk_entry_get_text( GtkEntry *entry );
|
|
</verb></tscreen>
|
|
|
|
Si quiere impedir que alguien cambie el contenido del <em/widget/ escribiendo
|
|
en él, utilice la función
|
|
|
|
<tscreen><verb>
|
|
void gtk_entry_set_editable( GtkEntry *entry,
|
|
gboolean editable );
|
|
</verb></tscreen>
|
|
|
|
Esta función permite camiar el estado de edición de un <em/widget/ Entry,
|
|
siendo el argumento <tt/editable/ TRUE o FALSE.
|
|
|
|
Si estamos utilizando el <em/widget/ Entry en un sitio donde no queremos
|
|
que el texto que se introduce sea visible, como por ejemplo cuando estamos
|
|
introduciendo una clave, podemos utilizar la función siguiente, que también
|
|
admite como argumento una bandera booleana.
|
|
|
|
<tscreen><verb>
|
|
void gtk_entry_set_visibility( GtkEntry *entry,
|
|
gboolean visible );
|
|
</verb></tscreen>
|
|
|
|
Se puede seleccionar una región del texto utilizando la siguiente función.
|
|
Esta función se puede utilizar después de poner algún texto por defecto en
|
|
el <em/widget/, haciéndole fácil al usuario eliminar este texto.
|
|
|
|
<tscreen><verb>
|
|
void gtk_entry_select_region( GtkEntry *entry,
|
|
gint start,
|
|
gint end );
|
|
</verb></tscreen>
|
|
|
|
Si queremos saber el momento en el que el usuario ha introducido el texto,
|
|
podemos conectar con las señales <tt/activate/ o <tt/changed/. <tt/activate/
|
|
se activa cuando el usuario aprieta la tecla enter en el <em/widget/.
|
|
<tt/changed/ se activa cuando cambia algo del texto, p.e. cuando se introduce
|
|
o se elimina algún carácter.
|
|
|
|
<tscreen><verb>
|
|
/* Principio del ejemplo entry entry.c */
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
void enter_callback(GtkWidget *widget, GtkWidget *entry)
|
|
{
|
|
gchar *entry_text;
|
|
entry_text = gtk_entry_get_text(GTK_ENTRY(entry));
|
|
printf("Entry contents: %s\n", entry_text);
|
|
}
|
|
|
|
void entry_toggle_editable (GtkWidget *checkbutton,
|
|
GtkWidget *entry)
|
|
{
|
|
gtk_entry_set_editable(GTK_ENTRY(entry),
|
|
GTK_TOGGLE_BUTTON(checkbutton)->active);
|
|
}
|
|
|
|
void entry_toggle_visibility (GtkWidget *checkbutton,
|
|
GtkWidget *entry)
|
|
{
|
|
gtk_entry_set_visibility(GTK_ENTRY(entry),
|
|
GTK_TOGGLE_BUTTON(checkbutton)->active);
|
|
}
|
|
|
|
int main (int argc, char *argv[])
|
|
{
|
|
|
|
GtkWidget *ventana;
|
|
GtkWidget *vbox, *hbox;
|
|
GtkWidget *entry;
|
|
GtkWidget *boton;
|
|
GtkWidget *check;
|
|
|
|
gtk_init (&argc, &argv);
|
|
|
|
/* crear una nueva ventana */
|
|
ventana = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
|
gtk_widget_set_usize( GTK_WIDGET (ventana), 200, 100);
|
|
gtk_window_set_title(GTK_WINDOW (ventana), "GTK Entry");
|
|
gtk_signal_connect(GTK_OBJECT (ventana), "delete_event",
|
|
(GtkSignalFunc) gtk_exit, NULL);
|
|
|
|
vbox = gtk_vbox_new (FALSE, 0);
|
|
gtk_container_add (GTK_CONTAINER (ventana), vbox);
|
|
gtk_widget_show (vbox);
|
|
|
|
entry = gtk_entry_new_with_max_length (50);
|
|
gtk_signal_connect(GTK_OBJECT(entry), "activate",
|
|
GTK_SIGNAL_FUNC(enter_callback),
|
|
entry);
|
|
gtk_entry_set_text (GTK_ENTRY (entry), "hello");
|
|
gtk_entry_append_text (GTK_ENTRY (entry), " world");
|
|
gtk_entry_select_region (GTK_ENTRY (entry),
|
|
0, GTK_ENTRY(entry)->text_length);
|
|
gtk_box_pack_start (GTK_BOX (vbox), entry, TRUE, TRUE, 0);
|
|
gtk_widget_show (entry);
|
|
|
|
hbox = gtk_hbox_new (FALSE, 0);
|
|
gtk_container_add (GTK_CONTAINER (vbox), hbox);
|
|
gtk_widget_show (hbox);
|
|
|
|
check = gtk_check_button_new_with_label("Editable");
|
|
gtk_box_pack_start (GTK_BOX (hbox), check, TRUE, TRUE, 0);
|
|
gtk_signal_connect (GTK_OBJECT(check), "toggled",
|
|
GTK_SIGNAL_FUNC(entry_toggle_editable), entry);
|
|
gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(check), TRUE);
|
|
gtk_widget_show (check);
|
|
|
|
check = gtk_check_button_new_with_label("Visible");
|
|
gtk_box_pack_start (GTK_BOX (hbox), check, TRUE, TRUE, 0);
|
|
gtk_signal_connect (GTK_OBJECT(check), "toggled",
|
|
GTK_SIGNAL_FUNC(entry_toggle_visibility), entry);
|
|
gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(check), TRUE);
|
|
gtk_widget_show (check);
|
|
|
|
boton = gtk_button_new_with_label ("Close");
|
|
gtk_signal_connect_object (GTK_OBJECT (boton), "clicked",
|
|
GTK_SIGNAL_FUNC(gtk_exit),
|
|
GTK_OBJECT (ventana));
|
|
gtk_box_pack_start (GTK_BOX (vbox), boton, TRUE, TRUE, 0);
|
|
GTK_WIDGET_SET_FLAGS (boton, GTK_CAN_DEFAULT);
|
|
gtk_widget_grab_default (boton);
|
|
gtk_widget_show (boton);
|
|
|
|
gtk_widget_show(ventana);
|
|
|
|
gtk_main();
|
|
return(0);
|
|
}
|
|
/* fin del ejemplo */
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Botones <em/spin/
|
|
<p>
|
|
El <em/widget/ botón <em/spin/ se utiliza normalmente para permitir
|
|
que el usuario elija un valor de un rango de valores. Consiste en una
|
|
caja para la entrada de texto con una flecha para arriba y otra para
|
|
abajo justo al lado de la caja. Si utilizamos alguna de las flechas
|
|
haremos que el valor suba o baje dentro del rango de los valores
|
|
posibles. También podemos introducir directamente un valor específico
|
|
(utilizando la caja de texto).
|
|
|
|
El <em/widget/ botón <em/spin/ permite tener valores con un número de
|
|
cifras decimales (o sin cifras decimales) y la posibilidad de
|
|
incrementarlo/decrementarlo en pasos configurables. La acción de
|
|
matener pulsado uno de los botones puede resultar (es opcional) en una
|
|
aceleración del cambio en el valor de acuerdo con el tiempo que se
|
|
mantenga pulsado.
|
|
|
|
El botón <em/spin/ utiliza un objeto <ref id="sec_Adjustment"
|
|
name="Ajuste"> para conservar la información referente al rango de
|
|
valores que puede tomar el botón <em/spin/. Esto hace que el
|
|
<em/widget/ botón <em/spin/ sea muy poderoso.
|
|
|
|
Recuerde que un <em/widget/ ajuste puede crearse con la siguiente
|
|
función, que ilustra la información que se guarda:
|
|
|
|
<tscreen><verb>
|
|
GtkObject *gtk_adjustment_new( gfloat valor,
|
|
gfloat inferior,
|
|
gfloat superior,
|
|
gfloat paso,
|
|
gfloat incremento_pagina,
|
|
gfloat tamano_pagina );
|
|
</verb></tscreen>
|
|
|
|
Estos atributos de un ajuste se utilizan en un botón <em/spin/ de la
|
|
forma siguiente:
|
|
|
|
<itemize>
|
|
<item> <tt/valor/: valor inicial del botón <em/spin/
|
|
<item> <tt/inferior/: valor inferior del rango
|
|
<item> <tt/superior/: valor superior del rango
|
|
<item> <tt/paso/: valor a incrementar/decrementar cuando pulsemos el
|
|
botón 1 en una flecha
|
|
<item> <tt/incremento_pagina/: valor a incrementar/decrementar cuando
|
|
pulsemos el botón 2 en una flecha
|
|
<item> <tt/tamano_pagina/: no se utiliza
|
|
</itemize>
|
|
|
|
Además, se puede utilizar el botón 3 para saltar directamente a los
|
|
valores <tt/superior/ o <tt/inferior/ cuando se pulsa en una de las
|
|
flechas. Veamos como crear un botón <em/spin/:
|
|
|
|
<tscreen><verb>
|
|
GtkWidget *gtk_spin_button_new( GtkAdjustment *ajuste,
|
|
gfloat aceleracion,
|
|
guint digitos );
|
|
</verb></tscreen>
|
|
|
|
El argumento <tt/aceleracion/ toma un valor entre 0.0 y 1.0 e indica
|
|
la aceleración que tendrá el botón <em/spin/. El argumento
|
|
<tt/digitos/ especifica el número de cifras decimales con que se
|
|
mostrará el valor.
|
|
|
|
Se puede reconfigurar un botón <em/spin/ después de su creación
|
|
utilizando la función:
|
|
|
|
<tscreen><verb>
|
|
void gtk_spin_button_configure( GtkSpinButton *boton_spin,
|
|
GtkAdjustment *ajuste,
|
|
gfloat aceleracion,
|
|
guint digitos );
|
|
</verb></tscreen>
|
|
|
|
El argumento <tt/boton_spin/ especifica el botón <em/spin/ que va a
|
|
reconfigurarse. El resto de argumentos son los que acabamos de
|
|
explicar.
|
|
|
|
Podemos establecer y obtener el ajuste utilizando las dos funciones
|
|
siguientes:
|
|
|
|
<tscreen><verb>
|
|
void gtk_spin_button_set_adjustment( GtkSpinButton *boton_spin,
|
|
GtkAdjustment *ajuste );
|
|
|
|
GtkAdjustment *gtk_spin_button_get_adjustment( GtkSpinButton *boton_spin );
|
|
</verb></tscreen>
|
|
|
|
El número de cifras decimales también puede alterarse utilizando:
|
|
|
|
<tscreen><verb>
|
|
void gtk_spin_button_set_digits( GtkSpinButton *boton_spin,
|
|
guint digitos) ;
|
|
</verb></tscreen>
|
|
|
|
El valor que un botón <em/spin/ está mostrando actualmente puede
|
|
cambiarse utilizando las siguientes funciones:
|
|
|
|
<tscreen><verb>
|
|
void gtk_spin_button_set_value( GtkSpinButton *boton_spin,
|
|
gfloat valor );
|
|
</verb></tscreen>
|
|
|
|
El valor actual de un botón <em/spin/ puede obtenerse como un entero o
|
|
como un flotante con las funciones siguientes:
|
|
|
|
<tscreen><verb>
|
|
gfloat gtk_spin_button_get_value_as_float( GtkSpinButton *boton_spin );
|
|
|
|
gint gtk_spin_button_get_value_as_int( GtkSpinButton *boton_spin );
|
|
</verb></tscreen>
|
|
|
|
Si quiere alterar el valor de un <em/spin/ de forma relativa a su
|
|
valor actual, puede utilizar la siguiente función:
|
|
|
|
<tscreen><verb>
|
|
void gtk_spin_button_spin( GtkSpinButton *boton_spin,
|
|
GtkSpinType direccion,
|
|
gfloat incremento );
|
|
</verb></tscreen>
|
|
|
|
El parámetro <tt/direccion/ puede tomar uno de los valores siguientes:
|
|
|
|
<itemize>
|
|
<item> GTK_SPIN_STEP_FORWARD
|
|
<item> GTK_SPIN_STEP_BACKWARD
|
|
<item> GTK_SPIN_PAGE_FORWARD
|
|
<item> GTK_SPIN_PAGE_BACKWARD
|
|
<item> GTK_SPIN_HOME
|
|
<item> GTK_SPIN_END
|
|
<item> GTK_SPIN_USER_DEFINED
|
|
</itemize>
|
|
|
|
Trataré de explicar todas las posibilidades que ofrece esta
|
|
función. Algunos de los valores que puede utilizar <tt/direccion/
|
|
hacen que se utilicen valores que están almacenados en el objeto
|
|
Ajuste que está asociado con el botón <em/spin/.
|
|
|
|
GTK_SPIN_STEP_FORWARD y GTK_SPIN_STEP_BACKWARD aumentan o disminuyen
|
|
(respectivamente) el valor del botón <em/spin/ por la cantidad
|
|
especificada por <tt/incremento/, a menos que <tt/incremento/ sea
|
|
igual a 0, en cuyo caso el valor se aumentará o disminuirá por el
|
|
valor especificado en <tt/paso/ dentro del Ajuste.
|
|
|
|
GTK_SPIN_PAGE_FORWARD y GTK_SPIN_PAGE_BACKWARD sencillamente alteran
|
|
el valor del botón <em/spin/ por la cantidad <tt/incremento/.
|
|
|
|
GTK_SPIN_HOME hace que el botón <em/spin/ tenga el mismo valor que el
|
|
valor inferior del rango Ajuste.
|
|
|
|
GTK_SPIN_END hace que el botón <em/spin/ tenga el mismo valor que el
|
|
valor superior del rango Ajuste.
|
|
|
|
GTK_SPIN_USER_DEFINED cambia el valor del botón <em/spin/ por la
|
|
cantidad especificada.
|
|
|
|
Ahora vamos a dejar de lado las funciones para establecer y obtener el
|
|
rango de los atributos del botón <em/spin/, y vamos a pasar a las
|
|
funciones que afectan a la apariencia y al comportamiento del
|
|
<em/widget/ botón <em/spin/ en sí mismo.
|
|
|
|
La primera de estas funciones se utiliza para restringir el contenido
|
|
de la caja de texto de un botón <em/spin/ a un valor numérico. Esto
|
|
evita que un usuario introduzca cualquier valor no númerico.
|
|
|
|
<tscreen><verb>
|
|
void gtk_spin_button_set_numeric( GtkSpinButton *boton_spin,
|
|
gboolean numerico );
|
|
</verb></tscreen>
|
|
|
|
Puede indicar si un botón <em/spin/ pasará del límite superior al
|
|
inferior utilizando la siguiente función:
|
|
|
|
<tscreen><verb>
|
|
void gtk_spin_button_set_wrap( GtkSpinButton *boton_spin,
|
|
gboolean wrap );
|
|
</verb></tscreen>
|
|
|
|
Puede hacer que un botón <em/spin/ redondee su valor al <tt/paso/ más
|
|
cercano, que se indica cuando creamos el Ajuste que se utiliza con el
|
|
botón <em/spin/. Para hacer que redondee tenemos que utilizar la
|
|
función siguiente:
|
|
|
|
<tscreen><verb>
|
|
void gtk_spin_button_set_snap_to_ticks( GtkSpinButton *boton_spin,
|
|
gboolean redondear );
|
|
</verb></tscreen>
|
|
|
|
Para política de actualización de un botón <em/spin/ puede cambiarse
|
|
con la siguiente función:
|
|
|
|
<tscreen><verb>
|
|
void gtk_spin_button_set_update_policy( GtkSpinButton *boton_spin,
|
|
GtkSpinButtonUpdatePolicy politica );
|
|
</verb></tscreen>
|
|
|
|
<!-- TODO: find out what this does - TRG -->
|
|
|
|
Los valores posibles de <tt/politica/ son o GTK_UPDATE_ALWAYS o
|
|
GTK_UPDATE_IF_VALID.
|
|
|
|
Estas políticas afectan al comportamiento de un botón <em/spin/ cuando
|
|
se lee el texto insertado en la caja de texto y se sincroniza con los
|
|
valores del Ajuste.
|
|
|
|
En el caso de GTK_UPDATE_IF_VALID el valor de un botón <em/spin/
|
|
cambiará si el texto introducido es un valor numérico contenido dentro
|
|
del rango especificado por el Ajuste. En caso contrario el texto
|
|
introducido se convierte al valor del botón <em/spin/.
|
|
|
|
En caso de utilizar GTK_UPDATE_ALWAYS se ignorarán los errores que
|
|
puedan ocurrir en la conversión del texto en un valor numérico.
|
|
|
|
El aspecto de los botones utilizados en un botón <em/spin/ pueden
|
|
cambiarse utilizando las siguientes funciones:
|
|
|
|
<tscreen><verb>
|
|
void gtk_spin_button_set_shadow_type( GtkSpinButton *boton_spin,
|
|
GtkShadowType tipo_sombra );
|
|
</verb></tscreen>
|
|
|
|
Como siempre, el <tt/tipo_sombra/ puede ser uno de los siguientes:
|
|
|
|
<itemize>
|
|
<item> GTK_SHADOW_IN
|
|
<item> GTK_SHADOW_OUT
|
|
<item> GTK_SHADOW_ETCHED_IN
|
|
<item> GTK_SHADOW_ETCHED_OUT
|
|
</itemize>
|
|
|
|
Finalmente, puede pedir de forma explícita que un botón <em/spin/ se
|
|
actualice a sí mismo:
|
|
|
|
<tscreen><verb>
|
|
void gtk_spin_button_update( GtkSpinButton *boton_spin );
|
|
</verb></tscreen>
|
|
|
|
Es hora de un nuevo ejemplo.
|
|
|
|
<tscreen><verb>
|
|
/* principio del ejemplo spinbutton spinbutton.c */
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
static GtkWidget *spinner1;
|
|
|
|
void toggle_snap( GtkWidget *widget,
|
|
GtkSpinButton *spin )
|
|
{
|
|
gtk_spin_button_set_snap_to_ticks (spin, GTK_TOGGLE_BUTTON (widget)->active);
|
|
}
|
|
|
|
void toggle_numeric( GtkWidget *widget,
|
|
GtkSpinButton *spin )
|
|
{
|
|
gtk_spin_button_set_numeric (spin, GTK_TOGGLE_BUTTON (widget)->active);
|
|
}
|
|
|
|
void change_digits( GtkWidget *widget,
|
|
GtkSpinButton *spin )
|
|
{
|
|
gtk_spin_button_set_digits (GTK_SPIN_BUTTON (spinner1),
|
|
gtk_spin_button_get_value_as_int (spin));
|
|
}
|
|
|
|
void get_value( GtkWidget *widget,
|
|
gpointer data )
|
|
{
|
|
gchar buf[32];
|
|
GtkLabel *etiqueta;
|
|
GtkSpinButton *spin;
|
|
|
|
spin = GTK_SPIN_BUTTON (spinner1);
|
|
etiqueta = GTK_LABEL (gtk_object_get_user_data (GTK_OBJECT (widget)));
|
|
if (GPOINTER_TO_INT (data) == 1)
|
|
sprintf (buf, "%d", gtk_spin_button_get_value_as_int (spin));
|
|
else
|
|
sprintf (buf, "%0.*f", spin->digits,
|
|
gtk_spin_button_get_value_as_float (spin));
|
|
gtk_label_set_text (etiqueta, buf);
|
|
}
|
|
|
|
|
|
int main( int argc,
|
|
char *argv[] )
|
|
{
|
|
GtkWidget *ventana;
|
|
GtkWidget *frame;
|
|
GtkWidget *hbox;
|
|
GtkWidget *main_vbox;
|
|
GtkWidget *vbox;
|
|
GtkWidget *vbox2;
|
|
GtkWidget *spinner2;
|
|
GtkWidget *spinner;
|
|
GtkWidget *boton;
|
|
GtkWidget *etiqueta;
|
|
GtkWidget *val_label;
|
|
GtkAdjustment *adj;
|
|
|
|
/* Inicializar GTK */
|
|
gtk_init(&argc, &argv);
|
|
|
|
ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
|
|
|
gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
|
|
GTK_SIGNAL_FUNC (gtk_main_quit),
|
|
NULL);
|
|
|
|
gtk_window_set_title (GTK_WINDOW (ventana), "Spin Button");
|
|
|
|
main_vbox = gtk_vbox_new (FALSE, 5);
|
|
gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 10);
|
|
gtk_container_add (GTK_CONTAINER (ventana), main_vbox);
|
|
|
|
frame = gtk_frame_new ("Not accelerated");
|
|
gtk_box_pack_start (GTK_BOX (main_vbox), frame, TRUE, TRUE, 0);
|
|
|
|
vbox = gtk_vbox_new (FALSE, 0);
|
|
gtk_container_set_border_width (GTK_CONTAINER (vbox), 5);
|
|
gtk_container_add (GTK_CONTAINER (frame), vbox);
|
|
|
|
/* spin del día, mes y año */
|
|
|
|
hbox = gtk_hbox_new (FALSE, 0);
|
|
gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 5);
|
|
|
|
vbox2 = gtk_vbox_new (FALSE, 0);
|
|
gtk_box_pack_start (GTK_BOX (hbox), vbox2, TRUE, TRUE, 5);
|
|
|
|
etiqueta = gtk_label_new ("Day :");
|
|
gtk_misc_set_alignment (GTK_MISC (etiqueta), 0, 0.5);
|
|
gtk_box_pack_start (GTK_BOX (vbox2), etiqueta, FALSE, TRUE, 0);
|
|
|
|
adj = (GtkAdjustment *) gtk_adjustment_new (1.0, 1.0, 31.0, 1.0,
|
|
5.0, 0.0);
|
|
spinner = gtk_spin_button_new (adj, 0, 0);
|
|
gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (spinner), TRUE);
|
|
gtk_spin_button_set_shadow_type (GTK_SPIN_BUTTON (spinner),
|
|
GTK_SHADOW_OUT);
|
|
gtk_box_pack_start (GTK_BOX (vbox2), spinner, FALSE, TRUE, 0);
|
|
|
|
vbox2 = gtk_vbox_new (FALSE, 0);
|
|
gtk_box_pack_start (GTK_BOX (hbox), vbox2, TRUE, TRUE, 5);
|
|
|
|
etiqueta = gtk_label_new ("Month :");
|
|
gtk_misc_set_alignment (GTK_MISC (etiqueta), 0, 0.5);
|
|
gtk_box_pack_start (GTK_BOX (vbox2), etiqueta, FALSE, TRUE, 0);
|
|
|
|
adj = (GtkAdjustment *) gtk_adjustment_new (1.0, 1.0, 12.0, 1.0,
|
|
5.0, 0.0);
|
|
spinner = gtk_spin_button_new (adj, 0, 0);
|
|
gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (spinner), TRUE);
|
|
gtk_spin_button_set_shadow_type (GTK_SPIN_BUTTON (spinner),
|
|
GTK_SHADOW_ETCHED_IN);
|
|
gtk_box_pack_start (GTK_BOX (vbox2), spinner, FALSE, TRUE, 0);
|
|
|
|
vbox2 = gtk_vbox_new (FALSE, 0);
|
|
gtk_box_pack_start (GTK_BOX (hbox), vbox2, TRUE, TRUE, 5);
|
|
|
|
etiqueta = gtk_label_new ("Year :");
|
|
gtk_misc_set_alignment (GTK_MISC (etiqueta), 0, 0.5);
|
|
gtk_box_pack_start (GTK_BOX (vbox2), etiqueta, FALSE, TRUE, 0);
|
|
|
|
adj = (GtkAdjustment *) gtk_adjustment_new (1998.0, 0.0, 2100.0,
|
|
1.0, 100.0, 0.0);
|
|
spinner = gtk_spin_button_new (adj, 0, 0);
|
|
gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (spinner), FALSE);
|
|
gtk_spin_button_set_shadow_type (GTK_SPIN_BUTTON (spinner),
|
|
GTK_SHADOW_IN);
|
|
gtk_widget_set_usize (spinner, 55, 0);
|
|
gtk_box_pack_start (GTK_BOX (vbox2), spinner, FALSE, TRUE, 0);
|
|
|
|
frame = gtk_frame_new ("Accelerated");
|
|
gtk_box_pack_start (GTK_BOX (main_vbox), frame, TRUE, TRUE, 0);
|
|
|
|
vbox = gtk_vbox_new (FALSE, 0);
|
|
gtk_container_set_border_width (GTK_CONTAINER (vbox), 5);
|
|
gtk_container_add (GTK_CONTAINER (frame), vbox);
|
|
|
|
hbox = gtk_hbox_new (FALSE, 0);
|
|
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 5);
|
|
|
|
vbox2 = gtk_vbox_new (FALSE, 0);
|
|
gtk_box_pack_start (GTK_BOX (hbox), vbox2, TRUE, TRUE, 5);
|
|
|
|
etiqueta = gtk_label_new ("Value :");
|
|
gtk_misc_set_alignment (GTK_MISC (etiqueta), 0, 0.5);
|
|
gtk_box_pack_start (GTK_BOX (vbox2), etiqueta, FALSE, TRUE, 0);
|
|
|
|
adj = (GtkAdjustment *) gtk_adjustment_new (0.0, -10000.0, 10000.0,
|
|
0.5, 100.0, 0.0);
|
|
spinner1 = gtk_spin_button_new (adj, 1.0, 2);
|
|
gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (spinner1), TRUE);
|
|
gtk_widget_set_usize (spinner1, 100, 0);
|
|
gtk_box_pack_start (GTK_BOX (vbox2), spinner1, FALSE, TRUE, 0);
|
|
|
|
vbox2 = gtk_vbox_new (FALSE, 0);
|
|
gtk_box_pack_start (GTK_BOX (hbox), vbox2, TRUE, TRUE, 5);
|
|
|
|
etiqueta = gtk_label_new ("Digits :");
|
|
gtk_misc_set_alignment (GTK_MISC (etiqueta), 0, 0.5);
|
|
gtk_box_pack_start (GTK_BOX (vbox2), etiqueta, FALSE, TRUE, 0);
|
|
|
|
adj = (GtkAdjustment *) gtk_adjustment_new (2, 1, 5, 1, 1, 0);
|
|
spinner2 = gtk_spin_button_new (adj, 0.0, 0);
|
|
gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (spinner2), TRUE);
|
|
gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
|
|
GTK_SIGNAL_FUNC (change_digits),
|
|
(gpointer) spinner2);
|
|
gtk_box_pack_start (GTK_BOX (vbox2), spinner2, FALSE, TRUE, 0);
|
|
|
|
hbox = gtk_hbox_new (FALSE, 0);
|
|
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 5);
|
|
|
|
boton = gtk_check_button_new_with_label ("Snap to 0.5-ticks");
|
|
gtk_signal_connect (GTK_OBJECT (boton), "clicked",
|
|
GTK_SIGNAL_FUNC (toggle_snap),
|
|
spinner1);
|
|
gtk_box_pack_start (GTK_BOX (vbox), boton, TRUE, TRUE, 0);
|
|
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (boton), TRUE);
|
|
|
|
boton = gtk_check_button_new_with_label ("Numeric only input mode");
|
|
gtk_signal_connect (GTK_OBJECT (boton), "clicked",
|
|
GTK_SIGNAL_FUNC (toggle_numeric),
|
|
spinner1);
|
|
gtk_box_pack_start (GTK_BOX (vbox), boton, TRUE, TRUE, 0);
|
|
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (boton), TRUE);
|
|
|
|
val_label = gtk_label_new ("");
|
|
|
|
hbox = gtk_hbox_new (FALSE, 0);
|
|
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 5);
|
|
boton = gtk_button_new_with_label ("Value as Int");
|
|
gtk_object_set_user_data (GTK_OBJECT (boton), val_label);
|
|
gtk_signal_connect (GTK_OBJECT (boton), "clicked",
|
|
GTK_SIGNAL_FUNC (get_value),
|
|
GINT_TO_POINTER (1));
|
|
gtk_box_pack_start (GTK_BOX (hbox), boton, TRUE, TRUE, 5);
|
|
|
|
boton = gtk_button_new_with_label ("Value as Float");
|
|
gtk_object_set_user_data (GTK_OBJECT (boton), val_label);
|
|
gtk_signal_connect (GTK_OBJECT (boton), "clicked",
|
|
GTK_SIGNAL_FUNC (get_value),
|
|
GINT_TO_POINTER (2));
|
|
gtk_box_pack_start (GTK_BOX (hbox), boton, TRUE, TRUE, 5);
|
|
|
|
gtk_box_pack_start (GTK_BOX (vbox), val_label, TRUE, TRUE, 0);
|
|
gtk_label_set_text (GTK_LABEL (val_label), "0");
|
|
|
|
hbox = gtk_hbox_new (FALSE, 0);
|
|
gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, TRUE, 0);
|
|
|
|
boton = gtk_button_new_with_label ("Close");
|
|
gtk_signal_connect_object (GTK_OBJECT (boton), "clicked",
|
|
GTK_SIGNAL_FUNC (gtk_widget_destroy),
|
|
GTK_OBJECT (ventana));
|
|
gtk_box_pack_start (GTK_BOX (hbox), boton, TRUE, TRUE, 5);
|
|
|
|
gtk_widget_show_all (ventana);
|
|
|
|
/* Entramos dentro del bucle de eventos */
|
|
gtk_main ();
|
|
|
|
return(0);
|
|
}
|
|
/* fin del ejemplo */
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Caja combinada (<em/Combo Box/)
|
|
<p>
|
|
La caja combinada o <em/combo box/ es otro sencillo <em/widget/
|
|
exclusivamente compuesto por otros <em/widgets/. Desde el punto de
|
|
vista del usuario, el <em/widget/ consiste en una caja para la
|
|
introducción de texto y un menú desplegable desde el que el usuario
|
|
puede seleccionar una de un conjunto predefinido de entradas. De forma
|
|
alternativa, el usuario puede introducir una opción diferente en la
|
|
caja de texto.
|
|
|
|
El siguiente extracto de la estructura que define un Combo Box
|
|
identifica algunos de sus componentes:
|
|
|
|
<tscreen><verb>
|
|
struct _GtkCombo {
|
|
GtkHBox hbox;
|
|
GtkWidget *entry;
|
|
GtkWidget *boton;
|
|
GtkWidget *popup;
|
|
GtkWidget *popwin;
|
|
GtkWidget *list;
|
|
... };
|
|
</verb></tscreen>
|
|
|
|
Como puede ver, el Combo Box tiene dos partes principales que tiene
|
|
que conocer: un <em/widget entry/ y un <em/widget list/ (lista).
|
|
|
|
Lo primero, para crear un combo box, utilice:
|
|
|
|
<tscreen><verb>
|
|
GtkWidget *gtk_combo_new( void );
|
|
</verb></tscreen>
|
|
|
|
Ahora, si quiere indicar la cadena que debe aparecer en la sección
|
|
entry del combo box, podrá hacerlo manipulando directamente el
|
|
<em/widget/ <tt/entry/:
|
|
|
|
<tscreen><verb>
|
|
gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(combo)->entry), "Mi cadena.");
|
|
</verb></tscreen>
|
|
|
|
Para introducir valores en la lista desplegable, puede utilizar la
|
|
función:
|
|
|
|
<tscreen><verb>
|
|
void gtk_combo_set_popdown_strings( GtkCombo *combo,
|
|
GList *cadenas );
|
|
</verb></tscreen>
|
|
|
|
Antes de llamar a esta función, tiene que ensamblar una GList con las
|
|
cadenas que quiere. GList es una implementación de una lista enlazada
|
|
que forma parte de <ref id="sec_glib" name="glib">, una biblioteca
|
|
base de GTK. Por el momento, la explicación fea y rápida es que tiene
|
|
que crear un puntero GList, hacerlo igual a NULL, y añadirle cadenas
|
|
de texto con la función
|
|
|
|
<tscreen><verb>
|
|
GList *g_list_append( GList *glist,
|
|
gpointer datos );
|
|
</verb></tscreen>
|
|
|
|
Es importante que inicialice el puntero GList a NULL antes de
|
|
utilizarlo. El valor devuelto por la función g_list_append debe
|
|
utilizarse como el nuevo puntero a la GList.
|
|
|
|
Aquí tenemos un trozo de código típico para crear un conjunto de
|
|
opciones:
|
|
|
|
<tscreen><verb>
|
|
GList *glist=NULL;
|
|
|
|
glist = g_list_append(glist, "Cadena 1");
|
|
glist = g_list_append(glist, "Cadena 2");
|
|
glist = g_list_append(glist, "Cadena 3");
|
|
glist = g_list_append(glist, "Cadena 4");
|
|
|
|
gtk_combo_set_popdown_strings( GTK_COMBO(combo), glist) ;
|
|
</verb></tscreen>
|
|
|
|
A partir de este momento tendrá un combo box completo funcionando. Hay
|
|
unos cuantos aspectos de su funcionamiento que puede cambiar. Para
|
|
hacerlo tiene las funciones siguientes:
|
|
|
|
<tscreen><verb>
|
|
void gtk_combo_set_use_arrows( GtkCombo *combo,
|
|
gint valor );
|
|
|
|
void gtk_combo_set_use_arrows_always( GtkCombo *combo,
|
|
gint valor );
|
|
|
|
void gtk_combo_set_case_sensitive( GtkCombo *combo,
|
|
gint valor );
|
|
</verb></tscreen>
|
|
|
|
<tt/gtk_combo_set_use_arrows()/ le deja al usuario cambiar el valor
|
|
del combo box utilizando las flechas de arriba/abajo. Utilizando estas
|
|
teclas no haremos salir la lista, pero se reemplazará el texto actual
|
|
del combo box con el siguiente elemento de la lista (superior o
|
|
inferior, según la tecla que se pulse). Esto se consigue buscando en
|
|
la lista el elemento correspondiente al valor actual del combo box y
|
|
seleccionando el anterior o el posterior (según corresponda).
|
|
Normalmente en una caja de entrada de texto las flechas se utilizan
|
|
para cambiar el foco (ie. el <em/widget/ que recibe la entrada del
|
|
teclado), pero en este caso será el TAB quien se ocupe. Cuando el
|
|
elemento actual sea el último de la lista y presione la flecha abajo
|
|
se cambiará el foco (lo mismo se aplica cuando estamos sobre el primer
|
|
elemento y pulsamos la tecla arriba).
|
|
|
|
Si el valor actual en la caja de entrada de texto no está en la lista,
|
|
entonces se desactiva la función de <tt/gtk_combo_set_use_arrows()/.
|
|
|
|
<tt/gtk_combo_set_use_arrows_always()/ igualmente permite la
|
|
utilización de las flechas arriba/abajo para cambiar el elemento
|
|
seleccionado por el siguiente/anterior de la lista, pero además trata
|
|
la lista como si fuese circular (ie. pasa del último al primer
|
|
elemento), desactivando completamente la utilidad de las teclas arriba
|
|
y abajo para cambiar el foco.
|
|
|
|
<tt/gtk_combo_set_case_sensitive()/ cambia entre una búsqueda por la
|
|
lista que discrimine entre mayúsculas y minúsculas y una búsqueda que
|
|
no discrimine. Se utiliza cuando se quiere que el <em/widget/ combo
|
|
complete el valor que se está introduciendo con un valor de la
|
|
lista. Dependiendo de esta función, se completará distinguiendo entre
|
|
mayúsculas y minúsculas o no. El <em/widget/ combo completará la
|
|
entrada actual si el usuario presiona la combinación de teclas MOD-1 y
|
|
`Tab'. MOD-1 normalmente es la tecla `Alt'. Hay algunos
|
|
administradores de ventanas que también utilizan esta combinación de
|
|
teclas, con lo que perderemos su posible utilización por parte de GTK.
|
|
|
|
Ahora que tenemos un combo box que actua como queremos que actue, todo
|
|
lo que nos queda es saber como hay que hacer para obtener los datos
|
|
que nos puede proporcionar. Esto es relativamente sencillo. La mayoría
|
|
del tiempo, de lo único que tiene que preocuparse es de obtener datos
|
|
de la caja de texto. Podemos acceder a la caja de texto mediante
|
|
GTK_ENTRY(GTK_COMBO(combo)->entry). Las dos cosas que son interesantes
|
|
hacer con esta caja son: enlazarla con la señal <tt/activate/, que
|
|
indica que el usuario ha presionado la tecla «Intro», y leer el
|
|
texto. Lo primero podemos hacerlo utilizando algo así:
|
|
|
|
<tscreen><verb>
|
|
gtk_signal_connect(GTK_OBJECT(GTK_COMB(combo)->entry), "activate",
|
|
GTK_SIGNAL_FUNC (mi_funcion_respuesta), mis_datos);
|
|
</verb></tscreen>
|
|
|
|
Para conseguir el texto que hay en la caja en cualquier momento sólo
|
|
tenemos que utilizar la función siguiente:
|
|
|
|
<tscreen><verb>
|
|
gchar *gtk_entry_get_text(GtkEntry *entry);
|
|
</verb></tscreen>
|
|
|
|
De esta forma:
|
|
|
|
<tscreen><verb>
|
|
char *cadena;
|
|
|
|
cadena = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(combo)->entry));
|
|
</verb></tscreen>
|
|
|
|
Esto es todo lo interesante. Existe otra función,
|
|
|
|
<tscreen><verb>
|
|
void gtk_combo_disable_activate(GtkCombo *combo);
|
|
</verb></tscreen>
|
|
|
|
que permite desactivar la señal <tt/activate/ en el <em/widget/ entry
|
|
dentro del combo box. Personalmente no se me ocurre ningún motivo para
|
|
utilizarla, pero existir existe.
|
|
|
|
<!-- There are also a function to set the string on a particular item, void
|
|
gtk_combo_set_item_string(GtkCombo *combo, GtkItem *item, const gchar
|
|
*item_value), but this requires that you have a pointer to the
|
|
appropriate GtkItem. Frankly, I have no idea how to do that.
|
|
-->
|
|
|
|
<!-- ************************************** -->
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1> Selección de Color
|
|
<p>
|
|
El <em/widget/ selección de color, nos permite (¡sorpresa!) la selección
|
|
interactiva de colores. Este <em/widget/ compuesto le permite al usuario
|
|
seleccionar un color manipulando los tripletes RGB (rojo, verde y azul) y
|
|
HSV (tono, saturación, valor). Para conseguirlo puede ajustar cada variable
|
|
mediante las regletas o introduciendo directamente el valor deseado.
|
|
También puede pinchar en la rueda de colores y seleccionar así el color
|
|
deseado. También se puede establecer, opcionalmente, la transparencia
|
|
del color.
|
|
|
|
El <em/widget/ de selección de color emite (por ahora) sólo una señal,
|
|
<tt/color_changed/, que se emite cuando cambia el color seleccionado,
|
|
ya sea mediante un cambio que haga el usuario o median el resultado
|
|
de una llamada a la función <tt/gtk_color_selection_set_color()/.
|
|
|
|
Echémosle un vistazo a lo que nos ofrece el <em/widget/ de selección de color.
|
|
El <em/widget/ tiene dos «sabores» diferentes; <tt/gtk_color_selection/
|
|
y <tt/gtk_color_selection_dialog/:
|
|
|
|
<tscreen><verb>
|
|
GtkWidget *gtk_color_selection_new( void );
|
|
</verb></tscreen>
|
|
|
|
Probablemente no utilizará este constructor directamente. Crea un <em/widget/
|
|
GtkColorSelection huérfano al que le tendrá que asignarle un padre. El
|
|
<em/widget/ GtkColorSelection está heredado del <em/widget/ GtkVBox.
|
|
|
|
<tscreen><verb>
|
|
GtkWidget *gtk_color_selection_dialog_new( const gchar *title );
|
|
</verb></tscreen>
|
|
|
|
Éste es el constructor del cuadro de selección de color más común. Crea un
|
|
<tt/GtkColorSelectionDialog/, heredado de un <tt/GtkDialog/. Consiste en un
|
|
<tt/GtkFrame/ con un <tt/GtkColorSelection/, un <tt/GtkHSeparator/ y un
|
|
<tt/GtkHBox/ con tres botones, «Aceptar», «Cancelar» y «Ayuda».
|
|
Puede utilizar estos botones accediendo a los <em/widgets/ <tt/ok_button/,
|
|
<tt/cancel_button/ y <tt/help_button/ de la estructura GtkColorSelectionDialog,
|
|
(es decir GTK_COLOR_SELECTION_DIALOG(colorseldialog)->ok_button).
|
|
|
|
<tscreen><verb>
|
|
void gtk_color_selection_set_update_policy( GtkColorSelection *colorsel,
|
|
GtkUpdateType policy );
|
|
</verb></tscreen>
|
|
|
|
Esta función se utiliza para indicar la política de actuación. La política
|
|
por defecto es <tt/GTK_UPDATE_CONTINUOUS/ que significa que el color
|
|
seleccionado se actualiza continuamente cuando el usuario arrastra la barra
|
|
o selecciona con el ratón un color de la rueda de colores. Si tiene problemas
|
|
de rendimiento, puede poner la política <tt/GTK_UPDATE_DISCONTINUOUS/ o
|
|
<tt/GTK_UPDATE_DELAYED/.
|
|
|
|
<tscreen><verb>
|
|
void gtk_color_selection_set_opacity( GtkColorSelection *colorsel,
|
|
gint use_opacity );
|
|
</verb></tscreen>
|
|
|
|
El <em/widget/ de selección de color admite el ajuste de la transparencia
|
|
de un color (también conocido como el canal alfa). Esta opción está
|
|
desactivada por defecto. Si se llama a esta función con <tt/use_opacity/
|
|
como TRUE se activa la transparencia. Si se utiliza <tt/use_opacity/ como
|
|
FALSE se desactiva la transparencia.
|
|
|
|
<tscreen><verb>
|
|
void gtk_color_selection_set_color( GtkColorSelection *colorsel,
|
|
gdouble *color );
|
|
</verb></tscreen>
|
|
|
|
Puede poner el color actual explicitamente haciendo uso de esta función con
|
|
un puntero a un vector de colores (de tipo <tt/gdouble/). La longitud del
|
|
vector depende de si está activada la transparencia. La posición 0 contiene
|
|
la componente roja del color, la 1 contiene la verde, la 2 la azul y la
|
|
transparencia está en la posición 3 (solamente si está activada la
|
|
transparencia, ver <tt/gtk_color_selection_set_opacity()/). Todos los
|
|
valores se encuentran entre 0.0 y 1.0.
|
|
|
|
<tscreen><verb>
|
|
void gtk_color_selection_get_color( GtkColorSelection *colorsel,
|
|
gdouble *color );
|
|
</verb></tscreen>
|
|
|
|
Cuando necesite preguntar por el color actual, normalmente cuando haya
|
|
recibido una señal <tt/color_changed/, utilice esta función. <tt/color/
|
|
es un puntero al vector de colores que se rellenará. Ver la descripción
|
|
de la función <tt/gtk_color_selection_set_color()/ para conocer la
|
|
estructura de este vector.
|
|
|
|
<!-- Need to do a whole section on DnD - TRG
|
|
Drag and drop
|
|
-------------
|
|
|
|
The color sample areas (right under the hue-saturation wheel) supports
|
|
drag and drop. The type of drag and drop is "application/x-color". The
|
|
message data consists of an array of 4 (or 5 if opacity is enabled)
|
|
gdouble values, where the value at position 0 is 0.0 (opacity on) or
|
|
1.0 (opacity off) followed by the red, green and blue values at
|
|
positions 1,2 and 3 respectively. If opacity is enabled, the opacity
|
|
is passed in the value at position 4.
|
|
-->
|
|
|
|
Aquí tenemos un pequeño ejemplo que muestra el uso de
|
|
<tt/GtkColorSelectionDialog/. El programa muestra una ventana con una
|
|
zona de dibujo. Pulsando en ella se abre un cuadro de diálogo de
|
|
selección del color, y cambiando el color en el cuadro de diálogo se
|
|
cambia el color de fondo de la zona de dibujo.
|
|
|
|
<tscreen><verb>
|
|
/* principio del ejemplo colorsel colorsel.c */
|
|
|
|
#include <glib.h>
|
|
#include <gdk/gdk.h>
|
|
#include <gtk/gtk.h>
|
|
|
|
GtkWidget *colorseldlg = NULL;
|
|
GtkWidget *drawingarea = NULL;
|
|
|
|
/* Manejador del cambio de color */
|
|
|
|
void color_changed_cb (GtkWidget *widget, GtkColorSelection *colorsel)
|
|
{
|
|
gdouble color[3];
|
|
GdkColor gdk_color;
|
|
GdkColormap *colormap;
|
|
|
|
/* Obtener el mapa de colores de la zona de dibujo */
|
|
|
|
colormap = gdk_window_get_colormap (drawingarea->window);
|
|
|
|
/* Obtener el color actual */
|
|
|
|
gtk_color_selection_get_color (colorsel,color);
|
|
|
|
/* Meterlo en un entero sin signo de 16 bits (0..65535) e insertarlo
|
|
en la estructura GdkColor */
|
|
|
|
gdk_color.red = (guint16)(color[0]*65535.0);
|
|
gdk_color.green = (guint16)(color[1]*65535.0);
|
|
gdk_color.blue = (guint16)(color[2]*65535.0);
|
|
|
|
/* Pedir memoria para el color */
|
|
|
|
gdk_color_alloc (colormap, &gdk_color);
|
|
|
|
/* Poner el color de fondo de la ventana */
|
|
|
|
gdk_window_set_background (drawingarea->window, &gdk_color);
|
|
|
|
/* Limpiar la ventana */
|
|
|
|
gdk_window_clear (drawingarea->window);
|
|
}
|
|
|
|
/* Manejador del evento Drawingarea */
|
|
|
|
gint area_event (GtkWidget *widget, GdkEvent *event, gpointer client_data)
|
|
{
|
|
gint handled = FALSE;
|
|
GtkWidget *colorsel;
|
|
|
|
/* Comprobar si hemos recibido un evento de pulsación de botón */
|
|
|
|
if (event->type == GDK_BUTTON_PRESS && colorseldlg == NULL)
|
|
{
|
|
/* Sí, ¡tenemos un evento y todavía no está el colorseldlg! */
|
|
|
|
handled = TRUE;
|
|
|
|
/* Crear el cuadro de diálogo de selección del color */
|
|
|
|
colorseldlg = gtk_color_selection_dialog_new("Select background color");
|
|
|
|
/* Obtener el widget GtkColorSelection */
|
|
|
|
colorsel = GTK_COLOR_SELECTION_DIALOG(colorseldlg)->colorsel;
|
|
|
|
/* Conectar con la señal «color_changed», darle al dato del
|
|
cliente el valor del widget colorsel */
|
|
|
|
gtk_signal_connect(GTK_OBJECT(colorsel), "color_changed",
|
|
(GtkSignalFunc)color_changed_cb, (gpointer)colorsel);
|
|
|
|
/* Mostrar el cuadro de diálogo */
|
|
|
|
gtk_widget_show(colorseldlg);
|
|
}
|
|
|
|
return handled;
|
|
}
|
|
|
|
/* Manipulador de los eventos cerrar y salir */
|
|
|
|
void destroy_window (GtkWidget *widget, gpointer client_data)
|
|
{
|
|
gtk_main_quit ();
|
|
}
|
|
|
|
/* Principal */
|
|
|
|
gint main (gint argc, gchar *argv[])
|
|
{
|
|
GtkWidget *ventana;
|
|
|
|
/* Inicializa el toolkit, y elimina las opciones relacionadas con
|
|
gtk incluidas en la línea de órdenes */
|
|
|
|
gtk_init (&argc,&argv);
|
|
|
|
/* Crea la ventana de más alto nivel, le da título y la política */
|
|
|
|
ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
|
gtk_window_set_title (GTK_WINDOW(ventana), "Color selection test");
|
|
gtk_window_set_policy (GTK_WINDOW(ventana), TRUE, TRUE, TRUE);
|
|
|
|
/* Enlaza con los eventos «delete» y «destroy», para que podamos
|
|
salir */
|
|
|
|
gtk_signal_connect (GTK_OBJECT(ventana), "delete_event",
|
|
(GtkSignalFunc)destroy_window, (gpointer)ventana);
|
|
|
|
gtk_signal_connect (GTK_OBJECT(ventana), "destroy",
|
|
(GtkSignalFunc)destroy_window, (gpointer)ventana);
|
|
|
|
/* Crea la zona de dibujo, pone el tamaño y caza los eventos de los
|
|
botones */
|
|
|
|
drawingarea = gtk_drawing_area_new ();
|
|
|
|
gtk_drawing_area_size (GTK_DRAWING_AREA(drawingarea), 200, 200);
|
|
|
|
gtk_widget_set_events (drawingarea, GDK_BUTTON_PRESS_MASK);
|
|
|
|
gtk_signal_connect (GTK_OBJECT(drawingarea), "event",
|
|
(GtkSignalFunc)area_event, (gpointer)drawingarea);
|
|
|
|
/* Add drawingarea to window, then show them both */
|
|
|
|
gtk_container_add (GTK_CONTAINER(ventana), drawingarea);
|
|
|
|
gtk_widget_show (drawingarea);
|
|
gtk_widget_show (ventana);
|
|
|
|
/* Entrar en el bucle principal de gtk (nunca sale de aquí) */
|
|
|
|
gtk_main ();
|
|
|
|
/* Para satisfacer a los compiladores pijos */
|
|
|
|
return 0;
|
|
}
|
|
/* final del ejemplo */
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1> Selección de ficheros
|
|
<p>
|
|
El <em/widget/ de selección de ficheros nos proporciona una forma
|
|
rápida y sencilla de mostrar un cuadro de diálogo para la selección de
|
|
un fichero. Ya viene con los botones Aceptar, Cancelar y Ayuda. Una
|
|
magnifica ayuda para acortar el tiempo de programación.
|
|
|
|
Para crear un nuevo cuadro de diálogo de selección de ficheros
|
|
utilice:
|
|
|
|
<tscreen><verb>
|
|
GtkWidget *gtk_file_selection_new( gchar *title );
|
|
</verb></tscreen>
|
|
|
|
Para poner el nombre del fichero en el cuadro de diálogo, por
|
|
ejemplo para poder utilizar un directorio o un fichero por defecto,
|
|
utilice la función:
|
|
|
|
<tscreen><verb>
|
|
void gtk_file_selection_set_filename( GtkFileSelection *filesel,
|
|
gchar *filename );
|
|
</verb></tscreen>
|
|
|
|
Para obtener el texto que el usuario ha introducido, utilice la función:
|
|
|
|
<tscreen><verb>
|
|
gchar *gtk_file_selection_get_filename( GtkFileSelection *filesel );
|
|
</verb></tscreen>
|
|
|
|
También hay punteros a los <em/widgets/ que contiene el cuadro de
|
|
diálogo. Son los siguientes:
|
|
|
|
<itemize>
|
|
<item>dir_list
|
|
<item>file_list
|
|
<item>selection_entry
|
|
<item>selection_text
|
|
<item>main_vbox
|
|
<item>ok_button
|
|
<item>cancel_button
|
|
<item>help_button
|
|
</itemize>
|
|
|
|
Lo más probable es que sólo utilice los punteros <tt/ok_button/,
|
|
<tt/cancel_button/, y <tt/help_button/ para controlar cuando se pulsan.
|
|
|
|
Aquí incluímos un ejemplo robado de <tt/testgtk.c/, modificado
|
|
para que se puede ejecutar independientemente. Como puede ver, no es
|
|
muy complicado crear un <em/widget/ para la selección de
|
|
ficheros. Aunque aparezca el botón de ayuda en la pantalla, no hace
|
|
nada y no tiene ninguna señal conectada.
|
|
|
|
<tscreen><verb>
|
|
/* principio del ejemplo filesel filesel.c */
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
/* Obtener el nombre del fichero e imprimirlo en la consola */
|
|
void file_ok_sel (GtkWidget *w, GtkFileSelection *fs)
|
|
{
|
|
g_print ("%s\n", gtk_file_selection_get_filename (GTK_FILE_SELECTION (fs)));
|
|
}
|
|
|
|
void destroy (GtkWidget *widget, gpointer data)
|
|
{
|
|
gtk_main_quit ();
|
|
}
|
|
|
|
int main (int argc, char *argv[])
|
|
{
|
|
GtkWidget *filew;
|
|
|
|
gtk_init (&argc, &argv);
|
|
|
|
/* Crear un nuevo widget de selección de ficheros */
|
|
filew = gtk_file_selection_new ("File selection");
|
|
|
|
gtk_signal_connect (GTK_OBJECT (filew), "destroy",
|
|
(GtkSignalFunc) destroy, &filew);
|
|
/* Conectar el ok_button con la función file_ok_sel */
|
|
gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (filew)->ok_button),
|
|
"clicked", (GtkSignalFunc) file_ok_sel, filew );
|
|
|
|
/* Conectar el cancel_button con la destrucción del widget */
|
|
gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION (filew)->cancel_button),
|
|
"clicked", (GtkSignalFunc) gtk_widget_destroy,
|
|
GTK_OBJECT (filew));
|
|
|
|
/* Damos el nombre del fichero, como si fuese un cuadro de diálogo para
|
|
grabar ficheros y estuviesemos dando un nombre por defecto */
|
|
gtk_file_selection_set_filename (GTK_FILE_SELECTION(filew),
|
|
"penguin.png");
|
|
|
|
gtk_widget_show(filew);
|
|
gtk_main ();
|
|
return 0;
|
|
}
|
|
/* fin del ejemplo */
|
|
</verb></tscreen>
|
|
|
|
<!-- ***************************************************************** -->
|
|
<sect> El <em/widget/ contenedor
|
|
<!-- ***************************************************************** -->
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1> El <em/widget/ EventBox<label id="sec_EventBox">
|
|
<label id="sec_The_EventBox_Widget">
|
|
<p>
|
|
Algunos <em/widget/ gtk no tienen asociada una ventana X, por lo que
|
|
sólo pueden dibujar en la de su padre. Debido a esto, no pueden
|
|
recibir ningún evento y si tienen un tamaño incorrecto, no se
|
|
recortarán correctamente por lo que puede que se sobreescriban ciertas
|
|
zonas, etc... Si necesita este tipo de <em/widgets/, el EventBox es
|
|
para usted.
|
|
|
|
Cuando se ve por primera vez, el <em/widget/ EventBox puede parecer
|
|
completamente inútil. No dibuja nada en la pantalla y no responde
|
|
a ningún evento. Sin embargo, tiene una utilidad - proporciona una
|
|
ventana X para su <em/widget/ hijo. Esto es importante ya que
|
|
muchos <em/widgets/ GTK no tienen una ventana X asociada. No tener una
|
|
ventana X ahorra memoria y mejora el rendimiento, pero tiene sus
|
|
desventajas. Un <em/widget/ sin una ventana X no puede recibir
|
|
eventos, y no realizará ningún recorte en sus contenidos. Aunque el
|
|
nombre <em/EventBox/ enfatiza su función de manejador de eventos, el
|
|
<em/widget/ también puede utilizarse para hacer los recortes.
|
|
(Y más... ver el ejemplo más abajo.)
|
|
|
|
Para crear un nuevo <em/widget/ EventBox, utilice:
|
|
|
|
<tscreen><verb>
|
|
GtkWidget *gtk_event_box_new( void );
|
|
</verb></tscreen>
|
|
|
|
Un <em/widget/ hijo puede añadirse a su EventBox así:
|
|
|
|
<tscreen><verb>
|
|
gtk_container_add( GTK_CONTAINER(event_box), widget );
|
|
</verb></tscreen>
|
|
|
|
El siguiente ejemplo demuestra los dos usos de EventBox - se crea
|
|
una etiqueta que se recorta dentro de una pequeña caja, y hace
|
|
que una pulsación del ratón en la misma finalice el programa.
|
|
|
|
<tscreen><verb>
|
|
/* principio del ejemplo eventbox eventbox.c */
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
int
|
|
main (int argc, char *argv[])
|
|
{
|
|
GtkWidget *ventana;
|
|
GtkWidget *event_box;
|
|
GtkWidget *etiqueta;
|
|
|
|
gtk_init (&argc, &argv);
|
|
|
|
ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
|
|
|
gtk_window_set_title (GTK_WINDOW (ventana), "Event Box");
|
|
|
|
gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
|
|
GTK_SIGNAL_FUNC (gtk_exit), NULL);
|
|
|
|
gtk_container_border_width (GTK_CONTAINER (ventana), 10);
|
|
|
|
/* Crea un EventBox y lo añade a nuestra ventana superior */
|
|
|
|
event_box = gtk_event_box_new ();
|
|
gtk_container_add (GTK_CONTAINER(ventana), event_box);
|
|
gtk_widget_show (event_box);
|
|
|
|
/* Crea una larga etiqueta */
|
|
|
|
etiqueta = gtk_label_new ("Click here to quit, quit, quit, quit, quit");
|
|
gtk_container_add (GTK_CONTAINER (event_box), etiqueta);
|
|
gtk_widget_show (etiqueta);
|
|
|
|
/* La recortamos. */
|
|
gtk_widget_set_usize (etiqueta, 110, 20);
|
|
|
|
/* Y enlazamos una acción con la etiqueta */
|
|
gtk_widget_set_events (event_box, GDK_BUTTON_PRESS_MASK);
|
|
gtk_signal_connect (GTK_OBJECT(event_box), "button_press_event",
|
|
GTK_SIGNAL_FUNC (gtk_exit), NULL);
|
|
|
|
/* Otra cosa más que necesita una ventana X ... */
|
|
|
|
gtk_widget_realize (event_box);
|
|
gdk_window_set_cursor (event_box->window, gdk_cursor_new (GDK_HAND1));
|
|
|
|
gtk_widget_show (ventana);
|
|
|
|
gtk_main ();
|
|
|
|
return 0;
|
|
}
|
|
/* Final del ejemplo */
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>El <em/widget/ alineamiento <label id="sec_Alignment">
|
|
<p>
|
|
El <em/widget/ alineamiento (<em/alignment/) le permitirá colocar un
|
|
<em/widget/ dentro de su ventana utilizando una posición y un tamaño
|
|
relativos al mismo <em/widget/ de alineamiento. Por ejemplo, puede ser
|
|
muy útil para centrar un <em/widget/ en la ventana.
|
|
|
|
Sólo hay dos funciones asociadas con el <em/widget/ alineamiento:
|
|
|
|
<tscreen><verb>
|
|
GtkWidget* gtk_alignment_new( gfloat xalign,
|
|
gfloat yalign,
|
|
gfloat xscale,
|
|
gfloat yscale );
|
|
|
|
void gtk_alignment_set( GtkAlignment *alignment,
|
|
gfloat xalign,
|
|
gfloat yalign,
|
|
gfloat xscale,
|
|
gfloat yscale );
|
|
</verb></tscreen>
|
|
|
|
La primera función crea un nuevo <em/widget/ alineamiento con los
|
|
parámetros especificados. La segunda función permite alterar los
|
|
parámetros de un <em/widget/ alineamiento ya existente.
|
|
|
|
Los cuatro parámetros de alineamiento son números en coma flotante que
|
|
pueden tener variar entre 0.0 y 1.0. Los argumentos <tt/xalign/ e
|
|
<tt/yalign/ afectan a la posición del <em/widget/ colocado dentro del
|
|
<em/widget/ de alineamiento. Los argumentos <tt/xscale/ e <tt/yscale/
|
|
afectan a la cantidad de espacio que ocupa el <em/widget/.
|
|
|
|
Se le puede añadir un <em/widget/ hijo a un alineamiento utilizando:
|
|
|
|
<tscreen><verb>
|
|
gtk_container_add( GTK_CONTAINER(alignment), child_widget );
|
|
</verb></tscreen>
|
|
|
|
Para ver un ejemplo de utilización del <em/widget/ alineamiento,
|
|
diríjase al ejemplo del <em/widget/ <ref id="sec_ProgressBar"
|
|
name="Barra de progreso">.
|
|
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1> Contenedor fijo
|
|
<p>
|
|
El contenedor fijo le permite situar <em/widgets/ en una posición fija
|
|
dentro de su ventana, relativa a la esquina superior izquierda. La
|
|
posición de los <em/widgets/ puede cambiarse dinámicamente.
|
|
|
|
Sólo hay tres funciones asociadas al <em/widget/ contenedor fijo:
|
|
|
|
<tscreen><verb>
|
|
GtkWidget* gtk_fixed_new( void );
|
|
|
|
void gtk_fixed_put( GtkFixed *fixed,
|
|
GtkWidget *widget,
|
|
gint16 x,
|
|
gint16 y );
|
|
|
|
void gtk_fixed_move( GtkFixed *fixed,
|
|
GtkWidget *widget,
|
|
gint16 x,
|
|
gint16 y );
|
|
</verb></tscreen>
|
|
|
|
La función <tt/gtk_fixed_new/ permite la creación de un nuevo
|
|
contenedor fijo.
|
|
|
|
<tt/gtk_fixed_put/ situa <tt/widget/ dentro del contenedor <tt/fixed/
|
|
en la posición especificada por <tt/x/ e <tt/y/.
|
|
|
|
<tt/gtk_fixed_move/ permite que mover hacia una nuevo posición el
|
|
<em/widget/ especificado.
|
|
|
|
El ejemplo siguiente muestra como utilizar el contenedor fijo.
|
|
|
|
<tscreen><verb>
|
|
/* principio del ejemplo fixed fixed.c */
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
/* Voy a ser un poco torpe y utilizar algunas variables
|
|
* globales para almacenar la posición del widget que
|
|
* hay dentro del contenedor */
|
|
gint x=50;
|
|
gint y=50;
|
|
|
|
/* Esta función de llamada mueve el botón a una nueva
|
|
* posición dentro del contenedor fijo. */
|
|
void move_button( GtkWidget *widget,
|
|
GtkWidget *fixed )
|
|
{
|
|
x = (x+30)%300;
|
|
y = (y+50)%300;
|
|
gtk_fixed_move( GTK_FIXED(fixed), widget, x, y);
|
|
}
|
|
|
|
int main( int argc,
|
|
char *argv[] )
|
|
{
|
|
/* GtkWidget es el tipo de almacenamiento para los widgets */
|
|
GtkWidget *ventana;
|
|
GtkWidget *fixed;
|
|
GtkWidget *boton;
|
|
gint i;
|
|
|
|
/* Inicializa GTK */
|
|
gtk_init(&argc, &argv);
|
|
|
|
/* Crear una nueva ventana */
|
|
ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
|
gtk_window_set_title(GTK_WINDOW(ventana), "Fixed Container");
|
|
|
|
/* Aquí conectamos el evento "destroy" al manejador de la señal */
|
|
gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
|
|
GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
|
|
|
|
/* Establecemos el ancho del borde la ventana */
|
|
gtk_container_set_border_width (GTK_CONTAINER (ventana), 10);
|
|
|
|
/* Creamos un contenedor fijo */
|
|
fixed = gtk_fixed_new();
|
|
gtk_container_add(GTK_CONTAINER(ventana), fixed);
|
|
gtk_widget_show(fixed);
|
|
|
|
for (i = 1 ; i <= 3 ; i++) {
|
|
/* Crea un nuevo botón con la etiqueta "Press me" */
|
|
boton = gtk_button_new_with_label ("Press me");
|
|
|
|
/* Cuando el botón reciba la señal "pulsado", llamará a la función
|
|
* move_button() pasándole el contenedor fijo como argumento. */
|
|
gtk_signal_connect (GTK_OBJECT (boton), "clicked",
|
|
GTK_SIGNAL_FUNC (move_button), fixed);
|
|
|
|
/* Esto mete el botón dentro de la ventana del window contenedor
|
|
* fijo. */
|
|
gtk_fixed_put (GTK_FIXED (fixed), boton, i*50, i*50);
|
|
|
|
/* El paso final es mostrar el widget recien creado */
|
|
gtk_widget_show (boton);
|
|
}
|
|
|
|
/* Mostrar la ventana */
|
|
gtk_widget_show (ventana);
|
|
|
|
/* Entrar en el bucle principal */
|
|
gtk_main ();
|
|
|
|
return(0);
|
|
}
|
|
/* fin del ejemplo */
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1> Contenedor capa
|
|
<p>
|
|
El contenedor capa es similar al contenedor fijo, excepto que permite
|
|
implementar una zona de <em/scroll/ infinita (donde infinito significa
|
|
menor de 2^32). Xwindows tiene una limitación en la que las ventanas
|
|
pueden tener un máximo de 32767 <em/pixels/ de alto o de ancho. El
|
|
contenedor capa sortea esta limitación con una exótica combinación de
|
|
ventanas y <em/bits/ de gravedad, <!-- Si alguien entiende que
|
|
significa esto: e98cuenc@criens.u-psud.fr --> para que puede tener un
|
|
suave <em/scroll/ aún cuando utilice una gran cantidad de <em/widgets/
|
|
hijos dentro de su zona de <em/scroll/.
|
|
|
|
Podrá crear un contenedor capa utilizando:
|
|
|
|
<tscreen><verb>
|
|
GtkWidget *gtk_layout_new( GtkAdjustment *hadjustment,
|
|
GtkAdjustment *vadjustment );
|
|
</verb></tscreen>
|
|
|
|
Como puede observar, podrá especificar (de forma opcional) los objetos
|
|
de ajuste que utilizará el <em/widget/ capa para hacer su <em/scroll/.
|
|
|
|
Puede añadir y mover <em/widgets/ dentro del contenedor capa
|
|
utilizando las dos funciones siguientes:
|
|
|
|
<tscreen><verb>
|
|
void gtk_layout_put( GtkLayout *layout,
|
|
GtkWidget *widget,
|
|
gint x,
|
|
gint y );
|
|
|
|
void gtk_layout_move( GtkLayout *layout,
|
|
GtkWidget *widget,
|
|
gint x,
|
|
gint y );
|
|
</verb></tscreen>
|
|
|
|
El tamaño del contenedor capa se puede establecer utilizando la
|
|
siguiente función:
|
|
|
|
<tscreen><verb>
|
|
void gtk_layout_set_size( GtkLayout *layout,
|
|
guint width,
|
|
guint height );
|
|
</verb></tscreen>
|
|
|
|
Los contenedores capa son uno de los poquísimos <em/widgets/ dentro de
|
|
GTK que se repintan ellos mismos en la pantalla cuando se cambian
|
|
utilizando las funciones anteriores (la inmensa mayoria de los
|
|
<em/widgets/ mandan una petición a la cola que será procesada cuando
|
|
se devuelva el control a la función <tt/gtk_main()/).
|
|
|
|
Cuando quiera hacer una gran cantidad de cambios dentro del contenedor
|
|
capa, podrá utilizar las dos funciones siguientes para desactivar y
|
|
reactivar la característica de repintado:
|
|
|
|
<tscreen><verb>
|
|
void gtk_layout_freeze( GtkLayout *layout );
|
|
|
|
void gtk_layout_thaw( GtkLayout *layout );
|
|
</verb></tscreen>
|
|
|
|
Las cuatro funciones finales a utilizar con los <em/widgets/capa son
|
|
para la manipulación de los <em/widgets/ de ajuste horizontal y
|
|
vertical:
|
|
|
|
<tscreen><verb>
|
|
GtkAdjustment* gtk_layout_get_hadjustment( GtkLayout *layout );
|
|
|
|
GtkAdjustment* gtk_layout_get_vadjustment( GtkLayout *layout );
|
|
|
|
void gtk_layout_set_hadjustment( GtkLayout *layout,
|
|
GtkAdjustment *adjustment );
|
|
|
|
void gtk_layout_set_vadjustment( GtkLayout *layout,
|
|
GtkAdjustment *adjustment);
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1> Marcos <label id="sec_Frames">
|
|
<p>
|
|
Los marcos pueden utilizarse para meter uno o un grupo de
|
|
<em/widgets/dentro de una caja puede ser (de forma opcional)
|
|
etiquetada. La posición de la etiqueta y el estilo de la caja pueden
|
|
modificarse.
|
|
|
|
Se puede crear un marco con la siguiente función:
|
|
|
|
<tscreen><verb>
|
|
GtkWidget *gtk_frame_new( const gchar *etiqueta );
|
|
</verb></tscreen>
|
|
|
|
La etiqueta se coloca por defecto en la esquina superior izquierda del
|
|
marco. Si el argumento <tt/etiqueta/ es NULL no se mostrará ninguna
|
|
etiqueta. Puede cambiarse el texto de la etiqueta utilizando la
|
|
función siguiente.
|
|
|
|
<tscreen><verb>
|
|
void gtk_frame_set_label( GtkFrame *frame,
|
|
const gchar *etiqueta );
|
|
</verb></tscreen>
|
|
|
|
La posición de la etiqueta se puede cambiar utilizado la función:
|
|
|
|
<tscreen><verb>
|
|
void gtk_frame_set_label_align( GtkFrame *frame,
|
|
gfloat xalign,
|
|
gfloat yalign );
|
|
</verb></tscreen>
|
|
|
|
<tt/xalign/ e <tt/yalign/ toman valores entre 0.0 y 1.0. <tt/yalign/
|
|
no se actualmente no se utiliza. El valor por defecto de <tt/xalign/
|
|
es 0.0, lo que coloca la etiqueta a la izquierda del marco.
|
|
|
|
La siguiente función altera el estilo de la caja que se utiliza para
|
|
señalar el marco.
|
|
|
|
<tscreen><verb>
|
|
void gtk_frame_set_shadow_type( GtkFrame *frame,
|
|
GtkShadowType type);
|
|
</verb></tscreen>
|
|
|
|
El argumento <tt/type/ puede tomar uno de los valores siguientes:
|
|
|
|
<itemize>
|
|
<item> GTK_SHADOW_NONE
|
|
<item> GTK_SHADOW_IN
|
|
<item> GTK_SHADOW_OUT
|
|
<item> GTK_SHADOW_ETCHED_IN (the default)
|
|
<item> GTK_SHADOW_ETCHED_OUT
|
|
</itemize>
|
|
|
|
El código siguiente ilustra la utilización del <em/widget/ marco.
|
|
|
|
<tscreen><verb>
|
|
/* principio del ejemplo frame frame.c */
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
int main( int argc,
|
|
char *argv[] )
|
|
{
|
|
/* GtkWidget es el tipo de almacenamiento para los widgets */
|
|
GtkWidget *ventana;
|
|
GtkWidget *frame;
|
|
GtkWidget *boton;
|
|
gint i;
|
|
|
|
/* Inicializa GTK */
|
|
gtk_init(&argc, &argv);
|
|
|
|
/* Crea una nueva ventana */
|
|
ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
|
gtk_window_set_title(GTK_WINDOW(ventana), "Frame Example");
|
|
|
|
/* Aquí conectamos el evento "destroy"al manejador de señal */
|
|
gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
|
|
GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
|
|
|
|
gtk_widget_set_usize(ventana, 300, 300);
|
|
|
|
/* Establecemos el ancho del borde de la ventana */
|
|
gtk_container_set_border_width (GTK_CONTAINER (ventana), 10);
|
|
|
|
/* Crea un marco */
|
|
frame = gtk_frame_new(NULL);
|
|
gtk_container_add(GTK_CONTAINER(ventana), frame);
|
|
|
|
/* Establece la etiqueta del marco */
|
|
gtk_frame_set_label( GTK_FRAME(frame), "GTK Frame Widget" );
|
|
|
|
/* Alinea la etiqueta a la derecha del marco */
|
|
gtk_frame_set_label_align( GTK_FRAME(frame), 1.0, 0.0);
|
|
|
|
/* Establece el estilo del marco */
|
|
gtk_frame_set_shadow_type( GTK_FRAME(frame), GTK_SHADOW_ETCHED_OUT);
|
|
|
|
gtk_widget_show(frame);
|
|
|
|
/* Muestra la ventana */
|
|
gtk_widget_show (ventana);
|
|
|
|
/* Entra dentro del bucle principal */
|
|
gtk_main ();
|
|
|
|
return(0);
|
|
}
|
|
/* fin del ejemplo */
|
|
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1> Marcos con proporciones fijas
|
|
<p>
|
|
El <em/widget aspect frame/ (marco proporcional) es como el <em/widget
|
|
frame/ (marco), excepto que conserva las proporciones (esto es, la
|
|
relación entre el ancho y el alto) del <em/widget/ hijo, añadiendo
|
|
espacio extra en caso de ser necesario. Esto es útil, por ejemplo, si
|
|
quiere hacer una vista previa de una gran imagen. El tamaño de la
|
|
vista previa debería variar cuando el usuario redimensione la ventana,
|
|
pero la proporción tiene que coincidir con la de la imagen original.
|
|
|
|
Para crear un nuevo marco proporcional utilice:
|
|
|
|
<tscreen><verb>
|
|
GtkWidget *gtk_aspect_frame_new( const gchar *etiqueta,
|
|
gfloat xalign,
|
|
gfloat yalign,
|
|
gfloat ratio,
|
|
gint obey_child);
|
|
</verb></tscreen>
|
|
|
|
<tt/xalign/ e <tt/yalign/ indican la alineación exactamente igual que
|
|
con los <em/widgets Alignment/. Si <tt/obey_child/ es TRUE, la
|
|
proporción de un <em/widget/ hijo será la misma que la proporción del
|
|
tamaño ideal que éste pida. En caso contrario, vendrá dada por
|
|
<tt/ratio/.
|
|
|
|
Para cambiar las opciones de un marco proporcional ya existente, puede
|
|
utilizar:
|
|
|
|
<tscreen><verb>
|
|
void gtk_aspect_frame_set( GtkAspectFrame *aspect_frame,
|
|
gfloat xalign,
|
|
gfloat yalign,
|
|
gfloat ratio,
|
|
gint obey_child);
|
|
</verb></tscreen>
|
|
|
|
Como por ejemplo, el siguiente programa utiliza un marco proporcional
|
|
para mostrar una zona de dibujo cuyas proporciones siempre será de
|
|
2:1, no importa como el usuario redimensione la ventana.
|
|
|
|
<tscreen><verb>
|
|
/* principio del ejemplo aspectframe aspectframe.c */
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
int
|
|
main (int argc, char *argv[])
|
|
{
|
|
GtkWidget *ventana;
|
|
GtkWidget *aspect_frame;
|
|
GtkWidget *drawing_area;
|
|
gtk_init (&argc, &argv);
|
|
|
|
ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
|
gtk_window_set_title (GTK_WINDOW (ventana), "Aspect Frame");
|
|
gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
|
|
GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
|
|
gtk_container_border_width (GTK_CONTAINER (ventana), 10);
|
|
|
|
/* Crear un aspect_frame y añadirlo a nuestra ventana superior */
|
|
|
|
aspect_frame = gtk_aspect_frame_new ("2x1", /* etiqueta */
|
|
0.5, /* centro x */
|
|
0.5, /* centro y */
|
|
2, /* tamañox/tamañoy = 2 */
|
|
FALSE /* ignorar el aspecto del hijo */);
|
|
|
|
gtk_container_add (GTK_CONTAINER(ventana), aspect_frame);
|
|
gtk_widget_show (aspect_frame);
|
|
|
|
/* Añadir un widget hijo al marco proporcional */
|
|
|
|
drawing_area = gtk_drawing_area_new ();
|
|
|
|
/* Pediremos una ventana de 200x200, pero el marco proporcional
|
|
* sólo no dejará una ventana de 200x100, ya que tenemos una
|
|
* relación de 2x1 */
|
|
gtk_widget_set_usize (drawing_area, 200, 200);
|
|
gtk_container_add (GTK_CONTAINER(aspect_frame), drawing_area);
|
|
gtk_widget_show (drawing_area);
|
|
|
|
gtk_widget_show (ventana);
|
|
gtk_main ();
|
|
return 0;
|
|
}
|
|
/* fin del ejemplo */
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
|
|
<sect1> El <em/widget/ ventana dividida (<em/Paned Window/)
|
|
<p>
|
|
El <em/widget/ ventana dividida es útil para cuando se quiere dividir
|
|
una zona en dos partes, con un tamaño relativo controlado por el
|
|
usuario. Entre las dos porciones de la ventana se dibuja un separador
|
|
con un botoncito que el usuario puede arrastrar para cambiar el tamaño
|
|
de cada zona. La división puede ser horizontal (HPaned) o vertical
|
|
(VPaned).
|
|
|
|
Para crear una nueva ventana dividida, utilice una de las siguientes
|
|
funciones:
|
|
|
|
<tscreen><verb>
|
|
GtkWidget *gtk_hpaned_new (void);
|
|
|
|
GtkWidget *gtk_vpaned_new (void);
|
|
</verb></tscreen>
|
|
|
|
Después de crear el <em/widget/ ventana dividida, tiene que añadirle
|
|
un <em/widget/ hijo a cada mitad. Para hacerlo, utilice:
|
|
|
|
<tscreen><verb>
|
|
void gtk_paned_add1 (GtkPaned *paned, GtkWidget *hijo);
|
|
|
|
void gtk_paned_add2 (GtkPaned *paned, GtkWidget *hijo);
|
|
</verb></tscreen>
|
|
|
|
<tt/gtk_paned_add1()/ añade el <em/widget/ hijo a la mitad que se
|
|
encuentra en la parte izquierda o superior de la ventana
|
|
dividida. <tt/gtk_paned_add2()/ añade el <em/widget/ a la mitad que
|
|
hay en la parte derecha o inferior de la ventana.
|
|
|
|
Por ejemplo, si queremos crear una parte del interface de usuario de
|
|
un programa de correo-e imaginario. Dividiremos verticalmente una
|
|
ventana en dos partes, teniendo en la parte superior una lista de los
|
|
mensajes de correo-e y en la parte inferior el texto de uno de estos
|
|
mensajes. El programa es bastante fácil de entender. Solo un par de
|
|
cosillas: no se puede añadir texto en un <em/widget/ de texto (Text)
|
|
si no se ha hecho antes <tt/gtk_widget_realize()/, pero como
|
|
demostración de una técnica alternativa, para añadir el texto
|
|
conectaremos un manipulador a la señal «realize». Y tenemos que
|
|
añadir la opción <tt/GTK_SHRINK/ a algunos de los elementos que hay en
|
|
la tabla con la ventana de texto y sus barras de desplazamiento, así
|
|
cuando la porción de abajo se haga más pequeña, se encogerá
|
|
correctamente en lugar de desaparecer por la parte de abajo de la
|
|
ventana.
|
|
|
|
<tscreen><verb>
|
|
/* principio del ejemplo paned paned.c */
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
/* Crear la lista de "messages" */
|
|
GtkWidget *
|
|
create_list (void)
|
|
{
|
|
|
|
GtkWidget *scrolled_window;
|
|
GtkWidget *list;
|
|
GtkWidget *list_item;
|
|
|
|
int i;
|
|
char buffer[16];
|
|
|
|
/* Crear una nueva ventana con barras de desplazamiento si hacen
|
|
falta */
|
|
scrolled_window = gtk_scrolled_window_new (NULL, NULL);
|
|
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
|
|
GTK_POLICY_AUTOMATIC,
|
|
GTK_POLICY_AUTOMATIC);
|
|
|
|
/* Crear una nueva lista y poner en ella la ventana con barras */
|
|
list = gtk_list_new ();
|
|
gtk_container_add (GTK_CONTAINER(scrolled_window), list);
|
|
gtk_widget_show (list);
|
|
|
|
/* Añadir algunos mensajes a la ventana */
|
|
for (i=0; i<10; i++) {
|
|
|
|
sprintf(buffer,"Message #%d",i);
|
|
list_item = gtk_list_item_new_with_label (buffer);
|
|
gtk_container_add (GTK_CONTAINER(list), list_item);
|
|
gtk_widget_show (list_item);
|
|
|
|
}
|
|
|
|
return scrolled_window;
|
|
}
|
|
|
|
/* Añadir algún texto a nuestro widget de texto - esta función se
|
|
invoca cada vez que se produce una señal realize en la
|
|
ventana. Podemos forzar esta señal mediante gtk_widget_realize, pero
|
|
primero tiene que formar parte de una jerarquía */
|
|
|
|
void
|
|
realize_text (GtkWidget *text, gpointer data)
|
|
{
|
|
gtk_text_freeze (GTK_TEXT (text));
|
|
gtk_text_insert (GTK_TEXT (text), NULL, &text->style->black, NULL,
|
|
"From: pathfinder@nasa.gov\n"
|
|
"To: mom@nasa.gov\n"
|
|
"Subject: Made it!\n"
|
|
"\n"
|
|
"We just got in this morning. The weather has been\n"
|
|
"great - clear but cold, and there are lots of fun sights.\n"
|
|
"Sojourner says hi. See you soon.\n"
|
|
" -Path\n", -1);
|
|
|
|
gtk_text_thaw (GTK_TEXT (text));
|
|
}
|
|
|
|
/* Creamos una zona con texto que muestra un "message" */
|
|
GtkWidget *
|
|
create_text (void)
|
|
{
|
|
GtkWidget *table;
|
|
GtkWidget *text;
|
|
GtkWidget *hscrollbar;
|
|
GtkWidget *vscrollbar;
|
|
|
|
/* Crea una tabla para contener el widget de texto y las barras de
|
|
desplazamiento */
|
|
table = gtk_table_new (2, 2, FALSE);
|
|
|
|
/* Pone un widget de texto en la esquina superior izquierda.
|
|
Observe la utilización de GTK_SHRINK en la dirección y */
|
|
text = gtk_text_new (NULL, NULL);
|
|
gtk_table_attach (GTK_TABLE (table), text, 0, 1, 0, 1,
|
|
GTK_FILL | GTK_EXPAND,
|
|
GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 0);
|
|
gtk_widget_show (text);
|
|
|
|
/* Pone una HScrollbar en la esquina inferior izquierda */
|
|
hscrollbar = gtk_hscrollbar_new (GTK_TEXT (text)->hadj);
|
|
gtk_table_attach (GTK_TABLE (table), hscrollbar, 0, 1, 1, 2,
|
|
GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
|
|
gtk_widget_show (hscrollbar);
|
|
|
|
/* Y una VScrollbar en la esquina superior derecha */
|
|
vscrollbar = gtk_vscrollbar_new (GTK_TEXT (text)->vadj);
|
|
gtk_table_attach (GTK_TABLE (table), vscrollbar, 1, 2, 0, 1,
|
|
GTK_FILL, GTK_EXPAND | GTK_FILL | GTK_SHRINK, 0, 0);
|
|
gtk_widget_show (vscrollbar);
|
|
|
|
/* Y un manejador para poner un mensaje en el widget de texto
|
|
cuando reciba realize */
|
|
gtk_signal_connect (GTK_OBJECT (text), "realize",
|
|
GTK_SIGNAL_FUNC (realize_text), NULL);
|
|
|
|
return table;
|
|
}
|
|
|
|
int
|
|
main (int argc, char *argv[])
|
|
{
|
|
GtkWidget *ventana;
|
|
GtkWidget *vpaned;
|
|
GtkWidget *list;
|
|
GtkWidget *text;
|
|
|
|
gtk_init (&argc, &argv);
|
|
|
|
ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
|
gtk_window_set_title (GTK_WINDOW (ventana), "Paned Windows");
|
|
gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
|
|
GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
|
|
gtk_container_border_width (GTK_CONTAINER (ventana), 10);
|
|
|
|
/* crea un widget vpaned y lo añade a nuestra ventana superior */
|
|
|
|
vpaned = gtk_vpaned_new ();
|
|
gtk_container_add (GTK_CONTAINER(ventana), vpaned);
|
|
gtk_widget_show (vpaned);
|
|
|
|
/* Ahora crea los contenidos de las dos mitades de la ventana */
|
|
|
|
list = create_list ();
|
|
gtk_paned_add1 (GTK_PANED(vpaned), list);
|
|
gtk_widget_show (list);
|
|
|
|
text = create_text ();
|
|
gtk_paned_add2 (GTK_PANED(vpaned), text);
|
|
gtk_widget_show (text);
|
|
gtk_widget_show (ventana);
|
|
gtk_main ();
|
|
return 0;
|
|
}
|
|
/* fin del ejemplo */
|
|
</verb></tscreen>
|
|
|
|
<!-- XXX -->
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1> <em/Viewports/ <label id="sec_Viewports">
|
|
<p>
|
|
Probablemente nunca le llegue a hacer falta utilizar el <em/widget/
|
|
Viewport directamente. Será mucho más probable que tenga que utilizar
|
|
el <em/widget/ <ref id="sec_ScrolledWindows" name="Ventanas con barras
|
|
de desplazamiento"> que a su vez hace uso de <em/viewport/.
|
|
|
|
Un <em/widget viewport/ le permite meter dentro un gran <em/widget/,
|
|
de forma que sólo verá una parte del mismo. Utiliza
|
|
<ref id="sec_Adjustment" name="ajustes"> para definir la zona que se
|
|
está viendo actualmente.
|
|
|
|
Para crear un <em/viewport/ hay que utilizar la función:
|
|
|
|
<tscreen><verb>
|
|
GtkWidget *gtk_viewport_new( GtkAdjustment *hadjustment,
|
|
GtkAdjustment *vadjustment );
|
|
</verb></tscreen>
|
|
|
|
Como puede observar, se pueden especificar los ajustes horizontal y
|
|
vertical que el <em/widget/ va a utilizar en el mismo momento de su
|
|
creación. El <em/widget/ creará sus propios ajustes en caso de que
|
|
reciba como argumento un valor NULL.
|
|
|
|
Puede obtener y establecer los ajustes después de que se haya
|
|
creado el <em/widget/ utilizado las cuatro funciones siguientes:
|
|
|
|
<tscreen><verb>
|
|
GtkAdjustment *gtk_viewport_get_hadjustment (GtkViewport *viewport );
|
|
|
|
GtkAdjustment *gtk_viewport_get_vadjustment (GtkViewport *viewport );
|
|
|
|
void gtk_viewport_set_hadjustment( GtkViewport *viewport,
|
|
GtkAdjustment *adjustment );
|
|
|
|
void gtk_viewport_set_vadjustment( GtkViewport *viewport,
|
|
GtkAdjustment *adjustment );
|
|
</verb></tscreen>
|
|
|
|
La única función relativa al <em/viewport/ que queda que altera su
|
|
apariencia es:
|
|
|
|
<tscreen><verb>
|
|
void gtk_viewport_set_shadow_type( GtkViewport *viewport,
|
|
GtkShadowType type );
|
|
</verb></tscreen>
|
|
|
|
Los valores posibles para el argumento <tt/type/ son:
|
|
<itemize>
|
|
<item> GTK_SHADOW_NONE,
|
|
<item> GTK_SHADOW_IN,
|
|
<item> GTK_SHADOW_OUT,
|
|
<item> GTK_SHADOW_ETCHED_IN,
|
|
<item> GTK_SHADOW_ETCHED_OUT
|
|
</itemize>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Ventanas con barras de desplazamiento <label id="sec_ScrolledWindows">
|
|
<p>
|
|
|
|
Las ventanas con barras de desplazamiento se utilizan para crear una zona
|
|
con barras de desplazamiento dentro de una ventana real. Puede insertar
|
|
cualquier tipo de <em/widget/ en una ventana con barras de
|
|
desplazamiento, y podrá utilizarlo sin importar su tamaño gracias a
|
|
las barras de desplazamiento.
|
|
|
|
La función siguiente se utiliza para crear una nueva ventana con
|
|
barras de desplazamiento.
|
|
|
|
<tscreen><verb>
|
|
GtkWidget *gtk_scrolled_window_new( GtkAdjustment *hadjustment,
|
|
GtkAdjustment *vadjustment );
|
|
</verb></tscreen>
|
|
|
|
Donde el primer argumento es el ajuste para la dirección horizontal, y
|
|
el segundo es el ajuste para la dirección vertical. Casi siempre valen
|
|
NULL.
|
|
|
|
<tscreen><verb>
|
|
void gtk_scrolled_window_set_policy( GtkScrolledWindow *scrolled_window,
|
|
GtkPolicyType hscrollbar_policy,
|
|
GtkPolicyType vscrollbar_policy );
|
|
</verb></tscreen>
|
|
|
|
Esta función establece la política que se utilizará con respecto a las
|
|
barras de desplazamiento. El primer argumento es la ventana con barras
|
|
de desplazamiento sobre la que queremos actuar. El segundo establece
|
|
la política para la barra de desplazamiento horizontal, y el tercero
|
|
la política para la barra de desplazamiento vertical.
|
|
|
|
La política puede ser GTK_POLICY_AUTOMATIC, o
|
|
GTK_POLICY_ALWAYS. GTK_POLICY_AUTOMATIC decidirá automáticamente si
|
|
necesita barras de desplazamiento, mientras que GTK_POLICY_ALWAYS pondrá
|
|
siempre las barras de desplazamiento.
|
|
|
|
Aquí tenemos un ejemplo sencillo que empaqueta 100 botones de
|
|
selección en una ventana con barras de desplazamiento. Solamente he
|
|
comentado las partes que debería ser nuevas para usted.
|
|
|
|
<tscreen><verb>
|
|
/* principio del ejemplo scrolledwin scrolledwin.c */
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
void destroy(GtkWidget *widget, gpointer data)
|
|
{
|
|
gtk_main_quit();
|
|
}
|
|
|
|
int main (int argc, char *argv[])
|
|
{
|
|
static GtkWidget *ventana;
|
|
GtkWidget *scrolled_window;
|
|
GtkWidget *table;
|
|
GtkWidget *boton;
|
|
char buffer[32];
|
|
int i, j;
|
|
|
|
gtk_init (&argc, &argv);
|
|
|
|
/* Crea un nuevo cuadro de diálogo para que la ventana con barras de
|
|
* desplazamiento se meta dentro. Un cuadro de diálogo es como una
|
|
* ventana normal excepto que tiene dentro una vbox y un separador
|
|
* horizontal. Es sólo un atajo para crear cuadros de diálogo */
|
|
ventana = gtk_dialog_new ();
|
|
gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
|
|
(GtkSignalFunc) destroy, NULL);
|
|
gtk_window_set_title (GTK_WINDOW (ventana), "dialog");
|
|
gtk_container_border_width (GTK_CONTAINER (ventana), 0);
|
|
gtk_widget_set_usize(ventana, 300, 300);
|
|
|
|
/* crea una nueva ventana con barras de desplazamiento. */
|
|
scrolled_window = gtk_scrolled_window_new (NULL, NULL);
|
|
|
|
gtk_container_border_width (GTK_CONTAINER (scrolled_window), 10);
|
|
|
|
/* la política es GTK_POLICY_AUTOMATIC, o GTK_POLICY_ALWAYS.
|
|
* GTK_POLICY_AUTOMATIC decidirá automáticamente si necesita
|
|
* barras de desplazamiento, mientras que GTK_POLICY_ALWAYS pondrá
|
|
* siempre las barras de desplazamiento. El primer argumento se
|
|
* refiere a la barra horizontal, el segundo a la vertical. */
|
|
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
|
|
GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
|
|
/* El cuadro de diálogo se crea con una vbox dentro de él. */
|
|
gtk_box_pack_start (GTK_BOX (GTK_DIALOG(ventana)->vbox), scrolled_window,
|
|
TRUE, TRUE, 0);
|
|
gtk_widget_show (scrolled_window);
|
|
|
|
/* crea una tabla de 10 por 10 casillas. */
|
|
table = gtk_table_new (10, 10, FALSE);
|
|
|
|
/* pone el espacio en x y en y a 10 */
|
|
gtk_table_set_row_spacings (GTK_TABLE (table), 10);
|
|
gtk_table_set_col_spacings (GTK_TABLE (table), 10);
|
|
|
|
/* empaqueta la tabla en la ventana con barras de desplazamiento */
|
|
gtk_container_add (GTK_CONTAINER (scrolled_window), table);
|
|
gtk_widget_show (table);
|
|
|
|
/* crea una rejilla de botones de selección en la tabla para
|
|
* demostrar la ventana con barras de desplazamiento. */
|
|
for (i = 0; i < 10; i++)
|
|
for (j = 0; j < 10; j++) {
|
|
sprintf (buffer, "botón (%d,%d)\n", i, j);
|
|
boton = gtk_toggle_button_new_with_label (buffer);
|
|
gtk_table_attach_defaults (GTK_TABLE (table), boton,
|
|
i, i+1, j, j+1);
|
|
gtk_widget_show (boton);
|
|
}
|
|
|
|
/* Añade un botón "close" en la parte de abajo del cuadro de
|
|
* diálogo */
|
|
boton = gtk_button_new_with_label ("close");
|
|
gtk_signal_connect_object (GTK_OBJECT (boton), "clicked",
|
|
(GtkSignalFunc) gtk_widget_destroy,
|
|
GTK_OBJECT (ventana));
|
|
|
|
/* hace que el botón puede ser elegido por defecto. */
|
|
|
|
GTK_WIDGET_SET_FLAGS (boton, GTK_CAN_DEFAULT);
|
|
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (ventana)->action_area), boton, TRUE, TRUE, 0);
|
|
|
|
/* Hace que el botón sea el elegido por defecto. Con pulsar la
|
|
* tecla "Enter" se activará este botón. */
|
|
gtk_widget_grab_default (boton);
|
|
gtk_widget_show (boton);
|
|
|
|
gtk_widget_show (ventana);
|
|
|
|
gtk_main();
|
|
|
|
return(0);
|
|
}
|
|
/* fin del ejemplo */
|
|
</verb></tscreen>
|
|
|
|
Juegue un poco redimensionando la ventana. Vea como actuan las barras
|
|
de desplazamiento. También puede utilizar la función
|
|
<tt/gtk_widget_set_usize()/ para poner el tamaño por defecto de la
|
|
ventana o de cualquier otro <em/widget/.
|
|
|
|
<!-- XXX -->
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Cajas de botones
|
|
<p>
|
|
Las cajas de botones son útiles para crear grupos de botones. Hay
|
|
cajas horizontales y verticales. Puede crear una nueva caja de botones
|
|
utilizando alguna de las funciones siguientes, que crean
|
|
respectivamente una caja horizontal y otra vertical:
|
|
|
|
<tscreen><verb>
|
|
GtkWidget *gtk_hbutton_box_new( void );
|
|
|
|
GtkWidget *gtk_vbutton_box_new( void );
|
|
</verb></tscreen>
|
|
|
|
Los únicos atributos pertenecientes a las cajas de botones son los
|
|
que definen como se distribuyen los botones. Puede cambiar el
|
|
espaciado que hay entre los botones con:
|
|
|
|
<tscreen><verb>
|
|
void gtk_hbutton_box_set_spacing_default( gint spacing );
|
|
|
|
void gtk_vbutton_box_set_spacing_default( gint spacing );
|
|
</verb></tscreen>
|
|
|
|
Igualmente, se pueden obtener los actuales valores para el espaciado
|
|
utilizando:
|
|
|
|
<tscreen><verb>
|
|
gint gtk_hbutton_box_get_spacing_default( void );
|
|
|
|
gint gtk_vbutton_box_get_spacing_default( void );
|
|
</verb></tscreen>
|
|
|
|
El segundo atributo al que podemos acceder afecta al esquema de los
|
|
botones dentro de la caja. Se establece utilizando:
|
|
|
|
<tscreen><verb>
|
|
void gtk_hbutton_box_set_layout_default( GtkButtonBoxStyle layout );
|
|
|
|
void gtk_vbutton_box_set_layout_default( GtkButtonBoxStyle layout );
|
|
</verb></tscreen>
|
|
|
|
El argumento <tt/layout/ puede tomar uno de los siguientes valores:
|
|
|
|
<itemize>
|
|
<item> GTK_BUTTONBOX_DEFAULT_STYLE
|
|
<item> GTK_BUTTONBOX_SPREAD
|
|
<item> GTK_BUTTONBOX_EDGE
|
|
<item> GTK_BUTTONBOX_START
|
|
<item> GTK_BUTTONBOX_END
|
|
</itemize>
|
|
|
|
Puede obtenerse el esquema actual utilizando:
|
|
|
|
<tscreen><verb>
|
|
GtkButtonBoxStyle gtk_hbutton_box_get_layout_default( void );
|
|
|
|
GtkButtonBoxStyle gtk_vbutton_box_get_layout_default( void );
|
|
</verb></tscreen>
|
|
|
|
Podemos añadir botones a una caja de botones utilizando (como
|
|
siempre) la función:
|
|
|
|
<tscreen><verb>
|
|
gtk_container_add( GTK_CONTAINER(button_box), child_widget );
|
|
</verb></tscreen>
|
|
|
|
Aquí hay un ejemplo que ilustra todos los diferentes esquemas que
|
|
podemos utilizar con las cajas de botones.
|
|
|
|
<tscreen><verb>
|
|
/* principio del ejemplo buttonbox buttonbox.c */
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
/* Crear una Caja de Botones con los parámetros
|
|
* especificados */
|
|
GtkWidget *create_bbox (gint horizontal,
|
|
char* title,
|
|
gint spacing,
|
|
gint child_w,
|
|
gint child_h,
|
|
gint layout)
|
|
{
|
|
GtkWidget *frame;
|
|
GtkWidget *bbox;
|
|
GtkWidget *boton;
|
|
|
|
frame = gtk_frame_new (title);
|
|
|
|
if (horizontal)
|
|
bbox = gtk_hbutton_box_new ();
|
|
else
|
|
bbox = gtk_vbutton_box_new ();
|
|
|
|
gtk_container_set_border_width (GTK_CONTAINER (bbox), 5);
|
|
gtk_container_add (GTK_CONTAINER (frame), bbox);
|
|
|
|
/* Establece la apariencia de la Caja de Botones */
|
|
gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), layout);
|
|
gtk_button_box_set_spacing (GTK_BUTTON_BOX (bbox), spacing);
|
|
gtk_button_box_set_child_size (GTK_BUTTON_BOX (bbox), child_w, child_h);
|
|
|
|
boton = gtk_button_new_with_label ("OK");
|
|
gtk_container_add (GTK_CONTAINER (bbox), boton);
|
|
|
|
boton = gtk_button_new_with_label ("Cancel");
|
|
gtk_container_add (GTK_CONTAINER (bbox), boton);
|
|
|
|
boton = gtk_button_new_with_label ("Help");
|
|
gtk_container_add (GTK_CONTAINER (bbox), boton);
|
|
|
|
return(frame);
|
|
}
|
|
|
|
int main( int argc,
|
|
char *argv[] )
|
|
{
|
|
static GtkWidget* ventana = NULL;
|
|
GtkWidget *main_vbox;
|
|
GtkWidget *vbox;
|
|
GtkWidget *hbox;
|
|
GtkWidget *frame_horz;
|
|
GtkWidget *frame_vert;
|
|
|
|
/* Inicializa GTK */
|
|
gtk_init( &argc, &argv );
|
|
|
|
ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
|
gtk_window_set_title (GTK_WINDOW (ventana), "Button Boxes");
|
|
|
|
gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
|
|
GTK_SIGNAL_FUNC(gtk_main_quit),
|
|
NULL);
|
|
|
|
gtk_container_set_border_width (GTK_CONTAINER (ventana), 10);
|
|
|
|
main_vbox = gtk_vbox_new (FALSE, 0);
|
|
gtk_container_add (GTK_CONTAINER (ventana), main_vbox);
|
|
|
|
frame_horz = gtk_frame_new ("Horizontal Button Boxes");
|
|
gtk_box_pack_start (GTK_BOX (main_vbox), frame_horz, TRUE, TRUE, 10);
|
|
|
|
vbox = gtk_vbox_new (FALSE, 0);
|
|
gtk_container_set_border_width (GTK_CONTAINER (vbox), 10);
|
|
gtk_container_add (GTK_CONTAINER (frame_horz), vbox);
|
|
|
|
gtk_box_pack_start (GTK_BOX (vbox),
|
|
create_bbox (TRUE, "Spread (spacing 40)", 40, 85, 20, GTK_BUTTONBOX_SPREAD),
|
|
TRUE, TRUE, 0);
|
|
|
|
gtk_box_pack_start (GTK_BOX (vbox),
|
|
create_bbox (TRUE, "Edge (spacing 30)", 30, 85, 20, GTK_BUTTONBOX_EDGE),
|
|
TRUE, TRUE, 5);
|
|
|
|
gtk_box_pack_start (GTK_BOX (vbox),
|
|
create_bbox (TRUE, "Start (spacing 20)", 20, 85, 20, GTK_BUTTONBOX_START),
|
|
TRUE, TRUE, 5);
|
|
|
|
gtk_box_pack_start (GTK_BOX (vbox),
|
|
create_bbox (TRUE, "End (spacing 10)", 10, 85, 20, GTK_BUTTONBOX_END),
|
|
TRUE, TRUE, 5);
|
|
|
|
frame_vert = gtk_frame_new ("Vertical Button Boxes");
|
|
gtk_box_pack_start (GTK_BOX (main_vbox), frame_vert, TRUE, TRUE, 10);
|
|
|
|
hbox = gtk_hbox_new (FALSE, 0);
|
|
gtk_container_set_border_width (GTK_CONTAINER (hbox), 10);
|
|
gtk_container_add (GTK_CONTAINER (frame_vert), hbox);
|
|
|
|
gtk_box_pack_start (GTK_BOX (hbox),
|
|
create_bbox (FALSE, "Spread (spacing 5)", 5, 85, 20, GTK_BUTTONBOX_SPREAD),
|
|
TRUE, TRUE, 0);
|
|
|
|
gtk_box_pack_start (GTK_BOX (hbox),
|
|
create_bbox (FALSE, "Edge (spacing 30)", 30, 85, 20, GTK_BUTTONBOX_EDGE),
|
|
TRUE, TRUE, 5);
|
|
|
|
gtk_box_pack_start (GTK_BOX (hbox),
|
|
create_bbox (FALSE, "Start (spacing 20)", 20, 85, 20, GTK_BUTTONBOX_START),
|
|
TRUE, TRUE, 5);
|
|
|
|
gtk_box_pack_start (GTK_BOX (hbox),
|
|
create_bbox (FALSE, "End (spacing 20)", 20, 85, 20, GTK_BUTTONBOX_END),
|
|
TRUE, TRUE, 5);
|
|
|
|
gtk_widget_show_all (ventana);
|
|
|
|
/* Entra dentro del bucle de eventos */
|
|
gtk_main ();
|
|
|
|
return(0);
|
|
}
|
|
/* fin del ejemplo */
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Barras de herramientas
|
|
<p>
|
|
Las barras de herramientas acostumbran a agrupar un conjunto de
|
|
<em>widgets</em> para hacer más sencilla la personalización
|
|
de su aspecto y composición. Típicamente una barra de herramientas
|
|
consiste en botones con iconos, etiquetas y <em/tips/ para los iconos
|
|
(pequeño texto descriptivo que aparece cuando se mantiene el ratón
|
|
sobre el icono), pero en realidad en una barra se puede poner
|
|
cualquier tipo de <em>widget</em>. Finalmente, los elementos se pueden
|
|
disponer de forma horizontal o vertical, y los botones pueden mostrar
|
|
iconos, etiquetas o ambos.
|
|
|
|
La creación de una barra de herramientas se hace (como puede que ya
|
|
haya sospechado) mediante la función siguiente:
|
|
|
|
<tscreen><verb>
|
|
GtkWidget *gtk_toolbar_new( GtkOrientation orientation,
|
|
GtkToolbarStyle style );
|
|
</verb></tscreen>
|
|
|
|
donde <tt/orientation/ puede ser:
|
|
|
|
<tscreen><verb>
|
|
GTK_ORIENTATION_HORIZONTAL
|
|
GTK_ORIENTATION_VERTICAL
|
|
</verb></tscreen>
|
|
|
|
y <tt/style/:
|
|
|
|
<tscreen><verb>
|
|
GTK_TOOLBAR_TEXT
|
|
GTK_TOOLBAR_ICONS
|
|
GTK_TOOLBAR_BOTH
|
|
</verb></tscreen>
|
|
|
|
La variable <tt/style/ se aplica a todos los botones que se crean con las
|
|
funciones `item' (pero no a los botones insertados en la barra de
|
|
herramientas como <em>widgets</em> separados).
|
|
|
|
Después de crear una barra de herramientas, se pueden añadir,
|
|
preañadir e insertar elementos (o sea, botones) en la misma. Los
|
|
campos que describen un elemento son el texto de la etiqueta, el texto
|
|
del <em/tip/, un texto para el <em/tip/ privado, un icono para el
|
|
botón y una función de llamada para el mismo. Por ejemplo, para añadir
|
|
un elemento puede utilizar la siguiente función:
|
|
|
|
<tscreen><verb>
|
|
GtkWidget *gtk_toolbar_append_item( GtkToolbar *toolbar,
|
|
const char *text,
|
|
const char *tooltip_text,
|
|
const char *tooltip_private_text,
|
|
GtkWidget *icon,
|
|
GtkSignalFunc callback,
|
|
gpointer user_data );
|
|
</verb></tscreen>
|
|
|
|
Si quiere utilizar <tt/gtk_toolbar_insert_item/, el único parámetro
|
|
adicional que debería especificar es la posición en la que quiere que
|
|
se introduzca el elemento.
|
|
|
|
Para añadir un espacio en blanco entre los elementos de la barra de
|
|
herramientas, puede utilizar la función siguiente:
|
|
|
|
<tscreen><verb>
|
|
void gtk_toolbar_append_space( GtkToolbar *toolbar );
|
|
|
|
void gtk_toolbar_prepend_space( GtkToolbar *toolbar );
|
|
|
|
void gtk_toolbar_insert_space( GtkToolbar *toolbar,
|
|
gint posicion );
|
|
|
|
</verb></tscreen>
|
|
|
|
Y el tamaño del espacio en blanco puede establecerse globalmente
|
|
para toda una barra de herramientas con la función:
|
|
|
|
<tscreen><verb>
|
|
void gtk_toolbar_set_space_size( GtkToolbar *toolbar,
|
|
gint space_size) ;
|
|
</verb></tscreen>
|
|
|
|
Si tiene que establecer la orientación de una barra de herramientas y
|
|
su estilo, puede hacerlo `al vuelo' con las funciones siguientes:
|
|
|
|
<tscreen><verb>
|
|
void gtk_toolbar_set_orientation( GtkToolbar *toolbar,
|
|
GtkOrientation orientation );
|
|
|
|
void gtk_toolbar_set_style( GtkToolbar *toolbar,
|
|
GtkToolbarStyle style );
|
|
|
|
void gtk_toolbar_set_tooltips( GtkToolbar *toolbar,
|
|
gint enable );
|
|
</verb></tscreen>
|
|
|
|
Para mostrar algunas otras cosas que pueden hacerse con una barra de
|
|
herramientas, vamos a ver el siguiente programa (interrumpiremos el
|
|
listado con alguna explicación adicional):
|
|
|
|
<tscreen><verb>
|
|
#include <gtk/gtk.h>
|
|
|
|
#include "gtk.xpm"
|
|
|
|
/* Esta función está conectada al botón Close o a la acción de cerrar
|
|
* la ventana desde el WM */
|
|
void delete_event (GtkWidget *widget, GdkEvent *event, gpointer data)
|
|
{
|
|
gtk_main_quit ();
|
|
}
|
|
</verb></tscreen>
|
|
|
|
Este principio ya debería de sonarle familiar, a no ser que éste sea
|
|
su primer programa GTK. En nuestro programa no habrá ninguna novedad,
|
|
salvo un bonito dibujo XPM que utilizaremos como icono para todos los
|
|
botones.
|
|
|
|
<tscreen><verb>
|
|
GtkWidget* close_button; // este botón emitirá la señal de cerrar el programa
|
|
GtkWidget* tooltips_button; // para activar/desactivar los tooltips
|
|
GtkWidget* text_button,
|
|
* icon_button,
|
|
* both_button; // botones circulares para el estilo de la barra
|
|
GtkWidget* entry; // un widget para meter texto para mostrar como
|
|
// empaquetar widgets en la barra de herramientas
|
|
</verb></tscreen>
|
|
|
|
En realidad no necesitamos todos los <em>widgets</em> que acabo de
|
|
poner, pero para aclarar las cosas un poco más los he puesto todos.
|
|
|
|
<tscreen><verb>
|
|
/* Esto es fácil... cuando uno de los botones cambia, sólo
|
|
* tenemos que comprobar quien está activo y hacer que el estilo
|
|
* de la barra de herramientas esté acorde con la elección
|
|
* ATENCIÓN: ¡nuestra barra de herramientas es data !
|
|
void radio_event (GtkWidget *widget, gpointer data)
|
|
{
|
|
if (GTK_TOGGLE_BUTTON (text_button)->active)
|
|
gtk_toolbar_set_style(GTK_TOOLBAR ( data ), GTK_TOOLBAR_TEXT);
|
|
else if (GTK_TOGGLE_BUTTON (icon_button)->active)
|
|
gtk_toolbar_set_style(GTK_TOOLBAR ( data ), GTK_TOOLBAR_ICONS);
|
|
else if (GTK_TOGGLE_BUTTON (both_button)->active)
|
|
gtk_toolbar_set_style(GTK_TOOLBAR ( data ), GTK_TOOLBAR_BOTH);
|
|
}
|
|
|
|
/* todavía más fácil, sólo hay que comprobar el botón de selección
|
|
* y activar/desactivar los tooltips */
|
|
void toggle_event (GtkWidget *widget, gpointer data)
|
|
{
|
|
gtk_toolbar_set_tooltips (GTK_TOOLBAR ( data ),
|
|
GTK_TOGGLE_BUTTON (widget)->active );
|
|
}
|
|
</verb></tscreen>
|
|
|
|
Lo de arriba son sólo dos funciones de llamada que se invocarán cuando
|
|
se presione uno de los botones de la barra de herramientas. Todo esto
|
|
ya debería resultarle familiar si ha utilizado alguna vez los botones
|
|
de selección (o los botones circulares)
|
|
|
|
<tscreen><verb>
|
|
int main (int argc, char *argv[])
|
|
{
|
|
/* Aquí está nuestra ventana principal (un cuadro de diálogo) y una
|
|
* caja flotante */
|
|
GtkWidget* dialog;
|
|
GtkWidget* handlebox;
|
|
|
|
/* De acuerdo, necesitamos una barra de herramientas, un icono con
|
|
* una máscara (una para todos los botones) y un widget icono donde
|
|
* meter el icono (crearemos un widget diferente para cada botón) */
|
|
GtkWidget * toolbar;
|
|
GdkPixmap * icon;
|
|
GdkBitmap * mask;
|
|
GtkWidget * iconw;
|
|
|
|
/* a esta función se le llama en todas las aplicación GTK */
|
|
gtk_init (&argc, &argv);
|
|
|
|
/* crear una ventana nueva con un título y el tamaño adecuado */
|
|
dialog = gtk_dialog_new ();
|
|
gtk_window_set_title ( GTK_WINDOW ( dialog ) , "GTKToolbar Tutorial");
|
|
gtk_widget_set_usize( GTK_WIDGET ( dialog ) , 600 , 300 );
|
|
GTK_WINDOW ( dialog ) ->allow_shrink = TRUE;
|
|
|
|
/* salimos si alguien intenta cerrarnos */
|
|
gtk_signal_connect ( GTK_OBJECT ( dialog ), "delete_event",
|
|
GTK_SIGNAL_FUNC ( delete_event ), NULL);
|
|
|
|
/* tenemos que mandar la señalo realize porque utilizamos pixmaps
|
|
* para los elementos que hay en la barra de herramientas */
|
|
gtk_widget_realize ( dialog );
|
|
|
|
/* para hacerlo más bonito ponemos la barra de herramientas en la
|
|
* caja flotante, para que así se pueda desatar de la ventana
|
|
* principal */
|
|
handlebox = gtk_handle_box_new ();
|
|
gtk_box_pack_start ( GTK_BOX ( GTK_DIALOG(dialog)->vbox ),
|
|
handlebox, FALSE, FALSE, 5 );
|
|
</verb></tscreen>
|
|
|
|
Lo de arriba debería ser parecido en cualquier aplicación GTK. Sólo
|
|
está la inicialización de GTK, la creación de la ventana, etc...
|
|
Solamente hay una cosa que probablemente necesite una explicación:
|
|
una barra de herramientas flotante. Una barra de herramientas flotante
|
|
sólo es otra barra donde pueden empaquetarse <em>widgets</em>. La
|
|
diferencia que tiene con una barra típica es que puede desatarse de la
|
|
ventana padre (o, de hecho, la barra de herramientas flotante permanece
|
|
en el padre, pero reducida a un rectángulo muy pequeño, mientras que
|
|
todos sus contenidos se pasan a una nueva ventana flotante). Es bonito
|
|
tener una barra de herramientas flotante, por lo que estos dos
|
|
<em>widgets</em> suelen aparecer juntos.
|
|
|
|
<tscreen><verb>
|
|
/* la barra de herramientas será horizontal, con iconos y texto, y
|
|
* con un espacio de 5pxl entre elementos y finalmente, la ponemos en
|
|
* nuestra caja flotante */
|
|
toolbar = gtk_toolbar_new ( GTK_ORIENTATION_HORIZONTAL,
|
|
GTK_TOOLBAR_BOTH );
|
|
gtk_container_border_width ( GTK_CONTAINER ( toolbar ) , 5 );
|
|
gtk_toolbar_set_space_size ( GTK_TOOLBAR ( toolbar ), 5 );
|
|
gtk_container_add ( GTK_CONTAINER ( handlebox ) , toolbar );
|
|
|
|
/* ahora creamos el icono con la máscara: utilizaremos el widget
|
|
* icon con todos los elementos de la barra de herramientas */
|
|
icon = gdk_pixmap_create_from_xpm_d ( dialog->window, &mask,
|
|
&dialog->style->white, gtk_xpm );
|
|
</verb></tscreen>
|
|
|
|
Bien, lo que acabamos de escribir es la inicialización del
|
|
<em>widget</em> de la barra de herramientas y la creación de un
|
|
<em>pixmap</em> GDK con su máscara. Si quiere saber algo más sobre la
|
|
utilización de <em>pixmaps</em>, vea la documentación de GDK o la
|
|
sección <ref id="sec_Pixmaps" name="Pixmaps"> en este tutorial.
|
|
|
|
<tscreen><verb>
|
|
/* nuestro primer elemento es el botón <close> */
|
|
iconw = gtk_pixmap_new ( icon, mask ); // icon widget
|
|
close_button =
|
|
gtk_toolbar_append_item ( GTK_TOOLBAR (toolbar), // nuestra barra
|
|
"Close", // etiqueta del botón
|
|
"Closes this app", // tooltip para el botón
|
|
"Private", // cadena privada del tooltip
|
|
iconw, // widget del icono
|
|
GTK_SIGNAL_FUNC (delete_event), // una señal
|
|
NULL );
|
|
gtk_toolbar_append_space ( GTK_TOOLBAR ( toolbar ) ); // espacio después del elemento
|
|
</verb></tscreen>
|
|
|
|
En el trozo de código de arriba puede ver como se hace la acción más
|
|
simple: añadir un botón a la barra de herramientas. Justo antes de
|
|
añadir un nuevo elemento, tenemos que construir un <em>widget
|
|
pixmap</em> para que sirva como icono para este elemento; este paso
|
|
tendrá que repetirse para cada nuevo elemento. Después del elemento
|
|
añadiremos un espacio en blanco en la barra de herramientas, para que
|
|
los elementos que añadamos a continuación no se toquen los unos a los
|
|
otros. Como puede ver, <tt/gtk_toolbar_append_item/ devuelve un
|
|
puntero al <em>widget</em> de nuestro nuevo botón recien creado, por
|
|
lo que podremos trabajar con él como siempre.
|
|
|
|
<tscreen><verb>
|
|
/* ahora, vamos a hacer nuestro grupo de botones circulares... */
|
|
iconw = gtk_pixmap_new ( icon, mask );
|
|
icon_button =
|
|
gtk_toolbar_append_element(GTK_TOOLBAR(toolbar),
|
|
GTK_TOOLBAR_CHILD_RADIOBUTTON, // un tipo de elemento
|
|
NULL, // puntero al widget
|
|
"Icon", // etiqueta
|
|
"Only icons in toolbar", // tooltip
|
|
"Private", // cadena privada del tooltip
|
|
iconw, // icono
|
|
GTK_SIGNAL_FUNC (radio_event), // señal
|
|
toolbar); // dato para la señal
|
|
gtk_toolbar_append_space ( GTK_TOOLBAR ( toolbar ) );
|
|
</verb></tscreen>
|
|
|
|
Aquí empezamos creando un grupo de botones circulares. Para hacerlo
|
|
hemos utilizado <tt/gtk_toolbar_append_element/. De hecho, utilizando
|
|
esta función se pueden añadir tanto elementos simples como espacios en
|
|
blanco (tipo = GTK_TOOLBAR_CHILD_SPACE o GTK_TOOLBAR_CHILD_BUTTON). En
|
|
el caso de arriba, hemos empezado creando un grupo de botones circulares.
|
|
Para crear más botones circulares para este grupo
|
|
necesitaremos un puntero al botón anterior del grupo, mediante el que
|
|
podremos construir fácilmente una lista de botones (ver la sección
|
|
<ref id="sec_Radio_Buttons" name="Botones circulares"> que se encuentra
|
|
más adelante en este tutorial).
|
|
|
|
<tscreen><verb>
|
|
/* los botones circulares que vienen a continuación están
|
|
relacionados con los anteriores */
|
|
iconw = gtk_pixmap_new ( icon, mask );
|
|
text_button =
|
|
gtk_toolbar_append_element(GTK_TOOLBAR(toolbar),
|
|
GTK_TOOLBAR_CHILD_RADIOBUTTON,
|
|
icon_button,
|
|
"Text",
|
|
"Only texts in toolbar",
|
|
"Private",
|
|
iconw,
|
|
GTK_SIGNAL_FUNC (radio_event),
|
|
toolbar);
|
|
gtk_toolbar_append_space ( GTK_TOOLBAR ( toolbar ) );
|
|
|
|
iconw = gtk_pixmap_new ( icon, mask );
|
|
both_button =
|
|
gtk_toolbar_append_element(GTK_TOOLBAR(toolbar),
|
|
GTK_TOOLBAR_CHILD_RADIOBUTTON,
|
|
text_button,
|
|
"Both",
|
|
"Icons and text in toolbar",
|
|
"Private",
|
|
iconw,
|
|
GTK_SIGNAL_FUNC (radio_event),
|
|
toolbar);
|
|
gtk_toolbar_append_space ( GTK_TOOLBAR ( toolbar ) );
|
|
gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(both_button),TRUE);
|
|
</verb></tscreen>
|
|
|
|
Al final hemos activado manualmente uno de los botones (en caso
|
|
contrario los botones permanecerían todos en estado activo,
|
|
impidiéndonos poder cambiar de uno a otro).
|
|
|
|
<tscreen><verb>
|
|
/* aquí tenemos un sencillo botón de selección */
|
|
iconw = gtk_pixmap_new ( icon, mask );
|
|
tooltips_button =
|
|
gtk_toolbar_append_element(GTK_TOOLBAR(toolbar),
|
|
GTK_TOOLBAR_CHILD_TOGGLEBUTTON,
|
|
NULL,
|
|
"Tooltips",
|
|
"Toolbar with or without tips",
|
|
"Private",
|
|
iconw,
|
|
GTK_SIGNAL_FUNC (toggle_event),
|
|
toolbar);
|
|
gtk_toolbar_append_space ( GTK_TOOLBAR ( toolbar ) );
|
|
gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(tooltips_button),TRUE);
|
|
</verb></tscreen>
|
|
|
|
Un botón de selección puede crearse de una forma obvia (si ya sabe como
|
|
crear botones circulares).
|
|
|
|
<tscreen><verb>
|
|
/* para empaquetar un widget en la barra de herramientas, sólo
|
|
* tenemos que crearlo y añadirlo en la barra con el tooltip
|
|
* apropiado */
|
|
entry = gtk_entry_new ();
|
|
gtk_toolbar_append_widget( GTK_TOOLBAR (toolbar),
|
|
entry,
|
|
"This is just an entry",
|
|
"Private" );
|
|
|
|
/* bien, no se ha creado con la barra, así que debemos mostrarlo
|
|
* explicitamente */
|
|
gtk_widget_show ( entry );
|
|
</verb></tscreen>
|
|
|
|
Como puede ver, añadir cualquier tipo de <em>widget</em> a la barra
|
|
de herramientas es fácil. Lo único que debe recordar es que este
|
|
<em>widget</em> debe mostrarse manualmente (al contrario que los demás
|
|
elementos que se mostrarán junto con la barra de herramientas).
|
|
|
|
<tscreen><verb>
|
|
/* ¡ Eso es ! mostremos algo. */
|
|
gtk_widget_show ( toolbar );
|
|
gtk_widget_show (handlebox);
|
|
gtk_widget_show ( dialog );
|
|
|
|
/* quedémonos en gtk_main y ¡esperemos a que empiece la diversión! */
|
|
gtk_main ();
|
|
|
|
return 0;
|
|
}
|
|
</verb></tscreen>
|
|
|
|
Y ya estamos en el final del tutorial sobre la barra de herramientas.
|
|
Por supuesto, para apreciar completamente el ejemplo, necesita además
|
|
del código este precioso icono XPM que le mostramos a continuación:
|
|
|
|
<tscreen><verb>
|
|
/* XPM */
|
|
static char * gtk_xpm[] = {
|
|
"32 39 5 1",
|
|
". c none",
|
|
"+ c black",
|
|
"@ c #3070E0",
|
|
"# c #F05050",
|
|
"$ c #35E035",
|
|
"................+...............",
|
|
"..............+++++.............",
|
|
"............+++++@@++...........",
|
|
"..........+++++@@@@@@++.........",
|
|
"........++++@@@@@@@@@@++........",
|
|
"......++++@@++++++++@@@++.......",
|
|
".....+++@@@+++++++++++@@@++.....",
|
|
"...+++@@@@+++@@@@@@++++@@@@+....",
|
|
"..+++@@@@+++@@@@@@@@+++@@@@@++..",
|
|
".++@@@@@@+++@@@@@@@@@@@@@@@@@@++",
|
|
".+#+@@@@@@++@@@@+++@@@@@@@@@@@@+",
|
|
".+##++@@@@+++@@@+++++@@@@@@@@$@.",
|
|
".+###++@@@@+++@@@+++@@@@@++$$$@.",
|
|
".+####+++@@@+++++++@@@@@+@$$$$@.",
|
|
".+#####+++@@@@+++@@@@++@$$$$$$+.",
|
|
".+######++++@@@@@@@++@$$$$$$$$+.",
|
|
".+#######+##+@@@@+++$$$$$$@@$$+.",
|
|
".+###+++##+##+@@++@$$$$$$++$$$+.",
|
|
".+###++++##+##+@@$$$$$$$@+@$$@+.",
|
|
".+###++++++#+++@$$@+@$$@++$$$@+.",
|
|
".+####+++++++#++$$@+@$$++$$$$+..",
|
|
".++####++++++#++$$@+@$++@$$$$+..",
|
|
".+#####+++++##++$$++@+++$$$$$+..",
|
|
".++####+++##+#++$$+++++@$$$$$+..",
|
|
".++####+++####++$$++++++@$$$@+..",
|
|
".+#####++#####++$$+++@++++@$@+..",
|
|
".+#####++#####++$$++@$$@+++$@@..",
|
|
".++####++#####++$$++$$$$$+@$@++.",
|
|
".++####++#####++$$++$$$$$$$$+++.",
|
|
".+++####+#####++$$++$$$$$$$@+++.",
|
|
"..+++#########+@$$+@$$$$$$+++...",
|
|
"...+++########+@$$$$$$$$@+++....",
|
|
".....+++######+@$$$$$$$+++......",
|
|
"......+++#####+@$$$$$@++........",
|
|
".......+++####+@$$$$+++.........",
|
|
".........++###+$$$@++...........",
|
|
"..........++##+$@+++............",
|
|
"...........+++++++..............",
|
|
".............++++..............."};
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1> Libros de notas (<em/Notebooks/)
|
|
<p>
|
|
El <em/widget/ Notebook es una colección de `páginas' que se solapan
|
|
las unas a las otras, cada una con un contenido diferente. Este
|
|
<em/widget/ se ha vuelto cada vez más común últimamente en la
|
|
programación de interfaces gráficos de usuario (GUI en inglés), y es
|
|
una buena forma de mostrar bloques de información similar que
|
|
necesitan aparecer de forma separada.
|
|
|
|
La primera función que necesita conocer, como probablemente ya habrá
|
|
adivinado, se utiliza para crear un nuevo <em/widget/ notebook.
|
|
|
|
<tscreen><verb>
|
|
GtkWidget *gtk_notebook_new( void );
|
|
</verb></tscreen>
|
|
|
|
Una vez haya crear el libro de notas, hay 12 funciones que se pueden
|
|
utilizar para trabajar con él. Echémosles un vistazo una a una.
|
|
|
|
La primera que estudiaremos será la que nos permita establecer la
|
|
posición de los indicadores de la página. Estos indicadores se pueden
|
|
poner en cuatro lugares diferentes: arriba, abajo, a la derecha o a la
|
|
izquierda.
|
|
|
|
<tscreen><verb>
|
|
void gtk_notebook_set_tab_pos( GtkNotebook *notebook,
|
|
GtkPositionType pos );
|
|
</verb></tscreen>
|
|
|
|
<tt/GtkPositionType/ debe tener uno de los valores siguientes (su significado
|
|
está bastante claro):
|
|
|
|
<itemize>
|
|
<item> GTK_POS_LEFT
|
|
<item> GTK_POS_RIGHT
|
|
<item> GTK_POS_TOP
|
|
<item> GTK_POS_BOTTOM
|
|
</itemize>
|
|
|
|
GTK_POS_TOP es el valor por defecto.
|
|
|
|
Lo siguiente que estudiaremos es como añadir páginas al libro de notas.
|
|
Hay tres formas de añadirle páginas al <em/widget/. Veamos las dos primeras
|
|
formas (son muy parecidas).
|
|
|
|
<tscreen><verb>
|
|
void gtk_notebook_append_page( GtkNotebook *notebook,
|
|
GtkWidget *hijo,
|
|
GtkWidget *tab_label );
|
|
|
|
void gtk_notebook_prepend_page( GtkNotebook *notebook,
|
|
GtkWidget *hijo,
|
|
GtkWidget *tab_label );
|
|
</verb></tscreen>
|
|
|
|
Estas funciones le añaden páginas al libro de notas insertándolas desde
|
|
el fondo del libro (añadiéndolas), o desde parte superior del libro
|
|
(preañadiéndolas). <tt/hijo/ es el <em/widget/ que se mete en la página
|
|
del libro de notas, y <tt/tab_label/ es la etiqueta para la página que
|
|
estamos añadiendo.
|
|
|
|
La función que queda que sirve para añadir una página contiene todas las
|
|
propiedades de las anteriores, pero además permite especificar en que
|
|
posición quiere que esté la página dentro del libro de notas.
|
|
|
|
<tscreen><verb>
|
|
void gtk_notebook_insert_page( GtkNotebook *notebook,
|
|
GtkWidget *hijo,
|
|
GtkWidget *tab_label,
|
|
gint posicion );
|
|
</verb></tscreen>
|
|
|
|
Los parámetros son los mismos que habían en las funciones _append_ y
|
|
_prepend_ excepto que hay uno más que antes, <tt/posicion/. Este
|
|
parámetro se utiliza para especificar en que lugar debe introducirse
|
|
la página.
|
|
|
|
Ahora que sabemos como añadir un página, veamos como podemos eliminar
|
|
una página del libro de notas.
|
|
|
|
<tscreen><verb>
|
|
void gtk_notebook_remove_page( GtkNotebook *notebook,
|
|
gint page_num );
|
|
</verb></tscreen>
|
|
|
|
Esta función coge la página especificada por <tt/page_num/ y la
|
|
elimina del <em/widget/ al que apunta <tt/notebook/.
|
|
|
|
Para saber cual es la página actual del libro de notas utilice la
|
|
función:
|
|
|
|
<tscreen><verb>
|
|
gint gtk_notebook_current_page( GtkNotebook *notebook );
|
|
</verb></tscreen>
|
|
|
|
Las dos funciones siguientes sirven para ir a la página siguiente o a
|
|
la anterior del libro de notas. Para utilizarlas sólo hay que
|
|
proporcionar el <em/widget/ notebook que queremos manipular. Nota:
|
|
cuando el libro de notas está en la última página y se llama a
|
|
<tt/gtk_notebook_next_page/, se pasará a la primera página. Sin
|
|
embargo, si el libro de notas está en la primera página, y se llama a
|
|
<tt/gtk_notebook_prev_page/, no se pasará a la última página.
|
|
|
|
<tscreen><verb>
|
|
void gtk_notebook_next_page( GtkNoteBook *notebook );
|
|
|
|
void gtk_notebook_prev_page( GtkNoteBook *notebook );
|
|
</verb></tscreen>
|
|
|
|
La siguiente función establece la página `activa'. Si quiere que se
|
|
abra el libro de notas por la página 5, por ejemplo, debe utilizar
|
|
esta función. Si no utiliza esta función el libro de notas empezará
|
|
por defecto en la primera página.
|
|
|
|
<tscreen><verb>
|
|
void gtk_notebook_set_page( GtkNotebook *notebook,
|
|
gint page_num );
|
|
</verb></tscreen>
|
|
|
|
Las dos funciones siguientes añaden o eliminan los indicadores de las
|
|
páginas o el borde del libro, respectivamente.
|
|
|
|
<tscreen><verb>
|
|
void gtk_notebook_set_show_tabs( GtkNotebook *notebook,
|
|
gint show_tabs);
|
|
|
|
void gtk_notebook_set_show_border( GtkNotebook *notebook,
|
|
gint show_border );
|
|
</verb></tscreen>
|
|
|
|
<tt/show_tabs/ y <tt/show_border/ puede ser TRUE o FALSE.
|
|
|
|
Ahora echémosle un vistaza a un ejemplo, sacado del código de
|
|
<tt/testgtk.c/ que viene con la distribución de GTK, y que muestra
|
|
la utilización de las 13 funciones. Este pequeño programa crea una
|
|
ventana con un libro de notas y seis botones. El libro de notas
|
|
contiene 11 páginas, incluidas de tres formas diferentes, añadidas,
|
|
insertadas, y preañadidas. Los botones le permiten rotar las
|
|
posiciones de los indicadores, añadir y eliminar los indicadores y el
|
|
borde, eliminar una página, cambiar páginas hacia delante y hacia
|
|
detrás, y salir del programa.
|
|
|
|
<tscreen><verb>
|
|
/* principio del ejemplo notebook notebook.c */
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
/* Esta función rota la posición de los indicadores */
|
|
void rotate_book (GtkButton *boton, GtkNotebook *notebook)
|
|
{
|
|
gtk_notebook_set_tab_pos (notebook, (notebook->tab_pos +1) %4);
|
|
}
|
|
|
|
/* Añade/Elimina los indicadores de la página y los bordes */
|
|
void tabsborder_book (GtkButton *boton, GtkNotebook *notebook)
|
|
{
|
|
gint tval = FALSE;
|
|
gint bval = FALSE;
|
|
if (notebook->show_tabs == 0)
|
|
tval = TRUE;
|
|
if (notebook->show_border == 0)
|
|
bval = TRUE;
|
|
|
|
gtk_notebook_set_show_tabs (notebook, tval);
|
|
gtk_notebook_set_show_border (notebook, bval);
|
|
}
|
|
|
|
/* Elimina una página del libro de notas */
|
|
void remove_book (GtkButton *boton, GtkNotebook *notebook)
|
|
{
|
|
gint page;
|
|
|
|
page = gtk_notebook_current_page(notebook);
|
|
gtk_notebook_remove_page (notebook, page);
|
|
/* Hay que redibujar el widget --
|
|
Esto fuerza que el widget se autoredibuje */
|
|
gtk_widget_draw(GTK_WIDGET(notebook), NULL);
|
|
}
|
|
|
|
void delete (GtkWidget *widget, GtkWidget *event, gpointer data)
|
|
{
|
|
gtk_main_quit ();
|
|
}
|
|
|
|
int main (int argc, char *argv[])
|
|
{
|
|
GtkWidget *ventana;
|
|
GtkWidget *boton;
|
|
GtkWidget *table;
|
|
GtkWidget *notebook;
|
|
GtkWidget *frame;
|
|
GtkWidget *etiqueta;
|
|
GtkWidget *checkbutton;
|
|
int i;
|
|
char bufferf[32];
|
|
char bufferl[32];
|
|
|
|
gtk_init (&argc, &argv);
|
|
|
|
ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
|
|
|
gtk_signal_connect (GTK_OBJECT (ventana), "delete_event",
|
|
GTK_SIGNAL_FUNC (delete), NULL);
|
|
|
|
gtk_container_border_width (GTK_CONTAINER (ventana), 10);
|
|
|
|
table = gtk_table_new(2,6,TRUE);
|
|
gtk_container_add (GTK_CONTAINER (ventana), table);
|
|
|
|
/* Crea un nuevo libro de notas, indicando la posición de los
|
|
indicadores */
|
|
notebook = gtk_notebook_new ();
|
|
gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_TOP);
|
|
gtk_table_attach_defaults(GTK_TABLE(table), notebook, 0,6,0,1);
|
|
gtk_widget_show(notebook);
|
|
|
|
/* le añadimos un montón de páginas al libro de notas */
|
|
for (i=0; i < 5; i++) {
|
|
sprintf(bufferf, "Append Frame %d", i+1);
|
|
sprintf(bufferl, "Page %d", i+1);
|
|
|
|
frame = gtk_frame_new (bufferf);
|
|
gtk_container_border_width (GTK_CONTAINER (frame), 10);
|
|
gtk_widget_set_usize (frame, 100, 75);
|
|
gtk_widget_show (frame);
|
|
|
|
etiqueta = gtk_label_new (bufferf);
|
|
gtk_container_add (GTK_CONTAINER (frame), etiqueta);
|
|
gtk_widget_show (etiqueta);
|
|
|
|
etiqueta = gtk_label_new (bufferl);
|
|
gtk_notebook_append_page (GTK_NOTEBOOK (notebook), frame, etiqueta);
|
|
}
|
|
|
|
|
|
/* Ahora añadimos una página en punto específico */
|
|
checkbutton = gtk_check_button_new_with_label ("Check me please!");
|
|
gtk_widget_set_usize(checkbutton, 100, 75);
|
|
gtk_widget_show (checkbutton);
|
|
|
|
etiqueta = gtk_label_new ("Add spot");
|
|
gtk_container_add (GTK_CONTAINER (checkbutton), etiqueta);
|
|
gtk_widget_show (etiqueta);
|
|
etiqueta = gtk_label_new ("Add page");
|
|
gtk_notebook_insert_page (GTK_NOTEBOOK (notebook), checkbutton, etiqueta, 2);
|
|
|
|
/* Y finalmente preañadimos páginas en el libro de notas */
|
|
for (i=0; i < 5; i++) {
|
|
sprintf(bufferf, "Prepend Frame %d", i+1);
|
|
sprintf(bufferl, "PPage %d", i+1);
|
|
|
|
frame = gtk_frame_new (bufferf);
|
|
gtk_container_border_width (GTK_CONTAINER (frame), 10);
|
|
gtk_widget_set_usize (frame, 100, 75);
|
|
gtk_widget_show (frame);
|
|
|
|
etiqueta = gtk_label_new (bufferf);
|
|
gtk_container_add (GTK_CONTAINER (frame), etiqueta);
|
|
gtk_widget_show (etiqueta);
|
|
|
|
etiqueta = gtk_label_new (bufferl);
|
|
gtk_notebook_prepend_page (GTK_NOTEBOOK(notebook), frame, etiqueta);
|
|
}
|
|
|
|
/* Decimos en que página empezar (página 4) */
|
|
gtk_notebook_set_page (GTK_NOTEBOOK(notebook), 3);
|
|
|
|
|
|
/* creamos un montón de botones */
|
|
boton = gtk_button_new_with_label ("close");
|
|
gtk_signal_connect_object (GTK_OBJECT (boton), "clicked",
|
|
GTK_SIGNAL_FUNC (delete), NULL);
|
|
gtk_table_attach_defaults(GTK_TABLE(table), boton, 0,1,1,2);
|
|
gtk_widget_show(boton);
|
|
|
|
boton = gtk_button_new_with_label ("next page");
|
|
gtk_signal_connect_object (GTK_OBJECT (boton), "clicked",
|
|
(GtkSignalFunc) gtk_notebook_next_page,
|
|
GTK_OBJECT (notebook));
|
|
gtk_table_attach_defaults(GTK_TABLE(table), boton, 1,2,1,2);
|
|
gtk_widget_show(boton);
|
|
|
|
boton = gtk_button_new_with_label ("prev page");
|
|
gtk_signal_connect_object (GTK_OBJECT (boton), "clicked",
|
|
(GtkSignalFunc) gtk_notebook_prev_page,
|
|
GTK_OBJECT (notebook));
|
|
gtk_table_attach_defaults(GTK_TABLE(table), boton, 2,3,1,2);
|
|
gtk_widget_show(boton);
|
|
|
|
boton = gtk_button_new_with_label ("tab position");
|
|
gtk_signal_connect_object (GTK_OBJECT (boton), "clicked",
|
|
(GtkSignalFunc) rotate_book, GTK_OBJECT(notebook));
|
|
gtk_table_attach_defaults(GTK_TABLE(table), boton, 3,4,1,2);
|
|
gtk_widget_show(boton);
|
|
|
|
boton = gtk_button_new_with_label ("tabs/border on/off");
|
|
gtk_signal_connect_object (GTK_OBJECT (boton), "clicked",
|
|
(GtkSignalFunc) tabsborder_book,
|
|
GTK_OBJECT (notebook));
|
|
gtk_table_attach_defaults(GTK_TABLE(table), boton, 4,5,1,2);
|
|
gtk_widget_show(boton);
|
|
|
|
boton = gtk_button_new_with_label ("remove page");
|
|
gtk_signal_connect_object (GTK_OBJECT (boton), "clicked",
|
|
(GtkSignalFunc) remove_book,
|
|
GTK_OBJECT(notebook));
|
|
gtk_table_attach_defaults(GTK_TABLE(table), boton, 5,6,1,2);
|
|
gtk_widget_show(boton);
|
|
|
|
gtk_widget_show(table);
|
|
gtk_widget_show(ventana);
|
|
|
|
gtk_main ();
|
|
|
|
return 0;
|
|
}
|
|
/* fin del ejemplo */
|
|
</verb></tscreen>
|
|
|
|
Espero que la explicación le ayude de alguna manera a crear libros de
|
|
notas en sus aplicaciones GTK.
|
|
|
|
<!-- ***************************************************************** -->
|
|
<sect> El <em/widget/ GtkCList
|
|
<!-- ***************************************************************** -->
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<p>
|
|
El <em>widget</em> GtkCList ha reemplazado al <em>widget</em> GtkList
|
|
(que sigue estando disponible).
|
|
|
|
El <em>widget</em> GtkCList es un <em>widget</em> de una lista
|
|
multicolumna que es capaz de manejar, literalmente, miles de filas de
|
|
información. Cada columna puede tener (opcionalmente) un título, que
|
|
puede estar activado (opcionalmente), permitiéndonos enlazar una
|
|
función con la selección.
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Creando un <em>widget</em> GtkCList
|
|
<p>
|
|
Crear un GtkCList es algo bastante sencillo, una vez que sabe como
|
|
crear un <em>widget</em> en general. Se proporcionan al menos dos
|
|
formas estándar de crearlo, la forma fácil y la forma difícil. Pero
|
|
antes de crear una GtkCList, hay una cosa que debemos saber: ¿Cuántas
|
|
columnas va a tener?
|
|
|
|
No todas las columnas tienen que ser visibles y pueden utilizarse para
|
|
almacenar datos que estén relacionados con una cierta celda de la
|
|
lista.
|
|
|
|
<tscreen><verb>
|
|
GtkWidget *gtk_clist_new ( gint columns );
|
|
|
|
GtkWidget *gtk_clist_new_with_titles( gint columns,
|
|
gchar *titles[] );
|
|
</verb></tscreen>
|
|
|
|
Esta primera aproximación al problema es muy sencilla, pero la segunda
|
|
requerirá alguna explicación adicional. Cada columna puede tener un
|
|
título asociado. Si utilizamos la segunda forma, deberemos proporcionar
|
|
punteros al texto del título, y el número de punteros debe ser igual
|
|
al número de columnas especificadas. Por supuesto, siempre podemos
|
|
utilizar la primera forma de creación y añadir más tarde los títulos
|
|
de forma manual.
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Modos de operación
|
|
<p>
|
|
Hay varios atributos que pueden utilizarse para alterar el aspecto
|
|
de un GtkCList. Primero tenemos
|
|
|
|
<tscreen><verb>
|
|
void gtk_clist_set_selection_mode( GtkCList *clist,
|
|
GtkSelectionMode mode );
|
|
</verb></tscreen>
|
|
|
|
que, como el propio nombre indica, establece el modo de selección de la
|
|
lista GtkCList. El primer argumento es el <em>widget</em> GtkCList, y el
|
|
segundo especifica el modo de selección de la celda (están definidos
|
|
en <tt/gtkenums.h/). En el momento de escribir esto, estaban
|
|
disponibles los siguientes modos:
|
|
|
|
<itemize>
|
|
<item> GTK_SELECTION_SINGLE - La selección o es NULL o contiene un
|
|
puntero GList a un elemento seleccionado.
|
|
|
|
<item> GTK_SELECTION_BROWSE - La selección es NULL si la lista no
|
|
contiene <em>widgets</em> o si los que contiene son insensibles, en
|
|
caso contrario contendrá un puntero GList hacia una estructura GList,
|
|
y por tanto con exactamente un elemento.
|
|
|
|
<item> GTK_SELECTION_MULTIPLE - La selección es NULL si no hay
|
|
seleccionados una lista de elementos o un puntero GList para el primer
|
|
elemento seleccionado.<!-- FIXME: Todo esto no se si tiene sentido -->
|
|
Éste apunta de nuevo a una estructura GList para el segundo elemento
|
|
seleccionado y continua así. Éste es, actualmente, el modo por
|
|
<bf>defecto</bf> para el <em>widget</em> GtkCList.
|
|
|
|
<item> GTK_SELECTION_EXTENDED - La selección siempre es NULL.
|
|
</itemize>
|
|
|
|
Puede que se añadan otros modos en versiones posteriores de GTK.
|
|
|
|
También tenemos
|
|
|
|
<tscreen><verb>
|
|
void gtk_clist_set_policy (GtkCList *clist,
|
|
GtkPolicyType vscrollbar_policy,
|
|
GtkPolicyType hscrollbar_policy);
|
|
</verb></tscreen>
|
|
|
|
que define que es lo que ocurre con las barras de desplazamiento. Los
|
|
siguientes valores son los posibles para las barras de desplazamiento
|
|
horizontal y vertical:
|
|
|
|
<itemize>
|
|
<item> GTK_POLICY_ALWAYS - La barra de desplazamiento siempre está ahí.
|
|
|
|
<item> GTK_POLICY_AUTOMATIC - La barra de desplazamiento estará ahí sólo
|
|
cuando el número de elementos en la GtkCList supere el número que puede
|
|
mostrarse en el <em>widget</em>.
|
|
</itemize>
|
|
|
|
También podemos definir como debería ser el aspecto del borde del
|
|
<em>widget</em> GtkCList. Esto lo podemos hacer mediante
|
|
|
|
<tscreen><verb>
|
|
void gtk_clist_set_border( GtkCList *clist,
|
|
GtkShadowType border );
|
|
</verb></tscreen>
|
|
|
|
Y los posibles valores para el segundo argumento son
|
|
|
|
<itemize>
|
|
<item> GTK_SHADOW_NONE
|
|
|
|
<item> GTK_SHADOW_IN
|
|
|
|
<item> GTK_SHADOW_OUT
|
|
|
|
<item> GTK_SHADOW_ETCHED_IN
|
|
|
|
<item> GTK_SHADOW_ETCHED_OUT
|
|
</itemize>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Trabajando con los títulos
|
|
<p>
|
|
Cuando cree un <em>widget</em> GtkCList, también obtendrá
|
|
automáticamente un conjunto de botones título. Vivirán en lo alto de
|
|
una ventana CList, y pueden actuar como botones normales que responden
|
|
cuando se pulsa sobre ellos, o bien pueden ser pasivos, en cuyo caso
|
|
no serán nada más que un título. Hay cuatro llamadas diferentes que
|
|
nos ayudarán a establecer el estado de los botones título.
|
|
|
|
<tscreen><verb>
|
|
void gtk_clist_column_title_active( GtkCList *clist,
|
|
gint column );
|
|
|
|
void gtk_clist_column_title_passive( GtkCList *clist,
|
|
gint column );
|
|
|
|
void gtk_clist_column_titles_active( GtkCList *clist );
|
|
|
|
void gtk_clist_column_titles_passive( GtkCList *clist );
|
|
</verb></tscreen>
|
|
|
|
Un título activo es aquel que actua como un botón normal, y uno pasivo
|
|
es sólo una etiqueta. Las primeras dos llamadas de arriba
|
|
activarán/desactivarán el botón título correspondiente a la columna
|
|
<tt/column/, mientras que las dos llamadas siguientes
|
|
activarán/desactivarán todos los botones título que hayan en el
|
|
<em>widget</em> <tt/clist/ que se le proporcione a la función.
|
|
|
|
Pero, por supuesto, habrá casos en el que no querremos utilizar los
|
|
botones título, así que también tenemos la posibilidad de ocultarlos y
|
|
de volverlos a mostrar utilizando las dos llamadas siguientes:
|
|
|
|
<tscreen><verb>
|
|
void gtk_clist_column_titles_show( GtkCList *clist );
|
|
|
|
void gtk_clist_column_titles_hide( GtkCList *clist );
|
|
</verb></tscreen>
|
|
|
|
Para que los títulos sean realmente útiles necesitamos un mecanismo
|
|
que nos permita darles el valor que nosotros queramos y cambiar ese
|
|
valor, y podremos hacerlo mediante
|
|
|
|
<tscreen><verb>
|
|
void gtk_clist_set_column_title( GtkCList *clist,
|
|
gint column,
|
|
gchar *title );
|
|
</verb></tscreen>
|
|
|
|
Debe llevar cuidado, ya que sólo se puede especificar el título de una
|
|
columna a la vez, por lo que si conoce todos los títulos desde el
|
|
principio, le sugiero que utilice <tt/gtk_clist_new_with_titles/ (como
|
|
se describe arriba) para establecerlos adecuadamente. Le ahorrará
|
|
tiempo de programación, y hará su programa más pequeño. Hay algunos
|
|
casos donde es mejor utilizar la forma manual, y uno de ellos es
|
|
cuando no todos los títulos son texto. GtkCList nos proporciona
|
|
botones título que pueden, de hecho, incorporar un <em>widget</em>
|
|
entero, por ejemplo un <em>pixmap</em>. Todo esto se hace mediante
|
|
|
|
<tscreen><verb>
|
|
void gtk_clist_set_column_widget( GtkCList *clist,
|
|
gint column,
|
|
GtkWidget *widget );
|
|
</verb></tscreen>
|
|
|
|
que no debería necesitar de explicaciones adicionales.
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Manipulando la lista en sí.
|
|
<p>
|
|
Es posible cambiar la justificación de una columna, y esto se hace
|
|
mediante
|
|
|
|
<tscreen><verb>
|
|
void gtk_clist_set_column_justification( GtkCList *clist,
|
|
gint column,
|
|
GtkJustification justification );
|
|
</verb></tscreen>
|
|
|
|
El tipo GtkJustification puede tomar los valores siguientes:
|
|
|
|
<itemize>
|
|
<item>GTK_JUSTIFY_LEFT - El texto en la columna empezará desde el lado
|
|
izquierdo.
|
|
|
|
<item>GTK_JUSTIFY_RIGHT - El texto en la columna empezará desde el
|
|
lado derecho.
|
|
|
|
<item>GTK_JUSTIFY_CENTER - El texto se colocará en el centro de la
|
|
columna.
|
|
|
|
<item>GTK_JUSTIFY_FILL - El texto utilizará todo el espacio disponible
|
|
en la columna. Normalmente se hace añadiendo espacios en blanco entre
|
|
las palabras (o entre letras por separado, si se trata de una sola
|
|
palabra). Más o menos de la misma forma en la que lo hace un
|
|
procesador de textos WYSIWYG.
|
|
</itemize>
|
|
|
|
La siguiente función es muy importante, y debería ser un estándar
|
|
para inicializar todos los <em>widgets</em> GtkCList. Cuando se crea
|
|
la lista, los anchos de las distintas columnas se eligen para que
|
|
coincidan con sus títulos, y éste es el ancho adecuado que tenemos que
|
|
poner, utilizando
|
|
|
|
<tscreen><verb>
|
|
void gtk_clist_set_column_width( GtkCList *clist,
|
|
gint column,
|
|
gint width );
|
|
</verb></tscreen>
|
|
|
|
Observe que el ancho viene dado en pixeles y no en letras. Lo mismo
|
|
vale para el alto de la celda en las columnas, pero como el valor por
|
|
defecto es la altura del tipo de letra actual, no es algo tan crítico
|
|
para la aplicación. De todas formas, la altura se cambia mediante
|
|
|
|
<tscreen><verb>
|
|
void gtk_clist_set_row_height( GtkCList *clist,
|
|
gint height );
|
|
</verb></tscreen>
|
|
|
|
De nuevo, hay que advertir que el ancho viene dado en pixeles.
|
|
|
|
También podemos ir hacia un elemento sin la intervención del usuario,
|
|
sin embargo hace falta que sepamos hacia donde queremos ir. O en otras
|
|
palabras, necesitamos la fila y la columna del elemento al que queremos
|
|
pasar.
|
|
|
|
<tscreen><verb>
|
|
void gtk_clist_moveto( GtkCList *clist,
|
|
gint row,
|
|
gint column,
|
|
gfloat row_align,
|
|
gfloat col_align );
|
|
</verb></tscreen>
|
|
|
|
Es importante comprender bien el significado de <tt/gfloat
|
|
row_align/. Tiene un valor entre 0.0 y 1.0, donde 0.0 significa que
|
|
debemos hacer que la fila seleccionada aparezca en la alto de la
|
|
lista, mientras que 1.0 significa que la fila aparecerá en la parte de
|
|
abajo. El resto de valores entre 0.0 y 1.0 son válidos y harán que la
|
|
fila aparezca entre la parte superior y la inferior. El último
|
|
argumento, <tt/gfloat col_align/ funciona igual, siendo 0.0 la
|
|
izquierda y 1.0 la derecha.
|
|
|
|
Dependiendo de las necesidades de la aplicación, puede que no tengamos
|
|
que hacer un desplazamiento hacia un elemento que ya sea visible. Por
|
|
tanto, ¿cómo podemos saber si ya es visible? Como siempre, hay una función
|
|
que sirve para averiguarlo
|
|
|
|
<tscreen><verb>
|
|
GtkVisibility gtk_clist_row_is_visible( GtkCList *clist,
|
|
gint row );
|
|
</verb></tscreen>
|
|
|
|
El valor devuelto es uno de los siguientes:
|
|
|
|
<itemize>
|
|
<item>GTK_VISIBILITY_NONE
|
|
|
|
<item>GTK_VISIBILITY_PARTIAL
|
|
|
|
<item>GTK_VISIBILITY_FULL
|
|
</itemize>
|
|
|
|
Como puede ver, sólo nos dice si una fila es visible. Actualmente no hay
|
|
ninguna forma de obtener el mismo dato para una columna. Sin embargo
|
|
podemos obtener información parcial, porque si el valor devuelto es
|
|
GTK_VISIBILITY_PARTIAL, entonces es que alguna parte está oculta,
|
|
pero no sabemos si es la fila que está cortada por la parte de abajo
|
|
de la lista, o si la fila tiene columnas que están fuera.
|
|
|
|
También podemos cambiar el color del primer y del segundo plano de una
|
|
fila en particular. Esto es útil para marcar la fila seleccionada por
|
|
el usuario, y las dos funciones que hay que utilizar son
|
|
|
|
<tscreen><verb>
|
|
void gtk_clist_set_foreground( GtkCList *clist,
|
|
gint row,
|
|
GdkColor *color );
|
|
|
|
void gtk_clist_set_background( GtkCList *clist,
|
|
gint row,
|
|
GdkColor *color );
|
|
</verb></tscreen>
|
|
|
|
Cuidado, ya que los colores deben estar asignados previamente en la
|
|
memoria.
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Añadiendo filas a la lista
|
|
<p>
|
|
Podemos añadir filas de dos formas. Se pueden añadir al final de la lista
|
|
utilizando
|
|
|
|
<tscreen><verb>
|
|
gint gtk_clist_append( GtkCList *clist,
|
|
gchar *text[] );
|
|
</verb></tscreen>
|
|
|
|
o podemos insertar una fila en un lugar determinado utilizando
|
|
|
|
<tscreen><verb>
|
|
void gtk_clist_insert( GtkCList *clist,
|
|
gint row,
|
|
gchar *text[] );
|
|
</verb></tscreen>
|
|
|
|
En ambas llamadas podemos proporcionar un conjunto de punteros que
|
|
serán los textos que queremos poner en las columnas. El número de
|
|
punteros debe ser igual al número de columnas en la lista. Si el
|
|
argumento <tt/text[]/ es NULL, entonces no habrá texto en las columnas
|
|
de la fila. Esto sería útil, por ejemplo, si queremos añadir
|
|
<em>pixmaps</em> en lugar de texto (en general para cualquier cosa que
|
|
haya que hacer manualmente).
|
|
|
|
De nuevo, cuidado ya que el número de filas y de columnas comienza en
|
|
0.
|
|
|
|
Para eliminar una fila individual podemos utilizar
|
|
|
|
<tscreen><verb>
|
|
void gtk_clist_remove( GtkCList *clist,
|
|
gint row );
|
|
</verb></tscreen>
|
|
|
|
Hay también una llamada que elimina todas las filas en la lista.
|
|
Es mucho más rápido que llamar a <tt/gtk_clist_remove/ una vez por
|
|
cada fila, que sería la única alternativa.
|
|
|
|
<tscreen><verb>
|
|
void gtk_clist_clear( GtkCList *clist );
|
|
</verb></tscreen>
|
|
|
|
También hay dos funciones que es conveniente utilizarlas cuando hay
|
|
que hacerle muchos cambios a una lista. Son para evitar que la lista
|
|
parpadee mientras es actualizada repetidamente, que puede ser muy
|
|
molesto para el usuario. Por tanto es una buena idea congelar la
|
|
lista, hacer los cambios, y descongelarla, que hará que la lista se
|
|
actualice en la pantalla.
|
|
|
|
<tscreen><verb>
|
|
void gtk_clist_freeze( GtkCList * clist );
|
|
|
|
void gtk_clist_thaw( GtkCList * clist );
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Poniendo texto y <em>pixmaps</em> en las celdas
|
|
<p>
|
|
Una celda puede contener un <em>pixmap</em>, texto o ambos. Para ponerlos
|
|
en las celdas, podemos utilizar las siguientes funciones.
|
|
|
|
<tscreen><verb>
|
|
void gtk_clist_set_text( GtkCList *clist,
|
|
gint row,
|
|
gint column,
|
|
gchar *text );
|
|
|
|
void gtk_clist_set_pixmap( GtkCList *clist,
|
|
gint row,
|
|
gint column,
|
|
GdkPixmap *pixmap,
|
|
GdkBitmap *mask );
|
|
|
|
void gtk_clist_set_pixtext( GtkCList *clist,
|
|
gint row,
|
|
gint column,
|
|
gchar *text,
|
|
guint8 spacing,
|
|
GdkPixmap *pixmap,
|
|
GdkBitmap *mask );
|
|
</verb></tscreen>
|
|
|
|
Son bastante sencillas de entender. Todas las llamadas tienen la
|
|
GtkCList como primer argumento, seguidas por la fila y la columna
|
|
de la celda, y seguidas por el dato que debe ponerse en la celda. El
|
|
argumento <tt/gint8 spacing/ en <tt/gtk_clist_set_pixtext/ es el
|
|
número de <em>pixels</em> entre el <em>pixmap</em> y el principio del
|
|
texto.
|
|
|
|
Para leer los datos que hay en una celda, podemos utilizar
|
|
|
|
<tscreen><verb>
|
|
gint gtk_clist_get_text( GtkCList *clist,
|
|
gint row,
|
|
gint column,
|
|
gchar **text );
|
|
|
|
gint gtk_clist_get_pixmap( GtkCList *clist,
|
|
gint row,
|
|
gint column,
|
|
GdkPixmap **pixmap,
|
|
GdkBitmap **mask );
|
|
|
|
gint gtk_clist_get_pixtext( GtkCList *clist,
|
|
gint row,
|
|
gint column,
|
|
gchar **text,
|
|
guint8 *spacing,
|
|
GdkPixmap **pixmap,
|
|
GdkBitmap **mask );
|
|
</verb></tscreen>
|
|
|
|
No es necesario leer todos los datos en caso de que no estemos
|
|
interesados. Cualquiera de los punteros que se supone contendrán los
|
|
valores a devolver (cualquiera excepto el <tt/clist/) pueden ser
|
|
NULL. Por lo que si sólo queremos leer el texto de una celda que es de
|
|
tipo <tt/pixtext/, deberíamos hacer lo siguiente, suponiendo que
|
|
<tt/clist/, <tt/row/ y <tt/column/ ya existan:
|
|
|
|
<tscreen><verb>
|
|
gchar *mytext;
|
|
|
|
gtk_clist_get_pixtext(clist, row, column, &mytext, NULL, NULL, NULL);
|
|
</verb></tscreen>
|
|
|
|
Hay una rutina más que está relacionada con lo que está dentro
|
|
de una celda de una <tt/clist/, y es
|
|
|
|
<tscreen><verb>
|
|
GtkCellType gtk_clist_get_cell_type( GtkCList *clist,
|
|
gint row,
|
|
gint column );
|
|
</verb></tscreen>
|
|
|
|
que devuelve el tipo de datos que hay en la celda. El valor devuelto es
|
|
uno de los siguientes
|
|
|
|
<itemize>
|
|
<item>GTK_CELL_EMPTY
|
|
|
|
<item>GTK_CELL_TEXT
|
|
|
|
<item>GTK_CELL_PIXMAP
|
|
|
|
<item>GTK_CELL_PIXTEXT
|
|
|
|
<item>GTK_CELL_WIDGET
|
|
</itemize>
|
|
|
|
También hay una función que nos permite especificar la indentación de
|
|
un celda (horizontal o vertical). El valor de la indentación es del
|
|
tipo <tt/gint/, viene dado en <em>pixeles</em>, y puede ser positivo o
|
|
negativo.
|
|
|
|
<tscreen><verb>
|
|
void gtk_clist_set_shift( GtkCList *clist,
|
|
gint row,
|
|
gint column,
|
|
gint vertical,
|
|
gint horizontal );
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Almacenando punteros a datos
|
|
<p>
|
|
Con una GtkCList es posible poner un puntero a datos en una
|
|
fila. Este puntero no será visible al usuario, pero puede serle útil
|
|
al programador.
|
|
|
|
De nuevo, las funciones son lo suficientemente autoexplicativas
|
|
|
|
<tscreen><verb>
|
|
void gtk_clist_set_row_data( GtkCList *clist,
|
|
gint row,
|
|
gpointer data );
|
|
|
|
void gtk_clist_set_row_data_full( GtkCList *clist,
|
|
gint row,
|
|
gpointer data,
|
|
GtkDestroyNotify destroy );
|
|
|
|
gpointer gtk_clist_get_row_data( GtkCList *clist,
|
|
gint row );
|
|
|
|
gint gtk_clist_find_row_from_data( GtkCList *clist,
|
|
gpointer data );
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Trabajando con la selección
|
|
<p>
|
|
También hay funciones que nos permiten forzar la (de)selección de una
|
|
fila. Son
|
|
|
|
<tscreen><verb>
|
|
void gtk_clist_select_row( GtkCList *clist,
|
|
gint row,
|
|
gint column );
|
|
|
|
void gtk_clist_unselect_row( GtkCList *clist,
|
|
gint row,
|
|
gint column );
|
|
</verb></tscreen>
|
|
|
|
Y también una función que tomará las coordenadas x e y (por ejemplo,
|
|
recibidas del ratón), mirará en la lista y devolverá la fila y la
|
|
columna que les corresponden.
|
|
|
|
<tscreen><verb>
|
|
gint gtk_clist_get_selection_info( GtkCList *clist,
|
|
gint x,
|
|
gint y,
|
|
gint *row,
|
|
gint *column );
|
|
</verb></tscreen>
|
|
|
|
Cuando detectemos algo interesante, como por ejemplo el movimiento del
|
|
ratón, o una pulsación en cualquier lugar de la lista, podemos leer
|
|
las coordenadas del ratón y encontrar en que elemento de la lista se
|
|
encuentra. ¿Engorroso? Afortunadamente, hay una forma más sencilla de
|
|
hacer las cosas...
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Las señales que lo hacen todo
|
|
<p>
|
|
Como con el resto de <em>widgets</em>, hay unas cuantas señales que
|
|
podemos utilizar. El <em>widget</em> GtkCList está derivado del
|
|
<em>widget</em> GtkContainer, y por tanto tiene las mismas
|
|
señales que éste, pero además añade las siguientes:
|
|
|
|
<itemize>
|
|
<item><tt/select_row/ - Esta señal enviará la siguiente información,
|
|
en este orden: GtkCList *clist, gint row, gint column, GtkEventButton
|
|
*event
|
|
|
|
<item><tt/unselect_row/ - Cuando el usuario deselecciona una fila, se
|
|
activará esta señal. Envia la misma información que <tt/select_row/
|
|
|
|
<item><tt/click_column/ - Envia GtkCList *clist, gint column
|
|
</itemize>
|
|
|
|
Por tanto si queremos conectar una llamada a <tt/select_row/, la
|
|
llamada se deberá declarar como
|
|
|
|
<tscreen><verb>
|
|
void select_row_callback(GtkWidget *widget,
|
|
gint row,
|
|
gint column,
|
|
GdkEventButton *event,
|
|
gpointer data);
|
|
</verb></tscreen>
|
|
|
|
La llamada se conectará, como siempre, con
|
|
|
|
<tscreen><verb>
|
|
gtk_signal_connect(GTK_OBJECT( clist),
|
|
"select_row"
|
|
GTK_SIGNAL_FUNC(select_row_callback),
|
|
NULL);
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Un ejemplo GtkCList
|
|
<p>
|
|
|
|
<tscreen><verb>
|
|
/* principio del ejemplo clist clist.c */
|
|
|
|
#include <gtk/gtk.h>
|
|
#include <glib.h>
|
|
|
|
/* Aquí tenemos algunos prototipos de las funciones de llamada */
|
|
void button_add_clicked( GtkWidget *boton, gpointer data);
|
|
void button_clear_clicked( GtkWidget *boton, gpointer data);
|
|
void button_hide_show_clicked( GtkWidget *boton, gpointer data);
|
|
void selection_made( GtkWidget *clist, gint row, gint column,
|
|
GdkEventButton *event, gpointer data);
|
|
|
|
gint main (int argc, gchar *argv[])
|
|
{
|
|
GtkWidget *ventana;
|
|
GtkWidget *vbox, *hbox;
|
|
GtkWidget *clist;
|
|
GtkWidget *button_add, *button_clear, *button_hide_show;
|
|
gchar *titles[2] = {"Ingredients","Amount"};
|
|
|
|
gtk_init(&argc, &argv);
|
|
|
|
|
|
ventana=gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
|
gtk_widget_set_usize(GTK_WIDGET(ventana), 300, 150);
|
|
|
|
gtk_window_set_title(GTK_WINDOW(ventana), "GtkCList Example");
|
|
gtk_signal_connect(GTK_OBJECT(ventana),
|
|
"destroy",
|
|
GTK_SIGNAL_FUNC(gtk_main_quit),
|
|
NULL);
|
|
|
|
vbox=gtk_vbox_new(FALSE, 5);
|
|
gtk_container_border_width(GTK_CONTAINER(vbox), 5);
|
|
gtk_container_add(GTK_CONTAINER(ventana), vbox);
|
|
gtk_widget_show(vbox);
|
|
|
|
/* Crear el GtkCList. Para este ejemplo utilizaremos 2 columnas */
|
|
clist = gtk_clist_new_with_titles( 2, titles);
|
|
|
|
/* Cuando se hace una selección, queremos saber algo acerca de
|
|
* ella. La función de llamada utilizada es selection_made, y su
|
|
* código lo podemos encontrar más abajo */
|
|
gtk_signal_connect(GTK_OBJECT(clist), "select_row",
|
|
GTK_SIGNAL_FUNC(selection_made),
|
|
NULL);
|
|
|
|
/* No es necesario ponerle sombra al borde, pero es bonito :) */
|
|
gtk_clist_set_border(GTK_CLIST(clist), GTK_SHADOW_OUT);
|
|
|
|
/* Lo que sí que es importante, es poner el ancho de las columnas
|
|
* ya no tendrán el valor correcto en caso contrario. Recuerde que
|
|
* las columnas se numeran desde el 0 en adelante (hasta el 1 en
|
|
* este caso).
|
|
*/
|
|
gtk_clist_set_column_width (GTK_CLIST(clist), 0, 150);
|
|
|
|
/* Scollbars _only when needed_ */
|
|
gtk_clist_set_policy(GTK_CLIST(clist), GTK_POLICY_AUTOMATIC,
|
|
GTK_POLICY_AUTOMATIC);
|
|
|
|
/* Añade el widget GtkCList a la caja vertical y lo muestra. */
|
|
gtk_box_pack_start(GTK_BOX(vbox), clist, TRUE, TRUE, 0);
|
|
gtk_widget_show(clist);
|
|
|
|
/* Crea los botones y los añade a la ventana. Ver la parte del
|
|
* tutorial sobre botones para ver más ejemplos y comentarios
|
|
* acerca de todo esto.
|
|
*/
|
|
hbox = gtk_hbox_new(FALSE, 0);
|
|
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
|
|
gtk_widget_show(hbox);
|
|
|
|
button_add = gtk_button_new_with_label("Add List");
|
|
button_clear = gtk_button_new_with_label("Clear List");
|
|
button_hide_show = gtk_button_new_with_label("Hide/Show titles");
|
|
|
|
gtk_box_pack_start(GTK_BOX(hbox), button_add, TRUE, TRUE, 0);
|
|
gtk_box_pack_start(GTK_BOX(hbox), button_clear, TRUE, TRUE, 0);
|
|
gtk_box_pack_start(GTK_BOX(hbox), button_hide_show, TRUE, TRUE, 0);
|
|
|
|
/* Conectar nuestras funciones de llamada a los tres botones */
|
|
gtk_signal_connect_object(GTK_OBJECT(button_add), "clicked",
|
|
GTK_SIGNAL_FUNC(button_add_clicked),
|
|
(gpointer) clist);
|
|
gtk_signal_connect_object(GTK_OBJECT(button_clear), "clicked",
|
|
GTK_SIGNAL_FUNC(button_clear_clicked),
|
|
(gpointer) clist);
|
|
gtk_signal_connect_object(GTK_OBJECT(button_hide_show), "clicked",
|
|
GTK_SIGNAL_FUNC(button_hide_show_clicked),
|
|
(gpointer) clist);
|
|
|
|
gtk_widget_show(button_add);
|
|
gtk_widget_show(button_clear);
|
|
gtk_widget_show(button_hide_show);
|
|
|
|
/* Ahora hemos terminado el interface y sólo nos queda mostrar la
|
|
* ventana y entrar en el bucle gtk_main.
|
|
*/
|
|
gtk_widget_show(ventana);
|
|
gtk_main();
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* El usuario pulsó el botón "Add List". */
|
|
void button_add_clicked( GtkWidget *boton, gpointer data)
|
|
{
|
|
int indx;
|
|
|
|
/* Algo tonto que añadir a la lista. 4 filas con 2 columnas cada
|
|
* una
|
|
*/
|
|
gchar *drink[4][2] = {{"Milk", "3 Oz"},
|
|
{"Water", "6 l"},
|
|
{"Carrots", "2"},
|
|
{"Snakes", "55"}};
|
|
|
|
/* Aquí hacemos la adición del texto. Se hace una vez por cada
|
|
* fila.
|
|
*/
|
|
for( indx=0; indx < 4; indx++)
|
|
gtk_clist_append( (GtkCList*) data, drink[indx]);
|
|
|
|
return;
|
|
}
|
|
|
|
/* El usuario pulsó el botón "Clear List" */
|
|
void button_clear_clicked( GtkWidget *boton, gpointer data)
|
|
{
|
|
/* Borrar la lista utilizando gtk_clist_clear. Esto es mucho más
|
|
* rápido que llamar a gtk_clist_remove una vez por cada fila.
|
|
*/
|
|
gtk_clist_clear((GtkCList*) data);
|
|
|
|
return;
|
|
}
|
|
|
|
/* El usuario pulsó el botón "Hide/Show titles". */
|
|
void button_hide_show_clicked( GtkWidget *boton, gpointer data)
|
|
{
|
|
/* Una bandera para recordar el estado. 0 = actualmente visible */
|
|
static short int flag = 0;
|
|
|
|
if (flag == 0)
|
|
{
|
|
/* Oculta los títulos y pone la bandera a 1 */
|
|
gtk_clist_column_titles_hide((GtkCList*) data);
|
|
flag++;
|
|
}
|
|
else
|
|
{
|
|
/* Muestra los títulos y pone la bandera a 0 */
|
|
gtk_clist_column_titles_show((GtkCList*) data);
|
|
flag--;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/* Se llegamos aquí, entonces el usuario ha seleccionado una fila de
|
|
* la lista.
|
|
*/
|
|
void selection_made( GtkWidget *clist, gint row, gint column,
|
|
GdkEventButton *event, gpointer data)
|
|
{
|
|
gchar *text;
|
|
|
|
/* Obtiene el texto que se ha almacenado en la fila y columna
|
|
* sobre las que se ha pulsado. Lo recibiremos como un puntero en
|
|
* el argumento text.
|
|
*/
|
|
gtk_clist_get_text(GTK_CLIST(clist), row, column, &text);
|
|
|
|
/* Imprime alguna información sobre la fila seleccionada */
|
|
g_print("You selected row %d. More specifically you clicked in column %d, and the text in this cell is %s\n\n", row, column, text);
|
|
|
|
return;
|
|
}
|
|
/* final del ejemplo */
|
|
</verb></tscreen>
|
|
|
|
<!-- ***************************************************************** -->
|
|
<sect> El <em>widget</em> árbol<label id="sec_Tree_Widgets">
|
|
<!-- ***************************************************************** -->
|
|
<p>
|
|
|
|
El propósito del <em>widget</em> GtkTree es mostrar datos organizados
|
|
de forma jerárquica. El <em>widget</em> GtkTree en sí es un contenedor
|
|
vertical para los <em>widgets</em> del tipo GtkTreeItem. GtkTree en
|
|
sí mismo no es muy diferente de GtkList - ambos están derivados
|
|
directamente de GtkContainer, y los métodos GtkContainer funcionan
|
|
igual en los <em>widgets</em> GtkTree que en los GtkList. La
|
|
diferencia es que los <em>widgets</em> GtkTree pueden anidarse
|
|
dentro de otros <em>widgets</em> GtkTree. Vamos a verlo de forma
|
|
resumida.
|
|
|
|
El <em>widget</em> GtkTree tiene su propia ventana, y tiene por
|
|
defecto un fondo de color blanco, como GtkList. La mayoría de los
|
|
métodos de GtkTree funcionan igual que sus correspondientes de
|
|
GtkList. Sin embargo, GtkTree no está derivado de GtkList, por lo que
|
|
no puede intercambiarlos.
|
|
|
|
<sect1> Creando un árbol
|
|
<p>
|
|
Puede crear un GtkTree de la forma usual, utilizando:
|
|
|
|
<tscreen><verb>
|
|
GtkWidget* gtk_tree_new( void );
|
|
</verb></tscreen>
|
|
|
|
Como el <em>widget</em> GtkList, un GtkTree crecerá cuando le añadan
|
|
elementos o cuando crezca alguno de sus subárboles. Por esta razón,
|
|
suelen venir dentro de una GtkScrolledWindow. Puede que quiera
|
|
utilizar <tt/gtk_widget_set_usize()/ con la ventana para asegurarse de
|
|
que es lo suficientemente grande como para poder ver todos los
|
|
elementos del árbol, ya que el valor por defecto de GtkScrolledWindow
|
|
es bastante pequeño.
|
|
|
|
Ahora que ya sabemos como crear un árbol, probablemente quiera
|
|
añadirle algunos elementos. <ref id="sec_Tree_Item_Widget" name="El
|
|
widget elemento de árbol"> más adelante explica todos los
|
|
detalles de GtkTreeItem. Por ahora, es suficiente con saber como crear
|
|
uno, utilizando:
|
|
|
|
<tscreen><verb>
|
|
GtkWidget* gtk_tree_item_new_with_label( gchar *etiqueta );
|
|
</verb></tscreen>
|
|
|
|
Puede añadirlo al árbol utilizando una de las siguientes funciones
|
|
(ver <ref id="sec_GtkTree_Functions" name="Funciones y macros">
|
|
más adelante para leer más opciones):
|
|
|
|
<tscreen><verb>
|
|
void gtk_tree_append( GtkTree *arbol,
|
|
GtkWidget *elemento_arbol );
|
|
|
|
void gtk_tree_prepend( GtkTree *arbol,
|
|
GtkWidget *elemento_arbol );
|
|
</verb></tscreen>
|
|
|
|
Observe que debe añadir elementos a un GtkTree de uno en uno - no
|
|
hay un equivalente a <tt/gtk_list_*_items()/.
|
|
|
|
<sect1> Añadiendo un Subárbol
|
|
<p>
|
|
Un subárbol se crea como cualquier otro <em>widget</em> GtkTree. Un
|
|
subárbol se añade a otro árbol bajo un elemento del mismo, utilizando:
|
|
|
|
<tscreen><verb>
|
|
void gtk_tree_item_set_subtree( GtkTreeItem *elemento_arbol,
|
|
GtkWidget *subarbol );
|
|
</verb></tscreen>
|
|
|
|
No necesita llamar a <tt/gtk_widget_show()/ en un subárbol ni antes ni
|
|
después de añadirlo a GtkTreeItem. Sin embargo, <em>deberá</em> haber
|
|
añadido el GtkTreeItem en cuestión a un árbol padre antes de llamar a
|
|
<em/gtk_tree_item_set_subtree()/. Esto se debe a que, técnicamente,
|
|
el padre del subárbol <em>no</em> es el GtkTreeItem «propietario»,
|
|
sino el GtkTree que contiene al GtkTreeItem.
|
|
|
|
Cuando le añade un subárbol a un GtkTreeItem, aparece el signo de un
|
|
más o de un menos a su lado, donde puede pinchar el usuario para
|
|
«expandirlo» u «contraerlo», o sea, para mostrar u ocultar su
|
|
subárbol. Los GtkTreeItems están contraídos por defecto. Observe que
|
|
cuando contrae un GtkTreeItem, cualquier elemento seleccionado en el
|
|
subárbol permanece seleccionado, que puede no coincidir con lo que el
|
|
usuario espera.
|
|
|
|
<sect1> Manejando la lista de selección
|
|
<p>
|
|
Como con GtkList, GtkTree tiene un campo <tt>selection</tt>, y
|
|
es posible controlar el comportamiento del árbol (de alguna manera)
|
|
estableciendo el tipo de selección, utilizando:
|
|
|
|
<tscreen><verb>
|
|
void gtk_tree_set_selection_mode( GtkTree *arbol,
|
|
GtkSelectionMode mode );
|
|
</verb></tscreen>
|
|
|
|
La semántica asociada con los distintos modos de selección está
|
|
descrita en la sección del <em>widget</em> GtkList. Como ocurría con
|
|
el <em>widget</em> GtkList, se enviarán las señales <tt/select_child/,
|
|
<tt/unselect_child/ (realmente no - ver <ref id="sec_GtkTree_Signals"
|
|
name="Señales"> más adelante para una explicación), y
|
|
<tt/selection_changed/ cuando los elementos de la lista sean
|
|
seleccionados o deseleccionados. Sin embargo, para aprovechar estas
|
|
señales, necesita conocer por medio <em>de que</em> <em>widget</em>
|
|
GtkTree serán emitidas, y donde encontrar una lista con los elementos
|
|
seleccionados.
|
|
|
|
Todo esto es una potencial fuente de confusión. La mejor manera de
|
|
entenderlo es imaginarse que aunque todos los <em>widgets</em> GtkTree
|
|
son creados iguales, algunos son más iguales que otros. Todos los
|
|
<em>widgets</em> GtkTree tienen su propia ventana X, y por tanto
|
|
pueden recibir eventos como pulsaciones de ratón (¡si sus hijos o
|
|
GtkTreeItems no las capturan primero!). Sin embargo, para hacer
|
|
que GTK_SELECTION_SINGLE y GTK_SELECTION_BROWSE funcionen bien, la
|
|
lista de elementos seleccionados debe ser específica al <em>widget</em>
|
|
GtkTree superior de la jerarquia, conocido como el «árbol raíz».
|
|
|
|
Por tanto no es una buena idea acceder al campo <tt>selection</tt>
|
|
directamente en un <em>widget</em> GtkTree arbitrario, a menos que
|
|
<em>sepa</em> que es el árbol raíz. En vez de eso, utilice la
|
|
macro GTK_TREE_SELECTION (arbol), que da la lista selección del árbol
|
|
raíz como un puntero <tt/GList/. Por supuesto, esta lista puede
|
|
incluir elementos que no estén en el subárbol en cuestión si el tipo
|
|
de selección es GTK_SELECTION_MULTIPLE.
|
|
|
|
Para terminar, las señales <tt/select_child/ (y tt/unselect_child/, en
|
|
teoría) son emitidas por todos los árboles, pero la señal
|
|
<em/selection_changed/ es emitida sólo por el árbol raíz. En
|
|
consecuencia, si quiere manipular la señal <tt/select_child/ de un
|
|
árbol y todos sus subárboles, tendrá que llamar a
|
|
<tt/gtk_signal_connect()/ una vez por cada subárbol.
|
|
|
|
<sect1> Estructura interna del <em>widget</em> árbol
|
|
<p>
|
|
La definición de la estructura GtkTree es ls siguiente:
|
|
|
|
<tscreen><verb>
|
|
struct _GtkTree
|
|
{
|
|
GtkContainer container;
|
|
|
|
GList *child;
|
|
|
|
GtkTree* root_tree; /* propietario de la lista de selección */
|
|
GtkWidget* tree_owner;
|
|
GList *selection;
|
|
guint level;
|
|
guint indent_value;
|
|
guint current_indent;
|
|
guint selection_mode : 2;
|
|
guint view_mode : 1;
|
|
guint view_line : 1;
|
|
};
|
|
</verb></tscreen>
|
|
|
|
Ya se han mencionado los peligros asociados con el acceso directo al
|
|
campo <tt>selection</tt>. Se puede acceder a los otros campos
|
|
importantes de la estructura mediante macros manipuladoras o
|
|
funciones de clase. GTK_TREE_IS_ROOT_TREE (arbol) devuelve un valor
|
|
booleano que indica si un árbol es árbol raíz de una jerarquia
|
|
GtkTree, mientras que GTK_TREE_ROOT_TREE (arbol) devuelve el árbol
|
|
raíz, un objeto de tipo GtkTree (recuerde transformarlo utilizando
|
|
GTK_WIDGET (arbol) si quiere utilizar con él alguna de la funciones
|
|
<tt/gtk_widget_*()/).
|
|
|
|
En lugar de acceder directamente al campo hijo de un <em>widget</em>
|
|
GtkTree, probablemente sea mejor transformarlo utilizando
|
|
GTK_CONTAINER (arbol), y pasárselo a la función
|
|
<tt/gtk_container_children()/. Con esto crearemos un duplicado de la
|
|
lista original, por lo que deberá eliminarlo de la memoria utilizando
|
|
<tt/g_list_free()/ después haber hecho con él lo que tenga que hacer,
|
|
o bien crear un bucle que lo vaya destruyendo de elemento en elemento,
|
|
como por ejemplo así:
|
|
|
|
<tscreen><verb>
|
|
hijo = gtk_container_children (GTK_CONTAINER (arbol));
|
|
while (hijo) {
|
|
do_something_nice (GTK_TREE_ITEM (hijo->data));
|
|
hijo = g_list_remove_link (hijo, hijo);
|
|
}
|
|
</verb></tscreen>
|
|
|
|
El campo <tt>tree_owner</tt> sólo está definido en subárboles, donde
|
|
apunta al <em>widget</em> GtkTreeItem que contiene al árbol en
|
|
cuestión. El campo <tt>level</tt> indica el nivel de profundidad de un
|
|
árbol en particular; los árboles raíz tienen un nivel 0, y cada nivel
|
|
sucesivo de subárboles tiene un nivel superior al del padre. Sólo se
|
|
puede asegurar que este campo contiene un valor correcto después de
|
|
que el <em>widget</em> GtkTree se dibuje en la pantalla.
|
|
|
|
<sect2> Señales<label id="sec_GtkTree_Signals">
|
|
<p>
|
|
<tscreen><verb>
|
|
void selection_changed( GtkTree *arbol );
|
|
</verb></tscreen>
|
|
|
|
Esta señal se emitirá cuando cambie el campo <tt>selection</tt> de
|
|
un GtkTree. Esto ocurre cuando se selecciona o deselecciona un hijo del
|
|
GtkTree.
|
|
|
|
<tscreen><verb>
|
|
void select_child( GtkTree *arbol,
|
|
GtkWidget *hijo );
|
|
</verb></tscreen>
|
|
|
|
Esta señal se emite cuando se está seleccionando un hijo del GtkTree.
|
|
Esto ocurre en las llamadas a <tt/gtk_tree_select_item()/,
|
|
<tt/gtk_tree_select_child()/, en <em>todas</em> las pulsaciones de
|
|
botón y llamadas a <tt/gtk_tree_item_toggle()/ y
|
|
<tt/gtk_item_toggle()/. Puede que a veces se invoque indirectamente en
|
|
otras ocasiones, cuando el hijo se añada o elimine del GtkTree.
|
|
|
|
<tscreen><verb>
|
|
void unselect_child (GtkTree *arbol,
|
|
GtkWidget *hijo);
|
|
</verb></tscreen>
|
|
|
|
Esta señal se emite cuando se deselecciona un hijo del GtkTree. Con
|
|
GTK+ 1.0.4, esto sólo parece ocurrir en las llamadas a
|
|
<tt/gtk_tree_unselect_item()/ o a <tt/gtk_tree_unselect_child()/, y quizás
|
|
en otras ocasiones, pero <em>no</em> cuando la pulsación de un botón
|
|
deselecciona un hijo, y tampoco por la emisión de la señal «toggle»
|
|
por <tt/gtk_item_toggle()/.
|
|
|
|
<sect2> Funciones y macros<label id="sec_GtkTree_Functions">
|
|
<p>
|
|
<tscreen><verb>
|
|
guint gtk_tree_get_type( void );
|
|
</verb></tscreen>
|
|
|
|
Devuelve el identificador de tipo de `GtkTree'.
|
|
|
|
<tscreen><verb>
|
|
GtkWidget* gtk_tree_new( void );
|
|
</verb></tscreen>
|
|
|
|
Crea un nuevo objeto GtkTree. El nuevo <em>widget</em> se devuelve como
|
|
un puntero a un objeto GtkWidget. Se devolverá NULL si se produce algún
|
|
error.
|
|
|
|
<tscreen><verb>
|
|
void gtk_tree_append( GtkTree *arbol,
|
|
GtkWidget *elemento_arbol );
|
|
</verb></tscreen>
|
|
|
|
Añade un árbol a un GtkTree.
|
|
|
|
<tscreen><verb>
|
|
void gtk_tree_prepend( GtkTree *arbol,
|
|
GtkWidget *elemento_arbol );
|
|
</verb></tscreen>
|
|
|
|
Preañade un árbol a un GtkTree.
|
|
|
|
<tscreen><verb>
|
|
void gtk_tree_insert( GtkTree *arbol,
|
|
GtkWidget *elemento_arbol,
|
|
gint posicion );
|
|
</verb></tscreen>
|
|
|
|
Inserta un árbol en un GtkTree en la posición de la lista especificada
|
|
por <tt>posicion.</tt>
|
|
|
|
<tscreen><verb>
|
|
void gtk_tree_remove_items( GtkTree *arbol,
|
|
GList *items );
|
|
</verb></tscreen>
|
|
|
|
Elimina una lista de elementos (en forma de una <tt/GList */) de un
|
|
GtkTree. Eliminar un elemento de un árbol lo dereferencia (y por tanto
|
|
normalmente) lo destruye (""), a él <em>y</em> a su subárbol, de
|
|
haberlo, <em>y</em> a todos los subárboles que contenga ese
|
|
subárbol. Si quiere eliminar sólo un elemento, deberá utilizar
|
|
<tt/gtk_container_remove()/.
|
|
|
|
<tscreen><verb>
|
|
void gtk_tree_clear_items( GtkTree *arbol,
|
|
gint start,
|
|
gint end );
|
|
</verb></tscreen>
|
|
|
|
Elimina los elementos de un GtkTree desde la posición <tt>start</tt>
|
|
hasta la posición <tt>end</tt>. De nuevo hay que llevarse cuidado
|
|
con donde se aplica la dereferencia, ya que <tt/gtk_tree_clear_items()/
|
|
simplemente construye una lista y se la pasa a
|
|
<tt/gtk_tree_remove_items()/.
|
|
|
|
<tscreen><verb>
|
|
void gtk_tree_select_item( GtkTree *arbol,
|
|
gint item );
|
|
</verb></tscreen>
|
|
|
|
Emite la señal <tt/select_item/ para el hijo que se encuentra en la
|
|
posición <tt>item</tt>, y por tanto selecciona a ese hijo (a menos que
|
|
lo deseleccione en un manejador de señal...)
|
|
|
|
<tscreen><verb>
|
|
void gtk_tree_unselect_item( GtkTree *arbol,
|
|
gint item );
|
|
</verb></tscreen>
|
|
|
|
Emite la señal <tt/unselect_item/ para el hijo en la posición
|
|
<tt>item</tt>, y por tanto deselecciona al hijo.
|
|
|
|
<tscreen><verb>
|
|
void gtk_tree_select_child( GtkTree *arbol,
|
|
GtkWidget *elemento_arbol );
|
|
</verb></tscreen>
|
|
|
|
Emite la señal <tt/select_item/ para el hijo <tt>elemento_arbol</tt>, y por tanto
|
|
lo selecciona.
|
|
|
|
<tscreen><verb>
|
|
void gtk_tree_unselect_child( GtkTree *arbol,
|
|
GtkWidget *elemento_arbol );
|
|
</verb></tscreen>
|
|
|
|
Emite la señal <tt/unselect_item/ para el hijo <tt>elemento_arbol</tt>, y por
|
|
tanto lo deselecciona.
|
|
|
|
<tscreen><verb>
|
|
gint gtk_tree_child_position( GtkTree *arbol,
|
|
GtkWidget *hijo );
|
|
</verb></tscreen>
|
|
|
|
Devuelve la posición en el árbol de <tt>child</tt>, a menos que
|
|
<tt>child</tt> no esté en el árbol, en cuya caso devuelve -1.
|
|
|
|
<tscreen><verb>
|
|
void gtk_tree_set_selection_mode( GtkTree *arbol,
|
|
GtkSelectionMode mode );
|
|
</verb></tscreen>
|
|
|
|
Establece el modo de selección, que puede ser uno de los siguientes
|
|
GTK_SELECTION_SINGLE (por defecto), GTK_SELECTION_BROWSE,
|
|
GTK_SELECTION_MULTIPLE, o GTK_SELECTION_EXTENDED. Esto sólo está
|
|
definido para los árboles raíz, que es donde tiene sentido, ya que el
|
|
árbol raíz es el «propietario» de la selección. Establecer este
|
|
valor en un subárbol no tiene ningún efecto en absoluto; el valor
|
|
simplemente será ignorado.
|
|
|
|
<tscreen><verb>
|
|
void gtk_tree_set_view_mode( GtkTree *arbol,
|
|
GtkTreeViewMode mode );
|
|
</verb></tscreen>
|
|
|
|
Establece el «modo de visión», que puede ser o GTK_TREE_VIEW_LINE
|
|
(por defecto) o GTK_TREE_VIEW_ITEM. El modo de visión se propaga
|
|
de un árbol a sus subárboles, y no puede establecerse en exclusiva
|
|
para un subárbol (esto no es exacto del todo - vea los comentarios en el
|
|
código de ejemplo).
|
|
|
|
El termino «modo de visión» es algo ambiguo - básicamente, controla
|
|
la forma en que se resalta a uno de los hijos del árbol cuando es
|
|
seleccionado. Si es GTK_TREE_VIEW_LINE, se resaltará el
|
|
<em>widget</em> GtkTreeItem completo, mientras que si es
|
|
GTK_TREE_VIEW_ITEM, sólo se resaltará el <em>widget</em> hijo (es
|
|
decir, lo que normalmente es la etiqueta).
|
|
|
|
<tscreen><verb>
|
|
void gtk_tree_set_view_lines( GtkTree *arbol,
|
|
guint flag );
|
|
</verb></tscreen>
|
|
|
|
Controla si se dibujarán las líneas de conexión entre los elementos
|
|
del árbol. <tt>flag</tt> es o TRUE, en cuyo caso se dibujarán, o
|
|
FALSE, en cuyo caso no se dibujarán.
|
|
|
|
<tscreen><verb>
|
|
GtkTree *GTK_TREE (gpointer obj);
|
|
</verb></tscreen>
|
|
|
|
Convierte un puntero genérico a `GtkTree *'.
|
|
|
|
<tscreen><verb>
|
|
GtkTreeClass *GTK_TREE_CLASS (gpointer class);
|
|
</verb></tscreen>
|
|
|
|
Convierte un puntero genérico a `GtkTreeClass *'.
|
|
|
|
<tscreen><verb>
|
|
gint GTK_IS_TREE (gpointer obj);
|
|
</verb></tscreen>
|
|
|
|
Determina si un puntero genérico se refiere a un objeto `GtkTree'.
|
|
|
|
<tscreen><verb>
|
|
gint GTK_IS_ROOT_TREE (gpointer obj)
|
|
</verb></tscreen>
|
|
|
|
Determina si un puntero genérico se refiere a un objeto `GtkTree'
|
|
<em>y</em> es un árbol raíz. Aunque la función acepta cualquier
|
|
puntero, los resultados de pasarle un puntero que no se refiera
|
|
a un GtkTree no están definidos y probablemente no tengan ningún
|
|
sentido.
|
|
|
|
<tscreen><verb>
|
|
GtkTree *GTK_TREE_ROOT_TREE (gpointer obj)
|
|
</verb></tscreen>
|
|
|
|
Devuelve el árbol raíz de un puntero a un objeto `GtkTree'. Seguimos
|
|
con el mismo problema que en el caso anterior.
|
|
|
|
<tscreen><verb>
|
|
GList *GTK_TREE_SELECTION(gpointer obj)
|
|
</verb></tscreen>
|
|
|
|
Devuelve la lista de selección del árbol raíz de un objeto
|
|
`GtkTree'. Seguimos con el mismo problema que antes.
|
|
|
|
<sect1> El <em>widget</em> elemento de árbol<label id="sec_Tree_Item_Widget">
|
|
<p>
|
|
El <em>widget</em> GtkTreeItem, cómo el GtkListItem, está derivado
|
|
de GtkItem, que de nuevo, está derivado de GtkBin. Sin embargo, el
|
|
elemento en sí mismo es un contenedor genérico que contiene un
|
|
<em>widget</em> hijo, que puede ser de cualquier tipo. El <em>widget</em>
|
|
GtkTreeItem tiene ciertos campos extra, pero el único que nos
|
|
interesa ahora es el campo <em>subárbol</em>.
|
|
|
|
La definición de la estructura GtkTreeItem es así:
|
|
|
|
<tscreen><verb>
|
|
struct _GtkTreeItem
|
|
{
|
|
GtkItem item;
|
|
|
|
GtkWidget *subtree;
|
|
GtkWidget *pixmaps_box;
|
|
GtkWidget *plus_pix_widget, *minus_pix_widget;
|
|
|
|
GList *pixmaps /* nodo pixmap para esta profundidad de color */
|
|
|
|
guint expanded : 1;
|
|
};
|
|
</verb></tscreen>
|
|
|
|
El campo <tt>pixmaps_box</tt> es un GtkEventBox que caza las pulsaciones
|
|
en el símbolo más/menos que controla la expansión y contracción. El
|
|
campo <tt>pixmaps</tt> apunta a una estructura de datos interna. Ya que
|
|
siempre puede obtener el subárbol de un GtkTreeItem de una forma
|
|
(relativamente) segura mediante la macro GTK_TREE_ITEM_SUBTREE (Item),
|
|
es aconsejable no tocar las tripas de un GtkTreeItem a menos que
|
|
<em>realmente</em> sepa que es lo que está haciendo.
|
|
|
|
Ya que está derivado directamente de un GtkItem, puede tratarse como
|
|
tal utilizando la macro GTK_ITEM (ElementoArbol). Un GtkTreeItem normalmente
|
|
tiene una etiqueta, por lo que tenemos a nuestra disposición la
|
|
función gtk_list_item_new_with_label(). Podemos conseguir el mismo
|
|
efecto utilizando código como el siguiente, que por ahora es sólo
|
|
una copia de la función gtk_tree_item_new_with_label():
|
|
|
|
<tscreen><verb>
|
|
elemento_arbol = gtk_tree_item_new ();
|
|
etiqueta_widget = gtk_label_new (etiqueta);
|
|
gtk_misc_set_alignment (GTK_MISC (etiqueta_widget), 0.0, 0.5);
|
|
|
|
gtk_container_add (GTK_CONTAINER (elemento_arbol), etiqueta_widget);
|
|
gtk_widget_show (etiqueta_widget);
|
|
</verb></tscreen>
|
|
|
|
Cómo no es obligatorio añadir una GtkLabel a un GtkTreeItem, puede
|
|
también añadirle un GtkHBox o una GtkArrow, o hasta un GtkNotebook
|
|
(aunque en esos casos su aplicación no será muy popular).
|
|
|
|
Si elimina todos los elementos de un subárbol, será destruido
|
|
y se eliminará la información sobre su padre, a menos que lo
|
|
referencie de antemano, además el GtkTreeItem que sea su propietario
|
|
se colapsará. Por lo tanto, si quiere que se mantenga el subárbol
|
|
tendrá que hacer algo así:
|
|
|
|
<tscreen><verb>
|
|
gtk_widget_ref (arbol);
|
|
propietario = GTK_TREE(arbol)->tree_owner;
|
|
gtk_container_remove (GTK_CONTAINER(arbol), item);
|
|
if (arbol->parent == NULL){
|
|
gtk_tree_item_expand (GTK_TREE_ITEM(propietario));
|
|
gtk_tree_item_set_subtree (GTK_TREE_ITEM(propietario), arbol);
|
|
}
|
|
else
|
|
gtk_widget_unref (arbol);
|
|
</verb></tscreen>
|
|
|
|
Finalmente, hay que mencionar que la opción de drag-n-drop (arrastar y
|
|
soltar) <em>funciona</em> con los GtkTreeItems. Sólo tiene que
|
|
asegurarse de que el GtkTreeItem que quiere convertir en un elemento
|
|
de arrastre o en un lugar en el que, además de haber sido añadido a
|
|
GtkTree, sino que además cada su <em>widget</em> padre tiene a su vez
|
|
un padre, y así hasta llegar al nivel más alto o ventana de diálogo,
|
|
cuando llamamos a <tt/gtk_widget_dnd_drag_set()/ o
|
|
<tt/gtk_widget_dnd_drop_set()/. En caso contrario, podrían ocurrir
|
|
cosas extrañas.
|
|
|
|
<sect2> Señales
|
|
<p>
|
|
GtkTreeItem hereda las señales <tt/select/, <tt/deselect/, y
|
|
<tt/toggle/ de GtkItem. Además, añade dos señales propias, <tt/expand/
|
|
y <tt/collapse/.
|
|
|
|
<tscreen><verb>
|
|
void select( GtkItem *elemento_arbol );
|
|
</verb></tscreen>
|
|
|
|
Esta señal se emite cuando un elemento está siendo seleccionado,
|
|
o bien después de que el usuario pinche en él, o bien cuando
|
|
el programa llame a <tt/gtk_tree_item_select()/,
|
|
<tt/gtk_item_select()/, o a <tt/gtk_tree_select_child()/.
|
|
|
|
<tscreen><verb>
|
|
void deselect( GtkItem *elemento_arbol );
|
|
</verb></tscreen>
|
|
|
|
Esta señal se emite cuando un elemento está siendo deseleccionado,
|
|
o bien después de que el usuario pinche en él, o bien cuando
|
|
el programa llame a <tt/gtk_tree_item_deselect()/ o a
|
|
<tt/gtk_item_deselect()/. En el caso de GtkTreeItems, también se
|
|
emitirá por <tt/gtk_tree_unselect_child()/, y a veces por
|
|
<tt/gtk_tree_select_child()/.
|
|
|
|
<tscreen><verb>
|
|
void toggle( GtkItem *elemento_arbol );
|
|
</verb></tscreen>
|
|
|
|
Esta señal se emite cuando el programa llama a <tt/gtk_item_toggle()/. El
|
|
efecto que tiene cuando se emite en un GtkTreeItem es llamar a
|
|
<tt/gtk_tree_select_child()/ (y nunca a
|
|
<tt/gtk_tree_unselect_child()/) en el árbol padre del elemento, si el
|
|
elemento tiene un árbol padre. Si no lo tiene, entonces se cambiará el
|
|
resaltado del elemento.
|
|
|
|
<tscreen><verb>
|
|
void expand( GtkTreeItem *elemento_arbol );
|
|
</verb></tscreen>
|
|
|
|
Esta señal se emite cuando se está expandiendo el subárbol del
|
|
elemento, esto es, cuando el usuario pincha en el signo más que
|
|
hay al lado del elemento, o cuando el programa llama a
|
|
<tt/gtk_tree_item_expand()/.
|
|
|
|
<tscreen><verb>
|
|
void collapse( GtkTreeItem *elemento_arbol );
|
|
</verb></tscreen>
|
|
|
|
Esta señal se emite cuando se está contrayendo el subárbol del
|
|
elemento, esto es, cuando el usuario pincha en el signo menos que hay
|
|
al lado del elemento, o cuando el programa llama a
|
|
<tt/gtk_tree_item_collapse()/.
|
|
|
|
<sect2> Funciones y Macros
|
|
<p>
|
|
<tscreen><verb>
|
|
guint gtk_tree_item_get_type( void );
|
|
</verb></tscreen>
|
|
|
|
Devuelve el identificador de tipo de `GtkTreeItem'.
|
|
|
|
<tscreen><verb>
|
|
GtkWidget* gtk_tree_item_new( void );
|
|
</verb></tscreen>
|
|
|
|
Crea un nuevo objeto GtkTreeItem. El nuevo <em>widget</em> se devuelve
|
|
como un puntero a un objeto GtkWidget. Se devolverá NULL si hay algún
|
|
fallo.
|
|
|
|
<tscreen><verb>
|
|
GtkWidget* gtk_tree_item_new_with_label (gchar *etiqueta);
|
|
</verb></tscreen>
|
|
|
|
Crea un nuevo objeto GtkTreeItem, teniendo una simple GtkLabel
|
|
como único hijo. El nuevo <em>widget</em> se devolverá como
|
|
un puntero a un objeto GtkWidget. Se devolverá NULL en caso
|
|
de haber algún fallo.
|
|
|
|
<tscreen><verb>
|
|
void gtk_tree_item_select( GtkTreeItem *elemento_arbol );
|
|
</verb></tscreen>
|
|
|
|
Esta función es básicamente un recubrimiento de una llamada a
|
|
gtk_item_select (GTK_ITEM (elemento_arbol)) que emitirá la
|
|
señal select.
|
|
|
|
<tscreen><verb>
|
|
void gtk_tree_item_deselect( GtkTreeItem *elemento_arbol );
|
|
</verb></tscreen>
|
|
|
|
Esta función es básicamente un recubrimiento de una llamada a
|
|
gtk_item_deselect (GTK_ITEM (elemento_arbol)) que emitirá la
|
|
señal deselect.
|
|
|
|
<tscreen><verb>
|
|
void gtk_tree_item_set_subtree( GtkTreeItem *elemento_arbol,
|
|
GtkWidget *subarbol );
|
|
</verb></tscreen>
|
|
|
|
Esta función añade <tt/subarbol/ a <tt/elemento_arbol/, mostrándolo si
|
|
<tt/elemento_arbol/ está expandido, u ocultándolo si <tt/elemento_arbol/ está
|
|
contraído. De nuevo, recuerde que el <tt/elemento_arbol/ ya debe de haber
|
|
sido añadido a un árbol para que esto funcione.
|
|
|
|
<tscreen><verb>
|
|
void gtk_tree_item_remove_subtree( GtkTreeItem *elemento_arbol );
|
|
</verb></tscreen>
|
|
|
|
Esto elimina todos los hijos de los subárboles del <tt/elemento_arbol/
|
|
(esto es, dereferencia y destruye a los subárboles hijos, y a los
|
|
hijos de los hijos y...), entonces elimina el subárbol en si mismo, y
|
|
oculta el signo más/menos.
|
|
|
|
<tscreen><verb>
|
|
void gtk_tree_item_expand( GtkTreeItem *elemento_arbol );
|
|
</verb></tscreen>
|
|
|
|
Esto emite la señal «expand» para el <tt/elemento_arbol/, que lo
|
|
expande.
|
|
|
|
<tscreen><verb>
|
|
void gtk_tree_item_collapse( GtkTreeItem *elemento_arbol );
|
|
</verb></tscreen>
|
|
|
|
Esto emite la señal «collapse» en el <tt/elemento_arbol/, que lo
|
|
contrae.
|
|
|
|
<tscreen><verb>
|
|
GtkTreeItem *GTK_TREE_ITEM (gpointer obj)
|
|
</verb></tscreen>
|
|
|
|
Convierte un puntero genérico en un `GtkTreeItem *'.
|
|
|
|
<tscreen><verb>
|
|
GtkTreeItemClass *GTK_TREE_ITEM_CLASS (gpointer obj)
|
|
</verb></tscreen>
|
|
|
|
Convierte un puntero genérico en un `GtkTreeItemClass'.
|
|
|
|
<tscreen><verb>
|
|
gint GTK_IS_TREE_ITEM (gpointer obj)
|
|
</verb></tscreen>
|
|
|
|
Determina si un puntero genérico se refiere a un objeto `GtkTreeItem'.
|
|
|
|
<tscreen><verb>
|
|
GtkWidget GTK_TREE_ITEM_SUBTREE (gpointer obj)
|
|
</verb></tscreen>
|
|
|
|
Devuelve un subárbol del elemento (<tt/obj/ debería apuntar a un
|
|
objeto `GtkTreeItem').
|
|
|
|
<sect1> Árbol ejemplo
|
|
<p>
|
|
Este ejemplo es muy parecido al árbol ejemplo que hay en
|
|
<tt/testgtk.c/, pero mucho menos completo (aunque mucho mejor
|
|
comentado). Pone una ventana con un árbol, y conecta todas las señales
|
|
de los objetos relevantes, con lo que podrá ver cuando se emiten.
|
|
|
|
<!-- Hay un comentario en el código que no se traducir -->
|
|
<tscreen><verb>
|
|
/* principio del ejemplo tree tree.c */
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
/* para todas las señales GtkItem:: y GtkTreeItem:: */
|
|
static void cb_itemsignal (GtkWidget *item, gchar *signame)
|
|
{
|
|
gchar *name;
|
|
GtkLabel *etiqueta;
|
|
|
|
/* Es un GtkBin, por lo que tiene un hijo, que sabemos que es una
|
|
* etiqueta, por lo que la cogemos */
|
|
etiqueta = GTK_LABEL (GTK_BIN (item)->child);
|
|
/* Conseguimos el texto de la etiqueta */
|
|
gtk_label_get (etiqueta, &name);
|
|
/* Conseguimos el nivel del árbol en el que se encuentra el elemento */
|
|
g_print ("%s called for item %s->%p, level %d\n", signame, name,
|
|
item, GTK_TREE (item->parent)->level);
|
|
}
|
|
|
|
/* nunca se llamará a esta función */
|
|
static void cb_unselect_child (GtkWidget *arbol_raiz, GtkWidget *hijo,
|
|
GtkWidget *subarbol)
|
|
{
|
|
g_print ("unselect_child called for root tree %p, subtree %p, child %p\n",
|
|
arbol_raiz, subarbol, hijo);
|
|
}
|
|
|
|
/* Se llamará a esta función cada vez que el usuario pulse en un
|
|
* elemento, esté o no seleccionado. */
|
|
whether it is already selected or not. */
|
|
static void cb_select_child (GtkWidget *arbol_raiz, GtkWidget *hijo,
|
|
GtkWidget *subarbol)
|
|
{
|
|
g_print ("select_child called for root tree %p, subtree %p, child %p\n",
|
|
arbol_raiz, subarbol, hijo);
|
|
}
|
|
|
|
static void cb_selection_changed (GtkWidget *arbol)
|
|
{
|
|
GList *i;
|
|
|
|
g_print ("selection_change called for tree %p\n", arbol);
|
|
g_print ("selected objects are:\n");
|
|
|
|
i = GTK_TREE_SELECTION(arbol);
|
|
while (i){
|
|
gchar *name;
|
|
GtkLabel *etiqueta;
|
|
GtkWidget *item;
|
|
|
|
/* Get a GtkWidget pointer from the list node */
|
|
item = GTK_WIDGET (i->data);
|
|
etiqueta = GTK_LABEL (GTK_BIN (item)->child);
|
|
gtk_label_get (etiqueta, &name);
|
|
g_print ("\t%s on level %d\n", name, GTK_TREE
|
|
(item->parent)->level);
|
|
i = i->next;
|
|
}
|
|
}
|
|
|
|
int main (int argc, char *argv[])
|
|
{
|
|
GtkWidget *ventana, *scrolled_win, *arbol;
|
|
static gchar *itemnames[] = {"Foo", "Bar", "Baz", "Quux",
|
|
"Maurice"};
|
|
gint i;
|
|
|
|
gtk_init (&argc, &argv);
|
|
|
|
/* una ventana general */
|
|
ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
|
gtk_signal_connect (GTK_OBJECT(ventana), "delete_event",
|
|
GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
|
|
gtk_container_border_width (GTK_CONTAINER(ventana), 5);
|
|
|
|
/* una ventana con barras de desplazamiento */
|
|
scrolled_win = gtk_scrolled_window_new (NULL, NULL);
|
|
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
|
|
GTK_POLICY_AUTOMATIC,
|
|
GTK_POLICY_AUTOMATIC);
|
|
gtk_widget_set_usize (scrolled_win, 150, 200);
|
|
gtk_container_add (GTK_CONTAINER(ventana), scrolled_win);
|
|
gtk_widget_show (scrolled_win);
|
|
|
|
/* Crear el árbol raíz */
|
|
arbol = gtk_tree_new();
|
|
g_print ("root tree is %p\n", arbol);
|
|
/* connect all GtkTree:: signals */
|
|
gtk_signal_connect (GTK_OBJECT(arbol), "select_child",
|
|
GTK_SIGNAL_FUNC(cb_select_child), arbol);
|
|
gtk_signal_connect (GTK_OBJECT(arbol), "unselect_child",
|
|
GTK_SIGNAL_FUNC(cb_unselect_child), arbol);
|
|
gtk_signal_connect (GTK_OBJECT(arbol), "selection_changed",
|
|
GTK_SIGNAL_FUNC(cb_selection_changed), arbol);
|
|
/* Añadirlo a la ventana con barras de desplazamiento */
|
|
gtk_container_add (GTK_CONTAINER(scrolled_win), arbol);
|
|
/* Poner el modo de selección */
|
|
gtk_tree_set_selection_mode (GTK_TREE(arbol),
|
|
GTK_SELECTION_MULTIPLE);
|
|
/* mostrar el árbol */
|
|
gtk_widget_show (arbol);
|
|
|
|
for (i = 0; i < 5; i++){
|
|
GtkWidget *subarbol, *item;
|
|
gint j;
|
|
|
|
/* Crear un elemento del árbol */
|
|
item = gtk_tree_item_new_with_label (itemnames[i]);
|
|
/* Conectar todas las señales GtkItem:: y GtkTreeItem:: */
|
|
gtk_signal_connect (GTK_OBJECT(item), "select",
|
|
GTK_SIGNAL_FUNC(cb_itemsignal), "select");
|
|
gtk_signal_connect (GTK_OBJECT(item), "deselect",
|
|
GTK_SIGNAL_FUNC(cb_itemsignal), "deselect");
|
|
gtk_signal_connect (GTK_OBJECT(item), "toggle",
|
|
GTK_SIGNAL_FUNC(cb_itemsignal), "toggle");
|
|
gtk_signal_connect (GTK_OBJECT(item), "expand",
|
|
GTK_SIGNAL_FUNC(cb_itemsignal), "expand");
|
|
gtk_signal_connect (GTK_OBJECT(item), "collapse",
|
|
GTK_SIGNAL_FUNC(cb_itemsignal), "collapse");
|
|
/* Añadirlo al árbol padre */
|
|
gtk_tree_append (GTK_TREE(arbol), item);
|
|
/* Mostrarlo - esto se puede hacer en cualquier momento */
|
|
gtk_widget_show (item);
|
|
/* Crear el subárbol de este elemento */
|
|
subarbol = gtk_tree_new();
|
|
g_print ("-> item %s->%p, subtree %p\n", itemnames[i], item,
|
|
subarbol);
|
|
|
|
/* Esto todavía es necesario si quiere que se llamen a están
|
|
* señales en el subárbol hijo. Note that selection_change will
|
|
* be signalled for the root tree regardless. */
|
|
gtk_signal_connect (GTK_OBJECT(subarbol), "select_child",
|
|
GTK_SIGNAL_FUNC(cb_select_child), subarbol);
|
|
gtk_signal_connect (GTK_OBJECT(subarbol), "unselect_child",
|
|
GTK_SIGNAL_FUNC(cb_unselect_child), subarbol);
|
|
/* Esto no tiene absolutamente ningún efecto, ya que se ignora
|
|
* completamente en los subárboles */
|
|
gtk_tree_set_selection_mode (GTK_TREE(subarbol),
|
|
GTK_SELECTION_SINGLE);
|
|
/* Esto tampoco hace nada, pero por una razón diferente - los
|
|
* valores view_mode y view_line de un árbol se propagan a los
|
|
* subárboles cuando son mapeados. Por tanto, establecer los
|
|
* valores después actualmente tendría (algún impredecible) efecto
|
|
*/
|
|
gtk_tree_set_view_mode (GTK_TREE(subarbol), GTK_TREE_VIEW_ITEM);
|
|
/* Establecer este subárbol del elemento - ¡Recuerde que no puede
|
|
* hacerlo hasta que se haya añadido a su árbol padre! */
|
|
gtk_tree_item_set_subtree (GTK_TREE_ITEM(item), subarbol);
|
|
|
|
for (j = 0; j < 5; j++){
|
|
GtkWidget *subitem;
|
|
|
|
/* Crea un elemento subárbol, más o menos lo mismo de antes */
|
|
subitem = gtk_tree_item_new_with_label (itemnames[j]);
|
|
/* Conectar todas las señales GtkItem:: y GtkTreeItem:: */
|
|
gtk_signal_connect (GTK_OBJECT(subitem), "select",
|
|
GTK_SIGNAL_FUNC(cb_itemsignal), "select");
|
|
gtk_signal_connect (GTK_OBJECT(subitem), "deselect",
|
|
GTK_SIGNAL_FUNC(cb_itemsignal), "deselect");
|
|
gtk_signal_connect (GTK_OBJECT(subitem), "toggle",
|
|
GTK_SIGNAL_FUNC(cb_itemsignal), "toggle");
|
|
gtk_signal_connect (GTK_OBJECT(subitem), "expand",
|
|
GTK_SIGNAL_FUNC(cb_itemsignal), "expand");
|
|
gtk_signal_connect (GTK_OBJECT(subitem), "collapse",
|
|
GTK_SIGNAL_FUNC(cb_itemsignal), "collapse");
|
|
g_print ("-> -> item %s->%p\n", itemnames[j], subitem);
|
|
/* Añadirlo a su árbol padre */
|
|
gtk_tree_append (GTK_TREE(subarbol), subitem);
|
|
/* Mostrarlo */
|
|
gtk_widget_show (subitem);
|
|
}
|
|
}
|
|
|
|
/* Mostrar la ventana y entrar en el bucle final */
|
|
gtk_widget_show (ventana);
|
|
gtk_main();
|
|
return 0;
|
|
}
|
|
/* fin del ejemplo */
|
|
</verb></tscreen>
|
|
|
|
<!-- ***************************************************************** -->
|
|
<sect> El <em>widget</em> menú
|
|
<!-- ***************************************************************** -->
|
|
<p>
|
|
Hay dos formas de crear menús, la fácil, y la difícil. Ambas tienen su
|
|
utilidad, aunque lo más probable es que normalmente utilice la
|
|
menufactory (la forma fácil). La forma «difícil» consiste en crear
|
|
todos los menús utilizando las llamadas directamente. La forma fácil
|
|
consiste en utilizar las llamadas de <tt/gtk_item_factory/. Es mucho
|
|
más fácil, pero aun así cada aproximación tiene sus ventajas y sus
|
|
inconvenientes.
|
|
|
|
La menufactory es mucho más fácil de utilizar, y tambíen es más fácil
|
|
añadir nuevos menús, aunque a larga, escribiendo unas cuántas
|
|
funciones de recubrimiento para crear menús utilizando el método
|
|
manual puede acabar siendo más útil. Con la itemfactory, no es posible
|
|
añadir imágenes o el carácter `/' a los menús.
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Creación manual de menús
|
|
<p>
|
|
Siguiendo la auténtica tradición de la enseñanza, vamos a enseñarle
|
|
primero la forma difícil. <tt>:)</tt>
|
|
|
|
Se utilizan tres <em>widgets</em> para hacer una barra de menús y
|
|
submenús:
|
|
<itemize>
|
|
<item>un elemento del menú, que es lo que el usuario quiere seleccionar,
|
|
p.e. 'Guardar'
|
|
<item>un menú, que actua como un contenedor para los elementos del menú, y
|
|
<item>una barra de menú, que es un contenedor para cada uno de los menús,
|
|
</itemize>
|
|
|
|
Todo esto se complica ligeramente por el hecho de que los
|
|
<em>widgets</em> de los elementos del menú se utilizan para dos cosas
|
|
diferentes. Están los <em>widgets</em> que se empaquetan en el menú, y
|
|
los que se empaquetan en una barra de menús, que cuando se selecciona,
|
|
activa el menú.
|
|
|
|
Vamos a ver las funciones que se utilizan para crear menús y barras
|
|
de menús. ésta primera función se utiliza para crear una barra de menús.
|
|
|
|
<tscreen><verb>
|
|
GtkWidget *gtk_menu_bar_new( void );
|
|
</verb></tscreen>
|
|
|
|
Como el propio nombre indica, esta función crea una nueva barra de
|
|
menús. Utilice <tt/gtk_container_add/ para empaquetarla en una
|
|
ventana, o las funciones <tt/box_pack/ para empaquetarla en una caja -
|
|
exactamente igual que si fuesen botones.
|
|
|
|
<tscreen><verb>
|
|
GtkWidget *gtk_menu_new( void );
|
|
</verb></tscreen>
|
|
|
|
Esta función devuelve un puntero a un nuevo menú, que no se debe
|
|
mostrar nunca (no hace falta utilizar <tt/gtk_widget_show/), es sólo
|
|
un contenedor para los elementos del menú. Espero que todo esto se
|
|
aclare un poco cuando vea en el ejemplo que hay más abajo.
|
|
|
|
Las siguientes dos llamadas se utilizan para crear elementos de menú
|
|
que se empaquetarán en el menú (y en la barra de menú).
|
|
|
|
<tscreen><verb>
|
|
GtkWidget *gtk_menu_item_new( void );
|
|
</verb></tscreen>
|
|
|
|
y
|
|
|
|
<tscreen><verb>
|
|
GtkWidget *gtk_menu_item_new_with_label( const char *etiqueta );
|
|
</verb></tscreen>
|
|
|
|
Estas llamadas se utilizan para crear los elementos del menú que
|
|
van a mostrarse. Recuerde que hay que distinguir entre un «menú»
|
|
creado con <tt/gtk_menu_new/ y un «elemento del menú» creado con las
|
|
funciones <tt/gtk_menu_item_new/. El elemento de menú será un botón
|
|
con una acción asociada, y un menú será un contenedor con los
|
|
elementos del menú.
|
|
|
|
Las funciones <tt/gtk_menu_new_with_label/ y <tt/gtk_menu_new/ son
|
|
sólo lo que espera que sean después de leer lo de los botones. Una
|
|
crea un nuevo elemento del menú con una etiqueta ya dentro, y la otra
|
|
crea un elemento del menú en blanco.
|
|
|
|
Una vez ha creado un elemento del menú tiene que ponerlo en un menú.
|
|
Esto se hace utilizando la función <tt/gtk_menu_append/. Para capturar
|
|
el momento en el que el elemento se selecciona por el usuario,
|
|
necesitamos conectar con la señal <tt/activate/ de la forma usual. Por
|
|
tanto, si quiere crear un menú estándar <tt/File/, con las opciones
|
|
<tt/Open/, <tt/Save/ y <tt/Quit/ el código debería ser algo como
|
|
|
|
<tscreen><verb>
|
|
file_menu = gtk_menu_new(); /* No hay que mostrar menús */
|
|
|
|
/* Crear los elementos del menú */
|
|
open_item = gtk_menu_item_new_with_label("Open");
|
|
save_item = gtk_menu_item_new_with_label("Save");
|
|
quit_item = gtk_menu_item_new_with_label("Quit");
|
|
|
|
/* Añadirlos al menú */
|
|
gtk_menu_append( GTK_MENU(file_menu), open_item);
|
|
gtk_menu_append( GTK_MENU(file_menu), save_item);
|
|
gtk_menu_append( GTK_MENU(file_menu), quit_item);
|
|
|
|
/* Enlazar las función de llamada a la señal "activate" */
|
|
gtk_signal_connect_object( GTK_OBJECT(open_items), "activate",
|
|
GTK_SIGNAL_FUNC(menuitem_response), (gpointer) "file.open");
|
|
gtk_signal_connect_object( GTK_OBJECT(save_items), "activate",
|
|
GTK_SIGNAL_FUNC(menuitem_response), (gpointer) "file.save");
|
|
|
|
/* Podemos enlazar el elemento de menú Quit con nuestra función de
|
|
* salida */
|
|
gtk_signal_connect_object( GTK_OBJECT(quit_items), "activate",
|
|
GTK_SIGNAL_FUNC(destroy), (gpointer) "file.quit");
|
|
|
|
/* Tenemos que mostrar los elementos del menú */We do need to show menu items */
|
|
gtk_widget_show( open_item );
|
|
gtk_widget_show( save_item );
|
|
gtk_widget_show( quit_item );
|
|
</verb></tscreen>
|
|
|
|
En este momento tendremos nuestro menú. Ahora necesitamos crear una
|
|
barra de menús y un elemento de menú para el elemento <tt/File/, que
|
|
vamos a añadir a nuestro menú. El código es el siguiente
|
|
|
|
<tscreen><verb>
|
|
menu_bar = gtk_menu_bar_new();
|
|
gtk_container_add( GTK_CONTAINER(ventana), menu_bar);
|
|
gtk_widget_show( menu_bar );
|
|
|
|
file_item = gtk_menu_item_new_with_label("File");
|
|
gtk_widget_show(file_item);
|
|
</verb></tscreen>
|
|
|
|
Ahora necesitamos asociar el menú con <tt/file_item/. Esto se hace con
|
|
la función
|
|
|
|
<tscreen>
|
|
void gtk_menu_item_set_submenu( GtkMenuItem *menu_item,
|
|
GtkWidget *submenu );
|
|
</tscreen>
|
|
|
|
Por lo que nuestro ejemplo continua con
|
|
|
|
<tscreen><verb>
|
|
gtk_menu_item_set_submenu( GTK_MENU_ITEM(file_item), file_menu );
|
|
</verb></tscreen>
|
|
|
|
Todo lo que queda por hacer es añadir el menú a la barra de menús, que
|
|
se hace mediante la función
|
|
|
|
<tscreen>
|
|
void gtk_menu_bar_append( GtkMenuBar *menu_bar, GtkWidget *menu_item);
|
|
</tscreen>
|
|
|
|
que en nuestro caso habrá que utilizar así:
|
|
|
|
<tscreen><verb>
|
|
gtk_menu_bar_append( GTK_MENU_BAR (menu_bar), file_item );
|
|
</verb></tscreen>
|
|
|
|
Si queremos que el menú esté alineado a la derecha en la barra de
|
|
menús, como suele estar la opción de ayuda, podemos utilizar la
|
|
función siguiente (otra vez en <tt/file_item/ en el ejemplo actual)
|
|
antes de enlazarla en la barra de menú.
|
|
|
|
<tscreen><verb>
|
|
void gtk_menu_item_right_justify( GtkMenuItem *menu_item );
|
|
</verb></tscreen>
|
|
|
|
Aquí hay un resumen de los pasos que son necesarios para crear una
|
|
barra de menús con los menús correspondientes ya enlazados:
|
|
|
|
<itemize>
|
|
<item> Crear un nuevo menú utilizando <tt/gtk_menu_new()/
|
|
<item> Utilizar multiples llamadas a <tt/gtk_menu_item_new()/ para
|
|
cada elemento que desee tener en su menú. Y utilizar
|
|
<tt/gtk_menu_append()/ para poner cada uno de esos nuevos elementos en
|
|
el menú.
|
|
<item> Crear un elemento de menú utilizando
|
|
<tt/gtk_menu_item_new()/. Ésta será la raíz del menú, el texto que
|
|
aparezca aquí estará en la barra de menús.
|
|
<item> Utilizar <tt/gtk_menu_item_set_submenu()/ para enlazar el menú
|
|
al elemento del menú raíz (el creado en el paso anterior).
|
|
<item> Crear una nueva barra de menús utilizando
|
|
<tt/gtk_menu_bar_new/. Este paso solo necesita hacerse una vez cuando
|
|
se crea una serie de menús en una barra de menús.
|
|
<item> Utilizar <tt/gtk_menu_bar_append/ para poner el menú raíz en la
|
|
barra de menús.
|
|
</itemize>
|
|
|
|
Para hacer un menú desplegable hay que seguir prácticamente los mismos
|
|
pasos. La única diferencia es que el menú no estará conectado
|
|
`automáticamente' a una barra de menú, sino que para que aparezca
|
|
deberá llamarse explícitamente a la función <tt/gtk_menu_popup()/
|
|
utilizando, por ejemplo, un evento de pulsación de botón. Siga los
|
|
pasos siguientes:
|
|
|
|
<itemize>
|
|
<item>Cree una función manejadora de eventos. Tiene que tener el
|
|
siguiente prototipo
|
|
<tscreen>
|
|
static gint handler( GtkWidget *widget,
|
|
GdkEvent *event );
|
|
</tscreen>
|
|
|
|
y utilice el evento para encontrar donde debe aparecer el menú.
|
|
|
|
<item>En el manejador de eventos, si el evento es una pulsación de un
|
|
botón del ratón, tratar <tt>event</tt> como un evento de botón
|
|
(que lo es) y utilizarlo como se indica en el código ejemplo para
|
|
pasarle información a <tt/gtk_menu_popup()/.
|
|
<item>Enlazar este manejador de eventos con el <em>widget</em> con
|
|
<tscreen>
|
|
gtk_signal_connect_object(GTK_OBJECT(widget), "event",
|
|
GTK_SIGNAL_FUNC (handler), GTK_OBJECT(menu));
|
|
</tscreen>
|
|
donde <tt>widget</tt> es el <em>widget</em> con el que esta conectando,
|
|
<tt>handler</tt> es la función manejadora, y <tt>menu</tt> es un menú
|
|
creado con <tt/gtk_menu_new()/. Éste puede ser un menú que esté
|
|
contenido en una barra de menús, como se puede ver en el código de
|
|
ejemplo.
|
|
</itemize>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Ejemplo de la creación manual de un menú
|
|
<p>
|
|
Esto debería funcionar. Échele un vistazo al ejemplo para aclarar los
|
|
conceptos.
|
|
|
|
<tscreen><verb>
|
|
/* principio del ejemplo menu menu.c */
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
static gint button_press (GtkWidget *, GdkEvent *);
|
|
static void menuitem_response (gchar *);
|
|
|
|
int main (int argc, char *argv[])
|
|
{
|
|
|
|
GtkWidget *ventana;
|
|
GtkWidget *menu;
|
|
GtkWidget *menu_bar;
|
|
GtkWidget *root_menu;
|
|
GtkWidget *menu_items;
|
|
GtkWidget *vbox;
|
|
GtkWidget *boton;
|
|
char buf[128];
|
|
int i;
|
|
|
|
gtk_init (&argc, &argv);
|
|
|
|
/* crear una nueva ventana */
|
|
ventana = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
|
gtk_widget_set_usize( GTK_WIDGET (ventana), 200, 100);
|
|
gtk_window_set_title(GTK_WINDOW (ventana), "GTK Menu Test");
|
|
gtk_signal_connect(GTK_OBJECT (ventana), "delete_event",
|
|
(GtkSignalFunc) gtk_main_quit, NULL);
|
|
|
|
/* Inicializar el widget-menu, y recuerde -- ¡¡Nunca haga
|
|
* gtk_show_widget() con el widget menu!!
|
|
* Éste es el menú que contiene todos los elementos del menú, el
|
|
* que se desplegará cuando pulse en el "Root Menu" en la
|
|
* aplicación
|
|
*/
|
|
menu = gtk_menu_new();
|
|
|
|
/* Ahora hacemos un pequeño bucle que crea tres elementos de menú
|
|
* para "test-menu". Recuerde llamar a gtk_menu_append. Aquí
|
|
* estamos añadiendo una lista de elementos de menú a nuestro
|
|
* menú. Normalmente tendríamos que cazar aquí la señal "clicked"
|
|
* de cada uno de los elementos del menú y le deberíamos dar una
|
|
* función de llamada a cada uno, pero lo vamos a omitimos para
|
|
* ahorrar espacio. */
|
|
|
|
for(i = 0; i < 3; i++)
|
|
{
|
|
/* Copia los nombres al búfer. */
|
|
sprintf(buf, "Test-undermenu - %d", i);
|
|
|
|
/* Crea un nuevo elemento de menú con un nombre... */
|
|
menu_items = gtk_menu_item_new_with_label(buf);
|
|
|
|
/* ...y lo añade al menú. */
|
|
gtk_menu_append(GTK_MENU (menu), menu_items);
|
|
|
|
/* Hace algo interesante cuando se selecciona el menuitem */
|
|
gtk_signal_connect_object(GTK_OBJECT(menu_items), "activate",
|
|
GTK_SIGNAL_FUNC(menuitem_response), (gpointer) g_strdup(buf));
|
|
|
|
/* Muestra el widget */
|
|
gtk_widget_show(menu_items);
|
|
}
|
|
|
|
/* Ésta es el menú raíz, y será la etiqueta mostrada en la
|
|
* barra de menús. No habrá ningún manejador de señal conectado, ya que
|
|
* lo único que hace es desplegar el resto del menú. */
|
|
root_menu = gtk_menu_item_new_with_label("Root Menu");
|
|
|
|
gtk_widget_show(root_menu);
|
|
|
|
/* Ahora especificamos que queremos que el recien creado "menu"
|
|
* sea el menú para el "root menu" */
|
|
gtk_menu_item_set_submenu(GTK_MENU_ITEM (root_menu), menu);
|
|
|
|
/* Un vbox para poner dentro un menú y un botón */
|
|
vbox = gtk_vbox_new(FALSE, 0);
|
|
gtk_container_add(GTK_CONTAINER(ventana), vbox);
|
|
gtk_widget_show(vbox);
|
|
|
|
/* Crear una barra de menú para que contenga al menú y la añadamos
|
|
* a nuestra ventana principal */
|
|
menu_bar = gtk_menu_bar_new();
|
|
gtk_box_pack_start(GTK_BOX(vbox), menu_bar, FALSE, FALSE, 2);
|
|
gtk_widget_show(menu_bar);
|
|
|
|
/* Crear un botón al que atar los menús como un popup */
|
|
boton = gtk_button_new_with_label("press me");
|
|
gtk_signal_connect_object(GTK_OBJECT(boton), "event",
|
|
GTK_SIGNAL_FUNC (button_press), GTK_OBJECT(menu));
|
|
gtk_box_pack_end(GTK_BOX(vbox), boton, TRUE, TRUE, 2);
|
|
gtk_widget_show(boton);
|
|
|
|
/* Y finalmente añadimos el elemento de menú y la barra de menú --
|
|
* éste es el elemento de menú "raíz" sobre el que he estado
|
|
* delirando =) */
|
|
gtk_menu_bar_append(GTK_MENU_BAR (menu_bar), root_menu);
|
|
|
|
/* siempre mostramos la ventana como último paso para que todo se
|
|
* pongo en pantalla a la vez. */
|
|
gtk_widget_show(ventana);
|
|
|
|
gtk_main ();
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Responde a una pulsación del botón enviando un menú como un widget
|
|
* Recuerde que el argumento "widget" es el menú que se está enviando,
|
|
* NO el botón que se ha pulsado.
|
|
*/
|
|
|
|
static gint button_press (GtkWidget *widget, GdkEvent *event)
|
|
{
|
|
|
|
if (event->type == GDK_BUTTON_PRESS) {
|
|
GdkEventButton *bevent = (GdkEventButton *) event;
|
|
gtk_menu_popup (GTK_MENU(widget), NULL, NULL, NULL, NULL,
|
|
bevent->button, bevent->time);
|
|
/* Le dice al que llamó a la rutina que hemos manejado el
|
|
* evento; la historia termina aquí. */
|
|
return TRUE;
|
|
}
|
|
|
|
/* Le dice al que llamó a la rutina que no hemos manejado el
|
|
* evento. */
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/* Imprime una cadena cuando se selecciona un elemento del menú */
|
|
|
|
static void menuitem_response (gchar *string)
|
|
{
|
|
printf("%s\n", string);
|
|
}
|
|
/* final del ejemplo */
|
|
</verb></tscreen>
|
|
|
|
También puede hacer que un elemento del menú sea insensible y, utilizando
|
|
una tabla de teclas aceleradoras, conectar las teclas con las funciones
|
|
del menú.
|
|
|
|
<!-- XXX Las dos sect1 que vienen han cambiado -->
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Utilizando GtkItemFactory
|
|
<p>
|
|
Ahora que le hemos enseñado la forma difícil, le mostraremos como
|
|
utilizar las llamadas <tt/gtk_item_factory/.
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Ejemplo de la fábrica de elementos
|
|
<p>
|
|
Aquí hay un ejemplo de cómo utilizar la fábrica de elementos
|
|
GTK.
|
|
|
|
<tscreen><verb>
|
|
/* principio del ejemplo menu itemfactory.h */
|
|
|
|
#include <gtk/gtk.h>
|
|
#include <strings.h>
|
|
|
|
/* La obligatoria función de llamada */
|
|
static void print_hello( GtkWidget *w,
|
|
gpointer data )
|
|
{
|
|
g_message ("Hello, World!\n");
|
|
}
|
|
|
|
/* Esta es la estructura GtkItemFactoryEntry utilizada para crear
|
|
nuevos menúes.
|
|
|
|
This is the GtkItemFactoryEntry structure used to generate new menus.
|
|
Elemento 1: La dirección del menú. La letra que hay
|
|
después del subrayado indica una tecla aceleradora
|
|
una vez que el menú esté abierto.
|
|
Elemento 2: La tecla aceleradora para la entrada del menú.
|
|
Elemento 3: La función de llamada.
|
|
Elemento 4: La acción de llamada. Cambia los parámetros que
|
|
se le pasan a la función de llamada. El valor por
|
|
defecto es 0.
|
|
Elemento 5: El tipo de elemento, se utiliza para definir de que
|
|
tipo de elemento se trata. Los valores posibles son:
|
|
|
|
NULL -> "<Item>"
|
|
"" -> "<Item>"
|
|
"<Title>" -> crea un elemento título
|
|
"<Item>" -> crea un simple elemento
|
|
"<CheckItem>" -> crea un elemento de comprobación
|
|
"<ToggleItem>" -> crea un elemento de selección
|
|
"<RadioItem>" -> crea un elemento circular
|
|
<path> -> dirección de un elemento circular
|
|
con el que enlazar
|
|
"<Separator>" -> crea un separador
|
|
"<Branch>" -> crea un elemento para contener
|
|
subelementos (de forma opcional)
|
|
"<LastBranch>" -> crea una rama justificada a la
|
|
derecha
|
|
*/
|
|
|
|
static GtkItemFactoryEntry menu_items[] = {
|
|
{ "/_File", NULL, NULL, 0, "<Branch>" },
|
|
{ "/File/_New", "<control>N", print_hello, 0, NULL },
|
|
{ "/File/_Open", "<control>O", print_hello, 0, NULL },
|
|
{ "/File/_Save", "<control>S", print_hello, 0, NULL },
|
|
{ "/File/Save _As", NULL, NULL, 0, NULL },
|
|
{ "/File/sep1", NULL, NULL, 0, "<Separator>" },
|
|
{ "/File/Quit", "<control>Q", gtk_main_quit, 0, NULL },
|
|
{ "/_Options", NULL, NULL, 0, "<Branch>" },
|
|
{ "/Options/Test", NULL, NULL, 0, NULL },
|
|
{ "/_Help", NULL, NULL, 0, "<LastBranch>" },
|
|
{ "/_Help/About", NULL, NULL, 0, NULL },
|
|
};
|
|
|
|
|
|
void get_main_menu( GtkWidget *ventana,
|
|
GtkWidget **menubar )
|
|
{
|
|
GtkItemFactory *item_factory;
|
|
GtkAccelGroup *accel_group;
|
|
gint nmenu_items = sizeof (menu_items) / sizeof (menu_items[0]);
|
|
|
|
accel_group = gtk_accel_group_new ();
|
|
|
|
/* Esta función inicializa la fábrica de elementos
|
|
Param 1: El tipo de menú - puede ser GTK_TYPE_MENU_BAR,
|
|
GTK_TYPE_MENU, o GTK_TYPE_OPTION_MENU.
|
|
Param 2: La dirección del menú.
|
|
Param 3: Un puntero a un gtk_accel_group. La fábrica de
|
|
elementos actualiza la tabla de teclas aceleradoras
|
|
mientras genera los menúes.
|
|
*/
|
|
|
|
item_factory = gtk_item_factory_new (GTK_TYPE_MENU_BAR, "<main>",
|
|
accel_group);
|
|
|
|
/* Esta función genera los elementos de menú. Pasa la
|
|
fábrica de elementos (item_factory), el número de elementos
|
|
del vector, el vector en sí, y cualquier dato de llamada para
|
|
los elementos de menú. */
|
|
gtk_item_factory_create_items (item_factory, nmenu_items, menu_items, NULL);
|
|
|
|
/* Enlaza el nuevo grupo acelerador a la ventana. */
|
|
gtk_accel_group_attach (accel_group, GTK_OBJECT (ventana));
|
|
|
|
if (menubar)
|
|
/* Finalmente, devuelve la barra de menú creada por la
|
|
* fábrica de elementos. */
|
|
*menubar = gtk_item_factory_get_widget (item_factory, "<main>");
|
|
}
|
|
|
|
int main( int argc,
|
|
char *argv[] )
|
|
{
|
|
GtkWidget *ventana;
|
|
GtkWidget *main_vbox;
|
|
GtkWidget *menubar;
|
|
|
|
gtk_init (&argc, &argv);
|
|
|
|
ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
|
gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
|
|
GTK_SIGNAL_FUNC (gtk_main_quit),
|
|
"WM destroy");
|
|
gtk_window_set_title (GTK_WINDOW(ventana), "Item Factory");
|
|
gtk_widget_set_usize (GTK_WIDGET(ventana), 300, 200);
|
|
|
|
main_vbox = gtk_vbox_new (FALSE, 1);
|
|
gtk_container_border_width (GTK_CONTAINER (main_vbox), 1);
|
|
gtk_container_add (GTK_CONTAINER (ventana), main_vbox);
|
|
gtk_widget_show (main_vbox);
|
|
|
|
get_main_menu (ventana, &menubar);
|
|
gtk_box_pack_start (GTK_BOX (main_vbox), menubar, FALSE, TRUE, 0);
|
|
gtk_widget_show (menubar);
|
|
|
|
gtk_widget_show (ventana);
|
|
gtk_main ();
|
|
|
|
return(0);
|
|
}
|
|
/* fin del ejemplo */
|
|
</verb></tscreen>
|
|
|
|
Por ahora, sólo está este ejemplo. Ya llegará una
|
|
explicación del mismo y más comentarios.
|
|
|
|
<!-- ***************************************************************** -->
|
|
<sect> El <em>widget</em> texto
|
|
<!-- ***************************************************************** -->
|
|
<p>
|
|
El <em>widget</em> texto permite mostrar y editar multiples líneas de
|
|
texto. Admite texto en varios colores y con varios tipos de letra,
|
|
permitiendo mezclarlos de cualquier forma que desee. También hay un
|
|
gran número de teclas para la edición de textos, que son compatibles
|
|
con Emacs.
|
|
|
|
El <em>widget</em> texto admite copiar-y-pegar, incluyendo la
|
|
utilización de doble y triple-click para seleccionar una palabra y una
|
|
línea completa, respectivamente.
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Creando y configurando un cuadro de texto
|
|
<p>
|
|
Sólo hay una función para crear un nuevo <em>widget</em> texto.
|
|
<tscreen><verb>
|
|
GtkWidget *gtk_text_new( GtkAdjustment *hadj,
|
|
GtkAdjustment *vadj );
|
|
</verb></tscreen>
|
|
|
|
Los argumentos nos permitirán dar al <em>widget</em> texto punteros a
|
|
<tt/GtkAdjustement/ que pueden ser utilizados para controlar la visión
|
|
de la posición del <em>widget</em>. Si le ponemos un valor NULL en
|
|
cualquiera de los dos argumentos (o en los dos), la función
|
|
<tt/gtk_text_new/ creará su propio ajuste.
|
|
|
|
<tscreen><verb>
|
|
void gtk_text_set_adjustments( GtkText *text,
|
|
GtkAdjustment *hadj,
|
|
GtkAdjustment *vadj );
|
|
</verb></tscreen>
|
|
|
|
La función de arriba permite cambiar en cualquier momento el ajuste
|
|
horizontal y vertical de un <em>widget</em> texto.
|
|
|
|
El <em>widget</em> texto no crea automáticamente sus propiar barras
|
|
de desplazamiento cuando el texto a mostrar es demasiado largo
|
|
para la ventana en la que se encuentra. Tenemos que crearlas y
|
|
añadirlas a la capa del <em>display</em> nosotros mismos.
|
|
|
|
<tscreen><verb>
|
|
vscrollbar = gtk_vscrollbar_new (GTK_TEXT(text)->vadj);
|
|
gtk_box_pack_start(GTK_BOX(hbox), vscrollbar, FALSE, FALSE, 0);
|
|
gtk_widget_show (vscrollbar);
|
|
</verb></tscreen>
|
|
|
|
El trozo de código de arriba crea una nueva barra de desplazamiento
|
|
vertical, y la conecta con el ajuste vertical del <em>widget</em>
|
|
de texto, <tt/text/. Entonces la empaqueta en un cuadro de la forma
|
|
usual.
|
|
|
|
Observe que, actualmente el <em>widget</em> GtkText no admite barras
|
|
de desplazamiento horizontal.
|
|
|
|
Principalmente hay dos maneras de utilizar un <em>widget</em> de
|
|
texto: permitiendo al usuario editar el texto, o permitiéndonos
|
|
mostrar varias líneas de texto al usuario. Para cambiar entre estos
|
|
dos modos de operación, el <em>widget</em> de texto tiene las
|
|
siguientes funciones:
|
|
|
|
<tscreen><verb>
|
|
void gtk_text_set_editable( GtkText *text,
|
|
gint editable );
|
|
</verb></tscreen>
|
|
|
|
El argumento <tt/editable/ es un valor TRUE o FALSE que especifica si se
|
|
permite al usuario editar los contenidos del <em>widget</em> texto.
|
|
Cuando el <em>widget</em> texto se pueda editar, mostrará un cursor
|
|
en la posición actual de inserción.
|
|
|
|
Sin embargo la utilización del <em>widget</em> en estos dos modos no
|
|
es algo permanente, ya que puede cambiar el estado editable del
|
|
<em>widget</em> texto e insertar texto en cualquier momento.
|
|
|
|
El <em>widget</em> texto corta las líneas de texto que son demasiado
|
|
largas para que quepan en una sólo línea en la ventana. Su
|
|
comportamiento por defecto es cortar las palabras donde se terminan
|
|
las líneas. Esto puede cambiarse utilizando la siguiente función:
|
|
|
|
<tscreen><verb>
|
|
void gtk_text_set_word_wrap( GtkText *text,
|
|
gint word_wrap );
|
|
</verb></tscreen>
|
|
|
|
Utilizando esta función podremos especificar que el <em>widget</em>
|
|
texto debería cortar las líneas largas en los límites de las
|
|
palabras. El argumento <tt/word_wrap/ es un valor TRUE o FALSE.
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Manipulación de texto
|
|
<P>
|
|
El punto actual de inserción en un <em>widget</em> texto puede
|
|
cambiarse utilizando
|
|
<tscreen><verb>
|
|
void gtk_text_set_point( GtkText *text,
|
|
guint index );
|
|
</verb></tscreen>
|
|
|
|
donde <tt/index/ es la posición en la que poner el punto de inserción.
|
|
|
|
Análogamente tenemos la función para obtener la posición del punto
|
|
de inserción:
|
|
|
|
<tscreen><verb>
|
|
guint gtk_text_get_point( GtkText *text );
|
|
</verb></tscreen>
|
|
|
|
Una función que es útil en combinación con las dos anteriores es
|
|
|
|
<tscreen><verb>
|
|
guint gtk_text_get_length( GtkText *text );
|
|
</verb></tscreen>
|
|
|
|
que devuelve la longitud actual del <em>widget</em> texto. La
|
|
longitud es el número de caracteres que hay en el bloque de texto,
|
|
incluyendo caracteres como el retorno de carro, que marca el final de
|
|
las líneas.
|
|
|
|
Para insertar texto en la posición actual del cursor, tendrá que
|
|
utilizar la función <tt/gtk_text_insert/, que nos permitirá
|
|
especificar los colores de fondo y de la letra y un tipo de letra para
|
|
el texto.
|
|
|
|
<tscreen><verb>
|
|
void gtk_text_insert( GtkText *text,
|
|
GdkFont *font,
|
|
GdkColor *fore,
|
|
GdkColor *back,
|
|
const char *chars,
|
|
gint length );
|
|
</verb></tscreen>
|
|
|
|
Pasar un valor <tt/NULL/ como el color de la letra (<tt/fore/), el
|
|
color de fondo (<tt/back/) o el tipo de letra (<tt/font/) hará que
|
|
se utilicen los valores que indiquen el estilo del <em>widget</em>.
|
|
Utilizar un valor de <tt/-1/ para el parámetro <tt/length/ hará
|
|
que se inserte todo el texto.
|
|
|
|
El <em/widget/ texto es uno de los pocos de GTK que se redibuja
|
|
a sí mismo dinámicamente, fuera de la función <tt/gtk_main/. Esto
|
|
significa que todos los cambios en el contenido del <em/widget/ texto
|
|
se manifestarán inmediatamente. Para permitirnos realizar varias
|
|
actualizaciones del <em/widget/ de texto sin que se redibuje
|
|
continuamente, podemos congelar el <em/widget/, lo que hará que pare
|
|
momentaneamente de redibujarse a sí mismo cada vez que haya algún
|
|
cambio. Podemos descongelarlo cuando hayamos acabado con nuestras
|
|
actualizaciones.
|
|
|
|
Las siguientes dos funciones realizarán la acción de congelar y
|
|
descongelar el <em/widget/:
|
|
|
|
<tscreen><verb>
|
|
void gtk_text_freeze( GtkText *text );
|
|
|
|
void gtk_text_thaw( GtkText *text );
|
|
</verb></tscreen>
|
|
|
|
Se puede borrar el texto que se encuentra en el punto actual de
|
|
inserción del <em/widget/ de texto mediante dos funciones. El valor
|
|
devuelto es TRUE o FALSE en función del éxito de la operación.
|
|
|
|
<tscreen><verb>
|
|
gint gtk_text_backward_delete( GtkText *text,
|
|
guint nchars );
|
|
|
|
gint gtk_text_forward_delete ( GtkText *text,
|
|
guint nchars );
|
|
</verb></tscreen>
|
|
|
|
Si quiere recuperar el contenido del <em/widget/ de texto, entonces
|
|
la macro <tt/GTK_TEXT_INDEX(t, index)/ le permitirá obtener el
|
|
carácter que se encuentra en la posición <tt/index/ del <em/widget/
|
|
de texto <tt/t/.
|
|
|
|
Para obtener mayores bloques de texto, podemos utilizar la función
|
|
|
|
<tscreen><verb>
|
|
gchar *gtk_editable_get_chars( GtkEditable *editable,
|
|
gint start_pos,
|
|
gint end_pos );
|
|
</verb></tscreen>
|
|
|
|
Esta es una función de la clase padre del <em/widget/ texto. Un valor
|
|
de -1 en <tt/end_pos/ significa el final del texto. El índice del
|
|
texto empieza en 0.
|
|
|
|
La función reserva un espacio de memoria para el bloque de texto,
|
|
por lo que no debe olvidarse de liberarlo con una llamada a
|
|
<tt/g_free/ cuando haya acabado el bloque.
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Atajos por teclado
|
|
<p>
|
|
El <em/widget/ texto tiene ciertos atajos de teclado preinstalados
|
|
para las funciones de edición estándar, movimiento y selección. Pueden
|
|
utilizarse mediante combinaciones de las teclas Control y Alt.
|
|
|
|
Además, si se mantiene apretada la tecla de Control y se utilizan las
|
|
teclas de movimiento, el cursor se moverá por palabras en lugar de por
|
|
caracteres. Manteniendo apretada la tecla Shift, las teclas de movimiento
|
|
harán que se extienda la selección.
|
|
|
|
<sect2>Atajos para el movimiento
|
|
<p>
|
|
<itemize>
|
|
<item> Ctrl-A Principio de línea
|
|
<item> Ctrl-E Final de línea
|
|
<item> Ctrl-N Línea siguiente
|
|
<item> Ctrl-P Línea anterior
|
|
<item> Ctrl-B Retrasarse un carácter
|
|
<item> Ctrl-F Adelantarse un carácter
|
|
<item> Alt-B Retrasarse una palabra
|
|
<item> Alt-F Adelantarse una palabra
|
|
</itemize>
|
|
|
|
<sect2>Atajos para la edición
|
|
<p>
|
|
<itemize>
|
|
<item> Ctrl-H Borrar el carácter anterior (Backspace)
|
|
<item> Ctrl-D Borrar el carácter siguiente (Suprimir)
|
|
<item> Ctrl-W Borrar la palabra anterior
|
|
<item> Alt-D Borrar la palabra siguiente
|
|
<item> Ctrl-K Borrar hasta el fin de la línea
|
|
<item> Ctrl-U Borrar la línea
|
|
</itemize>
|
|
|
|
<sect2>Atajos de selección
|
|
<p>
|
|
<itemize>
|
|
<item> Ctrl-X Cortar al portapapeles
|
|
<item> Ctrl-C Copiar al portapapeles
|
|
<item> Ctrl-V Pegar desde el portapapeles
|
|
</itemize>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Un ejemplo de GtkText
|
|
<p>
|
|
<tscreen><verb>
|
|
/* principio del ejemplo text text.c */
|
|
|
|
/* text.c */
|
|
|
|
#include <stdio.h>
|
|
#include <gtk/gtk.h>
|
|
|
|
void text_toggle_editable (GtkWidget *checkbutton,
|
|
GtkWidget *text)
|
|
{
|
|
gtk_text_set_editable(GTK_TEXT(text),
|
|
GTK_TOGGLE_BUTTON(checkbutton)->active);
|
|
}
|
|
|
|
void text_toggle_word_wrap (GtkWidget *checkbutton,
|
|
GtkWidget *text)
|
|
{
|
|
gtk_text_set_word_wrap(GTK_TEXT(text),
|
|
GTK_TOGGLE_BUTTON(checkbutton)->active);
|
|
}
|
|
|
|
void close_application( GtkWidget *widget, gpointer data )
|
|
{
|
|
gtk_main_quit();
|
|
}
|
|
|
|
int main (int argc, char *argv[])
|
|
{
|
|
GtkWidget *ventana;
|
|
GtkWidget *caja1;
|
|
GtkWidget *caja2;
|
|
GtkWidget *hbox;
|
|
GtkWidget *boton;
|
|
GtkWidget *check;
|
|
GtkWidget *separator;
|
|
GtkWidget *table;
|
|
GtkWidget *vscrollbar;
|
|
GtkWidget *text;
|
|
GdkColormap *cmap;
|
|
GdkColor colour;
|
|
GdkFont *fixed_font;
|
|
|
|
FILE *infile;
|
|
|
|
gtk_init (&argc, &argv);
|
|
|
|
ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
|
gtk_widget_set_usize (ventana, 600, 500);
|
|
gtk_window_set_policy (GTK_WINDOW(ventana), TRUE, TRUE, FALSE);
|
|
gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
|
|
GTK_SIGNAL_FUNC(close_application),
|
|
NULL);
|
|
gtk_window_set_title (GTK_WINDOW (ventana), "Text Widget Example");
|
|
gtk_container_border_width (GTK_CONTAINER (ventana), 0);
|
|
|
|
|
|
caja1 = gtk_vbox_new (FALSE, 0);
|
|
gtk_container_add (GTK_CONTAINER (ventana), caja1);
|
|
gtk_widget_show (caja1);
|
|
|
|
|
|
caja2 = gtk_vbox_new (FALSE, 10);
|
|
gtk_container_border_width (GTK_CONTAINER (caja2), 10);
|
|
gtk_box_pack_start (GTK_BOX (caja1), caja2, TRUE, TRUE, 0);
|
|
gtk_widget_show (caja2);
|
|
|
|
|
|
table = gtk_table_new (2, 2, FALSE);
|
|
gtk_table_set_row_spacing (GTK_TABLE (table), 0, 2);
|
|
gtk_table_set_col_spacing (GTK_TABLE (table), 0, 2);
|
|
gtk_box_pack_start (GTK_BOX (caja2), table, TRUE, TRUE, 0);
|
|
gtk_widget_show (table);
|
|
|
|
/* Crear el widget GtkText */
|
|
text = gtk_text_new (NULL, NULL);
|
|
gtk_text_set_editable (GTK_TEXT (text), TRUE);
|
|
gtk_table_attach (GTK_TABLE (table), text, 0, 1, 0, 1,
|
|
GTK_EXPAND | GTK_SHRINK | GTK_FILL,
|
|
GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
|
|
gtk_widget_show (text);
|
|
|
|
/* Añadir una barra de desplazamiento vertical al widget GtkText */
|
|
vscrollbar = gtk_vscrollbar_new (GTK_TEXT (text)->vadj);
|
|
gtk_table_attach (GTK_TABLE (table), vscrollbar, 1, 2, 0, 1,
|
|
GTK_FILL, GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
|
|
gtk_widget_show (vscrollbar);
|
|
|
|
/* Obtener el mapa de colores del sistema y conseguir el color rojo */
|
|
cmap = gdk_colormap_get_system();
|
|
colour.red = 0xffff;
|
|
colour.green = 0;
|
|
colour.blue = 0;
|
|
if (!gdk_color_alloc(cmap, &colour)) {
|
|
g_error("couldn't allocate colour");
|
|
}
|
|
|
|
/* Cargar un fuente de tamaño fijo */
|
|
fixed_font = gdk_font_load ("-misc-fixed-medium-r-*-*-*-140-*-*-*-*-*-*");
|
|
|
|
/* Al enviar la señal relize a un widget se crea una ventana para el
|
|
* mismo, y nos permite insertar texto */
|
|
gtk_widget_realize (text);
|
|
|
|
/* Congela el widget text, lo que nos permite hacer varias
|
|
* actualizaciones */
|
|
gtk_text_freeze (GTK_TEXT (text));
|
|
|
|
/* Insertamos algún texto coloreado */
|
|
gtk_text_insert (GTK_TEXT (text), NULL, &text->style->black, NULL,
|
|
"Supports ", -1);
|
|
gtk_text_insert (GTK_TEXT (text), NULL, &colour, NULL,
|
|
"colored ", -1);
|
|
gtk_text_insert (GTK_TEXT (text), NULL, &text->style->black, NULL,
|
|
"text and different ", -1);
|
|
gtk_text_insert (GTK_TEXT (text), fixed_font, &text->style->black, NULL,
|
|
"fonts\n\n", -1);
|
|
|
|
/* Cargamos el fichero text.c en la ventana de texto */
|
|
|
|
infile = fopen("text.c", "r");
|
|
|
|
if (infile) {
|
|
char buffer[1024];
|
|
int nchars;
|
|
|
|
while (1)
|
|
{
|
|
nchars = fread(buffer, 1, 1024, infile);
|
|
gtk_text_insert (GTK_TEXT (text), fixed_font, NULL,
|
|
NULL, buffer, nchars);
|
|
|
|
if (nchars < 1024)
|
|
break;
|
|
}
|
|
|
|
fclose (infile);
|
|
}
|
|
|
|
/* Descongelamos el widget text, permitiéndonos ver todos los
|
|
* cambios */
|
|
gtk_text_thaw (GTK_TEXT (text));
|
|
|
|
hbox = gtk_hbutton_box_new ();
|
|
gtk_box_pack_start (GTK_BOX (caja2), hbox, FALSE, FALSE, 0);
|
|
gtk_widget_show (hbox);
|
|
|
|
check = gtk_check_button_new_with_label("Editable");
|
|
gtk_box_pack_start (GTK_BOX (hbox), check, FALSE, FALSE, 0);
|
|
gtk_signal_connect (GTK_OBJECT(check), "toggled",
|
|
GTK_SIGNAL_FUNC(text_toggle_editable), text);
|
|
gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(check), TRUE);
|
|
gtk_widget_show (check);
|
|
check = gtk_check_button_new_with_label("Wrap Words");
|
|
gtk_box_pack_start (GTK_BOX (hbox), check, FALSE, TRUE, 0);
|
|
gtk_signal_connect (GTK_OBJECT(check), "toggled",
|
|
GTK_SIGNAL_FUNC(text_toggle_word_wrap), text);
|
|
gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(check), FALSE);
|
|
gtk_widget_show (check);
|
|
|
|
separator = gtk_hseparator_new ();
|
|
gtk_box_pack_start (GTK_BOX (caja1), separator, FALSE, TRUE, 0);
|
|
gtk_widget_show (separator);
|
|
|
|
caja2 = gtk_vbox_new (FALSE, 10);
|
|
gtk_container_border_width (GTK_CONTAINER (caja2), 10);
|
|
gtk_box_pack_start (GTK_BOX (caja1), caja2, FALSE, TRUE, 0);
|
|
gtk_widget_show (caja2);
|
|
|
|
boton = gtk_button_new_with_label ("close");
|
|
gtk_signal_connect (GTK_OBJECT (boton), "clicked",
|
|
GTK_SIGNAL_FUNC(close_application),
|
|
NULL);
|
|
gtk_box_pack_start (GTK_BOX (caja2), boton, TRUE, TRUE, 0);
|
|
GTK_WIDGET_SET_FLAGS (boton, GTK_CAN_DEFAULT);
|
|
gtk_widget_grab_default (boton);
|
|
gtk_widget_show (boton);
|
|
|
|
gtk_widget_show (ventana);
|
|
|
|
gtk_main ();
|
|
|
|
return 0;
|
|
}
|
|
/* fin del ejemplo */
|
|
</verb></tscreen>
|
|
|
|
<!-- ***************************************************************** -->
|
|
<sect> Los <em>widgets</em> no documentados
|
|
<!-- ***************************************************************** -->
|
|
<p>
|
|
¡Todos ellos necesitan de gente que los documente! :) Por favor,
|
|
considere el contribuir a nuestro tutorial.
|
|
|
|
Si debe utilizar uno de estos <em/widgets/, que permanecen no
|
|
documentados, le sugiero que le eche un vistazo a su fichero de
|
|
cabecera respectivo en la distribución GTK. Los nombre de las
|
|
funciones GTK son muy descriptivos. Una vez haya comprendido como
|
|
funcionan las cosas, no le será difícil ver como hay que utilizar un
|
|
<em/widget/ simplemente mirando su declaración de funciones. Con esto,
|
|
y unos cuántos ejemplos del código de otros, no debería tener
|
|
problemas.
|
|
|
|
Cuando haya comprendido todas las funciones de un nuevo <em/widget/
|
|
no documentado, por favor considere el hecho de escribir un tutorial
|
|
para él, para que así otros se puedan beneficiar del tiempo que usted
|
|
gastó.
|
|
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1> Calendar
|
|
<p>
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1> CTree
|
|
<p>
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1> Curves
|
|
<p>
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1> Drawing Area
|
|
<p>
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1> Font Selection Dialog
|
|
<p>
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1> Gamma Curve
|
|
<p>
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1> Image
|
|
<p>
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1> Packer
|
|
<p>
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1> Plugs and Sockets
|
|
<p>
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1> Preview
|
|
<p>
|
|
|
|
<!--
|
|
|
|
(This may need to be rewritten to follow the style of the rest of the tutorial)
|
|
|
|
<tscreen><verb>
|
|
|
|
Previews serve a number of purposes in GIMP/GTK. The most important one is
|
|
this. High quality images may take up to tens of megabytes of memory - easy!
|
|
Any operation on an image that big is bound to take a long time. If it takes
|
|
you 5-10 trial-and-errors (i.e. 10-20 steps, since you have to revert after
|
|
you make an error) to choose the desired modification, it make take you
|
|
literally hours to make the right one - if you don't run out of memory
|
|
first. People who have spent hours in color darkrooms know the feeling.
|
|
Previews to the rescue!
|
|
|
|
But the annoyance of the delay is not the only issue. Oftentimes it is
|
|
helpful to compare the Before and After versions side-by-side or at least
|
|
back-to-back. If you're working with big images and 10 second delays,
|
|
obtaining the Before and After impressions is, to say the least, difficult.
|
|
For 30M images (4"x6", 600dpi, 24 bit) the side-by-side comparison is right
|
|
out for most people, while back-to-back is more like back-to-1001, 1002,
|
|
..., 1010-back! Previews to the rescue!
|
|
|
|
But there's more. Previews allow for side-by-side pre-previews. In other
|
|
words, you write a plug-in (e.g. the filterpack simulation) which would have
|
|
a number of here's-what-it-would-look-like-if-you-were-to-do-this previews.
|
|
An approach like this acts as a sort of a preview palette and is very
|
|
effective for subtle changes. Let's go previews!
|
|
|
|
There's more. For certain plug-ins real-time image-specific human
|
|
intervention maybe necessary. In the SuperNova plug-in, for example, the
|
|
user is asked to enter the coordinates of the center of the future
|
|
supernova. The easiest way to do this, really, is to present the user with a
|
|
preview and ask him to interactively select the spot. Let's go previews!
|
|
|
|
Finally, a couple of misc uses. One can use previews even when not working
|
|
with big images. For example, they are useful when rendering complicated
|
|
patterns. (Just check out the venerable Diffraction plug-in + many other
|
|
ones!) As another example, take a look at the colormap rotation plug-in
|
|
(work in progress). You can also use previews for little logos inside you
|
|
plug-ins and even for an image of yourself, The Author. Let's go previews!
|
|
|
|
When Not to Use Previews
|
|
|
|
Don't use previews for graphs, drawing etc. GDK is much faster for that. Use
|
|
previews only for rendered images!
|
|
|
|
Let's go previews!
|
|
|
|
You can stick a preview into just about anything. In a vbox, an hbox, a
|
|
table, a button, etc. But they look their best in tight frames around them.
|
|
Previews by themselves do not have borders and look flat without them. (Of
|
|
course, if the flat look is what you want...) Tight frames provide the
|
|
necessary borders.
|
|
|
|
[Image][Image]
|
|
|
|
Previews in many ways are like any other widgets in GTK (whatever that
|
|
means) except they possess an additional feature: they need to be filled with
|
|
some sort of an image! First, we will deal exclusively with the GTK aspect
|
|
of previews and then we'll discuss how to fill them.
|
|
|
|
GtkWidget *preview!
|
|
|
|
Without any ado:
|
|
|
|
/* Create a preview widget,
|
|
set its size, an show it */
|
|
GtkWidget *preview;
|
|
preview=gtk_preview_new(GTK_PREVIEW_COLOR)
|
|
/*Other option:
|
|
GTK_PREVIEW_GRAYSCALE);*/
|
|
gtk_preview_size (GTK_PREVIEW (preview), WIDTH, HEIGHT);
|
|
gtk_widget_show(preview);
|
|
my_preview_rendering_function(preview);
|
|
|
|
Oh yeah, like I said, previews look good inside frames, so how about:
|
|
|
|
GtkWidget *create_a_preview(int Width,
|
|
int Height,
|
|
int Colorfulness)
|
|
{
|
|
GtkWidget *preview;
|
|
GtkWidget *frame;
|
|
|
|
frame = gtk_frame_new(NULL);
|
|
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
|
|
gtk_container_set_border_width (GTK_CONTAINER(frame),0);
|
|
gtk_widget_show(frame);
|
|
|
|
preview=gtk_preview_new (Colorfulness?GTK_PREVIEW_COLOR
|
|
:GTK_PREVIEW_GRAYSCALE);
|
|
gtk_preview_size (GTK_PREVIEW (preview), Width, Height);
|
|
gtk_container_add(GTK_CONTAINER(frame),preview);
|
|
gtk_widget_show(preview);
|
|
|
|
my_preview_rendering_function(preview);
|
|
return frame;
|
|
}
|
|
|
|
That's my basic preview. This routine returns the "parent" frame so you can
|
|
place it somewhere else in your interface. Of course, you can pass the
|
|
parent frame to this routine as a parameter. In many situations, however,
|
|
the contents of the preview are changed continually by your application. In
|
|
this case you may want to pass a pointer to the preview to a
|
|
"create_a_preview()" and thus have control of it later.
|
|
|
|
One more important note that may one day save you a lot of time. Sometimes
|
|
it is desirable to label you preview. For example, you may label the preview
|
|
containing the original image as "Original" and the one containing the
|
|
modified image as "Less Original". It might occur to you to pack the
|
|
preview along with the appropriate label into a vbox. The unexpected caveat
|
|
is that if the label is wider than the preview (which may happen for a
|
|
variety of reasons unforseeable to you, from the dynamic decision on the
|
|
size of the preview to the size of the font) the frame expands and no longer
|
|
fits tightly over the preview. The same problem can probably arise in other
|
|
situations as well.
|
|
|
|
[Image]
|
|
|
|
The solution is to place the preview and the label into a 2x1 table and by
|
|
attaching them with the following parameters (this is one possible variations
|
|
of course. The key is no GTK_FILL in the second attachment):
|
|
|
|
gtk_table_attach(GTK_TABLE(table),label,0,1,0,1,
|
|
0,
|
|
GTK_EXPAND|GTK_FILL,
|
|
0,0);
|
|
gtk_table_attach(GTK_TABLE(table),frame,0,1,1,2,
|
|
GTK_EXPAND,
|
|
GTK_EXPAND,
|
|
0,0);
|
|
|
|
|
|
And here's the result:
|
|
|
|
[Image]
|
|
|
|
Misc
|
|
|
|
Making a preview clickable is achieved most easily by placing it in a
|
|
boton. It also adds a nice border around the preview and you may not even
|
|
need to place it in a frame. See the Filter Pack Simulation plug-in for an
|
|
example.
|
|
|
|
This is pretty much it as far as GTK is concerned.
|
|
|
|
Filling In a Preview
|
|
|
|
In order to familiarize ourselves with the basics of filling in previews,
|
|
let's create the following pattern (contrived by trial and error):
|
|
|
|
[Image]
|
|
|
|
void
|
|
my_preview_rendering_function(GtkWidget *preview)
|
|
{
|
|
#define SIZE 100
|
|
#define HALF (SIZE/2)
|
|
|
|
guchar *row=(guchar *) malloc(3*SIZE); /* 3 bits per dot */
|
|
gint i, j; /* Coordinates */
|
|
double r, alpha, x, y;
|
|
|
|
if (preview==NULL) return; /* I usually add this when I want */
|
|
/* to avoid silly crashes. You */
|
|
/* should probably make sure that */
|
|
/* everything has been nicely */
|
|
/* initialized! */
|
|
for (j=0; j < ABS(cos(2*alpha)) ) { /* Are we inside the shape? */
|
|
/* glib.h contains ABS(x). */
|
|
row[i*3+0] = sqrt(1-r)*255; /* Define Red */
|
|
row[i*3+1] = 128; /* Define Green */
|
|
row[i*3+2] = 224; /* Define Blue */
|
|
} /* "+0" is for alignment! */
|
|
else {
|
|
row[i*3+0] = r*255;
|
|
row[i*3+1] = ABS(sin((float)i/SIZE*2*PI))*255;
|
|
row[i*3+2] = ABS(sin((float)j/SIZE*2*PI))*255;
|
|
}
|
|
}
|
|
gtk_preview_draw_row( GTK_PREVIEW(preview),row,0,j,SIZE);
|
|
/* Insert "row" into "preview" starting at the point with */
|
|
/* coordinates (0,j) first column, j_th row extending SIZE */
|
|
/* pixels to the right */
|
|
}
|
|
|
|
free(row); /* save some space */
|
|
gtk_widget_draw(preview,NULL); /* what does this do? */
|
|
gdk_flush(); /* or this? */
|
|
}
|
|
|
|
Non-GIMP users can have probably seen enough to do a lot of things already.
|
|
For the GIMP users I have a few pointers to add.
|
|
|
|
Image Preview
|
|
|
|
It is probably wise to keep a reduced version of the image around with just
|
|
enough pixels to fill the preview. This is done by selecting every n'th
|
|
pixel where n is the ratio of the size of the image to the size of the
|
|
preview. All further operations (including filling in the previews) are then
|
|
performed on the reduced number of pixels only. The following is my
|
|
implementation of reducing the image. (Keep in mind that I've had only basic
|
|
C!)
|
|
|
|
(UNTESTED CODE ALERT!!!)
|
|
|
|
typedef struct {
|
|
gint width;
|
|
gint height;
|
|
gint bbp;
|
|
guchar *rgb;
|
|
guchar *mask;
|
|
} ReducedImage;
|
|
|
|
enum {
|
|
SELECTION_ONLY,
|
|
SELECTION_IN_CONTEXT,
|
|
ENTIRE_IMAGE
|
|
};
|
|
|
|
ReducedImage *Reduce_The_Image(GDrawable *drawable,
|
|
GDrawable *mask,
|
|
gint LongerSize,
|
|
gint Selection)
|
|
{
|
|
/* This function reduced the image down to the the selected preview size */
|
|
/* The preview size is determine by LongerSize, i.e. the greater of the */
|
|
/* two dimensions. Works for RGB images only! */
|
|
gint RH, RW; /* Reduced height and reduced width */
|
|
gint width, height; /* Width and Height of the area being reduced */
|
|
gint bytes=drawable->bpp;
|
|
ReducedImage *temp=(ReducedImage *)malloc(sizeof(ReducedImage));
|
|
|
|
guchar *tempRGB, *src_row, *tempmask, *src_mask_row,R,G,B;
|
|
gint i, j, whichcol, whichrow, x1, x2, y1, y2;
|
|
GPixelRgn srcPR, srcMask;
|
|
gint NoSelectionMade=TRUE; /* Assume that we're dealing with the entire */
|
|
/* image. */
|
|
|
|
gimp_drawable_mask_bounds (drawable->id, &x1, &y1, &x2, &y2);
|
|
width = x2-x1;
|
|
height = y2-y1;
|
|
/* If there's a SELECTION, we got its bounds!)
|
|
|
|
if (width != drawable->width && height != drawable->height)
|
|
NoSelectionMade=FALSE;
|
|
/* Become aware of whether the user has made an active selection */
|
|
/* This will become important later, when creating a reduced mask. */
|
|
|
|
/* If we want to preview the entire image, overrule the above! */
|
|
/* Of course, if no selection has been made, this does nothing! */
|
|
if (Selection==ENTIRE_IMAGE) {
|
|
x1=0;
|
|
x2=drawable->width;
|
|
y1=0;
|
|
y2=drawable->height;
|
|
}
|
|
|
|
/* If we want to preview a selection with some surrounding area we */
|
|
/* have to expand it a little bit. Consider it a bit of a riddle. */
|
|
if (Selection==SELECTION_IN_CONTEXT) {
|
|
x1=MAX(0, x1-width/2.0);
|
|
x2=MIN(drawable->width, x2+width/2.0);
|
|
y1=MAX(0, y1-height/2.0);
|
|
y2=MIN(drawable->height, y2+height/2.0);
|
|
}
|
|
|
|
/* How we can determine the width and the height of the area being */
|
|
/* reduced. */
|
|
width = x2-x1;
|
|
height = y2-y1;
|
|
|
|
/* The lines below determine which dimension is to be the longer */
|
|
/* side. The idea borrowed from the supernova plug-in. I suspect I */
|
|
/* could've thought of it myself, but the truth must be told. */
|
|
/* Plagiarism stinks! */
|
|
if (width>height) {
|
|
RW=LongerSize;
|
|
RH=(float) height * (float) LongerSize/ (float) width;
|
|
}
|
|
else {
|
|
RH=LongerSize;
|
|
RW=(float)width * (float) LongerSize/ (float) height;
|
|
}
|
|
|
|
/* The entire image is stretched into a string! */
|
|
tempRGB = (guchar *) malloc(RW*RH*bytes);
|
|
tempmask = (guchar *) malloc(RW*RH);
|
|
|
|
gimp_pixel_rgn_init (&srcPR, drawable, x1, y1, width, height,
|
|
FALSE, FALSE);
|
|
gimp_pixel_rgn_init (&srcMask, mask, x1, y1, width, height,
|
|
FALSE, FALSE);
|
|
|
|
/* Grab enough to save a row of image and a row of mask. */
|
|
src_row = (guchar *) malloc (width*bytes);
|
|
src_mask_row = (guchar *) malloc (width);
|
|
|
|
for (i=0; i < RH; i++) {
|
|
whichrow=(float)i*(float)height/(float)RH;
|
|
gimp_pixel_rgn_get_row (&srcPR, src_row, x1, y1+whichrow, width);
|
|
gimp_pixel_rgn_get_row (&srcMask, src_mask_row, x1, y1+whichrow, width);
|
|
|
|
for (j=0; j < RW; j++) {
|
|
whichcol=(float)j*(float)width/(float)RW;
|
|
|
|
/* No selection made = each point is completely selected! */
|
|
if (NoSelectionMade)
|
|
tempmask[i*RW+j]=255;
|
|
else
|
|
tempmask[i*RW+j]=src_mask_row[whichcol];
|
|
|
|
/* Add the row to the one long string which now contains the image! */
|
|
tempRGB[i*RW*bytes+j*bytes+0]=src_row[whichcol*bytes+0];
|
|
tempRGB[i*RW*bytes+j*bytes+1]=src_row[whichcol*bytes+1];
|
|
tempRGB[i*RW*bytes+j*bytes+2]=src_row[whichcol*bytes+2];
|
|
|
|
/* Hold on to the alpha as well */
|
|
if (bytes==4)
|
|
tempRGB[i*RW*bytes+j*bytes+3]=src_row[whichcol*bytes+3];
|
|
}
|
|
}
|
|
temp->bpp=bytes;
|
|
temp->width=RW;
|
|
temp->height=RH;
|
|
temp->rgb=tempRGB;
|
|
temp->mask=tempmask;
|
|
return temp;
|
|
}
|
|
|
|
The following is a preview function which used the same ReducedImage type!
|
|
Note that it uses fakes transparency (if one is present by means of
|
|
fake_transparency which is defined as follows:
|
|
|
|
gint fake_transparency(gint i, gint j)
|
|
{
|
|
if ( ((i%20)- 10) * ((j%20)- 10)>0 )
|
|
return 64;
|
|
else
|
|
return 196;
|
|
}
|
|
|
|
Now here's the preview function:
|
|
|
|
void
|
|
my_preview_render_function(GtkWidget *preview,
|
|
gint changewhat,
|
|
gint changewhich)
|
|
{
|
|
gint Inten, bytes=drawable->bpp;
|
|
gint i, j, k;
|
|
float partial;
|
|
gint RW=reduced->width;
|
|
gint RH=reduced->height;
|
|
guchar *row=malloc(bytes*RW);;
|
|
|
|
|
|
for (i=0; i < RH; i++) {
|
|
for (j=0; j < RW; j++) {
|
|
|
|
row[j*3+0] = reduced->rgb[i*RW*bytes + j*bytes + 0];
|
|
row[j*3+1] = reduced->rgb[i*RW*bytes + j*bytes + 1];
|
|
row[j*3+2] = reduced->rgb[i*RW*bytes + j*bytes + 2];
|
|
|
|
if (bytes==4)
|
|
for (k=0; k<3; k++) {
|
|
float transp=reduced->rgb[i*RW*bytes+j*bytes+3]/255.0;
|
|
row[3*j+k]=transp*a[3*j+k]+(1-transp)*fake_transparency(i,j);
|
|
}
|
|
}
|
|
gtk_preview_draw_row( GTK_PREVIEW(preview),row,0,i,RW);
|
|
}
|
|
|
|
free(a);
|
|
gtk_widget_draw(preview,NULL);
|
|
gdk_flush();
|
|
}
|
|
|
|
Applicable Routines
|
|
|
|
guint gtk_preview_get_type (void);
|
|
/* No idea */
|
|
void gtk_preview_uninit (void);
|
|
/* No idea */
|
|
GtkWidget* gtk_preview_new (GtkPreviewType type);
|
|
/* Described above */
|
|
void gtk_preview_size (GtkPreview *preview,
|
|
gint width,
|
|
gint height);
|
|
/* Allows you to resize an existing preview. */
|
|
/* Apparently there's a bug in GTK which makes */
|
|
/* this process messy. A way to clean up a mess */
|
|
/* is to manually resize the window containing */
|
|
/* the preview after resizing the preview. */
|
|
|
|
void gtk_preview_put (GtkPreview *preview,
|
|
GdkWindow *ventana,
|
|
GdkGC *gc,
|
|
gint srcx,
|
|
gint srcy,
|
|
gint destx,
|
|
gint desty,
|
|
gint width,
|
|
gint height);
|
|
/* No idea */
|
|
|
|
void gtk_preview_put_row (GtkPreview *preview,
|
|
guchar *src,
|
|
guchar *dest,
|
|
gint x,
|
|
gint y,
|
|
gint w);
|
|
/* No idea */
|
|
|
|
void gtk_preview_draw_row (GtkPreview *preview,
|
|
guchar *data,
|
|
gint x,
|
|
gint y,
|
|
gint w);
|
|
/* Described in the text */
|
|
|
|
void gtk_preview_set_expand (GtkPreview *preview,
|
|
gint expand);
|
|
/* No idea */
|
|
|
|
/* No clue for any of the below but */
|
|
/* should be standard for most widgets */
|
|
void gtk_preview_set_gamma (double gamma);
|
|
void gtk_preview_set_color_cube (guint nred_shades,
|
|
guint ngreen_shades,
|
|
guint nblue_shades,
|
|
guint ngray_shades);
|
|
void gtk_preview_set_install_cmap (gint install_cmap);
|
|
void gtk_preview_set_reserved (gint nreserved);
|
|
GdkVisual* gtk_preview_get_visual (void);
|
|
GdkColormap* gtk_preview_get_cmap (void);
|
|
GtkPreviewInfo* gtk_preview_get_info (void);
|
|
|
|
That's all, folks!
|
|
|
|
</verb></tscreen>
|
|
|
|
-->
|
|
|
|
<!-- ***************************************************************** -->
|
|
<sect>Estableciendo los atributos de un <em/widget/<label id="sec_setting_widget_attributes">
|
|
<!-- ***************************************************************** -->
|
|
<p>
|
|
En este capítulo se describen las funciones utilizadas para manejar los
|
|
<em/widgets/. Pueden utilizarse para establecer el estilo, relleno,
|
|
tamaño, etc...
|
|
|
|
(Puede que deba hacer una sección completa para los aceleradores.)
|
|
|
|
<tscreen><verb>
|
|
void gtk_widget_install_accelerator( GtkWidget *widget,
|
|
GtkAcceleratorTable *table,
|
|
gchar *signal_name,
|
|
gchar key,
|
|
guint8 modifiers );
|
|
|
|
void gtk_widget_remove_accelerator ( GtkWidget *widget,
|
|
GtkAcceleratorTable *table,
|
|
gchar *signal_name);
|
|
|
|
void gtk_widget_activate( GtkWidget *widget );
|
|
|
|
void gtk_widget_set_name( GtkWidget *widget,
|
|
gchar *name );
|
|
|
|
gchar *gtk_widget_get_name( GtkWidget *widget );
|
|
|
|
void gtk_widget_set_sensitive( GtkWidget *widget,
|
|
gint sensitive );
|
|
|
|
void gtk_widget_set_style( GtkWidget *widget,
|
|
GtkStyle *style );
|
|
|
|
GtkStyle *gtk_widget_get_style( GtkWidget *widget );
|
|
|
|
GtkStyle *gtk_widget_get_default_style( void );
|
|
|
|
void gtk_widget_set_uposition( GtkWidget *widget,
|
|
gint x,
|
|
gint y );
|
|
|
|
void gtk_widget_set_usize( GtkWidget *widget,
|
|
gint width,
|
|
gint height );
|
|
|
|
void gtk_widget_grab_focus( GtkWidget *widget );
|
|
|
|
void gtk_widget_show( GtkWidget *widget );
|
|
|
|
void gtk_widget_hide( GtkWidget *widget );
|
|
</verb></tscreen>
|
|
|
|
<!-- ***************************************************************** -->
|
|
<sect>Tiempos de espera, ES (<em/IO/) y funciones ociosas (<em/idle/)<label id="sec_timeouts">
|
|
<!-- ***************************************************************** -->
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Tiempos de espera
|
|
<p>
|
|
Puede que se esté preguntando como hacer que GTK haga algo útil
|
|
cuando se encuentra en <tt/gtk_main/. Bien, tiene varias
|
|
opciones. Utilizando las rutinas siguientes puede crear una función
|
|
a la que se llamará cada <tt/interval/ milisegundos.
|
|
|
|
<tscreen><verb>
|
|
gint gtk_timeout_add( guint32 interval,
|
|
GtkFunction function,
|
|
gpointer data );
|
|
</verb></tscreen>
|
|
|
|
El primer argumento es el número de milisegundos que habrá entre dos
|
|
llamadas a su función. El segundo argumento es la función a la que
|
|
desea llamar, y el tercero, los datos que le pasará a ésta función.
|
|
El valor devuelto es un «identificador» (un valor entero) que puede
|
|
utilizar para detener las llamadas haciendo:
|
|
|
|
<tscreen><verb>
|
|
void gtk_timeout_remove( gint tag );
|
|
</verb></tscreen>
|
|
|
|
También puede hacer que cesen las llamadas a la función haciendo que
|
|
la misma devuelva cero o FALSE. Obviamente esto significa que si
|
|
quiere que se continue llamando a su función, deberá devolver un valor
|
|
distinto de cero, es decir TRUE.
|
|
|
|
La declaración de su función debería ser algo como:
|
|
|
|
<tscreen><verb>
|
|
gint timeout_callback( gpointer data );
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Monitorizando la ES
|
|
<p>
|
|
Otra característica divertida de GTK, es la habilidad que tiene de
|
|
comprobar datos por usted en un descriptor de fichero (tal y como
|
|
se devuelven por <tt/open(2)/ o <tt/socket(2)/). Esto es especialmente
|
|
útil para las aplicaciones de red. La función:
|
|
|
|
<tscreen><verb>
|
|
gint gdk_input_add( gint source,
|
|
GdkInputCondition condition,
|
|
GdkInputFunction function,
|
|
gpointer data );
|
|
</verb></tscreen>
|
|
|
|
Donde el primer argumento es el descriptor de fichero que desea vigilar,
|
|
y el segundo especifica que es lo que quiere que GDK busque. Puede ser uno
|
|
de los siguientes:
|
|
|
|
<itemize>
|
|
<item>GDK_INPUT_READ - Llama a su función cuando hay datos listos para
|
|
leerse del fichero.
|
|
|
|
<item>GDK_INPUT_WRITE - Llama a su función cuando el descriptor del
|
|
fichero está listo para la escritura.
|
|
</itemize>
|
|
|
|
Tal y como se habrá imaginado, el tercer argumento es la función a la
|
|
que desea que se llame cuando se den las condiciones anteriores, y el
|
|
cuarto son los datos que se le pasarán a ésta función.
|
|
|
|
El valor devuelto es un identificador que puede utilizarse para que GDK
|
|
pare de vigilar ese fichero, utilizando la función
|
|
|
|
<tscreen><verb>
|
|
void gdk_input_remove( gint tag );
|
|
</verb></tscreen>
|
|
|
|
La función a la que quiere que se llame deberá declararse así:
|
|
|
|
<tscreen><verb>
|
|
void input_callback( gpointer data,
|
|
gint source,
|
|
GdkInputCondition condition );
|
|
</verb></tscreen>
|
|
|
|
Donde <tt/source/ y <tt/condition/ están especificados más arriba.
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Funciones ociosas
|
|
<p>
|
|
<!-- Need to check on idle priorities - TRG -->
|
|
¿Qué le parece si tuviese una función a la que se llamase cuando
|
|
no ocurriese nada?
|
|
|
|
<tscreen><verb>
|
|
gint gtk_idle_add( GtkFunction function,
|
|
gpointer data );
|
|
</verb></tscreen>
|
|
|
|
Esto hace que GTK llame a la función especificada cuando no ocurra
|
|
nada más.
|
|
|
|
<tscreen><verb>
|
|
void gtk_idle_remove( gint tag );
|
|
</verb></tscreen>
|
|
|
|
No voy a explicar el significado de los argumentos ya que se parece
|
|
mucho a los que he explicado más arriba. La función a la que se apunta
|
|
mediante el primer argumento de <tt/gtk_idle_add/ será a la que se
|
|
llame cuando llegue el momento. Como antes, si devuelve FALSE hará que
|
|
cese de llamarse a la función.
|
|
|
|
<!-- ***************************************************************** -->
|
|
<sect>Manejo avanzado de eventos y señales<label id="sec_Adv_Events_and_Signals">
|
|
<!-- ***************************************************************** -->
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Funciones señal
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect2>Conectando y desconectando los manejadores de señal
|
|
<p>
|
|
|
|
<tscreen><verb>
|
|
guint gtk_signal_connect( GtkObject *object,
|
|
const gchar *name,
|
|
GtkSignalFunc func,
|
|
gpointer func_data );
|
|
|
|
guint gtk_signal_connect_after( GtkObject *object,
|
|
const gchar *name,
|
|
GtkSignalFunc func,
|
|
gpointer func_data );
|
|
|
|
guint gtk_signal_connect_object( GtkObject *object,
|
|
const gchar *name,
|
|
GtkSignalFunc func,
|
|
GtkObject *slot_object );
|
|
|
|
guint gtk_signal_connect_object_after( GtkObject *object,
|
|
const gchar *name,
|
|
GtkSignalFunc func,
|
|
GtkObject *slot_object );
|
|
|
|
guint gtk_signal_connect_full( GtkObject *object,
|
|
const gchar *name,
|
|
GtkSignalFunc func,
|
|
GtkCallbackMarshal marshal,
|
|
gpointer data,
|
|
GtkDestroyNotify destroy_func,
|
|
gint object_signal,
|
|
gint after );
|
|
|
|
guint gtk_signal_connect_interp( GtkObject *object,
|
|
const gchar *name,
|
|
GtkCallbackMarshal func,
|
|
gpointer data,
|
|
GtkDestroyNotify destroy_func,
|
|
gint after );
|
|
|
|
void gtk_signal_connect_object_while_alive( GtkObject *object,
|
|
const gchar *signal,
|
|
GtkSignalFunc func,
|
|
GtkObject *alive_object );
|
|
|
|
void gtk_signal_connect_while_alive( GtkObject *object,
|
|
const gchar *signal,
|
|
GtkSignalFunc func,
|
|
gpointer func_data,
|
|
GtkObject *alive_object );
|
|
|
|
void gtk_signal_disconnect( GtkObject *object,
|
|
guint handler_id );
|
|
|
|
void gtk_signal_disconnect_by_func( GtkObject *object,
|
|
GtkSignalFunc func,
|
|
gpointer data );
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect2>Bloqueando y desbloqueando los manejadores de señal
|
|
<p>
|
|
<tscreen><verb>
|
|
void gtk_signal_handler_block( GtkObject *object,
|
|
guint handler_id);
|
|
|
|
void gtk_signal_handler_block_by_func( GtkObject *object,
|
|
GtkSignalFunc func,
|
|
gpointer data );
|
|
|
|
void gtk_signal_handler_block_by_data( GtkObject *object,
|
|
gpointer data );
|
|
|
|
void gtk_signal_handler_unblock( GtkObject *object,
|
|
guint handler_id );
|
|
|
|
void gtk_signal_handler_unblock_by_func( GtkObject *object,
|
|
GtkSignalFunc func,
|
|
gpointer data );
|
|
|
|
void gtk_signal_handler_unblock_by_data( GtkObject *object,
|
|
gpointer data );
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect2>Emitiendo y deteniendo señales
|
|
<p>
|
|
<tscreen><verb>
|
|
void gtk_signal_emit( GtkObject *object,
|
|
guint signal_id,
|
|
... );
|
|
|
|
void gtk_signal_emit_by_name( GtkObject *object,
|
|
const gchar *name,
|
|
... );
|
|
|
|
void gtk_signal_emitv( GtkObject *object,
|
|
guint signal_id,
|
|
GtkArg *params );
|
|
|
|
void gtk_signal_emitv_by_name( GtkObject *object,
|
|
const gchar *name,
|
|
GtkArg *params );
|
|
|
|
guint gtk_signal_n_emissions( GtkObject *object,
|
|
guint signal_id );
|
|
|
|
guint gtk_signal_n_emissions_by_name( GtkObject *object,
|
|
const gchar *name );
|
|
|
|
void gtk_signal_emit_stop( GtkObject *object,
|
|
guint signal_id );
|
|
|
|
void gtk_signal_emit_stop_by_name( GtkObject *object,
|
|
const gchar *name );
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Emisión y propagación de señales
|
|
<p>
|
|
La emisión de señales es el proceso mediante el cual GTK+ ejecuta
|
|
todos los manejadores de un objeto y una señal en especial.
|
|
|
|
Primero, observe que el valor devuelto por la emisión de una
|
|
señal es el mismo que el valor devuelto por el <em>último</em>
|
|
manipulador ejecutado. Ya que las señales de los eventos son todas
|
|
del tipo GTK_RUN_LAST, el manejador por defecto (proporcionado por
|
|
GTK+) será de este tipo, a menos que lo conecte con
|
|
<tt/gtk_signal_connect_after()/.
|
|
|
|
La forma en que se maneja un evento (digamos GTK_BUTTON_PRESS), es la
|
|
siguiente:
|
|
|
|
<itemize>
|
|
<item>Empieza con el <em>widget</em> donde ocurrió el evento.
|
|
|
|
<item>Emite la señal genérica <tt/event/. Si esta señal devuelve un
|
|
valor TRUE, detiene todo el proceso.
|
|
|
|
<item>En caso contrario, emite una señal especifica,
|
|
«button_press_event» en nuestro caso. Si ésta devuelve TRUE, detiene
|
|
todo el proceso.
|
|
|
|
<item>En caso contrario, va al <em>widget</em> padre y repite los
|
|
pasos anteriores.
|
|
|
|
<item>Continua hasta que algún manejador de señal devuelva TRUE, o
|
|
hasta que se llegue al <em>widget</em> de más alto nivel.
|
|
</itemize>
|
|
|
|
Algunas consecuencias son:
|
|
<itemize>
|
|
<item>El valor que devuelva su manejador no tendrá ningún efecto si
|
|
hay un manejador por defecto, a menos que lo conecte mediante
|
|
<tt/gtk_signal_connect_after()/.
|
|
|
|
<item>Para evitar que el manejador por defecto se ejecute, necesita
|
|
conectar mediante <tt/gtk_signal_connect()/ y utilizar
|
|
<tt/gtk_signal_emit_stop_by_name()/ - el valor devuelto sólo se ve
|
|
afectado si la señal se propaga, y no sólo por el hecho de emitirse.
|
|
</itemize>
|
|
|
|
<!-- ***************************************************************** -->
|
|
<sect>Manejando selecciones
|
|
<!-- ***************************************************************** -->
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1> Contenido
|
|
<p>
|
|
Un tipo de comunicación entre procesos que se puede utilizar con GTK
|
|
son las <em/selecciones/. Una selección identifica un conjunto de
|
|
datos, por ejemplo, un trozo de texto, seleccionado por el usuario de
|
|
alguna manera, por ejemplo, cogiéndolo con el ratón. Sólo una
|
|
aplicación en un <em/display/ (la <em>propietaria</em>) puede tener
|
|
una selección en particular en un momento dado, por lo que cuando una
|
|
aplicación pide una selección, el propietario previo debe indicar al
|
|
usuario que la selección ya no es válida. Otras aplicaciones pueden
|
|
pedir el contenido de la selección de diferentes formas, llamadas
|
|
<em/objetivos/. Puede haber cualquier número de selecciones, pero la
|
|
mayoría de las aplicacion X sólo pueden manejar una, la <em/selección
|
|
primaria/.
|
|
|
|
En muchos casos, no es necesario para una aplicación GTK tratar por
|
|
sí misma con las selecciones. Los <em/widgets/ estándar, como el
|
|
<em/widget/ Entry, ya tienen la posibilidad de crear la selección
|
|
cuando sea necesario (p.e., cuando el usuario pase el ratón sobre el
|
|
texto manteniendo el botón derecho del ratón pulsado), y de recoger
|
|
los contenidos de la selección propiedad de otro <em/widget/, o de
|
|
otra aplicación (p.e., cuando el usuario pulsa el segundo botón del
|
|
ratón). Sin embargo, pueden haber casos en los que quiera darle a
|
|
otros <em/widgets/ la posibilidad de proporcionar la selección, o
|
|
puede que quiera recuperar objetivos que no estén admitidos por
|
|
defecto.
|
|
|
|
Un concepto fundamental que es necesario para comprender el manejo de
|
|
la selección es el de <em>átomo</em>. Un átomo es un entero que
|
|
identifica de una manera unívoca una cadena (en un cierto
|
|
<em/display/). Ciertos átomos están predefinidos por el servidor X, y
|
|
en algunos casos hay constantes en <tt>gtk.h</tt> que corresponden a
|
|
estos átomos. Por ejemplo la constante <tt>GDK_PRIMARY_SELECTION</tt>
|
|
corresponde a la cadena «PRIMARY». En otros casos, debería utilizar
|
|
las funciones <tt>gdk_atom_intern()</tt>, para obtener el átomo
|
|
correspondiente a una cadena, y <tt>gdk_atom_name()</tt>, para obtener
|
|
el nombre de un átomo. Ambas, selecciones y objetivos, están
|
|
identificados por átomos.
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1> Recuperando la selección
|
|
<p>
|
|
Recuperar la selección es un proceso asíncrono. Para comenzar el
|
|
proceso, deberá llamar a:
|
|
|
|
<tscreen><verb>
|
|
gint gtk_selection_convert( GtkWidget *widget,
|
|
GdkAtom selection,
|
|
GdkAtom target,
|
|
guint32 time );
|
|
</verb</tscreen>
|
|
|
|
Este proceso <em/convierte/ la selección en la forma especificada por
|
|
<tt/target/. Si es posible, el campo <tt/time/ debe ser el tiempo
|
|
desde que el evento lanzó la selección. Esto ayuda a asegurarse de que
|
|
los eventos ocurran en el orden en que el usuario los ha pedido. Sin
|
|
embargo, si no está disponible (por ejemplo, si se empezó la
|
|
conversión por una señal de «pulsación»), entonces puede utilizar la
|
|
constante <tt>GDK_CURRENT_TIME</tt>.
|
|
|
|
Cuando el propietario de la selección responda a la petición, se
|
|
enviará una señal «selection_received» a su aplicación. El manejador
|
|
de esta señal recibe un puntero a una estructura
|
|
<tt>GtkSelectionData</tt>, que se define como:
|
|
|
|
<tscreen><verb>
|
|
struct _GtkSelectionData
|
|
{
|
|
GdkAtom selection;
|
|
GdkAtom target;
|
|
GdkAtom type;
|
|
gint format;
|
|
guchar *data;
|
|
gint length;
|
|
};
|
|
</verb></tscreen>
|
|
|
|
<tt>selection</tt> y <tt>target</tt> son los valores que dió en su
|
|
llamada a <tt>gtk_selection_convert()</tt>. <tt>type</tt> es un átomo
|
|
que identifica el tipo de datos devueltos por el propietario de la
|
|
selección. Algunos valores posibles son «STRING», un cadena de
|
|
caracteres latin-1, «ATOM», una serie de átomos, «INTEGER», un
|
|
entero, etc. Muchos objetivos sólo pueden devolver un
|
|
tipo. <tt/format/ da la longitud de las unidades (por ejemplo
|
|
caracteres) en bits. Normalmente, no tiene porque preocuparse de todo
|
|
esto cuando recibe datos. <tt/data/ es un puntero a los datos
|
|
devueltos, y <tt/length/ da la longitud de los datos devueltos, en
|
|
bytes. Si <tt/length/ es negativo, es que a ocurrido un error y no se
|
|
puede obtener la selección. Esto podría ocurrir si no hay ninguna
|
|
aplicación que sea la propietaria de la selección, o si pide un
|
|
objetivo que la aplicación no admite. Actualmente se garantiza que el
|
|
búfer tendrá un byte más que <tt/length/; el byte extra siempre será
|
|
cero, por lo que no es necesario hacer una copia de las cadenas sólo
|
|
para añadirles un carácter nulo al final.
|
|
|
|
En el siguiente ejemplo, recuperamos el objetivo especial «TARGETS»,
|
|
que es una lista de todos los objetivos en los que se puede convertir
|
|
la selección.
|
|
|
|
<tscreen><verb>
|
|
/* principio del ejemplo selection gettargets.c */
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
void selection_received (GtkWidget *widget,
|
|
GtkSelectionData *selection_data,
|
|
gpointer data);
|
|
|
|
/* Manejador de señal invocado cuando el usuario pulsa en el botón
|
|
"Get Targets" */
|
|
void
|
|
get_targets (GtkWidget *widget, gpointer data)
|
|
{
|
|
static GdkAtom targets_atom = GDK_NONE;
|
|
|
|
/* Obtener el atom correpondiente a la cadena "TARGETS" */
|
|
if (targets_atom == GDK_NONE)
|
|
targets_atom = gdk_atom_intern ("TARGETS", FALSE);
|
|
|
|
/* Y pide el objetivo "TARGETS" de la selección primaria */
|
|
gtk_selection_convert (widget, GDK_SELECTION_PRIMARY, targets_atom,
|
|
GDK_CURRENT_TIME);
|
|
}
|
|
|
|
/* Manipulador de señal llamado cuando el propietario de la señal
|
|
* devuelve los datos */
|
|
void
|
|
selection_received (GtkWidget *widget, GtkSelectionData *selection_data,
|
|
gpointer data)
|
|
{
|
|
GdkAtom *atoms;
|
|
GList *item_list;
|
|
int i;
|
|
|
|
/* **** IMPORTANTE **** Comprobar si se da la recuperación de los
|
|
* datos */
|
|
if (selection_data->length < 0)
|
|
{
|
|
g_print ("Selection retrieval failed\n");
|
|
return;
|
|
}
|
|
/* Asegurarse de que obtenemos los datos de la forma esperada */
|
|
if (selection_data->type != GDK_SELECTION_TYPE_ATOM)
|
|
{
|
|
g_print ("Selection \"TARGETS\" was not returned as atoms!\n");
|
|
return;
|
|
}
|
|
|
|
/* Imprimir los atoms que hemos recibido */
|
|
atoms = (GdkAtom *)selection_data->data;
|
|
|
|
item_list = NULL;
|
|
for (i=0; i<selection_data->length/sizeof(GdkAtom); i++)
|
|
{
|
|
char *name;
|
|
name = gdk_atom_name (atoms[i]);
|
|
if (name != NULL)
|
|
g_print ("%s\n",name);
|
|
else
|
|
g_print ("(bad atom)\n");
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
int
|
|
main (int argc, char *argv[])
|
|
{
|
|
GtkWidget *ventana;
|
|
GtkWidget *boton;
|
|
|
|
gtk_init (&argc, &argv);
|
|
|
|
/* Crear la ventana superior */
|
|
|
|
ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
|
gtk_window_set_title (GTK_WINDOW (ventana), "Event Box");
|
|
gtk_container_border_width (GTK_CONTAINER (ventana), 10);
|
|
|
|
gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
|
|
GTK_SIGNAL_FUNC (gtk_exit), NULL);
|
|
|
|
/* Crear un botón que el usuario puede pulsar para obtener los
|
|
* objetivos */
|
|
|
|
boton = gtk_button_new_with_label ("Get Targets");
|
|
gtk_container_add (GTK_CONTAINER (ventana), boton);
|
|
|
|
gtk_signal_connect (GTK_OBJECT(boton), "clicked",
|
|
GTK_SIGNAL_FUNC (get_targets), NULL);
|
|
gtk_signal_connect (GTK_OBJECT(boton), "selection_received",
|
|
GTK_SIGNAL_FUNC (selection_received), NULL);
|
|
|
|
gtk_widget_show (boton);
|
|
gtk_widget_show (ventana);
|
|
|
|
gtk_main ();
|
|
|
|
return 0;
|
|
}
|
|
/* fin del ejemplo */
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1> Proporcionando la selección
|
|
<p>
|
|
Proporcionar la selección es un poco más complicado. Debe registrar
|
|
los manejadores a los que se llamarán cuando se le pida la
|
|
selección. Por cada par selección/objetivo que quiera manejar, deberá
|
|
hacer una llamada a:
|
|
|
|
<tscreen><verb>
|
|
void gtk_selection_add_handler( GtkWidget *widget,
|
|
GdkAtom selection,
|
|
GdkAtom target,
|
|
GtkSelectionFunction function,
|
|
GtkRemoveFunction remove_func,
|
|
gpointer data );
|
|
</verb></tscreen>
|
|
|
|
<tt/widget/, <tt/selection/, y <tt/target/ identifican las peticiones
|
|
que este manejador puede manipular. Si <tt/remove_func/ no es NULL, se
|
|
le llamará cuando se elimine el manejador de la señal. Esto es útil,
|
|
por ejemplo, para los lenguajes interpretados que necesitan mantener
|
|
una memoria de las referencias a <tt/data/.
|
|
|
|
La función de llamada tiene el prototipo:
|
|
|
|
<tscreen><verb>
|
|
typedef void (*GtkSelectionFunction)( GtkWidget *widget,
|
|
GtkSelectionData *selection_data,
|
|
gpointer data );
|
|
|
|
</verb></tscreen>
|
|
|
|
El <tt/GtkSelectionData/ es el mismo que hay más arriba, pero esta
|
|
vez, seremos nosotros los responsables de rellenar los campos
|
|
<tt/type/, <tt/format/, <tt/data/, y <tt/length/. (El campo
|
|
<tt/format/ es importante - el servidor X lo utiliza para saber si
|
|
tienen que intercambiarse los bytes que forman los datos o
|
|
no. Normalmente será 8 - es decir un carácter - o 32 - es decir un
|
|
entero.) Esto se hace llamando a la función:
|
|
|
|
<tscreen><verb>
|
|
void gtk_selection_data_set( GtkSelectionData *selection_data,
|
|
GdkAtom type,
|
|
gint format,
|
|
guchar *data,
|
|
gint length );
|
|
</verb></tscreen>
|
|
|
|
Esta función tiene la responsabilidad de hacer una copia de los datos
|
|
para que no tenga que preocuparse de ir guardándolos. (No debería
|
|
rellenar los campos de la estructura <tt/GtkSelectionData/ a mano.)
|
|
|
|
Cuando haga falta, puede pedir el propietario de la selección llamando
|
|
a:
|
|
|
|
<tscreen><verb>
|
|
gint gtk_selection_owner_set( GtkWidget *widget,
|
|
GdkAtom selection,
|
|
guint32 time );
|
|
</verb></tscreen>
|
|
|
|
Si otra aplicación pide el propietario de la selección, recibira un
|
|
«selection_clear_event».
|
|
|
|
Como ejemplo de proporciar la selección, el programa siguiente le añade
|
|
la posibilidad de selección a un botón de comprobación. Cuando se presione
|
|
el botón de comprobación, el programa pedirá la selección primaria. El
|
|
único objetivo que admite es un objetivo «STRING» (aparte de ciertos
|
|
objetivos como "TARGETS", proporcionados por GTK). Cuando se pida este
|
|
objetivo, se devolverá una representación del tiempo.
|
|
|
|
<tscreen><verb>
|
|
/* principio del ejemplo selection setselection.c */
|
|
|
|
#include <gtk/gtk.h>
|
|
#include <time.h>
|
|
|
|
/* Función de llamada para cuando el usuario cambia la selección */
|
|
void
|
|
selection_toggled (GtkWidget *widget, gint *have_selection)
|
|
{
|
|
if (GTK_TOGGLE_BUTTON(widget)->active)
|
|
{
|
|
*have_selection = gtk_selection_owner_set (widget,
|
|
GDK_SELECTION_PRIMARY,
|
|
GDK_CURRENT_TIME);
|
|
/* Si la demanda de la selección ha fallado, ponemos el botón en
|
|
* estado apagado */
|
|
if (!*have_selection)
|
|
gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON(widget), FALSE);
|
|
}
|
|
else
|
|
{
|
|
if (*have_selection)
|
|
{
|
|
/* Antes de eliminar la seleción poniendo el propietario a
|
|
* NULL, comprobamos si somos el propietario actual */
|
|
if (gdk_selection_owner_get (GDK_SELECTION_PRIMARY) == widget->window)
|
|
gtk_selection_owner_set (NULL, GDK_SELECTION_PRIMARY,
|
|
GDK_CURRENT_TIME);
|
|
*have_selection = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Llamado cuando otra aplicación pide la selección */
|
|
gint
|
|
selection_clear (GtkWidget *widget, GdkEventSelection *event,
|
|
gint *have_selection)
|
|
{
|
|
*have_selection = FALSE;
|
|
gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON(widget), FALSE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* Proporciona el tiempo actual como selección. */
|
|
void
|
|
selection_handle (GtkWidget *widget,
|
|
GtkSelectionData *selection_data,
|
|
gpointer data)
|
|
{
|
|
gchar *timestr;
|
|
time_t current_time;
|
|
|
|
current_time = time (NULL);
|
|
timestr = asctime (localtime(&current_time));
|
|
/* Cuando devolvemos una cadena, no debe terminar en NULL. La
|
|
* función lo hará por nosotros */
|
|
|
|
gtk_selection_data_set (selection_data, GDK_SELECTION_TYPE_STRING,
|
|
8, timestr, strlen(timestr));
|
|
}
|
|
|
|
int
|
|
main (int argc, char *argv[])
|
|
{
|
|
GtkWidget *ventana;
|
|
|
|
GtkWidget *selection_button;
|
|
|
|
static int have_selection = FALSE;
|
|
|
|
gtk_init (&argc, &argv);
|
|
|
|
/* Crear la ventana superior */
|
|
|
|
ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
|
gtk_window_set_title (GTK_WINDOW (ventana), "Event Box");
|
|
gtk_container_border_width (GTK_CONTAINER (ventana), 10);
|
|
|
|
gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
|
|
GTK_SIGNAL_FUNC (gtk_exit), NULL);
|
|
|
|
/* Crear un botón de selección para que actue como la selección */
|
|
|
|
selection_button = gtk_toggle_button_new_with_label ("Claim Selection");
|
|
gtk_container_add (GTK_CONTAINER (ventana), selection_button);
|
|
gtk_widget_show (selection_button);
|
|
|
|
gtk_signal_connect (GTK_OBJECT(selection_button), "toggled",
|
|
GTK_SIGNAL_FUNC (selection_toggled), &have_selection);
|
|
gtk_signal_connect (GTK_OBJECT(selection_button), "selection_clear_event",
|
|
GTK_SIGNAL_FUNC (selection_clear), &have_selection);
|
|
|
|
gtk_selection_add_handler (selection_button, GDK_SELECTION_PRIMARY,
|
|
GDK_SELECTION_TYPE_STRING,
|
|
selection_handle, NULL);
|
|
|
|
gtk_widget_show (selection_button);
|
|
gtk_widget_show (ventana);
|
|
|
|
gtk_main ();
|
|
|
|
return 0;
|
|
}
|
|
/* fin del ejemplo */
|
|
</verb></tscreen>
|
|
|
|
|
|
<!-- ***************************************************************** -->
|
|
<sect>Glib<label id="sec_glib">
|
|
<!-- ***************************************************************** -->
|
|
<p>
|
|
Glib proporciona muchas definiciones y funciones útiles disponibles
|
|
para su utilización cuando se crean aplicaciones GDK y GTK. Haré una
|
|
lista con todas ellas incluyendo una pequeña explicación. Muchas no
|
|
son más que duplicados de funciones estándar de libc por lo que no
|
|
entraré en detalle en la explicación de las mismas. Esta sección está
|
|
pensada principalmente para que se utilice como referencia, para que
|
|
sepa que es lo que puede utilizar.
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Definiciones
|
|
<p>
|
|
Las definiciones para los límites de muchos de los tipos estándar son:
|
|
|
|
<tscreen><verb>
|
|
G_MINFLOAT
|
|
G_MAXFLOAT
|
|
G_MINDOUBLE
|
|
G_MAXDOUBLE
|
|
G_MINSHORT
|
|
G_MAXSHORT
|
|
G_MININT
|
|
G_MAXINT
|
|
G_MINLONG
|
|
G_MAXLONG
|
|
</verb></tscreen>
|
|
|
|
Y también, los siguientes <tt/typedefs/. Cuando no se especifica el
|
|
tipo que debería aparecer a la izquierda significa que el mismo se
|
|
establecerá dinámicamente en función de la arquitectura. ¡Recuerde
|
|
evitar los calculos relativos al tamaño de un puntero si quiere que
|
|
su aplicación sea portable! P.e., un puntero en un Alpha tiene 8
|
|
bytes, pero 4 en Intel.
|
|
|
|
<tscreen><verb>
|
|
char gchar;
|
|
short gshort;
|
|
long glong;
|
|
int gint;
|
|
char gboolean;
|
|
|
|
unsigned char guchar;
|
|
unsigned short gushort;
|
|
unsigned long gulong;
|
|
unsigned int guint;
|
|
|
|
float gfloat;
|
|
double gdouble;
|
|
long double gldouble;
|
|
|
|
void* gpointer;
|
|
|
|
gint8
|
|
guint8
|
|
gint16
|
|
guint16
|
|
gint32
|
|
guint32
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Listas doblemente enlazadas
|
|
<p>
|
|
Las funciones siguientes se utilizan para crear, manipular, y destruir
|
|
listas doblemente enlazadas. Supondré que sabe lo que son las listas
|
|
enlazadas, ya que explicarlas va más allá del objetivo de este
|
|
documento. Por supuesto, no es necesario que conozca como manejar
|
|
todo esto para utilizar GTK, pero siempre es bonito aprender cosas.
|
|
|
|
<tscreen><verb>
|
|
GList *g_list_alloc( void );
|
|
|
|
void g_list_free( GList *list );
|
|
|
|
void g_list_free_1( GList *list );
|
|
|
|
GList *g_list_append( GList *list,
|
|
gpointer data );
|
|
|
|
GList *g_list_prepend( GList *list,
|
|
gpointer data );
|
|
|
|
GList *g_list_insert( GList *list,
|
|
gpointer data,
|
|
gint posicion );
|
|
|
|
GList *g_list_remove( GList *list,
|
|
gpointer data );
|
|
|
|
GList *g_list_remove_link( GList *list,
|
|
GList *link );
|
|
|
|
GList *g_list_reverse( GList *list );
|
|
|
|
GList *g_list_nth( GList *list,
|
|
gint n );
|
|
|
|
GList *g_list_find( GList *list,
|
|
gpointer data );
|
|
|
|
GList *g_list_last( GList *list );
|
|
|
|
GList *g_list_first( GList *list );
|
|
|
|
gint g_list_length( GList *list );
|
|
|
|
void g_list_foreach( GList *list,
|
|
GFunc func,
|
|
gpointer user_data );
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Listas simplemente enlazadas
|
|
<p>
|
|
Muchas de las funciones para las listas enlazadas son idénticas a las
|
|
de más arriba. Aquí hay una lista completa:
|
|
|
|
<tscreen><verb>
|
|
GSList *g_slist_alloc( void );
|
|
|
|
void g_slist_free( GSList *list );
|
|
|
|
void g_slist_free_1( GSList *list );
|
|
|
|
GSList *g_slist_append( GSList *list,
|
|
gpointer data );
|
|
|
|
GSList *g_slist_prepend( GSList *list,
|
|
gpointer data );
|
|
|
|
GSList *g_slist_insert( GSList *list,
|
|
gpointer data,
|
|
gint posicion );
|
|
|
|
GSList *g_slist_remove( GSList *list,
|
|
gpointer data );
|
|
|
|
GSList *g_slist_remove_link( GSList *list,
|
|
GSList *link );
|
|
|
|
GSList *g_slist_reverse( GSList *list );
|
|
|
|
GSList *g_slist_nth( GSList *list,
|
|
gint n );
|
|
|
|
GSList *g_slist_find( GSList *list,
|
|
gpointer data );
|
|
|
|
GSList *g_slist_last( GSList *list );
|
|
|
|
gint g_slist_length( GSList *list );
|
|
|
|
void g_slist_foreach( GSList *list,
|
|
GFunc func,
|
|
gpointer user_data );
|
|
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Control de la memoria
|
|
<p>
|
|
<tscreen><verb>
|
|
gpointer g_malloc( gulong size );
|
|
</verb></tscreen>
|
|
|
|
Reemplaza a <tt/malloc()/. No necesita comprobar el valor devuelto, ya
|
|
que ya lo hace por usted esta función.
|
|
|
|
<tscreen><verb>
|
|
gpointer g_malloc0( gulong size );
|
|
</verb></tscreen>
|
|
|
|
Lo mismo que antes, pero rellena con ceros la memoria antes de
|
|
devolver un puntero a ella.
|
|
|
|
<tscreen><verb>
|
|
gpointer g_realloc( gpointer mem,
|
|
gulong size );
|
|
</verb></tscreen>
|
|
|
|
Vuelve a reservar <tt/size/ bites de memoria empezando en
|
|
<tt/mem/. Obviamente, la memoria debe haber sido previamente reservada.
|
|
|
|
<tscreen><verb>
|
|
void g_free( gpointer mem );
|
|
</verb></tscreen>
|
|
|
|
Libera la memoria. Fácil.
|
|
|
|
<tscreen><verb>
|
|
void g_mem_profile( void );
|
|
</verb></tscreen>
|
|
|
|
Crea un fichero donde vuelca la memoria que se está utilizando, pero
|
|
tiene que añadir <tt/#define MEM_PROFILE/ en lo alto de
|
|
<tt>glib/gmem.c</tt> y tendrá que hacer un make y un make install.
|
|
|
|
<tscreen><verb>
|
|
void g_mem_check( gpointer mem );
|
|
</verb></tscreen>
|
|
|
|
Comprueba que una dirección de memoria es válida. Tiene que añadir
|
|
<tt/#define MEM_CHECK/ en lo alto de <tt/gmem.c/ y tiene que hacer un
|
|
make y un make install.
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Timers
|
|
<p>
|
|
Temporizadores...
|
|
|
|
<tscreen><verb>
|
|
GTimer *g_timer_new( void );
|
|
|
|
void g_timer_destroy( GTimer *timer );
|
|
|
|
void g_timer_start( GTimer *timer );
|
|
|
|
void g_timer_stop( GTimer *timer );
|
|
|
|
void g_timer_reset( GTimer *timer );
|
|
|
|
gdouble g_timer_elapsed( GTimer *timer,
|
|
gulong *microseconds );
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Manejo de cadenas de texto
|
|
<p>
|
|
Un puñado de funciones para manejar cadenas de texto. Parecen muy
|
|
interesantes, y probablemente sean mejores en muchos aspectos que las
|
|
funciones estándar de C, pero necesitan documentación.
|
|
|
|
<tscreen><verb>
|
|
GString *g_string_new( gchar *init );
|
|
|
|
void g_string_free( GString *string,
|
|
gint free_segment );
|
|
|
|
GString *g_string_assign( GString *lval,
|
|
gchar *rval );
|
|
|
|
GString *g_string_truncate( GString *string,
|
|
gint len );
|
|
|
|
GString *g_string_append( GString *string,
|
|
gchar *val );
|
|
|
|
GString *g_string_append_c( GString *string,
|
|
gchar c );
|
|
|
|
GString *g_string_prepend( GString *string,
|
|
gchar *val );
|
|
|
|
GString *g_string_prepend_c( GString *string,
|
|
gchar c );
|
|
|
|
void g_string_sprintf( GString *string,
|
|
gchar *fmt,
|
|
...);
|
|
|
|
void g_string_sprintfa ( GString *string,
|
|
gchar *fmt,
|
|
... );
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Funciones de error y funciones varias
|
|
<p>
|
|
<tscreen><verb>
|
|
gchar *g_strdup( const gchar *str );
|
|
</verb></tscreen>
|
|
|
|
Reemplaza a la función <tt/strdup/. Copia el contenido de la cadena
|
|
original en un nuevo lugar en memoria, y devuelve un puntero al nuevo
|
|
lugar.
|
|
|
|
<tscreen><verb>
|
|
gchar *g_strerror( gint errnum );
|
|
</verb></tscreen>
|
|
|
|
Recomiendo utilizar esta función para todos los mensages de error. Es
|
|
mucho más bonita, y más portable que <tt/perror()/ y demás funciones
|
|
clásicas. La salida es normalmente de la forma:
|
|
|
|
<tscreen><verb>
|
|
nombre del programa:función que falló:fichero o descripción adicional:strerror
|
|
</verb></tscreen>
|
|
|
|
Aquí hay un ejemplo de una llamada utilizada en nuestro programa
|
|
<tt/hello_world/:
|
|
|
|
<tscreen><verb>
|
|
g_print("hello_world:open:%s:%s\n", filename, g_strerror(errno));
|
|
</verb></tscreen>
|
|
|
|
<tscreen><verb>
|
|
void g_error( gchar *format, ... );
|
|
</verb></tscreen>
|
|
|
|
Imprime un mensaje de error. El formato es como el de <tt/printf/,
|
|
pero le añade <tt/** ERROR **: / a su mensaje, y sale del
|
|
programa. Sólo para errores fatales.
|
|
|
|
<tscreen><verb>
|
|
void g_warning( gchar *format, ... );
|
|
</verb></tscreen>
|
|
|
|
El mismo que el anterior, pero añade "** WARNING **: ", y no sale del
|
|
programa.
|
|
|
|
<tscreen><verb>
|
|
void g_message( gchar *format, ... );
|
|
</verb></tscreen>
|
|
|
|
Imprime <tt/message: / antes de la cadena que le pase.
|
|
|
|
<tscreen><verb>
|
|
void g_print( gchar *format, ... );
|
|
</verb></tscreen>
|
|
|
|
Reemplazo de <tt/printf()/.
|
|
|
|
Y nuestra última función:
|
|
|
|
<tscreen><verb>
|
|
gchar *g_strsignal( gint signum );
|
|
</verb></tscreen>
|
|
|
|
Imprime el nombre de la señal del sistema Unix que corresponde con el
|
|
número <tt/signum/. Útil para las funciones genéricas de manejo de señal.
|
|
|
|
Todo lo anterior está más o menos robado de <tt/glib.h/. Si alguien
|
|
quiere documentar una función, ¡sólo tiene que enviarme un correo-e!
|
|
|
|
<!-- ***************************************************************** -->
|
|
<sect>Ficheros rc de GTK
|
|
<!-- ***************************************************************** -->
|
|
<p>
|
|
GTK tiene su propia forma de conseguir los valores por defecto de una
|
|
aplicación, y es utilizando los ficheros <tt/rc/. Pueden ser
|
|
utilizados para poner los colores de cualquier <em/widget/, y también
|
|
pueden utilizarse para poner imágenes como fondos de algunos <em/widgets/.
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Funciones para los ficheros <tt/rc/
|
|
<p>
|
|
Cuando empiece su aplicación, debería incluir una llamada a:
|
|
|
|
<tscreen><verb>
|
|
void gtk_rc_parse( char *filename );
|
|
</verb></tscreen>
|
|
|
|
Poniendo el nombre del fichero de su rc. Esto hará que GTK analice
|
|
este fichero, y utilice el estilo para los <em/widgets/ que se definan
|
|
ahí.
|
|
|
|
Si desea tener un conjunto especial de <em/widgets/ con un estilo
|
|
diferente de los otros, o realizar cualquier otra división lógica de
|
|
los <em/widgets/, haga una llamada a:
|
|
|
|
<tscreen><verb>
|
|
void gtk_widget_set_name( GtkWidget *widget,
|
|
gchar *name );
|
|
</verb></tscreen>
|
|
|
|
Pasándole su nuevo <em/widget/ como primer argumento, y el nombre que
|
|
desea darle como el segundo. Mediante este nombre podrá cambiar los
|
|
atributos de ese <em/widget/.
|
|
|
|
Si hacemos algo así:
|
|
|
|
<tscreen><verb>
|
|
boton = gtk_button_new_with_label ("Botón especial");
|
|
gtk_widget_set_name (boton, "botón especial");
|
|
</verb></tscreen>
|
|
|
|
El botón tendrá el nombre «botón especial» y podría hacersele
|
|
referencia en el fichero <tt/rc/ como «botón especial.GtkButton».
|
|
[<--- ¡Verificadme! ]
|
|
|
|
El fichero de ejemplo <tt/rc/ que mostramos a continuación, establece las
|
|
propiedades de la ventana principal, y deja que todos los hijos de la
|
|
ventana principal hereden el estilo descrito por «main button». El
|
|
código utilizado en la aplicación es:
|
|
|
|
<tscreen><verb>
|
|
ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
|
gtk_widget_set_name (ventana, "main window");
|
|
</verb></tscreen>
|
|
|
|
Y el estilo se define en el fichero <tt/rc/ utilizando:
|
|
|
|
<tscreen><verb>
|
|
widget "main window.*GtkButton*" style "main_button"
|
|
</verb></tscreen>
|
|
|
|
Qué hace que todos los <em/widgets/ GtkButton de la «main window»
|
|
(ventana principal) tengan el estilo "main_buttons" tal y como se
|
|
define en el fichero <tt/rc/.
|
|
|
|
Como puede ver, es un sistema muy poderoso y flexible. Utilice su
|
|
imaginación para aprovecharse al máximo de este sistema.
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Formato de los ficheros <tt/rc/ de GTK
|
|
<p>
|
|
El formato de los ficheros GTK se muestra en el ejemplo de más
|
|
abajo. Éste es el fichero <tt/testgtkrc/ de la distribución GTK, pero he
|
|
añadido unos cuantos comentarios y alguna cosilla. Puede que quiera
|
|
incluir esta explicación en su aplicación para permitir al usuario
|
|
personalizar su aplicación.
|
|
|
|
Hay varias directivas para cambiar los atributos de un <em/widget/.
|
|
|
|
<itemize>
|
|
<item>fg - Establece el color de primer plano de un <em/widget/.
|
|
<item>bg - Establece el color de fondo de un <em/widget/.
|
|
<item>bg_pixmap - Establece la imagen que servirá de fondo al
|
|
<em/widget/ (como mosaico).
|
|
<item>font - Establece el tipo de letra que se utilizará con el
|
|
<em/widget/.
|
|
</itemize>
|
|
|
|
Además de esto, hay varios estados en el que puede estar un
|
|
<em/widget/, y puede especificar diferentes colores, imágenes y tipos
|
|
de letra para cada estado. Estos estados son:
|
|
|
|
<itemize>
|
|
<item>NORMAL - El estado normal de un <em/widget/, sin el ratón sobre
|
|
él, y no siendo presionado, etc...
|
|
<item>PRELIGHT - Cuando el ratón esté sobre este <em/widget/ se
|
|
utilizarán los colores definidos para este estado.
|
|
<item>ACTIVE - Cuando se presiona o se pulsa sobre el <em/widget/,
|
|
estará activo, y los atributos asignados por está etiqueta serán
|
|
utilizados.
|
|
<item>INSENSITIVE - Cuando un <em/widget/ es insensible, y no se puede
|
|
activar, tomará estos atributos.
|
|
<item>SELECTED - Cuando se seleccione un objeto, tomará estos atributos.
|
|
</itemize>
|
|
|
|
Cuando se utilizan las directivas «fg» y «bg» para poner los colores de
|
|
los <em/widgets/, se utilizará el formato siguiente:
|
|
|
|
<tscreen><verb>
|
|
fg[<STATE>] = { Red, Green, Blue }
|
|
</verb></tscreen>
|
|
|
|
Donde <tt/STATE/ es uno de los estados anteriores (PRELIGHT, ACTIVE,
|
|
etc...), y el <tt/Red/, <tt/Green/ y <tt/Blue/ (Rojo, Verde y Azul)
|
|
son valores en el rango 0 - 1.0, { 1.0, 1.0, 1.0 } es blanco. Deben
|
|
estar en formato flotante, o serán un 0, por lo que "1" no funcionará,
|
|
debe ser "1.0". Un "0" está bien ya que es lo mismo si no se
|
|
reconoce. Los valores no reconocidos se pondrán a 0.
|
|
|
|
<tt/bg_pixmap/ es muy similar al de arriba, salvo que los colores se
|
|
reemplazan por un nombre de fichero.
|
|
|
|
<tt/pixmap_path/ es una lista de los caminos (<em/paths/) separados por
|
|
«:». Estos caminos se utilizarán para buscar cualquier imagen que
|
|
indique.
|
|
|
|
La directiva sobre el tipo de letra es simplemente:
|
|
<tscreen><verb>
|
|
font = "<nombre del tipo de letra>"
|
|
</verb></tscreen>
|
|
|
|
Donde lo único difícil es saber la cadena del tipo de letra a
|
|
elegir. Utilizar <tt/xfontsel/ o un programa similar debería ayudar.
|
|
|
|
El <tt/widget_class/ establece el estilo de una clase de
|
|
<em/widgets/. Estas clases se muestran en el resumen de <em/widgets/
|
|
dentro de la jerarquía de clases.
|
|
|
|
La directiva <tt/widget/ hace que un conjunto específico de
|
|
<em/widgets/ tenga un estido determinado, sobreescribiendo cualquier
|
|
estilo anterior que tuviese esa clase de <em/widgets/. Estos
|
|
<em/widgets/ se registran dentro de la aplicación utilizando una
|
|
llamada a <tt/gtk_widget_set_name()/. Esto le permitirá especificar
|
|
los atributos de un <em/widget/ uno a uno, en vez de establecer los
|
|
atributos de toda una clase <em/widget/. Deberá documentar cualquiera
|
|
de estos <em/widgets/ especiales para que los usuarios puedan
|
|
personalizarlos.
|
|
|
|
Cuando la palabra clave <tt/parent/ se utiliza como un atributo, el
|
|
<em/widget/ tomará los atributos de su padre en la aplicación.
|
|
|
|
Puede asignar los atributos de un estilo previamente definido a uno
|
|
nuevo.
|
|
|
|
<tscreen><verb>
|
|
style "main_button" = "button"
|
|
{
|
|
font = "-adobe-helvetica-medium-r-normal--*-100-*-*-*-*-*-*"
|
|
bg[PRELIGHT] = { 0.75, 0, 0 }
|
|
}
|
|
</verb></tscreen>
|
|
|
|
Este ejemplo toma el estilo «button», y crea un nuevo estilo
|
|
«main_button» cambiando simplemente el tipo de letra y cambiando el
|
|
color de fondo cuando el <em/widget/ esté en estado <tt/PRELIGHT/.
|
|
|
|
Por supuesto, muchos de estos atributos no se aplican a todos los
|
|
<em/widgets/. Realmente es una cuestión de sentido común. Se utilizará
|
|
cualquier atributo que se pueda aplicar.
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Fichero <tt/rc/ de ejemplo
|
|
<p>
|
|
|
|
<!-- Esto hay que traducirlo -->
|
|
<tscreen><verb>
|
|
# pixmap_path "<dir 1>:<dir 2>:<dir 3>:..."
|
|
#
|
|
pixmap_path "/usr/include/X11R6/pixmaps:/home/imain/pixmaps"
|
|
#
|
|
# style <name> [= <name>]
|
|
# {
|
|
# <option>
|
|
# }
|
|
#
|
|
# widget <widget_set> style <style_name>
|
|
# widget_class <widget_class_set> style <style_name>
|
|
|
|
|
|
# Here is a list of all the possible states. Note that some do not apply to
|
|
# certain widgets.
|
|
#
|
|
# NORMAL - The normal state of a widget, without the mouse over top of
|
|
# it, and not being pressed etc.
|
|
#
|
|
# PRELIGHT - When the mouse is over top of the widget, colors defined
|
|
# using this state will be in effect.
|
|
#
|
|
# ACTIVE - When the widget is pressed or clicked it will be active, and
|
|
# the attributes assigned by this tag will be in effect.
|
|
#
|
|
# INSENSITIVE - When a widget is set insensitive, and cannot be
|
|
# activated, it will take these attributes.
|
|
#
|
|
# SELECTED - When an object is selected, it takes these attributes.
|
|
#
|
|
# Given these states, we can set the attributes of the widgets in each of
|
|
# these states using the following directives.
|
|
#
|
|
# fg - Sets the foreground color of a widget.
|
|
# fg - Sets the background color of a widget.
|
|
# bg_pixmap - Sets the background of a widget to a tiled pixmap.
|
|
# font - Sets the font to be used with the given widget.
|
|
#
|
|
|
|
# This sets a style called "button". The name is not really important, as
|
|
# it is assigned to the actual widgets at the bottom of the file.
|
|
|
|
style "window"
|
|
{
|
|
#This sets the padding around the window to the pixmap specified.
|
|
#bg_pixmap[<STATE>] = "<pixmap filename>"
|
|
bg_pixmap[NORMAL] = "warning.xpm"
|
|
}
|
|
|
|
style "scale"
|
|
{
|
|
#Sets the foreground color (font color) to red when in the "NORMAL"
|
|
#state.
|
|
|
|
fg[NORMAL] = { 1.0, 0, 0 }
|
|
|
|
#Sets the background pixmap of this widget to that of its parent.
|
|
bg_pixmap[NORMAL] = "<parent>"
|
|
}
|
|
|
|
style "button"
|
|
{
|
|
# This shows all the possible states for a button. The only one that
|
|
# doesn't apply is the SELECTED state.
|
|
|
|
fg[PRELIGHT] = { 0, 1.0, 1.0 }
|
|
bg[PRELIGHT] = { 0, 0, 1.0 }
|
|
bg[ACTIVE] = { 1.0, 0, 0 }
|
|
fg[ACTIVE] = { 0, 1.0, 0 }
|
|
bg[NORMAL] = { 1.0, 1.0, 0 }
|
|
fg[NORMAL] = { .99, 0, .99 }
|
|
bg[INSENSITIVE] = { 1.0, 1.0, 1.0 }
|
|
fg[INSENSITIVE] = { 1.0, 0, 1.0 }
|
|
}
|
|
|
|
# In this example, we inherit the attributes of the "button" style and then
|
|
# override the font and background color when prelit to create a new
|
|
# "main_button" style.
|
|
|
|
style "main_button" = "button"
|
|
{
|
|
font = "-adobe-helvetica-medium-r-normal--*-100-*-*-*-*-*-*"
|
|
bg[PRELIGHT] = { 0.75, 0, 0 }
|
|
}
|
|
|
|
style "toggle_button" = "button"
|
|
{
|
|
fg[NORMAL] = { 1.0, 0, 0 }
|
|
fg[ACTIVE] = { 1.0, 0, 0 }
|
|
|
|
# This sets the background pixmap of the toggle_button to that of its
|
|
# parent widget (as defined in the application).
|
|
bg_pixmap[NORMAL] = "<parent>"
|
|
}
|
|
|
|
style "text"
|
|
{
|
|
bg_pixmap[NORMAL] = "marble.xpm"
|
|
fg[NORMAL] = { 1.0, 1.0, 1.0 }
|
|
}
|
|
|
|
style "ruler"
|
|
{
|
|
font = "-adobe-helvetica-medium-r-normal--*-80-*-*-*-*-*-*"
|
|
}
|
|
|
|
# pixmap_path "~/.pixmaps"
|
|
|
|
# These set the widget types to use the styles defined above.
|
|
# The widget types are listed in the class hierarchy, but could probably be
|
|
# just listed in this document for the users reference.
|
|
|
|
widget_class "GtkWindow" style "window"
|
|
widget_class "GtkDialog" style "window"
|
|
widget_class "GtkFileSelection" style "window"
|
|
widget_class "*Gtk*Scale" style "scale"
|
|
widget_class "*GtkCheckButton*" style "toggle_button"
|
|
widget_class "*GtkRadioButton*" style "toggle_button"
|
|
widget_class "*GtkButton*" style "button"
|
|
widget_class "*Ruler" style "ruler"
|
|
widget_class "*GtkText" style "text"
|
|
|
|
# This sets all the buttons that are children of the "main window" to
|
|
# the main_button style. These must be documented to be taken advantage of.
|
|
widget "main window.*GtkButton*" style "main_button"
|
|
</verb></tscreen>
|
|
|
|
<!-- ***************************************************************** -->
|
|
<sect>Escribiendo sus propios <em/widgets/
|
|
<!-- ***************************************************************** -->
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1> Visión general
|
|
<p>
|
|
Aunque la distribución de GTK viene con muchos tipos de <em/widgets/
|
|
que debería cubrir todas la mayoría de las necesidades básicas, puede
|
|
que haya llegado el momento en que necesite crear su propio
|
|
<em/widget/. Debido a que GTK utiliza mucho la herencia de
|
|
<em/widgets/, y si ya hay un <em/widget/ que se acerque lo suficiente
|
|
a lo que quiere, tal vez pueda hacer un nuevo <em/widget/ con tan solo
|
|
unas cuantas líneas de código. Pero antes de empezar a trabajar en un
|
|
nuevo <em/widget/, asegúrese primero de que no hay nadie que ya haya
|
|
hecho otro parecido. Así evitará la duplicación de esfuerzo y
|
|
mantendrá el número de <em/widgets/ GTK en su valor mínimo, lo que
|
|
ayudará a que el código y la interfaz de las diferentes aplicaciones
|
|
sea consistente. Por otra parte, cuando haya acabado su <em/widget/,
|
|
anúncielo al mundo entreo para que todo el mundo se pueda
|
|
beneficiar. Probablemente el mejor lugar para hacerlo sea la
|
|
<tt>gtk-list</tt>.
|
|
|
|
Las fuentes completas de los <em/widgets/ de ejemplo están disponibles
|
|
en el mismo lugar en el que consiguió este tutorial, o en:
|
|
|
|
<htmlurl url="http://www.gtk.org/~otaylor/gtk/tutorial/"
|
|
name="http://www.gtk.org/~otaylor/gtk/tutorial/">
|
|
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1> La anatomía de un <em/widget/
|
|
<p>
|
|
Para crear un nuevo <em/widget/, es importante conocer como funcionan
|
|
los objetos de GTK. Esta sección es sólo un breve resumen. Ver la
|
|
documentación a la que se hace referencia para obtener más detalles.
|
|
|
|
Los widgets GTK están implementados siguiendo una orientación a
|
|
objetos. Sin embargo, están implementados en C estándar. De esta forma
|
|
se mejora enormemente la portabilidad y la estabilidad con respecto a
|
|
la actual generación de compiladores C++; sin embargo, con todo esto
|
|
no queremos decir que el creador de <em/widgets/ tenga que prestar
|
|
atención a ninguno de los detalles de implementación. La información
|
|
que es común a todos los <em/widgets/ de una clase de <em/widgets/
|
|
(p.e., a todos los <em/widgets/ botón) se almacena en la
|
|
<em>estructura de clase</em>. Sólo hay una copia de ésta en la que se
|
|
almacena información sobre las señales de la clase (que actuan como
|
|
funciones virtuales en C). Para permitir la herencia, el primer campo
|
|
en la estructura de la clase debe ser una copia de la estructura de la
|
|
clase del padre. La declaración de la estructura de la clase de
|
|
GtkButton debe ser algo así:
|
|
|
|
<tscreen><verb>
|
|
struct _GtkButtonClass
|
|
{
|
|
GtkContainerClass parent_class;
|
|
|
|
void (* pressed) (GtkButton *button);
|
|
void (* released) (GtkButton *button);
|
|
void (* clicked) (GtkButton *button);
|
|
void (* enter) (GtkButton *button);
|
|
void (* leave) (GtkButton *button);
|
|
};
|
|
</verb></tscreen>
|
|
|
|
Cuando un botón se trata como un contenedor (por ejemplo, cuando se le
|
|
cambia el tamaño), su estructura de clase puede convertirse a
|
|
GtkContainerClass, y los campos relevantes se utilizarán para manejar
|
|
las señales.
|
|
|
|
También hay una estructura que se crea para cada <em/widget/. Esta
|
|
estructura tiene campos para almacenar la información que es diferente
|
|
para cada copia del <em/widget/. Nosotros llamaremos a esta estructura
|
|
la <em>estructura objeto</em>. Para la clase botón, es así:
|
|
|
|
<tscreen><verb>
|
|
struct _GtkButton
|
|
{
|
|
GtkContainer container;
|
|
|
|
GtkWidget *child;
|
|
|
|
guint in_button : 1;
|
|
guint button_down : 1;
|
|
};
|
|
</verb></tscreen>
|
|
|
|
Observe que, como en la estructura de clase, el primer campo es la
|
|
estructura objeto de la clase padre, por lo que esta estructura puede
|
|
convertirse en la estructura de la clase del objeto padre cuando haga
|
|
falta.
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1> Creando un <em/widget/ compuesto
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect2> Introducción
|
|
<p>
|
|
Un tipo de widget que puede interesarnos es uno que sea un mero
|
|
agregado de otros <em/widgets/ GTK. Este tipo de <em/widget/ no hace
|
|
nada que no pueda hacerse sin la necesidad de crear un nuevo
|
|
<em/widget/, pero proporciona una forma conveniente de empaquetar los
|
|
elementos del interfaz de usuario para su reutilización. Los
|
|
<em/widgets/ <tt/FileSelection/ y <tt/ColorSelection/ incluidos en la
|
|
distribución estándar son ejemplos de este tipo de <em/widgets/.
|
|
|
|
El <em/widget/ ejemplo que hemos creado en esta sección es el
|
|
<em/widget/ Tictactoe, una matriz de 3x3 de botones de selección que
|
|
lanza una señal cuando están deseleccionados tres botones en una misma
|
|
fila, columna, o diagonal.
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect2> Escogiendo una clase padre
|
|
<p>
|
|
Normalmente la clase padre para un <em/widget/ compuesto es la clase
|
|
contenedor que tenga todos los elementos del <em/widget/
|
|
compuesto. Por ejemplo, la clase padre del <em/widget/
|
|
<tt/FileSelection/ es la clase <tt/Dialog/. Ya que nuestros botones se
|
|
ordenarán en una tabla, parece natural hacer que nuestra clase padre
|
|
sea la clase <tt/GtkTable/. Desafortunadamente, esto no
|
|
funcionaría. La creación de un <em/widget/ se divide en dos funciones
|
|
- una función <tt/NOMBREWIDGET_new()/ que utilizará el usuario, y una
|
|
función <tt/NOMBREWIDGET_init()/ que hará el trabajo básico de
|
|
inicializar el <em/widget/ que es independiente de los argumentos que
|
|
se le pasen a la función <tt/_new()/. Los <em/widgets/ derivados sólo
|
|
llaman a la función <tt/_init/ de su <em/widget/ padre. Pero esta
|
|
división del trabajo no funciona bien con las tablas, que necesitan
|
|
saber en el momento de su creación el número de filas y de columnas
|
|
que deben tener. A menos que queramos duplicar la mayor parte de lo
|
|
hecho en <tt/gtk_table_new()/ en nuestro <em/widget/ Tictactoe,
|
|
haremos mejor si evitamos derivar de GtkTable. Por esta razón,
|
|
derivaremos de <tt/GtkVBox/, y meteremos nuestra tabla dentro de la
|
|
caja vertical.
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect2> El fichero de cabecera
|
|
<p>
|
|
Cada clase <em/widget/ tiene un fichero de cabecera que declara el
|
|
objeto y las estructuras de clase para ese <em/widget/, así como las
|
|
funciones públicas. Un par de características que merecen dejarse
|
|
aparte. Para evitar la duplicación de definiciones, meteremos el
|
|
fichero de cabecera al completo entre:
|
|
|
|
<tscreen><verb>
|
|
#ifndef __TICTACTOE_H__
|
|
#define __TICTACTOE_H__
|
|
.
|
|
.
|
|
.
|
|
#endif /* __TICTACTOE_H__ */
|
|
</verb></tscreen>
|
|
|
|
Y para que los programas en C++ incluyan sin problemas el fichero de
|
|
cabecera, pondremos:
|
|
|
|
<tscreen><verb>
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif /* __cplusplus */
|
|
.
|
|
.
|
|
.
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif /* __cplusplus */
|
|
</verb></tscreen>
|
|
|
|
Con las funciones y las estructuras, declararemos tres macros estándar
|
|
en nuestro fichero de cabecera, <tt/TICTACTOE(obj)/,
|
|
<tt/TICTACTOE_CLASS(class)/, y <tt/IS_TICTACTOE(obj)/, que,
|
|
convierten, respectivamente, un puntero en un puntero al objeto o a la
|
|
estructura de la clase, y comprueba si un objeto es un <em/widget/
|
|
Tictactoe.
|
|
|
|
Aquí está el fichero de cabecera al completo:
|
|
|
|
<tscreen><verb>
|
|
/* tictactoe.h */
|
|
|
|
#ifndef __TICTACTOE_H__
|
|
#define __TICTACTOE_H__
|
|
|
|
#include <gdk/gdk.h>
|
|
#include <gtk/gtkvbox.h>
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif /* __cplusplus */
|
|
|
|
#define TICTACTOE(obj) GTK_CHECK_CAST (obj, tictactoe_get_type (), Tictactoe)
|
|
#define TICTACTOE_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, tictactoe_get_type (), TictactoeClass)
|
|
#define IS_TICTACTOE(obj) GTK_CHECK_TYPE (obj, tictactoe_get_type ())
|
|
|
|
|
|
typedef struct _Tictactoe Tictactoe;
|
|
typedef struct _TictactoeClass TictactoeClass;
|
|
|
|
struct _Tictactoe
|
|
{
|
|
GtkVBox vbox;
|
|
|
|
GtkWidget *botones[3][3];
|
|
};
|
|
|
|
struct _TictactoeClass
|
|
{
|
|
GtkVBoxClass parent_class;
|
|
|
|
void (* tictactoe) (Tictactoe *ttt);
|
|
};
|
|
|
|
guint tictactoe_get_type (void);
|
|
GtkWidget* tictactoe_new (void);
|
|
void tictactoe_clear (Tictactoe *ttt);
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif /* __cplusplus */
|
|
|
|
#endif /* __TICTACTOE_H__ */
|
|
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect2> La función <tt/_get_type()/.
|
|
<p>
|
|
Ahora continuaremos con la implementación de nuestro <em/widget/. Una
|
|
función del núcleo de todo <em/widget/ es
|
|
<tt/NOMBREWIDGET_get_type()/. Cuando se llame a esta función por
|
|
vez primera, le informará a GTK sobre la clase del <em/widget/, y
|
|
devolverá un ID que identificará unívocamente la clase <em/widget/. En
|
|
las llamadas siguientes, lo único que hará será devolver el ID.
|
|
|
|
<tscreen><verb>
|
|
guint
|
|
tictactoe_get_type ()
|
|
{
|
|
static guint ttt_type = 0;
|
|
|
|
if (!ttt_type)
|
|
{
|
|
GtkTypeInfo ttt_info =
|
|
{
|
|
"Tictactoe",
|
|
sizeof (Tictactoe),
|
|
sizeof (TictactoeClass),
|
|
(GtkClassInitFunc) tictactoe_class_init,
|
|
(GtkObjectInitFunc) tictactoe_init,
|
|
(GtkArgSetFunc) NULL,
|
|
(GtkArgGetFunc) NULL
|
|
};
|
|
|
|
ttt_type = gtk_type_unique (gtk_vbox_get_type (), &ttt_info);
|
|
}
|
|
|
|
return ttt_type;
|
|
}
|
|
</verb></tscreen>
|
|
|
|
La estructura GtkTypeInfo tiene la definición siguiente:
|
|
|
|
<tscreen><verb>
|
|
struct _GtkTypeInfo
|
|
{
|
|
gchar *type_name;
|
|
guint object_size;
|
|
guint class_size;
|
|
GtkClassInitFunc class_init_func;
|
|
GtkObjectInitFunc object_init_func;
|
|
GtkArgSetFunc arg_set_func;
|
|
GtkArgGetFunc arg_get_func;
|
|
};
|
|
</verb></tscreen>
|
|
|
|
Los utilidad de cada campo de esta estructura se explica por su propio
|
|
nombre. Ignoraremos por ahora los campos <tt/arg_set_func/
|
|
y <tt/arg_get_func/: son importantes, pero todavía es raro
|
|
utilizarlos, su papel es permitir que las opciones de los <em/wdigets/
|
|
puedan establecerse correctamente mediante lenguajes
|
|
interpretados. Una vez que GTK tiene una copia de esta estructura
|
|
correctamente rellenada, sabrá como crear objetos de un tipo
|
|
particular de <em/widget/.
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect2> La función <tt/_class_init()/
|
|
<p>
|
|
La función <tt/NOMBREWIDGET_class_init()/ inicializa los campos de la
|
|
estructura clase del <em/widget/, y establece las señales de la
|
|
clase. Para nuestro <em/widget/ Tictactoe será una cosa así:
|
|
|
|
<tscreen><verb>
|
|
|
|
enum {
|
|
TICTACTOE_SIGNAL,
|
|
LAST_SIGNAL
|
|
};
|
|
|
|
static gint tictactoe_signals[LAST_SIGNAL] = { 0 };
|
|
|
|
static void
|
|
tictactoe_class_init (TictactoeClass *class)
|
|
{
|
|
GtkObjectClass *object_class;
|
|
|
|
object_class = (GtkObjectClass*) class;
|
|
|
|
tictactoe_signals[TICTACTOE_SIGNAL] = gtk_signal_new ("tictactoe",
|
|
GTK_RUN_FIRST,
|
|
object_class->type,
|
|
GTK_SIGNAL_OFFSET (TictactoeClass, tictactoe),
|
|
gtk_signal_default_marshaller, GTK_TYPE_NONE, 0);
|
|
|
|
|
|
gtk_object_class_add_signals (object_class, tictactoe_signals, LAST_SIGNAL);
|
|
|
|
class->tictactoe = NULL;
|
|
}
|
|
</verb></tscreen>
|
|
|
|
Nuestro <em/widget/ sólo tiene una señal, la señal <tt/tictactoe/ que
|
|
se invoca cuando una fila, columna, o diagonal se rellena
|
|
completamente. No todos los <em/widgets/ compuestos necesitan señales,
|
|
por lo que si está leyendo esto por primera vez, puede que sea mejor
|
|
que pase a la sección siguiente, ya que las cosas van a complicarse un
|
|
poco.
|
|
|
|
La función:
|
|
|
|
<tscreen><verb>
|
|
gint gtk_signal_new( const gchar *name,
|
|
GtkSignalRunType run_type,
|
|
GtkType object_type,
|
|
gint function_offset,
|
|
GtkSignalMarshaller marshaller,
|
|
GtkType return_val,
|
|
guint nparams,
|
|
...);
|
|
</verb></tscreen>
|
|
|
|
crea una nueva señal. Los parámetros son:
|
|
|
|
<itemize>
|
|
<item> <tt/name/: El nombre de la señal.
|
|
<item> <tt/run_type/: Si el manejador por defecto se ejecuta antes o
|
|
despues del manejador de usuario. Normalmente debe ser
|
|
<tt/GTK_RUN_FIRST/, o <tt/GTK_RUN_LAST/, aunque hay otras
|
|
posibilidades.
|
|
<item> <tt/object_type/: El ID del objeto al que se le aplica esta
|
|
señal. (También se aplicará a los descendientes de los objetos)
|
|
<item> <tt/function_offset/: El desplazamiento en la estructura de la
|
|
clase de un puntero al manejador por defecto.
|
|
<item> <tt/marshaller/: Una función que se utiliza para invocar al
|
|
manejador de señal. Para los manejadores de señal que no tengan más
|
|
argumentos que el objeto que emitió la señal podemos utilizar la
|
|
función marshaller por defecto <tt/gtk_signal_default_marshaller/.
|
|
<item> <tt/return_val/: El tipo del valor devuelto.
|
|
<item> <tt/nparams/: El número de parámetros del manejador de señal
|
|
(distintos de los dos por defecto que hemos mencionado arriba).
|
|
<item> <tt/.../: Los tipos de los parámetros.
|
|
</itemize>
|
|
|
|
Cuando se especifican los tipos, se utilizará la enumeración
|
|
<tt/GtkType/:
|
|
|
|
<tscreen><verb>
|
|
typedef enum
|
|
{
|
|
GTK_TYPE_INVALID,
|
|
GTK_TYPE_NONE,
|
|
GTK_TYPE_CHAR,
|
|
GTK_TYPE_BOOL,
|
|
GTK_TYPE_INT,
|
|
GTK_TYPE_UINT,
|
|
GTK_TYPE_LONG,
|
|
GTK_TYPE_ULONG,
|
|
GTK_TYPE_FLOAT,
|
|
GTK_TYPE_DOUBLE,
|
|
GTK_TYPE_STRING,
|
|
GTK_TYPE_ENUM,
|
|
GTK_TYPE_FLAGS,
|
|
GTK_TYPE_BOXED,
|
|
GTK_TYPE_FOREIGN,
|
|
GTK_TYPE_CALLBACK,
|
|
GTK_TYPE_ARGS,
|
|
|
|
GTK_TYPE_POINTER,
|
|
|
|
/* it'd be great if the next two could be removed eventually */
|
|
GTK_TYPE_SIGNAL,
|
|
GTK_TYPE_C_CALLBACK,
|
|
|
|
GTK_TYPE_OBJECT
|
|
|
|
} GtkFundamentalType;
|
|
</verb></tscreen>
|
|
|
|
<tt/gtk_signal_new()/ devuelve un identificador entero único para la
|
|
señal, que almacenamos en el vector <tt/tictactoe_signals/, que
|
|
indexaremos utilizando una enumeración. (Convencionalmente, los
|
|
elementos de la enumeración son el nombre de la señal, en mayúsculas,
|
|
pero aquí tendríamos un conflicto con la macro <tt/TICTACTOE()/, por
|
|
lo que lo llamaremos <tt/TICTACTOE_SIGNAL/.
|
|
|
|
Después de crear nuestras señales, necesitamos llamar a GTK para
|
|
asociarlas con la clase Tictactoe. Hacemos esto llamando a
|
|
<tt/gtk_object_class_add_signals()/. Entonces haremos que el puntero
|
|
que apunta al manejador por defecto para la señal `tictactoe' sea NULL,
|
|
indicando que no hay ninguna acción por defecto.
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect2> La función <tt/_init()/.
|
|
<p>
|
|
Cada clase <em/widget/ también necesita una función para inicializar
|
|
la estructura del objeto. Normalmente, esta función tiene el limitado
|
|
rol de poner los distintos campos de la estructura a su valor por
|
|
defecto. Sin embargo para los <em/widgets/ de composición, esta
|
|
función también crea los distintos <em/widgets/ componentes.
|
|
|
|
<tscreen><verb>
|
|
static void
|
|
tictactoe_init (Tictactoe *ttt)
|
|
{
|
|
GtkWidget *table;
|
|
gint i,j;
|
|
|
|
table = gtk_table_new (3, 3, TRUE);
|
|
gtk_container_add (GTK_CONTAINER(ttt), table);
|
|
gtk_widget_show (table);
|
|
|
|
for (i=0;i<3; i++)
|
|
for (j=0;j<3; j++)
|
|
{
|
|
ttt->buttons[i][j] = gtk_toggle_button_new ();
|
|
gtk_table_attach_defaults (GTK_TABLE(table), ttt->buttons[i][j],
|
|
i, i+1, j, j+1);
|
|
gtk_signal_connect (GTK_OBJECT (ttt->buttons[i][j]), "toggled",
|
|
GTK_SIGNAL_FUNC (tictactoe_toggle), ttt);
|
|
gtk_widget_set_usize (ttt->buttons[i][j], 20, 20);
|
|
gtk_widget_show (ttt->buttons[i][j]);
|
|
}
|
|
}
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect2> Y el resto...
|
|
<p>
|
|
Hay una función más que cada <em/widget/ (excepto los <em/widget/ muy
|
|
básicos como GtkBin que no pueden crear objetos) tiene que
|
|
tener - la función que el usuario llama para crear un objeto de ese
|
|
tipo. Normalmente se llama <tt/NOMBREWIDGET_new()/. En algunos
|
|
<em/widgets/, que no es el caso del <em/widget/ Tictactoe, esta
|
|
función toma argumentos, y hace alguna inicialización en función de
|
|
estos. Las otras dos funciones son específicas al <em/widget/
|
|
Tictactoe.
|
|
|
|
<tt/tictactoe_clear()/ es una función pública que reinicia todos los
|
|
botones en el <em/widget/ a la posición alta. Observe la utilización
|
|
de <tt/gtk_signal_handler_block_by_data()/ para hacer que no se
|
|
ejecute nuestro manejador de señal innecesariamente por cambios en los
|
|
botones.
|
|
|
|
<tt/tictactoe_toggle()/ es el manejador de señal que se invoca cuando
|
|
el usuario pulsa un botón. Hace una comprobación para ver si hay
|
|
alguna combinación ganadora, y si la hay, emite la señal
|
|
«tictactoe».
|
|
|
|
<tscreen><verb>
|
|
GtkWidget*
|
|
tictactoe_new ()
|
|
{
|
|
return GTK_WIDGET ( gtk_type_new (tictactoe_get_type ()));
|
|
}
|
|
|
|
void
|
|
tictactoe_clear (Tictactoe *ttt)
|
|
{
|
|
int i,j;
|
|
|
|
for (i=0;i<3;i++)
|
|
for (j=0;j<3;j++)
|
|
{
|
|
gtk_signal_handler_block_by_data (GTK_OBJECT(ttt->buttons[i][j]), ttt);
|
|
gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (ttt->buttons[i][j]),
|
|
FALSE);
|
|
gtk_signal_handler_unblock_by_data (GTK_OBJECT(ttt->buttons[i][j]), ttt);
|
|
}
|
|
}
|
|
|
|
static void
|
|
tictactoe_toggle (GtkWidget *widget, Tictactoe *ttt)
|
|
{
|
|
int i,k;
|
|
|
|
static int rwins[8][3] = { { 0, 0, 0 }, { 1, 1, 1 }, { 2, 2, 2 },
|
|
{ 0, 1, 2 }, { 0, 1, 2 }, { 0, 1, 2 },
|
|
{ 0, 1, 2 }, { 0, 1, 2 } };
|
|
static int cwins[8][3] = { { 0, 1, 2 }, { 0, 1, 2 }, { 0, 1, 2 },
|
|
{ 0, 0, 0 }, { 1, 1, 1 }, { 2, 2, 2 },
|
|
{ 0, 1, 2 }, { 2, 1, 0 } };
|
|
|
|
int success, found;
|
|
|
|
for (k=0; k<8; k++)
|
|
{
|
|
success = TRUE;
|
|
found = FALSE;
|
|
|
|
for (i=0;i<3;i++)
|
|
{
|
|
success = success &&
|
|
GTK_TOGGLE_BUTTON(ttt->buttons[rwins[k][i]][cwins[k][i]])->active;
|
|
found = found ||
|
|
ttt->buttons[rwins[k][i]][cwins[k][i]] == widget;
|
|
}
|
|
|
|
if (success && found)
|
|
{
|
|
gtk_signal_emit (GTK_OBJECT (ttt),
|
|
tictactoe_signals[TICTACTOE_SIGNAL]);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
</verb></tscreen>
|
|
|
|
Y finalmente, un programa ejemplo que utiliza nuestro <em/widget/
|
|
Tictactoe:
|
|
|
|
<tscreen><verb>
|
|
#include <gtk/gtk.h>
|
|
#include "tictactoe.h"
|
|
|
|
/* Invocado cuando se completa una fila, columna o diagonal */
|
|
void
|
|
win (GtkWidget *widget, gpointer data)
|
|
{
|
|
g_print ("Yay!\n");
|
|
tictactoe_clear (TICTACTOE (widget));
|
|
}
|
|
|
|
int
|
|
main (int argc, char *argv[])
|
|
{
|
|
GtkWidget *ventana;
|
|
GtkWidget *ttt;
|
|
|
|
gtk_init (&argc, &argv);
|
|
|
|
ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
|
|
|
gtk_window_set_title (GTK_WINDOW (ventana), "Aspect Frame");
|
|
|
|
gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
|
|
GTK_SIGNAL_FUNC (gtk_exit), NULL);
|
|
|
|
gtk_container_border_width (GTK_CONTAINER (ventana), 10);
|
|
|
|
/* Create a new Tictactoe widget */
|
|
ttt = tictactoe_new ();
|
|
gtk_container_add (GTK_CONTAINER (ventana), ttt);
|
|
gtk_widget_show (ttt);
|
|
|
|
/* And attach to its "tictactoe" signal */
|
|
gtk_signal_connect (GTK_OBJECT (ttt), "tictactoe",
|
|
GTK_SIGNAL_FUNC (win), NULL);
|
|
|
|
gtk_widget_show (ventana);
|
|
|
|
gtk_main ();
|
|
|
|
return 0;
|
|
}
|
|
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1> Creando un <em/widget/ desde cero.
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect2> Introducción
|
|
<p>
|
|
En esta sección, averiguaremos como se dibujan los <em/widgets/ a sí
|
|
mismos en pantalla y como interactuan con los eventos. Como ejemplo,
|
|
crearemos un marcador analógico con un puntero que el usuario
|
|
podrá arrastrar para hacer que el marcador tenga un valor dado.
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect2> Mostrando un <em/widget/ en la pantalla
|
|
<p>
|
|
Hay varios pasos que están involucrados en el dibujado en pantalla.
|
|
Después de que el <em/widget/ se cree con una llamada a
|
|
<tt/NOMBREWIDGET_new()/, se necesitarán muchas más funciones:
|
|
|
|
<itemize>
|
|
<item> <tt/NOMBREWIDGET_realize()/ es la responsable de crear una
|
|
ventana X para el <em/widget/, si tiene alguna.
|
|
<item> <tt/NOMBREWIDGET_map()/ se invoca después de las llamadas del
|
|
usuario
|
|
<tt/gtk_widget_show()/. Es la responsable de asegurarse de que el
|
|
<em/widget/ está dibujado (<em/mapeado/) en la pantalla. Para una
|
|
clase contenedor, también deberá ocuparse de llamar a las funciones
|
|
<tt/map()/ de cada <em/widget/ hijo.
|
|
<item> <tt/NOMBREWIDGET_draw()/ se invoca cuando se llama a
|
|
<tt/gtk_widget_draw()/ desde el <em/widget/ de uno de sus
|
|
antepasados. Hace las llamadas necesarias a las funciones de dibujo
|
|
para dibujar el <em/widget/ en la pantalla. Para los <em/widgets/
|
|
contenedores, esta función debe llamar a las <tt/gtk_widget_draw/ de
|
|
sus <em/widgets/ hijos.
|
|
<item> <tt/NOMBREWIDGET_expose()/ es un manejador de los eventos
|
|
<tt/expose/ del <em/widget/. Hace las llamadas necesarias a las
|
|
funciones de dibujo para dibujar la parte expuesta en la
|
|
pantalla. Para los <em/widgets/ contenedores, esta función debe
|
|
generar los eventos <tt/expose/ de sus <em/widgets/ hijos que no
|
|
tengan su propia ventana. (Si tuviesen su propia ventana, X generaría
|
|
los eventos <tt/expose/ necesarios)
|
|
</itemize>
|
|
|
|
Las últimas dos funciones son bastante similares - ambas son
|
|
responsables de dibujar el <em/widget/ en pantalla. De hecho en muchos
|
|
<em/widgets/ realmente no importa la diferencia que hay entre ambas
|
|
funciones. La función <em/draw()/ que hay por defecto en
|
|
la clase <em/widget/ simplemente genera un evento <tt/expose/
|
|
artificial de la zona a redibujar. Sin embargo, algunos tipos de
|
|
<em/widgets/ puede ahorrarse trabajo distinguiendo entre las dos
|
|
funciones. Por ejemplo, si un <em/widget/ tiene varias ventanas X,
|
|
entonces, como los eventos <tt/expose/ identifican a la ventana
|
|
expuesta, podrán redibujar sólo la ventana afectada, lo que no es
|
|
posible con llamadas a <tt/draw()/.
|
|
|
|
Los <em/widgets/ contenedores, aunque no utilicen la diferecia
|
|
existente entre las dos funciones por sí mismos, no pueden utilizar
|
|
simplemente las funciones <tt/draw()/ que hay por defecto ya que sus
|
|
<em/widgets/ hijos puede que tengan que utilizar la diferencia. Sin
|
|
embargo, sería un derroche duplicar el código de dibujado entre las
|
|
dos funciones. Lo normal es que cada <em/widget/ tenga una función
|
|
llamada <tt/NOMBREWIDGET_paint()/ que haga el trabajo de dibujar el
|
|
<em/widget/, ésta función será a la que se llame por las funciones
|
|
<tt/draw()/ y <tt/expose()/.
|
|
|
|
En nuestro ejemplo, como el <em/widget/ Dial no es un <em/widget/
|
|
contenedor, y sólo tiene una ventana, podemos tomar el camino más
|
|
corto, utilizar la función <tt/draw()/ por defecto y sólo
|
|
implementar la función <tt/expose()/.
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect2> Los orígenes del <em/widget/ Dial
|
|
<p>
|
|
Así como todos los animales terrestes son variaciones del primer
|
|
anfíbio que salió del barro, los <em/widgets/ Gtk tienden a nacer
|
|
como variaciones de algún otro <em/widget/ escrito previamente. Por
|
|
tanto, aunque esta sección se titule `Creando un <em/widget/ de la
|
|
nada', el <em/widget/ Dial empieza realmente con el código fuente
|
|
del <em/widget/ Range. He tomado éste como punto de arranque porque
|
|
sería bonito que nuestro dial tuviese la misma interfaz que los
|
|
<em/widgets/ Scale, que son sólo una especialización del <em/widget/
|
|
Range. Por tanto, aunque el código fuente se presente más adelante en
|
|
su forma final, no implica que fuese escrito de esta forma <em>deus ex
|
|
machina</em>. Si todavía no está familiarizado, desde el punto de
|
|
vista del escritor de aplicaciones, con la forma de funcionar de los
|
|
<em/widgets/ Scale, sería una buena idea echarles un vistazo antes de
|
|
continuar.
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect2> Los comienzos
|
|
<p>
|
|
Nuestro <em/widget/ tiene un aspecto algo parecido al del <em/widget/
|
|
Tictactoe. Primero, tenemos un fichero de cabecera:
|
|
|
|
<tscreen><verb>
|
|
/* GTK - The GIMP Toolkit
|
|
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library; if not, write to the Free
|
|
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
|
|
#ifndef __GTK_DIAL_H__
|
|
#define __GTK_DIAL_H__
|
|
|
|
#include <gdk/gdk.h>
|
|
#include <gtk/gtkadjustment.h>
|
|
#include <gtk/gtkwidget.h>
|
|
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif /* __cplusplus */
|
|
|
|
|
|
#define GTK_DIAL(obj) GTK_CHECK_CAST (obj, gtk_dial_get_type (), GtkDial)
|
|
#define GTK_DIAL_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, gtk_dial_get_type (), GtkDialClass)
|
|
#define GTK_IS_DIAL(obj) GTK_CHECK_TYPE (obj, gtk_dial_get_type ())
|
|
|
|
|
|
typedef struct _GtkDial GtkDial;
|
|
typedef struct _GtkDialClass GtkDialClass;
|
|
|
|
struct _GtkDial
|
|
{
|
|
GtkWidget widget;
|
|
|
|
/* política de actualización
|
|
* (GTK_UPDATE_[CONTINUOUS/DELAYED/DISCONTINUOUS]) */
|
|
guint policy : 2;
|
|
|
|
/* Botón actualmente presionado o 0 si no hay ninguno */
|
|
guint8 boton;
|
|
|
|
/* Dimensión de los componendes del dial */
|
|
gint radius;
|
|
gint pointer_width;
|
|
|
|
/* ID del temporizador de actualización, o 0 si no hay ninguno */
|
|
guint32 timer;
|
|
|
|
/* ángulo actual */
|
|
gfloat angle;
|
|
|
|
/* Viejos valores almacenados del adjustment, para que así no
|
|
* tengamos que saber cuando cambia algo */
|
|
gfloat old_value;
|
|
gfloat old_lower;
|
|
gfloat old_upper;
|
|
|
|
/* El objeto adjustment que almacena los datos para este dial */
|
|
GtkAdjustment *adjustment;
|
|
};
|
|
|
|
struct _GtkDialClass
|
|
{
|
|
GtkWidgetClass parent_class;
|
|
};
|
|
|
|
|
|
GtkWidget* gtk_dial_new (GtkAdjustment *adjustment);
|
|
guint gtk_dial_get_type (void);
|
|
GtkAdjustment* gtk_dial_get_adjustment (GtkDial *dial);
|
|
void gtk_dial_set_update_policy (GtkDial *dial,
|
|
GtkUpdateType policy);
|
|
|
|
void gtk_dial_set_adjustment (GtkDial *dial,
|
|
GtkAdjustment *adjustment);
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif /* __cplusplus */
|
|
|
|
|
|
#endif /* __GTK_DIAL_H__ */
|
|
</verb></tscreen>
|
|
|
|
Como vamos a ir con este <em/widget/ un poco más lejos que con el
|
|
último que creamos, ahora tenemos unos cuantos campos más en la
|
|
estructura de datos, pero el resto de las cosas son muy parecidas.
|
|
|
|
Ahora, después de incluir los ficheros de cabecera, y declarar unas
|
|
cuantas constantes, tenemos algunas funciones que proporcionan
|
|
información sobre el <em/widget/ y lo inicializan:
|
|
|
|
<tscreen><verb>
|
|
#include <math.h>
|
|
#include <stdio.h>
|
|
#include <gtk/gtkmain.h>
|
|
#include <gtk/gtksignal.h>
|
|
|
|
#include "gtkdial.h"
|
|
|
|
#define SCROLL_DELAY_LENGTH 300
|
|
#define DIAL_DEFAULT_SIZE 100
|
|
|
|
/* Declaraciones de funciones */
|
|
|
|
[ omitido para salvar espacio ]
|
|
|
|
/* datos locales */
|
|
|
|
static GtkWidgetClass *parent_class = NULL;
|
|
|
|
guint
|
|
gtk_dial_get_type ()
|
|
{
|
|
static guint dial_type = 0;
|
|
|
|
if (!dial_type)
|
|
{
|
|
GtkTypeInfo dial_info =
|
|
{
|
|
"GtkDial",
|
|
sizeof (GtkDial),
|
|
sizeof (GtkDialClass),
|
|
(GtkClassInitFunc) gtk_dial_class_init,
|
|
(GtkObjectInitFunc) gtk_dial_init,
|
|
(GtkArgSetFunc) NULL,
|
|
(GtkArgGetFunc) NULL,
|
|
};
|
|
|
|
dial_type = gtk_type_unique (gtk_widget_get_type (), &dial_info);
|
|
}
|
|
|
|
return dial_type;
|
|
}
|
|
|
|
static void
|
|
gtk_dial_class_init (GtkDialClass *class)
|
|
{
|
|
GtkObjectClass *object_class;
|
|
GtkWidgetClass *widget_class;
|
|
|
|
object_class = (GtkObjectClass*) class;
|
|
widget_class = (GtkWidgetClass*) class;
|
|
|
|
parent_class = gtk_type_class (gtk_widget_get_type ());
|
|
|
|
object_class->destroy = gtk_dial_destroy;
|
|
|
|
widget_class->realize = gtk_dial_realize;
|
|
widget_class->expose_event = gtk_dial_expose;
|
|
widget_class->size_request = gtk_dial_size_request;
|
|
widget_class->size_allocate = gtk_dial_size_allocate;
|
|
widget_class->button_press_event = gtk_dial_button_press;
|
|
widget_class->button_release_event = gtk_dial_button_release;
|
|
widget_class->motion_notify_event = gtk_dial_motion_notify;
|
|
}
|
|
|
|
static void
|
|
gtk_dial_init (GtkDial *dial)
|
|
{
|
|
dial->button = 0;
|
|
dial->policy = GTK_UPDATE_CONTINUOUS;
|
|
dial->timer = 0;
|
|
dial->radius = 0;
|
|
dial->pointer_width = 0;
|
|
dial->angle = 0.0;
|
|
dial->old_value = 0.0;
|
|
dial->old_lower = 0.0;
|
|
dial->old_upper = 0.0;
|
|
dial->adjustment = NULL;
|
|
}
|
|
|
|
GtkWidget*
|
|
gtk_dial_new (GtkAdjustment *adjustment)
|
|
{
|
|
GtkDial *dial;
|
|
|
|
dial = gtk_type_new (gtk_dial_get_type ());
|
|
|
|
if (!adjustment)
|
|
adjustment = (GtkAdjustment*) gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
|
|
|
|
gtk_dial_set_adjustment (dial, adjustment);
|
|
|
|
return GTK_WIDGET (dial);
|
|
}
|
|
|
|
static void
|
|
gtk_dial_destroy (GtkObject *object)
|
|
{
|
|
GtkDial *dial;
|
|
|
|
g_return_if_fail (object != NULL);
|
|
g_return_if_fail (GTK_IS_DIAL (object));
|
|
|
|
dial = GTK_DIAL (object);
|
|
|
|
if (dial->adjustment)
|
|
gtk_object_unref (GTK_OBJECT (dial->adjustment));
|
|
|
|
if (GTK_OBJECT_CLASS (parent_class)->destroy)
|
|
(* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
|
|
}
|
|
</verb></tscreen>
|
|
|
|
Observe que ésta función <tt/init()/ hace menos cosas de las que hacía
|
|
la función <tt/init()/ que utilizamos con el <em/widget/ Tictactoe, ya
|
|
que éste no es un <em/widget/ compuesto, y la función <tt/new()/ hace
|
|
más cosas, ya que ahora admite un argumento. Observe también que
|
|
cuando almacenamos un puntero en un objeto Adjustment, incrementamos
|
|
su contador interno, (y lo decrementamos cuando ya no lo utilizamos)
|
|
por lo que GTK puede saber cuando se puede destruir sin que se
|
|
produzcan problemas.
|
|
|
|
<p>
|
|
Aquí tenemos unas cuantas funciones para manipular las opciones del
|
|
<em/widget/:
|
|
|
|
<tscreen><verb>
|
|
GtkAdjustment*
|
|
gtk_dial_get_adjustment (GtkDial *dial)
|
|
{
|
|
g_return_val_if_fail (dial != NULL, NULL);
|
|
g_return_val_if_fail (GTK_IS_DIAL (dial), NULL);
|
|
|
|
return dial->adjustment;
|
|
}
|
|
|
|
void
|
|
gtk_dial_set_update_policy (GtkDial *dial,
|
|
GtkUpdateType policy)
|
|
{
|
|
g_return_if_fail (dial != NULL);
|
|
g_return_if_fail (GTK_IS_DIAL (dial));
|
|
|
|
dial->policy = policy;
|
|
}
|
|
|
|
void
|
|
gtk_dial_set_adjustment (GtkDial *dial,
|
|
GtkAdjustment *adjustment)
|
|
{
|
|
g_return_if_fail (dial != NULL);
|
|
g_return_if_fail (GTK_IS_DIAL (dial));
|
|
|
|
if (dial->adjustment)
|
|
{
|
|
gtk_signal_disconnect_by_data (GTK_OBJECT (dial->adjustment), (gpointer) dial);
|
|
gtk_object_unref (GTK_OBJECT (dial->adjustment));
|
|
}
|
|
|
|
dial->adjustment = adjustment;
|
|
gtk_object_ref (GTK_OBJECT (dial->adjustment));
|
|
|
|
gtk_signal_connect (GTK_OBJECT (adjustment), "changed",
|
|
(GtkSignalFunc) gtk_dial_adjustment_changed,
|
|
(gpointer) dial);
|
|
gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed",
|
|
(GtkSignalFunc) gtk_dial_adjustment_value_changed,
|
|
(gpointer) dial);
|
|
|
|
dial->old_value = adjustment->value;
|
|
dial->old_lower = adjustment->lower;
|
|
dial->old_upper = adjustment->upper;
|
|
|
|
gtk_dial_update (dial);
|
|
}
|
|
</verb></tscreen>
|
|
|
|
<sect2> <tt/gtk_dial_realize()/
|
|
|
|
<p>
|
|
Ahora vienen algunas funciones nuevas. Primero, tenemos una función
|
|
que hace el trabajo de crear la ventana X. A la función se le pasará
|
|
una máscara <tt/gdk_window_new()/ que especifica que campos de la
|
|
estructura <tt/GdkWindowAttr/ tienen datos (los campos restantes
|
|
tendrán los valores por defecto). También es bueno fijarse en la forma
|
|
en que se crea la máscara de eventos. Llamamos a
|
|
<tt/gtk_widget_get_events()/ para recuperar la máscara de eventos que
|
|
el usuario ha especificado para su <em/widget/ (con
|
|
<tt/gtk_widget_set_events()/), y añadir nosotros mismos los eventos
|
|
en los que estemos interesados.
|
|
|
|
<p>
|
|
Después de crear la ventana, decidiremos su estilo y su fondo, y
|
|
pondremos un puntero al <em/widget/ en el campo de datos del usuario
|
|
de la <tt/GdkWindow/. Este último paso le permite a GTK despachar los
|
|
eventos que hayan para esta ventana hacia el <em/widget/ correcto.
|
|
|
|
<tscreen><verb>
|
|
static void
|
|
gtk_dial_realize (GtkWidget *widget)
|
|
{
|
|
GtkDial *dial;
|
|
GdkWindowAttr attributes;
|
|
gint attributes_mask;
|
|
|
|
g_return_if_fail (widget != NULL);
|
|
g_return_if_fail (GTK_IS_DIAL (widget));
|
|
|
|
GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
|
|
dial = GTK_DIAL (widget);
|
|
|
|
attributes.x = widget->allocation.x;
|
|
attributes.y = widget->allocation.y;
|
|
attributes.width = widget->allocation.width;
|
|
attributes.height = widget->allocation.height;
|
|
attributes.wclass = GDK_INPUT_OUTPUT;
|
|
attributes.window_type = GDK_WINDOW_CHILD;
|
|
attributes.event_mask = gtk_widget_get_events (widget) |
|
|
GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK |
|
|
GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK |
|
|
GDK_POINTER_MOTION_HINT_MASK;
|
|
attributes.visual = gtk_widget_get_visual (widget);
|
|
attributes.colormap = gtk_widget_get_colormap (widget);
|
|
|
|
attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
|
|
widget->window = gdk_window_new (widget->parent->window, &attributes, attributes_mask);
|
|
|
|
widget->style = gtk_style_attach (widget->style, widget->window);
|
|
|
|
gdk_window_set_user_data (widget->window, widget);
|
|
|
|
gtk_style_set_background (widget->style, widget->window, GTK_STATE_ACTIVE);
|
|
}
|
|
</verb></tscreen>
|
|
|
|
<sect2> Negociación del tamaño
|
|
|
|
<p>
|
|
Antes de que se muestre por primera vez la ventana conteniendo un
|
|
<em/widget/, y cuando quiera que la capa de la ventana cambie, GTK le
|
|
preguntara a cada <em/widget/ hijo por su tamaño deseado. Esta
|
|
petición se controla mediante la función
|
|
<tt/gtk_dial_size_request()/. Como nuestro <em/widget/ no es un
|
|
<em/widget/ contenedor, y no tiene ninguna limitación en su tamaño,
|
|
nos contentaremos con devolver un valor por defecto.
|
|
|
|
<tscreen><verb>
|
|
static void
|
|
gtk_dial_size_request (GtkWidget *widget,
|
|
GtkRequisition *requisition)
|
|
{
|
|
requisition->width = DIAL_DEFAULT_SIZE;
|
|
requisition->height = DIAL_DEFAULT_SIZE;
|
|
}
|
|
</verb></tscreen>
|
|
|
|
<p>
|
|
Después de que todos los <em/widgets/ hayan pedido su tamaño ideal,
|
|
se calculará la ventana y cada <em/widget/ hijo será informado de su
|
|
tamaño actual. Normalmente, éste será al menos tan grande como el
|
|
pedido, pero si por ejemplo, el usuario ha redimensionado la ventana,
|
|
entonces puede que el tamaño que se le de al <em/widget/ sea menor
|
|
que el que pidió. La notificación del tamaño se maneja mediante la
|
|
función <tt/gtk_dial_size_allocate()/. Fíjese que esta función calcula
|
|
los tamaños de los diferentes elementos que componen la ventana para
|
|
su uso futuro, así como todo el trabajo sucio que poner los
|
|
<em/widgets/ de la ventana X en la nueva posición y con el nuevo
|
|
tamaño.
|
|
|
|
<tscreen><verb>
|
|
static void
|
|
gtk_dial_size_allocate (GtkWidget *widget,
|
|
GtkAllocation *allocation)
|
|
{
|
|
GtkDial *dial;
|
|
|
|
g_return_if_fail (widget != NULL);
|
|
g_return_if_fail (GTK_IS_DIAL (widget));
|
|
g_return_if_fail (allocation != NULL);
|
|
|
|
widget->allocation = *allocation;
|
|
if (GTK_WIDGET_REALIZED (widget))
|
|
{
|
|
dial = GTK_DIAL (widget);
|
|
|
|
gdk_window_move_resize (widget->window,
|
|
allocation->x, allocation->y,
|
|
allocation->width, allocation->height);
|
|
|
|
dial->radius = MAX(allocation->width,allocation->height) * 0.45;
|
|
dial->pointer_width = dial->radius / 5;
|
|
}
|
|
}
|
|
</verb></tscreen>.
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect2> <tt/gtk_dial_expose()/
|
|
|
|
<p>
|
|
Como se mencionó arriba, todo el dibujado de este <em/widget/ se hace
|
|
en el manejador de los eventos <tt/expose/. No hay mucho destacable
|
|
aquí, excepto la utilización de la función <tt/gtk_draw_polygon/ para
|
|
dibujar el puntero con un degradado tridimensional de acuerdo con los
|
|
colores almacenados en el estilo del <em/widget/.
|
|
|
|
<tscreen><verb>
|
|
static gint
|
|
gtk_dial_expose (GtkWidget *widget,
|
|
GdkEventExpose *event)
|
|
{
|
|
GtkDial *dial;
|
|
GdkPoint points[3];
|
|
gdouble s,c;
|
|
gdouble theta;
|
|
gint xc, yc;
|
|
gint tick_length;
|
|
gint i;
|
|
|
|
g_return_val_if_fail (widget != NULL, FALSE);
|
|
g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
|
|
g_return_val_if_fail (event != NULL, FALSE);
|
|
|
|
if (event->count > 0)
|
|
return FALSE;
|
|
|
|
dial = GTK_DIAL (widget);
|
|
|
|
gdk_window_clear_area (widget->window,
|
|
0, 0,
|
|
widget->allocation.width,
|
|
widget->allocation.height);
|
|
|
|
xc = widget->allocation.width/2;
|
|
yc = widget->allocation.height/2;
|
|
|
|
/* Dibujar las rayitas */
|
|
|
|
for (i=0; i<25; i++)
|
|
{
|
|
theta = (i*M_PI/18. - M_PI/6.);
|
|
s = sin(theta);
|
|
c = cos(theta);
|
|
|
|
tick_length = (i%6 == 0) ? dial->pointer_width : dial->pointer_width/2;
|
|
|
|
gdk_draw_line (widget->window,
|
|
widget->style->fg_gc[widget->state],
|
|
xc + c*(dial->radius - tick_length),
|
|
yc - s*(dial->radius - tick_length),
|
|
xc + c*dial->radius,
|
|
yc - s*dial->radius);
|
|
}
|
|
|
|
/* Dibujar el puntero */
|
|
|
|
s = sin(dial->angle);
|
|
c = cos(dial->angle);
|
|
|
|
|
|
points[0].x = xc + s*dial->pointer_width/2;
|
|
points[0].y = yc + c*dial->pointer_width/2;
|
|
points[1].x = xc + c*dial->radius;
|
|
points[1].y = yc - s*dial->radius;
|
|
points[2].x = xc - s*dial->pointer_width/2;
|
|
points[2].y = yc - c*dial->pointer_width/2;
|
|
|
|
gtk_draw_polygon (widget->style,
|
|
widget->window,
|
|
GTK_STATE_NORMAL,
|
|
GTK_SHADOW_OUT,
|
|
points, 3,
|
|
TRUE);
|
|
|
|
return FALSE;
|
|
}
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect2> Manejo de eventos
|
|
|
|
<p>
|
|
El resto del código del <em/widget/ controla varios tipos de eventos,
|
|
y no es muy diferente del que podemos encontrar en muchas aplicaciones
|
|
GTK. Pueden ocurrir dos tipos de eventos - el usuario puede pulsar en
|
|
el <em/widget/ con el ratón y arrastrar para mover el puntero, o el
|
|
valor del objeto Adjustement puede cambiar debido a alguna
|
|
circunstancia externa.
|
|
|
|
<p>
|
|
Cuando el usuario pulsa en el <em/widget/, haremos una comprobación
|
|
para ver si la pulsación se hizo lo suficientemente cerca del
|
|
puntero, y si así fue, almacenamos el botón que pulsó el usuario en
|
|
en el campo <tt/button/ de la estructura del <em/widget/, y grabamos
|
|
todos los eventos del ratón con una llamada a <tt/gtk_grab_add()/. El
|
|
movimiento del ratón hará que se recalcule el valor del control
|
|
(mediante la función <tt/gtk_dial_update_mouse/). Dependiendo de la
|
|
política que sigamos, o bien se generarán instantáneamente los eventos
|
|
<tt/value_changed/ (<tt/GTK_UPDATE_CONTINUOUS/), o bien después de una
|
|
espera del temporizador establecido mediante <tt/gtk_timeout_add()/
|
|
(<tt/GTK_UPDATE_DELAYED/), o bien sólo cuando se levante el botón
|
|
(<tt/GTK_UPDATE_DISCONTINUOUS/).
|
|
|
|
<tscreen><verb>
|
|
static gint
|
|
gtk_dial_button_press (GtkWidget *widget,
|
|
GdkEventButton *event)
|
|
{
|
|
GtkDial *dial;
|
|
gint dx, dy;
|
|
double s, c;
|
|
double d_parallel;
|
|
double d_perpendicular;
|
|
|
|
g_return_val_if_fail (widget != NULL, FALSE);
|
|
g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
|
|
g_return_val_if_fail (event != NULL, FALSE);
|
|
|
|
dial = GTK_DIAL (widget);
|
|
|
|
/* Determinar si la pulsación del botón fue dentro de la región del
|
|
puntero - esto lo hacemos calculando la distancia x e y del punto
|
|
donde se pulsó el botón ratón de la línea que se ha pasado mediante el
|
|
puntero */
|
|
|
|
dx = event->x - widget->allocation.width / 2;
|
|
dy = widget->allocation.height / 2 - event->y;
|
|
|
|
s = sin(dial->angle);
|
|
c = cos(dial->angle);
|
|
|
|
d_parallel = s*dy + c*dx;
|
|
d_perpendicular = fabs(s*dx - c*dy);
|
|
|
|
if (!dial->button &&
|
|
(d_perpendicular < dial->pointer_width/2) &&
|
|
(d_parallel > - dial->pointer_width))
|
|
{
|
|
gtk_grab_add (widget);
|
|
|
|
dial->button = event->button;
|
|
|
|
gtk_dial_update_mouse (dial, event->x, event->y);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gint
|
|
gtk_dial_button_release (GtkWidget *widget,
|
|
GdkEventButton *event)
|
|
{
|
|
GtkDial *dial;
|
|
|
|
g_return_val_if_fail (widget != NULL, FALSE);
|
|
g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
|
|
g_return_val_if_fail (event != NULL, FALSE);
|
|
|
|
dial = GTK_DIAL (widget);
|
|
|
|
if (dial->button == event->button)
|
|
{
|
|
gtk_grab_remove (widget);
|
|
|
|
dial->button = 0;
|
|
|
|
if (dial->policy == GTK_UPDATE_DELAYED)
|
|
gtk_timeout_remove (dial->timer);
|
|
|
|
if ((dial->policy != GTK_UPDATE_CONTINUOUS) &&
|
|
(dial->old_value != dial->adjustment->value))
|
|
gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gint
|
|
gtk_dial_motion_notify (GtkWidget *widget,
|
|
GdkEventMotion *event)
|
|
{
|
|
GtkDial *dial;
|
|
GdkModifierType mods;
|
|
gint x, y, mask;
|
|
|
|
g_return_val_if_fail (widget != NULL, FALSE);
|
|
g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
|
|
g_return_val_if_fail (event != NULL, FALSE);
|
|
|
|
dial = GTK_DIAL (widget);
|
|
|
|
if (dial->button != 0)
|
|
{
|
|
x = event->x;
|
|
y = event->y;
|
|
|
|
if (event->is_hint || (event->window != widget->window))
|
|
gdk_window_get_pointer (widget->window, &x, &y, &mods);
|
|
|
|
switch (dial->button)
|
|
{
|
|
case 1:
|
|
mask = GDK_BUTTON1_MASK;
|
|
break;
|
|
case 2:
|
|
mask = GDK_BUTTON2_MASK;
|
|
break;
|
|
case 3:
|
|
mask = GDK_BUTTON3_MASK;
|
|
break;
|
|
default:
|
|
mask = 0;
|
|
break;
|
|
}
|
|
|
|
if (mods & mask)
|
|
gtk_dial_update_mouse (dial, x,y);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gint
|
|
gtk_dial_timer (GtkDial *dial)
|
|
{
|
|
g_return_val_if_fail (dial != NULL, FALSE);
|
|
g_return_val_if_fail (GTK_IS_DIAL (dial), FALSE);
|
|
|
|
if (dial->policy == GTK_UPDATE_DELAYED)
|
|
gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
gtk_dial_update_mouse (GtkDial *dial, gint x, gint y)
|
|
{
|
|
gint xc, yc;
|
|
gfloat old_value;
|
|
|
|
g_return_if_fail (dial != NULL);
|
|
g_return_if_fail (GTK_IS_DIAL (dial));
|
|
|
|
xc = GTK_WIDGET(dial)->allocation.width / 2;
|
|
yc = GTK_WIDGET(dial)->allocation.height / 2;
|
|
|
|
old_value = dial->adjustment->value;
|
|
dial->angle = atan2(yc-y, x-xc);
|
|
|
|
if (dial->angle < -M_PI/2.)
|
|
dial->angle += 2*M_PI;
|
|
|
|
if (dial->angle < -M_PI/6)
|
|
dial->angle = -M_PI/6;
|
|
|
|
if (dial->angle > 7.*M_PI/6.)
|
|
dial->angle = 7.*M_PI/6.;
|
|
|
|
dial->adjustment->value = dial->adjustment->lower + (7.*M_PI/6 - dial->angle) *
|
|
(dial->adjustment->upper - dial->adjustment->lower) / (4.*M_PI/3.);
|
|
|
|
if (dial->adjustment->value != old_value)
|
|
{
|
|
if (dial->policy == GTK_UPDATE_CONTINUOUS)
|
|
{
|
|
gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
|
|
}
|
|
else
|
|
{
|
|
gtk_widget_draw (GTK_WIDGET(dial), NULL);
|
|
|
|
if (dial->policy == GTK_UPDATE_DELAYED)
|
|
{
|
|
if (dial->timer)
|
|
gtk_timeout_remove (dial->timer);
|
|
|
|
dial->timer = gtk_timeout_add (SCROLL_DELAY_LENGTH,
|
|
(GtkFunction) gtk_dial_timer,
|
|
(gpointer) dial);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</verb></tscreen>
|
|
|
|
<p>
|
|
Cambios en el Adjustment por motivos externos significa que se le
|
|
comunicarán a nuestro <em/widget/ mediante las señales <tt/changed/ y
|
|
<tt/value_changed/. Los manejadores de estas funciones llaman a
|
|
<tt/gtk_dial_update()/ para comprobar los argumentos, calcular el
|
|
nuevo ángulo del puntero, y redibujar el <em/widget/ (llamando a
|
|
<tt/gtk_widget_draw()/).
|
|
|
|
<tscreen><verb>
|
|
static void
|
|
gtk_dial_update (GtkDial *dial)
|
|
{
|
|
gfloat new_value;
|
|
|
|
g_return_if_fail (dial != NULL);
|
|
g_return_if_fail (GTK_IS_DIAL (dial));
|
|
|
|
new_value = dial->adjustment->value;
|
|
|
|
if (new_value < dial->adjustment->lower)
|
|
new_value = dial->adjustment->lower;
|
|
|
|
if (new_value > dial->adjustment->upper)
|
|
new_value = dial->adjustment->upper;
|
|
|
|
if (new_value != dial->adjustment->value)
|
|
{
|
|
dial->adjustment->value = new_value;
|
|
gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
|
|
}
|
|
|
|
dial->angle = 7.*M_PI/6. - (new_value - dial->adjustment->lower) * 4.*M_PI/3. /
|
|
(dial->adjustment->upper - dial->adjustment->lower);
|
|
|
|
gtk_widget_draw (GTK_WIDGET(dial), NULL);
|
|
}
|
|
|
|
static void
|
|
gtk_dial_adjustment_changed (GtkAdjustment *adjustment,
|
|
gpointer data)
|
|
{
|
|
GtkDial *dial;
|
|
|
|
g_return_if_fail (adjustment != NULL);
|
|
g_return_if_fail (data != NULL);
|
|
|
|
dial = GTK_DIAL (data);
|
|
|
|
if ((dial->old_value != adjustment->value) ||
|
|
(dial->old_lower != adjustment->lower) ||
|
|
(dial->old_upper != adjustment->upper))
|
|
{
|
|
gtk_dial_update (dial);
|
|
|
|
dial->old_value = adjustment->value;
|
|
dial->old_lower = adjustment->lower;
|
|
dial->old_upper = adjustment->upper;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gtk_dial_adjustment_value_changed (GtkAdjustment *adjustment,
|
|
gpointer data)
|
|
{
|
|
GtkDial *dial;
|
|
|
|
g_return_if_fail (adjustment != NULL);
|
|
g_return_if_fail (data != NULL);
|
|
|
|
dial = GTK_DIAL (data);
|
|
|
|
if (dial->old_value != adjustment->value)
|
|
{
|
|
gtk_dial_update (dial);
|
|
|
|
dial->old_value = adjustment->value;
|
|
}
|
|
}
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect2> Posibles mejoras
|
|
<p>
|
|
|
|
El <em/widget/ Dial tal y como lo hemos descrito tiene unas 670
|
|
líneas de código. Aunque pueda parecer un poco exagerado, todavía
|
|
no hemos escrito demasiado código, ya que la mayoría de las líneas
|
|
son de ficheros de cabecera y de adornos. Todavía se le pueden hacer
|
|
algunas mejoras a este <em/widget/:
|
|
|
|
<itemize>
|
|
<item> Si prueba el <em/widget/, verá que el puntero cambia a
|
|
pantallazos cuando se le arrastra. Esto es debido a que todo el
|
|
<em/widget/ se borra cada vez que se mueve el puntero, antes de
|
|
redibujarse. Normalmente, la mejor forma de tratar este problema es
|
|
dibujar en un <em/pixmap/ que no represente lo que se ve directamente
|
|
en pantalla, y copiar el resultado final en la pantalla en sólo un
|
|
paso. (El <em/widget/ ProgressBar funciona de esta forma.)
|
|
|
|
<item> El usuario debería ser capaz de utilizar las flechas de arriba
|
|
y abajo para aumentar y decrementar el valor.
|
|
|
|
<item> Sería bonito si el <em/widget/ tuviese botones para
|
|
incrementar y decrementar el valor a saltos más o menos
|
|
grandes. Es posible utilizar <em/widgets/ botón, aunque también
|
|
queremos que los botones pudiesen realizar la operación de incrementar
|
|
o decrementar varias veces, mientras se mantenga el botón pulsado, tal
|
|
y como lo hacen las flechas en una barra de desplazamiento. La mayoría
|
|
del código para implementar todo esto lo podemos encontrar en el
|
|
<em/widget/ GtkRange.
|
|
|
|
<item> El <em/widget/ Dial puede utilizarse en un <em/widget/
|
|
contenedor con un simple <em/widget/ hijo colocado en la parte
|
|
inferior entre los botones antes mencionados. El usuario puede añadir
|
|
(según prefiera) una etiqueta o un <em/widget/ entry para mostrar el
|
|
valor actual del marcador.
|
|
|
|
</itemize>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1> Aprendiendo más
|
|
|
|
<p>
|
|
Sólo se han descrito una pequeña parte de los muchos detalles
|
|
involucrados en la creación de <em/widgets/, la mejor fuente de
|
|
ejemplos es el código mismo de GTK. Hágase algunas preguntas acerca
|
|
del <em/widget/ que desea crear: ¿es un <em/widget/ contenedor?
|
|
¿Debe tener su propia ventana? ¿Es una modificación de un
|
|
<em/widget/ existente? En ese momento busque un <em/widget/ similar, y
|
|
comience a hacer los cambios.
|
|
¡Buena suerte!
|
|
|
|
<!-- ***************************************************************** -->
|
|
<sect>Scribble, un sencillo programa de dibujo de ejemplo
|
|
<!-- ***************************************************************** -->
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1> Objetivos
|
|
|
|
<p>
|
|
En esta sección, vamos a crear un sencillo programa de dibujo. En el
|
|
proceso, vamos a examinar como se manejan los eventos de ratón, como
|
|
dibujar en una ventana, y como mejorar el dibujado utilizando un
|
|
<em/pixmap/ intermedio. Después de crear el programa de dibujo, lo
|
|
ampliaremos añadiendole la posibilidad de utilizar dispositivos
|
|
XInput, como tabletas digitalizadoras. GTK proporciona las rutinas que
|
|
nos darán la posibilidad de obtener información extra, como la presión
|
|
y la inclinación, de todo tipo de dispositivos de una forma sencilla.
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1> Manejo de eventos
|
|
|
|
<p>
|
|
Las señales GTK sobre las que ya hemos discutido son para las
|
|
acciones de alto nivel, como cuando se selecciona un elemento de un
|
|
menú. Sin embargo a veces es útil tratar con los acontecimientos a
|
|
bajo nivel, como cuando se mueve el ratón, o cuando se está
|
|
presionando una tecla. También hay señales GTK relacionadas con
|
|
estos <em/eventos/ de bajo nivel. Los manejadores de estas señales
|
|
tienen un parámetro extra que es un puntero a una estructura
|
|
conteniendo información sobre el evento. Por ejemplo, a los manejadores
|
|
de los eventos de movimiento se les pasa una estructura
|
|
<tt/GdkEventMotion/ que es (en parte) así:
|
|
|
|
<tscreen><verb>
|
|
struct _GdkEventMotion
|
|
{
|
|
GdkEventType type;
|
|
GdkWindow *ventana;
|
|
guint32 time;
|
|
gdouble x;
|
|
gdouble y;
|
|
...
|
|
guint state;
|
|
...
|
|
};
|
|
</verb></tscreen>
|
|
|
|
<tt/type/ adquirirá su valor adecuado dependiendo del tipo de evento,
|
|
en nuestro caso <tt/GDK_MOTION_NOTIFY/, <tt/ventana/ es la ventana en
|
|
la que ocurre el evento. <tt/x/ e <tt/y/ dan las coordenadas del
|
|
evento, y <tt/state/ especifica cual es la modificación que ha habido
|
|
cuando ocurrió el evento (esto es, especifica que teclas han cambiado
|
|
su estado y que botones del ratón se han presionado.) Es la
|
|
operación OR (O) de algunos de los siguientes valores:
|
|
|
|
<tscreen><verb>
|
|
GDK_SHIFT_MASK
|
|
GDK_LOCK_MASK
|
|
GDK_CONTROL_MASK
|
|
GDK_MOD1_MASK
|
|
GDK_MOD2_MASK
|
|
GDK_MOD3_MASK
|
|
GDK_MOD4_MASK
|
|
GDK_MOD5_MASK
|
|
GDK_BUTTON1_MASK
|
|
GDK_BUTTON2_MASK
|
|
GDK_BUTTON3_MASK
|
|
GDK_BUTTON4_MASK
|
|
GDK_BUTTON5_MASK
|
|
</verb></tscreen>
|
|
|
|
<p>
|
|
Como con las otras señales, para especificar que es lo que pasa cuando
|
|
ocurre un evento, llamaremos a <tt>gtk_signal_connect()</tt>. Pero
|
|
también necesitamos decirle a GTK sobre que eventos queremos ser
|
|
informados. Para ello, llamaremos a la función:
|
|
|
|
<tscreen><verb>
|
|
void gtk_widget_set_events (GtkWidget *widget,
|
|
gint events);
|
|
</verb></tscreen>
|
|
|
|
El segundo campo especifica los eventos en los que estamos
|
|
interesados. Es el OR (O) de las constantes que especifican los
|
|
diferentes tipos de eventos. Por las referencias futuras que podamos
|
|
hacer, presentamos aquí los tipos de eventos que hay disponibles:
|
|
|
|
<tscreen><verb>
|
|
GDK_EXPOSURE_MASK
|
|
GDK_POINTER_MOTION_MASK
|
|
GDK_POINTER_MOTION_HINT_MASK
|
|
GDK_BUTTON_MOTION_MASK
|
|
GDK_BUTTON1_MOTION_MASK
|
|
GDK_BUTTON2_MOTION_MASK
|
|
GDK_BUTTON3_MOTION_MASK
|
|
GDK_BUTTON_PRESS_MASK
|
|
GDK_BUTTON_RELEASE_MASK
|
|
GDK_KEY_PRESS_MASK
|
|
GDK_KEY_RELEASE_MASK
|
|
GDK_ENTER_NOTIFY_MASK
|
|
GDK_LEAVE_NOTIFY_MASK
|
|
GDK_FOCUS_CHANGE_MASK
|
|
GDK_STRUCTURE_MASK
|
|
GDK_PROPERTY_CHANGE_MASK
|
|
GDK_PROXIMITY_IN_MASK
|
|
GDK_PROXIMITY_OUT_MASK
|
|
</verb></tscreen>
|
|
|
|
Hay unos cuantas sutilezas que debemos respetar cuando llamamos a
|
|
<tt/gtk_widget_set_events()/. Primero, debemos llamar a esta función
|
|
antes de que se cree la ventana X para el <em/widget/ GTK. En
|
|
términos prácticos, significa que debemos llamarla inmediatamente
|
|
después de crear el <em/widget/. Segundo, el <em/widget/ debe tener
|
|
una ventana X asociado. Por motivos de eficiencia, hay muchos
|
|
<em/widgets/ que no tienen su propia ventana, sino que dibujan en la
|
|
de su padre. Estos <em/widgets/ son:
|
|
|
|
<tscreen><verb>
|
|
GtkAlignment
|
|
GtkArrow
|
|
GtkBin
|
|
GtkBox
|
|
GtkImage
|
|
GtkItem
|
|
GtkLabel
|
|
GtkPixmap
|
|
GtkScrolledWindow
|
|
GtkSeparator
|
|
GtkTable
|
|
GtkAspectFrame
|
|
GtkFrame
|
|
GtkVBox
|
|
GtkHBox
|
|
GtkVSeparator
|
|
GtkHSeparator
|
|
</verb></tscreen>
|
|
|
|
Para capturar eventos para estos <em/widgets/, necesita utilizar un
|
|
<em/widget/ EventBox. Vea la sección <ref
|
|
id="sec_The_EventBox_Widget" name="El widget EventBox"> para más
|
|
detalles.
|
|
|
|
<p>
|
|
Para nuestro programa de dibujo, queremos saber cuando se presiona el
|
|
botón del ratón y cuando se mueve, por lo que debemos especificar
|
|
los eventos <tt/GDK_POINTER_MOTION_MASK/ y
|
|
<tt/GDK_BUTTON_PRESS_MASK/. También queremos saber cuando necesitamos
|
|
redibujar nuestra ventana, por lo que especificaremos el evento
|
|
<tt/GDK_EXPOSURE_MASK/. Aunque queremos estar informados mediante un
|
|
evento <tt/Configure/ cuando cambie el tamaño de nuestra ventana, no
|
|
tenemos que especificar la correspondiente <tt/GDK_STRUCTURE_MASK/,
|
|
porque ya está activada por defecto para todas las ventanas.
|
|
|
|
<p>
|
|
Tenemos un problema con lo que acabamos de hacer, y tiene que ver con
|
|
la utilización de <tt/GDK_POINTER_MOTION_MASK/. Si especificamos este
|
|
evento, el servidor añadirá un evento de movimiento a la cola de
|
|
eventos cada vez que el usuario mueva el ratón. Imagine que nos
|
|
cuesta 0'1 segundo tratar el evento de movimiento, pero que el
|
|
servidor X añade a la cola un nuevo evento de moviento cada 0'05
|
|
segundos. Pronto nos iremos quedando retrasados con respecto al resto
|
|
de los eventos. Si el usuario dibuja durante 5 segundos, ¡nos llevará
|
|
otros 5 segundos el cazarle después de que hay levantado el botón
|
|
del ratón! Lo que queremos es sólo un evento de movimiento por cada
|
|
evento que procesemos. La manera de hacerlo es especificando
|
|
<tt/GDK_POINTER_MOTION_HINT_MASK/.
|
|
|
|
<p>
|
|
Cuando especificamos <tt/GDK_POINTER_MOTION_HINT_MASK/, el servidor
|
|
nos envia un evento de movimiento la primera ver que el puntero se
|
|
mueve depués de entrar en nuestra ventana, o después de que se
|
|
apriete o se suelte un botón (y se reciba el evento
|
|
correspondiente). Los eventos de movimiento restantes se eliminarán a
|
|
no ser que preguntemos especificamente por la posición del puntero
|
|
utilizando la función:
|
|
|
|
<tscreen><verb>
|
|
GdkWindow* gdk_window_get_pointer (GdkWindow *ventana,
|
|
gint *x,
|
|
gint *y,
|
|
GdkModifierType *mask);
|
|
</verb></tscreen>
|
|
|
|
(Hay otra función, <tt>gtk_widget_get_pointer()</tt> que tiene una
|
|
interfaz más sencillo, pero esta simplificación le resta utilidad, ya
|
|
que sólo devuelve la posición del ratón, y no si alguno de sus botones
|
|
está presionado.)
|
|
|
|
<p>
|
|
El código para establecer los eventos para nuestra ventana es el
|
|
siguiente:
|
|
|
|
<tscreen><verb>
|
|
gtk_signal_connect (GTK_OBJECT (drawing_area), "expose_event",
|
|
(GtkSignalFunc) expose_event, NULL);
|
|
gtk_signal_connect (GTK_OBJECT(drawing_area),"configure_event",
|
|
(GtkSignalFunc) configure_event, NULL);
|
|
gtk_signal_connect (GTK_OBJECT (drawing_area), "motion_notify_event",
|
|
(GtkSignalFunc) motion_notify_event, NULL);
|
|
gtk_signal_connect (GTK_OBJECT (drawing_area), "button_press_event",
|
|
(GtkSignalFunc) button_press_event, NULL);
|
|
|
|
gtk_widget_set_events (drawing_area, GDK_EXPOSURE_MASK
|
|
| GDK_LEAVE_NOTIFY_MASK
|
|
| GDK_BUTTON_PRESS_MASK
|
|
| GDK_POINTER_MOTION_MASK
|
|
| GDK_POINTER_MOTION_HINT_MASK);
|
|
</verb></tscreen>
|
|
|
|
Vamos a dejar los manejadores de los eventos <tt/expose_event/ y
|
|
<tt/configure_event/ para después. Los manejadores de
|
|
<tt/motion_notify_event/ y de <tt/button_press_event/ son bastante
|
|
simples:
|
|
|
|
<tscreen><verb>
|
|
static gint
|
|
button_press_event (GtkWidget *widget, GdkEventButton *event)
|
|
{
|
|
if (event->button == 1 && pixmap != NULL)
|
|
draw_brush (widget, event->x, event->y);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gint
|
|
motion_notify_event (GtkWidget *widget, GdkEventMotion *event)
|
|
{
|
|
int x, y;
|
|
GdkModifierType state;
|
|
|
|
if (event->is_hint)
|
|
gdk_window_get_pointer (event->window, &x, &y, &state);
|
|
else
|
|
{
|
|
x = event->x;
|
|
y = event->y;
|
|
state = event->state;
|
|
}
|
|
|
|
if (state & GDK_BUTTON1_MASK && pixmap != NULL)
|
|
draw_brush (widget, x, y);
|
|
|
|
return TRUE;
|
|
}
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1> El <em/widget/ DrawingArea, y dibujando
|
|
|
|
<p>
|
|
Vamos a pasar al proceso de dibujar en la pantalla. El <em/widget/ que
|
|
utilizaremos será el DrawingArea. Un <em/widget/ DrawingArea es
|
|
esencialmente una ventana X y nada más. Es un lienzo en blanco en
|
|
el que podemos dibujar lo que queramos. Crearemos un área de dibujo
|
|
utilizando la llamada:
|
|
|
|
<tscreen><verb>
|
|
GtkWidget* gtk_drawing_area_new (void);
|
|
</verb></tscreen>
|
|
|
|
Se puede especificar un tamaño por defecto para el <em/widget/
|
|
llamando a:
|
|
|
|
<tscreen><verb>
|
|
void gtk_drawing_area_size (GtkDrawingArea *darea,
|
|
gint width,
|
|
gint height);
|
|
</verb></tscreen>
|
|
|
|
Se puede cambiar el tamaño por defecto, como para todos los
|
|
<em/widgets/, llamando a <tt/gtk_widget_set_usize()/, y esto, además,
|
|
puede cambiarse si el usuario cambia manualmente el tamaño de la
|
|
ventana que contiene el área de dibujo.
|
|
|
|
<p>
|
|
Debemos hacer notar que cuando creamos un <em/widget/ DrawingArea,
|
|
seremos <em/completamente/ responsables de dibujar su contenido. Si
|
|
nuestra ventana se tapa y se vuelve a poner al descubierto,
|
|
obtendremos un evento de exposición y deberemos redibujar lo que se
|
|
había tapado.
|
|
|
|
<p>
|
|
Tener que recordar todo lo que se dibujó en la pantalla para que
|
|
podamos redibujarla convenientemente es, por decirlo de alguna manera
|
|
suave, una locura. Además puede quedar mal si hay que borrar partes
|
|
de la pantalla y hay que redibujarlas paso a paso. La solución a este
|
|
problema es utilizar un <em>pixmap</em> intermedio. En lugar de
|
|
dibujar directamente en la pantalla, dibujaremos en una imagen que
|
|
estará almacenada en la memoria del servidor, pero que no se mostrará,
|
|
y cuando cambie la imagen o se muestren nuevas partes de
|
|
la misma, copiaremos las porciones relevantes en la pantalla.
|
|
|
|
<p>
|
|
Para crear un <em/pixmap/ intermedio, llamaremos a la función:
|
|
|
|
<tscreen><verb>
|
|
GdkPixmap* gdk_pixmap_new (GdkWindow *ventana,
|
|
gint width,
|
|
gint height,
|
|
gint depth);
|
|
</verb></tscreen>
|
|
|
|
El parámetro <tt/widget/ especifica una ventana GDK de las que este
|
|
<em/pixmap/ tomará algunas propiedades. <tt/width/ y <tt/height/
|
|
especifican el tamaño del <em/pixmap/. <tt/depth/ especifica la
|
|
<em/profundidad del color/, que es el número de bits por pixel de la
|
|
nueva ventana. Si la profundidad que se especifica es <tt/-1/, se
|
|
utilizará la misma profundidad de color que tenga la <tt/ventana/.
|
|
|
|
<p>
|
|
Creamos nuestro <em/pixmap/ en nuestro manejador del evento
|
|
<tt/configure_event/. Este evento se genera cada vez que cambia el
|
|
tamaño de la ventana, incluyendo cuando ésta se crea.
|
|
|
|
<tscreen><verb>
|
|
/* Backing pixmap for drawing area */
|
|
static GdkPixmap *pixmap = NULL;
|
|
|
|
/* Create a new backing pixmap of the appropriate size */
|
|
static gint
|
|
configure_event (GtkWidget *widget, GdkEventConfigure *event)
|
|
{
|
|
if (pixmap)
|
|
gdk_pixmap_unref(pixmap);
|
|
|
|
pixmap = gdk_pixmap_new(widget->window,
|
|
widget->allocation.width,
|
|
widget->allocation.height,
|
|
-1);
|
|
gdk_draw_rectangle (pixmap,
|
|
widget->style->white_gc,
|
|
TRUE,
|
|
0, 0,
|
|
widget->allocation.width,
|
|
widget->allocation.height);
|
|
|
|
return TRUE;
|
|
}
|
|
</verb></tscreen>
|
|
|
|
La llamada a <tt/gdk_draw_rectangle()/ rellena todo el <em/pixmap/ de
|
|
blanco. Hablaremos más de todo esto en un momento.
|
|
|
|
<p>
|
|
Nuestro manejador del evento de exposición simplemente copia la
|
|
porción relevante del <em/pixmap/ en la pantalla (determinaremos la
|
|
zona a redibujar utilizando el campo <tt/event->area/ del evento de
|
|
exposición):
|
|
|
|
<tscreen><verb>
|
|
/* Redraw the screen from the backing pixmap */
|
|
static gint
|
|
expose_event (GtkWidget *widget, GdkEventExpose *event)
|
|
{
|
|
gdk_draw_pixmap(widget->window,
|
|
widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
|
|
pixmap,
|
|
event->area.x, event->area.y,
|
|
event->area.x, event->area.y,
|
|
event->area.width, event->area.height);
|
|
|
|
return FALSE;
|
|
}
|
|
</verb></tscreen>
|
|
|
|
Ahora ya sabemos como mantener la pantalla actualizada con el
|
|
contenido de nuestro <em/pixmap/, pero ¿cómo podemos dibujar algo
|
|
interesante en nuestro <em/pixmap/? Hay un gran número de llamadas en
|
|
la biblioteca GDK para dibujar en los <em/dibujables/. Un dibujable es
|
|
simplemente algo sobre lo que se puede dibujar. Puede ser una ventana,
|
|
un <em/pixmap/, un <em/bitmap/ (una imagen en blanco y negro), etc. Ya
|
|
hemos visto arriba dos de estas llamadas,
|
|
<tt>gdk_draw_rectangle()</tt> y <tt>gdk_draw_pixmap()</tt>. La lista
|
|
completa de funciones para dibujar es:
|
|
|
|
<tscreen><verb>
|
|
gdk_draw_line ()
|
|
gdk_draw_rectangle ()
|
|
gdk_draw_arc ()
|
|
gdk_draw_polygon ()
|
|
gdk_draw_string ()
|
|
gdk_draw_text ()
|
|
gdk_draw_pixmap ()
|
|
gdk_draw_bitmap ()
|
|
gdk_draw_image ()
|
|
gdk_draw_points ()
|
|
gdk_draw_segments ()
|
|
</verb></tscreen>
|
|
|
|
Ver la documentación de estas funciones o el fichero de cabecera
|
|
<tt><gdk/gdk.h></tt> para obtener más detalles sobre estas
|
|
funciones. Todas comparten los dos primeros argumentos. El primero es
|
|
el dibujable en el que se dibujará, y el segundo argumento es un
|
|
<em/contexto gráfico/ (GC).
|
|
|
|
<p>
|
|
Un contexto gráfico reúne la información sobre cosas como el color
|
|
de fondo y del color de lo que se dibuja, el ancho de la línea,
|
|
etc... GDK tiene un conjunto completo de funciones para crear y
|
|
modificar los contextos gráficos. Cada <em/widget/ tiene un GC
|
|
asociado. (Que puede modificarse en un fichero gtkrc, ver la sección
|
|
«Ficheros rc de GTK».) Estos, junto con otras cosas, almacenan
|
|
GC's. Algunos ejemplos de como acceder a estos GC's son:
|
|
|
|
<tscreen><verb>
|
|
widget->style->white_gc
|
|
widget->style->black_gc
|
|
widget->style->fg_gc[GTK_STATE_NORMAL]
|
|
widget->style->bg_gc[GTK_WIDGET_STATE(widget)]
|
|
</verb></tscreen>
|
|
|
|
Los campos <tt>fg_gc</tt>, <tt>bg_gc</tt>, <tt>dark_gc</tt>, y
|
|
<tt>light_gc</tt> se indexan con un parámetro del tipo
|
|
<tt/GtkStateType/ que puede tomar uno de los valores:
|
|
|
|
<tscreen><verb>
|
|
GTK_STATE_NORMAL,
|
|
GTK_STATE_ACTIVE,
|
|
GTK_STATE_PRELIGHT,
|
|
GTK_STATE_SELECTED,
|
|
GTK_STATE_INSENSITIVE
|
|
</verb></tscreen>
|
|
|
|
Por ejemplo, para el <tt/GTK_STATE_SELECTED/, el color que se utiliza
|
|
para pintar por defecto es el blanco y el color del fondo por defecto,
|
|
es el azul oscuro.
|
|
|
|
<p>
|
|
Nuestra función <tt/draw_brush()/, que es la que dibuja en la
|
|
pantalla, será la siguiente:
|
|
|
|
<tscreen><verb>
|
|
/* Draw a rectangle on the screen */
|
|
static void
|
|
draw_brush (GtkWidget *widget, gdouble x, gdouble y)
|
|
{
|
|
GdkRectangle update_rect;
|
|
|
|
update_rect.x = x - 5;
|
|
update_rect.y = y - 5;
|
|
update_rect.width = 10;
|
|
update_rect.height = 10;
|
|
gdk_draw_rectangle (pixmap,
|
|
widget->style->black_gc,
|
|
TRUE,
|
|
update_rect.x, update_rect.y,
|
|
update_rect.width, update_rect.height);
|
|
gtk_widget_draw (widget, &update_rect);
|
|
}
|
|
</verb></tscreen>
|
|
|
|
Después de que dibujemos el rectángulo representando la brocha en el
|
|
<em/pixmap/ llamaremos a la función:
|
|
|
|
<tscreen><verb>
|
|
void gtk_widget_draw (GtkWidget *widget,
|
|
GdkRectangle *area);
|
|
</verb></tscreen>
|
|
|
|
que le informa a X de que la zona dada por el parámetro <tt/area/
|
|
necesita actualizarse. X generará un evento de exposición
|
|
(combinando posiblemente distintas zonas pasadas mediante distintas
|
|
llamadas a <tt/gtk_widget_draw()/) que hará que nuestro manejador de
|
|
eventos de exposición copie las porciones relevantes en la pantalla.
|
|
|
|
<p>
|
|
Ya hemos cubierto el programa de dibujo completo, excepto unos cuantos
|
|
detalles mundanos como crear la ventana principal. El código completo
|
|
está disponible en el mismo lugar en el que consiguió este tutorial,
|
|
o en:
|
|
|
|
<htmlurl url="http://www.gtk.org/~otaylor/gtk/tutorial/"
|
|
name="http://www.gtk.org/~otaylor/gtk/tutorial/">
|
|
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1> Añadiendo la capacidad de utilizar XInput
|
|
|
|
<p>
|
|
Ahora es posible comprar dispositos de entrada bastante baratos, como
|
|
tabletas digitalizadoras, que permiten dibujar de forma artística
|
|
mucho más fácilmente de cómo lo haríamos con un ratón. La forma
|
|
más sencilla de utilizar estos dispositivos es simplemente
|
|
reemplazando a los ratones, pero así perdemos muchas de las ventajas
|
|
de este tipo de dispositivos, como por ejemplo:
|
|
|
|
<itemize>
|
|
<item> Sensibilidad a la presión
|
|
<item> Información sobre la inclinación
|
|
<item> Colocación subpixel
|
|
<item> Multiples entradas (por ejemplo, un lápiz con una punta y una
|
|
goma)
|
|
</itemize>
|
|
|
|
Para información sobre la extensión XInput, ver el <htmlurl
|
|
url="http://www.msc.cornell.edu/~otaylor/xinput/XInput-HOWTO.html"
|
|
name="XInput-HOWTO">.
|
|
|
|
<p>
|
|
Si examinamos la definición completa de, por ejemplo, la estructura
|
|
<tt/GdkEventMotion/, veremos que tiene campos para almacenar la
|
|
información de los dispositivos extendidos.
|
|
|
|
<tscreen><verb>
|
|
struct _GdkEventMotion
|
|
{
|
|
GdkEventType type;
|
|
GdkWindow *ventana;
|
|
guint32 time;
|
|
gdouble x;
|
|
gdouble y;
|
|
gdouble pressure;
|
|
gdouble xtilt;
|
|
gdouble ytilt;
|
|
guint state;
|
|
gint16 is_hint;
|
|
GdkInputSource source;
|
|
guint32 deviceid;
|
|
};
|
|
</verb></tscreen>
|
|
|
|
<tt/pressure/ da la presión como un número de coma flotante entre 0
|
|
y 1. <tt/xtilt/ e <tt/ytilt/ pueden tomar valores entre -1 y 1,
|
|
correspondiendo al grado de inclinación en cada dirección. <tt/source/
|
|
y <tt/deviceid/ especifican el dispositivo para el que ocurre el
|
|
evento de dos maneras diferentes. <tt/source/ da alguna información
|
|
simple sobre el tipo de dispositivo. Puede tomar los valores de la
|
|
enumeración siguiente:
|
|
|
|
<tscreen><verb>
|
|
GDK_SOURCE_MOUSE
|
|
GDK_SOURCE_PEN
|
|
GDK_SOURCE_ERASER
|
|
GDK_SOURCE_CURSOR
|
|
</verb></tscreen>
|
|
|
|
<tt/deviceid/ especifica un número único ID para el dispositivo. Puede
|
|
utilizarse para obtener más información sobre el dispositivo
|
|
utilizando la función <tt/gdk_input_list_devices()/ (ver abajo). El
|
|
valor especial <tt/GDK_CORE_POINTER/ se utiliza para el núcleo del
|
|
dispositivo apuntador. (Normalmente el ratón.)
|
|
|
|
<sect2> Activando la información del dispositivo extendido
|
|
|
|
<p>
|
|
Para informar a GTK de nuestro interés en la información sobre los
|
|
dispositivos extendidos, sólo tenemos que añadirle una línea a
|
|
nuestro programa:
|
|
|
|
<tscreen><verb>
|
|
gtk_widget_set_extension_events (drawing_area, GDK_EXTENSION_EVENTS_CURSOR);
|
|
</verb></tscreen>
|
|
|
|
Dando el valor <tt/GDK_EXTENSION_EVENTS_CURSOR/ decimos que estamos
|
|
interesados en los eventos de extensión, pero sólo si no tenemos que
|
|
dibujar nuestro propio cursor. Ver la sección <ref
|
|
id="sec_Further_Sophistications" name="Sofisticaciones adicionales">
|
|
más abajo para obtener más información sobre el dibujado del
|
|
cursor. También podríamos dar los valores
|
|
<tt/GDK_EXTENSION_EVENTS_ALL/ si queremos dibujar nuestro propio
|
|
cursor, o <tt/GDK_EXTENSION_EVENTS_NONE/ para volver al estado
|
|
inicial.
|
|
|
|
<p>
|
|
Todavía no hemos llegado al final de la historia. Por defecto, no hay
|
|
ningún dispositivo extra activado. Necesitamos un mecanismo que
|
|
permita a los usuarios activar y configurar sus dispositivos
|
|
extra. GTK proporciona el <em/widget/ InputDialog para automatizar el
|
|
proceso. El siguiente procedimiento utiliza el <em/widget/
|
|
InputDialog. Crea el cuadro de diálogo si no ha sido ya creado, y lo
|
|
pone en primer plano en caso contrario.
|
|
|
|
<tscreen><verb>
|
|
void
|
|
input_dialog_destroy (GtkWidget *w, gpointer data)
|
|
{
|
|
*((GtkWidget **)data) = NULL;
|
|
}
|
|
|
|
void
|
|
create_input_dialog ()
|
|
{
|
|
static GtkWidget *inputd = NULL;
|
|
|
|
if (!inputd)
|
|
{
|
|
inputd = gtk_input_dialog_new();
|
|
|
|
gtk_signal_connect (GTK_OBJECT(inputd), "destroy",
|
|
(GtkSignalFunc)input_dialog_destroy, &inputd);
|
|
gtk_signal_connect_object (GTK_OBJECT(GTK_INPUT_DIALOG(inputd)->close_button),
|
|
"clicked",
|
|
(GtkSignalFunc)gtk_widget_hide,
|
|
GTK_OBJECT(inputd));
|
|
gtk_widget_hide ( GTK_INPUT_DIALOG(inputd)->save_button);
|
|
|
|
gtk_widget_show (inputd);
|
|
}
|
|
else
|
|
{
|
|
if (!GTK_WIDGET_MAPPED(inputd))
|
|
gtk_widget_show(inputd);
|
|
else
|
|
gdk_window_raise(inputd->window);
|
|
}
|
|
}
|
|
</verb></tscreen>
|
|
|
|
(Tome nota de la manera en que hemos manejado el cuadro de
|
|
diálogo. Conectando la señal <tt/destroy/, nos aseguramos de que no
|
|
tendremos un puntero al cuadro de diálogo después de que haya sido
|
|
destruido, lo que nos podría llevar a un segfault.)
|
|
|
|
<p>
|
|
El InputDialog tiene dos botones «Cerrar» y «Guardar», que por
|
|
defecto no tienen ninguna acción asignada. En la función anterior
|
|
hemos hecho que «Cerrar» oculte el cuadro de diálogo, ocultando el
|
|
botón «Guardar», ya que no implementaremos en este programa la
|
|
acción de guardar las opciones de XInput.
|
|
|
|
<sect2> Utilizando la información de los dispositivos extras
|
|
|
|
<p>
|
|
Una vez hemos activado el dispositivo, podemos utilizar la
|
|
información que hay respecto a los dispositivos extendidos en los
|
|
campos extras de las estructuras de los eventos. De hecho, es bueno
|
|
utilizar esa información ya que esos campos tienen unos valores por
|
|
defecto razonables aún cuando no se activen los eventos extendidos.
|
|
|
|
<p>
|
|
Un cambio que tenemos que hacer es llamar a
|
|
<tt/gdk_input_window_get_pointer()/ en vez de a
|
|
<tt/gdk_window_get_pointer/. Esto es necesario porque
|
|
<tt/gdk_window_get_pointer/ no devuelve la información de los
|
|
dispositivos extra.
|
|
|
|
<tscreen><verb>
|
|
void gdk_input_window_get_pointer (GdkWindow *ventana,
|
|
guint32 deviceid,
|
|
gdouble *x,
|
|
gdouble *y,
|
|
gdouble *pressure,
|
|
gdouble *xtilt,
|
|
gdouble *ytilt,
|
|
GdkModifierType *mask);
|
|
</verb></tscreen>
|
|
|
|
Cuando llamamos a esta función, necesitamos especificar tanto el ID
|
|
del dispositivo como la ventana. Normalmente, obtendremos el ID del
|
|
dispositivo del campo <tt/deviceid/ de una estructura de evento. De
|
|
nuevo, esta función devolverá valores razonables cuando no estén
|
|
activados los eventos extendidos. (En ese caso, <tt/event->deviceid/
|
|
tendrá el valor <tt/GDK_CORE_POINTER/).
|
|
|
|
Por tanto la estructura básica de nuestros manejadores de los
|
|
eventos de movimiento y de pulsación del botón del ratón no
|
|
cambiarán mucho - sólo tenemos que añadir código para manejar la
|
|
información extra.
|
|
|
|
<tscreen><verb>
|
|
static gint
|
|
button_press_event (GtkWidget *widget, GdkEventButton *event)
|
|
{
|
|
print_button_press (event->deviceid);
|
|
|
|
if (event->button == 1 && pixmap != NULL)
|
|
draw_brush (widget, event->source, event->x, event->y, event->pressure);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gint
|
|
motion_notify_event (GtkWidget *widget, GdkEventMotion *event)
|
|
{
|
|
gdouble x, y;
|
|
gdouble pressure;
|
|
GdkModifierType state;
|
|
|
|
if (event->is_hint)
|
|
gdk_input_window_get_pointer (event->window, event->deviceid,
|
|
&x, &y, &pressure, NULL, NULL, &state);
|
|
else
|
|
{
|
|
x = event->x;
|
|
y = event->y;
|
|
pressure = event->pressure;
|
|
state = event->state;
|
|
}
|
|
|
|
if (state & GDK_BUTTON1_MASK && pixmap != NULL)
|
|
draw_brush (widget, event->source, x, y, pressure);
|
|
|
|
return TRUE;
|
|
}
|
|
</verb></tscreen>
|
|
|
|
También tenemos que hacer algo con la nueva información. Nuestra
|
|
nueva función <tt/draw_brush()/ dibuja con un color diferente
|
|
dependiendo de <tt/event->source/ y cambia el tamaño de la brocha
|
|
dependiendo de la presión.
|
|
|
|
<tscreen><verb>
|
|
/* Draw a rectangle on the screen, size depending on pressure,
|
|
and color on the type of device */
|
|
static void
|
|
draw_brush (GtkWidget *widget, GdkInputSource source,
|
|
gdouble x, gdouble y, gdouble pressure)
|
|
{
|
|
GdkGC *gc;
|
|
GdkRectangle update_rect;
|
|
|
|
switch (source)
|
|
{
|
|
case GDK_SOURCE_MOUSE:
|
|
gc = widget->style->dark_gc[GTK_WIDGET_STATE (widget)];
|
|
break;
|
|
case GDK_SOURCE_PEN:
|
|
gc = widget->style->black_gc;
|
|
break;
|
|
case GDK_SOURCE_ERASER:
|
|
gc = widget->style->white_gc;
|
|
break;
|
|
default:
|
|
gc = widget->style->light_gc[GTK_WIDGET_STATE (widget)];
|
|
}
|
|
|
|
update_rect.x = x - 10 * pressure;
|
|
update_rect.y = y - 10 * pressure;
|
|
update_rect.width = 20 * pressure;
|
|
update_rect.height = 20 * pressure;
|
|
gdk_draw_rectangle (pixmap, gc, TRUE,
|
|
update_rect.x, update_rect.y,
|
|
update_rect.width, update_rect.height);
|
|
gtk_widget_draw (widget, &update_rect);
|
|
}
|
|
</verb></tscreen>
|
|
|
|
<sect2> Obteniendo más información de un dispositivo
|
|
|
|
<p>
|
|
Como ejemplo de como podemos obtener más información de un
|
|
dispositivo, nuestro programa imprimirá el nombre del dispositivo que
|
|
genera cada pulsación de botón. Para encontrar el nombre de un
|
|
dispositivo, llamaremos a la función:
|
|
|
|
<tscreen><verb>
|
|
GList *gdk_input_list_devices (void);
|
|
</verb></tscreen>
|
|
|
|
que devuelve una GList (una lista enlazada de la biblioteca glib)
|
|
de estructuras <tt/GdkDeviceInfo/. La estructura <tt/GdkDeviceInfo/ se
|
|
define como:
|
|
|
|
<tscreen><verb>
|
|
struct _GdkDeviceInfo
|
|
{
|
|
guint32 deviceid;
|
|
gchar *name;
|
|
GdkInputSource source;
|
|
GdkInputMode mode;
|
|
gint has_cursor;
|
|
gint num_axes;
|
|
GdkAxisUse *axes;
|
|
gint num_keys;
|
|
GdkDeviceKey *keys;
|
|
};
|
|
</verb></tscreen>
|
|
|
|
Muchos de estos campos son información de configuración que puede
|
|
ignorar, a menos que quiera permitir la opción de grabar la
|
|
configuración de XInput. El campo que nos interesa ahora es <tt/name/
|
|
que es simplemente el nombre que X le asigna al dispositivo. El otro
|
|
campo que no tiene información sobre la configuración es
|
|
<tt/has_cursor/. Si <tt/has_cursor/ es falso, tendremos que dibujar
|
|
nuestro propio cursor. Pero como hemos especificado
|
|
<tt/GDK_EXTENSION_EVENTS_CURSOR/, no tendremos que preocuparnos por
|
|
esto.
|
|
|
|
<p>
|
|
Nuestra función <tt/print_button_press()/ simplemente recorre la
|
|
lista devuelta hasta que encuentra una coincidencia, y entonces
|
|
imprime el nombre del dispositivo.
|
|
|
|
<tscreen><verb>
|
|
static void
|
|
print_button_press (guint32 deviceid)
|
|
{
|
|
GList *tmp_list;
|
|
|
|
/* gdk_input_list_devices returns an internal list, so we shouldn't
|
|
free it afterwards */
|
|
tmp_list = gdk_input_list_devices();
|
|
|
|
while (tmp_list)
|
|
{
|
|
GdkDeviceInfo *info = (GdkDeviceInfo *)tmp_list->data;
|
|
|
|
if (info->deviceid == deviceid)
|
|
{
|
|
printf("Button press on device '%s'\n", info->name);
|
|
return;
|
|
}
|
|
|
|
tmp_list = tmp_list->next;
|
|
}
|
|
}
|
|
</verb></tscreen>
|
|
|
|
Con esto hemos completado los cambios para `XInputizar' nuestro
|
|
programa. Como ocurría con la primera versión, el código completo se
|
|
encuentra disponible en el mismo sitio donde obtuvo este tutorial, o
|
|
desde:
|
|
|
|
<htmlurl url="http://www.gtk.org/~otaylor/gtk/tutorial/"
|
|
name="http://www.gtk.org/~otaylor/gtk/tutorial/">
|
|
|
|
|
|
<sect2> Sofisticaciones adicionales <label id="sec_Further_Sophistications">
|
|
|
|
<p>
|
|
Aunque ahora nuestro programa admite XInput bastante bien, todavía
|
|
falla en algunas características que deberían estar disponibles en una
|
|
aplicación bien hecha. Primero, el usuario no debería tener que
|
|
configurar su dispositivo cada vez que ejecute el programa, por lo que
|
|
debería estar disponible la opción de guardar la configuración del
|
|
dispositivo. Esto se hace recorriendo el resultado de
|
|
<tt/gdk_input_list_devices()/ y escribiendo la configuración en un
|
|
fichero.
|
|
|
|
<p>
|
|
Para cargar la configuración del dispositivo cuando se vuelva a
|
|
ejecutar el programa, puede utilizar las funciones que proporciona GDK
|
|
para cambiar la configuración de los dispositivos:
|
|
|
|
<tscreen><verb>
|
|
gdk_input_set_extension_events()
|
|
gdk_input_set_source()
|
|
gdk_input_set_mode()
|
|
gdk_input_set_axes()
|
|
gdk_input_set_key()
|
|
</verb></tscreen>
|
|
|
|
(La lista devuelta por <tt/gdk_input_list_devices()/ no debería
|
|
modificarse directamente.) Podemos encontrar un ejemplo de como debe
|
|
utilizarse en el programa de dibujo <tt/gsumi/. (Disponible en
|
|
<htmlurl url="http://www.msc.cornell.edu/~otaylor/gsumi/"
|
|
name="http://www.msc.cornell.edu/~otaylor/gsumi/">) Estaría bien
|
|
tener un procedimiento estándar para poder hacer todo esto en
|
|
cualquier aplicaciones. Probablemente se llegue a esto en una capa
|
|
superior a GTK, quizás en la biblioteca GNOME.
|
|
|
|
<p>
|
|
El programa tiene otra carencia importante que ya hemos mencionado más
|
|
arriba, y es la falta del cursor. Ninguna plataforma distinta de
|
|
XFree86 permite utilizar simultaneamente un dispositivo como puntero
|
|
núcleo y como dispositivo directamente utilizable por una
|
|
aplicación. Ver el <url
|
|
url="http://www.msc.cornell.edu/~otaylor/xinput/XInput-HOWTO.html"
|
|
name="XInput-HOWTO"> para más información sobre esto. Con esto
|
|
queremos decir que si quiere tener la máxima audiencia necesita
|
|
dibujar su propio cursor.
|
|
|
|
<p>
|
|
Una aplicación que dibuja su propio cursor necesita hacer dos cosas:
|
|
determinar si el dispositivo actual necesita que se dibuje un cursor o
|
|
no, y determinar si el dispositivo está «próximo». (Si el
|
|
dispositivo es una tableta digitalizadora, queda muy bonito que el
|
|
cursor desaparezca cuando el lápiz se separa de la tableta. Cuando el
|
|
lápiz está tocando la tableta, se dice que el dispositivo está
|
|
«próximo»). Lo primero se hace buscando la lista de dispositivos,
|
|
tal y como hicimos para encontrar el nombre del dispositivo. Lo
|
|
segundo se consigue seleccionando los eventos
|
|
<em/proximity_out/. Podemos encontrar un ejemplo de como dibujar
|
|
nuestro propio cursor en el programa `testinput' que viene con la
|
|
distribución de GTK.
|
|
|
|
<!-- ***************************************************************** -->
|
|
<sect>Trucos para escribir aplicaciones GTK
|
|
<!-- ***************************************************************** -->
|
|
|
|
<p>
|
|
Esta sección es sólo un compendio de sabiduria, de guías generales
|
|
de estilo y de consejos para crear buenas aplicaciones GTK. Y es
|
|
totalmente inútil por ahora ya que esta frase es sólo un tópico :)
|
|
|
|
¡Utilice GNU autoconf y automake! Son sus amigos :) Pretendo poner
|
|
aquí una rápida introducción a ambos.
|
|
|
|
<!-- ***************************************************************** -->
|
|
<sect>Contribuyendo <label id="sec_Contributing">
|
|
<!-- ***************************************************************** -->
|
|
|
|
<p>
|
|
Este documento, como muchos otros grandes paquetes de programas que
|
|
hay por ahí, fue creado de forma libre por voluntarios. Si comprende
|
|
algo de GTK que todavía no se ha documentado, por favor piense en
|
|
contribuir a este documento.
|
|
|
|
<p>
|
|
Si decide contribuir, por favor mande un correo-e con su texto a Tony
|
|
Gale, <tt><htmlurl url="mailto:gale@gtk.org"
|
|
name="gale@gtk.org"></tt>. Recuerde que todas las partes que componen
|
|
este documento son libre, y cualquier añadido que haga debe ser
|
|
libre. Esto es, la gente debe de poder utilizar cualquier trozo de sus
|
|
ejemplos en sus programas, podrán distribuir copias de su documento
|
|
como deseen, etc...
|
|
<p>
|
|
Gracias.
|
|
|
|
<!-- ***************************************************************** -->
|
|
<sect>Créditos
|
|
<!-- ***************************************************************** -->
|
|
<p>
|
|
Quiero agradecer a las siguientes personas por sus contribuciones a
|
|
este texto.
|
|
|
|
<itemize>
|
|
<item>Bawer Dagdeviren, <tt><htmlurl url="mailto:chamele0n@geocities.com"
|
|
name="chamele0n@geocities.com"></tt> por el tutorial sobre los menús.
|
|
|
|
<item>Raph Levien, <tt><htmlurl url="mailto:raph@acm.org"
|
|
name="raph@acm.org"></tt> por el «hola mundo» a la GTK, el
|
|
empaquetado de <em/widgets/, y su sabiduría general. Ha donado
|
|
generosamente un hogar para este tutorial.
|
|
|
|
<item>Peter Mattis, <tt><htmlurl url="mailto:petm@xcf.berkeley.edu"
|
|
name="petm@xcf.berkeley.edu"></tt> por el más simple de los programas
|
|
GTK... y por la posibilidad de hacerlo :)
|
|
|
|
<item>Werner Koch <tt><htmlurl url="mailto:werner.koch@guug.de"
|
|
name="werner.koch@guug.de"></tt> por convertir el texto original a
|
|
SGML, y por la jerarquia de clases de <em/widgets/.
|
|
|
|
<item>Mark Crichton <tt><htmlurl url="mailto:crichton@expert.cc.purdue.edu"
|
|
name="crichton@expert.cc.purdue.edu"></tt> por el código del menú
|
|
factory, y el tutorial sobre el empaquetamiento de las tablas.
|
|
|
|
<item>Owen Taylor <tt><htmlurl url="mailto:owt1@cornell.edu"
|
|
name="owt1@cornell.edu"></tt> por la sección sobre el <em/widget/
|
|
EventBox (y el parche para el distro). También es el responsable
|
|
del código de las selecciones y el tutorial, así como de la
|
|
sección de escribiendo su propio <em/widget/ GTK, y la aplicación de
|
|
ejemplo. ¡Muchas gracias por toda tu ayuda, Owen!
|
|
|
|
<item>Mark VanderBoom <tt><htmlurl url="mailto:mvboom42@calvin.edu"
|
|
name="mvboom42@calvin.edu"></tt> por su fantástico trabajo sobre los
|
|
<em/widgets/ Notebook, Progress Bar, Dialog, y selección de ficheros.
|
|
¡Muchas gracias Mark!
|
|
Has sido de una gran ayuda.
|
|
|
|
<item>Tim Janik <tt><htmlurl url="mailto:timj@gtk.org"
|
|
name="timj@gtk.org"></tt> por su gran trabajo en el <em/widget/ List.
|
|
Gracias Tim :)
|
|
|
|
<item>Rajat Datta <tt><htmlurl url="mailto:rajat@ix.netcom.com"
|
|
name="rajat@ix.netcom.com"</tt> por el excelente trabajo con el
|
|
tutorial Pixmap.
|
|
|
|
<item>Michael K. Johnson <tt><htmlurl url="mailto:johnsonm@redhat.com"
|
|
name="johnsonm@redhat.com"></tt> por la información y el código de
|
|
los menús ("popup").
|
|
|
|
<item>David Huggins-Daines <tt><htmlurl url="mailto:bn711@freenet.carleton.ca"
|
|
name="bn711@freenet.carleton.ca"></tt> por las secciones sobre los
|
|
<em/widgets/ Range y Tree.
|
|
|
|
<item>Stefan Mars <tt><htmlurl url="mailto:mars@lysator.liu.se"
|
|
name="mars@lysator.liu.se"></tt> por la sección GtkCList
|
|
</itemize>
|
|
<p>
|
|
Y a todos los que han comentado y ayudado a refinar este documento.
|
|
<p>
|
|
Gracias.
|
|
|
|
<!-- ***************************************************************** -->
|
|
<sect> Copyright del Tutorial y notas sobre los permisos
|
|
<!-- ***************************************************************** -->
|
|
|
|
<p>
|
|
Esta traducción está bajo la misma licencia bajo la que está
|
|
el documento original. A continuación se presenta la traducción
|
|
de la licencia y la licencia en versión original. En caso de haber
|
|
alguna discrepancia entre la traducción y la licencia original, se
|
|
aplicará esta última.
|
|
|
|
El Tutorial GTK tiene Copyright (C) 1997 Ian Main.
|
|
|
|
Copyright (C) 1998 Tony Gale.
|
|
<p>
|
|
Se da permiso para hacer y distribuir copias idénticas de este manual
|
|
siempre que se incluya el copyright en todas las copias.
|
|
<p>
|
|
Se da permiso para copiar y distribuir versiones modificadas de este
|
|
documento bajo las mismas condiciones que para las copias idénticas,
|
|
siempre que el copyright se incluya exactamente tal y como se
|
|
encuentra en el original, y que el trabajo completo derivado de este
|
|
documento se distribuya bajo los términos de un permiso idéntico a
|
|
éste.
|
|
<P>
|
|
Se da permiso para copiar y distribuir traducciones de este documento
|
|
en otro lenguaje, bajo las condiciones arriba mencionadas para las
|
|
versiones modificadas.
|
|
<P>
|
|
Si se propone incluir este documento en un trabajo que vaya a ser
|
|
impreso, por favor contacte con el encargado del mantenimiento, y
|
|
haremos un esfuerzo para asegurarnos de que dispone de la información
|
|
lo más actualizada posible.
|
|
<P>
|
|
No hay ninguna garantia de que este documento se mantenga activo lo
|
|
suficiente como para conseguir cumplir con su propósito. Se
|
|
proporciona como un documento libre. Como tal, los autores y
|
|
encargados del mantenimiento de la información que se da en el
|
|
documento no pueden dar ninguna garantia de que la misma esté al día.
|
|
<P>
|
|
-----------------------------
|
|
<p>
|
|
The GTK Tutorial is Copyright (C) 1997 Ian Main.
|
|
|
|
Copyright (C) 1998 Tony Gale.
|
|
<p>
|
|
Permission is granted to make and distribute verbatim copies of this
|
|
manual provided the copyright notice and this permission notice are
|
|
preserved on all copies.
|
|
<P>Permission is granted to copy and distribute modified versions of
|
|
this document under the conditions for verbatim copying, provided that
|
|
this copyright notice is included exactly as in the original,
|
|
and that the entire resulting derived work is distributed under
|
|
the terms of a permission notice identical to this one.
|
|
<P>Permission is granted to copy and distribute translations of this
|
|
document into another language, under the above conditions for modified
|
|
versions.
|
|
<P>If you are intending to incorporate this document into a published
|
|
work, please contact the maintainer, and we will make an effort
|
|
to ensure that you have the most up to date information available.
|
|
<P>There is no guarantee that this document lives up to its intended
|
|
purpose. This is simply provided as a free resource. As such,
|
|
the authors and maintainers of the information provided within can
|
|
not make any guarantee that the information is even accurate.
|
|
|
|
<sect1>Acerca de la traducción
|
|
|
|
<p>
|
|
Esta traduccion tiene copyright (C) 1999 de Joaquín Cuenca Abela
|
|
<tt><htmlurl url="mailto:e98cuenc@criens.u-psud.fr"
|
|
name="<e98cuenc@criens.u-psud.fr>"></tt>
|
|
y de Eduardo Anglada Varela
|
|
<tt><htmlurl url="mailto:eduardo.anglada@adi.uam.es"
|
|
name="<eduardo.anglada@adi.uam.es>"></tt>.
|
|
Si tiene cualquier
|
|
duda, sugerencia o corrección no dude en consultarnos.
|
|
|
|
Gracias a Manuel de Vega Barreiro <tt><htmlurl
|
|
url="mailto:barreiro@arrakis.es"
|
|
name="<barreiro@arrakis.es>"></tt> por haber hospedado las
|
|
versiones beta y la versión actual de este tutorial en su página
|
|
web Linux Landia <tt><url
|
|
url="http://www.croftj.net/~barreiro/spain/gnome/"
|
|
name="www.croftj.net/~barreiro/spain/gnome/"></tt>.
|
|
|
|
</sect1>
|
|
|
|
<!-- ***************************************************************** -->
|
|
<appendix>
|
|
<!-- ***************************************************************** -->
|
|
|
|
<!-- ***************************************************************** -->
|
|
<sect> Señales GTK <label id="sec_GTK_Signals">
|
|
<!-- ***************************************************************** -->
|
|
<p>
|
|
GTK+, al ser un conjunto de <em/widgets/ orientado al objeto, tiene
|
|
una jerarquía de herencias. Este mecanismo de herencia se aplica a las
|
|
señales. Por eso, debe utilizar el árbol de jerarquías de los
|
|
<em/widgets/ cuando utilice las señales que aparecen en esta sección.
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>GtkObject
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<p>
|
|
<tscreen><verb>
|
|
void GtkObject::destroy (GtkObject *,
|
|
gpointer);
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>GtkWidget
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<p>
|
|
<tscreen><verb>
|
|
|
|
void GtkWidget::show (GtkWidget *,
|
|
gpointer);
|
|
void GtkWidget::hide (GtkWidget *,
|
|
gpointer);
|
|
void GtkWidget::map (GtkWidget *,
|
|
gpointer);
|
|
void GtkWidget::unmap (GtkWidget *,
|
|
gpointer);
|
|
void GtkWidget::realize (GtkWidget *,
|
|
gpointer);
|
|
void GtkWidget::unrealize (GtkWidget *,
|
|
gpointer);
|
|
void GtkWidget::draw (GtkWidget *,
|
|
ggpointer,
|
|
gpointer);
|
|
void GtkWidget::draw-focus (GtkWidget *,
|
|
gpointer);
|
|
void GtkWidget::draw-default (GtkWidget *,
|
|
gpointer);
|
|
void GtkWidget::size-request (GtkWidget *,
|
|
ggpointer,
|
|
gpointer);
|
|
void GtkWidget::size-allocate (GtkWidget *,
|
|
ggpointer,
|
|
gpointer);
|
|
void GtkWidget::state-changed (GtkWidget *,
|
|
GtkStateType,
|
|
gpointer);
|
|
void GtkWidget::parent-set (GtkWidget *,
|
|
GtkObject *,
|
|
gpointer);
|
|
void GtkWidget::style-set (GtkWidget *,
|
|
GtkStyle *,
|
|
gpointer);
|
|
void GtkWidget::add-accelerator (GtkWidget *,
|
|
gguint,
|
|
GtkAccelGroup *,
|
|
gguint,
|
|
GdkModifierType,
|
|
GtkAccelFlags,
|
|
gpointer);
|
|
void GtkWidget::remove-accelerator (GtkWidget *,
|
|
GtkAccelGroup *,
|
|
gguint,
|
|
GdkModifierType,
|
|
gpointer);
|
|
gboolean GtkWidget::event (GtkWidget *,
|
|
GdkEvent *,
|
|
gpointer);
|
|
gboolean GtkWidget::button-press-event (GtkWidget *,
|
|
GdkEvent *,
|
|
gpointer);
|
|
gboolean GtkWidget::button-release-event (GtkWidget *,
|
|
GdkEvent *,
|
|
gpointer);
|
|
gboolean GtkWidget::motion-notify-event (GtkWidget *,
|
|
GdkEvent *,
|
|
gpointer);
|
|
gboolean GtkWidget::delete-event (GtkWidget *,
|
|
GdkEvent *,
|
|
gpointer);
|
|
gboolean GtkWidget::destroy-event (GtkWidget *,
|
|
GdkEvent *,
|
|
gpointer);
|
|
gboolean GtkWidget::expose-event (GtkWidget *,
|
|
GdkEvent *,
|
|
gpointer);
|
|
gboolean GtkWidget::key-press-event (GtkWidget *,
|
|
GdkEvent *,
|
|
gpointer);
|
|
gboolean GtkWidget::key-release-event (GtkWidget *,
|
|
GdkEvent *,
|
|
gpointer);
|
|
gboolean GtkWidget::enter-notify-event (GtkWidget *,
|
|
GdkEvent *,
|
|
gpointer);
|
|
gboolean GtkWidget::leave-notify-event (GtkWidget *,
|
|
GdkEvent *,
|
|
gpointer);
|
|
gboolean GtkWidget::configure-event (GtkWidget *,
|
|
GdkEvent *,
|
|
gpointer);
|
|
gboolean GtkWidget::focus-in-event (GtkWidget *,
|
|
GdkEvent *,
|
|
gpointer);
|
|
gboolean GtkWidget::focus-out-event (GtkWidget *,
|
|
GdkEvent *,
|
|
gpointer);
|
|
gboolean GtkWidget::map-event (GtkWidget *,
|
|
GdkEvent *,
|
|
gpointer);
|
|
gboolean GtkWidget::unmap-event (GtkWidget *,
|
|
GdkEvent *,
|
|
gpointer);
|
|
gboolean GtkWidget::property-notify-event (GtkWidget *,
|
|
GdkEvent *,
|
|
gpointer);
|
|
gboolean GtkWidget::selection-clear-event (GtkWidget *,
|
|
GdkEvent *,
|
|
gpointer);
|
|
gboolean GtkWidget::selection-request-event (GtkWidget *,
|
|
GdkEvent *,
|
|
gpointer);
|
|
gboolean GtkWidget::selection-notify-event (GtkWidget *,
|
|
GdkEvent *,
|
|
gpointer);
|
|
void GtkWidget::selection-get (GtkWidget *,
|
|
GtkSelectionData *,
|
|
gguint,
|
|
gpointer);
|
|
void GtkWidget::selection-received (GtkWidget *,
|
|
GtkSelectionData *,
|
|
gguint,
|
|
gpointer);
|
|
gboolean GtkWidget::proximity-in-event (GtkWidget *,
|
|
GdkEvent *,
|
|
gpointer);
|
|
gboolean GtkWidget::proximity-out-event (GtkWidget *,
|
|
GdkEvent *,
|
|
gpointer);
|
|
void GtkWidget::drag-begin (GtkWidget *,
|
|
GdkDragContext *,
|
|
gpointer);
|
|
void GtkWidget::drag-end (GtkWidget *,
|
|
GdkDragContext *,
|
|
gpointer);
|
|
void GtkWidget::drag-data-delete (GtkWidget *,
|
|
GdkDragContext *,
|
|
gpointer);
|
|
void GtkWidget::drag-leave (GtkWidget *,
|
|
GdkDragContext *,
|
|
gguint,
|
|
gpointer);
|
|
gboolean GtkWidget::drag-motion (GtkWidget *,
|
|
GdkDragContext *,
|
|
ggint,
|
|
ggint,
|
|
gguint,
|
|
gpointer);
|
|
gboolean GtkWidget::drag-drop (GtkWidget *,
|
|
GdkDragContext *,
|
|
ggint,
|
|
ggint,
|
|
gguint,
|
|
gpointer);
|
|
void GtkWidget::drag-data-get (GtkWidget *,
|
|
GdkDragContext *,
|
|
GtkSelectionData *,
|
|
gguint,
|
|
gguint,
|
|
gpointer);
|
|
void GtkWidget::drag-data-received (GtkWidget *,
|
|
GdkDragContext *,
|
|
ggint,
|
|
ggint,
|
|
GtkSelectionData *,
|
|
gguint,
|
|
gguint,
|
|
gpointer);
|
|
gboolean GtkWidget::client-event (GtkWidget *,
|
|
GdkEvent *,
|
|
gpointer);
|
|
gboolean GtkWidget::no-expose-event (GtkWidget *,
|
|
GdkEvent *,
|
|
gpointer);
|
|
gboolean GtkWidget::visibility-notify-event (GtkWidget *,
|
|
GdkEvent *,
|
|
gpointer);
|
|
void GtkWidget::debug-msg (GtkWidget *,
|
|
GtkString *,
|
|
gpointer);
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>GtkData
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<p>
|
|
<tscreen><verb>
|
|
void GtkData::disconnect (GtkData *,
|
|
gpointer);
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>GtkContainer
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<p>
|
|
<tscreen><verb>
|
|
void GtkContainer::add (GtkContainer *,
|
|
GtkWidget *,
|
|
gpointer);
|
|
void GtkContainer::remove (GtkContainer *,
|
|
GtkWidget *,
|
|
gpointer);
|
|
void GtkContainer::check-resize (GtkContainer *,
|
|
gpointer);
|
|
GtkDirectionType GtkContainer::focus (GtkContainer *,
|
|
GtkDirectionType,
|
|
gpointer);
|
|
void GtkContainer::set-focus-child (GtkContainer *,
|
|
GtkWidget *,
|
|
gpointer);
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>GtkCalendar
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<p>
|
|
<tscreen><verb>
|
|
void GtkCalendar::month-changed (GtkCalendar *,
|
|
gpointer);
|
|
void GtkCalendar::day-selected (GtkCalendar *,
|
|
gpointer);
|
|
void GtkCalendar::day-selected-double-click (GtkCalendar *,
|
|
gpointer);
|
|
void GtkCalendar::prev-month (GtkCalendar *,
|
|
gpointer);
|
|
void GtkCalendar::next-month (GtkCalendar *,
|
|
gpointer);
|
|
void GtkCalendar::prev-year (GtkCalendar *,
|
|
gpointer);
|
|
void GtkCalendar::next-year (GtkCalendar *,
|
|
gpointer);
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>GtkEditable
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<p>
|
|
<tscreen><verb>
|
|
void GtkEditable::changed (GtkEditable *,
|
|
gpointer);
|
|
void GtkEditable::insert-text (GtkEditable *,
|
|
GtkString *,
|
|
ggint,
|
|
ggpointer,
|
|
gpointer);
|
|
void GtkEditable::delete-text (GtkEditable *,
|
|
ggint,
|
|
ggint,
|
|
gpointer);
|
|
void GtkEditable::activate (GtkEditable *,
|
|
gpointer);
|
|
void GtkEditable::set-editable (GtkEditable *,
|
|
gboolean,
|
|
gpointer);
|
|
void GtkEditable::move-cursor (GtkEditable *,
|
|
ggint,
|
|
ggint,
|
|
gpointer);
|
|
void GtkEditable::move-word (GtkEditable *,
|
|
ggint,
|
|
gpointer);
|
|
void GtkEditable::move-page (GtkEditable *,
|
|
ggint,
|
|
ggint,
|
|
gpointer);
|
|
void GtkEditable::move-to-row (GtkEditable *,
|
|
ggint,
|
|
gpointer);
|
|
void GtkEditable::move-to-column (GtkEditable *,
|
|
ggint,
|
|
gpointer);
|
|
void GtkEditable::kill-char (GtkEditable *,
|
|
ggint,
|
|
gpointer);
|
|
void GtkEditable::kill-word (GtkEditable *,
|
|
ggint,
|
|
gpointer);
|
|
void GtkEditable::kill-line (GtkEditable *,
|
|
ggint,
|
|
gpointer);
|
|
void GtkEditable::cut-clipboard (GtkEditable *,
|
|
gpointer);
|
|
void GtkEditable::copy-clipboard (GtkEditable *,
|
|
gpointer);
|
|
void GtkEditable::paste-clipboard (GtkEditable *,
|
|
gpointer);
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>GtkTipsQuery
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<p>
|
|
<tscreen><verb>
|
|
void GtkTipsQuery::start-query (GtkTipsQuery *,
|
|
gpointer);
|
|
void GtkTipsQuery::stop-query (GtkTipsQuery *,
|
|
gpointer);
|
|
void GtkTipsQuery::widget-entered (GtkTipsQuery *,
|
|
GtkWidget *,
|
|
GtkString *,
|
|
GtkString *,
|
|
gpointer);
|
|
gboolean GtkTipsQuery::widget-selected (GtkTipsQuery *,
|
|
GtkWidget *,
|
|
GtkString *,
|
|
GtkString *,
|
|
GdkEvent *,
|
|
gpointer);
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>GtkCList
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<p>
|
|
<tscreen><verb>
|
|
void GtkCList::select-row (GtkCList *,
|
|
ggint,
|
|
ggint,
|
|
GdkEvent *,
|
|
gpointer);
|
|
void GtkCList::unselect-row (GtkCList *,
|
|
ggint,
|
|
ggint,
|
|
GdkEvent *,
|
|
gpointer);
|
|
void GtkCList::row-move (GtkCList *,
|
|
ggint,
|
|
ggint,
|
|
gpointer);
|
|
void GtkCList::click-column (GtkCList *,
|
|
ggint,
|
|
gpointer);
|
|
void GtkCList::resize-column (GtkCList *,
|
|
ggint,
|
|
ggint,
|
|
gpointer);
|
|
void GtkCList::toggle-focus-row (GtkCList *,
|
|
gpointer);
|
|
void GtkCList::select-all (GtkCList *,
|
|
gpointer);
|
|
void GtkCList::unselect-all (GtkCList *,
|
|
gpointer);
|
|
void GtkCList::undo-selection (GtkCList *,
|
|
gpointer);
|
|
void GtkCList::start-selection (GtkCList *,
|
|
gpointer);
|
|
void GtkCList::end-selection (GtkCList *,
|
|
gpointer);
|
|
void GtkCList::toggle-add-mode (GtkCList *,
|
|
gpointer);
|
|
void GtkCList::extend-selection (GtkCList *,
|
|
GtkScrollType,
|
|
ggfloat,
|
|
gboolean,
|
|
gpointer);
|
|
void GtkCList::scroll-vertical (GtkCList *,
|
|
GtkScrollType,
|
|
ggfloat,
|
|
gpointer);
|
|
void GtkCList::scroll-horizontal (GtkCList *,
|
|
GtkScrollType,
|
|
ggfloat,
|
|
gpointer);
|
|
void GtkCList::abort-column-resize (GtkCList *,
|
|
gpointer);
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>GtkNotebook
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<p>
|
|
<tscreen><verb>
|
|
void GtkNotebook::switch-page (GtkNotebook *,
|
|
ggpointer,
|
|
gguint,
|
|
gpointer);
|
|
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>GtkList
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<p>
|
|
<tscreen><verb>
|
|
void GtkList::selection-changed (GtkList *,
|
|
gpointer);
|
|
void GtkList::select-child (GtkList *,
|
|
GtkWidget *,
|
|
gpointer);
|
|
void GtkList::unselect-child (GtkList *,
|
|
GtkWidget *,
|
|
gpointer);
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>GtkMenuShell
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<p>
|
|
<tscreen><verb>
|
|
void GtkMenuShell::deactivate (GtkMenuShell *,
|
|
gpointer);
|
|
void GtkMenuShell::selection-done (GtkMenuShell *,
|
|
gpointer);
|
|
void GtkMenuShell::move-current (GtkMenuShell *,
|
|
GtkMenuDirectionType,
|
|
gpointer);
|
|
void GtkMenuShell::activate-current (GtkMenuShell *,
|
|
gboolean,
|
|
gpointer);
|
|
void GtkMenuShell::cancel (GtkMenuShell *,
|
|
gpointer);
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>GtkToolbar
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<p>
|
|
<tscreen><verb>
|
|
void GtkToolbar::orientation-changed (GtkToolbar *,
|
|
ggint,
|
|
gpointer);
|
|
void GtkToolbar::style-changed (GtkToolbar *,
|
|
ggint,
|
|
gpointer);
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>GtkTree
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<p>
|
|
<tscreen><verb>
|
|
void GtkTree::selection-changed (GtkTree *,
|
|
gpointer);
|
|
void GtkTree::select-child (GtkTree *,
|
|
GtkWidget *,
|
|
gpointer);
|
|
void GtkTree::unselect-child (GtkTree *,
|
|
GtkWidget *,
|
|
gpointer);
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>GtkButton
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<p>
|
|
<tscreen><verb>
|
|
void GtkButton::pressed (GtkButton *,
|
|
gpointer);
|
|
void GtkButton::released (GtkButton *,
|
|
gpointer);
|
|
void GtkButton::clicked (GtkButton *,
|
|
gpointer);
|
|
void GtkButton::enter (GtkButton *,
|
|
gpointer);
|
|
void GtkButton::leave (GtkButton *,
|
|
gpointer);
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>GtkItem
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<p>
|
|
<tscreen><verb>
|
|
void GtkItem::select (GtkItem *,
|
|
gpointer);
|
|
void GtkItem::deselect (GtkItem *,
|
|
gpointer);
|
|
void GtkItem::toggle (GtkItem *,
|
|
gpointer);
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>GtkWindow
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<p>
|
|
<tscreen><verb>
|
|
void GtkWindow::set-focus (GtkWindow *,
|
|
ggpointer,
|
|
gpointer);
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>GtkHandleBox
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<p>
|
|
<tscreen><verb>
|
|
void GtkHandleBox::child-attached (GtkHandleBox *,
|
|
GtkWidget *,
|
|
gpointer);
|
|
void GtkHandleBox::child-detached (GtkHandleBox *,
|
|
GtkWidget *,
|
|
gpointer);
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>GtkToggleButton
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<p>
|
|
<tscreen><verb>
|
|
void GtkToggleButton::toggled (GtkToggleButton *,
|
|
gpointer);
|
|
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>GtkMenuItem
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<p>
|
|
<tscreen><verb>
|
|
void GtkMenuItem::activate (GtkMenuItem *,
|
|
gpointer);
|
|
void GtkMenuItem::activate-item (GtkMenuItem *,
|
|
gpointer);
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>GtkListItem
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<p>
|
|
<tscreen><verb>
|
|
void GtkListItem::toggle-focus-row (GtkListItem *,
|
|
gpointer);
|
|
void GtkListItem::select-all (GtkListItem *,
|
|
gpointer);
|
|
void GtkListItem::unselect-all (GtkListItem *,
|
|
gpointer);
|
|
void GtkListItem::undo-selection (GtkListItem *,
|
|
gpointer);
|
|
void GtkListItem::start-selection (GtkListItem *,
|
|
gpointer);
|
|
void GtkListItem::end-selection (GtkListItem *,
|
|
gpointer);
|
|
void GtkListItem::toggle-add-mode (GtkListItem *,
|
|
gpointer);
|
|
void GtkListItem::extend-selection (GtkListItem *,
|
|
GtkEnum,
|
|
ggfloat,
|
|
gboolean,
|
|
gpointer);
|
|
void GtkListItem::scroll-vertical (GtkListItem *,
|
|
GtkEnum,
|
|
ggfloat,
|
|
gpointer);
|
|
void GtkListItem::scroll-horizontal (GtkListItem *,
|
|
GtkEnum,
|
|
ggfloat,
|
|
gpointer);
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>GtkTreeItem
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<p>
|
|
<tscreen><verb>
|
|
void GtkTreeItem::collapse (GtkTreeItem *,
|
|
gpointer);
|
|
void GtkTreeItem::expand (GtkTreeItem *,
|
|
gpointer);
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>GtkCheckMenuItem
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<p>
|
|
<tscreen><verb>
|
|
void GtkCheckMenuItem::toggled (GtkCheckMenuItem *,
|
|
gpointer);
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>GtkInputDialog
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<p>
|
|
<tscreen><verb>
|
|
void GtkInputDialog::enable-device (GtkInputDialog *,
|
|
ggint,
|
|
gpointer);
|
|
void GtkInputDialog::disable-device (GtkInputDialog *,
|
|
ggint,
|
|
gpointer);
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>GtkColorSelection
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<p>
|
|
<tscreen><verb>
|
|
void GtkColorSelection::color-changed (GtkColorSelection *,
|
|
gpointer);
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>GtkStatusBar
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<p>
|
|
<tscreen><verb>
|
|
void GtkStatusbar::text-pushed (GtkStatusbar *,
|
|
gguint,
|
|
GtkString *,
|
|
gpointer);
|
|
void GtkStatusbar::text-popped (GtkStatusbar *,
|
|
gguint,
|
|
GtkString *,
|
|
gpointer);
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>GtkCTree
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<p>
|
|
<tscreen><verb>
|
|
void GtkCTree::tree-select-row (GtkCTree *,
|
|
GtkCTreeNode *,
|
|
ggint,
|
|
gpointer);
|
|
void GtkCTree::tree-unselect-row (GtkCTree *,
|
|
GtkCTreeNode *,
|
|
ggint,
|
|
gpointer);
|
|
void GtkCTree::tree-expand (GtkCTree *,
|
|
GtkCTreeNode *,
|
|
gpointer);
|
|
void GtkCTree::tree-collapse (GtkCTree *,
|
|
ggpointer,
|
|
gpointer);
|
|
void GtkCTree::tree-move (GtkCTree *,
|
|
GtkCTreeNode *,
|
|
GtkCTreeNode *,
|
|
GtkCTreeNode *,
|
|
gpointer);
|
|
void GtkCTree::change-focus-row-expansion (GtkCTree *,
|
|
GtkCTreeExpansionType,
|
|
gpointer);
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>GtkCurve
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<p>
|
|
<tscreen><verb>
|
|
void GtkCurve::curve-type-changed (GtkCurve *,
|
|
gpointer);
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>GtkAdjustment
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<p>
|
|
<tscreen><verb>
|
|
void GtkAdjustment::changed (GtkAdjustment *,
|
|
gpointer);
|
|
void GtkAdjustment::value-changed (GtkAdjustment *,
|
|
gpointer);
|
|
</verb></tscreen>
|
|
|
|
<!-- ***************************************************************** -->
|
|
<sect> Tipos de eventos GDK<label id="sec_GDK_Event_Types">
|
|
<!-- ***************************************************************** -->
|
|
<p>
|
|
Los siguientes tipos de datos se pasan en los manejadores de los
|
|
eventos por GTK+. Para cada tipo de dato que se muestra, se muestran
|
|
las señales que utilizan ese tipo de dato.
|
|
|
|
<itemize>
|
|
<item> GdkEvent
|
|
<itemize>
|
|
<item>drag_end_event
|
|
</itemize>
|
|
|
|
<item> GdkEventType
|
|
|
|
<item> GdkEventAny
|
|
<itemize>
|
|
<item>delete_event
|
|
<item>destroy_event
|
|
<item>map_event
|
|
<item>unmap_event
|
|
<item>no_expose_event
|
|
</itemize>
|
|
|
|
<item> GdkEventExpose
|
|
<itemize>
|
|
<item>expose_event
|
|
</itemize>
|
|
|
|
<item> GdkEventNoExpose
|
|
|
|
<item> GdkEventVisibility
|
|
|
|
<item> GdkEventMotion
|
|
<itemize>
|
|
<item>motion_notify_event
|
|
</itemize>
|
|
|
|
<item> GdkEventButton
|
|
<itemize>
|
|
<item>button_press_event
|
|
<item>button_release_event
|
|
</itemize>
|
|
|
|
<item> GdkEventKey
|
|
<itemize>
|
|
<item>key_press_event
|
|
<item>key_release_event
|
|
</itemize>
|
|
|
|
<item> GdkEventCrossing
|
|
<itemize>
|
|
<item>enter_notify_event
|
|
<item>leave_notify_event
|
|
</itemize>
|
|
|
|
<item> GdkEventFocus
|
|
<itemize>
|
|
<item>focus_in_event
|
|
<item>focus_out_event
|
|
</itemize>
|
|
|
|
<item> GdkEventConfigure
|
|
<itemize>
|
|
<item>configure_event
|
|
</itemize>
|
|
|
|
<item> GdkEventProperty
|
|
<itemize>
|
|
<item>property_notify_event
|
|
</itemize>
|
|
|
|
<item> GdkEventSelection
|
|
<itemize>
|
|
<item>selection_clear_event
|
|
<item>selection_request_event
|
|
<item>selection_notify_event
|
|
</itemize>
|
|
|
|
<item> GdkEventProximity
|
|
<itemize>
|
|
<item>proximity_in_event
|
|
<item>proximity_out_event
|
|
</itemize>
|
|
|
|
<item> GdkEventDragBegin
|
|
<itemize>
|
|
<item>drag_begin_event
|
|
</itemize>
|
|
|
|
<item> GdkEventDragRequest
|
|
<itemize>
|
|
<item>drag_request_event
|
|
</itemize>
|
|
|
|
<item> GdkEventDropEnter
|
|
<itemize>
|
|
<item>drop_enter_event
|
|
</itemize>
|
|
|
|
<item> GdkEventDropLeave
|
|
<itemize>
|
|
<item>drop_leave_event
|
|
</itemize>
|
|
|
|
<item> GdkEventDropDataAvailable
|
|
<itemize>
|
|
<item>drop_data_available_event
|
|
</itemize>
|
|
|
|
<item> GdkEventClient
|
|
<itemize>
|
|
<item>client_event
|
|
</itemize>
|
|
|
|
<item> GdkEventOther
|
|
<itemize>
|
|
<item>other_event
|
|
</itemize>
|
|
</itemize>
|
|
|
|
El tipo de dato <tt/GdkEventType/ es un tipo de dato especial que se
|
|
utiliza por todos los otros tipos de datos como un indicador del tipo
|
|
de dato que se le está pasando al manejador de señal. Como verá
|
|
más adelante, cada una de estructuras de los datos de los eventos
|
|
tienen un miembro de este tipo. Se define como la siguiente
|
|
enumeración:
|
|
|
|
<tscreen><verb>
|
|
typedef enum
|
|
{
|
|
GDK_NOTHING = -1,
|
|
GDK_DELETE = 0,
|
|
GDK_DESTROY = 1,
|
|
GDK_EXPOSE = 2,
|
|
GDK_MOTION_NOTIFY = 3,
|
|
GDK_BUTTON_PRESS = 4,
|
|
GDK_2BUTTON_PRESS = 5,
|
|
GDK_3BUTTON_PRESS = 6,
|
|
GDK_BUTTON_RELEASE = 7,
|
|
GDK_KEY_PRESS = 8,
|
|
GDK_KEY_RELEASE = 9,
|
|
GDK_ENTER_NOTIFY = 10,
|
|
GDK_LEAVE_NOTIFY = 11,
|
|
GDK_FOCUS_CHANGE = 12,
|
|
GDK_CONFIGURE = 13,
|
|
GDK_MAP = 14,
|
|
GDK_UNMAP = 15,
|
|
GDK_PROPERTY_NOTIFY = 16,
|
|
GDK_SELECTION_CLEAR = 17,
|
|
GDK_SELECTION_REQUEST = 18,
|
|
GDK_SELECTION_NOTIFY = 19,
|
|
GDK_PROXIMITY_IN = 20,
|
|
GDK_PROXIMITY_OUT = 21,
|
|
GDK_DRAG_BEGIN = 22,
|
|
GDK_DRAG_REQUEST = 23,
|
|
GDK_DROP_ENTER = 24,
|
|
GDK_DROP_LEAVE = 25,
|
|
GDK_DROP_DATA_AVAIL = 26,
|
|
GDK_CLIENT_EVENT = 27,
|
|
GDK_VISIBILITY_NOTIFY = 28,
|
|
GDK_NO_EXPOSE = 29,
|
|
GDK_OTHER_EVENT = 9999 /* Anacrónico, utilice en su lugar los
|
|
filtros */
|
|
} GdkEventType;
|
|
</verb></tscreen>
|
|
|
|
El otro tipo de evento que es diferente del resto es el mismo
|
|
<tt/GdkEvent/. Ésta es una unión de todos los otros tipos de
|
|
datos, que permite que se convierta en un tipo de dato de evento
|
|
específico con un manejador de señal.
|
|
|
|
<!-- Just a big list for now, needs expanding upon - TRG -->
|
|
Por tanto, los tipos de los datos de los eventos se definen como
|
|
sigue:
|
|
|
|
<tscreen><verb>
|
|
struct _GdkEventAny
|
|
{
|
|
GdkEventType type;
|
|
GdkWindow *window;
|
|
gint8 send_event;
|
|
};
|
|
|
|
struct _GdkEventExpose
|
|
{
|
|
GdkEventType type;
|
|
GdkWindow *window;
|
|
gint8 send_event;
|
|
GdkRectangle area;
|
|
gint count; /* Si count no es, entonces es el número de eventos que
|
|
* siguen. */
|
|
};
|
|
|
|
struct _GdkEventNoExpose
|
|
{
|
|
GdkEventType type;
|
|
GdkWindow *window;
|
|
gint8 send_event;
|
|
/* XXX: ¿Hay alguien que necesite los campos major_code y minor_code
|
|
de X ? */
|
|
};
|
|
|
|
struct _GdkEventVisibility
|
|
{
|
|
GdkEventType type;
|
|
GdkWindow *window;
|
|
gint8 send_event;
|
|
GdkVisibilityState state;
|
|
};
|
|
|
|
struct _GdkEventMotion
|
|
{
|
|
GdkEventType type;
|
|
GdkWindow *window;
|
|
gint8 send_event;
|
|
guint32 time;
|
|
gdouble x;
|
|
gdouble y;
|
|
gdouble pressure;
|
|
gdouble xtilt;
|
|
gdouble ytilt;
|
|
guint state;
|
|
gint16 is_hint;
|
|
GdkInputSource source;
|
|
guint32 deviceid;
|
|
gdouble x_root, y_root;
|
|
};
|
|
|
|
struct _GdkEventButton
|
|
{
|
|
GdkEventType type;
|
|
GdkWindow *window;
|
|
gint8 send_event;
|
|
guint32 time;
|
|
gdouble x;
|
|
gdouble y;
|
|
gdouble pressure;
|
|
gdouble xtilt;
|
|
gdouble ytilt;
|
|
guint state;
|
|
guint button;
|
|
GdkInputSource source;
|
|
guint32 deviceid;
|
|
gdouble x_root, y_root;
|
|
};
|
|
|
|
struct _GdkEventKey
|
|
{
|
|
GdkEventType type;
|
|
GdkWindow *window;
|
|
gint8 send_event;
|
|
guint32 time;
|
|
guint state;
|
|
guint keyval;
|
|
gint length;
|
|
gchar *string;
|
|
};
|
|
|
|
struct _GdkEventCrossing
|
|
{
|
|
GdkEventType type;
|
|
GdkWindow *window;
|
|
gint8 send_event;
|
|
GdkWindow *subwindow;
|
|
GdkNotifyType detail;
|
|
};
|
|
|
|
struct _GdkEventFocus
|
|
{
|
|
GdkEventType type;
|
|
GdkWindow *window;
|
|
gint8 send_event;
|
|
gint16 in;
|
|
};
|
|
|
|
struct _GdkEventConfigure
|
|
{
|
|
GdkEventType type;
|
|
GdkWindow *window;
|
|
gint8 send_event;
|
|
gint16 x, y;
|
|
gint16 width;
|
|
gint16 height;
|
|
};
|
|
|
|
struct _GdkEventProperty
|
|
{
|
|
GdkEventType type;
|
|
GdkWindow *window;
|
|
gint8 send_event;
|
|
GdkAtom atom;
|
|
guint32 time;
|
|
guint state;
|
|
};
|
|
|
|
struct _GdkEventSelection
|
|
{
|
|
GdkEventType type;
|
|
GdkWindow *window;
|
|
gint8 send_event;
|
|
GdkAtom selection;
|
|
GdkAtom target;
|
|
GdkAtom property;
|
|
guint32 requestor;
|
|
guint32 time;
|
|
};
|
|
|
|
/* Este tipo de evento se utiliza muy raramente. Solamente es
|
|
* importante para los programas que utilizan XInput y que dibujar su
|
|
* propio cursor */
|
|
|
|
struct _GdkEventProximity
|
|
{
|
|
GdkEventType type;
|
|
GdkWindow *window;
|
|
gint8 send_event;
|
|
guint32 time;
|
|
GdkInputSource source;
|
|
guint32 deviceid;
|
|
};
|
|
|
|
struct _GdkEventDragRequest
|
|
{
|
|
GdkEventType type;
|
|
GdkWindow *window;
|
|
gint8 send_event;
|
|
guint32 requestor;
|
|
union {
|
|
struct {
|
|
guint protocol_version:4;
|
|
guint sendreply:1;
|
|
guint willaccept:1;
|
|
guint delete_data:1; /* No borrar si se ha mandado un enlace,
|
|
sólo si se ha mandado el dato */
|
|
guint senddata:1;
|
|
guint reserved:22;
|
|
} flags;
|
|
glong allflags;
|
|
} u;
|
|
guint8 isdrop; /* Este evento gdk puede ser generado por un par de
|
|
eventos X - esto le permite a las aplicaciones
|
|
saber si ha ocurrido realmente el soltado (drop),
|
|
o si sólo hemos cambiado el valor de algunos datos
|
|
*/
|
|
|
|
GdkPoint drop_coords;
|
|
gchar *data_type;
|
|
guint32 timestamp;
|
|
};
|
|
|
|
struct _GdkEventDragBegin
|
|
{
|
|
GdkEventType type;
|
|
GdkWindow *window;
|
|
gint8 send_event;
|
|
union {
|
|
struct {
|
|
guint protocol_version:4;
|
|
guint reserved:28;
|
|
} flags;
|
|
glong allflags;
|
|
} u;
|
|
};
|
|
|
|
struct _GdkEventDropEnter
|
|
{
|
|
GdkEventType type;
|
|
GdkWindow *window;
|
|
gint8 send_event;
|
|
guint32 requestor;
|
|
union {
|
|
struct {
|
|
guint protocol_version:4;
|
|
guint sendreply:1;
|
|
guint extended_typelist:1;
|
|
guint reserved:26;
|
|
} flags;
|
|
glong allflags;
|
|
} u;
|
|
};
|
|
|
|
struct _GdkEventDropLeave
|
|
{
|
|
GdkEventType type;
|
|
GdkWindow *window;
|
|
gint8 send_event;
|
|
guint32 requestor;
|
|
union {
|
|
struct {
|
|
guint protocol_version:4;
|
|
guint reserved:28;
|
|
} flags;
|
|
glong allflags;
|
|
} u;
|
|
};
|
|
|
|
struct _GdkEventDropDataAvailable
|
|
{
|
|
GdkEventType type;
|
|
GdkWindow *window;
|
|
gint8 send_event;
|
|
guint32 requestor;
|
|
union {
|
|
struct {
|
|
guint protocol_version:4;
|
|
guint isdrop:1;
|
|
guint reserved:25;
|
|
} flags;
|
|
glong allflags;
|
|
} u;
|
|
gchar *data_type; /* tipo MIME */
|
|
gulong data_numbytes;
|
|
gpointer data;
|
|
guint32 timestamp;
|
|
GdkPoint coords;
|
|
};
|
|
|
|
struct _GdkEventClient
|
|
{
|
|
GdkEventType type;
|
|
GdkWindow *window;
|
|
gint8 send_event;
|
|
GdkAtom message_type;
|
|
gushort data_format;
|
|
union {
|
|
char b[20];
|
|
short s[10];
|
|
long l[5];
|
|
} data;
|
|
};
|
|
|
|
struct _GdkEventOther
|
|
{
|
|
GdkEventType type;
|
|
GdkWindow *window;
|
|
gint8 send_event;
|
|
GdkXEvent *xevent;
|
|
};
|
|
</verb></tscreen>
|
|
|
|
<!-- ***************************************************************** -->
|
|
<sect> Código ejemplo
|
|
<!-- ***************************************************************** -->
|
|
<p>
|
|
A continuación tenemos el código ejemplo que se ha utilizado en el
|
|
texto anterior y que no se ha incluido al completo en otro lugar.
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1>Tictactoe
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect2>tictactoe.h
|
|
<p>
|
|
<tscreen><verb>
|
|
/* principio del ejemplo tictactoe tictactoe.h */
|
|
|
|
/* GTK - The GIMP Toolkit
|
|
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
* Boston, MA 02111-1307, USA.
|
|
*/
|
|
#ifndef __TICTACTOE_H__
|
|
#define __TICTACTOE_H__
|
|
|
|
|
|
#include <gdk/gdk.h>
|
|
#include <gtk/gtkvbox.h>
|
|
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif /* __cplusplus */
|
|
|
|
#define TICTACTOE(obj) GTK_CHECK_CAST (obj, tictactoe_get_type (), Tictactoe)
|
|
#define TICTACTOE_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, tictactoe_get_type (), TictactoeClass)
|
|
#define IS_TICTACTOE(obj) GTK_CHECK_TYPE (obj, tictactoe_get_type ())
|
|
|
|
|
|
typedef struct _Tictactoe Tictactoe;
|
|
typedef struct _TictactoeClass TictactoeClass;
|
|
|
|
struct _Tictactoe
|
|
{
|
|
GtkVBox vbox;
|
|
|
|
GtkWidget *botones[3][3];
|
|
};
|
|
|
|
struct _TictactoeClass
|
|
{
|
|
GtkVBoxClass parent_class;
|
|
|
|
void (* tictactoe) (Tictactoe *ttt);
|
|
};
|
|
|
|
guint tictactoe_get_type (void);
|
|
GtkWidget* tictactoe_new (void);
|
|
void tictactoe_clear (Tictactoe *ttt);
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif /* __cplusplus */
|
|
|
|
#endif /* __TICTACTOE_H__ */
|
|
|
|
/* fin del ejemplo */
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect2>tictactoe.c
|
|
<p>
|
|
<tscreen><verb>
|
|
/* principio del ejemplo tictactoe tictactoe.c */
|
|
|
|
/* GTK - The GIMP Toolkit
|
|
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
* Boston, MA 02111-1307, USA.
|
|
*/
|
|
#include "gtk/gtksignal.h"
|
|
#include "gtk/gtktable.h"
|
|
#include "gtk/gtktogglebutton.h"
|
|
#include "tictactoe.h"
|
|
|
|
enum {
|
|
TICTACTOE_SIGNAL,
|
|
LAST_SIGNAL
|
|
};
|
|
|
|
static void tictactoe_class_init (TictactoeClass *klass);
|
|
static void tictactoe_init (Tictactoe *ttt);
|
|
static void tictactoe_toggle (GtkWidget *widget, Tictactoe *ttt);
|
|
|
|
static gint tictactoe_signals[LAST_SIGNAL] = { 0 };
|
|
|
|
guint
|
|
tictactoe_get_type ()
|
|
{
|
|
static guint ttt_type = 0;
|
|
|
|
if (!ttt_type)
|
|
{
|
|
GtkTypeInfo ttt_info =
|
|
{
|
|
"Tictactoe",
|
|
sizeof (Tictactoe),
|
|
sizeof (TictactoeClass),
|
|
(GtkClassInitFunc) tictactoe_class_init,
|
|
(GtkObjectInitFunc) tictactoe_init,
|
|
(GtkArgSetFunc) NULL,
|
|
(GtkArgGetFunc) NULL
|
|
};
|
|
|
|
ttt_type = gtk_type_unique (gtk_vbox_get_type (), &ttt_info);
|
|
}
|
|
|
|
return ttt_type;
|
|
}
|
|
|
|
static void
|
|
tictactoe_class_init (TictactoeClass *class)
|
|
{
|
|
GtkObjectClass *object_class;
|
|
|
|
object_class = (GtkObjectClass*) class;
|
|
|
|
tictactoe_signals[TICTACTOE_SIGNAL] = gtk_signal_new ("tictactoe",
|
|
GTK_RUN_FIRST,
|
|
object_class->type,
|
|
GTK_SIGNAL_OFFSET (TictactoeClass, tictactoe),
|
|
gtk_signal_default_marshaller, GTK_TYPE_NONE, 0);
|
|
|
|
|
|
gtk_object_class_add_signals (object_class, tictactoe_signals, LAST_SIGNAL);
|
|
|
|
class->tictactoe = NULL;
|
|
}
|
|
|
|
static void
|
|
tictactoe_init (Tictactoe *ttt)
|
|
{
|
|
GtkWidget *table;
|
|
gint i,j;
|
|
|
|
table = gtk_table_new (3, 3, TRUE);
|
|
gtk_container_add (GTK_CONTAINER(ttt), table);
|
|
gtk_widget_show (table);
|
|
|
|
for (i=0;i<3; i++)
|
|
for (j=0;j<3; j++)
|
|
{
|
|
ttt->buttons[i][j] = gtk_toggle_button_new ();
|
|
gtk_table_attach_defaults (GTK_TABLE(table), ttt->buttons[i][j],
|
|
i, i+1, j, j+1);
|
|
gtk_signal_connect (GTK_OBJECT (ttt->buttons[i][j]), "toggled",
|
|
GTK_SIGNAL_FUNC (tictactoe_toggle), ttt);
|
|
gtk_widget_set_usize (ttt->buttons[i][j], 20, 20);
|
|
gtk_widget_show (ttt->buttons[i][j]);
|
|
}
|
|
}
|
|
|
|
GtkWidget*
|
|
tictactoe_new ()
|
|
{
|
|
return GTK_WIDGET ( gtk_type_new (tictactoe_get_type ()));
|
|
}
|
|
|
|
void
|
|
tictactoe_clear (Tictactoe *ttt)
|
|
{
|
|
int i,j;
|
|
|
|
for (i=0;i<3;i++)
|
|
for (j=0;j<3;j++)
|
|
{
|
|
gtk_signal_handler_block_by_data (GTK_OBJECT(ttt->buttons[i][j]), ttt);
|
|
gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (ttt->buttons[i][j]),
|
|
FALSE);
|
|
gtk_signal_handler_unblock_by_data (GTK_OBJECT(ttt->buttons[i][j]), ttt);
|
|
}
|
|
}
|
|
|
|
static void
|
|
tictactoe_toggle (GtkWidget *widget, Tictactoe *ttt)
|
|
{
|
|
int i,k;
|
|
|
|
static int rwins[8][3] = { { 0, 0, 0 }, { 1, 1, 1 }, { 2, 2, 2 },
|
|
{ 0, 1, 2 }, { 0, 1, 2 }, { 0, 1, 2 },
|
|
{ 0, 1, 2 }, { 0, 1, 2 } };
|
|
static int cwins[8][3] = { { 0, 1, 2 }, { 0, 1, 2 }, { 0, 1, 2 },
|
|
{ 0, 0, 0 }, { 1, 1, 1 }, { 2, 2, 2 },
|
|
{ 0, 1, 2 }, { 2, 1, 0 } };
|
|
|
|
int success, found;
|
|
|
|
for (k=0; k<8; k++)
|
|
{
|
|
success = TRUE;
|
|
found = FALSE;
|
|
|
|
for (i=0;i<3;i++)
|
|
{
|
|
success = success &&
|
|
GTK_TOGGLE_BUTTON(ttt->buttons[rwins[k][i]][cwins[k][i]])->active;
|
|
found = found ||
|
|
ttt->buttons[rwins[k][i]][cwins[k][i]] == widget;
|
|
}
|
|
|
|
if (success && found)
|
|
{
|
|
gtk_signal_emit (GTK_OBJECT (ttt),
|
|
tictactoe_signals[TICTACTOE_SIGNAL]);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* fin del ejemplo */
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect2>ttt_test.c
|
|
<p>
|
|
<tscreen><verb>
|
|
/* principio del ejemplo tictactoe ttt_test.c */
|
|
|
|
#include <gtk/gtk.h>
|
|
#include "tictactoe.h"
|
|
|
|
void
|
|
win (GtkWidget *widget, gpointer data)
|
|
{
|
|
g_print ("Yay!\n");
|
|
tictactoe_clear (TICTACTOE (widget));
|
|
}
|
|
|
|
int
|
|
main (int argc, char *argv[])
|
|
{
|
|
GtkWidget *ventana;
|
|
GtkWidget *ttt;
|
|
|
|
gtk_init (&argc, &argv);
|
|
|
|
ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
|
|
|
gtk_window_set_title (GTK_WINDOW (ventana), "Aspect Frame");
|
|
|
|
gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
|
|
GTK_SIGNAL_FUNC (gtk_exit), NULL);
|
|
|
|
gtk_container_border_width (GTK_CONTAINER (ventana), 10);
|
|
|
|
ttt = tictactoe_new ();
|
|
|
|
gtk_container_add (GTK_CONTAINER (ventana), ttt);
|
|
gtk_widget_show (ttt);
|
|
|
|
gtk_signal_connect (GTK_OBJECT (ttt), "tictactoe",
|
|
GTK_SIGNAL_FUNC (win), NULL);
|
|
|
|
gtk_widget_show (ventana);
|
|
|
|
gtk_main ();
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* fin del ejemplo */
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1> GtkDial
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect2> gtkdial.h
|
|
<p>
|
|
<tscreen><verb>
|
|
/* principio del ejmplo gtkdial gtkdial.h */
|
|
|
|
/* GTK - The GIMP Toolkit
|
|
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
* Boston, MA 02111-1307, USA.
|
|
*/
|
|
#ifndef __GTK_DIAL_H__
|
|
#define __GTK_DIAL_H__
|
|
|
|
|
|
#include <gdk/gdk.h>
|
|
#include <gtk/gtkadjustment.h>
|
|
#include <gtk/gtkwidget.h>
|
|
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif /* __cplusplus */
|
|
|
|
|
|
#define GTK_DIAL(obj) GTK_CHECK_CAST (obj, gtk_dial_get_type (), GtkDial)
|
|
#define GTK_DIAL_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, gtk_dial_get_type (), GtkDialClass)
|
|
#define GTK_IS_DIAL(obj) GTK_CHECK_TYPE (obj, gtk_dial_get_type ())
|
|
|
|
|
|
typedef struct _GtkDial GtkDial;
|
|
typedef struct _GtkDialClass GtkDialClass;
|
|
|
|
struct _GtkDial
|
|
{
|
|
GtkWidget widget;
|
|
|
|
/* política de actualización
|
|
* (GTK_UPDATE_[CONTINUOUS/DELAYED/DISCONTINUOUS]) */
|
|
guint policy : 2;
|
|
|
|
/* Botón actualmente presionado o 0 si no hay ninguno */
|
|
guint8 boton;
|
|
|
|
/* Dimensión de los componendes del dial */
|
|
gint radius;
|
|
gint pointer_width;
|
|
|
|
/* ID del temporizador de actualización, o 0 si no hay ninguno */
|
|
guint32 timer;
|
|
|
|
/* ángulo actual */
|
|
gfloat angle;
|
|
|
|
/* Viejos valores almacenados del adjustment, para que así no
|
|
* tengamos que saber cuando cambia algo */
|
|
gfloat old_value;
|
|
gfloat old_lower;
|
|
gfloat old_upper;
|
|
|
|
/* El objeto adjustment que almacena los datos para este dial */
|
|
GtkAdjustment *adjustment;
|
|
};
|
|
|
|
struct _GtkDialClass
|
|
{
|
|
GtkWidgetClass parent_class;
|
|
};
|
|
|
|
|
|
GtkWidget* gtk_dial_new (GtkAdjustment *adjustment);
|
|
guint gtk_dial_get_type (void);
|
|
GtkAdjustment* gtk_dial_get_adjustment (GtkDial *dial);
|
|
void gtk_dial_set_update_policy (GtkDial *dial,
|
|
GtkUpdateType policy);
|
|
|
|
void gtk_dial_set_adjustment (GtkDial *dial,
|
|
GtkAdjustment *adjustment);
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif /* __cplusplus */
|
|
|
|
|
|
#endif /* __GTK_DIAL_H__ */
|
|
/* fin del ejemplo */
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect2> gtkdial.c
|
|
<p>
|
|
<tscreen><verb>
|
|
/* principio del ejemplo gtkdial gtkdial.c */
|
|
|
|
/* GTK - The GIMP Toolkit
|
|
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
* Boston, MA 02111-1307, USA.
|
|
*/
|
|
#include <math.h>
|
|
#include <stdio.h>
|
|
#include <gtk/gtkmain.h>
|
|
#include <gtk/gtksignal.h>
|
|
|
|
#include "gtkdial.h"
|
|
|
|
#define SCROLL_DELAY_LENGTH 300
|
|
#define DIAL_DEFAULT_SIZE 100
|
|
|
|
/* declaraciones de funciones */
|
|
|
|
static void gtk_dial_class_init (GtkDialClass *klass);
|
|
static void gtk_dial_init (GtkDial *dial);
|
|
static void gtk_dial_destroy (GtkObject *object);
|
|
static void gtk_dial_realize (GtkWidget *widget);
|
|
static void gtk_dial_size_request (GtkWidget *widget,
|
|
GtkRequisition *requisition);
|
|
static void gtk_dial_size_allocate (GtkWidget *widget,
|
|
GtkAllocation *allocation);
|
|
static gint gtk_dial_expose (GtkWidget *widget,
|
|
GdkEventExpose *event);
|
|
static gint gtk_dial_button_press (GtkWidget *widget,
|
|
GdkEventButton *event);
|
|
static gint gtk_dial_button_release (GtkWidget *widget,
|
|
GdkEventButton *event);
|
|
static gint gtk_dial_motion_notify (GtkWidget *widget,
|
|
GdkEventMotion *event);
|
|
static gint gtk_dial_timer (GtkDial *dial);
|
|
|
|
static void gtk_dial_update_mouse (GtkDial *dial, gint x, gint y);
|
|
static void gtk_dial_update (GtkDial *dial);
|
|
static void gtk_dial_adjustment_changed (GtkAdjustment *adjustment,
|
|
gpointer data);
|
|
static void gtk_dial_adjustment_value_changed (GtkAdjustment *adjustment,
|
|
gpointer data);
|
|
|
|
/* datos locales */
|
|
|
|
static GtkWidgetClass *parent_class = NULL;
|
|
|
|
guint
|
|
gtk_dial_get_type ()
|
|
{
|
|
static guint dial_type = 0;
|
|
|
|
if (!dial_type)
|
|
{
|
|
GtkTypeInfo dial_info =
|
|
{
|
|
"GtkDial",
|
|
sizeof (GtkDial),
|
|
sizeof (GtkDialClass),
|
|
(GtkClassInitFunc) gtk_dial_class_init,
|
|
(GtkObjectInitFunc) gtk_dial_init,
|
|
(GtkArgSetFunc) NULL,
|
|
(GtkArgGetFunc) NULL,
|
|
};
|
|
|
|
dial_type = gtk_type_unique (gtk_widget_get_type (), &dial_info);
|
|
}
|
|
|
|
return dial_type;
|
|
}
|
|
|
|
static void
|
|
gtk_dial_class_init (GtkDialClass *class)
|
|
{
|
|
GtkObjectClass *object_class;
|
|
GtkWidgetClass *widget_class;
|
|
|
|
object_class = (GtkObjectClass*) class;
|
|
widget_class = (GtkWidgetClass*) class;
|
|
|
|
parent_class = gtk_type_class (gtk_widget_get_type ());
|
|
|
|
object_class->destroy = gtk_dial_destroy;
|
|
|
|
widget_class->realize = gtk_dial_realize;
|
|
widget_class->expose_event = gtk_dial_expose;
|
|
widget_class->size_request = gtk_dial_size_request;
|
|
widget_class->size_allocate = gtk_dial_size_allocate;
|
|
widget_class->button_press_event = gtk_dial_button_press;
|
|
widget_class->button_release_event = gtk_dial_button_release;
|
|
widget_class->motion_notify_event = gtk_dial_motion_notify;
|
|
}
|
|
|
|
static void
|
|
gtk_dial_init (GtkDial *dial)
|
|
{
|
|
dial->button = 0;
|
|
dial->policy = GTK_UPDATE_CONTINUOUS;
|
|
dial->timer = 0;
|
|
dial->radius = 0;
|
|
dial->pointer_width = 0;
|
|
dial->angle = 0.0;
|
|
dial->old_value = 0.0;
|
|
dial->old_lower = 0.0;
|
|
dial->old_upper = 0.0;
|
|
dial->adjustment = NULL;
|
|
}
|
|
|
|
GtkWidget*
|
|
gtk_dial_new (GtkAdjustment *adjustment)
|
|
{
|
|
GtkDial *dial;
|
|
|
|
dial = gtk_type_new (gtk_dial_get_type ());
|
|
|
|
if (!adjustment)
|
|
adjustment = (GtkAdjustment*) gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
|
|
|
|
gtk_dial_set_adjustment (dial, adjustment);
|
|
|
|
return GTK_WIDGET (dial);
|
|
}
|
|
|
|
static void
|
|
gtk_dial_destroy (GtkObject *object)
|
|
{
|
|
GtkDial *dial;
|
|
|
|
g_return_if_fail (object != NULL);
|
|
g_return_if_fail (GTK_IS_DIAL (object));
|
|
|
|
dial = GTK_DIAL (object);
|
|
|
|
if (dial->adjustment)
|
|
gtk_object_unref (GTK_OBJECT (dial->adjustment));
|
|
|
|
if (GTK_OBJECT_CLASS (parent_class)->destroy)
|
|
(* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
|
|
}
|
|
|
|
GtkAdjustment*
|
|
gtk_dial_get_adjustment (GtkDial *dial)
|
|
{
|
|
g_return_val_if_fail (dial != NULL, NULL);
|
|
g_return_val_if_fail (GTK_IS_DIAL (dial), NULL);
|
|
|
|
return dial->adjustment;
|
|
}
|
|
|
|
void
|
|
gtk_dial_set_update_policy (GtkDial *dial,
|
|
GtkUpdateType policy)
|
|
{
|
|
g_return_if_fail (dial != NULL);
|
|
g_return_if_fail (GTK_IS_DIAL (dial));
|
|
|
|
dial->policy = policy;
|
|
}
|
|
|
|
void
|
|
gtk_dial_set_adjustment (GtkDial *dial,
|
|
GtkAdjustment *adjustment)
|
|
{
|
|
g_return_if_fail (dial != NULL);
|
|
g_return_if_fail (GTK_IS_DIAL (dial));
|
|
|
|
if (dial->adjustment)
|
|
{
|
|
gtk_signal_disconnect_by_data (GTK_OBJECT (dial->adjustment), (gpointer) dial);
|
|
gtk_object_unref (GTK_OBJECT (dial->adjustment));
|
|
}
|
|
|
|
dial->adjustment = adjustment;
|
|
gtk_object_ref (GTK_OBJECT (dial->adjustment));
|
|
|
|
gtk_signal_connect (GTK_OBJECT (adjustment), "changed",
|
|
(GtkSignalFunc) gtk_dial_adjustment_changed,
|
|
(gpointer) dial);
|
|
gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed",
|
|
(GtkSignalFunc) gtk_dial_adjustment_value_changed,
|
|
(gpointer) dial);
|
|
|
|
dial->old_value = adjustment->value;
|
|
dial->old_lower = adjustment->lower;
|
|
dial->old_upper = adjustment->upper;
|
|
|
|
gtk_dial_update (dial);
|
|
}
|
|
|
|
static void
|
|
gtk_dial_realize (GtkWidget *widget)
|
|
{
|
|
GtkDial *dial;
|
|
GdkWindowAttr attributes;
|
|
gint attributes_mask;
|
|
|
|
g_return_if_fail (widget != NULL);
|
|
g_return_if_fail (GTK_IS_DIAL (widget));
|
|
|
|
GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
|
|
dial = GTK_DIAL (widget);
|
|
|
|
attributes.x = widget->allocation.x;
|
|
attributes.y = widget->allocation.y;
|
|
attributes.width = widget->allocation.width;
|
|
attributes.height = widget->allocation.height;
|
|
attributes.wclass = GDK_INPUT_OUTPUT;
|
|
attributes.window_type = GDK_WINDOW_CHILD;
|
|
attributes.event_mask = gtk_widget_get_events (widget) |
|
|
GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK |
|
|
GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK |
|
|
GDK_POINTER_MOTION_HINT_MASK;
|
|
attributes.visual = gtk_widget_get_visual (widget);
|
|
attributes.colormap = gtk_widget_get_colormap (widget);
|
|
|
|
attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
|
|
widget->window = gdk_window_new (widget->parent->window, &attributes, attributes_mask);
|
|
|
|
widget->style = gtk_style_attach (widget->style, widget->window);
|
|
|
|
gdk_window_set_user_data (widget->window, widget);
|
|
|
|
gtk_style_set_background (widget->style, widget->window, GTK_STATE_ACTIVE);
|
|
}
|
|
|
|
static void
|
|
gtk_dial_size_request (GtkWidget *widget,
|
|
GtkRequisition *requisition)
|
|
{
|
|
requisition->width = DIAL_DEFAULT_SIZE;
|
|
requisition->height = DIAL_DEFAULT_SIZE;
|
|
}
|
|
|
|
static void
|
|
gtk_dial_size_allocate (GtkWidget *widget,
|
|
GtkAllocation *allocation)
|
|
{
|
|
GtkDial *dial;
|
|
|
|
g_return_if_fail (widget != NULL);
|
|
g_return_if_fail (GTK_IS_DIAL (widget));
|
|
g_return_if_fail (allocation != NULL);
|
|
|
|
widget->allocation = *allocation;
|
|
dial = GTK_DIAL (widget);
|
|
|
|
if (GTK_WIDGET_REALIZED (widget))
|
|
{
|
|
|
|
gdk_window_move_resize (widget->window,
|
|
allocation->x, allocation->y,
|
|
allocation->width, allocation->height);
|
|
|
|
}
|
|
dial->radius = MIN(allocation->width,allocation->height) * 0.45;
|
|
dial->pointer_width = dial->radius / 5;
|
|
}
|
|
|
|
static gint
|
|
gtk_dial_expose (GtkWidget *widget,
|
|
GdkEventExpose *event)
|
|
{
|
|
GtkDial *dial;
|
|
GdkPoint points[3];
|
|
gdouble s,c;
|
|
gdouble theta;
|
|
gint xc, yc;
|
|
gint tick_length;
|
|
gint i;
|
|
|
|
g_return_val_if_fail (widget != NULL, FALSE);
|
|
g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
|
|
g_return_val_if_fail (event != NULL, FALSE);
|
|
|
|
if (event->count > 0)
|
|
return FALSE;
|
|
|
|
dial = GTK_DIAL (widget);
|
|
|
|
gdk_window_clear_area (widget->window,
|
|
0, 0,
|
|
widget->allocation.width,
|
|
widget->allocation.height);
|
|
|
|
xc = widget->allocation.width/2;
|
|
yc = widget->allocation.height/2;
|
|
|
|
/* Dibuja las rayitas */
|
|
|
|
for (i=0; i<25; i++)
|
|
{
|
|
theta = (i*M_PI/18. - M_PI/6.);
|
|
s = sin(theta);
|
|
c = cos(theta);
|
|
|
|
tick_length = (i%6 == 0) ? dial->pointer_width : dial->pointer_width/2;
|
|
|
|
gdk_draw_line (widget->window,
|
|
widget->style->fg_gc[widget->state],
|
|
xc + c*(dial->radius - tick_length),
|
|
yc - s*(dial->radius - tick_length),
|
|
xc + c*dial->radius,
|
|
yc - s*dial->radius);
|
|
}
|
|
|
|
/* Dibuja el puntero */
|
|
|
|
s = sin(dial->angle);
|
|
c = cos(dial->angle);
|
|
|
|
|
|
points[0].x = xc + s*dial->pointer_width/2;
|
|
points[0].y = yc + c*dial->pointer_width/2;
|
|
points[1].x = xc + c*dial->radius;
|
|
points[1].y = yc - s*dial->radius;
|
|
points[2].x = xc - s*dial->pointer_width/2;
|
|
points[2].y = yc - c*dial->pointer_width/2;
|
|
|
|
gtk_draw_polygon (widget->style,
|
|
widget->window,
|
|
GTK_STATE_NORMAL,
|
|
GTK_SHADOW_OUT,
|
|
points, 3,
|
|
TRUE);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gint
|
|
gtk_dial_button_press (GtkWidget *widget,
|
|
GdkEventButton *event)
|
|
{
|
|
GtkDial *dial;
|
|
gint dx, dy;
|
|
double s, c;
|
|
double d_parallel;
|
|
double d_perpendicular;
|
|
|
|
g_return_val_if_fail (widget != NULL, FALSE);
|
|
g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
|
|
g_return_val_if_fail (event != NULL, FALSE);
|
|
|
|
dial = GTK_DIAL (widget);
|
|
|
|
|
|
/* Determinar si la pulsación del botón fue dentro de la región del
|
|
puntero - esto lo hacemos calculando la distancia x e y del punto
|
|
donde se pulsó el botón ratón de la línea que se ha pasado mediante el
|
|
puntero */
|
|
|
|
dx = event->x - widget->allocation.width / 2;
|
|
dy = widget->allocation.height / 2 - event->y;
|
|
|
|
s = sin(dial->angle);
|
|
c = cos(dial->angle);
|
|
|
|
d_parallel = s*dy + c*dx;
|
|
d_perpendicular = fabs(s*dx - c*dy);
|
|
|
|
if (!dial->button &&
|
|
(d_perpendicular < dial->pointer_width/2) &&
|
|
(d_parallel > - dial->pointer_width))
|
|
{
|
|
gtk_grab_add (widget);
|
|
|
|
dial->button = event->button;
|
|
|
|
gtk_dial_update_mouse (dial, event->x, event->y);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gint
|
|
gtk_dial_button_release (GtkWidget *widget,
|
|
GdkEventButton *event)
|
|
{
|
|
GtkDial *dial;
|
|
|
|
g_return_val_if_fail (widget != NULL, FALSE);
|
|
g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
|
|
g_return_val_if_fail (event != NULL, FALSE);
|
|
|
|
dial = GTK_DIAL (widget);
|
|
|
|
if (dial->button == event->button)
|
|
{
|
|
gtk_grab_remove (widget);
|
|
|
|
dial->button = 0;
|
|
|
|
if (dial->policy == GTK_UPDATE_DELAYED)
|
|
gtk_timeout_remove (dial->timer);
|
|
|
|
if ((dial->policy != GTK_UPDATE_CONTINUOUS) &&
|
|
(dial->old_value != dial->adjustment->value))
|
|
gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gint
|
|
gtk_dial_motion_notify (GtkWidget *widget,
|
|
GdkEventMotion *event)
|
|
{
|
|
GtkDial *dial;
|
|
GdkModifierType mods;
|
|
gint x, y, mask;
|
|
|
|
g_return_val_if_fail (widget != NULL, FALSE);
|
|
g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
|
|
g_return_val_if_fail (event != NULL, FALSE);
|
|
|
|
dial = GTK_DIAL (widget);
|
|
|
|
if (dial->button != 0)
|
|
{
|
|
x = event->x;
|
|
y = event->y;
|
|
|
|
if (event->is_hint || (event->window != widget->window))
|
|
gdk_window_get_pointer (widget->window, &x, &y, &mods);
|
|
|
|
switch (dial->button)
|
|
{
|
|
case 1:
|
|
mask = GDK_BUTTON1_MASK;
|
|
break;
|
|
case 2:
|
|
mask = GDK_BUTTON2_MASK;
|
|
break;
|
|
case 3:
|
|
mask = GDK_BUTTON3_MASK;
|
|
break;
|
|
default:
|
|
mask = 0;
|
|
break;
|
|
}
|
|
|
|
if (mods & mask)
|
|
gtk_dial_update_mouse (dial, x,y);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gint
|
|
gtk_dial_timer (GtkDial *dial)
|
|
{
|
|
g_return_val_if_fail (dial != NULL, FALSE);
|
|
g_return_val_if_fail (GTK_IS_DIAL (dial), FALSE);
|
|
|
|
if (dial->policy == GTK_UPDATE_DELAYED)
|
|
gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
gtk_dial_update_mouse (GtkDial *dial, gint x, gint y)
|
|
{
|
|
gint xc, yc;
|
|
gfloat old_value;
|
|
|
|
g_return_if_fail (dial != NULL);
|
|
g_return_if_fail (GTK_IS_DIAL (dial));
|
|
|
|
xc = GTK_WIDGET(dial)->allocation.width / 2;
|
|
yc = GTK_WIDGET(dial)->allocation.height / 2;
|
|
|
|
old_value = dial->adjustment->value;
|
|
dial->angle = atan2(yc-y, x-xc);
|
|
|
|
if (dial->angle < -M_PI/2.)
|
|
dial->angle += 2*M_PI;
|
|
|
|
if (dial->angle < -M_PI/6)
|
|
dial->angle = -M_PI/6;
|
|
|
|
if (dial->angle > 7.*M_PI/6.)
|
|
dial->angle = 7.*M_PI/6.;
|
|
|
|
dial->adjustment->value = dial->adjustment->lower + (7.*M_PI/6 - dial->angle) *
|
|
(dial->adjustment->upper - dial->adjustment->lower) / (4.*M_PI/3.);
|
|
|
|
if (dial->adjustment->value != old_value)
|
|
{
|
|
if (dial->policy == GTK_UPDATE_CONTINUOUS)
|
|
{
|
|
gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
|
|
}
|
|
else
|
|
{
|
|
gtk_widget_draw (GTK_WIDGET(dial), NULL);
|
|
|
|
if (dial->policy == GTK_UPDATE_DELAYED)
|
|
{
|
|
if (dial->timer)
|
|
gtk_timeout_remove (dial->timer);
|
|
|
|
dial->timer = gtk_timeout_add (SCROLL_DELAY_LENGTH,
|
|
(GtkFunction) gtk_dial_timer,
|
|
(gpointer) dial);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
gtk_dial_update (GtkDial *dial)
|
|
{
|
|
gfloat new_value;
|
|
|
|
g_return_if_fail (dial != NULL);
|
|
g_return_if_fail (GTK_IS_DIAL (dial));
|
|
|
|
new_value = dial->adjustment->value;
|
|
|
|
if (new_value < dial->adjustment->lower)
|
|
new_value = dial->adjustment->lower;
|
|
|
|
if (new_value > dial->adjustment->upper)
|
|
new_value = dial->adjustment->upper;
|
|
|
|
if (new_value != dial->adjustment->value)
|
|
{
|
|
dial->adjustment->value = new_value;
|
|
gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
|
|
}
|
|
|
|
dial->angle = 7.*M_PI/6. - (new_value - dial->adjustment->lower) * 4.*M_PI/3. /
|
|
(dial->adjustment->upper - dial->adjustment->lower);
|
|
|
|
gtk_widget_draw (GTK_WIDGET(dial), NULL);
|
|
}
|
|
|
|
static void
|
|
gtk_dial_adjustment_changed (GtkAdjustment *adjustment,
|
|
gpointer data)
|
|
{
|
|
GtkDial *dial;
|
|
|
|
g_return_if_fail (adjustment != NULL);
|
|
g_return_if_fail (data != NULL);
|
|
|
|
dial = GTK_DIAL (data);
|
|
|
|
if ((dial->old_value != adjustment->value) ||
|
|
(dial->old_lower != adjustment->lower) ||
|
|
(dial->old_upper != adjustment->upper))
|
|
{
|
|
gtk_dial_update (dial);
|
|
|
|
dial->old_value = adjustment->value;
|
|
dial->old_lower = adjustment->lower;
|
|
dial->old_upper = adjustment->upper;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gtk_dial_adjustment_value_changed (GtkAdjustment *adjustment,
|
|
gpointer data)
|
|
{
|
|
GtkDial *dial;
|
|
|
|
g_return_if_fail (adjustment != NULL);
|
|
g_return_if_fail (data != NULL);
|
|
|
|
dial = GTK_DIAL (data);
|
|
|
|
if (dial->old_value != adjustment->value)
|
|
{
|
|
gtk_dial_update (dial);
|
|
|
|
dial->old_value = adjustment->value;
|
|
}
|
|
}
|
|
/* fin del ejemplo */
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1> Scribble
|
|
<p>
|
|
<tscreen><verb>
|
|
/* principio del ejemplo scribble-simple scribble-simple.c */
|
|
|
|
/* GTK - The GIMP Toolkit
|
|
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
* Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
/* Creamos un backing pixmap para la zona donde dibujamos */
|
|
static GdkPixmap *pixmap = NULL;
|
|
|
|
/* Creamos un nuevo backing pixmap del tamaño apropiado */
|
|
static gint
|
|
configure_event (GtkWidget *widget, GdkEventConfigure *event)
|
|
{
|
|
if (pixmap)
|
|
gdk_pixmap_unref(pixmap);
|
|
|
|
pixmap = gdk_pixmap_new(widget->window,
|
|
widget->allocation.width,
|
|
widget->allocation.height,
|
|
-1);
|
|
gdk_draw_rectangle (pixmap,
|
|
widget->style->white_gc,
|
|
TRUE,
|
|
0, 0,
|
|
widget->allocation.width,
|
|
widget->allocation.height);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* Redibujamos la pantalla con el backing pixmap */
|
|
static gint
|
|
expose_event (GtkWidget *widget, GdkEventExpose *event)
|
|
{
|
|
gdk_draw_pixmap(widget->window,
|
|
widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
|
|
pixmap,
|
|
event->area.x, event->area.y,
|
|
event->area.x, event->area.y,
|
|
event->area.width, event->area.height);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/* Dibujamos un rectángulo en la pantalla */
|
|
static void
|
|
draw_brush (GtkWidget *widget, gdouble x, gdouble y)
|
|
{
|
|
GdkRectangle update_rect;
|
|
|
|
update_rect.x = x - 5;
|
|
update_rect.y = y - 5;
|
|
update_rect.width = 10;
|
|
update_rect.height = 10;
|
|
gdk_draw_rectangle (pixmap,
|
|
widget->style->black_gc,
|
|
TRUE,
|
|
update_rect.x, update_rect.y,
|
|
update_rect.width, update_rect.height);
|
|
gtk_widget_draw (widget, &update_rect);
|
|
}
|
|
|
|
static gint
|
|
button_press_event (GtkWidget *widget, GdkEventButton *event)
|
|
{
|
|
if (event->button == 1 && pixmap != NULL)
|
|
draw_brush (widget, event->x, event->y);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gint
|
|
motion_notify_event (GtkWidget *widget, GdkEventMotion *event)
|
|
{
|
|
int x, y;
|
|
GdkModifierType state;
|
|
|
|
if (event->is_hint)
|
|
gdk_window_get_pointer (event->window, &x, &y, &state);
|
|
else
|
|
{
|
|
x = event->x;
|
|
y = event->y;
|
|
state = event->state;
|
|
}
|
|
|
|
if (state & GDK_BUTTON1_MASK && pixmap != NULL)
|
|
draw_brush (widget, x, y);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
quit ()
|
|
{
|
|
gtk_exit (0);
|
|
}
|
|
|
|
int
|
|
main (int argc, char *argv[])
|
|
{
|
|
GtkWidget *ventana;
|
|
GtkWidget *drawing_area;
|
|
GtkWidget *vbox;
|
|
|
|
GtkWidget *boton;
|
|
|
|
gtk_init (&argc, &argv);
|
|
|
|
ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
|
gtk_widget_set_name (ventana, "Test Input");
|
|
|
|
vbox = gtk_vbox_new (FALSE, 0);
|
|
gtk_container_add (GTK_CONTAINER (ventana), vbox);
|
|
gtk_widget_show (vbox);
|
|
|
|
gtk_signal_connect (GTK_OBJECT (ventana), "destroy",
|
|
GTK_SIGNAL_FUNC (quit), NULL);
|
|
|
|
/* Crear la zona de dibujado */
|
|
|
|
drawing_area = gtk_drawing_area_new ();
|
|
gtk_drawing_area_size (GTK_DRAWING_AREA (drawing_area), 200, 200);
|
|
gtk_box_pack_start (GTK_BOX (vbox), drawing_area, TRUE, TRUE, 0);
|
|
|
|
gtk_widget_show (drawing_area);
|
|
|
|
/* Las señales utilizadas para manejar el backing pixmap */
|
|
|
|
gtk_signal_connect (GTK_OBJECT (drawing_area), "expose_event",
|
|
(GtkSignalFunc) expose_event, NULL);
|
|
gtk_signal_connect (GTK_OBJECT(drawing_area),"configure_event",
|
|
(GtkSignalFunc) configure_event, NULL);
|
|
|
|
/* Señales evento */
|
|
|
|
gtk_signal_connect (GTK_OBJECT (drawing_area), "motion_notify_event",
|
|
(GtkSignalFunc) motion_notify_event, NULL);
|
|
gtk_signal_connect (GTK_OBJECT (drawing_area), "button_press_event",
|
|
(GtkSignalFunc) button_press_event, NULL);
|
|
|
|
gtk_widget_set_events (drawing_area, GDK_EXPOSURE_MASK
|
|
| GDK_LEAVE_NOTIFY_MASK
|
|
| GDK_BUTTON_PRESS_MASK
|
|
| GDK_POINTER_MOTION_MASK
|
|
| GDK_POINTER_MOTION_HINT_MASK);
|
|
|
|
/* .. Y un botón para salir */
|
|
boton = gtk_button_new_with_label ("Quit");
|
|
gtk_box_pack_start (GTK_BOX (vbox), boton, FALSE, FALSE, 0);
|
|
|
|
gtk_signal_connect_object (GTK_OBJECT (boton), "clicked",
|
|
GTK_SIGNAL_FUNC (gtk_widget_destroy),
|
|
GTK_OBJECT (ventana));
|
|
gtk_widget_show (boton);
|
|
|
|
gtk_widget_show (ventana);
|
|
|
|
gtk_main ();
|
|
|
|
return 0;
|
|
}
|
|
/* fin del ejemplo */
|
|
</verb></tscreen>
|
|
|
|
<!-- ***************************************************************** -->
|
|
<sect> El <em>widget</em> lista
|
|
<!-- ***************************************************************** -->
|
|
<p>
|
|
ATENCIÓN: El <em>widget</em> GtkList ha sido reemplazado por el
|
|
<em>widget</em> GtkCList.
|
|
|
|
El <em>widget</em> GtkList está diseñado para actuar como un
|
|
contenedor vertical de <em>widgets</em> que deben ser del tipo
|
|
GtkListItem.
|
|
|
|
Un <em>widget</em> GtkList tiene su propia ventana para recibir
|
|
eventos y su propio color de fondo, que normalmente es blanco.
|
|
Como es un objeto derivado directamente de GtkContainer puede tratarse
|
|
utilizando la macro GTK_CONTAINER(List), ver el <em>widget</em>
|
|
GtkContainer para obtener más información.
|
|
|
|
Debería familiarizarse con la utilización de un GList y con sus
|
|
funciones relacionadas <tt/g_list_*()/ para ser capaz de explotar el
|
|
<em>widget</em> GtkList hasta su límite.
|
|
|
|
Sólo hay un campo dentro de la definición de la estructura del
|
|
<em>widget</em> GtkList que nos es de interés, y es:
|
|
|
|
<tscreen><verb>
|
|
struct _GtkList
|
|
{
|
|
...
|
|
GList *selection;
|
|
guint selection_mode;
|
|
...
|
|
};
|
|
</verb></tscreen>
|
|
|
|
El campo <tt/selection/ de un GtkList apunta a una lista enlazada de
|
|
todos los elementos que están actualmente seleccionados, o NULL si la
|
|
selección está vacia. Por lo tanto para saber quien es la actual
|
|
selección debemos leer el campo <tt/GTK_LIST()->selection/, pero no
|
|
modificarlo ya que los campos de los que está constituido GtkList
|
|
están controlados por las funciones gtk_list_*().
|
|
|
|
El <tt/selection_mode/ de la GtkList determina las posibilidades de
|
|
selección de una GtkList y por tanto los contenidos del campo
|
|
<tt/GTK_LIST()->selection/. El <tt/selection_mode/ puede tener uno de
|
|
los valores siguientes:
|
|
|
|
<itemize>
|
|
<item> GTK_SELECTION_SINGLE - La selección es o NULL o contiene un
|
|
puntero a un GList con un solo elemento seleccionado.
|
|
|
|
<item> GTK_SELECTION_BROWSE - La selección es NULL si la lista no
|
|
contiene <em>widgets</em> o si los que contiene no son sensibles, en
|
|
cualquier otro caso contiene un puntero GList a una estructura GList,
|
|
y contendrá por tanto un solo elemento.
|
|
|
|
<item> GTK_SELECTION_MULTIPLE - La selección es NULL si no hay
|
|
elementos seleccionados o un puntero GList hacia el primer elemento
|
|
seleccionado. ("That in turn") apunta a una estructura GList para
|
|
el segundo elemento seleccionado y así.
|
|
|
|
<item> GTK_SELECTION_EXTENDED - La selección siempre es NULL.
|
|
</itemize>
|
|
|
|
El valor por defecto es GTK_SELECTION_MULTIPLE.
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1> Señales
|
|
<p>
|
|
<tscreen><verb>
|
|
void selection_changed( GtkList *list );
|
|
</verb></tscreen>
|
|
|
|
Se invocará esta señal cuando cambie el campo <tt/selection/ de un
|
|
GtkList. Es decir, cuando un hijo de una GtkList se selecciona o
|
|
deselecciona.
|
|
|
|
<tscreen><verb>
|
|
void select_child( GtkList *list,
|
|
GtkWidget *hijo);
|
|
</verb></tscreen>
|
|
|
|
Se invoca esta señal cuando un hijo de la GtkList está siendo
|
|
seleccionado. Esto ocurre principalmente en llamadas a
|
|
<tt/gtk_list_select_item()/, a <tt/gtk_list_select_child()/, cuando se
|
|
pulsa algún botón y a veces se lanza indirectamente cuando se añade o
|
|
se elimina un hijo del GtkList.
|
|
|
|
<tscreen><verb>
|
|
void unselect_child( GtkList *list,
|
|
GtkWidget *hijo );
|
|
</verb></tscreen>
|
|
|
|
Se invoca esta señal cuando un hijo del GtkList está siendo
|
|
deseleccionado. Esto ocurre principalmente cuando ocurre una llamada a
|
|
<tt/gtk_list_unselect_item()/, <tt/gtk_list_unselect_item()/,
|
|
pulsaciones de botón y a veces se lanza indirectamente cuando se añade
|
|
o se elimina algún hijo de la GtkList.
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1> Funciones
|
|
<p>
|
|
<tscreen><verb>
|
|
guint gtk_list_get_type( void );
|
|
</verb></tscreen>
|
|
|
|
Devuelve el identificador de tipo `GtkList'.
|
|
|
|
<tscreen><verb>
|
|
GtkWidget *gtk_list_new( void );
|
|
</verb></tscreen>
|
|
|
|
Crea un nuevo objeto GtkList. Se devuelve el nuevo <em/widget/ como un
|
|
puntero a un objeto GtkWidget. Se devuelve NULL en caso de producirse
|
|
algún fallo.
|
|
|
|
<tscreen><verb>
|
|
void gtk_list_insert_items( GtkList *list,
|
|
GList *items,
|
|
gint posicion );
|
|
</verb></tscreen>
|
|
|
|
Introduce elementos en la lista, comenzando en la posición
|
|
<tt/posicion/. <tt/items/ es una lista doblemente enlazada donde cada
|
|
puntero de datos de cada nodo se supone que apunta a una nueva
|
|
GtkListItem (recien creada). Los nodos GList de <tt/items/ son
|
|
controlados por la lista.
|
|
|
|
<tscreen><verb>
|
|
void gtk_list_append_items( GtkList *list,
|
|
GList *items);
|
|
</verb></tscreen>
|
|
|
|
Introduce elementos tal y como lo hace <tt/gtk_list_insert_items()/,
|
|
pero los mete en el final de la lista. Los nodos GList de <tt/items/
|
|
son controlados por la lista.
|
|
|
|
<tscreen><verb>
|
|
void gtk_list_prepend_items( GtkList *list,
|
|
GList *items);
|
|
</verb></tscreen>
|
|
|
|
Introduce elementos tal y como lo hace <tt/gtk_list_insert_items()/,
|
|
pero los mete al principio de la lista. Los nodos GList de <tt/items/
|
|
son controlados por la lista.
|
|
|
|
<tscreen><verb>
|
|
void gtk_list_remove_items( GtkList *list,
|
|
GList *items);
|
|
</verb></tscreen>
|
|
|
|
Elimina elementos de la lista. <tt/items/ es una lista doblemente
|
|
enlazada donde cada puntero de datos de cada nodo se supone que apunta
|
|
a un hijo directo de la lista. El ejecutar o no
|
|
<tt/g_list_free(items)/ cuando la función termine de ejecutarse es
|
|
responsabilidad del que llama a la misma. Está bajo su responsabilidad
|
|
la destrucción de los elementos de la lista.
|
|
|
|
<tscreen><verb>
|
|
void gtk_list_clear_items( GtkList *list,
|
|
gint start,
|
|
gint end );
|
|
</verb></tscreen>
|
|
|
|
Elimina y destruye los elementos de la lista. Esta operación afectará
|
|
a todos los <em/widgets/ que se encuentren en la lista y en el rango
|
|
especificado por <tt/start/ y <tt/end/.
|
|
|
|
<tscreen><verb>
|
|
void gtk_list_select_item( GtkList *list,
|
|
gint item );
|
|
</verb></tscreen>
|
|
|
|
Invoca la señal <tt/select_child/ para el elemento especificado
|
|
mediante su posición actual en la lista.
|
|
|
|
<tscreen><verb>
|
|
void gtk_list_unselect_item( GtkList *list,
|
|
gint item);
|
|
</verb></tscreen>
|
|
|
|
Invoca la señal <tt/unselect_child/ para un elemento especificado
|
|
mediante su posición actual en la lista.
|
|
|
|
<tscreen><verb>
|
|
void gtk_list_select_child( GtkList *list,
|
|
GtkWidget *hijo);
|
|
</verb></tscreen>
|
|
|
|
Invoca la señal <tt/select_child/ para el hijo especificado.
|
|
|
|
<tscreen><verb>
|
|
void gtk_list_unselect_child( GtkList *list,
|
|
GtkWidget *hijo);
|
|
</verb></tscreen>
|
|
|
|
Invoca la señal <tt/unselect_child/ para el hijo especificado.
|
|
|
|
<tscreen><verb>
|
|
gint gtk_list_child_position( GtkList *list,
|
|
GtkWidget *hijo);
|
|
</verb></tscreen>
|
|
|
|
Devuelve la posición de <tt/hijo/ en la lista. Se devuelve «-1» en
|
|
caso de producirse algún error.
|
|
|
|
<tscreen><verb>
|
|
void gtk_list_set_selection_mode( GtkList *list,
|
|
GtkSelectionMode mode );
|
|
</verb></tscreen>
|
|
|
|
Pone el modo de selección, que puede ser <tt/GTK_SELECTION_SINGLE/,
|
|
<tt/GTK_SELECTION_BROWSE/, <tt/GTK_SELECTION_MULTIPLE/ o
|
|
<tt/GTK_SELECTION_EXTENDED/.
|
|
|
|
<tscreen><verb>
|
|
GtkList *GTK_LIST( gpointer obj );
|
|
</verb></tscreen>
|
|
|
|
Convierte un puntero general en `GtkList *'. Para más información *Note
|
|
Standard Macros::.
|
|
|
|
<tscreen><verb>
|
|
GtkListClass *GTK_LIST_CLASS( gpointer class);
|
|
</verb></tscreen>
|
|
|
|
Convierte un puntero general en `GtkListClass *'. Para más información
|
|
*Note Standard Macros::.
|
|
|
|
<tscreen><verb>
|
|
gint GTK_IS_LIST( gpointer obj);
|
|
</verb></tscreen>
|
|
|
|
Determina si un puntero general se refiere a un objeto `GtkList'. Para
|
|
más información, *Note Standard Macros::.
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1> Ejemplo
|
|
<p>
|
|
A continuación tenemos un programa ejemplo que muestra los cambios de
|
|
la selección de un GtkList, y le deja «arrestar» elementos de la
|
|
lista en una prisión, seleccionándolos con el botón derecho del ratón.
|
|
|
|
<tscreen><verb>
|
|
/* principio del ejemplo list list.c */
|
|
|
|
/* incluye los ficheros de cabecera de gtk+
|
|
* incluye stdio.h, que necesitamos para la función printf()
|
|
*/
|
|
#include <gtk/gtk.h>
|
|
#include <stdio.h>
|
|
|
|
/* ésta es nuestra cadena de identificación para almacenar datos en la
|
|
* lista de elementos
|
|
*/
|
|
const gchar *list_item_data_key="list_item_data";
|
|
|
|
|
|
/* prototipos para los manejadores de señal que vamos a conectar con
|
|
* el widget GtkList
|
|
*/
|
|
static void sigh_print_selection (GtkWidget *gtklist,
|
|
gpointer func_data);
|
|
static void sigh_button_event (GtkWidget *gtklist,
|
|
GdkEventButton *event,
|
|
GtkWidget *frame);
|
|
|
|
|
|
/* función principal donde se establece el interface con el usuario */
|
|
|
|
gint main (int argc, gchar *argv[])
|
|
{
|
|
GtkWidget *separator;
|
|
GtkWidget *ventana;
|
|
GtkWidget *vbox;
|
|
GtkWidget *scrolled_window;
|
|
GtkWidget *frame;
|
|
GtkWidget *gtklist;
|
|
GtkWidget *boton;
|
|
GtkWidget *list_item;
|
|
GList *dlist;
|
|
guint i;
|
|
gchar buffer[64];
|
|
|
|
|
|
/* inicializar gtk+ (y consecuentemente gdk) */
|
|
|
|
gtk_init(&argc, &argv);
|
|
|
|
|
|
/* crear una ventana donde meter todos los widgets y conectar
|
|
* gtk_main_quit() con el evento "destroy" de la ventana para
|
|
* poder controlar los eventos de cerrado de ventana del
|
|
* administrador de ventanas
|
|
*/
|
|
ventana=gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
|
gtk_window_set_title(GTK_WINDOW(ventana), "GtkList Example");
|
|
gtk_signal_connect(GTK_OBJECT(ventana),
|
|
"destroy",
|
|
GTK_SIGNAL_FUNC(gtk_main_quit),
|
|
NULL);
|
|
|
|
|
|
/* dentro de la ventana necesitamos una caja para alinear los
|
|
* widgets verticalmente */
|
|
vbox=gtk_vbox_new(FALSE, 5);
|
|
gtk_container_border_width(GTK_CONTAINER(vbox), 5);
|
|
gtk_container_add(GTK_CONTAINER(ventana), vbox);
|
|
gtk_widget_show(vbox);
|
|
|
|
/* Ésta es la ventana con barras de desplazamiento donde meteremos
|
|
* el widget GtkList */
|
|
scrolled_window=gtk_scrolled_window_new(NULL, NULL);
|
|
gtk_widget_set_usize(scrolled_window, 250, 150);
|
|
gtk_container_add(GTK_CONTAINER(vbox), scrolled_window);
|
|
gtk_widget_show(scrolled_window);
|
|
|
|
/* crear el widget GtkList
|
|
* conectar la función manipuladora de señal
|
|
* sigh_print_selection() a la señal "selection_changed" del
|
|
* GtkList para imprimir los elementos seleccionados cada vez que
|
|
* cambie la selección */
|
|
gtklist=gtk_list_new();
|
|
gtk_container_add(GTK_CONTAINER(scrolled_window), gtklist);
|
|
gtk_widget_show(gtklist);
|
|
gtk_signal_connect(GTK_OBJECT(gtklist),
|
|
"selection_changed",
|
|
GTK_SIGNAL_FUNC(sigh_print_selection),
|
|
NULL);
|
|
|
|
/* creamos una "Prisión" donde meteremos una lista de elementos ;)
|
|
*/
|
|
frame=gtk_frame_new("Prison");
|
|
gtk_widget_set_usize(frame, 200, 50);
|
|
gtk_container_border_width(GTK_CONTAINER(frame), 5);
|
|
gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);
|
|
gtk_container_add(GTK_CONTAINER(vbox), frame);
|
|
gtk_widget_show(frame);
|
|
|
|
/* conectamos el manipulador de señal sigh_button_event() al
|
|
* GtkList que manejará la lista de elementos "arrestados"
|
|
*/
|
|
gtk_signal_connect(GTK_OBJECT(gtklist),
|
|
"button_release_event",
|
|
GTK_SIGNAL_FUNC(sigh_button_event),
|
|
frame);
|
|
|
|
/* crear un separador
|
|
*/
|
|
separator=gtk_hseparator_new();
|
|
gtk_container_add(GTK_CONTAINER(vbox), separator);
|
|
gtk_widget_show(separator);
|
|
|
|
/* crear finalmente un botón y conectar su señal "clicked" con la
|
|
* destrucción de la ventana
|
|
*/
|
|
boton=gtk_button_new_with_label("Close");
|
|
gtk_container_add(GTK_CONTAINER(vbox), boton);
|
|
gtk_widget_show(boton);
|
|
gtk_signal_connect_object(GTK_OBJECT(boton),
|
|
"clicked",
|
|
GTK_SIGNAL_FUNC(gtk_widget_destroy),
|
|
GTK_OBJECT(ventana));
|
|
|
|
|
|
/* ahora creamos 5 elementos de lista, teniendo cada uno su propia
|
|
* etiqueta y añadiéndolos a la GtkList mediante
|
|
* gtk_container_add() también consultaremos la cadena de texto de
|
|
* la etiqueta y la asociaremos con la list_item_data_key para
|
|
* cada elemento de la lista
|
|
*/
|
|
for (i=0; i<5; i++) {
|
|
GtkWidget *etiqueta;
|
|
gchar *string;
|
|
|
|
sprintf(buffer, "ListItemContainer with Label #%d", i);
|
|
etiqueta=gtk_label_new(buffer);
|
|
list_item=gtk_list_item_new();
|
|
gtk_container_add(GTK_CONTAINER(list_item), etiqueta);
|
|
gtk_widget_show(etiqueta);
|
|
gtk_container_add(GTK_CONTAINER(gtklist), list_item);
|
|
gtk_widget_show(list_item);
|
|
gtk_label_get(GTK_LABEL(etiqueta), &string);
|
|
gtk_object_set_data(GTK_OBJECT(list_item),
|
|
list_item_data_key,
|
|
string);
|
|
}
|
|
/* aquí, estamos creando otras 5 etiquetas, esta vez utilizaremos
|
|
* gtk_list_item_new_with_label() para la creación
|
|
* no podemos consultar la cadena de texto de la etiqueta ya que
|
|
* no tenemos el puntero de etiquetas y por tanto lo único que
|
|
* haremos será asociar el list_item_data_key de cada elemento de
|
|
* la lista con la misma cadena de texto. Para añadirlo a la lista
|
|
* de elementos los pondremos en lista doblemente enlazada
|
|
* (GList), y entonces los añadimos mediante una simple llamada a
|
|
* gtk_list_append_items()
|
|
* como utilizamos g_list_prepend() para poner los elementos en la
|
|
* lista doblemente enlazada, su orden será descendente (en vez de
|
|
* ascendente como cuando utilizamos g_list_append())
|
|
*/
|
|
dlist=NULL;
|
|
for (; i<10; i++) {
|
|
sprintf(buffer, "List Item with Label %d", i);
|
|
list_item=gtk_list_item_new_with_label(buffer);
|
|
dlist=g_list_prepend(dlist, list_item);
|
|
gtk_widget_show(list_item);
|
|
gtk_object_set_data(GTK_OBJECT(list_item),
|
|
list_item_data_key,
|
|
"ListItem with integrated Label");
|
|
}
|
|
gtk_list_append_items(GTK_LIST(gtklist), dlist);
|
|
|
|
/* finalmente queremos ver la ventana, ¿verdad? ;)
|
|
*/
|
|
gtk_widget_show(ventana);
|
|
|
|
/* y nos metemos en el bucle de eventos de gtk
|
|
*/
|
|
gtk_main();
|
|
|
|
/* llegaremos aquí después de que se llame a gtk_main_quit(), lo
|
|
* que ocurre si se destruye la ventana
|
|
*/
|
|
return 0;
|
|
}
|
|
|
|
/* éste es el manejador de señal que se conectó a los eventos de
|
|
* pulsar/soltar de los botones de la GtkList
|
|
*/
|
|
void
|
|
sigh_button_event (GtkWidget *gtklist,
|
|
GdkEventButton *event,
|
|
GtkWidget *frame)
|
|
{
|
|
/* sólo hacemos algo si el tercer botón (el botón derecho) se
|
|
* levanta
|
|
*/
|
|
if (event->type==GDK_BUTTON_RELEASE &&
|
|
event->button==3) {
|
|
GList *dlist, *free_list;
|
|
GtkWidget *new_prisoner;
|
|
|
|
/* sacar la lista de elementos que están actualmente
|
|
* seleccionados y que serán nuestro próximos prisioneros ;)
|
|
*/
|
|
dlist=GTK_LIST(gtklist)->selection;
|
|
if (dlist)
|
|
new_prisoner=GTK_WIDGET(dlist->data);
|
|
else
|
|
new_prisoner=NULL;
|
|
|
|
/* buscar por elementos de la lista ya encarcelados, los
|
|
* volveremos a poner en la lista, recordar que hay que
|
|
* eliminar la lista doblemente enlazada que devuelve
|
|
* gtk_container_children()
|
|
*/
|
|
dlist=gtk_container_children(GTK_CONTAINER(frame));
|
|
free_list=dlist;
|
|
while (dlist) {
|
|
GtkWidget *list_item;
|
|
|
|
list_item=dlist->data;
|
|
|
|
gtk_widget_reparent(list_item, gtklist);
|
|
|
|
dlist=dlist->next;
|
|
}
|
|
g_list_free(free_list);
|
|
|
|
/* si tenemos un nuevo prisionero, lo eliminamos de la GtkList
|
|
* y lo ponemos en el marco "Prisión". Primero tenemos que
|
|
* deseleccionarlo
|
|
*/
|
|
if (new_prisoner) {
|
|
GList static_dlist;
|
|
|
|
static_dlist.data=new_prisoner;
|
|
static_dlist.next=NULL;
|
|
static_dlist.prev=NULL;
|
|
|
|
gtk_list_unselect_child(GTK_LIST(gtklist),
|
|
new_prisoner);
|
|
gtk_widget_reparent(new_prisoner, frame);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* éste es el manipulador de señal que se llama si GtkList emite la
|
|
* señal "selection_changed"
|
|
*/
|
|
void
|
|
sigh_print_selection (GtkWidget *gtklist,
|
|
gpointer func_data)
|
|
{
|
|
GList *dlist;
|
|
|
|
/* sacar la lista doblemente enlazada de los elementos
|
|
* seleccionados en GtkList, ¡recuerde que hay que tratarla como
|
|
* de solo lectura!
|
|
*/
|
|
dlist=GTK_LIST(gtklist)->selection;
|
|
|
|
/* si no hay elementos seleccionados no queda nada por hacer
|
|
* excepto informar al usuario
|
|
*/
|
|
if (!dlist) {
|
|
g_print("Selection cleared\n");
|
|
return;
|
|
}
|
|
/* Bien, conseguimos una selección y la imprimimos
|
|
*/
|
|
g_print("The selection is a ");
|
|
|
|
/* obtenemos la lista de elementos de la lista doblemente enlazada
|
|
* y entonces consultamos los datos asociados con la
|
|
* list_item_data_key que acabamos de imprimir
|
|
*/
|
|
while (dlist) {
|
|
GtkObject *list_item;
|
|
gchar *item_data_string;
|
|
|
|
list_item=GTK_OBJECT(dlist->data);
|
|
item_data_string=gtk_object_get_data(list_item,
|
|
list_item_data_key);
|
|
g_print("%s ", item_data_string);
|
|
|
|
dlist=dlist->next;
|
|
}
|
|
g_print("\n");
|
|
}
|
|
/* fin del ejemplo */
|
|
</verb></tscreen>
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1> El <em/widget/ GtkListItem
|
|
<p>
|
|
El <em/widget/ GtkListItem está diseñado para comportarse como un
|
|
contenedor que tiene un hijo, proporcionando funciones para la
|
|
selección/deselección justo como las necesitan los hijos del
|
|
<em/widget/ GtkList.
|
|
|
|
Un GtkListItem tiene su propia ventana para recibir eventos y tiene su
|
|
propio color de fondo, que normalmente es blanco.
|
|
|
|
Como está derivado directamente de un GtkItem, puede tratarse como tal
|
|
utilizando la macro GTK_ITEM(ListItem), ver el <em/widget/ GtkItem
|
|
para más detalles. Normalmente un GtkListItem sólo tiene una etiqueta
|
|
para identificar, por ejemplo, el nombre de un fichero dentro de una
|
|
GtkList -- por lo tanto se proporciona la función
|
|
<tt/gtk_list_item_new_with_label()/. Se puede conseguir el mismo
|
|
efecto creando un GtkLabel, poniendo su alineación a <tt/xalign=0/ e
|
|
<tt/yalign=0.5/ y seguido de una adición al contenedor GtkListItem.
|
|
|
|
Nadie le obliga a meter un GtkLabel en un GtkListItem, puede meter un
|
|
GtkVBox o un GtkArrow, etc...
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1> Señales
|
|
<p>
|
|
Un GtkListItem no crea por sí misma nuevas señales, pero hereda las
|
|
señales de un GtkItem. Para más información *Note GtkItem::.
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1> Funciones
|
|
<p>
|
|
<tscreen><verb>
|
|
guint gtk_list_item_get_type( void );
|
|
</verb></tscreen>
|
|
|
|
Devuelve el identificador de tipo `GtkListItem'.
|
|
|
|
<tscreen><verb>
|
|
GtkWidget *gtk_list_item_new( void );
|
|
</verb></tscreen>
|
|
|
|
Crea un nuevo objeto GtkListItem. Se devuelve el nuevo <em/widget/
|
|
como un puntero a un objeto GtkWidget. Se devuelve NULL en caso de
|
|
producirse algún error.
|
|
|
|
<tscreen><verb>
|
|
GtkWidget *gtk_list_item_new_with_label( gchar *etiqueta );
|
|
</verb></tscreen>
|
|
|
|
Crea un nuevo objeto GtkListItem, con una sola GtkLabel como único
|
|
hijo. Se devuelve el nuevo <em/widget/ como un puntero a un objeto
|
|
GtkWidget. Se devuelve NULL en caso de producirse algún error.
|
|
|
|
<tscreen><verb>
|
|
void gtk_list_item_select( GtkListItem *list_item );
|
|
</verb></tscreen>
|
|
|
|
Esta función es, básicamente, un recubrimiento de una llamada a
|
|
<tt/gtk_item_select (GTK_ITEM (list_item))/, y emitirá la señal
|
|
<tt/select/. Para más información *Note GtkItem::.
|
|
|
|
<tscreen><verb>
|
|
void gtk_list_item_deselect( GtkListItem *list_item );
|
|
</verb></tscreen>
|
|
|
|
Esta función es, básicamente, un recubrimiento de una llamada a
|
|
<tt/gtk_item_deselect (GTK_ITEM (list_item))/, y emitirá la señal
|
|
<tt/deselect/. Para más información *Note GtkItem::.
|
|
|
|
<tscreen><verb>
|
|
GtkListItem *GTK_LIST_ITEM( gpointer obj );
|
|
</verb></tscreen>
|
|
|
|
Convierte un puntero general a `GtkListItem *'. Para más información
|
|
*Note Standard Macros::.
|
|
|
|
<tscreen><verb>
|
|
GtkListItemClass *GTK_LIST_ITEM_CLASS( gpointer class );
|
|
</verb></tscreen>
|
|
|
|
Convierte un puntero general a `GtkListItemClass *'. Para más
|
|
información *Note Standard Macros::.
|
|
|
|
<tscreen><verb>
|
|
gint GTK_IS_LIST_ITEM( gpointer obj );
|
|
</verb></tscreen>
|
|
|
|
Determina si un puntero general se refiere a un puntero
|
|
`GtkListItem'. Para más información *Note Standard Macros::.
|
|
|
|
<!-- ----------------------------------------------------------------- -->
|
|
<sect1> Ejemplo
|
|
<p>
|
|
Para ver un ejemplo de todo esto, mire el de GtkList, que también
|
|
cubre la utilización un GtkListItem.
|
|
|
|
</article>
|