mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-11-06 00:30:08 +00:00
8341 lines
284 KiB
Plaintext
8341 lines
284 KiB
Plaintext
|
|
||
|
<!doctype linuxdoc system>
|
||
|
<article>
|
||
|
<title>GTK Tutorial
|
||
|
<author>Ian Main, <tt><htmlurl url="mailto:slow@intergate.bc.ca"
|
||
|
name="slow@intergate.bc.ca"></tt>
|
||
|
|
||
|
<date>December 1, 1997 - Traduzione Aggiornata al 19 Gennaio 1998
|
||
|
|
||
|
<abstract>Tradotto da Michel Morelli, <tt><htmlurl url="mailto:ziobudda@chiara.dei.unipd.it" name="ziobudda@chiara.dei.unipd.it"></tt>, Daniele Canazza, <tt><htmlurl url="mailto:dcanazz@tin.it" name="dcanazz@tin.it"></tt> e Antonio Schifano, <tt><htmlurl url="mailto:schifano@cli.di.unipi.it" name="schifano@cli.di.unipi.it"></tt>
|
||
|
</abstract>
|
||
|
|
||
|
<sect>Introduzione
|
||
|
<p>
|
||
|
GTK (GIMP Toolkit) era orginariamente sviluppato come toolkit per il programma
|
||
|
GIMP (General Image Manipulation Program). GTK è costruito sulla base del
|
||
|
kit di disegno di GIMP, il GDK (GIMP Drawing Kit) il quale è costruito a sua
|
||
|
volta attorno alle funzioni della Xlib. E' chiamato ``toolkit di GIMP'' perché
|
||
|
era inizialmente scritto per sviluppare GIMP, ma ora viene utilizzato nello
|
||
|
sviluppo di molti progetti software liberi. Gli autori sono
|
||
|
<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>
|
||
|
|
||
|
<p>
|
||
|
GTK è essenzialmente una API (application programmers interface)
|
||
|
orientata agli oggetti.
|
||
|
Anche se scritto completamente in C, è implementato usando l'idea delle
|
||
|
classi e delle funzioni di callback (puntatori a funzioni).
|
||
|
|
||
|
<p>
|
||
|
C'è anche una terza componente chiamata glib che contiene una serie di
|
||
|
implementazioni differenti di alcune chiamate di funzioni standard e anche
|
||
|
alcune funzioni aggiuntive, per esempio per la manipolazione delle liste
|
||
|
collegate, eccetera. Le funzioni sostitutive sono usate per migliorare la
|
||
|
portabilità di GTK. Alcune delle funzioni implementate qui non sono
|
||
|
disponibili o non sono standard, altre sono uniche come g_strerror().
|
||
|
Altre contengono miglioramenti alle stesse della libc come g_malloc che ha
|
||
|
delle utility di debugging migliorate.
|
||
|
|
||
|
<p>
|
||
|
Questo tutorial è un tentativo di documentare il meglio possibile la libreria gtk
|
||
|
e non pretende di essere completo. Questo tutorial suppone una buona conoscenza del
|
||
|
linugaggio C e di come creare programmi in C. Saranno facilitati i lettori che hanno una
|
||
|
precedente esperienza nella programmazione in X. Se il GTK è il primo insieme di widget
|
||
|
che studiate, siete pregati di dirmi come avete trovato questo tutorial e che tipo di problemi
|
||
|
avete avuto.
|
||
|
Notate che c'è anche una versione per il C++ della libreria GTK (chiamata GTK--), quindi
|
||
|
se preferite utilizzare questo linguaggio al posto del C potreste cercare questa versione
|
||
|
e non la GTK normale.
|
||
|
Ci sono poi un ``wrapper'' Objective C e un collegamento a Guile, ma non ne seguo
|
||
|
l'evoluzione.
|
||
|
|
||
|
<p>
|
||
|
Mi farebbe molto piacere conoscere qualsiasi problema che abbiate avuto nell'imparare il GTK
|
||
|
da questo documento e apprezzerei anche critiche sul come migliorarlo.
|
||
|
|
||
|
<sect>Iniziamo
|
||
|
<p>
|
||
|
La prima cosa da fare è certamente quella di scaricare il GTK e installarlo. Potete prendere
|
||
|
l'ultima versione dal sito ftp.gimp.org nella directory /pub/gimp. Un'altra possibile sorgente
|
||
|
di informazioni è il sito http://www.gimp.org/gtk. GTK usa il comando GNU autoconf per
|
||
|
autoconfigurarsi.
|
||
|
Una volta estratti i file dall'archivio tar, eseguite configure --help per vedere una lista delle
|
||
|
opzioni del comando configure.
|
||
|
|
||
|
<p>
|
||
|
Per iniziare la nostra introduzione a GTK, cominceremo con il più semplice programma
|
||
|
possibile . Questo programma crea una finestra con dimensioni (in pixel) di 200x200 e
|
||
|
l'unica possibilità di uscita è di ucciderlo ucciso usando la shell o il Window Manager.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
#include <gtk/gtk.h>
|
||
|
|
||
|
int main (int argc, char *argv[])
|
||
|
{
|
||
|
GtkWidget *window;
|
||
|
|
||
|
gtk_init (&argc, &argv);
|
||
|
|
||
|
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
||
|
gtk_widget_show (window);
|
||
|
|
||
|
gtk_main ();
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Tutti i programmi certamente includeranno <gtk/gtk.h> che dichiara le variabili, le funzioni,
|
||
|
le strutture, etc. che saranno usate nella tua applicazione GTK.
|
||
|
|
||
|
<p>
|
||
|
La linea seguente:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
gtk_init (&argc, &argv);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
invoca la funzione gtk_init(gint *argc, gchar ***argv) che sarà usata in tutte le
|
||
|
applicazioni GTK. Questa funzione sistema alcune cose al posto nostro, come la visuale
|
||
|
predefinita e la mappa dei colori, e procede poi chiamando gdk_init(gint *argc, gchar ***argv).
|
||
|
Questa funzione inizializza la libreria per l'uso, setta il gestore predefinito dei segnali
|
||
|
e guarda negli argomenti, passati via linea di comando alla tua applicazione, alla ricerca
|
||
|
di uno di questi argomenti:
|
||
|
<itemize>
|
||
|
<item> <tt/--display/
|
||
|
<item> <tt/--debug-level/
|
||
|
<item> <tt/--no-xshm/
|
||
|
<item> <tt/--sync/
|
||
|
<item> <tt/--show-events/
|
||
|
<item> <tt/--no-show-events/
|
||
|
</itemize>
|
||
|
<p>
|
||
|
Rimuove questi argomenti dalla lista degli argomenti passati, lasciando quelli non
|
||
|
riconosciuti a disposizione della tua applicazione che potrà tenerne conto o ignorarli.
|
||
|
In questo modo si crea un set di argomenti standard accettato da tutte le applicazione GTK.
|
||
|
|
||
|
<p>
|
||
|
Le seguenti 2 linee di codice creano e mostrano la finestra.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
||
|
gtk_widget_show (window);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
L'argomento GTK_WINDOW_TOPLEVEL specifica che noi vogliamo che la nostra finestra si
|
||
|
sottometta alle decorazioni del windows manager e alla posizione che quest'ultimo indicherà.
|
||
|
Invece di creare una finestra avente dimensioni 0x0, la dimensione di una finestra senza
|
||
|
figli (altri widget, come i bottoni, etc) è predefinita a 200x200 così che si possa manipolarla.
|
||
|
La funzione gtk_widget_show() fa sì che GTK sappia che abbiamo finito di settare gli
|
||
|
attributi di questo widget e che quindi quest'ultimo può essere visualizzato.
|
||
|
|
||
|
<p>
|
||
|
L'ultima linea ci fa entrare nel ciclo principale del GTK.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
gtk_main ();
|
||
|
</verb></tscreen>
|
||
|
|
||
|
gtk_main() è un'altra chiamata che tu vedrete in tutte le applicazioni GTK. Quando il controllo
|
||
|
raggiunge questo punto, l'applicazione si metterà a dormire aspettando che si verifichino eventi
|
||
|
di X (come la pressione di un bottone o di un tasto), timeout o notifiche di Input/Output dei file
|
||
|
Nel nostro esempio, comunque, tutti gli eventi sono ignorati.
|
||
|
|
||
|
<sect1>Hello World in GTK
|
||
|
<p>
|
||
|
Ok, ora un programma con un widget (un bottone). E' il classico ``Hello World'' alla GTK.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
|
||
|
#include <gtk/gtk.h>
|
||
|
|
||
|
|
||
|
/* E' una funzione di ritorno (callback). Gli argomenti passati sono ignorati in questo
|
||
|
* esempio.
|
||
|
* Piu' informazioni sulle callback in seguito. */
|
||
|
|
||
|
void hello (GtkWidget *widget, gpointer data)
|
||
|
{
|
||
|
g_print ("Hello World\n");
|
||
|
}
|
||
|
|
||
|
gint delete_event(GtkWidget *widget, gpointer data)
|
||
|
{
|
||
|
g_print ("delete event occured\n");
|
||
|
/* Se si dà TRUE al manipolatore del segnale ``delete_event'', GTK emettera' il segnale
|
||
|
``destroy''. Fornire FALSE significa non volere che la finestra sia distrutta.
|
||
|
Cambia FALSE con TRUE e la finestra principale sara' distrutta con un "delete_event"
|
||
|
*/
|
||
|
|
||
|
/* Un'altra callback */
|
||
|
void destroy (GtkWidget *widget, gpointer data)
|
||
|
{
|
||
|
gtk_main_quit ();
|
||
|
}
|
||
|
|
||
|
int main (int argc, char *argv[])
|
||
|
{
|
||
|
/* GtkWidget e' il tipo di dato per i Widget */
|
||
|
GtkWidget *window;
|
||
|
GtkWidget *button;
|
||
|
|
||
|
/* Questa e' una chiamata presente in tutte le applicazioni GTK. Gli argomenti della
|
||
|
linea di comando vengono scorsi e restituiti alla applicazione */
|
||
|
gtk_init (&argc, &argv);
|
||
|
|
||
|
/* Crea una nuova finestra */
|
||
|
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
||
|
|
||
|
/* Quando alla finestra viene passato il segnale ``delete_event'' (questo
|
||
|
* segnale viene passato Windows Manager di solito con l'opzione 'close'
|
||
|
* o con la barra del titolo (title bar)) noi chiediamo che la funzione
|
||
|
* delete_event() (definita sopra) venga invocata.
|
||
|
* Il dato passato come argomento alla funzione di ritorno é NULL
|
||
|
* ed é ignorato dalla funzione stessa. */
|
||
|
gtk_signal_connect (GTK_OBJECT (window), "delete_event",
|
||
|
GTK_SIGNAL_FUNC (destroy), NULL);
|
||
|
|
||
|
/* Qui connettiamo l'evento ``destroy'' al gestore del segnale.
|
||
|
* Questo evento accade quando noi chiamimo la funzione gtk_widget_destroy()
|
||
|
* sulla finestra o se ritorniamo TRUE dalla callback ``delete_event''. */
|
||
|
gtk_signal_connect (GTK_OBJECT (window), "destroy",
|
||
|
GTK_SIGNAL_FUNC (destroy), NULL);
|
||
|
|
||
|
/* Setta il bordo interno della finestra */
|
||
|
gtk_container_border_width (GTK_CONTAINER (window), 10);
|
||
|
|
||
|
/* Crea un nuovo bottone avente etichetta (label) uguale a ``Hello World'' */
|
||
|
button = gtk_button_new_with_label ("Hello World");
|
||
|
|
||
|
/* Quando il bottone riceve il segnale ``clicked'', invochera' la funzione
|
||
|
* hello() passando NULL come argomento della funzione. La funzione
|
||
|
* hello() é definita sopra. */
|
||
|
gtk_signal_connect (GTK_OBJECT (button), "clicked",
|
||
|
GTK_SIGNAL_FUNC (hello), NULL);
|
||
|
|
||
|
/* Questo farà sì che la finestra venga distrutta dalla chiamata
|
||
|
* gtk_widget_destroy(window) quando il bottone verrà premuto. Ancora,
|
||
|
* questo segnale (``destroy'') puo' arrivare da qui o dal windows
|
||
|
* manager */
|
||
|
gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
|
||
|
GTK_SIGNAL_FUNC (gtk_widget_destroy),
|
||
|
GTK_OBJECT (window));
|
||
|
|
||
|
/* Questo inserisce il bottone nella finestra
|
||
|
* (un contenitore GTK) */
|
||
|
gtk_container_add (GTK_CONTAINER (window), button);
|
||
|
|
||
|
/* Il passo finale é il mostrare questo nuovo widget appena creato */
|
||
|
gtk_widget_show (button);
|
||
|
|
||
|
/* e la finestra */
|
||
|
gtk_widget_show (window);
|
||
|
|
||
|
/* Tutte le applicazioni GTK devono avere la funzione gtk_main().
|
||
|
* Il controllo finisce qui e attende un evento (come la pressione
|
||
|
* di un tasto o l'evento di un mouse).
|
||
|
gtk_main ();
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
</verb></tscreen>
|
||
|
|
||
|
<sect1>Compilare hello World
|
||
|
<p>
|
||
|
Per compilare si utilizza :
|
||
|
|
||
|
<tscreen><verb>
|
||
|
gcc -Wall -g helloworld.c -o hello_world -L/usr/X11R6/lib \
|
||
|
-lglib -lgdk -lgtk -lX11 -lXext -lm
|
||
|
</verb></tscreen>
|
||
|
<p>
|
||
|
Le librerie sopra (glib, gtk,...) devono essere tutte nel percorso predefinito
|
||
|
delle librerie. Se cosi' non fosse aggiungi ``-L<directory>'' e il gcc
|
||
|
guarderà in questa directory per cercare le librerie di cui necessita.
|
||
|
Per esempio sul mio sistema debian-linux io ho dovuto aggiungere
|
||
|
<tt>-L/usr/X11R6/lib</> per riuscire a far trovare le librerie di X11.
|
||
|
|
||
|
<p>
|
||
|
L'odine della dichiarazione delle librerie é significativo. Il linker
|
||
|
sa quali funzioni di una libreria ha bisogno prima di processarla.
|
||
|
|
||
|
<p>
|
||
|
le librerie che noi linkiamo sono:
|
||
|
<itemize>
|
||
|
<item> la libreria glib (-lglib), contiene varie funzioni, ma solo
|
||
|
g_print() é usato in questo esempio. GTK si appoggia a questa
|
||
|
libreria cosi' devi sempre, comunque, linkarla. Vedi comunque la <ref
|
||
|
id="sec_glib" name="glib"> sezione sulla glib per altri dettagli.
|
||
|
<item>La libreria GDK (-lgdk), la copertura della X11.
|
||
|
<item>La libreria GTK (-lgtk), la libreria dei widget, basata sulla GDK.
|
||
|
<item>La libreria xlib(-lX11) la quale è usata dalla GDK.
|
||
|
<item>La libreria Xext(-lXext). Questa contiene il codice per le pixmap a
|
||
|
memoria condivisa e altre estensioni di X.
|
||
|
<item>La libreria matematica (-lm). Questa é usata dalla GTK per vari scopi.
|
||
|
</itemize>
|
||
|
|
||
|
<sect1>Teoria dei segnali e delle funzioni di ritorno (callback)
|
||
|
<p>
|
||
|
Prima di guardare in dettaglio ``Hello World'', discuteremo gli eventi e le
|
||
|
funzioni di ritorno. GTK è un toolkit guidato dagli eventi, il che significa
|
||
|
che se ne starà a dorimire in gtk_main finché non succederà un evento ed il
|
||
|
controllo passerà alla funzione appropriata.
|
||
|
|
||
|
<p>
|
||
|
Questo passaggio di controllo è fatto usando l'idea dei segnali. Quando succede un
|
||
|
evento, come la pressione di un bottone del mouse, verrà emesso il segnale appropriato
|
||
|
dal widget che é stato premuto.
|
||
|
Questo è il modo in cui GTK fa molto del suo utile lavoro. Per fare sì che un
|
||
|
bottone esegua una azione, noi prepareremo un gestore del segnale che catturi
|
||
|
questi segnali e chiami la funzione corretta. Questo è fatto usando una
|
||
|
funzione del tipo:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
gint gtk_signal_connect (GtkObject *object,
|
||
|
gchar *name,
|
||
|
GtkSignalFunc func,
|
||
|
gpointer func_data);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
<p>
|
||
|
Dove, il primo argomento è il widget che emetterà il segnale, il secondo è il nome
|
||
|
del segnale che si vuole catturare,il terzo è la funzione che verrà invocata
|
||
|
quando il segnale sarà catturato e il quarto è il dato che potr essere passato a
|
||
|
questa funzione.
|
||
|
|
||
|
<p>
|
||
|
La funzione specificata come terzo argomento è chiamata ``funzione di ritorno (callback)'',
|
||
|
e dovrebbe essere della forma:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void callback_func(GtkWidget *widget, gpointer *callback_data);
|
||
|
</verb></tscreen>
|
||
|
<p>
|
||
|
Dove il primo argomento sarà un puntatore al widget che emette il segnale e il
|
||
|
secondo un puntatore al dato passato come ultimo argomento della funzione
|
||
|
gtk_signal_connect() come descritto sopra.
|
||
|
|
||
|
<p>
|
||
|
Un'altra chiamata usata nell'esempio Hello World è:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
gint gtk_signal_connect_object (GtkObject *object,
|
||
|
gchar *name,
|
||
|
GtkSignalFunc func,
|
||
|
GtkObject *slot_object);
|
||
|
</verb></tscreen>
|
||
|
<p>
|
||
|
gtk_signal_connect_object() è uguale a gtk_signal_connect() eccetto che la
|
||
|
funzione di callback usa solo un argomento, un puntatore ad un'oggetto GTK.
|
||
|
Cosi' quando usa questa funzione per connettere i segnali, la callback
|
||
|
potrebbe essere della forma :
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void callback_func (GtkObject *object);
|
||
|
</verb></tscreen>
|
||
|
<p>
|
||
|
Dove object è di solito un widget. Noi, generalmente, non assegnamo una callback per
|
||
|
gtk_signal_connect_object. Queste sono invocate ,usualmente, per chiamare
|
||
|
una funzione GTK che accetta un widget singolo o un oggetto come argomento,
|
||
|
come nel caso dell'esempio Hello World.
|
||
|
|
||
|
Lo scopo di avere due funzioni per connettere i segnali è semplicemente quello di
|
||
|
permettere alla funzione di callback di avere un numero di argomenti diverso.
|
||
|
Molte funzioni della libreria GTK accettano solo un singolo puntatore ad un widget
|
||
|
GTK come argomento, così per queste si può usare la funzione gtk_signal_connect_object(),
|
||
|
mentre per le vostre funzioni potreste aver bisogno di passare dati supplementari alle
|
||
|
funzioni di ritorno.
|
||
|
|
||
|
<sect1>Attraverso Hello World passo per passo
|
||
|
<p>
|
||
|
Ora che conosciamo la teoria che vi è dietro, iniziamo ad essere più chiari
|
||
|
camminando attraverso il programma di Hello World.
|
||
|
|
||
|
<p>
|
||
|
Questa è la funzione di callback che sarà invocata quando il bottone è clickato.
|
||
|
Noi, in questo esempio, ignoriamo sia il widget che i dati passati, ma non è
|
||
|
difficile farci invece qualcosa. Il prossimo esempio userà l'argomento passato
|
||
|
per dire quale bottone è stato premuto.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void hello (GtkWidget *widget, gpointer *data)
|
||
|
{
|
||
|
g_print ("Hello World\n");
|
||
|
}
|
||
|
</verb></tscreen>
|
||
|
|
||
|
<p>
|
||
|
Questa callback è un po' speciale. L'evento ``delete'' avviene quanto il Window Manager
|
||
|
manda questo evento all'applicazione. Qui abbiamo una scelta da fare: cosa fare di questo evento.
|
||
|
Possiamo ignorarlo, creare qualche tipo di risposta, o semplicemente terminare
|
||
|
l'applicazione.
|
||
|
|
||
|
Il valore che si restituisce in questa callback fa sì che la GTK sappia cosa fare.
|
||
|
Restituire FALSE significa che noi non vogliamo che il segnale ``destroy'' sia emesso,
|
||
|
quindi far sì che la nostra applicazione continui a procedere. Ritornare TRUE vuole dire
|
||
|
far emettere il segnale ``destroy'' il quale chiamerà il gestore del segnale ``destroy''
|
||
|
(o meglio : la nostra funzione di callback).
|
||
|
|
||
|
<tscreen><verb>
|
||
|
gint delete_event(GtkWidget *widget, gpointer data)
|
||
|
{
|
||
|
g_print ("delete event occured\n");
|
||
|
|
||
|
return (FALSE);
|
||
|
}
|
||
|
</verb></tscreen>
|
||
|
|
||
|
<p>
|
||
|
Questa è un'altra funzione di callback la quale fa uscire dal programma chiamando
|
||
|
gtk_main_quit(). Non c'è molto da dire al riguardo, è abbastanza auto-esplicativa.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void destroy (GtkWidget *widget, gpointer *data)
|
||
|
{
|
||
|
gtk_main_quit ();
|
||
|
}
|
||
|
</verb></tscreen>
|
||
|
<p>
|
||
|
Ritengo che conosciate la funzione main()... si, come tutte le altre applicazioni
|
||
|
anche le applicazioni GTK hanno questa funzione.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
int main (int argc, char *argv[])
|
||
|
{
|
||
|
</verb></tscreen>
|
||
|
|
||
|
<p>
|
||
|
Questa parte dichiara un puntatore ad una struttura di tipo GtkWidget. Queste sono
|
||
|
usate sotto per creare una finestra ed un bottone.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
GtkWidget *window;
|
||
|
GtkWidget *button;
|
||
|
</verb></tscreen>
|
||
|
<p>
|
||
|
Qui vi è ancora la nostra gtk_init. Come prima questa inizializza il toolkit e
|
||
|
analizza gli argomenti trovati nella linea di comando_ Tutti gli argomenti riconosciuti
|
||
|
nella linea di comando sono rimossi dalla lista degli argomenti e vengono così modificati
|
||
|
argc e argv per far sì che sembri che questi non siano mai esisitie permettere alla
|
||
|
tua applicazione di analizzare gli argomenti rimasti.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
gtk_init (&argc, &argv);
|
||
|
</verb></tscreen>
|
||
|
<p>
|
||
|
Crea una nuova finestra. Questo viene spiegato abbastanza approfonditamente più avanti.
|
||
|
Viene allocata la memoria per la struttura GtkWidget *window così che si punti ad una struttura
|
||
|
valida. In questo modo si predispone la nuova finestra, ma non la si visualizza fino a sotto dove, quasi
|
||
|
alla fine del nostro programma, invochiamo gtk_widget_show(window).
|
||
|
<tscreen><verb>
|
||
|
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
||
|
</verb></tscreen>
|
||
|
<p>
|
||
|
Questo è un esempio di come connettere un gestore dei segnali con un oggetto, in questo
|
||
|
caso la finestra. Qui viene catturato il segnale ``destroy''. Questo è emesso quando usiamo
|
||
|
il Window Manager per uccidere la finestra (e noi restituiamo TRUE dal gestore di ``delete_event'')
|
||
|
o quando emettiamo la chiamata gtk_widget_destroy() passando l'oggetto finestra
|
||
|
come oggetto da distruggere. Sistemando le cose così, trattiamo entrambi i casi con una singola
|
||
|
chiamata. Qui è giusto invocare la funzione destroy() definita sopra con NULL come argomento,
|
||
|
la quale termina l'applicazione GTK per noi.
|
||
|
Questo ci permetterà di utilizzare il Window Manager per uccidere il programma.
|
||
|
<!-- fino a qui -->
|
||
|
<p>
|
||
|
GTK_OBJECT e GTK_SIGNAL_FUNC sono macro che interpretano il casting e il controllo di tipo per noi,
|
||
|
così da rendere piu' leggibile il codice.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
gtk_signal_connect (GTK_OBJECT (window), "destroy",
|
||
|
GTK_SIGNAL_FUNC (destroy), NULL);
|
||
|
</verb></tscreen>
|
||
|
<p>
|
||
|
La prossima funzione è usata per settare un attributo di un oggetto contenitore. Questo
|
||
|
sistema la finestra così da avere un'area vuota all'interno della finestrra larga 10 pixel dove
|
||
|
non potrà andare nessun widget. Ci sono altre funzioni simili che vedremo nella
|
||
|
sezione <ref id="sec_setting_widget_attributes" name="Settare gli attributi del Widget.">
|
||
|
|
||
|
<p>
|
||
|
E ancora, GTK_CONTAINER è una macro per interpretare il casting di tipo.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
gtk_container_border_width (GTK_CONTAINER (window), 10);
|
||
|
</verb></tscreen>
|
||
|
<p>
|
||
|
Questa chiamata crea un nuovo bottone. Alloca spazio in memoria per un nuovo GtkWidget,
|
||
|
inizializzandolo e facendo sì che il puntatore a bottone punti ad esso.
|
||
|
Quando sarà visualizzato, avrà etichetta ``Hello World''.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
button = gtk_button_new_with_label ("Hello World");
|
||
|
</verb></tscreen>
|
||
|
<p>
|
||
|
Qui prendiamo il bottone e gli facciamo fare qualcosa di utile.
|
||
|
Gli colleghiamo un un gestore di segnale in modo che quando emetterà il
|
||
|
segnale ``clicked'', verrà invocata la nostra funzione hello(). Il dato passato
|
||
|
alla funzione è ignorato, cosicché alla funzione di callback hello() passiamo
|
||
|
semplicemente NULL. Evidentemente il segnale ``clicked'' viene emesso quando
|
||
|
premiamo il bottone con il mouse.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
gtk_signal_connect (GTK_OBJECT (button), "clicked",
|
||
|
GTK_SIGNAL_FUNC (hello), NULL);
|
||
|
</verb></tscreen>
|
||
|
<p>
|
||
|
Usiamo questo bottone anche per uscire dal programma. Questo illustrera'
|
||
|
come il segnale ``destroy'' può arrivare sia dal Window Manager che dal nostro programma.
|
||
|
Quando il bottone è ``clicked'', come sopra, chiamera' la funzione di callback
|
||
|
hello() e poi questa nell'ordine in cui sono definite. Si possono avere
|
||
|
tante funzioni di callback, quante sono necessarie, e saranno eseguite nell'ordine in cui
|
||
|
sono connesse. Visto che la funzione gtk_widget_destroy() accetta come argomento solo un
|
||
|
GtkWidget *widget, usiamo la funzione gtk_signal_connect_object()
|
||
|
al posto della semplice gtk_signal_connect().
|
||
|
|
||
|
<tscreen><verb>
|
||
|
gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
|
||
|
GTK_SIGNAL_FUNC (gtk_widget_destroy),
|
||
|
GTK_OBJECT (window));
|
||
|
</verb></tscreen>
|
||
|
<p>
|
||
|
Questa é una chiamata di ``impacchettamento'' che sarà spiegata più avanti.
|
||
|
Ma è molto facile da capire. Semplicemente dice alla libreria GTK che il
|
||
|
bottone è da mettere nella finestra dove sarà visualizzato.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
gtk_container_add (GTK_CONTAINER (window), button);
|
||
|
</verb></tscreen>
|
||
|
<p>
|
||
|
A questo punto abbiamo predisposto tutto quello che ci eravamo prefissati.
|
||
|
Con tutti i gestori di segnale a posto e il bottone messo nella finestra in cui
|
||
|
dovrebbe essere, possiamo dire a GTK di mostrare gli oggetti sullo schermo.
|
||
|
L'oggetto finestra viene mostrato per ultimo così che la finestra completa di tutti
|
||
|
i suoi oggetti sarà mostrata in una volta sola, invece di vedere
|
||
|
prima la finestra spoglia e poi la comparsa del bottone all'interno di essa.
|
||
|
Per quanto, con questi semplici esempi, questo l'avrai già notato.
|
||
|
<tscreen><verb>
|
||
|
gtk_widget_show (button);
|
||
|
|
||
|
gtk_widget_show (window);
|
||
|
</verb></tscreen>
|
||
|
<p>
|
||
|
E naturalmente chiamiamo gtk_main(), la quale aspetta l'arrivo degli eventi
|
||
|
dal server X e chiamerà l'oggetto interessato per fargli emettere il segnale
|
||
|
adeguato.
|
||
|
<tscreen><verb>
|
||
|
gtk_main ();
|
||
|
</verb></tscreen>
|
||
|
E il return finale. Il controllo ritorna qui dopo che viene invocata gtk_quit().
|
||
|
|
||
|
<tscreen><verb>
|
||
|
return 0;
|
||
|
</verb></tscreen>
|
||
|
<p>
|
||
|
Ora, quando premiamo il bottone del mouse su un bottone GTK, questo oggetto
|
||
|
emette il segnale ``clicked''. Per poter utilizzare queste informazioni, il nostro
|
||
|
programma predispone un gestore di segnale per catturare quel segnale, il quale
|
||
|
avvia la funzione da noi scelta. Nel nostro esempio, quando il bottone creato viene
|
||
|
clickato , la funzione hello() è invocata con un argomento NULL, dopoodiché
|
||
|
viene invocato il successivo gestore di questo segnale. Questo chiama la funziona
|
||
|
gtk_widget_destroy(), passandole l'oggetto-finestra (window) come argomento, che
|
||
|
distruggerà la finestra. Questo fa sì che la finestra emetta il segnale
|
||
|
``destroy'' che viene catturato e che fa invocare la funzione di ritorno
|
||
|
destroy(), che semplicemente esce dal programma GTK.
|
||
|
|
||
|
<p>
|
||
|
Un'altro modo in cui possono andare le cose è l'uso del window manager per uccidere
|
||
|
la finestra. Questo causera' l'emissione del segnale ``delete_event'' che
|
||
|
automaticamente chiamerà il gestore del segnale ``delete_event''. Se qui noi
|
||
|
restituiamo il valore FALSE, la finestra non verrà toccata e tutto procederà come
|
||
|
se nulla fosse successo. Dare invece il valore TRUE causerà l'emissione da parte
|
||
|
di GTK del segnale ``destroy'' il quale, a sua volta, invocherà la callback ``destroy'',
|
||
|
uscendo dall'applicazione.
|
||
|
|
||
|
<p>
|
||
|
Nota che questi segnali non sono gli stessi del sistema Unix e che non sono
|
||
|
implementati usando quei segnali, anche se la terminologia è praticamente identica.
|
||
|
|
||
|
<sect>Proseguiamo
|
||
|
<p>
|
||
|
<sect1>Tipi di Dato
|
||
|
<p>
|
||
|
Ci sono alcune cose che avrete probabilmente notato nei precedenti esempi che
|
||
|
hanno bisogno di una spiegazione. I gint, gchar ecc. che vedete sono tipi di dato
|
||
|
riferiti rispettivamente a int e char. Questo viene fatto per rimediare alla brutta
|
||
|
dipendenza dalle dimensioni di semplici tipi di dato quando si fanno dei calcoli.
|
||
|
Un buon esempio è ``gint32'' il quale sarà un tipo di dato riferito ad un intero a
|
||
|
32 bit per tutte le piattaforme x86 e ad un 64 bit per gli alpha.
|
||
|
I tipi di dato sono ben spiegati più avanti ed intuitivi. Sono definiti in
|
||
|
glib/glib.h (il quale viene incluso da gtk.h).
|
||
|
|
||
|
<p>
|
||
|
Noterete anche la possibilità di utilizzare un GtkWidget quando la funzione richiede
|
||
|
un GtkObject. GTK è una libreria orienta agli oggetti ed un widget è un oggetto.
|
||
|
|
||
|
<sect1>Altri Dettagli sui Segnali
|
||
|
<p>
|
||
|
Diamo un'altra occhiata alla dichiarazione della funzione gtk_signal_connect.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
gint gtk_signal_connect (GtkObject *object, gchar *name,
|
||
|
GtkSignalFunc func, gpointer func_data);
|
||
|
</verb></tscreen>
|
||
|
Notate il valore di ritorno definito come gint? questo è un identificatore per
|
||
|
la tua funzione di callback. Come detto sopra, si possono avere più funzioni di
|
||
|
ritorno per ogni segnale e per ogni ogetto a seconda delle necessità. ed ognuna sarà
|
||
|
eseguita in sequenza, nell'ordine in cui sono state collegate. Questo identificatore
|
||
|
ti permette di rimuovere una funzione dalla lista delle funzioni di ritorno tramite
|
||
|
la seguente chiamata
|
||
|
<tscreen><verb>
|
||
|
void gtk_signal_disconnect (GtkObject *object,
|
||
|
gint id);
|
||
|
</verb></tscreen>
|
||
|
Così passando il widget da cui vuoi rimuovere il gestore di segnale, e
|
||
|
l'identificativo restituito da una delle funzioni signal_connect, puoi rimuovere
|
||
|
il gestore di segnale che desideri da quella del widget.
|
||
|
|
||
|
<p>
|
||
|
Un'altra funzione per rimuovere tutti i segnali di un widget in una volta sola è:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
gtk_signal_handlers_destroy (GtkObject *object);
|
||
|
</verb></tscreen>
|
||
|
<p>
|
||
|
Questa chiamata è abbastanza auto esplicativa. Semplicemente rimuove tutti i segnali
|
||
|
collegati al widget che passi alla funzione come argomento.
|
||
|
|
||
|
<sect1>Miglioriamo Hello World
|
||
|
|
||
|
<p>
|
||
|
Diamo un'occhiata ad una migliorata versione di Hello World con altri esempi sulle
|
||
|
callback. Questo anche ci introdurrà al nostro prossimo argomento,
|
||
|
l'impacchettamento dei widget.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
#include <gtk/gtk.h>
|
||
|
|
||
|
/* La nostra funzione di callback migliorata. I dati passati a questa
|
||
|
* vengono stampati su stdout. */
|
||
|
void callback (GtkWidget *widget, gpointer *data)
|
||
|
{
|
||
|
g_print ("Hello again - %s was pressed\n", (char *) data);
|
||
|
}
|
||
|
|
||
|
/* Un'altra callback */
|
||
|
void delete_event (GtkWidget *widget, gpointer *data)
|
||
|
{
|
||
|
gtk_main_quit ();
|
||
|
}
|
||
|
|
||
|
int main (int argc, char *argv[])
|
||
|
{
|
||
|
/* GtkWidget e' il tipo di dato per i widget */
|
||
|
GtkWidget *window;
|
||
|
GtkWidget *button;
|
||
|
GtkWidget *box1;
|
||
|
|
||
|
/* Questa funzione e' invocata in tutte le applicazioni GTK, gli
|
||
|
argomenti sono analizzati e restituiti all'applicazione. */
|
||
|
gtk_init (&argc, &argv);
|
||
|
|
||
|
/* Crea una nuova finestra */
|
||
|
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
||
|
|
||
|
/* Questa e' una nuova chiamata. Assegna "Hello Buttons" come titolo
|
||
|
della nostra finestra */
|
||
|
gtk_window_set_title (GTK_WINDOW (window), "Hello Buttons!");
|
||
|
|
||
|
/* Qui settiamo il gestore per il segnale "delete_event" che
|
||
|
immediatamente esce dalla applicazione.
|
||
|
gtk_signal_connect (GTK_OBJECT (window), "delete_event",
|
||
|
GTK_SIGNAL_FUNC (delete_event), NULL);
|
||
|
|
||
|
|
||
|
/* predispone il bordo della finestra */
|
||
|
gtk_container_border_width (GTK_CONTAINER (window), 10);
|
||
|
|
||
|
/* creiamo una scatola dove mettere tutti i widget. Questa è descritta
|
||
|
dettagliatamente nella sezione "packing". La scatola non è realmente
|
||
|
visibile, è solamente usata per sistemare i widget. */
|
||
|
box1 = gtk_hbox_new(FALSE, 0);
|
||
|
|
||
|
/* Inseriamo la scatola nella finestra */
|
||
|
gtk_container_add (GTK_CONTAINER (window), box1);
|
||
|
|
||
|
/* Creiamo un nuovo bottone con etichetta "Button 1" */
|
||
|
button = gtk_button_new_with_label ("Button 1");
|
||
|
|
||
|
/* Quando il bottone e' premuto, noi invocheremo la funzione di callback,
|
||
|
con un puntatore alla stringa "button 1" come proprio argomento) */
|
||
|
gtk_signal_connect (GTK_OBJECT (button), "clicked",
|
||
|
GTK_SIGNAL_FUNC (callback), (gpointer) "button 1");
|
||
|
|
||
|
/* invece di aggiungerlo alla finestra, lo inseriamo nella scatola invisibile,
|
||
|
la quale e' stata inserita nella finstra. */
|
||
|
gtk_box_pack_start(GTK_BOX(box1), button, TRUE, TRUE, 0);
|
||
|
|
||
|
/* Ricordati sempre questo passo. Dice a GTK che la preparazione di questo
|
||
|
bottone e' finita e che quindi puo' essere mostrato. */
|
||
|
gtk_widget_show(button);
|
||
|
|
||
|
/* Facciamo la stessa cosa per il secondo bottone. */
|
||
|
button = gtk_button_new_with_label ("Button 2");
|
||
|
|
||
|
/* Chiamiamo la stessa funzione ma passandogli un argomento differente,
|
||
|
gli passiamo un puntatore alla stringa "button 2" */
|
||
|
gtk_signal_connect (GTK_OBJECT (button), "clicked",
|
||
|
GTK_SIGNAL_FUNC (callback), (gpointer) "button 2");
|
||
|
|
||
|
gtk_box_pack_start(GTK_BOX(box1), button, TRUE, TRUE, 0);
|
||
|
|
||
|
/* L'ordine nel quale i bottoni sono visualizzati non e' realmente importante,
|
||
|
ma io ti raccomando di mostrare per ultima la finestra cosi' che tutto
|
||
|
sia visualizzato in una volta sola */
|
||
|
gtk_widget_show(button);
|
||
|
|
||
|
gtk_widget_show(box1);
|
||
|
|
||
|
gtk_widget_show (window);
|
||
|
|
||
|
/* e ora ci mettiamo in gtk_main e aspettiamo che il diverimento inizi.
|
||
|
gtk_main ();
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
</verb></tscreen>
|
||
|
<p>
|
||
|
Compilate questo programma usando gli stessi argomenti di link del nostro primo
|
||
|
esempio. Noterete che questa volta non c'è un modo semplice per uscire dal programma,
|
||
|
si deve usare il nostro window manager o la linea di comando per uccidere
|
||
|
l'applicazione.
|
||
|
Un buon esercizio per il lettore è quello di inserire un tezo bottone ``quit'' che
|
||
|
faccia uscire dal programma. Potete anche divertirvi con le opzioni di
|
||
|
gtk_box_pack_start() mentre leggete il prossimo capitolo. Provate a ridimensionare
|
||
|
la finestra ed a osservare cosa succede.
|
||
|
|
||
|
<p>
|
||
|
Solo una piccola nota, c'è un'altra definizione di gtk_window_new() -
|
||
|
GTK_WINDOW_DIALOG. Questa interagisce con il window manager in un modo un po'
|
||
|
diverso, e dovrebbe essere usata per finestre temporanee.
|
||
|
|
||
|
<sect>Come ``Impacchettare'' i Widget
|
||
|
<p>
|
||
|
Nel momento in cui si crea un'applicazione, normalmente si avrà la necessità di mettere più
|
||
|
di un unico bottone all'interno di una finestra. Il nostro primo esempio ``Hello World''
|
||
|
usava un solo oggetto, cosicché abbiamo potuto usare semplicemente una chiamata
|
||
|
a gtk_container_add per impacchettare il widget nella finestra. Quando invece si vuole
|
||
|
inserire più di un unico widget in una finestra, come si fa a controllare dove vengono
|
||
|
posizionati i propri oggetti? E' qui che entra in gioco il meccanismo dell'``impacchettamento''.
|
||
|
<sect1>Teoria delle Scatole per Impacchettamento
|
||
|
<p>
|
||
|
La maggior parte dell'impacchettamento viene effettuata creando delle scatole
|
||
|
come nell'esempio più sopra. Le scatole sono dei contenitori invisibili di
|
||
|
widget che possiamo usare per imballarci i nostri oggetti e che esistono in
|
||
|
due varietà: in particolare si possono avere scatole orizzontali (hbox) e
|
||
|
verticali (vbox).
|
||
|
Quando si impacchentano degli oggetti in una scatola orizzontale, gli oggetti vengono inseriti
|
||
|
orizzontalmente da sinistra a destra oppure da destra a sinistra a seconda della
|
||
|
chiamata di funzione che si usa. In una scatola verticale, gli oggetti vengono inseriti
|
||
|
dall'alto in basso o viceversa. Si può usare qualsiasi combinazione di scatole
|
||
|
all'interno o a fianco di altre scatole, fino ad ottenere l'effetto desiderato.
|
||
|
<p>
|
||
|
Per creare una nuova scatola orizzontale, si usa una chiamata a gtk_hbox_new(), mentre
|
||
|
per le scatole verticali si usa gtk_vbox_new(). Per inserire i widget
|
||
|
all'interno di questi contenitori si usano le funzioni gtk_box_pack_start() e
|
||
|
gtk_box_pack_end(). La funzione gtk_box_pack_start() comincerà dall'alto verso il
|
||
|
basso in una vbox e da sinistra a destra in una hbox. gtk_box_pack_end() fa l'opposto,
|
||
|
impacchettando dal basso verso l'alto in una vbox e da destra a sinistra in una hbox.
|
||
|
Queste funzioni ci permettono di giustificare a destra o a sinistra i nostri
|
||
|
widget, e possono essere mescolate in qualsiasi modo per ottenere l'effetto desiderato.
|
||
|
Useremo gtk_box_pack_start() nella maggior parte dei nostri esempi. Un oggetto può
|
||
|
essere costituito da un altro contenitore o da un oggetto grafico. Infatti, molti
|
||
|
oggetti grafici sono a loro volta dei contenitori, compreso il bottone, anche se
|
||
|
tipicamente all'interno del bottone mettiamo solo una etichetta.
|
||
|
<p>
|
||
|
|
||
|
Usando queste chiamate, GTK riesce a capire dove si vogliono piazzare i propri
|
||
|
widget, in modo di essere poi in grado di effettuare il ridimensionamento
|
||
|
automatico e altre cose interessanti. Esiste poi un insieme di opzioni che riguardano
|
||
|
il modo in cui i propri oggetti grafici dovrebbero essere impacchettati. Come
|
||
|
si può immaginare, questo metodo dà una buona flessibilità nella creazione e
|
||
|
nella disposizione dei propri widget.
|
||
|
<sect1>Dettagli sulle Scatole
|
||
|
<p>
|
||
|
A causa di questa flessibilità, le scatole per impacchettamento del GTK
|
||
|
possono, di primo acchito, creare un po' di disorientamento. Sono infatti disponibili
|
||
|
molte opzioni, e non è immediato il modo in cui si combinano l'una con l'altra.
|
||
|
Alla fine però, si possono ottenere essenzialmente cinque diversi stili.
|
||
|
|
||
|
<p>
|
||
|
<?
|
||
|
<IMG ALIGN="center" SRC="packbox1.gif"
|
||
|
VSPACE="15" HSPACE="10" ALT="Box Packing Example Image" WIDTH="528"
|
||
|
HEIGHT="235">
|
||
|
>
|
||
|
|
||
|
|
||
|
Ogni linea contiene una scatola orizzontale (hbox) con diversi bottoni.
|
||
|
La chiamata a gtk_box_pack è una scorciatoia per la chiamata di impacchettamento
|
||
|
di ognuno dei bottoni nella hbox. Ognuno dei bottoni viene impacchettato nella
|
||
|
hbox nello stesso modo (cioè, con gli stessi argomenti per la funzione gtk_box_pack_start ()).
|
||
|
<p>
|
||
|
Questa è la dichiarazione della funzione gtk_box_pack_start.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void gtk_box_pack_start (GtkBox *box,
|
||
|
GtkWidget *child,
|
||
|
gint expand,
|
||
|
gint fill,
|
||
|
gint padding);
|
||
|
</verb></tscreen>
|
||
|
Il primo argomento è la scatola nella quale si stanno inscatolando i
|
||
|
widget, il secondo è il widget stesso. Gli oggetti per ora saranno
|
||
|
bottoni, quindi quello che faremo sarà impacchettare bottoni in scatole.
|
||
|
<p>
|
||
|
L'argomento ``expand'' in gtk_box_pack_start() o gtk_box_pack_end() controlla
|
||
|
se gli oggetti devono essere sistemati nella scatola in modo da riempire tutto
|
||
|
lo spazio in diponibile presente nella scatola, in modo che la scatola si espanda fino
|
||
|
ad occupare tutta l'area assegnatale (valore TRUE).
|
||
|
La scatola può anche essere rimpiciolita in modo da contenere esattamente i
|
||
|
widget (valore FALSE). Assegnare a expand il valore FALSE permette di giustificare
|
||
|
a destra o sinistra i propri oggetti. In caso contrario, tutti gli ogetti si espandono
|
||
|
fino ad adattarsi alla scatola, e il medesimo effetto si può ottenere usando solo una
|
||
|
delle funzioni gtk_box_pack_start o pack_end.
|
||
|
<p>
|
||
|
L'argomento ``fill'' delle funzioni gtk_box_pack stabilisce se lo spazio disponibile
|
||
|
nella scatola deve essere allocato agli oggetti (TRUE) o se deve essere mantenuto
|
||
|
come riempimento attorno a questi oggetti (FALSE). Questo argomento ha effetto
|
||
|
solo se a expand è assegnato il valore TRUE.
|
||
|
<p>
|
||
|
Quando si crea una nuova scatola, la funzione ha questo aspetto:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
GtkWidget * gtk_hbox_new (gint homogeneous,
|
||
|
gint spacing);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
L'argomento homogeneous di gtk_hbox_new (la stesso per gtk_vbox_new)
|
||
|
determina se ogni oggetto nella scatola deve avere la stessa dimensione (cioè
|
||
|
la stessa ampiezza in una hbox o la stessa altezza in una vbox). Se è settato,
|
||
|
l'argomento expand delle routine gtk_box_pack è sempre attivato.
|
||
|
<p>
|
||
|
Qual è la differenza fra la spaziatura (che è stabilita quando la scatola
|
||
|
viene creata) e il riempimento (che viene stabilito quando gli elementi vengono
|
||
|
impacchettati)? La spaziatura viene inserita fra gli oggetti, mentre il
|
||
|
riempimento viene aggiuno a ciascuno dei lati dell'oggetti. La seguente figura
|
||
|
dovrebbe chiarire meglio questo punto:
|
||
|
|
||
|
<?
|
||
|
<IMG ALIGN="center" SRC="packbox2.gif"
|
||
|
VSPACE="15" HSPACE="10" ALT="Box Packing Example Image" WIDTH="509"
|
||
|
HEIGHT="213">
|
||
|
>
|
||
|
|
||
|
|
||
|
Di seguito è riportato il codice usato per creare le immagini precedenti.
|
||
|
L'ho commentato in modo piuttosto pesante, in modo che non dovreste avere
|
||
|
problemi nel seguirlo. Compilatelo voi stessi e preovate a giocarci un po'.
|
||
|
|
||
|
<sect1>Programma Dimostrativo di Impacchettamento
|
||
|
<p>
|
||
|
|
||
|
<tscreen><verb>
|
||
|
#include "gtk/gtk.h"
|
||
|
|
||
|
void
|
||
|
delete_event (GtkWidget *widget, gpointer *data)
|
||
|
{
|
||
|
gtk_main_quit ();
|
||
|
}
|
||
|
|
||
|
/* Costruisco una nuova hbox riempita con bottoni-etichette. Gli
|
||
|
* argomenti per le varabili che ci interessano sono passati
|
||
|
* in questa funzione. Non mostriamo la scatola, ma mostriamo
|
||
|
* tutto quello che c'è dentro. */
|
||
|
GtkWidget *make_box (gint homogeneous, gint spacing,
|
||
|
gint expand, gint fill, gint padding)
|
||
|
{
|
||
|
GtkWidget *box;
|
||
|
GtkWidget *button;
|
||
|
char padstr[80];
|
||
|
|
||
|
/* costruisco una nuova hbox con i valori appropriati di
|
||
|
* homogeneous e spacing */
|
||
|
box = gtk_hbox_new (homogeneous, spacing);
|
||
|
|
||
|
/* costruisco una serie di bottoni con i valori appropriati */
|
||
|
button = gtk_button_new_with_label ("gtk_box_pack");
|
||
|
gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
|
||
|
gtk_widget_show (button);
|
||
|
|
||
|
button = gtk_button_new_with_label ("(box,");
|
||
|
gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
|
||
|
gtk_widget_show (button);
|
||
|
|
||
|
button = gtk_button_new_with_label ("button,");
|
||
|
gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
|
||
|
gtk_widget_show (button);
|
||
|
|
||
|
/* costruisco un bottone con l'etichetta che dipende dal valore di
|
||
|
* expand. */
|
||
|
if (expand == TRUE)
|
||
|
button = gtk_button_new_with_label ("TRUE,");
|
||
|
else
|
||
|
button = gtk_button_new_with_label ("FALSE,");
|
||
|
|
||
|
gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
|
||
|
gtk_widget_show (button);
|
||
|
|
||
|
/* Questo è la stessa cosa della creazione del bottone per "expand"
|
||
|
* più sopra, ma usa la forma breve. */
|
||
|
button = gtk_button_new_with_label (fill ? "TRUE," : "FALSE,");
|
||
|
gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
|
||
|
gtk_widget_show (button);
|
||
|
|
||
|
sprintf (padstr, "%d);", padding);
|
||
|
|
||
|
button = gtk_button_new_with_label (padstr);
|
||
|
gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
|
||
|
gtk_widget_show (button);
|
||
|
|
||
|
return box;
|
||
|
}
|
||
|
|
||
|
int
|
||
|
main (int argc, char *argv[])
|
||
|
{
|
||
|
GtkWidget *window;
|
||
|
GtkWidget *button;
|
||
|
GtkWidget *box1;
|
||
|
GtkWidget *box2;
|
||
|
GtkWidget *separator;
|
||
|
GtkWidget *label;
|
||
|
GtkWidget *quitbox;
|
||
|
int which;
|
||
|
|
||
|
/* La nostra inizializzazione, non dimenticatela! :) */
|
||
|
gtk_init (&argc, &argv);
|
||
|
|
||
|
if (argc != 2) {
|
||
|
fprintf (stderr, "uso: packbox num, dove num è 1, 2, o 3.\n");
|
||
|
/* questo fa solo un po' di pulizia in GTK, ed esce con un valore 1. */
|
||
|
gtk_exit (1);
|
||
|
}
|
||
|
|
||
|
which = atoi (argv[1]);
|
||
|
|
||
|
/* Creiamo la nostra finestra */
|
||
|
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
||
|
|
||
|
/* Ci si dovrebbe sempre ricordare di connettere il segnale di destroy
|
||
|
* alla finestra principale. Ciò è molto importante per avere un funzionamento
|
||
|
* corretto dal punto di vista intuitivo */
|
||
|
gtk_signal_connect (GTK_OBJECT (window), "delete_event",
|
||
|
GTK_SIGNAL_FUNC (delete_event), NULL);
|
||
|
gtk_container_border_width (GTK_CONTAINER (window), 10);
|
||
|
|
||
|
/* Creiamo una scatola verticale (vbox) in cui impacchettare quelle
|
||
|
* orizzontali. Questo ci permette di impilare le scatole orizzontali
|
||
|
* piene di bottoni una sull'altra in questa vbox. */
|
||
|
|
||
|
box1 = gtk_vbox_new (FALSE, 0);
|
||
|
|
||
|
/* Decide quale esempio si deve mostrare. Corrispondono alle figure precedenti */
|
||
|
switch (which) {
|
||
|
case 1:
|
||
|
/* creare una nuova etichetta. */
|
||
|
label = gtk_label_new ("gtk_hbox_new (FALSE, 0);");
|
||
|
|
||
|
/* allineare l'etichetta al lato sinistro. Discuteremo questa e altre
|
||
|
* funzioni nella sezione dedicata agli attributi degli oggetti grafici. */
|
||
|
gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
|
||
|
|
||
|
/* Impacchettare l'etichetta nella scatola verticale (vbox box1).
|
||
|
* Ricordare che gli oggetti che vengono aggiunti in una vbox vengono
|
||
|
* impacchettati uno sopra all'altro in ordine. */
|
||
|
gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0);
|
||
|
|
||
|
/* mostrare l'etichetta */
|
||
|
gtk_widget_show (label);
|
||
|
|
||
|
/* chiamare la nostra funzione make_box - homogeneous = FALSE,
|
||
|
* spacing = 0, expand = FALSE, fill = FALSE, padding = 0 */
|
||
|
box2 = make_box (FALSE, 0, FALSE, FALSE, 0);
|
||
|
gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
|
||
|
gtk_widget_show (box2);
|
||
|
|
||
|
/* chiamare la nostra funzione make_box - homogeneous = FALSE, spacing = 0,
|
||
|
* expand = FALSE, fill = FALSE, padding = 0 */
|
||
|
box2 = make_box (FALSE, 0, TRUE, FALSE, 0);
|
||
|
gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
|
||
|
gtk_widget_show (box2);
|
||
|
|
||
|
/* Gli argomenti sono: homogeneous, spacing, expand, fill, padding */
|
||
|
box2 = make_box (FALSE, 0, TRUE, TRUE, 0);
|
||
|
gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
|
||
|
gtk_widget_show (box2);
|
||
|
|
||
|
/* Questo crea un separatore. Li conosceremo meglio in seguito,
|
||
|
* comunque sono piuttosto semplici. */
|
||
|
separator = gtk_hseparator_new ();
|
||
|
|
||
|
/* Impacchetta il separatore nella vbox. Ricordare che stiamo impacchettando
|
||
|
* ognuno di questi oggetti in una vbox, cosicché essi verranno
|
||
|
* impacchettati verticalmente. */
|
||
|
gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
|
||
|
gtk_widget_show (separator);
|
||
|
|
||
|
/* crea un'altra nuova etichetta e mostrala. */
|
||
|
label = gtk_label_new ("gtk_hbox_new (TRUE, 0);");
|
||
|
gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
|
||
|
gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0);
|
||
|
gtk_widget_show (label);
|
||
|
|
||
|
/* Gli argomenti sono: homogeneous, spacing, expand, fill, padding */
|
||
|
box2 = make_box (TRUE, 0, TRUE, FALSE, 0);
|
||
|
gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
|
||
|
gtk_widget_show (box2);
|
||
|
|
||
|
/* Gli argomenti sono: homogeneous, spacing, expand, fill, padding */
|
||
|
box2 = make_box (TRUE, 0, TRUE, TRUE, 0);
|
||
|
gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
|
||
|
gtk_widget_show (box2);
|
||
|
|
||
|
/* ancora un nuovo separatore. */
|
||
|
separator = gtk_hseparator_new ();
|
||
|
/* Gli ultimi 3 argumenti per gtk_box_pack_start sono: expand, fill, padding. */
|
||
|
gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
|
||
|
gtk_widget_show (separator);
|
||
|
|
||
|
break;
|
||
|
|
||
|
case 2:
|
||
|
|
||
|
/* creare una nuova etichetta, ricordare che box1 è la vbox creata
|
||
|
* vicino all'inizio di main() */
|
||
|
label = gtk_label_new ("gtk_hbox_new (FALSE, 10);");
|
||
|
gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
|
||
|
gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0);
|
||
|
gtk_widget_show (label);
|
||
|
|
||
|
/* Gli argomenti sono: homogeneous, spacing, expand, fill, padding */
|
||
|
box2 = make_box (FALSE, 10, TRUE, FALSE, 0);
|
||
|
gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
|
||
|
gtk_widget_show (box2);
|
||
|
|
||
|
/* Gli argomenti sono: homogeneous, spacing, expand, fill, padding */
|
||
|
box2 = make_box (FALSE, 10, TRUE, TRUE, 0);
|
||
|
gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
|
||
|
gtk_widget_show (box2);
|
||
|
|
||
|
separator = gtk_hseparator_new ();
|
||
|
/* Gli ultimi tre arcomenti di gtk_box_pack_start sono: expand, fill, padding. */
|
||
|
gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
|
||
|
gtk_widget_show (separator);
|
||
|
|
||
|
label = gtk_label_new ("gtk_hbox_new (FALSE, 0);");
|
||
|
gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
|
||
|
gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0);
|
||
|
gtk_widget_show (label);
|
||
|
|
||
|
/* Gli argomenti sono: homogeneous, spacing, expand, fill, padding */
|
||
|
box2 = make_box (FALSE, 0, TRUE, FALSE, 10);
|
||
|
gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
|
||
|
gtk_widget_show (box2);
|
||
|
|
||
|
/* Gli argomenti sono: homogeneous, spacing, expand, fill, padding */
|
||
|
box2 = make_box (FALSE, 0, TRUE, TRUE, 10);
|
||
|
gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
|
||
|
gtk_widget_show (box2);
|
||
|
|
||
|
separator = gtk_hseparator_new ();
|
||
|
/* Gli ultimi tre argomenti di gtk_box_pack_start sono: expand, fill, padding. */
|
||
|
gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
|
||
|
gtk_widget_show (separator);
|
||
|
break;
|
||
|
|
||
|
case 3:
|
||
|
|
||
|
/* Questo dimostra la possibilità di usare use gtk_box_pack_end() per
|
||
|
* giustificare gli oggetti a destra. Per prima cosa creiamo una
|
||
|
|
||
|
* nuova scatola come prima. */
|
||
|
box2 = make_box (FALSE, 0, FALSE, FALSE, 0);
|
||
|
/* creiamo l'etichetta che sarà aggiunta alla fine. */
|
||
|
label = gtk_label_new ("end");
|
||
|
/* impacchettiamola usando gtk_box_pack_end(), così che viene inserita
|
||
|
* sul lato destro della hbox creata nella chiamata a the make_box(). */
|
||
|
gtk_box_pack_end (GTK_BOX (box2), label, FALSE, FALSE, 0);
|
||
|
/* mostriamo l'etichetta. */
|
||
|
gtk_widget_show (label);
|
||
|
|
||
|
/* impacchettiamo box2 in box1 (the vbox, ricordate? :) */
|
||
|
gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
|
||
|
gtk_widget_show (box2);
|
||
|
|
||
|
/* un separatore per il fondo */
|
||
|
separator = gtk_hseparator_new ();
|
||
|
/* Questo assegna esplicitamente al separatore l'ampiezza di 400 pixel
|
||
|
* e l'altezza di 5 pixel. Ciò fa sì che la hbox che abbiamo creato sia
|
||
|
* anche essa larga 400 pixel, e che l'etichetta finale sia separata dalle
|
||
|
* altre etichette nella hbox. In caso contrario, tutti gli oggetti nella
|
||
|
* hbox sarebbero impacchettati il più vicino possibile. */
|
||
|
gtk_widget_set_usize (separator, 400, 5);
|
||
|
/* impacchetta il separatore nella vbox (box1) creata vicino all'inizio
|
||
|
* di main() */
|
||
|
gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
|
||
|
gtk_widget_show (separator);
|
||
|
}
|
||
|
|
||
|
/* Creare un'altra nuova hbox.. ricordate che ne possiamo usare quante ne vogliamo! */
|
||
|
quitbox = gtk_hbox_new (FALSE, 0);
|
||
|
|
||
|
/* Il nostro bottone di uscita. */
|
||
|
button = gtk_button_new_with_label ("Quit");
|
||
|
|
||
|
|
||
|
/* Configuriamo il segnale per distruggere la finestra. Ricordate che
|
||
|
* ciò manderà alla finestra il segnale "destroy", che verrà catturato
|
||
|
* dal nostro gestore di segnali che abbiamo definito in precedenza. */
|
||
|
gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
|
||
|
GTK_SIGNAL_FUNC (gtk_widget_destroy),
|
||
|
GTK_OBJECT (window));
|
||
|
/* impacchetta il bottone in quitbox.
|
||
|
* Gli ultimi tre argomenti di gtk_box_pack_start sono: expand, fill, padding. */
|
||
|
gtk_box_pack_start (GTK_BOX (quitbox), button, TRUE, FALSE, 0);
|
||
|
/* impacchetta quitbox nella vbox (box1) */
|
||
|
gtk_box_pack_start (GTK_BOX (box1), quitbox, FALSE, FALSE, 0);
|
||
|
|
||
|
/* impacchetta la vbox (box1), che ora contiene tutti i nostri oggetti,
|
||
|
* nella finestra principale. */
|
||
|
gtk_container_add (GTK_CONTAINER (window), box1);
|
||
|
|
||
|
/* e mostra tutto quel che rimane */
|
||
|
gtk_widget_show (button);
|
||
|
gtk_widget_show (quitbox);
|
||
|
|
||
|
gtk_widget_show (box1);
|
||
|
/* Mostriamo la finestra alla fine in modo che tutto spunti fuori assieme. */
|
||
|
gtk_widget_show (window);
|
||
|
|
||
|
/* E, naturalmente, la nostra funzione main. */
|
||
|
gtk_main ();
|
||
|
|
||
|
/* Il controllo ritorna a questo punto quando viene chiamata gtk_main_quit(),
|
||
|
* ma non quando si usa gtk_exit. */
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
</verb></tscreen>
|
||
|
|
||
|
<p>
|
||
|
<sect1>Impacchettamento con uso di Tabelle
|
||
|
<p>
|
||
|
Diamo ora un'occhiata ad un altro modo di impacchettare - le Tabelle.
|
||
|
In certe situazioni, possono risultare estremamente utili.
|
||
|
|
||
|
Usando le tabelle, creiamo una griglia in cui possiamo piazzare gli oggetti.
|
||
|
Gli oggetti possono occupare tanti spazi quanti ne specifichiamo.
|
||
|
|
||
|
Naturalmente, la prima cosa da vedere è la funzione gtk_table_new:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
GtkWidget* gtk_table_new (gint rows,
|
||
|
gint columns,
|
||
|
gint homogeneous);
|
||
|
</verb></tscreen>
|
||
|
<p>
|
||
|
Il primo argomento rappresenta il numero di righe da mettere nella tabella,
|
||
|
mentre il secondo è ovviamente il numero di colonne.
|
||
|
|
||
|
L'argomento homogeneous ha a che fare con il modo in cui le caselle della tabella
|
||
|
sono dimensionate. Se homogeneous ha il valore TRUE, le caselle sono ridimensionate
|
||
|
fino alla dimensione del più grande oggetto contenuto nella tabelle. Se è FALSE, la
|
||
|
dimensione delle caselleè decisa dal più alto oggetto in una certa riga e dal più
|
||
|
largo oggetto in una stessa colonna.
|
||
|
|
||
|
Le righe e le colonne sono disposte a partire da 0 fino a n, dove n è il numero
|
||
|
che era stato specificato nella chiamata a gtk_table_new. Così, se specificate
|
||
|
rows = 2 e columns = 2, lo schema avrà questo aspetto:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
0 1 2
|
||
|
0+----------+----------+
|
||
|
| | |
|
||
|
1+----------+----------+
|
||
|
| | |
|
||
|
2+----------+----------+
|
||
|
</verb></tscreen>
|
||
|
<p>
|
||
|
Notate che il sistema di coordinate ha origine nel vertice in alto a sinistra. Per
|
||
|
mettere un oggetto in una tabella, usate la seguente funzione:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void gtk_table_attach (GtkTable *table,
|
||
|
GtkWidget *child,
|
||
|
gint left_attach,
|
||
|
gint right_attach,
|
||
|
gint top_attach,
|
||
|
gint bottom_attach,
|
||
|
gint xoptions,
|
||
|
gint yoptions,
|
||
|
gint xpadding,
|
||
|
gint ypadding);
|
||
|
</verb></tscreen>
|
||
|
<p>
|
||
|
In cui il primo argomento (``table'') è la tabella che avete creato e il secondo
|
||
|
(``child'') è l'oggetto che volete piazzare nella tabella.
|
||
|
|
||
|
Gli argomenti ``attach'' (right, left, top, bottom) specificano dove mettere l'oggetto
|
||
|
e quante caselle adoperare. Se volete mettere un bottone nella casella in basso a destra
|
||
|
nella nostra tabella 2x2, e volete che esso riempia SOLO quella casella, dovete porre
|
||
|
left_attach = 1, right_attach = 2, top_attach = 1, bottom_attach = 2.
|
||
|
|
||
|
Se invece volete che un oggetto si prenda tutta la riga più in alto nella nostra tabella
|
||
|
2x2, dovreste usare left_attach = 0, right_attach =2, top_attach = 0,
|
||
|
bottom_attach = 1.
|
||
|
|
||
|
Gli argomenti ``xoptions'' e ``yoptions'' sono usati per specificare le opzioni di impacchettamento;
|
||
|
di essi si può fare l'OR in modo di ottenere opzioni multiple.
|
||
|
|
||
|
Le opzioni sono:
|
||
|
<itemize>
|
||
|
<item>GTK_FILL - Se la parte di tabella in cui si vuole inserire il widget è più
|
||
|
grande dell'oggetto, e se si specifica GTK_FILL, l'oggetto viene espanso fino ad
|
||
|
occupare tutto lo spazio disponibile.
|
||
|
|
||
|
<item>GTK_SHRINK - Se si alloca all'oggetto nella tabella meno spazio del necessario
|
||
|
(di solito succede quando l'utente ridimensiona la finestra), allora normalmente
|
||
|
l'oggetto verrebbe spinto fuori dal fondo della finestra fino a sparire.
|
||
|
Se invece si specifica GTK_SHRINK is specified, gli oggetti si rimpiccioliscono
|
||
|
assieme alla tabella.
|
||
|
|
||
|
<item>GTK_EXPAND - Questo fa sì che la tabella si espanda fino ad occupare tutto lo
|
||
|
spazio che rimane nella finestra.
|
||
|
</itemize>
|
||
|
|
||
|
Il riempimento funziona come nelle scatole, con la creazione di un'area vuota
|
||
|
attorno all'oggetto la cui dimensione viene specificata in pixel.
|
||
|
|
||
|
La funzione gtk_table_attach() ha UN MUCCHIO di opzioni. Quindi, ecco una scorciatoia:
|
||
|
|
||
|
<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>
|
||
|
|
||
|
Le xoptions e yoptions vengono posti per difetto a GTK_FILL | GTK_EXPAND, e sia xpadding
|
||
|
che ypadding vengono posti a 0. Il resto degli argomenti sono identici a quelli della funzione
|
||
|
precedente.
|
||
|
|
||
|
Ci sono poi le funzioni gtk_table_set_row_spacing() and gtk_table_set_col_spacing().
|
||
|
Queste mettono dello spazio fra le righe (o colonne)in corrispondenza di una specifica
|
||
|
riga (o colonna).
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void gtk_table_set_row_spacing (GtkTable *table,
|
||
|
gint row,
|
||
|
gint spacing);
|
||
|
</verb></tscreen>
|
||
|
e
|
||
|
<tscreen><verb>
|
||
|
void gtk_table_set_col_spacing (GtkTable *table,
|
||
|
gint column,
|
||
|
gint spacing);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Notate che per le colonne lo spazio viene posto alla destra della colonna, mentre
|
||
|
per le righe lo spazio viene posto al di sotto della riga.
|
||
|
|
||
|
Si può poi inserire una spaziatura identica fra tutte le righe e/o colonne usando:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void gtk_table_set_row_spacings (GtkTable *table,
|
||
|
gint spacing);
|
||
|
</verb></tscreen>
|
||
|
<p>
|
||
|
e
|
||
|
<tscreen><verb>
|
||
|
void gtk_table_set_col_spacings (GtkTable *table,
|
||
|
gint spacing);
|
||
|
</verb></tscreen>
|
||
|
<p>
|
||
|
Notate che con queste chiamate, all'ultima riga e all'ultima colonna
|
||
|
non viene assegnata alcuna spaziatura.
|
||
|
|
||
|
<sect1>Esempio di Impacchettamento con Tabelle
|
||
|
<p>
|
||
|
Per il momento, si prega di fare riferimento all'esempio di tabella in
|
||
|
testgtk.c distribuito con i sorgenti di gtk.
|
||
|
|
||
|
|
||
|
<sect>Panoramica sui Widget
|
||
|
<p>
|
||
|
<p>
|
||
|
La procedura generale di creazione di un widget in GTK prevede i seguenti passi:
|
||
|
<enum>
|
||
|
<item> gtk_*_new - una delle varie funzioni che servono per greare un nuovo widget.
|
||
|
In questa sezione le vedremo tutte in dettaglio.
|
||
|
|
||
|
<item> Connettere tutti i segnali che si vogliono usare alle funzione gestione appropriate.
|
||
|
|
||
|
<item> Assegnare gli attributi all'oggetto.
|
||
|
|
||
|
<item> Impacchettare l'oggetto in un contenitore usando la chiamate appropriata,
|
||
|
per esempio gtk_container_add() o gtk_box_pack_start().
|
||
|
|
||
|
<item> Mostrare l'oggetto con gtk_widget_show().
|
||
|
</enum>
|
||
|
<p>
|
||
|
gtk_widget_show() fa sì che GTK sappia che abbiamo terminato di assegnare gli
|
||
|
attributi dell'oggetto grafico, e che è pronto per essere visualizzato.
|
||
|
Si può anche usare la funzione gtk_widget_hide per farlo sparire di nuovo.
|
||
|
L'ordine in cui mostrate gli oggetti grafici non è importante, ma io suggerisco
|
||
|
di mostrare per ultima la finestra, in modo che questa spunti fuori già completa,
|
||
|
invece di vedere i singoli oggetti che arrivano sullo schermo a mano a mano che si
|
||
|
formano. I figli di un oggetto grafico (anche una finestra è un oggetto grafico) non
|
||
|
vengono infatti mostrati finché la finestra stessa non viene mostrata usando la
|
||
|
funzione gtk_widget_show().
|
||
|
|
||
|
|
||
|
<sect1> Casting
|
||
|
<p>
|
||
|
Noterete andando avanti che GTK usa un sistema di casting di tipo. Questa operazione
|
||
|
viene sempre effettuata usando delle macro che allo stesso tempo controllano la
|
||
|
possibilità di effettuare il cast sull'elemento dato e lo effettuano realmente.
|
||
|
Alcune macro che avrete modo di incontrare sono:
|
||
|
|
||
|
<itemize>
|
||
|
<item> GTK_WIDGET(widget)
|
||
|
<item> GTK_OBJECT(object)
|
||
|
<item> GTK_SIGNAL_FUNC(function)
|
||
|
<item> GTK_CONTAINER(container)
|
||
|
<item> GTK_WINDOW(window)
|
||
|
<item> GTK_BOX(box)
|
||
|
</itemize>
|
||
|
|
||
|
Tutte queste funzioni sono usate per fare il cast di argomenti di funzione. Le vedrete
|
||
|
negli esempi, e capirete se è il caso di usarle semplicemente guardando alle
|
||
|
dichiarazioni delle funzioni.
|
||
|
|
||
|
Come potrete vedere più sotto nella gerarchia delle classi, tutti i GtkWidgets
|
||
|
sono derivati dalla classe base GtkObject. Ciò significa che potete usare un
|
||
|
widget in ogni posto in cui una funzione richiede un oggetto - semplicemente
|
||
|
usate la macro GTK_OBJECT().
|
||
|
|
||
|
Per esempio:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
gtk_signal_connect(GTK_OBJECT(button), "clicked",
|
||
|
GTK_SIGNAL_FUNC(callback_function), callback_data);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Questo fa il cast del bottone in un oggetto e fornisce alla chiamata di ritorno
|
||
|
un cast al puntatore a funzione.
|
||
|
|
||
|
Molti oggetti grafici sono anche contenitori. Se guardate alla gerarchia delle
|
||
|
classi più sotto, vedrete che molti oggetti grafici sono derivati dalla classe
|
||
|
GtkContainer. Ognuna di queste classi può essere usata, con la macro GTK_CONTAINER,
|
||
|
come argomento per funzioni che richiedono un contenitore.
|
||
|
|
||
|
Sfortunatamente, in questo tutorial non si parlerà in modo estensivo di queste macro,
|
||
|
ma raccomando di dare un'occhiata ai file header di GTK. Può essere una cosa molto
|
||
|
educativa. Infatti, non è difficile imparare come funziona un oggetto solo guardando
|
||
|
le dichiarazioni delle funzioni.
|
||
|
|
||
|
<p>
|
||
|
<sect1>Gerarchia degli Oggetti Grafici
|
||
|
<p>
|
||
|
Ecco, per vostro riferimento, la gerarchia delle classi usata per implementare gli
|
||
|
oggetti grafici.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
GtkObject
|
||
|
+-- GtkData
|
||
|
| \-- GtkAdjustment
|
||
|
|
|
||
|
\-- GtkWidget
|
||
|
+-- GtkContainer
|
||
|
| +-- GtkBin
|
||
|
| | +-- GtkAlignment
|
||
|
| | +-- GtkFrame
|
||
|
| | | *-- GtkAspectFrame
|
||
|
| | |
|
||
|
| | +-- GtkItem
|
||
|
| | | +-- GtkListItem
|
||
|
| | | +-- GtkMenuItem
|
||
|
| | | | +-- GtkCheckMenuItem
|
||
|
| | | | *-- GtkRadioMenuItem
|
||
|
| | | |
|
||
|
| | | *-- GtkTreeItem
|
||
|
| | |
|
||
|
| | +-- GtkViewport
|
||
|
| | \-- GtkWindow
|
||
|
| | +-- GtkDialog
|
||
|
| | \-- GtkFileSelection
|
||
|
| |
|
||
|
| +-- GtkBox
|
||
|
| | +-- GtkHBox
|
||
|
| | \-- GtkVBox
|
||
|
| | +-- GtkColorSelection
|
||
|
| | \-- GtkCurve
|
||
|
| |
|
||
|
| +-- GtkButton
|
||
|
| | +-- GtkOptionMenu
|
||
|
| | \-- GtkToggleButton
|
||
|
| | \-- GtkCheckButton
|
||
|
| | \-- GtkRadioButton
|
||
|
| |
|
||
|
| +-- GtkList
|
||
|
| +-- GtkMenuShell
|
||
|
| | +-- GtkMenu
|
||
|
| | \-- GtkMenuBar
|
||
|
| |
|
||
|
| +-- GtkNotebook
|
||
|
| +-- GtkScrolledWindow
|
||
|
| +-- GtkTable
|
||
|
| \-- GtkTree
|
||
|
|
|
||
|
+-- GtkDrawingArea
|
||
|
+-- GtkEntry
|
||
|
+-- GtkMisc
|
||
|
| +-- GtkArrow
|
||
|
| +-- GtkImage
|
||
|
| +-- GtkLabel
|
||
|
| \-- GtkPixmap
|
||
|
|
|
||
|
+-- GtkPreview
|
||
|
+-- GtkProgressBar
|
||
|
+-- GtkRange
|
||
|
| +-- GtkScale
|
||
|
| | +-- GtkHScale
|
||
|
| | \-- GtkVScale
|
||
|
| |
|
||
|
| \-- GtkScrollbar
|
||
|
| +-- GtkHScrollbar
|
||
|
| \-- GtkVScrollbar
|
||
|
|
|
||
|
+-- GtkRuler
|
||
|
| +-- GtkHRuler
|
||
|
| \-- GtkVRuler
|
||
|
|
|
||
|
\-- GtkSeparator
|
||
|
+-- GtkHSeparator
|
||
|
\-- GtkVSeparator
|
||
|
|
||
|
</verb></tscreen>
|
||
|
<p>
|
||
|
|
||
|
<sect1>Oggetti senza Finestre
|
||
|
<p>
|
||
|
Gli oggetti seguenti non hanno una finestra associata. Se volete catturare
|
||
|
degli eventi, dovrete usare l'oggetto GtkEventBox. Vedete anche la sezione su
|
||
|
<ref id="sec_The_EventBox_Widget" name="Il Widget EventBox">
|
||
|
|
||
|
<tscreen><verb>
|
||
|
GtkAlignment
|
||
|
GtkArrow
|
||
|
GtkBin
|
||
|
GtkBox
|
||
|
GtkImage
|
||
|
GtkItem
|
||
|
GtkLabel
|
||
|
GtkPaned
|
||
|
GtkPixmap
|
||
|
GtkScrolledWindow
|
||
|
GtkSeparator
|
||
|
GtkTable
|
||
|
GtkViewport
|
||
|
GtkAspectFrame
|
||
|
GtkFrame
|
||
|
GtkVPaned
|
||
|
GtkHPaned
|
||
|
GtkVBox
|
||
|
GtkHBox
|
||
|
GtkVSeparator
|
||
|
GtkHSeparator
|
||
|
</verb></tscreen>
|
||
|
<p>
|
||
|
Proseguiremo la nostra esplorazione di GTK esaminando uno alla volta tutti
|
||
|
gli oggetti, creando qualche semplice funzione per mostrarli. Un'altra
|
||
|
buona sorgente è il programma testgtk.c che viene fornito con GTK. Potete
|
||
|
trovarlo in gtk/testgtk.c.
|
||
|
|
||
|
<sect>Il Widget Bottone (Button)
|
||
|
<p>
|
||
|
<sect1>Bottoni Normali
|
||
|
<p>
|
||
|
Ormai abbiamo visto tutto quello che c'è da vedere riguardo all'oggetto
|
||
|
``bottone''. E' piuttosto semplice, ma ci sono due modi per crare un bottone.
|
||
|
Potete usare gtk_button_new_with_label() per creare un bottone con una
|
||
|
etichetta, o usare gtk_button_new() per creare un bottone vuoto. In tal caso è poi
|
||
|
vostro compito impacchettare un'etichetta o una pixmap sul bottone creato.
|
||
|
Per fare ciò, create una nuova scatola, e poi impacchettateci i vostri
|
||
|
oggetti usando la solita gtk_box_pack_start, e infine usate la funzione
|
||
|
gtk_container_add per impacchettare la scatola nel bottone.
|
||
|
<p>
|
||
|
Ecco un esempio di utilizzo di gtk_button_new per creare un bottone con
|
||
|
un'immagine ed un'etichetta su di sè. Ho separato il codice usato per
|
||
|
creare la scatola in modo che lo possiate usare nei vostri programmi.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
#include <gtk/gtk.h>
|
||
|
|
||
|
|
||
|
/* crea una nuova hbox contenente un'immagine ed un'etichetta
|
||
|
* e ritorna la scatola creata. */
|
||
|
|
||
|
GtkWidget *xpm_label_box (GtkWidget *parent, gchar *xpm_filename, gchar *label_text)
|
||
|
{
|
||
|
GtkWidget *box1;
|
||
|
GtkWidget *label;
|
||
|
GtkWidget *pixmapwid;
|
||
|
GdkPixmap *pixmap;
|
||
|
GdkBitmap *mask;
|
||
|
GtkStyle *style;
|
||
|
|
||
|
/* creare una scatola per una xpm ed una etichetta */
|
||
|
box1 = gtk_hbox_new (FALSE, 0);
|
||
|
gtk_container_border_width (GTK_CONTAINER (box1), 2);
|
||
|
|
||
|
/* ottengo lo stile del bottone. Penso che sia per avere il colore
|
||
|
* dello sfondo. Se qualcuno sa il vero motivo, è pregato di dirmelo. */
|
||
|
style = gtk_widget_get_style(parent);
|
||
|
|
||
|
/* e ora via con le faccende dell'xpm stuff. Carichiamo l'xpm*/
|
||
|
pixmap = gdk_pixmap_create_from_xpm (parent->window, &mask,
|
||
|
&style->bg[GTK_STATE_NORMAL],
|
||
|
xpm_filename);
|
||
|
pixmapwid = gtk_pixmap_new (pixmap, mask);
|
||
|
|
||
|
/* creiamo l'etichetta per il bottone */
|
||
|
label = gtk_label_new (label_text);
|
||
|
|
||
|
/* impacchettiamo la pixmap e l'etichetta nella scatola */
|
||
|
gtk_box_pack_start (GTK_BOX (box1),
|
||
|
pixmapwid, FALSE, FALSE, 3);
|
||
|
|
||
|
gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 3);
|
||
|
|
||
|
gtk_widget_show(pixmapwid);
|
||
|
gtk_widget_show(label);
|
||
|
|
||
|
return (box1);
|
||
|
}
|
||
|
|
||
|
/* la nostra solita funzione di callback */
|
||
|
void callback (GtkWidget *widget, gpointer *data)
|
||
|
{
|
||
|
g_print ("Hello again - %s was pressed\n", (char *) data);
|
||
|
}
|
||
|
|
||
|
|
||
|
int main (int argc, char *argv[])
|
||
|
{
|
||
|
/* GtkWidget è il tipo per contenere gli oggetti */
|
||
|
GtkWidget *window;
|
||
|
GtkWidget *button;
|
||
|
GtkWidget *box1;
|
||
|
|
||
|
gtk_init (&argc, &argv);
|
||
|
|
||
|
/* creiamo una nuova finestra */
|
||
|
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
||
|
|
||
|
gtk_window_set_title (GTK_WINDOW (window), "Pixmap'd Buttons!");
|
||
|
|
||
|
/* E' una buona idea fare questo per tutte le finestre. */
|
||
|
gtk_signal_connect (GTK_OBJECT (window), "destroy",
|
||
|
GTK_SIGNAL_FUNC (gtk_exit), NULL);
|
||
|
|
||
|
|
||
|
/* assegnamo lo spessore del bordo della finestra */
|
||
|
gtk_container_border_width (GTK_CONTAINER (window), 10);
|
||
|
|
||
|
/* creiamo un nuovo bottone */
|
||
|
button = gtk_button_new ();
|
||
|
|
||
|
/* Ormai dovreste esservi abituati a vedere la maggior parte di
|
||
|
* queste funzioni */
|
||
|
gtk_signal_connect (GTK_OBJECT (button), "clicked",
|
||
|
GTK_SIGNAL_FUNC (callback), (gpointer) "cool button");
|
||
|
|
||
|
/* questa chiama la nostra funzione di creazione di scatole */
|
||
|
box1 = xpm_label_box(window, "info.xpm", "cool button");
|
||
|
|
||
|
/* impacchetta e mostra tutti i nostri oggetti */
|
||
|
gtk_widget_show(box1);
|
||
|
|
||
|
gtk_container_add (GTK_CONTAINER (button), box1);
|
||
|
|
||
|
gtk_widget_show(button);
|
||
|
|
||
|
gtk_container_add (GTK_CONTAINER (window), button);
|
||
|
|
||
|
gtk_widget_show (window);
|
||
|
|
||
|
/* mettiti in gtk_main e aspetta che cominci il divertimento! */
|
||
|
gtk_main ();
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
</verb></tscreen>
|
||
|
La funzione xpm_label_box può essere usata per impacchettare delle xpm
|
||
|
e delle etichette su qualsiasi oggetto che può essere un contenitore.
|
||
|
|
||
|
<sect1> Bottoni a Commutazione (Toggle Buttons)
|
||
|
<p>
|
||
|
I bottoni a commutazione sono molto simili ai bottoni normali, tranne che per il
|
||
|
fatto che essi si trovano sempre in uno di due stati, che si alternano ad ogni
|
||
|
click. Possono trovarsi nello stato ``premuto'', e quando li si ripreme, tornano
|
||
|
ad essere sollevati. Ri-clickandoli, torneranno giù.
|
||
|
|
||
|
I bottoni a commutazione sono la base per i bottoni di controllo (check button) e
|
||
|
per i radio-bottoni, e quindi molte delle chiamate disponibili per i bottoni
|
||
|
a commutazione vengono ereditati dai radio-bottoni e dai bottoni di controllo.
|
||
|
Ma vedremo questi aspetti nel momento in cui li incontreremo.
|
||
|
|
||
|
Creare un nuovo bottone a commutazione:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
GtkWidget* gtk_toggle_button_new (void);
|
||
|
|
||
|
GtkWidget* gtk_toggle_button_new_with_label (gchar *label);
|
||
|
</verb></tscreen>
|
||
|
<p>
|
||
|
Come potete immaginare, queste funzioni lavorano in modo identico che per
|
||
|
i bottoni normali. La prima crea un bottone a commutazione vuoto e la seconda un
|
||
|
bottone con un'etichetta.
|
||
|
<p>
|
||
|
Per ottenere lo stato dei widget a commutazione, compresi i radio-bottoni e i
|
||
|
bottoni di controllo, si può usare una macro come mostrato nell'esempio
|
||
|
più sotto. In questo modo lo stato dell'oggetto commutabile viene valutato in
|
||
|
una funzione di ritorno. Il segnale emesso dai bottoni a commutazione
|
||
|
(toggle button, il radio button o il check button) che ci interessa è il segnale
|
||
|
``toggled''. Per controllare lo stato di questi bottoni, create un gestore di
|
||
|
segnali che catturi il ``toggled'', e usate la macro per determinare
|
||
|
il suo stato. La funzione di callback avrà un aspetto più o meno così:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void toggle_button_callback (GtkWidget *widget, gpointer data)
|
||
|
{
|
||
|
if (GTK_TOGGLE_BUTTON (widget)->active)
|
||
|
{
|
||
|
/* Se il programma si è arrivato a questo punto, il bottone
|
||
|
* a commutazione è sollevato */
|
||
|
|
||
|
} else {
|
||
|
|
||
|
/* il bottone è abbassato */
|
||
|
}
|
||
|
}
|
||
|
</verb></tscreen>
|
||
|
|
||
|
<!--
|
||
|
|
||
|
COMMENTED!
|
||
|
|
||
|
<tscreen><verb>
|
||
|
guint gtk_toggle_button_get_type (void);
|
||
|
</verb></tscreen>
|
||
|
<p>
|
||
|
No idea... they all have this, but I dunno what it is :)
|
||
|
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void gtk_toggle_button_set_mode (GtkToggleButton *toggle_button,
|
||
|
gint draw_indicator);
|
||
|
</verb></tscreen>
|
||
|
<p>
|
||
|
No idea.
|
||
|
-->
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void gtk_toggle_button_set_state (GtkToggleButton *toggle_button,
|
||
|
gint state);
|
||
|
</verb></tscreen>
|
||
|
<p>
|
||
|
La chiamata qui sopra può essere usata per fare l'assegnazione dello stato
|
||
|
del bottone a commutazione e dei suoi figli, il radio-bottone e il bottone di
|
||
|
controllo. Passando come primo argomento a questa funzione il vostro bottone e
|
||
|
come secondo argomento il valore TRUE o FALSE, si può specificare se il
|
||
|
bottone deve essere sollevato (rilasciato) o abbassato (premuto). Il valore
|
||
|
di difetto è sollevato, cioè FALSE.
|
||
|
|
||
|
Notate che quando usate la funzione gtk_toggle_button_set_state(), e lo
|
||
|
stato viene cambiato, si ha il risultato che il bottone emette il segnale
|
||
|
``clicked''.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void gtk_toggle_button_toggled (GtkToggleButton *toggle_button);
|
||
|
</verb></tscreen>
|
||
|
<p>
|
||
|
Questa funzione semplicemente commuta il bottone, ed emette il segnale ``toggled''.
|
||
|
|
||
|
<sect1> Bottoni di Controllo (Check Buttons)
|
||
|
<p>
|
||
|
I bottoni di controllo ereditano molte proprietà e funzioni dal bottone a commutazione,
|
||
|
ma hanno un aspetto un po' diverso. Invece di essere bottoni contenenti del testo,
|
||
|
si tratta di quadratini con del testo alla propria destra. Questi bottoni sono
|
||
|
spesso usati nelle applicazioni per commutare fra lo stato attivato e disattivato delle
|
||
|
opzioni.
|
||
|
|
||
|
Le due funzioni di creazione sono analoghe a quelle del bottone normale..
|
||
|
|
||
|
<tscreen><verb>
|
||
|
GtkWidget* gtk_check_button_new (void);
|
||
|
|
||
|
GtkWidget* gtk_check_button_new_with_label (gchar *label);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
La funzione new_with_label crea un bottone di controllo con una etichetta
|
||
|
a fianco di esso.
|
||
|
|
||
|
Per controllare lo stato del check button si opera in modo identico al bottone
|
||
|
a commutazione.
|
||
|
|
||
|
<sect1> Radio-Bottoni (Radio Buttons)
|
||
|
<p>
|
||
|
I radio-bottoni sono simili ai bottoni di controllo, tranne che per il
|
||
|
fatto che sono sempre raggruppati in modo che solo uno alla volta di essi
|
||
|
può essere selezionato (premuto). Tornano utili quando nella propria applicazione
|
||
|
si ha bisogno di selezionare una opzione da una breve lista.
|
||
|
|
||
|
La creazione di un nuovo radio-bottone si fa con una di queste chiamate:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
GtkWidget* gtk_radio_button_new (GSList *group);
|
||
|
|
||
|
GtkWidget* gtk_radio_button_new_with_label (GSList *group,
|
||
|
gchar *label);
|
||
|
</verb></tscreen>
|
||
|
<p>
|
||
|
Avrete notato l'argomento in più che c'è in queste chiamate. Queste hanno
|
||
|
infatti bisogno dela specificazione di un ``gruppo'' per svolgere il loro compito.
|
||
|
Per il primo bottone di un gruppo si deve passare come primo argomento il valore
|
||
|
NULL. Dopodiché potete creare un gruppo usando la funzione:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
GSList* gtk_radio_button_group (GtkRadioButton *radio_button);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
<p>
|
||
|
A questo punto potete passare questo gruppo ad ogni chiamata successiva a
|
||
|
gtk_radio_button_new o new_with_label. E' anche una buona idea specificare
|
||
|
esplicitamente quale dei bottoni dovrà essere quello premuto per difetto,
|
||
|
usando:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void gtk_toggle_button_set_state (GtkToggleButton *toggle_button,
|
||
|
gint state);
|
||
|
</verb></tscreen>
|
||
|
<p>
|
||
|
Questa funzione è descritta nella sezione sui bottoni a commutazione, e funziona
|
||
|
nello stesso identico modo.
|
||
|
|
||
|
<p>
|
||
|
[Inserirò un esempio di come usare questi oggetti, penso che sarebbe molto
|
||
|
utile]
|
||
|
|
||
|
|
||
|
<sect> Alcuni Widget
|
||
|
<p>
|
||
|
<sect1> L'Etichetta (Label)
|
||
|
<p>
|
||
|
Le etichette sono molto usate in GTK, e sono relativamente semplici. Le
|
||
|
etichette non emettono segnali, dal momento che non hanno una finestra
|
||
|
X a loro assegnata. Se avete la necessità di avere dei segnali o di fare
|
||
|
delle operazioni di clipping, potete usare il widget EventBox.
|
||
|
|
||
|
Per creare una nuova etichetta, si usa:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
GtkWidget* gtk_label_new (char *str);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
In cui l'unico argomento è la stringa che si vuole sia mostrata.
|
||
|
|
||
|
Per cambiare il testo dell'etichetta dopo che è stata creata, si usa
|
||
|
la funzione:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void gtk_label_set (GtkLabel *label,
|
||
|
char *str);
|
||
|
</verb></tscreen>
|
||
|
<p>
|
||
|
in cui il primo argomento è l'etichetta creata in precedenza (di cui si
|
||
|
fa il cast usando la macro GTK_LABEL()), mentre il secondo è la nuova
|
||
|
stringa.
|
||
|
|
||
|
Nel caso, lo spazio necessario per la nuova stringa verrà regolato automaticamente.
|
||
|
|
||
|
Per ottenere la stringa corrente si usa:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void gtk_label_get (GtkLabel *label,
|
||
|
char **str);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
in cui il primo argomento è l'etichetta che avete creato, e il secondo
|
||
|
è il valore di ritorno per la stringa.
|
||
|
|
||
|
|
||
|
<sect1>Il Widget Suggerimenti (Tooltips)
|
||
|
<p>
|
||
|
I suggerimenti sono piccole stringhe di testo che spuntano quando lasciate il
|
||
|
puntatore su un bottone o un altro widget per qualche secondo. Sono piuttosto
|
||
|
semplici da usare, per cui ne darò la spiegazione senza corredarla di esempi.
|
||
|
Se volede vedere un po' di codice, date un'occhiata al programma testgtk.c
|
||
|
distribuito con GTK.
|
||
|
<p>
|
||
|
Con alcuni widget (per esempio con l'etichetta) i suggerimenti non funzionano.
|
||
|
<p>
|
||
|
La prima chiamata che si usa per creare un nuovo tooltip è la seguente.
|
||
|
In una data funzione, è necessario chiamarla una sola volta: il GtkTooltip
|
||
|
che viene ritornato da questa funzione può essere usato per creare suggerimenti
|
||
|
multipli.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
GtkTooltips *gtk_tooltips_new (void);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Una volta creato un nuovo suggerimento e il widget su cui lo volete usare,
|
||
|
basta usare la seguente chiamata per fare l'assegnazione:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void gtk_tooltips_set_tips (GtkTooltips *tooltips,
|
||
|
GtkWidget *widget,
|
||
|
gchar *tips_text);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Il primo argomento è il suggerimento che era già stato creato, che è seguito
|
||
|
dal widget da cui volete che spunti il suggerimento e dal testo che volete
|
||
|
venga mostrato.
|
||
|
<p>
|
||
|
Ecco un piccolo esempio:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
GtkTooltips *tooltips;
|
||
|
GtkWidget *button;
|
||
|
...
|
||
|
tooltips = gtk_tooltips_new ();
|
||
|
button = gtk_button_new_with_label ("button 1");
|
||
|
...
|
||
|
gtk_tooltips_set_tips (tooltips, button, "This is button 1");
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Ci sono anche altre funzioni che si usano con i suggerimenti. Eccone una lista
|
||
|
con una breve descrizione di quello che fanno.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void gtk_tooltips_destroy (GtkTooltips *tooltips);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Distrugge un suggerimento esistente.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void gtk_tooltips_enable (GtkTooltips *tooltips);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Abilita un gruppo di suggerimenti disbilitato.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void gtk_tooltips_disable (GtkTooltips *tooltips);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Disabilita un gruppo di suggerimenti abilitato.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void gtk_tooltips_set_delay (GtkTooltips *tooltips,
|
||
|
gint delay);
|
||
|
|
||
|
</verb></tscreen>
|
||
|
Stabilisce quanti millisecondi si deve mantenere il puntatore sopra al
|
||
|
widget prima che venga mostrato il suggerimento. Il valore di difetto
|
||
|
è di 1000 millisecondi.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void gtk_tooltips_set_tips (GtkTooltips *tooltips,
|
||
|
GtkWidget *widget,
|
||
|
gchar *tips_text);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Cambia il testo di un suggerimento già esistente.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void gtk_tooltips_set_colors (GtkTooltips *tooltips,
|
||
|
GdkColor *background,
|
||
|
GdkColor *foreground);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Assegna i colori di primo piano e di sfondo dei suggerimenti. (Non ho idea
|
||
|
di come si specifichino i colori).
|
||
|
<p>
|
||
|
E questo è tutto riguardo alle funzioni relative ai suggerimenti. Più
|
||
|
di quanto avreste mai voluto sapere :)
|
||
|
|
||
|
<sect1> La Barra di Avanzamento (Progress Bar)
|
||
|
<p>
|
||
|
Le barre di avanzamento sono usate per mostrare lo stato di una operazione. Come potete
|
||
|
vedere nel frammento di codice qui sotto, sono piuttosto semplici da usare.
|
||
|
Ma prima vediamo come cominciare con la chiamata per creare una nuova progrss
|
||
|
bar.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
GtkWidget *gtk_progress_bar_new (void);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Ora che la barra di avanzamento è stata creata, possiamo usarla..
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void gtk_progress_bar_update (GtkProgressBar *pbar, gfloat percentage);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Il primo argomento è la barra di avanzamento su cui volete lavorare, e il secondo
|
||
|
è la quantità 'completato', cioè la quantità di riempimento della progress
|
||
|
bar fra 0 e 100% (un numero reale fra 0 e 1).
|
||
|
|
||
|
Le barre di avanzamento sono usate di solito con funzioni di timeout o altre di
|
||
|
questo tipo (vedi alla sezione <ref id="sec_timeouts" name="Timeouts,
|
||
|
I/O and Idle Functions">) per dare l'illusione del multitasking. Tutte
|
||
|
usano la funzione gtk_progress_bar_update nello stesso modo.
|
||
|
|
||
|
Ecco un esempio di barra di avanzamento, in cui l'aggiornamento avviene usando
|
||
|
dei timeout. Questo codice vi mostra anche come riinizializzare le
|
||
|
barre di avanzamento.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
#include <gtk/gtk.h>
|
||
|
|
||
|
static int ptimer = 0;
|
||
|
int pstat = TRUE;
|
||
|
|
||
|
/* Questa funzione incrementa e aggiorna la barra di avanzamento, e la rimette
|
||
|
a zero se pstat è FALSE */
|
||
|
gint progress (gpointer data)
|
||
|
{
|
||
|
gfloat pvalue;
|
||
|
|
||
|
/* ottiene il valore corrente della status bar */
|
||
|
pvalue = GTK_PROGRESS_BAR (data)->percentage;
|
||
|
|
||
|
if ((pvalue >= 1.0) || (pstat == FALSE)) {
|
||
|
pvalue = 0.0;
|
||
|
pstat = TRUE;
|
||
|
}
|
||
|
pvalue += 0.01;
|
||
|
|
||
|
gtk_progress_bar_update (GTK_PROGRESS_BAR (data), pvalue);
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
/* Questa funzione segnala la riinizializzazione della
|
||
|
barra di avanzamento */
|
||
|
void progress_r (void)
|
||
|
{
|
||
|
pstat = FALSE;
|
||
|
}
|
||
|
|
||
|
void destroy (GtkWidget *widget, gpointer *data)
|
||
|
{
|
||
|
gtk_main_quit ();
|
||
|
}
|
||
|
|
||
|
int main (int argc, char *argv[])
|
||
|
{
|
||
|
GtkWidget *window;
|
||
|
GtkWidget *button;
|
||
|
GtkWidget *label;
|
||
|
GtkWidget *table;
|
||
|
GtkWidget *pbar;
|
||
|
|
||
|
gtk_init (&argc, &argv);
|
||
|
|
||
|
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
||
|
|
||
|
gtk_signal_connect (GTK_OBJECT (window), "delete_event",
|
||
|
GTK_SIGNAL_FUNC (destroy), NULL);
|
||
|
|
||
|
gtk_container_border_width (GTK_CONTAINER (window), 10);
|
||
|
|
||
|
table = gtk_table_new(3,2,TRUE);
|
||
|
gtk_container_add (GTK_CONTAINER (window), table);
|
||
|
|
||
|
label = gtk_label_new ("Progress Bar Example");
|
||
|
gtk_table_attach_defaults(GTK_TABLE(table), label, 0,2,0,1);
|
||
|
gtk_widget_show(label);
|
||
|
/* Crea una nuova barra di avanzamento, impacchettala nella tabella
|
||
|
e mostrala */
|
||
|
pbar = gtk_progress_bar_new ();
|
||
|
gtk_table_attach_defaults(GTK_TABLE(table), pbar, 0,2,1,2);
|
||
|
gtk_widget_show (pbar);
|
||
|
|
||
|
/* Attiva un timeout che gestisca l'aggiornamento automatico della barra */
|
||
|
ptimer = gtk_timeout_add (100, progress, pbar);
|
||
|
|
||
|
/* Questo bottone segnala alla barra che deve essere resettata */
|
||
|
button = gtk_button_new_with_label ("Reset");
|
||
|
gtk_signal_connect (GTK_OBJECT (button), "clicked",
|
||
|
GTK_SIGNAL_FUNC (progress_r), NULL);
|
||
|
gtk_table_attach_defaults(GTK_TABLE(table), button, 0,1,2,3);
|
||
|
gtk_widget_show(button);
|
||
|
|
||
|
button = gtk_button_new_with_label ("Cancel");
|
||
|
gtk_signal_connect (GTK_OBJECT (button), "clicked",
|
||
|
GTK_SIGNAL_FUNC (destroy), NULL);
|
||
|
|
||
|
gtk_table_attach_defaults(GTK_TABLE(table), button, 1,2,2,3);
|
||
|
gtk_widget_show (button);
|
||
|
|
||
|
gtk_widget_show(table);
|
||
|
gtk_widget_show(window);
|
||
|
|
||
|
gtk_main ();
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
</verb></tscreen>
|
||
|
|
||
|
In questo programmino ci sono quattro aree che riguardano il modo di
|
||
|
uso generale delle Barre di Avanzamento; le vediamo ora nell'ordine.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
pbar = gtk_progress_bar_new ();
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Questo codice crea una nuova barra ciamata pbar.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
ptimer = gtk_timeout_add (100, progress, pbar);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Questo codice usa dei timeout per abilitare degli intervalli di tempo uguali.
|
||
|
Per usare le barre di avanzamento non è però necessario servirsi di timeout.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
pvalue = GTK_PROGRESS_BAR (data)->percentage;
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Qui si assegna a pvalue il valore corrente della percentuale di avanzamento.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
gtk_progress_bar_update (GTK_PROGRESS_BAR (data), pvalue);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Infine, questo codice aggiorna la barra di avanzamento con il valore di pvalue.
|
||
|
|
||
|
Questo è tutto quanto c'è da sapere sulle barre di avanzamento, divertitevi.
|
||
|
|
||
|
<sect1> Dialoghi
|
||
|
<p>
|
||
|
|
||
|
Il widget ``Dialogo'' è molto semplice: si tratta in realtà di una finestra
|
||
|
con alcuni elementi pre-impacchettati. La struttura di un dialogo è la
|
||
|
seguente:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
struct GtkDialog
|
||
|
{
|
||
|
GtkWindow window;
|
||
|
|
||
|
GtkWidget *vbox;
|
||
|
GtkWidget *action_area;
|
||
|
};
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Come potete vedere, crea semplicemente una finestra vi inserisce una vbox
|
||
|
in cima, poi un separatore e infine una hbox come ``area di azione''.
|
||
|
|
||
|
Un Dialogo può essere utilizzato per messaggi per l'utente e
|
||
|
altri scopi simili. E' un widget molto essenziale, che ha una sola funzione,
|
||
|
e precisamente:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
GtkWidget* gtk_dialog_new (void);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Per cui, per creare una nuova finestra di dialogo, uate:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
GtkWidget window;
|
||
|
window = gtk_dialog_new ();
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Questa funzione crea una finestra di dialogo, dopodiché sta a voi
|
||
|
utilizzarla. Potete mettere un bottone nella action_area facendo
|
||
|
qualcosa del tipo:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
button = ...
|
||
|
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->action_area), button,
|
||
|
TRUE, TRUE, 0);
|
||
|
gtk_widget_show (button);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Potreste anche aggiungere, ad esempio, un'etichetta all'area della vbox,
|
||
|
con qualcosa di questo genere:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
label = gtk_label_new ("Dialogs are groovy");
|
||
|
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->vbox), label, TRUE,
|
||
|
TRUE, 0);
|
||
|
gtk_widget_show (label);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Per provare a usare una finestra di dialogo, potreste provare a mettere
|
||
|
due bottoni nella action_area, per esempio un bottone ``Cancella'' ed un
|
||
|
bottone ``OK'' e un'etichetta nella vbox che chieda qualcosa all'utente o
|
||
|
segnali un errore. Poi potreste collegare un diverso segnale a ciascun
|
||
|
bottone ed eseguire l'operazione che l'utente che viene scelta dall'utente.
|
||
|
|
||
|
|
||
|
<sect1> Pixmaps
|
||
|
<p>
|
||
|
|
||
|
Le Pixmap sono strutture dati che contengono immagini. Queste immagini
|
||
|
possono poi essere utilizzate in varie occasioni, per esempio come
|
||
|
icone sul desktop X-Window o come cusori. Una bitmap è una pixmap a due
|
||
|
colori.
|
||
|
|
||
|
Per usare una pixmap in GTK, dobbiamo in primo luogo creare una struttura
|
||
|
GdkPixmap utilizzando le routine disponibili nello strato GDK. Una Pixmap
|
||
|
può essere creata a partire da dati presenti in memoria o letti da un file.
|
||
|
Vedremo ora una ad una le chiamate utilizzate per creare una pixmap.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
GdkPixmap *gdk_bitmap_create_from_data( GdkWindow *window,
|
||
|
gchar *data,
|
||
|
gint width,
|
||
|
gint height );
|
||
|
</verb></tscreen>
|
||
|
<p>
|
||
|
Si usa questa routine per creare una pixmap ad un solo piano (2 colori) da
|
||
|
dati disponibili in memoria. Ogni bit nei dati indica lo stato acceso o
|
||
|
spento di un pixel. L'altezza (height) e la larghezza (width) sono espresse
|
||
|
in pixel. GdkWindow è un puntatore alla finestra corrente, dal momento che
|
||
|
le risorse di una pixmap hanno significato solo nel contesto dello schermo
|
||
|
in cui deve essere mostrata.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
GdkPixmap* gdk_pixmap_create_from_data( GdkWindow *window,
|
||
|
gchar *data,
|
||
|
gint width,
|
||
|
gint height,
|
||
|
gint depth,
|
||
|
GdkColor *fg,
|
||
|
GdkColor *bg );
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Questa è usata per creare una pixmap con la profondità data (depth, ossia
|
||
|
numero di colori) usando i dati specificati. fg e bg indicano i colori da
|
||
|
usare per il primo piano e per lo sfondo.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
GdkPixmap* gdk_pixmap_create_from_xpm( GdkWindow *window,
|
||
|
GdkBitmap **mask,
|
||
|
GdkColor *transparent_color,
|
||
|
const gchar *filename );
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Il formato XPM è una rappresentazione di pixmap leggibile per X Window. E' una
|
||
|
rappresentazione molto diffusa, e sono disponibili parecchi programmi per creare
|
||
|
immagini in questo formato. Il file specificato da ``filename'' deve contenere
|
||
|
un'immagine in questo formato, che viene caricato nella struttura pixmap.
|
||
|
La maschera (mask) specifica quali pixel della pixmap devono essere opachi.
|
||
|
Tutti gli altri pixel sono colorati usando il colore specificato da
|
||
|
transparent_color. Più sotto mostreremo un esempio di uso di questa funzione.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
GdkPixmap* gdk_pixmap_create_from_xpm_d (GdkWindow *window,
|
||
|
GdkBitmap **mask,
|
||
|
GdkColor *transparent_color,
|
||
|
gchar **data);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Si possono incorporare piccole immagini all'interno di un programma sotto
|
||
|
forma di dati in formato XPM. In questo modo, invece di leggerli da un file,
|
||
|
si possono usare questi dati per creare una pixmap. Un esempio di questo tipo
|
||
|
di dati è
|
||
|
|
||
|
<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>
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void gdk_pixmap_destroy( GdkPixmap *pixmap );
|
||
|
</verb></tscreen>
|
||
|
<p>
|
||
|
Quando abbiamo finito di usare una pixmap e pensiamo di non doverla riutilizzare
|
||
|
presto, è una buona idea liberare queste risorse usando la funzione
|
||
|
dk_pixmap_destroy. Le pixmap devono essere considerate una risorsa preziosa.
|
||
|
|
||
|
Quando abbiamo creato una pixmap, possiamo mostrarla come un widget GTK.
|
||
|
E' necessario creare un widget pixmap che contenga una pixmap GDK. Questa
|
||
|
operazione viene compiuta usando
|
||
|
|
||
|
<tscreen><verb>
|
||
|
GtkWidget* gtk_pixmap_new( GdkPixmap *pixmap,
|
||
|
GdkBitmap *mask );
|
||
|
</verb></tscreen>
|
||
|
<p>
|
||
|
Le altre chiamate per i widget pixmap sono
|
||
|
|
||
|
<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>
|
||
|
<p>
|
||
|
La funzione gtk_pixmap_set viene usata per cambiare la pixmap che viene
|
||
|
gestita correntemente dal widget.
|
||
|
gtk_pixmap_set is used to change the pixmap that the widget is currently
|
||
|
managing. ``val'' è la pixmap che è stata creata usando il GDK.
|
||
|
Segue un esempio di uso di una pixmap in un bottone.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
|
||
|
#include <gtk/gtk.h>
|
||
|
|
||
|
|
||
|
/* dat XPM dell'icona Apri File */
|
||
|
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. ",
|
||
|
" ......... ",
|
||
|
" ",
|
||
|
" "};
|
||
|
|
||
|
|
||
|
/* quando invocata (con il segnale delete_event), termina l'applicazione. */
|
||
|
void close_application( GtkWidget *widget, gpointer *data ) {
|
||
|
gtk_main_quit();
|
||
|
}
|
||
|
|
||
|
|
||
|
/* invocata se il bottone è clickato. Stampa semplicemente un messaggio */
|
||
|
void button_clicked( GtkWidget *widget, gpointer *data ) {
|
||
|
printf( "button clicked\n" );
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
int main( int argc, char *argv[] )
|
||
|
{
|
||
|
/* i widget sono memorizzati nel tipo GtkWidget */
|
||
|
GtkWidget *window, *pixmapwid, *button;
|
||
|
GdkPixmap *pixmap;
|
||
|
GdkBitmap *mask;
|
||
|
GtkStyle *style;
|
||
|
|
||
|
/* crea la finestra principale, e collega il segnale delete_event
|
||
|
alla terminazione dell'applicazione */
|
||
|
gtk_init( &argc, &argv );
|
||
|
window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
|
||
|
gtk_signal_connect( GTK_OBJECT (window), "delete_event",
|
||
|
GTK_SIGNAL_FUNC (close_application), NULL );
|
||
|
gtk_container_border_width( GTK_CONTAINER (window), 10 );
|
||
|
gtk_widget_show( window );
|
||
|
|
||
|
/* la pixmap proviene da gdk */
|
||
|
style = gtk_widget_get_style( window );
|
||
|
pixmap = gdk_pixmap_create_from_xpm_d( window->window, &mask,
|
||
|
&style->bg[GTK_STATE_NORMAL],
|
||
|
(gchar **)xpm_data );
|
||
|
|
||
|
/* un widget pixmap per contenere la pixmap */
|
||
|
pixmapwid = gtk_pixmap_new( pixmap, mask );
|
||
|
gtk_widget_show( pixmapwid );
|
||
|
|
||
|
/* un bottone per contenere il widget pixmap */
|
||
|
button = gtk_button_new();
|
||
|
gtk_container_add( GTK_CONTAINER(button), pixmapwid );
|
||
|
gtk_container_add( GTK_CONTAINER(window), button );
|
||
|
gtk_widget_show( button );
|
||
|
|
||
|
gtk_signal_connect( GTK_OBJECT(button), "clicked",
|
||
|
GTK_SIGNAL_FUNC(button_clicked), NULL );
|
||
|
|
||
|
/* mostra la finestra */
|
||
|
gtk_main ();
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
</verb></tscreen>
|
||
|
|
||
|
|
||
|
Per caricare una pixmap da un file XPM chiamato icon0.xpm che si trova
|
||
|
nella direttorio corrente, avremmo creato la pixmap in questo modo:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
/* carica una pixmap da un file */
|
||
|
pixmap = gdk_pixmap_create_from_xpm( window->window, &mask,
|
||
|
&style->bg[GTK_STATE_NORMAL],
|
||
|
"./icon0.xpm" );
|
||
|
pixmapwid = gtk_pixmap_new( pixmap, mask );
|
||
|
gtk_widget_show( pixmapwid );
|
||
|
gtk_container_add( GTK_CONTAINER(window), pixmapwid );
|
||
|
</verb></tscreen>
|
||
|
|
||
|
|
||
|
Usare le Sagome
|
||
|
<p>
|
||
|
Uno degli svantaggi di usare le pixmap è costituito dal fatto che l'oggetto
|
||
|
mostrato è sempre rettangolare, a prescindere dall'immagine. Ci piacerebbe
|
||
|
invece poter crare dei desktop e delle immagini con forme più naturali. Per
|
||
|
esempio, per l'interfaccia di un gioco, potremmo volere avere dei pulsanti
|
||
|
circolari. Il modo per ottenere questo effetto è di usare delle finestre
|
||
|
sagomate.
|
||
|
|
||
|
Una finestra sagomata è semplicemente una pixmap in cui i pixel dello
|
||
|
sfondo sono trasparenti. In questo modo, se l'immagine di sfondo è
|
||
|
multicolore, possiamo evitare di sovrascriverla con un bordo rettangolare
|
||
|
attorno all'icona. Il prossimo esempio mostra una carriola sul desktop.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
|
||
|
#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 ",
|
||
|
" ",
|
||
|
" "};
|
||
|
|
||
|
|
||
|
/* quando invocata (con il segnale delete_event), termina l'applicazione. */
|
||
|
void close_application( GtkWidget *widget, gpointer *data ) {
|
||
|
gtk_main_quit();
|
||
|
}
|
||
|
|
||
|
|
||
|
int main (int argc, char *argv[])
|
||
|
{
|
||
|
/* il tipo di dato per i widget è GtkWidget */
|
||
|
GtkWidget *window, *pixmap, *fixed;
|
||
|
GdkPixmap *gdk_pixmap;
|
||
|
GdkBitmap *mask;
|
||
|
GtkStyle *style;
|
||
|
GdkGC *gc;
|
||
|
|
||
|
/* crea la finestra principale e collega il segnale delete_event per
|
||
|
terminare l'applicazione. Notare che non mettiamo un titolo
|
||
|
alla finestra. */
|
||
|
gtk_init (&argc, &argv);
|
||
|
window = gtk_window_new( GTK_WINDOW_POPUP );
|
||
|
gtk_signal_connect (GTK_OBJECT (window), "delete_event",
|
||
|
GTK_SIGNAL_FUNC (close_application), NULL);
|
||
|
gtk_widget_show (window);
|
||
|
|
||
|
/* ora occupiamoci della pixmap e del widget pixmap */
|
||
|
style = gtk_widget_get_default_style();
|
||
|
gc = style->black_gc;
|
||
|
gdk_pixmap = gdk_pixmap_create_from_xpm_d( window->window, &mask,
|
||
|
&style->bg[GTK_STATE_NORMAL],
|
||
|
WheelbarrowFull_xpm );
|
||
|
pixmap = gtk_pixmap_new( gdk_pixmap, mask );
|
||
|
gtk_widget_show( pixmap );
|
||
|
|
||
|
/* Per mostrare la pixmap, usiamo un widget "fixed" in cui metterla */
|
||
|
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(window), fixed );
|
||
|
gtk_widget_show( fixed );
|
||
|
|
||
|
/* Questa maschera tutto tranne l'immagine stessa */
|
||
|
gtk_widget_shape_combine_mask( window, mask, 0, 0 );
|
||
|
|
||
|
/* mostra la finestra */
|
||
|
gtk_widget_set_uposition( window, 20, 400 );
|
||
|
gtk_widget_show( window );
|
||
|
gtk_main ();
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
</verb></tscreen>
|
||
|
<p>
|
||
|
Per rendere sensibile l'immagine della carriola, potremmo collegare
|
||
|
il segnale di pressione del bottone in modo che venga compiuta una certa
|
||
|
azione. Le prossime linee renderebbero l'immagine sensibile alla pressione
|
||
|
di un bottone del mouse che fa sì che l'applicazione termini.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
gtk_widget_set_events( window,
|
||
|
gtk_widget_get_events( window ) |
|
||
|
GDK_BUTTON_PRESS_MASK );
|
||
|
|
||
|
gtk_signal_connect( GTK_OBJECT(window), "button_press_event",
|
||
|
GTK_SIGNAL_FUNC(close_application), NULL );
|
||
|
</verb></tscreen>
|
||
|
|
||
|
|
||
|
<sect> Widget Contenitore
|
||
|
|
||
|
<sect1> Il widget Blocco Note (Notebook)
|
||
|
<p>
|
||
|
Il widget Blocco note è un insieme di pagine sovrapposte l'una con l'altra,
|
||
|
ognuna contente cose diverse. Questo widget è diventato molto comune nella
|
||
|
programmazione delle interfacce utente ed è un buon metodo per mostrare informazioni
|
||
|
tra loro correlate ma che debbano essere mostrate separatamente.
|
||
|
|
||
|
<p>
|
||
|
La prima funzione da invocare che si deve conoscere, come si può intuire, è usata
|
||
|
per creare un nuovo Blocco Note.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
GtkWidget* gtk_notebook_new (void);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Una volta che il notebook è sato creato, ci sono 12 funzioni che possono
|
||
|
operare sul widget notebook. Guardiamole individualmente.
|
||
|
|
||
|
La prima che vediamo riguarda come posizionare l'indicatore di pagina.
|
||
|
Questi inidicatori di pagina o ``linguette'' (come possono anche essere chiamati)
|
||
|
possono essere posizionati in quattro posti: alto, basso, sinistra.destra.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void gtk_notebook_set_tab_pos (GtkNotebook *notebook, GtkPositionType pos);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
GtkPositionType sarà uno dei seguenti valori (molto autoesplicativi)
|
||
|
<itemize>
|
||
|
<item> GTK_POS_LEFT
|
||
|
<item> GTK_POS_RIGHT
|
||
|
<item> GTK_POS_TOP
|
||
|
<item> GTK_POS_BOTTOM
|
||
|
</itemize>
|
||
|
|
||
|
GTK_POS_TOP e' il valore predefinito.
|
||
|
|
||
|
Ora vediamo come aggiugere le pagine al Blocco Note. Ci sono 3 modi per farlo. Diamo
|
||
|
un'occhiata ai primi due insieme, viste che sono molto simili.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void gtk_notebook_append_page (GtkNotebook *notebook, GtkWidget *child, GtkWidget *tab_label);
|
||
|
|
||
|
void gtk_notebook_prepend_page (GtkNotebook *notebook, GtkWidget *child, GtkWidget *tab_label);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Queste funzioni aggiungono pagine al notebook inserendole rispettivamente alla fine
|
||
|
(append) o all'inizio (prepend). *child è il widget che è posto nella pagina del
|
||
|
notebook e *tab_label e la intestazione della pagina stessa.
|
||
|
|
||
|
L'ultima funzione per aggiungere una pagina al notebook contiene tutte le proprietà
|
||
|
delle precedenti due, ma permette di specificare dove posizionare la pagina che
|
||
|
si vuole inserire.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void gtk_notebook_insert_page (GtkNotebook *notebook, GtkWidget *child, GtkWidget *tab_label, gint position);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
I parametri sono gli stessi di _append_ e _prepend_ tranne che per il parametro in
|
||
|
più: ``position''.
|
||
|
Questo parametro viene usato per specificare in che posizione ineserire la pagina.
|
||
|
|
||
|
Ora che conosciamo come aggiungere le pagine, vediamo come poter toglierne una.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void gtk_notebook_remove_page (GtkNotebook *notebook, gint page_num);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Questa funzione prende il numero della pagina specificata dal campo page_num e
|
||
|
rimuove la pagina corrispondente dal Blocco Note.
|
||
|
|
||
|
Per trovare qual'è la pagina corrente nel notebook bisogna usare la funzione:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
gint gtk_notebook_current_page (GtkNotebook *notebook);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Le prossime due funzioni sono semplicemente delle chiamate che muovono la pagina del
|
||
|
notebook avanti o indietro. Semplicemente forniscono le chiamate alle rispettive
|
||
|
funzioni del widget notebook su si può operare. NB: quando un notebook è
|
||
|
correntemente sull'ultima pagina e viene invocata la funzione gtk_notebook_next_page,
|
||
|
il notebook ritornerà automaticamente alla prima pagina. Logicamente succede anche
|
||
|
il contrario quando invochi gtk_notebook_prev_page e ti trovi sulla prima pagina.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void gtk_notebook_next_page (GtkNoteBook *notebook);
|
||
|
void gtk_notebook_prev_page (GtkNoteBook *notebook);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
La prossima funzione stabilisce la pagina ``attiva''. Se si vuole che la pagina
|
||
|
principale del notebook sia per esempio la 5 (ad esempio) si può usare questa
|
||
|
funzione.
|
||
|
Se non si usa questa funzione la pagina principale sarà la 1.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void gtk_notebook_set_page (GtkNotebook *notebook, gint page_num);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Le prossime due funzioni aggiungono o rimuovono, rispettivamente, le intestazioni e
|
||
|
i bordi delle pagine.
|
||
|
|
||
|
<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>
|
||
|
|
||
|
show_tabs e show_border posso avere come valore TRUE o FALSE (0 or 1).
|
||
|
|
||
|
Diamo ora una occhiata ad un esempio. Si tratta di una espansione del codice preso
|
||
|
dal file testgtk.c che è compreso in tutte le distribuzioni, e mostra
|
||
|
tutte le 13 funzioni. Questo piccolo programma crea una finestra con un notebook
|
||
|
e 6 bottoni. Il notebook contiene 11 pagine, aggiunte nei 3 modi differenti (alla
|
||
|
fine, all'inizio o in qualsiasi posizione). I bottoni permettono di girare le
|
||
|
intestazioni, aggiungere/rimuovere le intestazioni e i bordi, rimuovere una
|
||
|
pagina, cambiare la pagina avanti e indietro e uscire dal programma.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
|
||
|
#include <gtk/gtk.h>
|
||
|
|
||
|
/* Queta funzione ruota le posizione delle linguette delle pagine */
|
||
|
void rotate_book (GtkButton *button, GtkNotebook *notebook)
|
||
|
{
|
||
|
gtk_notebook_set_tab_pos (notebook, (notebook->tab_pos +1) %4);
|
||
|
}
|
||
|
|
||
|
/* Aggiunge e rimuove le linguette e i bordi */
|
||
|
void tabsborder_book (GtkButton *button, 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);
|
||
|
}
|
||
|
|
||
|
/* Rimuove una pagina */
|
||
|
void remove_book (GtkButton *button, GtkNotebook *notebook)
|
||
|
{
|
||
|
gint page;
|
||
|
|
||
|
page = gtk_notebook_current_page(notebook);
|
||
|
gtk_notebook_remove_page (notebook, page);
|
||
|
/* E' necessario fare un refresh del widget --
|
||
|
Questo forza il widget a ridisegnarsi. */
|
||
|
gtk_widget_draw(GTK_WIDGET(notebook), NULL);
|
||
|
}
|
||
|
|
||
|
void delete (GtkWidget *widget, gpointer *data)
|
||
|
{
|
||
|
gtk_main_quit ();
|
||
|
}
|
||
|
|
||
|
int main (int argc, char *argv[])
|
||
|
{
|
||
|
GtkWidget *window;
|
||
|
GtkWidget *button;
|
||
|
GtkWidget *table;
|
||
|
GtkWidget *notebook;
|
||
|
GtkWidget *frame;
|
||
|
GtkWidget *label;
|
||
|
GtkWidget *checkbutton;
|
||
|
int i;
|
||
|
char bufferf[32];
|
||
|
char bufferl[32];
|
||
|
|
||
|
gtk_init (&argc, &argv);
|
||
|
|
||
|
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
||
|
|
||
|
gtk_signal_connect (GTK_OBJECT (window), "destroy",
|
||
|
GTK_SIGNAL_FUNC (destroy), NULL);
|
||
|
|
||
|
gtk_container_border_width (GTK_CONTAINER (window), 10);
|
||
|
|
||
|
table = gtk_table_new(2,6,TRUE);
|
||
|
gtk_container_add (GTK_CONTAINER (window), table);
|
||
|
|
||
|
/* Crea un nuovo notebook, e tabilisce la posizione delle linguette */
|
||
|
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);
|
||
|
|
||
|
/* appende una parte delle pagine */
|
||
|
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);
|
||
|
|
||
|
label = gtk_label_new (bufferf);
|
||
|
gtk_container_add (GTK_CONTAINER (frame), label);
|
||
|
gtk_widget_show (label);
|
||
|
|
||
|
label = gtk_label_new (bufferl);
|
||
|
gtk_notebook_append_page (GTK_NOTEBOOK (notebook), frame, label);
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Ora aggiungiamo una pagina in una certa posizione */
|
||
|
checkbutton = gtk_check_button_new_with_label ("Check me please!");
|
||
|
gtk_widget_set_usize(checkbutton, 100, 75);
|
||
|
gtk_widget_show (checkbutton);
|
||
|
|
||
|
label = gtk_label_new ("Add spot");
|
||
|
gtk_container_add (GTK_CONTAINER (checkbutton), label);
|
||
|
gtk_widget_show (label);
|
||
|
label = gtk_label_new ("Add page");
|
||
|
gtk_notebook_insert_page (GTK_NOTEBOOK (notebook), checkbutton, label, 2);
|
||
|
|
||
|
/* Ora finalmente aggiungiamo le pagine all'inizio */
|
||
|
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);
|
||
|
|
||
|
label = gtk_label_new (bufferf);
|
||
|
gtk_container_add (GTK_CONTAINER (frame), label);
|
||
|
gtk_widget_show (label);
|
||
|
|
||
|
label = gtk_label_new (bufferl);
|
||
|
gtk_notebook_prepend_page (GTK_NOTEBOOK(notebook), frame, label);
|
||
|
}
|
||
|
|
||
|
/* Stabilisce quale sarà la prima pagina che sarà visualizzata. */
|
||
|
gtk_notebook_set_page (GTK_NOTEBOOK(notebook), 3);
|
||
|
|
||
|
|
||
|
/* Crea un set di bottoni */
|
||
|
button = gtk_button_new_with_label ("close");
|
||
|
gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
|
||
|
GTK_SIGNAL_FUNC (destroy), NULL);
|
||
|
gtk_table_attach_defaults(GTK_TABLE(table), button, 0,1,1,2);
|
||
|
gtk_widget_show(button);
|
||
|
|
||
|
button = gtk_button_new_with_label ("next page");
|
||
|
gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
|
||
|
(GtkSignalFunc) gtk_notebook_next_page,
|
||
|
GTK_OBJECT (notebook));
|
||
|
gtk_table_attach_defaults(GTK_TABLE(table), button, 1,2,1,2);
|
||
|
gtk_widget_show(button);
|
||
|
|
||
|
button = gtk_button_new_with_label ("prev page");
|
||
|
gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
|
||
|
(GtkSignalFunc) gtk_notebook_prev_page,
|
||
|
GTK_OBJECT (notebook));
|
||
|
gtk_table_attach_defaults(GTK_TABLE(table), button, 2,3,1,2);
|
||
|
gtk_widget_show(button);
|
||
|
|
||
|
button = gtk_button_new_with_label ("tab position");
|
||
|
gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
|
||
|
(GtkSignalFunc) rotate_book, GTK_OBJECT(notebook));
|
||
|
gtk_table_attach_defaults(GTK_TABLE(table), button, 3,4,1,2);
|
||
|
gtk_widget_show(button);
|
||
|
|
||
|
button = gtk_button_new_with_label ("tabs/border on/off");
|
||
|
gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
|
||
|
(GtkSignalFunc) tabsborder_book,
|
||
|
GTK_OBJECT (notebook));
|
||
|
gtk_table_attach_defaults(GTK_TABLE(table), button, 4,5,1,2);
|
||
|
gtk_widget_show(button);
|
||
|
|
||
|
button = gtk_button_new_with_label ("remove page");
|
||
|
gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
|
||
|
(GtkSignalFunc) remove_book,
|
||
|
GTK_OBJECT(notebook));
|
||
|
gtk_table_attach_defaults(GTK_TABLE(table), button, 5,6,1,2);
|
||
|
gtk_widget_show(button);
|
||
|
|
||
|
gtk_widget_show(table);
|
||
|
gtk_widget_show(window);
|
||
|
|
||
|
gtk_main ();
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
</verb></tscreen>
|
||
|
<p>
|
||
|
E speriamo che questo vi aiuti a creare i Blocco Note per le vostre applicazioni GTK!
|
||
|
|
||
|
<sect1> Finestre Scorribili (Scrolled Windows)
|
||
|
<p>
|
||
|
Le Finestre Scorribili sono usate per creare areee scorribili in una vera finestra.
|
||
|
Si può inserire qualsiasi tipo di widget in questo tipo di finestra, e possono poi
|
||
|
essere accessibili a prescindere dalle dimensioni usando le barre di scorrimento.
|
||
|
|
||
|
La funzione seguente è usata per creare una nuova scrolled window.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
GtkWidget* gtk_scrolled_window_new (GtkAdjustment *hadjustment,
|
||
|
GtkAdjustment *vadjustment);
|
||
|
</verb></tscreen>
|
||
|
<p>
|
||
|
Il primo argomento è l'aggiustamento (di quanto scendere ogni
|
||
|
volta) orizzontale e il secondo è quello verticale. A questi si assegna
|
||
|
quasi sempre il valore NULL.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void gtk_scrolled_window_set_policy (GtkScrolledWindow *scrolled_window,
|
||
|
GtkPolicyType hscrollbar_policy,
|
||
|
GtkPolicyType vscrollbar_policy);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Questa funzione stabilisce la politica da usare nella barra di scorrimento. Il primo
|
||
|
argomento è la finestra scorribile interessata. Il secondo stabilisce la politica
|
||
|
per la barra di scorrimento orizzontale e il terzo è quello per la politca verticale.
|
||
|
|
||
|
La politica può essere GTK_POLICY AUTOMATIC o GTK_POLICY_ALWAYS.
|
||
|
GTK_POLICY_AUTOMATIC decide automaticamente se la barra di scorrimento deve essere
|
||
|
visualizzata, mentre con GTK_POLICY_ALWAYS la barra verrà sempre mostrata.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
#include <gtk/gtk.h>
|
||
|
|
||
|
void destroy(GtkWidget *widget, gpointer *data)
|
||
|
{
|
||
|
gtk_main_quit();
|
||
|
}
|
||
|
|
||
|
int main (int argc, char *argv[])
|
||
|
{
|
||
|
static GtkWidget *window;
|
||
|
GtkWidget *scrolled_window;
|
||
|
GtkWidget *table;
|
||
|
GtkWidget *button;
|
||
|
char buffer[32];
|
||
|
int i, j;
|
||
|
|
||
|
gtk_init (&argc, &argv);
|
||
|
|
||
|
/* Crea una nuove finestra di dialogo in cui la scrolled window sarà
|
||
|
inserita. Una finestra di dialogo è semplicemente come una
|
||
|
finestra normale, ma ha anche un vbox e un separatore orizzontale
|
||
|
già inseriti per difetto. E'un modo semplice per
|
||
|
creare finestre di dialogo. */
|
||
|
window = gtk_dialog_new ();
|
||
|
gtk_signal_connect (GTK_OBJECT (window), "destroy",
|
||
|
(GtkSignalFunc) destroy, NULL);
|
||
|
gtk_window_set_title (GTK_WINDOW (window), "dialog");
|
||
|
gtk_container_border_width (GTK_CONTAINER (window), 0);
|
||
|
|
||
|
/* crea una nuova finestra scorribile. */
|
||
|
scrolled_window = gtk_scrolled_window_new (NULL, NULL);
|
||
|
|
||
|
gtk_container_border_width (GTK_CONTAINER (scrolled_window), 10);
|
||
|
|
||
|
/* la politica è GTK_POLICY AUTOMATIC per lo scorrimento orizzontale e
|
||
|
GTK_POLICY_ALWAYS per quello verticale. */
|
||
|
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
|
||
|
GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
|
||
|
|
||
|
/* La finestra di dialogo è creata con un vbox già inserito.*/
|
||
|
gtk_box_pack_start (GTK_BOX (GTK_DIALOG(window)->vbox), scrolled_window,
|
||
|
TRUE, TRUE, 0);
|
||
|
gtk_widget_show (scrolled_window);
|
||
|
|
||
|
/* crea una tablella di10 x 10. */
|
||
|
table = gtk_table_new (10, 10, FALSE);
|
||
|
|
||
|
/* setta lo spazio tra ogni cella di 10 pixel sia verticale sia orizzontale*/
|
||
|
gtk_table_set_row_spacings (GTK_TABLE (table), 10);
|
||
|
gtk_table_set_col_spacings (GTK_TABLE (table), 10);
|
||
|
|
||
|
/* inserisce la tabella nella finestra scorribile*/
|
||
|
gtk_container_add (GTK_CONTAINER (scrolled_window), table);
|
||
|
gtk_widget_show (table);
|
||
|
|
||
|
/* questo semplicemente crea una griglia di bottoni nella tabelle per
|
||
|
dimostrare il comportamento della finestra scorribile */
|
||
|
for (i = 0; i < 10; i++)
|
||
|
for (j = 0; j < 10; j++) {
|
||
|
sprintf (buffer, "button (%d,%d)\n", i, j);
|
||
|
button = gtk_toggle_button_new_with_label (buffer);
|
||
|
gtk_table_attach_defaults (GTK_TABLE (table), button,
|
||
|
i, i+1, j, j+1);
|
||
|
gtk_widget_show (button);
|
||
|
}
|
||
|
|
||
|
/* Aggiunge un bottone "close" alla fine della finestra */
|
||
|
button = gtk_button_new_with_label ("close");
|
||
|
gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
|
||
|
(GtkSignalFunc) gtk_widget_destroy,
|
||
|
GTK_OBJECT (window));
|
||
|
|
||
|
/* questo fa sì che questo bottone sia quello predefinito */
|
||
|
|
||
|
GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
|
||
|
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->action_area), button, TRUE, TRUE, 0);
|
||
|
|
||
|
/* Questo ottiene il bottone predefinito. Premendo semplicemente l'"enter" il
|
||
|
bottone si avvierà */
|
||
|
gtk_widget_grab_default (button);
|
||
|
gtk_widget_show (button);
|
||
|
|
||
|
gtk_widget_show (window);
|
||
|
|
||
|
gtk_main();
|
||
|
|
||
|
return(0);
|
||
|
}
|
||
|
</verb></tscreen>
|
||
|
<p>
|
||
|
Prova a giocare con il ridemensionamento della finestra. Noterete la reazione della
|
||
|
barra di scorrimento. Potete anche usare la funzione gtk_widget_set_usize() per
|
||
|
assegnare la dimensione predefinita della finestra o di un widget.
|
||
|
<!-- (ndMichel: questa chiamata non funziona per i bottoni!) -->
|
||
|
|
||
|
|
||
|
<sect> Il Widgets Lista
|
||
|
<p>
|
||
|
Il widget GtkList serve come contenitore verticale per altri widget che
|
||
|
devono essere di tipo GtkListItem.
|
||
|
|
||
|
Un widget GtkList possiede una sua propria finestra per ricevere eventi
|
||
|
e un suo proprio colore di sfondo che di solito è bianco. Dal momento
|
||
|
che è direttamente derivato dal widget GtkContainer, può essere trattato
|
||
|
come tale usando la macro GTK_CONTAINER(List); si veda il widget GtkContainer
|
||
|
per ulteriori dettagli.
|
||
|
Per usare il widget GtkList in tutte le sue potenzialità, si dovrebbe essere
|
||
|
già familiari con l'uso della GList e delle relative funzioni g_list_*().
|
||
|
|
||
|
All'interno della definizione della struttura del widget GtkList c'è un
|
||
|
campo che sarà per noi di grande interesse, cioè:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
struct _GtkList
|
||
|
{
|
||
|
...
|
||
|
GList *selection;
|
||
|
guint selection_mode;
|
||
|
...
|
||
|
};
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Il campo ``selection'' in un GtkList punta a una lista collegata di tutti
|
||
|
gli elementi che sono selezionati correntemente, oppure a NULL se la
|
||
|
selezione è vuota. Quindi, per avere informazioni sulla selezione corrente,
|
||
|
leggiamo il campo GTK_LIST()->selection, senza però modificarlo dal momento
|
||
|
che i campi interni debbono essere gestiti dalle funzioni gtk_list_*().
|
||
|
|
||
|
Le modalità di selezione in una GtkList, e quindi il contenuto di
|
||
|
GTK_LIST()->selection, sono determinate dal campo selection_mode:
|
||
|
|
||
|
selection_mode può assumere uno dei seguenti valori:
|
||
|
<itemize>
|
||
|
<item> GTK_SELECTION_SINGLE - La selezione può essere o NULL oppure
|
||
|
un puntatore GList* per un singolo elemento
|
||
|
selezionato.
|
||
|
|
||
|
<item> GTK_SELECTION_BROWSE - La selezione è null se la lista non contiene
|
||
|
alcun widget o se ha solo widget non sensibili,
|
||
|
oppure può contenere un puntatore a una struttura
|
||
|
GList, e quindi esattamente un elemento di lista.
|
||
|
|
||
|
<item> GTK_SELECTION_MULTIPLE - La selezione è ``NULL'' se non è selezionato
|
||
|
alcun elemento di lista, oppure un puntatore GList al
|
||
|
primo elemento selezionato. Quello, a sua volta, punta
|
||
|
a una struttura GList per il secondo elemento selezionato
|
||
|
e così via.
|
||
|
|
||
|
<item> GTK_SELECTION_EXTENDED - La selezione è sempre NULL.
|
||
|
</itemize>
|
||
|
<p>
|
||
|
Il valore per difetto è GTK_SELECTION_MULTIPLE.
|
||
|
|
||
|
<sect1> Segnali
|
||
|
<p>
|
||
|
<tscreen><verb>
|
||
|
void GtkList::selection_changed (GtkList *LIST)
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Questo segnale verrà invocato ogni volta che il campo di
|
||
|
selezione di una GtkList è cambiato. Questo accade quando
|
||
|
un figlio della GtkList viene selezionato o deselezionato.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void GtkList::select_child (GtkList *LIST, GtkWidget *CHILD)
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Questo segnale viene invocato quando un fuglio di una GtkList
|
||
|
sta per essere selezionato. Questo accade principalmente in
|
||
|
occasione di chiamate a gtk_list_select_item() e gtk_list_select_child(),
|
||
|
di pressioni di bottoni e a volte può venir fatto scattare indirettamente
|
||
|
in altre occasioni, in cui vengono aggiunti o rimossi dei figli
|
||
|
dalla GtkList.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void GtkList::unselect_child (GtkList *LIST, GtkWidget *CHILD)
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Questo segnale viene invocato quando un figlio della GtkList sta
|
||
|
per essere deselezionato. Ciò accade principalmente in occasione
|
||
|
di chiamate a gtk_list_unselect_item() e gtk_list_unselect_child(),
|
||
|
di pressioni di bottoni, e a volte può venir fatto scattare indirettamente
|
||
|
in altre occasioni, in cui vengono aggiunti o rimossi dei figli
|
||
|
dalla GtkList.
|
||
|
|
||
|
<sect1> Funzioni
|
||
|
<p>
|
||
|
<tscreen><verb>
|
||
|
guint gtk_list_get_type (void)
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Restituisce l'identificatore di tipo `GtkList'.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
GtkWidget* gtk_list_new (void)
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Crea un nuovo oggetto `GtkList'. Il nuovo widget viene
|
||
|
restituito sotto forma di un puntoatore ad un oggetto
|
||
|
`GtkWidgetì'. In caso di fallimento, viene ritornato NULL.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void gtk_list_insert_items (GtkList *LIST, GList *ITEMS, gint POSITION)
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Inserisce degli elementi di lista nella LIST, a partire da
|
||
|
POSITION. ITEMS ITEMS è una lista doppiamente collegata, in
|
||
|
cui ci si aspetta che i puntatori di ogni nodo puntino a
|
||
|
un GtkListItem appena creato. I nodi GList di ITEMS vengono
|
||
|
assunti dalla LIST.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void gtk_list_append_items (GtkList *LIST, GList *ITEMS)
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Inserisce elementi di lista proprio come gtk_list_insert_items(),
|
||
|
ma alla fine della LIST. I nodi GList di ITEMS vengono
|
||
|
assunti dalla LIST.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void gtk_list_prepend_items (GtkList *LIST, GList *ITEMS)
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Inserisce elementi di lista proprio come gtk_list_insert_items(),
|
||
|
ma al principio della LIST. I nodi GList di ITEMS vengono
|
||
|
assunti dalla LIST.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void gtk_list_remove_items (GtkList *LIST, GList *ITEMS)
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Rimuove degli elementi di lista dalla LIST. ITEMS è una lista
|
||
|
doppiamente collegata in cui ci si aspetta che i puntatori di
|
||
|
ogni nodo puntino a un figlio diretto di LIST. E' poi responsabilità
|
||
|
del chiamante di fare una chiamata a g_list_free(ITEMS). E' anche
|
||
|
necessario che il chiamante distrugga lui stesso gli elementi della
|
||
|
lista.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void gtk_list_clear_items (GtkList *LIST, gint START, gint END)
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Rimuove e distrugge elementi di lista da LIST. Un widget ne è
|
||
|
interessato se la sua posizione corrente all'interno di LIST è compreso
|
||
|
fra START ed END.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void gtk_list_select_item (GtkList *LIST, gint ITEM)
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Invoca il segnale GtkList::select_child per un elemento di lista
|
||
|
specificato dalla sua posizione corrente all'interno di LIST.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void gtk_list_unselect_item (GtkList *LIST, gint ITEM)
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Invoca il segnale GtkList::unselect_child per un elemento di lista
|
||
|
specificato dalla sua posizione corrente all'interno di LIST.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void gtk_list_select_child (GtkList *LIST, GtkWidget *CHILD)
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Invoca il segnale GtkList::select_child per uno specifico CHILD.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void gtk_list_unselect_child (GtkList *LIST, GtkWidget *CHILD)
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Invoca il segnale GtkList::unselect_child per uno specifico CHILD.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
gint gtk_list_child_position (GtkList *LIST, GtkWidget *CHILD)
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Restituisce la posizione di CHILD all'interno di LIST. In caso di fallimento,
|
||
|
viene restituito `-1'.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void gtk_list_set_selection_mode (GtkList *LIST, GtkSelectionMode MODE)
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Assegna a LIST il modo di selezione MODE, che può essere uno fra
|
||
|
GTK_SELECTION_SINGLE, GTK_SELECTION_BROWSE, GTK_SELECTION_MULTIPLE o
|
||
|
GTK_SELECTION_EXTENDED.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
GtkList* GTK_LIST (gpointer OBJ)
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Fa il cast di un generico puntatore a `GtkList*'. Per maggiori
|
||
|
informazioni vedere Standard Macros::.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
GtkListClass* GTK_LIST_CLASS (gpointer CLASS)
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Fa il cast di un generico puntatore a `GtkListClass*'. Per maggiori
|
||
|
informazioni vedere Standard Macros::.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
gint GTK_IS_LIST (gpointer OBJ)
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Determina se un generico puntatore si riferisce ad un oggetto `GtkList'.
|
||
|
Per maggiori informazioni vedere Standard Macros::.
|
||
|
|
||
|
|
||
|
<sect1> Esempio
|
||
|
<p>
|
||
|
Diamo di seguito un programma di esempio che stamperà i campbiamenti
|
||
|
della selezione di una GtkList, e vi lascia ``imprigionare'' gli elementi
|
||
|
di una lista selezionandoli con il pulsante destro del mouse:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
/* compilate questo programma con:
|
||
|
* $ gcc -I/usr/local/include/ -lgtk -lgdk -lglib -lX11 -lm -Wall main.c
|
||
|
*/
|
||
|
|
||
|
/* includiamo i file header di gtk+
|
||
|
* includiamo stdio.h, ne abbiamo bisogno per printf()
|
||
|
*/
|
||
|
#include <gtk/gtk.h>
|
||
|
#include <stdio.h>
|
||
|
|
||
|
/* Questa e' la nostra stringa di identificazione dei dati per assegnarli
|
||
|
* ad elementi di lista
|
||
|
*/
|
||
|
const gchar *list_item_data_key="list_item_data";
|
||
|
|
||
|
|
||
|
/* prototipi per i gestori di segnale che connetteremo
|
||
|
* al widget GtkList
|
||
|
*/
|
||
|
static void sigh_print_selection (GtkWidget *gtklist,
|
||
|
gpointer func_data);
|
||
|
static void sigh_button_event (GtkWidget *gtklist,
|
||
|
GdkEventButton *event,
|
||
|
GtkWidget *frame);
|
||
|
|
||
|
|
||
|
/* funzione main per predisporre l'interfaccia utente */
|
||
|
|
||
|
gint main (int argc, gchar *argv[])
|
||
|
{
|
||
|
GtkWidget *separator;
|
||
|
GtkWidget *window;
|
||
|
GtkWidget *vbox;
|
||
|
GtkWidget *scrolled_window;
|
||
|
GtkWidget *frame;
|
||
|
GtkWidget *gtklist;
|
||
|
GtkWidget *button;
|
||
|
GtkWidget *list_item;
|
||
|
GList *dlist;
|
||
|
guint i;
|
||
|
gchar buffer[64];
|
||
|
|
||
|
|
||
|
/* inizializza gtk+ (e di conseguenza gdk) */
|
||
|
|
||
|
gtk_init(&argc, &argv);
|
||
|
|
||
|
|
||
|
/* crea una finestra in cui mettere tutti i widget
|
||
|
* connette gtk_main_quit() al segnale "destroy" della finestra
|
||
|
* per gestire le richieste di chiusura finestra del window manager
|
||
|
*/
|
||
|
window=gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
||
|
gtk_window_set_title(GTK_WINDOW(window), "GtkList Example");
|
||
|
gtk_signal_connect(GTK_OBJECT(window),
|
||
|
"destroy",
|
||
|
GTK_SIGNAL_FUNC(gtk_main_quit),
|
||
|
NULL);
|
||
|
|
||
|
|
||
|
/* all'interno della finestra abbiamo bisogno di una scatola
|
||
|
* in cui mettere i widget verticalmente */
|
||
|
vbox=gtk_vbox_new(FALSE, 5);
|
||
|
gtk_container_border_width(GTK_CONTAINER(vbox), 5);
|
||
|
gtk_container_add(GTK_CONTAINER(window), vbox);
|
||
|
gtk_widget_show(vbox);
|
||
|
|
||
|
/* questa è la finestra scorribile in cui mettere il 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);
|
||
|
|
||
|
/* crea il widget GtkList
|
||
|
* connette il gestore di segnale sigh_print_selection()
|
||
|
* al segnale "selection_changed" della GtkList, per stampare
|
||
|
* gli elementi selezionati ogni volta che la selezione cambia
|
||
|
*/
|
||
|
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);
|
||
|
|
||
|
/* creiamo una "Prigione" (Prison) in cui mettere gli elementi di lista ;)
|
||
|
*/
|
||
|
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);
|
||
|
|
||
|
/* connette il gestore di segnale sigh_button_event() alla GtkList
|
||
|
* il quale gestira' l'"imprigionamento" degli elementi di lista
|
||
|
*/
|
||
|
gtk_signal_connect(GTK_OBJECT(gtklist),
|
||
|
"button_release_event",
|
||
|
GTK_SIGNAL_FUNC(sigh_button_event),
|
||
|
frame);
|
||
|
|
||
|
/* crea un separatore
|
||
|
*/
|
||
|
separator=gtk_hseparator_new();
|
||
|
gtk_container_add(GTK_CONTAINER(vbox), separator);
|
||
|
gtk_widget_show(separator);
|
||
|
|
||
|
/* infine creiamo un bottone e connettiamone il segnale "clicked"
|
||
|
* alla distruzione della finestra
|
||
|
*/
|
||
|
button=gtk_button_new_with_label("Close");
|
||
|
gtk_container_add(GTK_CONTAINER(vbox), button);
|
||
|
gtk_widget_show(button);
|
||
|
gtk_signal_connect_object(GTK_OBJECT(button),
|
||
|
"clicked",
|
||
|
GTK_SIGNAL_FUNC(gtk_widget_destroy),
|
||
|
GTK_OBJECT(window));
|
||
|
|
||
|
|
||
|
/* a questo punto creiamo 5 elementi di lista, ognuno con la
|
||
|
* propria etichetta, e li aggiungiamo alla GtkList usando
|
||
|
* gtk_container_add(). Inoltre, recuperiamo la stringa di testo
|
||
|
* dall'etichetta e la associamo, per ogni elemento, a
|
||
|
* list_item_data_key
|
||
|
*/
|
||
|
for (i=0; i<5; i++) {
|
||
|
GtkWidget *label;
|
||
|
gchar *string;
|
||
|
|
||
|
sprintf(buffer, "ListItemContainer with Label #%d", i);
|
||
|
label=gtk_label_new(buffer);
|
||
|
list_item=gtk_list_item_new();
|
||
|
gtk_container_add(GTK_CONTAINER(list_item), label);
|
||
|
gtk_widget_show(label);
|
||
|
gtk_container_add(GTK_CONTAINER(gtklist), list_item);
|
||
|
gtk_widget_show(list_item);
|
||
|
gtk_label_get(GTK_LABEL(label), &string);
|
||
|
gtk_object_set_data(GTK_OBJECT(list_item),
|
||
|
list_item_data_key,
|
||
|
string);
|
||
|
}
|
||
|
|
||
|
/* qui creiamo altre 5 etichette, questa volta usando
|
||
|
* per la creazione gtk_list_item_new_with_label().
|
||
|
* Non possiamo recuperare la stringa di testo dall'etichetta
|
||
|
* dal momento che non disponiamo di puntatori alle etichette,
|
||
|
* quindi associamo semplicemente il list_item_data_key di ogni
|
||
|
* elemento di lista con la medesima stringa di testo.
|
||
|
* Per aggiungere elementi di lista, li mettiamo tutti in una lista
|
||
|
* doppiamente collegata (GList), e quindi li aggiungiamo con una
|
||
|
* unica chiamata a gtk_list_append_items().
|
||
|
* Dal momento che usiamo g_list_prepend() per mettere gli elementi
|
||
|
* nella lista doppiamente collegata, il loro ordine sara' discendente
|
||
|
* (invece che ascendente come sarebbe se usassimo 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);
|
||
|
|
||
|
/* e finalmente vogliamo vedere la finestra, non e' vero? ;)
|
||
|
*/
|
||
|
gtk_widget_show(window);
|
||
|
|
||
|
/* lancia il ciclo principale di gtk
|
||
|
*/
|
||
|
gtk_main();
|
||
|
|
||
|
/* si arriva a questo punto dopo la chiamata di gtk_main_quit(),
|
||
|
* il che accade quando viene distrutta la finestra principale
|
||
|
*/
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* questo e' il gestore di segnale che e' stato connesso all'evento di
|
||
|
* pressione/rilascio del bottone della GtkList
|
||
|
*/
|
||
|
void
|
||
|
sigh_button_event (GtkWidget *gtklist,
|
||
|
GdkEventButton *event,
|
||
|
GtkWidget *frame)
|
||
|
{
|
||
|
/* facciamo qualcosa solo nel caso di rilascio del terzo bottone
|
||
|
* (quello piu' a destra)
|
||
|
*/
|
||
|
if (event->type==GDK_BUTTON_RELEASE &&
|
||
|
event->button==3) {
|
||
|
GList *dlist, *free_list;
|
||
|
GtkWidget *new_prisoner;
|
||
|
|
||
|
/* recuperiamo l'elemento di lista selezionato correntemente,
|
||
|
* che sara' il nostro prossimo prigioniero ;)
|
||
|
*/
|
||
|
dlist=GTK_LIST(gtklist)->selection;
|
||
|
if (dlist)
|
||
|
new_prisoner=GTK_WIDGET(dlist->data);
|
||
|
else
|
||
|
new_prisoner=NULL;
|
||
|
|
||
|
/* cerchiamo elementi di lista gia' imprigionati,
|
||
|
* li rimetteremo nella lista.
|
||
|
* Ricordare di liberare la lista doppiamente collegata
|
||
|
* che viene restituita da 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);
|
||
|
|
||
|
/* se abbiamo un nuovo prigioniero, lo rimuoviamo
|
||
|
* dalla GtkList e lo mettiamo nella cornice della
|
||
|
* "Prigione". Dobbiamo prima deselezionare l'elemento
|
||
|
*/
|
||
|
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);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* questo e' il gestore di segnaleche viene chiamato de la
|
||
|
* GtkList emette il segnale "selection_changed"
|
||
|
*/
|
||
|
void
|
||
|
sigh_print_selection (GtkWidget *gtklist,
|
||
|
gpointer func_data)
|
||
|
{
|
||
|
GList *dlist;
|
||
|
|
||
|
/* recuperiamo la lista doppiamente collegata degli
|
||
|
* elementi selezionati della GtkList, ricordate di
|
||
|
* trattarla come sola lettura
|
||
|
*/
|
||
|
dlist=GTK_LIST(gtklist)->selection;
|
||
|
|
||
|
/* se non ci sono elementi selezionati non c'e' altro da
|
||
|
* fare che dirlo all'utente
|
||
|
*/
|
||
|
if (!dlist) {
|
||
|
g_print("Selection cleared\n");
|
||
|
return;
|
||
|
}
|
||
|
/* ok, abbiamo una selezione e quindi lo scriviamo
|
||
|
*/
|
||
|
g_print("The selection is a ");
|
||
|
|
||
|
/* ottieniamo l'elemento di lista dalla lista doppiamente
|
||
|
* collegata e poi richiediamo i dati associati con
|
||
|
* list_item_data_key. Poi semplicemente li stampiamo
|
||
|
*/
|
||
|
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");
|
||
|
}
|
||
|
</verb></tscreen>
|
||
|
|
||
|
<sect1> Il Widget Elemento di Lista (List Item)
|
||
|
<p>
|
||
|
Il widget GtkListItem è progettato allo scopo di essere un contenitore
|
||
|
collegato ad un figlio, per fornire le funzioni per la selezione e deselezione
|
||
|
allo stesso modo in cui il widget GtkList ne ha bisogno per i propri figli.
|
||
|
|
||
|
Un GtkListItem ha la sua propria finestra per ricevere eventi, e ha il suo
|
||
|
proprio colore di sfondo, che di solito è bianco.
|
||
|
|
||
|
Dal momento che questo widget deriva direttamente da GtkItem, può essere
|
||
|
trattato come tale usando la macro GTK_ITEM(ListItem), vedere il widget
|
||
|
GtkItem per ulteriori informazioni.
|
||
|
Di solito un GtkListItem ha solo un'etichetta per identificare per esempio
|
||
|
un nome di file all'interno di una GtkList -- per cui viene fornita la
|
||
|
funzione appropriata gtk_list_item_new_with_label(). Si può ottenere lo
|
||
|
stesso effetto creando una GtkLabel da sola, assegnando al suo allineamento
|
||
|
i valori xalign=0 e yalign=0.5, aggiungendo successivamente un contenitore
|
||
|
alla GtkListItem.
|
||
|
|
||
|
Dal momento che non si è obbligati a mettere una GtkLabel, si può anche
|
||
|
aggiungere una GtkVBox una GtkArrow ecc. alla GtkListItem.
|
||
|
|
||
|
<sect1> Segnali
|
||
|
<p>
|
||
|
Un GtkListItem non crea alcun nuovo segnale di per se, ma eredita
|
||
|
i segnali di GtkItem. Per ulteriori informazioni, vedere GtkItem::.
|
||
|
|
||
|
|
||
|
<sect1> Funzioni
|
||
|
<p>
|
||
|
|
||
|
<tscreen><verb>
|
||
|
guint gtk_list_item_get_type (void)
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Restituisce l'identificatore di tipo `GtkListItem'.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
GtkWidget* gtk_list_item_new (void)
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Crea un nuovo oggetto `GtkListItem'. Il nuovo widget viene restituito
|
||
|
sottoforma di un puntatore ad un oggetto `GtkWidget'. In caso di
|
||
|
fallimento, viene restituito `NULL'.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
GtkWidget* gtk_list_item_new_with_label (gchar *LABEL)
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Cre un nuovo oggetto `GtkListItem', avente come unico figlio
|
||
|
un GtkLabel. Il nuovo widget viene restituito
|
||
|
sottoforma di un puntatore ad un oggetto `GtkWidget'. In caso di
|
||
|
fallimento, viene restituito `NULL'.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void gtk_list_item_select (GtkListItem *LIST_ITEM)
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Questa funzione è essenzialmente un wrapper per una chiamata a
|
||
|
gtk_item_select (GTK_ITEM (list_item)) che emetterà il segnale
|
||
|
GtkItem::select.
|
||
|
Vedere GtkItem:: per maggiori informazioni.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void gtk_list_item_deselect (GtkListItem *LIST_ITEM)
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Questa funzione è essenzialmente un wrapper per una chiamata a
|
||
|
gtk_item_deselect (GTK_ITEM (list_item)) che emetterà il segnale
|
||
|
GtkItem::deselect.
|
||
|
Vedere GtkItem:: per maggiori informazioni.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
GtkListItem* GTK_LIST_ITEM (gpointer OBJ)
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Effettua il cast di un puntatore generico a `GtkListItem*'. Vedere
|
||
|
Standard Macros:: per maggiorni informazioni.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
GtkListItemClass* GTK_LIST_ITEM_CLASS (gpointer CLASS)
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Effettua il cast di un puntatore generico a `GtkListItemClass*'. Vedere
|
||
|
Standard Macros:: per maggiorni informazioni.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
gint GTK_IS_LIST_ITEM (gpointer OBJ)
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Determina se un puntatore generico si riferisce ad un oggetto
|
||
|
`GtkListItem'. Vedere Standard Macros:: per maggiorni informazioni.
|
||
|
|
||
|
<sect1> Esempio
|
||
|
<p>
|
||
|
Come esempio su questo argomento, si veda quello relativo alla GtkList,
|
||
|
che riguarda anche l'uso del GtkListItem.
|
||
|
|
||
|
<sect> Selezione di File (File Selections)
|
||
|
<p>
|
||
|
|
||
|
Il widget Selezione di File è un modo rapido e semplice per mostrare una
|
||
|
finestra di dialogo `File'. Questa si presenta completa di bottoni Ok,
|
||
|
Cancel e Help, un buon modo per tagliare i tempi di programmazione.
|
||
|
|
||
|
Per creare una nuova finestra di selezione file usate:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
GtkWidget* gtk_file_selection_new (gchar *title);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Per assegnare il nome del file, ad esempio per predisporre una certa
|
||
|
directory o per dare un certo nome di file per difetto, usate la seguente
|
||
|
funzione:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void gtk_file_selection_set_filename (GtkFileSelection *filesel, gchar *filename);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Per recuperare il testo che l'utente ha inserito o che ha selezionato con
|
||
|
il mouse, si usa la funzione:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
gchar* gtk_file_selection_get_filename (GtkFileSelection *filesel);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Ci sono anche dei puntatori ai widget che sono contenuti all'interno
|
||
|
del widget di selezione file. Si tratta di:
|
||
|
|
||
|
<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>
|
||
|
|
||
|
Molto probabilmente potreste voler usare i puntatori a ok_button,
|
||
|
cancel_button e help_button per segnalarne l'uso.
|
||
|
|
||
|
Ecco un esempio rubato da testgtk.c, nodificato per essere eseguito da
|
||
|
solo. Come potrete vedere, non c'è molto più che la creazione di un
|
||
|
widget di selezione file. In questo esempio, il bottone Help non fa nulla
|
||
|
mentre è mostrato allo schermo, dal momento che non c'è alcun segnale
|
||
|
collegato con esso.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
#include <gtk/gtk.h>
|
||
|
|
||
|
/* Recupera il nome di file selezionato e stampalo a console */
|
||
|
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);
|
||
|
|
||
|
/* Crea un nuovo widget di selezione file */
|
||
|
filew = gtk_file_selection_new ("File selection");
|
||
|
|
||
|
gtk_signal_connect (GTK_OBJECT (filew), "destroy",
|
||
|
(GtkSignalFunc) destroy, &filew);
|
||
|
/* Connette ok_button alla funzione file_ok_sel */
|
||
|
gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (filew)->ok_button),
|
||
|
"clicked", (GtkSignalFunc) file_ok_sel, filew );
|
||
|
|
||
|
/* Connette cancel_button alla funzione di distruzione del widget */
|
||
|
gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION (filew)->cancel_button),
|
||
|
"clicked", (GtkSignalFunc) gtk_widget_destroy,
|
||
|
GTK_OBJECT (filew));
|
||
|
|
||
|
/* Preassegnamo un nome di file, come se stessimo dando un valore per difetto in
|
||
|
dialogo di tipo `` salva con nome '' */
|
||
|
gtk_file_selection_set_filename (GTK_FILE_SELECTION(filew),
|
||
|
"penguin.png");
|
||
|
|
||
|
gtk_widget_show(filew);
|
||
|
gtk_main ();
|
||
|
return 0;
|
||
|
}
|
||
|
</verb></tscreen>
|
||
|
|
||
|
<sect>Il Widget Menù (Menu Widgets)
|
||
|
<p>
|
||
|
Ci sono due modi per creare dei menù, quello facile e quello difficile.
|
||
|
Ognuno è più adatto per certe circostanze, ma di solito si può usare il
|
||
|
modo semplice, cioé menu_factory (la ``fabbrica dei menù''). Il modo
|
||
|
``difficile'' è di crearsi tutti i menù usando direttamente le chiamate.
|
||
|
Quello semplice è di usare le chiamate di tipo gtk_menu_factory. Anche se
|
||
|
è un modo molto più semplice, ci sono svantaggi e vantaggi per ciascuno
|
||
|
dei due approcci.
|
||
|
|
||
|
La menufactory è molto più semplice da usare e per aggiungere dei nuovi
|
||
|
menù, anche se scriversi un po' di funzioni per creare dei menù con il
|
||
|
metodo manuale può dare risultati molto migliori dal punto di vista
|
||
|
dell'usabilità. Con la menufactory, non è possibile mettere immagini o
|
||
|
segni '/' nei menù.
|
||
|
<p>
|
||
|
<sect1>Creazione Manuale di Menù
|
||
|
<p>
|
||
|
Seguendo la tradizionale arte dell'insegnamento, partiamo dal modo
|
||
|
difficile. <tt>:)</>
|
||
|
<p>
|
||
|
Diamo un'occhiata alle funzioni usate per creare dei menù.
|
||
|
Con questa prima funzione si crea un nuovo menù:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
GtkWidget *gtk_menu_bar_new()
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Questa funzione crea una nuova barra di menù. Per impacchettarla in una
|
||
|
finestra o si usa la funzione gtk_container_add, oppure, per impacchettarla
|
||
|
in una scatola, le funzioni box_pack - come con i bottoni.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
GtkWidget *gtk_menu_new();
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Questa funzione restituisce un puntatore ad un nuovo menù, non viene mai
|
||
|
realmente mostrato (con gtk_widget_show), serve solo per contenere gli
|
||
|
elementi del menù. Spero che il tutto risulti più chiaro quando dare
|
||
|
un'occhiata all'esempio più sotto.
|
||
|
<p>
|
||
|
Le prossime due chiamate sono usate per creare degli elementi che poi
|
||
|
vengono impacchettati nel menù.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
GtkWidget *gtk_menu_item_new()
|
||
|
</verb></tscreen>
|
||
|
|
||
|
e
|
||
|
|
||
|
<tscreen><verb>
|
||
|
GtkWidget *gtk_menu_item_new_with_label(const char *label)
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Queste chiamate sono usate per creare i menu che devono essere mostrati.
|
||
|
Ricordate la differenza che esiste fra un ``menù'' come quelli creati con
|
||
|
gtk_menu_new e un ``elemento di menù'' (menu item) come quelli creati con
|
||
|
la funzione creata con gtk_menu_item_new. L'elemento di menù sarà un bottone
|
||
|
vero e proprio con una azione associata, mentre un menù è solo un contenitore
|
||
|
che li raccoglie.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
gtk_menu_item_append()
|
||
|
|
||
|
gtk_menu_item_set_submenu()
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Le funzioni gtk_menu_item_new_with_label e gtk_menu_item_new si comportano esattamente come
|
||
|
vi aspettereste dopo aver visto le funzioni che riguardano i bottoni. La prima
|
||
|
crea un elemento di menù con un'etichetta già applicata, mentre la seconda crea
|
||
|
un nuovo elemento di menù vuoto.
|
||
|
<p>
|
||
|
Ecco i passi necessari per creare una barra di menù con i relativi menù collegati:
|
||
|
<itemize>
|
||
|
<item> Create un nuovo menù con gtk_menu_new()
|
||
|
<item> Create un elementoa di menù con using gtk_menu_item_new(). Questo rappresenta
|
||
|
la base del menù, e il testo che appare qui sarà sulla barra stessa.
|
||
|
<item> Usate delle chiamate multiple a gtk_menu_item_new() per ognuno degli
|
||
|
elementi che volete mettere nel vostro menù. Usate inoltre gtk_menu_item_append()
|
||
|
per mettere assieme ognuno di questi nuovo elementi. Si crea così una lista di
|
||
|
elementi di menù.
|
||
|
<item> Usate gtk_menu_item_set_submenu() per attaccare gli elementi di menù
|
||
|
creati all'elemento di menù base (quello creato nel secondo passaggio).
|
||
|
<item> Create una nuova barra di menù usando gtk_menu_bar_new. Questo passo
|
||
|
necessita di essere effettuato una sola volta quando si crea una serie di
|
||
|
menù su una serie di menù su una sola barra.
|
||
|
<item> Usate gtk_menu_bar_append per mettere il menù base sulla barra dei menù.
|
||
|
</itemize>
|
||
|
<p>
|
||
|
Creare un menù a comparsa è più o meno la stessa cosa. La differenza è che il
|
||
|
il menù non viene attivato ``automaticamente'' da una barra, bensì esplicitamente
|
||
|
con la chiamata alla funzione gtk_menu_popup() da un evento di pressione di
|
||
|
un pulsante.
|
||
|
Seguite questi passaggi:
|
||
|
<itemize>
|
||
|
<item>Create una funzione di gestione di un evento. Essa deve seguire il prototipo
|
||
|
<tscreen>
|
||
|
static gint handler(GtkWidget *widget, GdkEvent *event);
|
||
|
</tscreen>
|
||
|
e usare l'evento per scoprire dove il menu deve essere fatto comparire.
|
||
|
<item>Nel gestore di evento, se questo è la pressione di un bottone, trattate
|
||
|
<tt>event</tt> come l'evento relativo ad un bottone (cosa che in effetti è)
|
||
|
e usatelo come mostrato nel codice di esempio per passare informazioni a
|
||
|
gtk_menu_popup().
|
||
|
<item>Collegate il gestore di evento a un widget con
|
||
|
<tscreen>
|
||
|
gtk_signal_connect_object(GTK_OBJECT(widget), "event",
|
||
|
GTK_SIGNAL_FUNC (handler), GTK_OBJECT(menu));
|
||
|
</tscreen>
|
||
|
in cui <tt>widget</tt> è il widget a cui state effettuando il collegamento, e
|
||
|
<tt>handler</tt> è la funzione di gestione, mentre <tt>menu</tt> è un menù
|
||
|
creato con gtk_menu_new(). Quest'ultimo può essere un menù che viene anche
|
||
|
attivato da una barra di menù, come mostrato nel codice di esempio.
|
||
|
</itemize>
|
||
|
<p>
|
||
|
<sect1>Esempio di Menù Manuale
|
||
|
<p>
|
||
|
Per la teoria dovrebbe essere abbastanza. Diamo un'occhiata ad un esempio che
|
||
|
ci aiuti a chiarire le cose.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
|
||
|
#include <gtk/gtk.h>
|
||
|
|
||
|
static gint button_press (GtkWidget *, GdkEvent *);
|
||
|
static void menuitem_response (gchar *);
|
||
|
|
||
|
|
||
|
int main (int argc, char *argv[])
|
||
|
{
|
||
|
|
||
|
GtkWidget *window;
|
||
|
GtkWidget *menu;
|
||
|
GtkWidget *menu_bar;
|
||
|
GtkWidget *root_menu;
|
||
|
GtkWidget *menu_items;
|
||
|
GtkWidget *vbox;
|
||
|
GtkWidget *button;
|
||
|
char buf[128];
|
||
|
int i;
|
||
|
|
||
|
gtk_init (&argc, &argv);
|
||
|
|
||
|
/* crea una nuova finestra */
|
||
|
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
||
|
gtk_window_set_title(GTK_WINDOW (window), "GTK Menu Test");
|
||
|
gtk_signal_connect(GTK_OBJECT (window), "delete_event",
|
||
|
(GtkSignalFunc) gtk_exit, NULL);
|
||
|
|
||
|
/* Inizializziamo il menù, e ricordate: mai applicare
|
||
|
* gtk_show_widget() al widget menù!!
|
||
|
* Questo è il menù che contiene gli elementi, quello che
|
||
|
* spunta quando si fa click sul "Menù radice" nell'applicazione */
|
||
|
menu = gtk_menu_new();
|
||
|
|
||
|
/* Questo è il menù radice, e l'etichetta sarà il nome del menù che
|
||
|
* verrà mostrato sulla barra dei menù. Non ci sarà alcun gestore di
|
||
|
* segnale collegato, dal momento che non fa altro che mostrare il resto
|
||
|
* del menù quando viene premuto. */
|
||
|
root_menu = gtk_menu_item_new_with_label("Root Menu");
|
||
|
|
||
|
gtk_widget_show(root_menu);
|
||
|
|
||
|
/* Ora creiamo un ciclo che crea tre elementi di menu per "test-menu".
|
||
|
* Notete la chiamata a gtk_menu_append. In questo punto aggiungiamo una
|
||
|
* lista di elementi al nostro menù. Normalmente, dovremmo poi catturare
|
||
|
* il segnale di attivazione per ognuno degli elementi del menu, e creare
|
||
|
* una funzione di ritorno per ciascuno di essi, ma qui non li mettiamo per
|
||
|
* brevità. */
|
||
|
|
||
|
for(i = 0; i < 3; i++)
|
||
|
{
|
||
|
/* Copia i nomi in buf. */
|
||
|
sprintf(buf, "Test-undermenu - %d", i);
|
||
|
|
||
|
/* Crea un nuovo elemento di menù con un nome... */
|
||
|
menu_items = gtk_menu_item_new_with_label(buf);
|
||
|
|
||
|
/* ...e aggiungilo al menù. */
|
||
|
gtk_menu_append(GTK_MENU (menu), menu_items);
|
||
|
|
||
|
/* Fa qualcosa di interessante quando si seleziona l'elemento */
|
||
|
gtk_signal_connect_object(GTK_OBJECT(menu_items), "activate",
|
||
|
GTK_SIGNAL_FUNC(menuitem_response), (gpointer) g_strdup(buf));
|
||
|
|
||
|
/* Mostra il widget */
|
||
|
gtk_widget_show(menu_items);
|
||
|
}
|
||
|
|
||
|
/* Ora specifichiamo che vogliamo che il menù che abbiamo appena creato
|
||
|
* sia il menù radice *//
|
||
|
gtk_menu_item_set_submenu(GTK_MENU_ITEM (root_menu), menu);
|
||
|
|
||
|
/* Una vbox in cui mettere un menù ed un bottone: */
|
||
|
vbox = gtk_vbox_new(FALSE, 0);
|
||
|
gtk_container_add(GTK_CONTAINER(window), vbox);
|
||
|
gtk_widget_show(vbox);
|
||
|
|
||
|
/* Crea una barra dei menù per metterci i menù e l'aggiunge alla finestra principale */
|
||
|
menu_bar = gtk_menu_bar_new();
|
||
|
gtk_box_pack_start(GTK_BOX(vbox), menu_bar, FALSE, FALSE, 2);
|
||
|
gtk_widget_show(menu_bar);
|
||
|
|
||
|
/* Crea un bottone a cui collegare un menù */
|
||
|
button = gtk_button_new_with_label("press me");
|
||
|
gtk_signal_connect_object(GTK_OBJECT(button), "event",
|
||
|
GTK_SIGNAL_FUNC (button_press), GTK_OBJECT(menu));
|
||
|
gtk_box_pack_end(GTK_BOX(vbox), button, TRUE, TRUE, 2);
|
||
|
gtk_widget_show(button);
|
||
|
|
||
|
/* E finalmente attacchiamo l'elemento di menù alla barra dei menù -- questo
|
||
|
* è l'elemento di menù "radice" di cui parlavo */
|
||
|
gtk_menu_bar_append(GTK_MENU_BAR (menu_bar), root_menu);
|
||
|
|
||
|
/* La finestra va mostrata sempre come ultimo passo in modo che sia già
|
||
|
* completa di tutti i suoi elementi. */
|
||
|
gtk_widget_show(window);
|
||
|
|
||
|
gtk_main ();
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/* Risponde alla pressione di un bottone impostando un menù che
|
||
|
* viene passato come widget.
|
||
|
* Notate che l'argomento "widget" si riferisce al menù impostato
|
||
|
* e NON al bottone premuto.
|
||
|
*/
|
||
|
|
||
|
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);
|
||
|
/* Riferisce al codice chiamante che abbiamo trattato l'evento;
|
||
|
* la faccenda finisce qui. */
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
/* Riferisce al codice chiamante che abbiamo trattato l'evento; passa avanti. */
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Stampa una stringa quando viene selezionato un elemento di menù */
|
||
|
|
||
|
static void menuitem_response (gchar *string)
|
||
|
{
|
||
|
printf("%s\n", string);
|
||
|
}
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Si può anche fare in modo che un elemento di menù sia insensibile e, usando
|
||
|
una tabella di acelleratori, collegare dei tasti a delle funzioni di menù.
|
||
|
<p>
|
||
|
<sect1>Usare GtkMenuFactory
|
||
|
<p>
|
||
|
Ora che vi abbiamo mostrato il modo difficile, ecco invece come si fa usando
|
||
|
le chiamate di gtk_menu_factory.
|
||
|
<p>
|
||
|
<sect1>Esempio di Menu Factory
|
||
|
<p>
|
||
|
Ecco un esempio di utilizzo della ``Fabbrica'' di Menù di GTK (Menu Factory).
|
||
|
Questo è il primo file, menus.h. Teniemo dei file menus.c e main.c separati
|
||
|
a causa delle variabili globali usate nel file menus.c.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
#ifndef __MENUS_H__
|
||
|
#define __MENUS_H__
|
||
|
|
||
|
#ifdef __cplusplus
|
||
|
extern "C" {
|
||
|
#endif /* __cplusplus */
|
||
|
|
||
|
void get_main_menu (GtkWidget **menubar, GtkAcceleratorTable **table);
|
||
|
void menus_create(GtkMenuEntry *entries, int nmenu_entries);
|
||
|
|
||
|
#ifdef __cplusplus
|
||
|
}
|
||
|
#endif /* __cplusplus */
|
||
|
|
||
|
#endif /* __MENUS_H__ */
|
||
|
</verb></tscreen>
|
||
|
<p>
|
||
|
Ed ecco il file menus.c.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
|
||
|
#include <gtk/gtk.h>
|
||
|
#include <strings.h>
|
||
|
|
||
|
#include "main.h"
|
||
|
|
||
|
|
||
|
static void menus_remove_accel(GtkWidget * widget, gchar * signal_name, gchar * path);
|
||
|
static gint menus_install_accel(GtkWidget * widget, gchar * signal_name, gchar key, gchar modifiers, gchar * path);
|
||
|
void menus_init(void);
|
||
|
void menus_create(GtkMenuEntry * entries, int nmenu_entries);
|
||
|
|
||
|
/* Questa è la struttuta GtkMenuEntry, che viene usata per creare dei nuovi
|
||
|
* menù. Il primo membro à la stringa di definizione del menù. Il secondo
|
||
|
* è il tasto acceleratore predefinito, usato per accedere a questa funzione
|
||
|
* con la tastiera. Il terzo è la funzione di ritorno che viene chiamata
|
||
|
* quando si seleziona con la tastiera o il mouse questo elemento di menù.
|
||
|
* L'ultimo membro costituisce il dato che viene passato alla funzione di
|
||
|
* ritorno. */
|
||
|
|
||
|
static GtkMenuEntry menu_items[] =
|
||
|
{
|
||
|
{"<Main>/File/New", "<control>N", NULL, NULL},
|
||
|
{"<Main>/File/Open", "<control>O", NULL, NULL},
|
||
|
{"<Main>/File/Save", "<control>S", NULL, NULL},
|
||
|
{"<Main>/File/Save as", NULL, NULL, NULL},
|
||
|
{"<Main>/File/<separator>", NULL, NULL, NULL},
|
||
|
{"<Main>/File/Quit", "<control>Q", file_quit_cmd_callback, "OK, I'll quit"},
|
||
|
{"<Main>/Options/Test", NULL, NULL, NULL}
|
||
|
};
|
||
|
|
||
|
/* calculail numero di menu_item */
|
||
|
static int nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
|
||
|
|
||
|
static int initialize = TRUE;
|
||
|
static GtkMenuFactory *factory = NULL;
|
||
|
static GtkMenuFactory *subfactory[1];
|
||
|
static GHashTable *entry_ht = NULL;
|
||
|
|
||
|
void get_main_menu(GtkWidget ** menubar, GtkAcceleratorTable ** table)
|
||
|
{
|
||
|
if (initialize)
|
||
|
menus_init();
|
||
|
|
||
|
if (menubar)
|
||
|
*menubar = subfactory[0]->widget;
|
||
|
if (table)
|
||
|
*table = subfactory[0]->table;
|
||
|
}
|
||
|
|
||
|
void menus_init(void)
|
||
|
{
|
||
|
if (initialize) {
|
||
|
initialize = FALSE;
|
||
|
|
||
|
factory = gtk_menu_factory_new(GTK_MENU_FACTORY_MENU_BAR);
|
||
|
subfactory[0] = gtk_menu_factory_new(GTK_MENU_FACTORY_MENU_BAR);
|
||
|
|
||
|
gtk_menu_factory_add_subfactory(factory, subfactory[0], "<Main>");
|
||
|
menus_create(menu_items, nmenu_items);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void menus_create(GtkMenuEntry * entries, int nmenu_entries)
|
||
|
{
|
||
|
char *accelerator;
|
||
|
int i;
|
||
|
|
||
|
if (initialize)
|
||
|
menus_init();
|
||
|
|
||
|
if (entry_ht)
|
||
|
for (i = 0; i < nmenu_entries; i++) {
|
||
|
accelerator = g_hash_table_lookup(entry_ht, entries[i].path);
|
||
|
if (accelerator) {
|
||
|
if (accelerator[0] == '\0')
|
||
|
entries[i].accelerator = NULL;
|
||
|
else
|
||
|
entries[i].accelerator = accelerator;
|
||
|
}
|
||
|
}
|
||
|
gtk_menu_factory_add_entries(factory, entries, nmenu_entries);
|
||
|
|
||
|
for (i = 0; i < nmenu_entries; i++)
|
||
|
if (entries[i].widget) {
|
||
|
gtk_signal_connect(GTK_OBJECT(entries[i].widget), "install_accelerator",
|
||
|
(GtkSignalFunc) menus_install_accel,
|
||
|
entries[i].path);
|
||
|
gtk_signal_connect(GTK_OBJECT(entries[i].widget), "remove_accelerator",
|
||
|
(GtkSignalFunc) menus_remove_accel,
|
||
|
entries[i].path);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static gint menus_install_accel(GtkWidget * widget, gchar * signal_name, gchar key, gchar modifiers, gchar * path)
|
||
|
{
|
||
|
char accel[64];
|
||
|
char *t1, t2[2];
|
||
|
|
||
|
accel[0] = '\0';
|
||
|
if (modifiers & GDK_CONTROL_MASK)
|
||
|
strcat(accel, "<control>");
|
||
|
if (modifiers & GDK_SHIFT_MASK)
|
||
|
strcat(accel, "<shift>");
|
||
|
if (modifiers & GDK_MOD1_MASK)
|
||
|
strcat(accel, "<alt>");
|
||
|
|
||
|
t2[0] = key;
|
||
|
t2[1] = '\0';
|
||
|
strcat(accel, t2);
|
||
|
|
||
|
if (entry_ht) {
|
||
|
t1 = g_hash_table_lookup(entry_ht, path);
|
||
|
g_free(t1);
|
||
|
} else
|
||
|
entry_ht = g_hash_table_new(g_string_hash, g_string_equal);
|
||
|
|
||
|
g_hash_table_insert(entry_ht, path, g_strdup(accel));
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
static void menus_remove_accel(GtkWidget * widget, gchar * signal_name, gchar * path)
|
||
|
{
|
||
|
char *t;
|
||
|
|
||
|
if (entry_ht) {
|
||
|
t = g_hash_table_lookup(entry_ht, path);
|
||
|
g_free(t);
|
||
|
|
||
|
g_hash_table_insert(entry_ht, path, g_strdup(""));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void menus_set_sensitive(char *path, int sensitive)
|
||
|
{
|
||
|
GtkMenuPath *menu_path;
|
||
|
|
||
|
if (initialize)
|
||
|
menus_init();
|
||
|
|
||
|
menu_path = gtk_menu_factory_find(factory, path);
|
||
|
if (menu_path)
|
||
|
gtk_widget_set_sensitive(menu_path->widget, sensitive);
|
||
|
else
|
||
|
g_warning("Impossibile assegnare sensibilità a menù inesistente: %s", path);
|
||
|
}
|
||
|
|
||
|
</verb></tscreen>
|
||
|
<p>
|
||
|
Ed ecco main.h
|
||
|
|
||
|
<tscreen><verb>
|
||
|
#ifndef __MAIN_H__
|
||
|
#define __MAIN_H__
|
||
|
|
||
|
|
||
|
#ifdef __cplusplus
|
||
|
extern "C" {
|
||
|
#endif /* __cplusplus */
|
||
|
|
||
|
void file_quit_cmd_callback(GtkWidget *widget, gpointer data);
|
||
|
|
||
|
#ifdef __cplusplus
|
||
|
}
|
||
|
#endif /* __cplusplus */
|
||
|
|
||
|
#endif /* __MAIN_H__ */
|
||
|
|
||
|
</verb></tscreen>
|
||
|
<p>
|
||
|
E main.c
|
||
|
|
||
|
<tscreen><verb>
|
||
|
#include <gtk/gtk.h>
|
||
|
|
||
|
#include "main.h"
|
||
|
#include "menus.h"
|
||
|
|
||
|
|
||
|
int main(int argc, char *argv[])
|
||
|
{
|
||
|
GtkWidget *window;
|
||
|
GtkWidget *main_vbox;
|
||
|
GtkWidget *menubar;
|
||
|
|
||
|
GtkAcceleratorTable *accel;
|
||
|
|
||
|
gtk_init(&argc, &argv);
|
||
|
|
||
|
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
||
|
gtk_signal_connect(GTK_OBJECT(window), "destroy",
|
||
|
GTK_SIGNAL_FUNC(file_quit_cmd_callback),
|
||
|
"WM destroy");
|
||
|
gtk_window_set_title(GTK_WINDOW(window), "Menu Factory");
|
||
|
gtk_widget_set_usize(GTK_WIDGET(window), 300, 200);
|
||
|
|
||
|
main_vbox = gtk_vbox_new(FALSE, 1);
|
||
|
gtk_container_border_width(GTK_CONTAINER(main_vbox), 1);
|
||
|
gtk_container_add(GTK_CONTAINER(window), main_vbox);
|
||
|
gtk_widget_show(main_vbox);
|
||
|
|
||
|
get_main_menu(&menubar, &accel);
|
||
|
gtk_window_add_accelerator_table(GTK_WINDOW(window), accel);
|
||
|
gtk_box_pack_start(GTK_BOX(main_vbox), menubar, FALSE, TRUE, 0);
|
||
|
gtk_widget_show(menubar);
|
||
|
|
||
|
gtk_widget_show(window);
|
||
|
gtk_main();
|
||
|
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
/* Questo è per mostrare come si usano le funzioni di ritorno quando
|
||
|
* si utilizza la MenuFactory. Spesso, si mettono tutte le funzioni di
|
||
|
* callback in un file separato, e le si fanno chiamare le funzioni
|
||
|
* appropriate da lì. Così le cose sono più organizzate. */
|
||
|
void file_quit_cmd_callback (GtkWidget *widget, gpointer data)
|
||
|
{
|
||
|
g_print ("%s\n", (char *) data);
|
||
|
gtk_exit(0);
|
||
|
}
|
||
|
</verb></tscreen>
|
||
|
<p>
|
||
|
Ed infine un bel makefile per semplificare la compilazione.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
CC = gcc
|
||
|
PROF = -g
|
||
|
C_FLAGS = -Wall $(PROF) -L/usr/local/include -DDEBUG
|
||
|
L_FLAGS = $(PROF) -L/usr/X11R6/lib -L/usr/local/lib
|
||
|
L_POSTFLAGS = -lgtk -lgdk -lglib -lXext -lX11 -lm
|
||
|
PROGNAME = at
|
||
|
|
||
|
O_FILES = menus.o main.o
|
||
|
|
||
|
$(PROGNAME): $(O_FILES)
|
||
|
rm -f $(PROGNAME)
|
||
|
$(CC) $(L_FLAGS) -o $(PROGNAME) $(O_FILES) $(L_POSTFLAGS)
|
||
|
|
||
|
.c.o:
|
||
|
$(CC) -c $(C_FLAGS) $<
|
||
|
|
||
|
clean:
|
||
|
rm -f core *.o $(PROGNAME) nohup.out
|
||
|
distclean: clean
|
||
|
rm -f *~
|
||
|
</verb></tscreen>
|
||
|
<p>
|
||
|
Per il momento, accontentatevi di questo esempio. Più avanti aggiungeremo
|
||
|
una spiegazione ed un bel po' di commenti.
|
||
|
|
||
|
|
||
|
<sect> Widget non documentati
|
||
|
<p>
|
||
|
Per questi sarebbe utile il contributo degli autori! :) Prendete in
|
||
|
considerazione la possibilità di contribuire al nostro tutorial.
|
||
|
|
||
|
Se dovete usare uno di questi widget non documentati, vi suggeriamo
|
||
|
caldamente di dare un'occhiata ai loro rispettivi file header nella
|
||
|
distribuzione di GTK. I nomi delle funzioni di GTK sono molto descrittivi.
|
||
|
Non appena si capisce come funzionano le cose, non è
|
||
|
difficile dedurre il modo d'uso di un widget semplicemente guardando la
|
||
|
dichiarazione di funzione ad esso associata. Aggiungendo a questo qualche
|
||
|
spunto tratto dal codice di altri non dovrebbero esserci problemi.
|
||
|
|
||
|
Quando avrete raggiunto una comprensione globale di tutte le funzioni
|
||
|
di un widget non documentato, considerate la possibilità di scrivere
|
||
|
un tutorial su di esso, in modo che altri possano beneficiare del
|
||
|
vostro lavoro.
|
||
|
|
||
|
<sect1> Ingressi di testo (Text Entries)
|
||
|
<p>
|
||
|
|
||
|
<sect1> Selezioni di colore (Color Selections)
|
||
|
<p>
|
||
|
|
||
|
<sect1> Controlli di intervallo (Range Controls)
|
||
|
<p>
|
||
|
|
||
|
<sect1> Righelli (Rulers)
|
||
|
<p>
|
||
|
|
||
|
<sect1> Caselle di testo (Text Boxes)
|
||
|
<p>
|
||
|
|
||
|
<sect1> Anteprime
|
||
|
<p>
|
||
|
|
||
|
(Potrebbe essere necessario riscrivere questa parte per conformarsi allo stile
|
||
|
del resto del tutorial)
|
||
|
|
||
|
<p>
|
||
|
Le anteprime servono a un certo numero di cose in GIMP/GTK. La più
|
||
|
importante è questa: a risoluzioni molto alte le immagini possono
|
||
|
facilmente occupare diverse decine di megabyte di memoria; ogni operazione
|
||
|
su immagini così grosse può richiedere molto tempo. Se per la
|
||
|
scelta di una data modifica vi occorrono 5-10 tentativi (cioè 10-20
|
||
|
passi, poiché è necessario ripristinare l'originale se si
|
||
|
è commesso un errore), possono volerci letteralmente delle ore per
|
||
|
fare quella giusta - se non si rimane a corto di memoria prima! Coloro che
|
||
|
hanno passato ore in camera oscura conoscono la sensazione. In questi casi
|
||
|
le anteprime sono utilissime!
|
||
|
|
||
|
Ma la seccatura dell'attesa non è l'unico caso. Spesso è utile
|
||
|
confrontare la versione precedente con la successiva affiancandole, o almeno
|
||
|
alternandole. Se si sta lavorando con grandi immagini e ritardi di una decina
|
||
|
di secondi un confronto efficace è quantomeno difficile da fare.
|
||
|
Per immagini di 30 mega (4 pollici per 6 pollici, 600 punti per pollice, 24 bit)
|
||
|
tale confronto risulta impraticabile per la maggior parte degli utenti. In
|
||
|
questo caso le anteprime sono di grande aiuto!
|
||
|
|
||
|
Ma c'è di più. Con le anteprime è possibile scrivere
|
||
|
plug-in per ottenere addirittura anteprime di anteprime (per esempio, la
|
||
|
simulazione del pacchetto di filtri). Questi plug-in possono così
|
||
|
fornire un certo numero di anticipazioni di quel che si otterrebbe applicando
|
||
|
certe opzioni. Un simile approccio funziona come una tavolozza di anteprime,
|
||
|
ed è molto efficace per piccoli cambiamenti!
|
||
|
|
||
|
Non è finita. Per alcuni plug-in può essere necessario un
|
||
|
intervento umano in tempo reale specifico per ogni immagine. Nel plug-in
|
||
|
SuperNova, ad esempio, vengono chieste le coordinate del centro della
|
||
|
futura supernova. Il modo più semplice per fare questo è
|
||
|
senza dubbio quello di mostrare un'anteprima all'utente chiedendogli di
|
||
|
selezionare interattivamente il centro.
|
||
|
|
||
|
Infine, un paio di applicazioni tipiche. Le anteprime possono essere usate
|
||
|
anche quando non si sta lavorando con grandi immagini. Per esempio, sono
|
||
|
utili quando si stanno calcolando dei pattern complicati (date un'occhiata
|
||
|
al venerabile plug in ``Diffraction'' e a molti altri!). Altro esempio:
|
||
|
date un'occhiata al plug-in di rotazione della mappa dei colori (in allestimento).
|
||
|
Le anteprime possono anche essere usate per visualizzare in un plug-in
|
||
|
piccoli logo o, addirittura, l'immagine dell'Autore!
|
||
|
|
||
|
Quando non usare le anteprime
|
||
|
|
||
|
Le anteprime non vanno usate per grafici, disegni ecc., poiché per
|
||
|
queste cose GDK è molto più veloce. Le anteprime vanno usate
|
||
|
solo per immagini derivate da un'elaborazione!
|
||
|
|
||
|
Le anteprime possono essere inserite dappertutto. In un vbox, in un hbox,
|
||
|
in una tabella, in un bottone, ecc. Sicuramente però hanno il loro
|
||
|
look migliore se bordate con delle cornici (frame). Le anteprime non hanno
|
||
|
bordi propri e appaiono piatte senza (naturalmente, se quel che si vuole
|
||
|
è proprio un aspetto piatto...). I bordi possono essere creati con
|
||
|
delle cornici.
|
||
|
|
||
|
[Image][Image]
|
||
|
|
||
|
Le anteprime sono per molti aspetti simili agli altri widget in GTK (con
|
||
|
tutto ciò che questo implica), con l'eccezione di avere una
|
||
|
caratteristica in più: è necessario che siano riempite con
|
||
|
qualche tipo di immagine! Inizialmente parleremo solo dell'aspetto GTK
|
||
|
delle anteprime e successivamente discuteremo di come riempirle.
|
||
|
|
||
|
Semplicemente:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
/* Crea un widget di anteprima,
|
||
|
inizializzane le dimensioni
|
||
|
e visualizzalo */
|
||
|
GtkWidget *preview;
|
||
|
preview=gtk_preview_new(GTK_PREVIEW_COLOR)
|
||
|
/* Alternativamente:
|
||
|
GTK_PREVIEW_GRAYSCALE);*/
|
||
|
gtk_preview_size (GTK_PREVIEW (preview), WIDTH, HEIGHT);
|
||
|
gtk_widget_show(preview);
|
||
|
my_preview_rendering_function(preview);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Come già detto, le anteprime hanno un buon aspetto dentro le cornici,
|
||
|
quindi:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
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_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;
|
||
|
}
|
||
|
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Questa è una semplice anteprima. Questa funzione restituisce la cornice
|
||
|
``madre'', in modo che sia possibile metterla in qualche altro posto nella vostra
|
||
|
interfaccia. Naturalmente è possibile passare alla routine la cornice
|
||
|
madre come parametro. In molte situazioni, comunque, il contenuto di un'anteprima
|
||
|
viene aggiornato continuamente dall'applicazione; in questi casi potreste
|
||
|
preferire passare alla funzione ``create_a_preview()'' un puntatore
|
||
|
all'anteprima, ottenendone così il controllo dopo.
|
||
|
|
||
|
Un'avvertimento più importante che potrebbe un giorno risparmiarvi
|
||
|
tanto tempo perso: a volte è preferibile etichettare le anteprime;
|
||
|
ad esempio, è possibile etichettare l'anteprima contenente l'immagine
|
||
|
originale come ``Originale'' e quella contenente l'immagine modificata come
|
||
|
``Modificata''. Potrebbe capitarvi di impacchettare in un vbox l'anteprima
|
||
|
insieme con l'etichetta associata. L'insidia inattesa sta nel fatto che se
|
||
|
l'etichetta è più ampia dell'anteprima (cosa che può
|
||
|
accadere per una varietà di motivi da voi non prevedibili, come il
|
||
|
fatto che la dimensione dell'anteprima viene decisa dinamicamente, o la
|
||
|
dimensione del font), la cornice si espande e non risulta più
|
||
|
perfettamente aderente all'anteprima. Questo stesso problema probabilmente
|
||
|
può verificarsi anche in altre situazioni.
|
||
|
|
||
|
[Image]
|
||
|
|
||
|
La soluzione è quella di mettere l'anteprima e l'etichetta in una
|
||
|
tabella 2x1 e di legarle insieme chiamando la funzione gtk_table_attach con
|
||
|
i seguenti parametri (questa è una delle varianti possibili,
|
||
|
naturalmente; l'importante è che non ci sia GTK_FILL nella seconda
|
||
|
gtk_table_attach):
|
||
|
|
||
|
<tscreen><verb>
|
||
|
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);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Ed ecco il risultato:
|
||
|
|
||
|
[Image]
|
||
|
|
||
|
Altri suggerimenti
|
||
|
|
||
|
La maniera più semplice per rendere cliccabile un'anteprima è
|
||
|
quella di metterla dentro un bottone. Questo ha anche l'effetto di aggiungere
|
||
|
un bel bordo attorno all'anteprima, il che rende superfluo metterla in una
|
||
|
cornice.
|
||
|
|
||
|
Questo è tutto per quel che riguarda GTK.
|
||
|
|
||
|
|
||
|
Completare un'anteprima
|
||
|
|
||
|
Per impratichirci con le basi del completamento delle anteprime, creiamo
|
||
|
il seguente disegno (trovato per tentativi):
|
||
|
|
||
|
[Image]
|
||
|
|
||
|
<tscreen><verb>
|
||
|
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; /* Di solito aggiungo questo per */
|
||
|
/* evitare piantamenti stupidi. */
|
||
|
/* Probabilmente bisognerebbe */
|
||
|
/* assicurarsi che tutto sia stato*/
|
||
|
/* inizializzato con successo */
|
||
|
for (j=0; j < ABS(cos(2*alpha)) ) { /* Siamo dentro la sagoma? */
|
||
|
/* glib.h contiene ABS(x). */
|
||
|
row[i*3+0] = sqrt(1-r)*255; /* Definisce il Rosso */
|
||
|
row[i*3+1] = 128; /* Definisce il Verde */
|
||
|
row[i*3+2] = 224; /* Definisce il Blu */
|
||
|
} /* "+0" è per allineamento */
|
||
|
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);
|
||
|
/* Inserisce "row" in "preview" a partire del punto avente */
|
||
|
/* coordinate (0,j) prima colonna, j-esima riga, per SIZE */
|
||
|
/* pixel verso destra */
|
||
|
}
|
||
|
|
||
|
free(row); /* libera un po' di memoria */
|
||
|
gtk_widget_draw(preview,NULL); /* indovina cosa fa questo? */
|
||
|
gdk_flush(); /* e questo? */
|
||
|
}
|
||
|
</verb></tscreen>
|
||
|
Coloro che non usano GIMP probabilmente hanno già visto abbastanza
|
||
|
per fare molte cose. Per gli utenti GIMP c'è ancora qualcosa da
|
||
|
aggiungere.
|
||
|
|
||
|
Anteprima dell'immagine
|
||
|
|
||
|
Probabilmente è opportuno tenere pronta una versione ridotta dell'immagine,
|
||
|
grande quanto basta per riempire l'anteprima. Questo può essere fatto
|
||
|
selezionando un pixel ogni n, dove n è il rapporto tra la dimensione
|
||
|
dell'immagine e la dimensione dell'anteprima. Tutte le operazioni successive
|
||
|
(compreso il riempimento dell'anteprima) sono fatte solo sul ridotto numero
|
||
|
di pixel selezionati. Di seguito è riportata un'implementazione della
|
||
|
riduzione dell'immagine (si tenga presente che ho preso solo lezioni basilari
|
||
|
di C!).
|
||
|
|
||
|
|
||
|
(ATTENZIONE: CODICE NON VERIFICATO!!!)
|
||
|
|
||
|
<tscreen><verb>
|
||
|
|
||
|
typedef struct {
|
||
|
gint width;
|
||
|
gint height;
|
||
|
gint bbp;
|
||
|
guchar *rgb;
|
||
|
guchar *mask;
|
||
|
} ReducedImage;
|
||
|
|
||
|
enum {
|
||
|
SELECTION_ONLY,
|
||
|
SELCTION_IN_CONTEXT,
|
||
|
ENTIRE_IMAGE
|
||
|
};
|
||
|
|
||
|
ReducedImage *Reduce_The_Image(GDrawable *drawable,
|
||
|
GDrawable *mask,
|
||
|
gint LongerSize,
|
||
|
gint Selection)
|
||
|
{
|
||
|
/* Questa funzione riduce l'immagine alla dimens. scelta per l'anteprima */
|
||
|
/* La dimensione dell'anteprima è determinata da LongerSize, cioè la più */
|
||
|
/* grande delle dimensioni. Funziona solo per immagini RGB! */
|
||
|
gint RH, RW; /* Altezza ridotta e larghezza ridotta */
|
||
|
gint width, height; /* Larghezza e altezza dell'area da ridurre */
|
||
|
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; /* Assumiamo di trattare l'intera immagine */
|
||
|
|
||
|
gimp_drawable_mask_bounds (drawable->id, &x1, &y1, &x2, &y2);
|
||
|
width = x2-x1;
|
||
|
height = y2-y1;
|
||
|
/* Se c'è una SELEZIONE, ne abbiamo avuto gli estremi! */
|
||
|
|
||
|
if (width != drawable->width && height != drawable->height)
|
||
|
NoSelectionMade=FALSE;
|
||
|
/* Controlliamo se l'utente ha una selezione attiva. Questo */
|
||
|
/* diventerà importante dopo, alla creazione di una maschera ridotta */
|
||
|
|
||
|
/* Se si vuole l'anteprima dell'immagine intera, annulla quanto sopra */
|
||
|
/* Naturalmente, in assenza di una selezione, questo non cambia nulla */
|
||
|
if (Selection==ENTIRE_IMAGE) {
|
||
|
x1=0;
|
||
|
x2=drawable->width;
|
||
|
y1=0;
|
||
|
y2=drawable->height;
|
||
|
}
|
||
|
|
||
|
/* Se si vuole l'anteprima di una selezione con parte dell'area */
|
||
|
/* circostante bisogna espanderla un po'. */
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
/* Così si determinano larghezza e altezza dell'area da ridurre. */
|
||
|
width = x2-x1;
|
||
|
height = y2-y1;
|
||
|
|
||
|
/* Le linee seguenti determinano quale dimensione deve essere il */
|
||
|
/* lato più lungo. L'idea è presa dal plug-in supernova. Ritengo */
|
||
|
/* che avrei potuto pensarci da solo, ma la verità va detta. */
|
||
|
/* Brutta cosa il plagio! */
|
||
|
if (width>height) {
|
||
|
RW=LongerSize;
|
||
|
RH=(float) height * (float) LongerSize/ (float) width;
|
||
|
}
|
||
|
else {
|
||
|
RH=LongerSize;
|
||
|
RW=(float)width * (float) LongerSize/ (float) height;
|
||
|
}
|
||
|
|
||
|
/* L'intera immagine viene "stirata" in una stringa! */
|
||
|
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);
|
||
|
|
||
|
/* Prendine abbastanza da contenere una riga di immagine e una di maschera */
|
||
|
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;
|
||
|
|
||
|
/* Nessuna selezione = tutti i punti sono completamente selezionati */
|
||
|
if (NoSelectionMade)
|
||
|
tempmask[i*RW+j]=255;
|
||
|
else
|
||
|
tempmask[i*RW+j]=src_mask_row[whichcol];
|
||
|
|
||
|
/* Aggiungi la riga alla lunga stringa che ora contiene l'immagine */
|
||
|
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];
|
||
|
|
||
|
/* Mantieni anche la trasparenza (alpha) */
|
||
|
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;
|
||
|
}
|
||
|
</verb></tscreen>
|
||
|
|
||
|
|
||
|
La seguente è una funzione di anteprima che usa lo stesso tipo
|
||
|
ReducedImage! Si noti che usa una finta trasparenza - se ne è presente
|
||
|
una, tramite fake_transparency che è definita come segue:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
gint fake_transparency(gint i, gint j)
|
||
|
{
|
||
|
if ( ((i%20)- 10) * ((j%20)- 10)>0 )
|
||
|
return 64;
|
||
|
else
|
||
|
return 196;
|
||
|
}
|
||
|
|
||
|
</verb></tscreen>
|
||
|
E adesso la funzione per l'anteprima:
|
||
|
<tscreen><verb>
|
||
|
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();
|
||
|
}
|
||
|
|
||
|
Funzioni Applicabili
|
||
|
|
||
|
guint gtk_preview_get_type (void);
|
||
|
/* No idea */
|
||
|
void gtk_preview_uninit (void);
|
||
|
/* No idea */
|
||
|
GtkWidget* gtk_preview_new (GtkPreviewType type);
|
||
|
/* Descritta precedentemente */
|
||
|
void gtk_preview_size (GtkPreview *preview,
|
||
|
gint width,
|
||
|
gint height);
|
||
|
/* Permette di ridimensionare un'anteprima esistente */
|
||
|
/* Pare che un bug in GTK renda disordinato questo */
|
||
|
/* processo. Un modo di rimettere le cose a posto */
|
||
|
/* è quello di ridimensionare manualmente */
|
||
|
/* la finestra contenente l'anteprima dopo aver */
|
||
|
/* ridimensionato l'anteprima. */
|
||
|
|
||
|
void gtk_preview_put (GtkPreview *preview,
|
||
|
GdkWindow *window,
|
||
|
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);
|
||
|
/* Descritta nel testo */
|
||
|
|
||
|
void gtk_preview_set_expand (GtkPreview *preview,
|
||
|
gint expand);
|
||
|
/* No idea */
|
||
|
|
||
|
/* Nessun indizio per le seguenti, ma dovrebbero */
|
||
|
/* essere standard per la maggior parte dei widget */
|
||
|
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);
|
||
|
|
||
|
E' tutto!
|
||
|
|
||
|
</verb></tscreen>
|
||
|
|
||
|
|
||
|
<sect1> Curve
|
||
|
<p>
|
||
|
|
||
|
|
||
|
<sect>Il Widget EventBox<label id="sec_The_EventBox_Widget">
|
||
|
<p>
|
||
|
E' disponibile solo a partire dalla distribuzione gtk+970916.tar.gz.
|
||
|
<p>
|
||
|
Alcuni widget gtk non sono associati a finestre X, sicché
|
||
|
semplicemente disegnano sui loro genitori. Per questo motivo essi non possono
|
||
|
ricevere eventi e se sono sovradimensionati non vengono troncati, ma rischiano
|
||
|
di sovrapporsi, generando confusione. Se si vuole di più da questi
|
||
|
widget si può ricorrere agli EventBox.
|
||
|
|
||
|
A prima vista il widget EventBox potrebbe sembrare completamente inutile. Non
|
||
|
disegna nulla sullo schermo e non risponde a nessun evento. Tuttavia ha
|
||
|
una funzione: fornire una finestra X al suo widget figlio. Ciò
|
||
|
è importante in quanto molti widget GTK non hanno una finestra X
|
||
|
associata. Se questo da una parte risparmia memoria e migliora le prestazioni,
|
||
|
dall'altra introduce degli svantaggi: un widget senza una finestra X non
|
||
|
può ricevere eventi, e non taglia in alcun modo il suo contenuto.
|
||
|
Sebbene il nome ``EventBox'' (casella di eventi) enfasizzi la funzione di
|
||
|
gestione degli eventi, il widget può essere usato anche per
|
||
|
limitare la dimensione dei widget figli (ma anche per altro: si veda
|
||
|
l'esempio seguente).
|
||
|
|
||
|
<p>
|
||
|
Per creare un widget di tipo EventBox:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
GtkWidget* gtk_event_box_new (void);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
<p>
|
||
|
All'EventBox si può aggiungere un widget figlio:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
gtk_container_add (GTK_CONTAINER(event_box), widget);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
<p>
|
||
|
The following example demonstrates both uses of an EventBox - a label
|
||
|
is created that clipped to a small box, and set up so that a
|
||
|
mouse-click on the label causes the program to exit.
|
||
|
Il seguente esempio mostra entrambi gli usi di un EventBox - si crea
|
||
|
un'etichetta limitata da un rettangolo piccolo, fatta in modo che
|
||
|
cliccando con il mouse su di essa il programma termina.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
#include <gtk/gtk.h>
|
||
|
|
||
|
int
|
||
|
main (int argc, char *argv[])
|
||
|
{
|
||
|
GtkWidget *window;
|
||
|
GtkWidget *event_box;
|
||
|
GtkWidget *label;
|
||
|
|
||
|
gtk_init (&argc, &argv);
|
||
|
|
||
|
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
||
|
|
||
|
gtk_window_set_title (GTK_WINDOW (window), "Event Box");
|
||
|
|
||
|
gtk_signal_connect (GTK_OBJECT (window), "destroy",
|
||
|
GTK_SIGNAL_FUNC (gtk_exit), NULL);
|
||
|
|
||
|
gtk_container_border_width (GTK_CONTAINER (window), 10);
|
||
|
|
||
|
/* Crea un EventBox e lo aggiunge alla finestra principale */
|
||
|
|
||
|
event_box = gtk_event_box_new ();
|
||
|
gtk_container_add (GTK_CONTAINER(window), event_box);
|
||
|
gtk_widget_show (event_box);
|
||
|
|
||
|
/* Crea una etichetta lunga */
|
||
|
|
||
|
label = gtk_label_new ("Click here to quit, quit, quit, quit, quit");
|
||
|
gtk_container_add (GTK_CONTAINER (event_box), label);
|
||
|
gtk_widget_show (label);
|
||
|
|
||
|
/* Limitane le dimensioni */
|
||
|
gtk_widget_set_usize (label, 110, 20);
|
||
|
|
||
|
/* E collega ad essa una azione */
|
||
|
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);
|
||
|
|
||
|
/* Un'altra cosa per cui si ha bisogno di una finestra X ... */
|
||
|
|
||
|
gtk_widget_realize (event_box);
|
||
|
gdk_window_set_cursor (event_box->window, gdk_cursor_new (GDK_HAND1));
|
||
|
|
||
|
gtk_widget_show (window);
|
||
|
|
||
|
gtk_main ();
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
</verb></tscreen>
|
||
|
|
||
|
<sect>Selezionare gli Attributi dei Widget<label id="sec_setting_widget_attributes">
|
||
|
<p>
|
||
|
Qui si descrivono le funzioni per la gestione dei widget. Esse possono essere
|
||
|
usate per impostarne lo stile, il padding, le dimensioni, ...
|
||
|
|
||
|
(Forse andrebbe fatta un'intera sezione sugli acceleratori).
|
||
|
|
||
|
<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>Funzioni periodiche, di I/O e di attesa<label id="sec_timeouts">
|
||
|
<p>
|
||
|
<sect1>Funzioni periodiche
|
||
|
<p>
|
||
|
Probabilmente vi sarete chiesti come far fare qualcosa di utile a GTK
|
||
|
durante la chiamata alla gtk_main(). Ci sono diverse possibilità.
|
||
|
Usando le seguenti funzioni si possono creare funzioni che vengono chiamate
|
||
|
periodicamente.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
gint gtk_timeout_add (guint32 interval,
|
||
|
GtkFunction function,
|
||
|
gpointer data);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Il primo argomento è il numero di millisecondi tra le chiamate alla
|
||
|
funzione. Il secondo è la funzione periodica, mentre il terzo
|
||
|
rappresenta i dati che vengono passati alla funzione. Il valore restituito
|
||
|
è un'etichetta che può essere utilizzata per fermare la chiamata
|
||
|
periodica, passandolo alla funzione:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void gtk_timeout_remove (gint tag);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
La chiamata periodica si ferma anche se la funzione periodica ritorna zero
|
||
|
o FALSE. Naturalmente questo vuol dire che se si vuole che la funzione periodica
|
||
|
continui ad essere richiamata, essa deve restituire un valore non nullo,
|
||
|
cioè TRUE.
|
||
|
|
||
|
La dichiarazione della funzione periodica dovrebbe essere come questa:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
gint timeout_callback (gpointer data);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
<sect1>Controllo dell'I/O
|
||
|
<p>
|
||
|
Un'altra utile caratteristica di GTK è la possibilità di fargli
|
||
|
controllare che siano verificate certe condizioni su un descrittore di file
|
||
|
(come quelli restituiti da open(2) o socket(2)). Questo è utile in
|
||
|
particolar modo per le applicazioni di rete. La funzione è la seguente:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
gint gdk_input_add (gint source,
|
||
|
GdkInputCondition condition,
|
||
|
GdkInputFunction function,
|
||
|
gpointer data);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Il primo argomento è il descrittore che si desidera venga controllato,
|
||
|
mentre il secondo specifica quale condizione si vuole che GDK controlli.
|
||
|
Questa può essere una tra:
|
||
|
<p>
|
||
|
GDK_INPUT_READ - Chiama la funzione quando ci sono dati pronti per la lettura
|
||
|
nel descrittore di file.
|
||
|
<p>
|
||
|
GDK_INPUT_WRITE - Chiama la funzione quando il descrittore di file è
|
||
|
pronto per la scrittura.
|
||
|
<p>
|
||
|
Come sicuramente avrete già intuito, il terzo parametro è la
|
||
|
funzione da chiamare quando la condizione specificata è soddisfatta,
|
||
|
mentre il quarto rappresenta i dati da passare a questa funzione.
|
||
|
<p>
|
||
|
Il valore di ritorno è un etichetta che può essere usata per
|
||
|
fermare il controllo di GDK sul descrittore di file, usando la seguente
|
||
|
funzione:
|
||
|
<p>
|
||
|
<tscreen><verb>
|
||
|
void gdk_input_remove (gint tag);
|
||
|
</verb></tscreen>
|
||
|
<p>
|
||
|
La funzione da richiamare va dichiarata così:
|
||
|
<p>
|
||
|
<tscreen><verb>
|
||
|
void input_callback (gpointer data, gint source,
|
||
|
GdkInputCondition condition);
|
||
|
</verb></tscreen>
|
||
|
<p>
|
||
|
|
||
|
<sect1>Funzioni di attesa (``Idle'')
|
||
|
<p>
|
||
|
Cosa fare se si ha una funzione che si vuole venga chiamata quando non
|
||
|
sta accadendo nient'altro?
|
||
|
|
||
|
<tscreen><verb>
|
||
|
gint gtk_idle_add (GtkFunction function,
|
||
|
gpointer data);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Questa fa si che GDK chiami la funzione specificata quando non c'è
|
||
|
nessuna altra operazione in corso.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void gtk_idle_remove (gint tag);
|
||
|
</verb></tscreen>
|
||
|
<p>
|
||
|
Non ci soffermeremo sul significato dei parametri in quanto del tutto analoghi
|
||
|
ai precedenti. La funzione puntata dal primo argomento della gtk_idle_add
|
||
|
viene chiamata non appena se ne presenta l'opportunità; come
|
||
|
negli altri casi, se essa restituisce FALSE non viene più chiamata.
|
||
|
|
||
|
|
||
|
<sect>La gestione delle selezioni
|
||
|
|
||
|
<sect1> Overview
|
||
|
|
||
|
<p>
|
||
|
|
||
|
Le <em>selezioni</em> sono un tipo di comunicazione tra processi
|
||
|
supportato da GTK. Una selezione identifica un frammento di dati; per
|
||
|
esempio, una porzione di testo selezionata dall'utente in qualche modo,
|
||
|
magari con il mouse. Su un display solo un'applicazione alla volta
|
||
|
(il <em>proprietario</em>) puó essere proprietaria di una
|
||
|
particolare selezione, sicché quando un'applicazione richiede
|
||
|
una selezione il precedente proprietario deve comunicare all'utente che
|
||
|
la selezione è stata ceduta. Altre applicazioni possono richiedere
|
||
|
il contenuto di una selezione in diverse forme, chiamate <em>obiettivi</em>.
|
||
|
Ci può essere un numero qualsiasi di selezioni, ma la maggior parte
|
||
|
delle applicazioni X può gestirne solo una, la <em>selezione
|
||
|
primaria</em>.
|
||
|
|
||
|
<p>
|
||
|
Nella maggior parte dei casi per una applicazione GTK non è
|
||
|
necessario gestire esplicitamente le selezioni. I widget standard,
|
||
|
come quello di Ingresso, hanno già la capacità di
|
||
|
chiedere la selezione se necessario (p. e., quando l'utente
|
||
|
seleziona sul testo), e di recuperare il contenuto di una selezione
|
||
|
di un altro widget o di un'altra applicazione (p. e., quando l'utente
|
||
|
clicca il tasto centrale del mouse). Ci possono comunque essere dei
|
||
|
casi nei quali si vuole dare ad altri widget la capacità di
|
||
|
fornire la selezione, o si vogliono recuperare degli obiettivi non
|
||
|
supportati direttamente.
|
||
|
|
||
|
<p>
|
||
|
Un concetto fondamentale necessario per comprendere la gestione delle
|
||
|
selezioni è quello di <em>atomo</em>. Un atomo è un intero
|
||
|
che identifica univocamente una stringa (su un certo display).
|
||
|
Certi atomi sono predefiniti dal server X, e in alcuni casi in <tt>gtk.h</tt>
|
||
|
ci sono costanti corrispondenti a questi atomi. Per esempio, la costante
|
||
|
<tt>GDK_PRIMARY_SELECTION</tt> corrisponde alla stringa ``PRIMARY''.
|
||
|
Negli altri casi bisogna usare le funzioni <tt>gdk_atom_intern()</tt>
|
||
|
per ottenere l'atomo corrispondente ad una stringa, e <tt>gdk_atom_name()</tt>
|
||
|
per ottenere il nome di un atomo. Sia le selezioni sia gli obiettivi sono
|
||
|
identificati da atomi.
|
||
|
|
||
|
<sect1> Recuperare le selezioni
|
||
|
|
||
|
<p>
|
||
|
|
||
|
Il recupero di una selezione è un processo asincrono. Per iniziare
|
||
|
il processo, si chiama:
|
||
|
<tscreen><verb>
|
||
|
gint gtk_selection_convert (GtkWidget *widget,
|
||
|
GdkAtom selection,
|
||
|
GdkAtom target,
|
||
|
guint32 time)
|
||
|
</verb</tscreen>
|
||
|
|
||
|
Questo <em>converte</em> la selezione nella forma specificata
|
||
|
dall'obiettivo <tt/target/. Se possibile, il campo <tt/time/
|
||
|
dovrebbe essere il tempo dell'evento che ha attivato la selezione.
|
||
|
Questo aiuta a far si che gli eventi avvengano nell'ordine in cui
|
||
|
l'utente li ha richiesti. Se comunque non fosse disponibile (per
|
||
|
esempio, se la conversione è stata attivata da un segnale di
|
||
|
``cliccato''), allora si può usare la costante
|
||
|
<tt>GDK_CURRENT_TIME</tt>.
|
||
|
|
||
|
<p>
|
||
|
Quando il proprietario di una selezione risponde ad una richiesta,
|
||
|
un segnale ``selection_received'' (selezione ricevuta) viene inviato
|
||
|
alla vostra applicazione. Il gestore di questo segnale riceve un
|
||
|
puntatore ad una struttura <tt>GtkSelectionData</tt>, che è
|
||
|
definita nel modo seguente:
|
||
|
<tscreen><verb>
|
||
|
struct _GtkSelectionData
|
||
|
{
|
||
|
GdkAtom selection;
|
||
|
GdkAtom target;
|
||
|
GdkAtom type;
|
||
|
gint format;
|
||
|
guchar *data;
|
||
|
gint length;
|
||
|
};
|
||
|
</verb></tscreen>
|
||
|
<tt>selection</tt> e <tt>target</tt> sono i valori da voi specificati
|
||
|
nella chiamata <tt>gtk_selection_convert()</tt>. <tt>type</tt> è
|
||
|
un atomo che identifica il tipo di dati restituiti dal proprietario della
|
||
|
selezione. Alcuni valori possibili sono ``STRING'', una stringa di
|
||
|
caratteri latin-1, ``ATOM'', una serie di atomi, ``INTEGER'', un intero, ecc.
|
||
|
La maggior parte degli obiettivi può restituire solo un tipo.
|
||
|
<tt/format/ ci dà la lunghezza delle unità (per esempio caratteri)
|
||
|
in bit. Di solito, quando si ricevono i dati non ci si cura di questo.
|
||
|
<tt>data</tt> è un puntatore ai dati restituiti, e <tt>length</tt>
|
||
|
è la lunghezza dei dati restituiti, in byte. Se <tt>length</tt>
|
||
|
è negativo allora si è verificato un errore e non è
|
||
|
stato possibile recuperare la selezione. Questo può avvenire se
|
||
|
nessuna applicazione era proprietaria della selezione, o se si è
|
||
|
richiesto un obiettivo non supportato dall'applicazione. Viene garantito
|
||
|
che il buffer sia un byte più lungo di <tt>length</tt>; il byte
|
||
|
in più sarà sempre zero, in modo che non sia necessario
|
||
|
ricopiare le stringhe solo per farle terminare con zero.
|
||
|
|
||
|
<p>
|
||
|
Nell'esempio che segue viene recuperato l'obiettivo speciale ``TARGETS'',
|
||
|
che è una lista di tutti gli obiettivi in cui può essere
|
||
|
convertita la selezione.
|
||
|
<tscreen><verb>
|
||
|
#include <gtk/gtk.h>
|
||
|
|
||
|
void selection_received (GtkWidget *widget,
|
||
|
GtkSelectionData *selection_data,
|
||
|
gpointer data);
|
||
|
|
||
|
/* Gestore di segnale chiamato quando l'utente clicca nel bottone */
|
||
|
/* "Get Targets" */
|
||
|
void
|
||
|
get_targets (GtkWidget *widget, gpointer data)
|
||
|
{
|
||
|
static GdkAtom targets_atom = GDK_NONE;
|
||
|
|
||
|
/* Prende l'atomo corrispondente alla stringa "TARGETS" */
|
||
|
if (targets_atom == GDK_NONE)
|
||
|
targets_atom = gdk_atom_intern ("TARGETS", FALSE);
|
||
|
|
||
|
/* E richiede l'obiettivo "TARGETS" per la selezione primaria */
|
||
|
gtk_selection_convert (widget, GDK_SELECTION_PRIMARY, targets_atom,
|
||
|
GDK_CURRENT_TIME);
|
||
|
}
|
||
|
|
||
|
/* Gestore di segnale chiamato quando il proprietario della selezione */
|
||
|
/* restituisce i dati */
|
||
|
void
|
||
|
selection_received (GtkWidget *widget, GtkSelectionData *selection_data,
|
||
|
gpointer data)
|
||
|
{
|
||
|
GdkAtom *atoms;
|
||
|
GList *item_list;
|
||
|
int i;
|
||
|
|
||
|
/* **** IMPORTANTE **** Controlla che il recupero sia riuscito */
|
||
|
if (selection_data->length < 0)
|
||
|
{
|
||
|
g_print ("Selection retrieval failed\n");
|
||
|
return;
|
||
|
}
|
||
|
/* Make sure we got the data in the expected form */
|
||
|
if (selection_data->type != GDK_SELECTION_TYPE_ATOM)
|
||
|
{
|
||
|
g_print ("Selection \"TARGETS\" was not returned as atoms!\n");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* Stampa gli atomi ricevuti */
|
||
|
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 *window;
|
||
|
GtkWidget *button;
|
||
|
|
||
|
gtk_init (&argc, &argv);
|
||
|
|
||
|
/* Create the toplevel window */
|
||
|
|
||
|
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
||
|
gtk_window_set_title (GTK_WINDOW (window), "Event Box");
|
||
|
gtk_container_border_width (GTK_CONTAINER (window), 10);
|
||
|
|
||
|
gtk_signal_connect (GTK_OBJECT (window), "destroy",
|
||
|
GTK_SIGNAL_FUNC (gtk_exit), NULL);
|
||
|
|
||
|
/* Crea un bottone che l'utente può cliccare per ottenere gli obiettivi */
|
||
|
|
||
|
button = gtk_button_new_with_label ("Get Targets");
|
||
|
gtk_container_add (GTK_CONTAINER (window), button);
|
||
|
|
||
|
gtk_signal_connect (GTK_OBJECT(button), "clicked",
|
||
|
GTK_SIGNAL_FUNC (get_targets), NULL);
|
||
|
gtk_signal_connect (GTK_OBJECT(button), "selection_received",
|
||
|
GTK_SIGNAL_FUNC (selection_received), NULL);
|
||
|
|
||
|
gtk_widget_show (button);
|
||
|
gtk_widget_show (window);
|
||
|
|
||
|
gtk_main ();
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
</verb></tscreen>
|
||
|
|
||
|
<sect1> Fornire una selezione
|
||
|
|
||
|
<p>
|
||
|
Fornire la selezione è un po' più complicato. Bisogna
|
||
|
registrare i gestori che verranno chiamati quando viene richiesta la
|
||
|
propria selezione. Per ogni coppia selezione/obiettivo che si gestirà
|
||
|
occorre una chiamata 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/, e <tt/target/ identificano le richieste
|
||
|
che questo gestore soddisferà. <tt/remove_func/, se non è
|
||
|
NULL, verrà chiamato quando il gestore di segnale viene rimosso.
|
||
|
Questo è utile, per esempio, per linguaggi interpretati ai quali
|
||
|
serve di tener traccia di un conteggio di riferimento per <tt/data/.
|
||
|
|
||
|
<p>
|
||
|
La funzione di richiamo ha la forma:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
typedef void (*GtkSelectionFunction) (GtkWidget *widget,
|
||
|
GtkSelectionData *selection_data,
|
||
|
gpointer data);
|
||
|
|
||
|
</verb></tscreen>
|
||
|
|
||
|
La GtkSelectionData è la stessa di prima, ma stavolta siamo
|
||
|
responsabili di riempire i campi <tt/type/, <tt/format/, <tt/data/,
|
||
|
e <tt/length/. (Il campo <tt/format/ qui è effettivamente
|
||
|
importante - il server X lo usa per capire se occorre che i byte
|
||
|
dei dati vengano scambiati o no. Di solito sarà 8 - cioè
|
||
|
un carattere - o 32 - cioè un intero.) Questo viene fatto
|
||
|
chiamando la funzione:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void gtk_selection_data_set (GtkSelectionData *selection_data,
|
||
|
GdkAtom type,
|
||
|
gint format,
|
||
|
guchar *data,
|
||
|
gint length);
|
||
|
</verb></tscreen>
|
||
|
Questa funzione si prende cura di fare propriamente una copia dei dati
|
||
|
in modo che non ci si debba preoccupare di conservarli (è opportuno
|
||
|
evitare di riempire a mano i campi della struttura GtkSelectionData).
|
||
|
|
||
|
<p>
|
||
|
Quando richiesto dall'utente, richiederete la proprietà della selezione
|
||
|
chiamando:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
gint gtk_selection_owner_set (GtkWidget *widget,
|
||
|
GdkAtom selection,
|
||
|
guint32 time);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Se un'altra applicazione richiede la proprietà della selezione,
|
||
|
riceverete un evento di azzeramento della selezione (``selection_clear_event'').
|
||
|
|
||
|
Come esempio di fornitura della selezione, il programma seguente aggiunge
|
||
|
la funzionalità di selezione a un bottone di attivazione. Quando il
|
||
|
bottone viene premuto, il programma richiede la selezione primaria.
|
||
|
L'unico obiettivo supportato (oltre a certi obiettivi come ``TARGETS''
|
||
|
fornito dalla stessa GTK) è l'obiettivo ``STRING''. Quando viene
|
||
|
richiesto questo obiettivo, viene restituita una rappresentazione stringa
|
||
|
del tempo.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
#include <gtk/gtk.h>
|
||
|
#include <time.h>
|
||
|
|
||
|
/* Richiamata quando l'utente attiva la selezione */
|
||
|
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);
|
||
|
/* se il richiamo della selezione è fallito, si riporta il
|
||
|
bottone nello stato non premuto */
|
||
|
if (!*have_selection)
|
||
|
gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON(widget), FALSE);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (*have_selection)
|
||
|
{
|
||
|
/* Prima di annullare la selezione mettendone a NULL il proprietario,
|
||
|
controlliamo se siamo i veri proprietari */
|
||
|
if (gdk_selection_owner_get (GDK_SELECTION_PRIMARY) == widget->window)
|
||
|
gtk_selection_owner_set (NULL, GDK_SELECTION_PRIMARY,
|
||
|
GDK_CURRENT_TIME);
|
||
|
*have_selection = FALSE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Chiamata quando un'altra applicazione richiede la selezione */
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
/* Fornisce come selezione il tempo attuale */
|
||
|
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));
|
||
|
/* Quando si restituisce una singola stringa, non occorre che finisca
|
||
|
con NULL. Questo verrà fatto automaticamente */
|
||
|
|
||
|
gtk_selection_data_set (selection_data, GDK_SELECTION_TYPE_STRING,
|
||
|
8, timestr, strlen(timestr));
|
||
|
}
|
||
|
|
||
|
int
|
||
|
main (int argc, char *argv[])
|
||
|
{
|
||
|
GtkWidget *window;
|
||
|
|
||
|
GtkWidget *selection_button;
|
||
|
|
||
|
static int have_selection = FALSE;
|
||
|
|
||
|
gtk_init (&argc, &argv);
|
||
|
|
||
|
/* Crea la finestra di livello superiore */
|
||
|
|
||
|
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
||
|
gtk_window_set_title (GTK_WINDOW (window), "Event Box");
|
||
|
gtk_container_border_width (GTK_CONTAINER (window), 10);
|
||
|
|
||
|
gtk_signal_connect (GTK_OBJECT (window), "destroy",
|
||
|
GTK_SIGNAL_FUNC (gtk_exit), NULL);
|
||
|
|
||
|
/* Crea un bottone a commutazione che agisce come la selezione */
|
||
|
|
||
|
selection_button = gtk_toggle_button_new_with_label ("Claim Selection");
|
||
|
gtk_container_add (GTK_CONTAINER (window), 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, NULL);
|
||
|
|
||
|
gtk_widget_show (selection_button);
|
||
|
gtk_widget_show (window);
|
||
|
|
||
|
gtk_main ();
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
</verb></tscreen>
|
||
|
|
||
|
|
||
|
|
||
|
<sect>La glib<label id="sec_glib">
|
||
|
<p>
|
||
|
La glib fornisce molte funzioni e definizioni utili pronte all'uso quando si
|
||
|
creano applicazioni GDK e GTK. Qui verranno elencate tutte, con una
|
||
|
breve spiegazione. Molte sono duplicati delle funzioni standard della libc,
|
||
|
e quindi per queste non si scenderà nei dettagli. Questa vuole essere una
|
||
|
lista di riferimento, in modo che si sappia cosa è possibile usare.
|
||
|
|
||
|
<sect1>Definizioni
|
||
|
<p>
|
||
|
Le definizioni per gli estremi di molti dei tipi standard sono:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
G_MINFLOAT
|
||
|
G_MAXFLOAT
|
||
|
G_MINDOUBLE
|
||
|
G_MAXDOUBLE
|
||
|
G_MINSHORT
|
||
|
G_MAXSHORT
|
||
|
G_MININT
|
||
|
G_MAXINT
|
||
|
G_MINLONG
|
||
|
G_MAXLONG
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Ci sono anche le seguenti definizioni di tipo. Quelle rimaste non specificate
|
||
|
sono dipendenti dall'architettura. Si ricordi di evitare di fare affidamento
|
||
|
sulla dimensione di un puntatore se si vuole la portabilità! P.e., un puntatore
|
||
|
su un Alpha è lungo 8 byte, ma 4 su un 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>Liste a doppio collegamento
|
||
|
<p>
|
||
|
le seguenti funzioni sono usate per creare, gestire e distruggere liste a
|
||
|
doppio collegamento. Si assume che il lettore sappia già cosa sono le liste
|
||
|
collegate, poiché descriverle è fuori dagli scopi di questo documento.
|
||
|
Naturalmente non è necessario conoscerle per l'uso generale di GTK, per
|
||
|
quanto conoscerle sia comunque interessante.
|
||
|
|
||
|
<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 position);
|
||
|
|
||
|
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>Liste a collegamento singolo
|
||
|
<p>
|
||
|
Molte delle funzioni per le liste a collegamento singolo sono identiche alle
|
||
|
precedenti. Eccone 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 position);
|
||
|
|
||
|
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>Gestione della memoria
|
||
|
<p>
|
||
|
<tscreen><verb>
|
||
|
gpointer g_malloc (gulong size);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Questa è una sostituta di malloc(). Non occorre controllare il valore
|
||
|
restituito, in quanto lo fa già questa funzione.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
gpointer g_malloc0 (gulong size);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Come la precedente, ma la memoria viene azzerata prima di restituire un
|
||
|
puntatore ad essa.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
gpointer g_realloc (gpointer mem,
|
||
|
gulong size);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Riloca ``size'' byte di memoria che inizia a ``mem''. Ovviamente, la memoria
|
||
|
dovrebbe essere stata allocata precedentemente.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void g_free (gpointer mem);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Libera la memoria. Facile!
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void g_mem_profile (void);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Emette un profilo della memoria usata, ma occorre ricompilare e reinstallare
|
||
|
la libreria aggiungendo #define MEM_PROFILE all'inizio del file glib/gmem.c.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void g_mem_check (gpointer mem);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Controlla che una locazione di memoria sia valida. Occorre ricompilare e
|
||
|
reinstallare la libreria aggiungendo #define MEM_CHECK all'inizio del file
|
||
|
gmem.c.
|
||
|
|
||
|
<sect1>Timer
|
||
|
<p>
|
||
|
Funzioni legate ai timer...
|
||
|
|
||
|
<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>Gestione delle stringhe
|
||
|
<p>
|
||
|
Un'accozzaglia di funzioni per la gestione delle stringhe. Sembrano tutte molto
|
||
|
interessanti, e probabilmente migliori per molte caratteristiche delle funzioni
|
||
|
standard del C per le stringhe, ma necessitano di documentazione.
|
||
|
|
||
|
<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>Funzioni d'utilità e di errore
|
||
|
<p>
|
||
|
<tscreen><verb>
|
||
|
gchar* g_strdup (const gchar *str);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Funzione sostitutiva della strdup. Copia i contenuti originari delle stringhe
|
||
|
in memoria appena allocata, restituendo un puntatore ad essa.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
gchar* g_strerror (gint errnum);
|
||
|
</verb></tscreen>
|
||
|
Si raccomanda di usare questa gunzione per tutti i messaggi di errore. E' molto
|
||
|
più graziosa, e più portabile di perror() o di altre. L'output di solito ha la
|
||
|
forma:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
nome programma:funzione fallita:file o altre descrizioni:strerror
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Di seguito un esempio di una chiamata di questo tipo usata nel nostro
|
||
|
programma 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>
|
||
|
|
||
|
Visualizza un messaggio di errore. Il formato è come quello di printf,
|
||
|
ma prepone ``** ERROR **: '' al messaggio e termina il programma. Da usare solo
|
||
|
per errori gravi.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void g_warning (gchar *format, ...);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Come la precedente, ma prepone ``** WARNING **: '' e non termina il programma.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void g_message (gchar *format, ...);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Visualizza ``message: '' e poi il messaggio.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void g_print (gchar *format, ...);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Sostituta di printf().
|
||
|
|
||
|
L'ultima funzione:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
gchar* g_strsignal (gint signum);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Visualizza il nome del messaggio del sistema Unix associato al numero di
|
||
|
segnale. Utile nelle funzioni generiche di gestione dei segnali.
|
||
|
|
||
|
Tutte le funzioni elencate sono più o meno prese da glib.h. Se qualcuno volesse
|
||
|
documentare qualche funzione, mandi una email all'autore!
|
||
|
|
||
|
<sect>I file rc di GTK
|
||
|
<p>
|
||
|
GTK ha un suo modo di trattare le preferenze delle applicazioni, usando
|
||
|
i file rc. Questi possono essere usati per scegliere i colori di quasi tutti
|
||
|
i widget, e possono anche essere usati per inserire delle pixmap nello sfondo
|
||
|
di alcuni widget.
|
||
|
|
||
|
<sect1>Funzioni per i file rc
|
||
|
<p>
|
||
|
All'inizio della vostra applicazione dovrebbe esserci una chiamata a
|
||
|
<tscreen><verb>
|
||
|
void gtk_rc_parse (char *filename);
|
||
|
</verb></tscreen>
|
||
|
<p>
|
||
|
passando come parametro il nome del vostro file rc. Questo farà si che GTK
|
||
|
analizzi tale file e usi le impostazioni di stile per i tipi di widget ivi
|
||
|
definite.
|
||
|
<p>
|
||
|
Se si desidera avere un insieme speciale di widget che abbia uno stile diverso
|
||
|
dagli altri, o qualsiasi altra divisione logica dei widget, si chiami
|
||
|
<tscreen><verb>
|
||
|
void gtk_widget_set_name (GtkWidget *widget,
|
||
|
gchar *name);
|
||
|
</verb></tscreen>
|
||
|
<p>
|
||
|
passando un widget appena creato come primo argomento, e il nome che gli si
|
||
|
vuole dare come secondo. Questo consentirà di cambiare gli attributi di
|
||
|
questo widget per nome tramite il file rc.
|
||
|
<p>
|
||
|
Effettuando una chiamata come questa:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
button = gtk_button_new_with_label ("Special Button");
|
||
|
gtk_widget_set_name (button, "special button");
|
||
|
</verb></tscreen>
|
||
|
<p>
|
||
|
allora a questo bottone viene dato il nome ``special button'' ed esso può essere
|
||
|
riferito per nome nel file rc come ``special button.GtkButton''. [<--- Verificatemi!]
|
||
|
<p>
|
||
|
Il seguente esempio di file rc imposta le proprietà della finestra principale,
|
||
|
e fa si che tutti i figli di questa finestra ereditino lo stile descritto
|
||
|
dallo stile ``main button''. Il codice usato nell'applicazione è:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
||
|
gtk_widget_set_name (window, "main window");
|
||
|
</verb></tscreen>
|
||
|
<p>
|
||
|
Lo stile viene definito nel file rc usando:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
widget "main window.*GtkButton*" style "main_button"
|
||
|
</verb></tscreen>
|
||
|
<p>
|
||
|
che assegna a tutti i widget GtkButton nella finestra principale lo stile
|
||
|
``main_buttons'' secondo la definizione data nel file rc.
|
||
|
<p>
|
||
|
Come si può vedere, questo sistema è molto potente e flessibile. Usate la
|
||
|
vostra immaginazione per trarre il massimo vantaggio da esso.
|
||
|
|
||
|
<sect1>Il formato dei file rc di GTK
|
||
|
<p>
|
||
|
Nell'esempio che segue viene illustrato il formato del file GTK. Si tratta
|
||
|
del file testgkrc dalla distribuzione del GTK, a cui sono stati aggiunti
|
||
|
vari commenti e varie cose. Potete includere questa spiegazione nella
|
||
|
vostra applicazione per consentire all'utente di personalizzarla finemente.
|
||
|
<p>
|
||
|
There are several directives to change the attributes of a widget.
|
||
|
Ci sono diverse direttive per cambiare gli attributi di un widget.
|
||
|
<itemize>
|
||
|
<item>fg - Assegna il colore di primo piano di un widget.
|
||
|
<item>bg - Assegna il colore di sfondo di un widget.
|
||
|
<item>bg_pixmap - Inserisce nello sfondo di un widget una pixmap.
|
||
|
<item>font - Sceglie il font da usarsi con il dato widget.
|
||
|
</itemize>
|
||
|
<p>
|
||
|
Inoltre ci sono diversi stati in cui può trovarsi un widget, e si possono
|
||
|
assegnare diversi colori, pixmap e font per ogni stato. Essi sono:
|
||
|
<itemize>
|
||
|
<item>NORMAL - Lo stato normale di un widget, quando il mouse non si trova su
|
||
|
di esso, quando non è premuto, ecc.
|
||
|
<item>PRELIGHT (evidenziato)- Quando il mouse si trova sopra al widget
|
||
|
verranno usati i colori assegnati per questo stato.
|
||
|
<item>ACTIVE (attivo) - Quando il widget è premuto o cliccato esso sarà attivo,
|
||
|
e verranno usati gli attributi assegnati da questa etichetta.
|
||
|
<item>INSENSITIVE (insensibile)- Quando un widget viene reso insensibile,
|
||
|
e non può essere attivato, prenderà questi attributi.
|
||
|
<item>SELECTED (selezionato) - Quando un oggetto viene selezionato, prende
|
||
|
questi attributi.
|
||
|
</itemize>
|
||
|
<p>
|
||
|
Quando si usano le parole chiave ``fg'' e ``bg'' per assegnare i colori dei
|
||
|
widget il formato è:
|
||
|
<tscreen><verb>
|
||
|
fg[<STATE>] = { Rosso, Verde, Blu }
|
||
|
</verb></tscreen>
|
||
|
<p>
|
||
|
Dove STATE è uno degli stati visti prima (PRELIGHT, ACTIVE ecc.), e Rosso,
|
||
|
Verde e Blu sono valori nell'intervallo 0 - 1.0; { 1.0, 1.0, 1.0 } rappresenta
|
||
|
il bianco.
|
||
|
Devono essere in formato float, o verranno visti come 0, sicché un ``1'' diretto
|
||
|
non funziona, deve essere ``1.0''. Uno ``0'' diretto va invece bene, poiché poco
|
||
|
importa se non viene riconosciuto: valori non riconosciuti vengono considerati
|
||
|
0.
|
||
|
<p>
|
||
|
bg_pixmap è molto simile al precedente, tranne per i colori che vengono
|
||
|
sostituiti dal nome di un file.
|
||
|
|
||
|
pixmap_path è una lista di percorsi separati da ``:''. In questi percorsi vengono
|
||
|
cercate le pixmap specificate.
|
||
|
<p>
|
||
|
La direttiva font è semplicemente:
|
||
|
<tscreen><verb>
|
||
|
font = "<font name>"
|
||
|
</verb></tscreen>
|
||
|
<p>
|
||
|
dove l'unica parte complicata è immaginare la stringa del font. Allo scopo
|
||
|
può servire usare xfontsel o una utilità analoga.
|
||
|
<p>
|
||
|
``widget_class'' assegna lo stile di una classe di widget. Queste classi sono
|
||
|
elencate nell'introduzione ai widget sulla gerarchia delle classi.
|
||
|
<p>
|
||
|
La direttiva ``widget'' assegna un insieme di widget dal nome specificato ad
|
||
|
un dato stile, annullando qualsiasi stile assegnato per la data classe di widget.
|
||
|
Questi widget vengono registrati nell'applicazione usando la chiamata
|
||
|
gtk_widget_set_name(). Questo consente di specificare gli attributi di un
|
||
|
widget singlarmente, piuttosto che assegnando gli attributi di un'intera classe
|
||
|
di widget. E' opportuno documentare tutti questi widget speciali in modo che
|
||
|
gli utenti possano personalizzarli.
|
||
|
<p>
|
||
|
Quando la parola chiave ``<tt>parent</>'' viene usata come un attributo, il
|
||
|
widget erediterà gli attributi del suo genitore nell'applicazione.
|
||
|
<p>
|
||
|
Quando si definisce uno stile si possono assegnare gli attributi di uno
|
||
|
stile definito precedentemente a quello nuovo.
|
||
|
<tscreen><verb>
|
||
|
style "main_button" = "button"
|
||
|
{
|
||
|
font = "-adobe-helvetica-medium-r-normal--*-100-*-*-*-*-*-*"
|
||
|
bg[PRELIGHT] = { 0.75, 0, 0 }
|
||
|
}
|
||
|
</verb></tscreen>
|
||
|
<p>
|
||
|
Questo esempio prende lo stile ``button'' e crea un nuovo stile
|
||
|
semplicemente cambiando il font e il colore di sfondo dello stato ``prelight''
|
||
|
nello stile ``button''.
|
||
|
<p>
|
||
|
Naturalmente, molti di questi attributi non sono applicabili a tutti i widget.
|
||
|
E' veramente un semplice problema di buon senso. Tutto quello che potrebbe
|
||
|
applicarsi, dovrebbe.
|
||
|
|
||
|
<sect1>Esempio di file rc
|
||
|
<p>
|
||
|
|
||
|
<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>
|
||
|
|
||
|
|
||
|
# Ecco una lista di tutti gli stati possibili. Si noti che alcuni non sono
|
||
|
# applicabili a certi widget.
|
||
|
#
|
||
|
# NORMAL - Lo stato normale di un widget, quando il mouse non si trova su
|
||
|
# di esso, quando non è premuto, ecc.
|
||
|
#
|
||
|
# PRELIGHT (evidenziato)- Quando il mouse si trova sopra al widget
|
||
|
# verranno usati i colori assegnati per questo stato.
|
||
|
#
|
||
|
# ACTIVE (attivo) - Quando il widget è premuto o cliccato esso sarà attivo,
|
||
|
# e verranno usati gli attributi assegnati da questa etichetta.
|
||
|
#
|
||
|
# INSENSITIVE (insensibile)- Quando un widget viene reso insensibile,
|
||
|
# e non può essere attivato, prenderà questi attributi.
|
||
|
#
|
||
|
# SELECTED (selezionato) - Quando un oggetto viene selezionato, prende
|
||
|
# questi attributi.
|
||
|
#
|
||
|
# Dati questi stati, è possibile assegnare gli attributi dei widget in
|
||
|
# ognuno di questi stati usando le seguenti direttive.
|
||
|
#
|
||
|
# fg - Assegna il colore di primo piano di un widget.
|
||
|
# bg - Assegna il colore di sfondo di un widget.
|
||
|
# bg_pixmap - Inserisce nello sfondo di un widget una pixmap.
|
||
|
# font - Sceglie il font da usarsi con il dato widget.
|
||
|
#
|
||
|
|
||
|
# Questo è uno stile chiamato "button". Il nome non è veramente importante,
|
||
|
# in quanto viene assegnato ai veri widget alla fine del file.
|
||
|
|
||
|
style "window"
|
||
|
{
|
||
|
# Questo inserisce nella spaziatura attorno alla finestra la pixmap
|
||
|
# specificata.
|
||
|
#bg_pixmap[<STATE>] = "<pixmap filename>"
|
||
|
bg_pixmap[NORMAL] = "warning.xpm"
|
||
|
}
|
||
|
|
||
|
style "scale"
|
||
|
{
|
||
|
# Mette il colore di primo piano (il colore del font) a rosso nello
|
||
|
# stato "NORMAL".
|
||
|
|
||
|
fg[NORMAL] = { 1.0, 0, 0 }
|
||
|
|
||
|
# Inserisce nello sfondo del gadget la stessa pixmap usata dal suo genitore.
|
||
|
bg_pixmap[NORMAL] = "<parent>"
|
||
|
}
|
||
|
|
||
|
style "button"
|
||
|
{
|
||
|
# Questo mostra tutti i possibili stati per un bottone. L'unico che
|
||
|
# non è applicabile è lo stato "SELECTED".
|
||
|
|
||
|
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 questi esempio ereditiamo gli attributi dello stile "button" e poi
|
||
|
# alteriamo il font e il colore di sfondo quando evidenziato per creare
|
||
|
# un nuovo stile "main_button".
|
||
|
|
||
|
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 }
|
||
|
|
||
|
# Questo seleziona come pixmap di sfondo per il toggle_button quella del
|
||
|
# suo widget genitore (definita nell'applicazione).
|
||
|
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"
|
||
|
|
||
|
# Queste assegnano ai tipi di widget gli stili definiti prima.
|
||
|
# I tipi di widget sono elencati nella gerarchia delle classi, ma probabilmente
|
||
|
# dovrebbero essere elencati in questo documento come riferimento per l'utente.
|
||
|
|
||
|
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"
|
||
|
|
||
|
# Questo assegna lo stile main_button a tutti i bottoni che sono figli della
|
||
|
# "main window" (finestra principale). Questi devono essere documenati per
|
||
|
# potersene avvantaggiare.
|
||
|
widget "main window.*GtkButton*" style "main_button"
|
||
|
</verb></tscreen>
|
||
|
|
||
|
|
||
|
|
||
|
<sect>Scrivere un proprio Widget
|
||
|
|
||
|
<p>
|
||
|
<sect1> Panoramica
|
||
|
<p>
|
||
|
Anche se la distribuzione GTK contiene molto tipi di widget che possono
|
||
|
coprire molte necessità basilari, può essere necessario costruirsi
|
||
|
un proprio widget. GTK usa molto l'ereditarietà tra i vari
|
||
|
widget e, di solito, vi è un widget che si avvicina a quello che ti
|
||
|
servirebbe, ed è spesso possibile creare un nuovo widget con poche linee
|
||
|
di codice. Ma prima di iniziare il lavoro su un nuovo widget, vediamo
|
||
|
se qualcuno non lo ha già creato. Questo eviterà un duplicazione
|
||
|
di lavoro e farà sì che i widget non-GTK puri siano minimi, così da
|
||
|
aiutare sia chi crea il codice che chi l'interfaccia per applicazioni GTK
|
||
|
molto grosse. D'altra parte, quando hai finito di scrivere un widget,
|
||
|
annuncialo a tutto il mondo così che le altre persone ne possano
|
||
|
beneficiare. Il miglioro modo dove farlo è la <tt>gtk-list</tt>.
|
||
|
|
||
|
<sect1> L'anatomia di un widget
|
||
|
|
||
|
<p>
|
||
|
Per creare un nuovo widget è importante aver capito come gli ogetti
|
||
|
di GTK lavorano. Questa sezione è solo una breve spiegazione. Guarda la
|
||
|
documentazione di riferimento per maggiori dettagli.
|
||
|
|
||
|
<p>
|
||
|
I widget GTK sono implementati in un modo orientato agli oggetti,
|
||
|
anche se usando il C standard. Questo aumenta notevolmente la portabilità
|
||
|
e la stabilità, specialmente per le correnti generazioni di compilatori C++;
|
||
|
comunque questo significa che chi scrive un widget deve fare attenzione
|
||
|
ad alcuni dettagli di implementazione. L'informazione comune a tutte le
|
||
|
istanze di una classe di widget (ad esempio: a tutti i bottoni) è memorizzata
|
||
|
<em>class structure</em>. C'e' solamente una copia di questo in cui
|
||
|
sono memorizzate le informazioni riguardanti i segnali della classe
|
||
|
(assomiglia ad una funzione virtuale in C). Per supportare l'ereditarietà
|
||
|
il primo campo della struttura di una classe deve essere una copia della
|
||
|
struttura della classe genitore. La dichiarazione della struttura della
|
||
|
classe GtkButton è:
|
||
|
|
||
|
<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>
|
||
|
|
||
|
<p>
|
||
|
Quando un bottone viene trattato come un contenitore (ad esempio quando viene
|
||
|
ridimensionato) si può fare il cast della struttura della sua classe con la
|
||
|
GtkContainerClass, e usare i campi rilevanti per gestire i segnali.
|
||
|
|
||
|
<p>
|
||
|
C'è anche una struttura per ogni widget che viene creata
|
||
|
ad ogni istanza. Questa struttura ha campi per
|
||
|
memorizzare le informazioni che sono differenti per ogni volta che il widget
|
||
|
viene istanziato. Chiameremo questa struttura la <em> struttura
|
||
|
oggetto</em>. Per la classe Bottone, questa ha l'aspetto:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
struct _GtkButton
|
||
|
{
|
||
|
GtkContainer container;
|
||
|
|
||
|
GtkWidget *child;
|
||
|
|
||
|
guint in_button : 1;
|
||
|
guint button_down : 1;
|
||
|
};
|
||
|
</verb></tscreen>
|
||
|
|
||
|
<p>
|
||
|
Si noti che, similmente alla struttura della classe, il primo campo
|
||
|
è la struttura dell'oggetto della classe madre, così che, se necessario, si può fare il
|
||
|
cast di questa struttura con quella dell'oggetto della classe madre.
|
||
|
|
||
|
<sect1> Creare un Widget composto
|
||
|
|
||
|
<sect2> Introduzione
|
||
|
|
||
|
<p>
|
||
|
Un tipo di widget a cui potreste essere interessati è un widget che
|
||
|
è semplicemnte un aggregato di altri widget GTK. Questo tipo di
|
||
|
widget non fa nulla che non possa essere fatto creando un nuovo
|
||
|
widget, ma fornisce un modo conveniente per inscatolare elementi
|
||
|
dell'interfaccia utente per poi riutilizzarli.
|
||
|
I widget FileSelection e ColorSelection della ditribuzione standard
|
||
|
sono esempi di questo tipo di widget.
|
||
|
|
||
|
<p>
|
||
|
Il widget di esempio che creeremo in questo capitolo è il
|
||
|
Tictactoe, un vettore 3x3 di bottoni a commutazione il quale emette
|
||
|
un segnale quando tutti e 3 i bottoni di una riga, colonna o di una
|
||
|
diagonale sono premuti.
|
||
|
|
||
|
<sect2> Scegliere la classe madre
|
||
|
|
||
|
<p>
|
||
|
La classe madre per un widget composto e' tipicamente la classe
|
||
|
contenitrice che racchiude tutti gli elementi del widget composto.
|
||
|
Per esempio, la classe madre del widget FileSelection è la classe
|
||
|
Dialog. Visto che i nostri bottoni sono inseriti in una tabella, è
|
||
|
naturale pensare che la nostra classe madre possa essere la GtkTable.
|
||
|
Sfortunatamente, così non è. La creazione di un widget è diviso
|
||
|
tra 2 funzioni : la funzione <tt/WIDGETNAME_new()/ che viene invocata
|
||
|
dall'utente, e la funzione <tt/WIDGETNAME_init()/ che ha il compito
|
||
|
principale di inizializzare il widget che è indipendente dai valori
|
||
|
passati alla funzione <tt/_new()/. Widget figli o discendenti possono
|
||
|
chiamare, solamente, la funzione del loro widget genitore.
|
||
|
Ma questa divisione del lavoro non funziona bene per la tabella, la
|
||
|
quale, quando creata, necessita di conoscere il numero di righe e
|
||
|
colonne che la comporrà. A meno che non vogliamo duplicare molte delle
|
||
|
fuinzionalità della <tt/gtk_table_new()/ nel nostro widget
|
||
|
Tictactoe, faremmo meglio a evitare di derivarlo dalla GtkTable. Per questa
|
||
|
ragione lo deriviamo invece da GtkVBox, e uniamo la nostra tabella
|
||
|
dentro il VBox.
|
||
|
|
||
|
<sect2> Il File Header
|
||
|
|
||
|
<p>
|
||
|
Ogni classe di widget ha un file header il quale dichiara l'oggetto e la
|
||
|
struttura della classe del widget, comprese le funzioni pubbliche.
|
||
|
Per prevenire duplicati di definizioni, noi includiamo l'intero file header fra:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
#ifndef __TICTACTOE_H__
|
||
|
#define __TICTACTOE_H__
|
||
|
.
|
||
|
.
|
||
|
.
|
||
|
#endif /* __TICTACTOE_H__ */
|
||
|
</verb></tscreen>
|
||
|
|
||
|
E per far felici i programmi in C++ che includono il nostro file header, in:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
#ifdef __cplusplus
|
||
|
extern "C" {
|
||
|
#endif /* __cplusplus */
|
||
|
.
|
||
|
.
|
||
|
.
|
||
|
#ifdef __cplusplus
|
||
|
}
|
||
|
#endif /* __cplusplus */
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Insieme alle funzioni e alle strutture, dichiariamo tre macro
|
||
|
standard nel nostro file header, <tt/TICTACTOE(obj)/,
|
||
|
<tt/TICTACTOE_CLASS(klass)/, e <tt/IS_TICTACTOE(obj)/, i quali rispettivamente
|
||
|
fanno il cast di un puntatore ad un puntatore ad un ogetto od ad una struttura
|
||
|
di classe, e guarda se un oggetto è un widget Tictactoe.
|
||
|
|
||
|
|
||
|
Qui vi è il file header completo:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
|
||
|
#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 *buttons[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 funzione <tt/_get_type()/
|
||
|
|
||
|
<p>
|
||
|
Continuiamo ora con l'implementazione del nostro widget. Una funzione
|
||
|
basilare di ogni widget è la funzione <tt/WIDGETNAME_get_type()/.
|
||
|
Questa funzione, quando chiamata la prima volta, comunica a GTK la classe
|
||
|
del widget, e ottiene un identificativo univoco per la classe del
|
||
|
widget. Chiamate successive restituiscono semplicemente l'identificativo.
|
||
|
|
||
|
<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,
|
||
|
(GtkArgFunc) NULL,
|
||
|
};
|
||
|
|
||
|
ttt_type = gtk_type_unique (gtk_vbox_get_type (), &ttt_info);
|
||
|
}
|
||
|
|
||
|
return ttt_type;
|
||
|
}
|
||
|
</verb></tscreen>
|
||
|
|
||
|
<p>
|
||
|
La struttura GtkTypeInfo ha la seguente definizione:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
struct _GtkTypeInfo
|
||
|
{
|
||
|
gchar *type_name;
|
||
|
guint object_size;
|
||
|
guint class_size;
|
||
|
GtkClassInitFunc class_init_func;
|
||
|
GtkObjectInitFunc object_init_func;
|
||
|
GtkArgFunc arg_func;
|
||
|
};
|
||
|
</verb></tscreen>
|
||
|
|
||
|
<p>
|
||
|
I campi di questa struttura sono abbastanza auto-esplicativi.
|
||
|
Ignoreremo, per ora, il campo <tt/arg_func/: ha un ruolo importante, ma
|
||
|
non ancora largamente implementato, nel permettere ai linguaggi interpretati
|
||
|
di settare convenientemente le opzioni del widget.
|
||
|
Una volta che il GTK ha completato correttamente una copia di questa
|
||
|
struttura, sa come creare un oggetto di un particolare widget.
|
||
|
|
||
|
<sect2> La funzione <tt/_class_init()/
|
||
|
<p>
|
||
|
La funzione <tt/WIDGETNAME_class_init()/ inizialiazza i campi della
|
||
|
struttura della classe del widget, e setta ogni segnale della classe.
|
||
|
Per il nostro widget Tictactoe ha il seguente aspetto:
|
||
|
|
||
|
<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_ARG_NONE, 0);
|
||
|
|
||
|
|
||
|
gtk_object_class_add_signals (object_class, tictactoe_signals, LAST_SIGNAL);
|
||
|
|
||
|
class->tictactoe = NULL;
|
||
|
}
|
||
|
</verb></tscreen>
|
||
|
|
||
|
<p>
|
||
|
Il nostro widget ha semplicemente il segnale ``tictactoe'' che è
|
||
|
invocato quando una riga, colonna o diagonale è completamente premuta.
|
||
|
Non tutti i widget composti necessitano di segnali, quindi se stai
|
||
|
leggendo questo per la prima volta, puoi anche saltare alla prossima sezione,
|
||
|
dal momento che a questo punto le cose diventano un po' complicate.
|
||
|
|
||
|
La funzione:
|
||
|
<tscreen><verb>
|
||
|
gint gtk_signal_new (gchar *name,
|
||
|
GtkSignalRunType run_type,
|
||
|
gint object_type,
|
||
|
gint function_offset,
|
||
|
GtkSignalMarshaller marshaller,
|
||
|
GtkArgType return_val,
|
||
|
gint nparams,
|
||
|
...);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
crea un nuovo segnale. I parametri sono:
|
||
|
|
||
|
<itemize>
|
||
|
<item> <tt/name/: Il nome del segnale.
|
||
|
<item> <tt/run_type/: Se il segstore predefinito viene eseguito prima o dopo
|
||
|
di quello dell'utente. Di norma questo sarà <tt/GTK_RUN_FIRST/, o <tt/GTK_RUN_LAST/,
|
||
|
anche se ci sono altre possibilità.
|
||
|
<item> <tt/object_type/: l'identificativo dell'oggetto a cui questo segnale si
|
||
|
riferisce. Esso sarà anche applicato agli oggetti discendenti.
|
||
|
<item> <tt/function_offset/: L'offset nella struttura della classe di un
|
||
|
puntatore al gestore predefinito.
|
||
|
<item> <tt/marshaller/: una funzione che è usata per invocare il gestore
|
||
|
del segnale. Per gestori di segnali che non hanno argomenti oltre
|
||
|
all'oggetto che emette il segnale e i dati dell'utente, possiamo usare
|
||
|
la funzione predefinita <tt/gtk_signal_default_marshaller/
|
||
|
<item> <tt/return_val/: Il tipo del valore di ritorno.
|
||
|
<item> <tt/nparams/: Il numero di parametri del gestore di segnali (oltre
|
||
|
ai due predefiniti menzionati sopra)
|
||
|
<item> <tt/.../: i tipi dei parametri
|
||
|
</itemize>
|
||
|
|
||
|
Quando si specificano i tipi, si usa l'enumerazione <tt/GtkArgType/:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
typedef enum
|
||
|
{
|
||
|
GTK_ARG_INVALID,
|
||
|
GTK_ARG_NONE,
|
||
|
GTK_ARG_CHAR,
|
||
|
GTK_ARG_SHORT,
|
||
|
GTK_ARG_INT,
|
||
|
GTK_ARG_LONG,
|
||
|
GTK_ARG_POINTER,
|
||
|
GTK_ARG_OBJECT,
|
||
|
GTK_ARG_FUNCTION,
|
||
|
GTK_ARG_SIGNAL
|
||
|
} GtkArgType;
|
||
|
</verb></tscreen>
|
||
|
|
||
|
<p>
|
||
|
<tt/gtk_signal_new()/ restituisce un identificatore unico intero per il segnale,
|
||
|
che memorizziamo nel vettore <tt/tictactoe_signals/, che
|
||
|
indicizzeremo usando una enumerazione. (Convenzionalmente, gli elementi dell'enumerazione
|
||
|
sono i nomi dei segnali, in maiuscolo,
|
||
|
ma qui ci potrebbe essere un conflitto con la macro <tt/TICTACTOE()/,
|
||
|
quindi l'abbiamo chiamato <tt/TICTACTOE_SIGNAL/
|
||
|
|
||
|
Dopo aver creato un nostro segnale, abbiamo bisogno di dire a GTK
|
||
|
di associare il nostro segnale alla classe Tictactoe. Lo facciamo
|
||
|
invocando <tt/gtk_object_class_add_signals()/. Settiamo quindi a NULL
|
||
|
il puntatore che punta al gestore predefinito per il segnale
|
||
|
``tictactoe'' a NULL, indicando che non ci sono azioni predefinite.
|
||
|
|
||
|
<sect2> La funzione <tt/_init()/
|
||
|
|
||
|
<p>
|
||
|
|
||
|
Ogni classe di Widget necessita anche di una funzione per inizializzare
|
||
|
la struttura dell'oggetto. Usualmente questa funzione ha il ruolo abbastanza
|
||
|
limitato di assegnare ai campi della struttura i valori predefiniti.
|
||
|
Per widget composti, comunque, questa funzione crea, anche,
|
||
|
i widget componenti del widget composto.
|
||
|
|
||
|
<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> E il resto...
|
||
|
|
||
|
<p>
|
||
|
|
||
|
C'è un'altra funzione che ogni widget (eccetto i Widget di base come
|
||
|
GtkBin che non possono essere instanziati) deve avere : la funzione
|
||
|
che l'utente invoca per creare un oggetto di quel tipo. Questa è
|
||
|
convenzionalmente chiamata <tt/WIDGETNAME_new()/. In alcuni widget,
|
||
|
non nel caso del nostro Tictactoe, questa funzione richiede degli
|
||
|
argomenti, e fa alcune operazioni basandosi su di essi. Le altre
|
||
|
due funzioni sono specifiche del widget Tictactoe.
|
||
|
|
||
|
<p>
|
||
|
<tt/tictactoe_clear()/ è una funzione pubblica che resetta tutti i
|
||
|
bottoni, nel widget, allo stato iniziale (non premuto). Notate l'uso
|
||
|
di <tt/gtk_signal_handler_block_by_data()/ per impedire che il nostro
|
||
|
gestore dei segnali venga attivato quando non ce n'è bisogno.
|
||
|
|
||
|
<p>
|
||
|
<tt/tictactoe_toggle()/ è il gestore del segnale che viene invocato
|
||
|
quando l'utente preme il bottone. Esso guarda se vi è
|
||
|
qualche combinazione vincente che coinvolge i bottoni premuti, e nel
|
||
|
caso ci fosse, emette il segnale ``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>
|
||
|
|
||
|
<p>
|
||
|
|
||
|
E finalmente un programma di esempio che usa il nostro widget
|
||
|
Tictactoe:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
#include <gtk/gtk.h>
|
||
|
#include "tictactoe.h"
|
||
|
|
||
|
/* Invocato quando una riga, colonna o diagonale e' completata. */
|
||
|
void
|
||
|
win (GtkWidget *widget, gpointer data)
|
||
|
{
|
||
|
g_print ("Yay!\n");
|
||
|
tictactoe_clear (TICTACTOE (widget));
|
||
|
}
|
||
|
|
||
|
int
|
||
|
main (int argc, char *argv[])
|
||
|
{
|
||
|
GtkWidget *window;
|
||
|
GtkWidget *ttt;
|
||
|
|
||
|
gtk_init (&argc, &argv);
|
||
|
|
||
|
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
||
|
|
||
|
gtk_window_set_title (GTK_WINDOW (window), "Aspect Frame");
|
||
|
|
||
|
gtk_signal_connect (GTK_OBJECT (window), "destroy",
|
||
|
GTK_SIGNAL_FUNC (gtk_exit), NULL);
|
||
|
|
||
|
gtk_container_border_width (GTK_CONTAINER (window), 10);
|
||
|
|
||
|
/* Crea un nuovo widget Tictactoe. */
|
||
|
ttt = tictactoe_new ();
|
||
|
gtk_container_add (GTK_CONTAINER (window), ttt);
|
||
|
gtk_widget_show (ttt);
|
||
|
|
||
|
/* E gli aggancia il segnale "tictactoe" */
|
||
|
gtk_signal_connect (GTK_OBJECT (ttt), "tictactoe",
|
||
|
GTK_SIGNAL_FUNC (win), NULL);
|
||
|
|
||
|
gtk_widget_show (window);
|
||
|
|
||
|
gtk_main ();
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
</verb></tscreen>
|
||
|
|
||
|
|
||
|
<sect1> Creare un widget a partire da zero
|
||
|
|
||
|
<sect2> Introduzione
|
||
|
|
||
|
<p>
|
||
|
|
||
|
In questa sezione impareremo meglio come i widget si mostrano sullo schermo
|
||
|
e interagiscono con gli eventi. Come esempio, creeremo
|
||
|
un widget di quadrante analogico con un puntatore che l'utente
|
||
|
può trascinare per assegnare il valore.
|
||
|
|
||
|
<sect2> Mostrare un widget sullo schermo
|
||
|
|
||
|
<p>
|
||
|
Ci sono alcuni passi che sono necessari nella visualizzazione sullo
|
||
|
schermo. Dopo che il widget è stato creato con una chiamata a
|
||
|
<tt/WIDGETNAME_new()/, sono necessarie alcune altre funzioni:
|
||
|
|
||
|
<itemize>
|
||
|
<item> <tt/WIDGETNAME_realize()/ è responsabile della creazione di
|
||
|
una finestra X per il widget se ne ha una.
|
||
|
<item> <tt/WIDGETNAME_map()/ è invocata dopo che l'utente ha
|
||
|
chiamato <tt/gtk_widget_show()/. E' responsabile di vedere se il
|
||
|
widget è attualmente disegnato sullo schermo (<em/mappato/). Per
|
||
|
una classe contenitore, essa deve anche creare chiamate alle
|
||
|
funzioni <tt/map()/> per ogni widget figlio.
|
||
|
<item> <tt/WIDGETNAME_draw()/ è invocata quando
|
||
|
<tt/gtk_widget_draw()/ viene chiamata per il widget o per uno dei suoi
|
||
|
predecessori. Esso fa sì che l'attuale chiamata alla
|
||
|
funzione di disegno del widget disegni il widget sullo schermo.
|
||
|
Per la classe contenitore, questa funzione deve eseguire le
|
||
|
chiamate alla funzioni <tt/gtk_widget_draw()/ di ogni suo widget
|
||
|
figlio.
|
||
|
<item> <tt/WIDGETNAME_expose()/ è un gestore per l'evento di esposizione
|
||
|
per il widget. Esso crea le chiamate necessarie alle funzioni di disegno
|
||
|
per disegnare la porzione che si è resa visibile. Per le classi
|
||
|
contenitore, questa funzione deve generare gli eventi di ``expose'' per
|
||
|
tutti i widget figli che non hanno una propria finestra (se essi hanno
|
||
|
una loro finestra, sarà X che genererà i necessari eventi di expose).
|
||
|
</itemize>
|
||
|
|
||
|
<p>
|
||
|
Potete notare che le ultime due funzioni sono molto simili, ognuna è
|
||
|
responsabile per il disegno del widget sullo schermo. Infatti molti
|
||
|
tipi di widget non sanno relamente la differenza tra le due.
|
||
|
La funzione di predefinita <tt/draw()/ nella classe widget, semplicemente
|
||
|
genera un sintetico evento di ``expose'' per l'area da ridisegnare.
|
||
|
Comunque, alcuni tipi di widget possono risparmiare tempo distinguendo
|
||
|
le due funzioni. Per esempio, se un widget ha piu' finestre X, allora
|
||
|
visto che l'evento ``expose'' identifica solo la finestra esposta,
|
||
|
esso può ridisegnare solo la finestra interessata, cosa che non è
|
||
|
possibile per chiamate a <tt/draw()/.
|
||
|
|
||
|
<p>
|
||
|
I widget contenitori, anche se essi non farebbero differenze,
|
||
|
non possono semplicemente usare la funzione <tt/draw()/ perchè per i
|
||
|
loro widget figli la differenza potrebbere essere importante. Comunque,
|
||
|
sarebbe uno spreco duplicare il codice di disegno nelle due
|
||
|
funzioni. La convenzione è che questi widget abbiano una funzione
|
||
|
chiamata <tt/WIDGETNAME_paint()/ che disegna il widget, che è poi
|
||
|
chiamata dalle funzioni <tt/draw()/ e <tt/expose()/
|
||
|
|
||
|
<p>
|
||
|
Nell'approccio del nostro esempio, visto che il widget, ha
|
||
|
una sola finestra, possiamo utilizzare il modo piu' semplice
|
||
|
ed usare la funzione predefinita <tt/draw()/ e implementare
|
||
|
solamente la funzione <tt/expose()/.
|
||
|
|
||
|
<sect2> Le origini del widget Dial
|
||
|
|
||
|
<p>
|
||
|
Come tutti gli animali terresti sono semplicemente varianti del primo
|
||
|
amfibio, i widget Gtk tendono ad essere varianti di altri widget, precedentemente
|
||
|
scritti. Così, anche se questa sezione è intitolata ``Creare
|
||
|
un widget a partire da zero", il nostro widget inizia in realtà con il codice
|
||
|
sorgente del widget Range. Questo è stato preso come punto d'inizio
|
||
|
perche' sarebbe carino se il nostro widget avesse la
|
||
|
stessa interfaccia del widget Scale il quale è semplicemente una
|
||
|
specializzazione del widget Range. Così, sebbene il codice sorgente e'
|
||
|
presentato sotto in forma definitiva, non si deve pensare che sia stato
|
||
|
scritto <em>deus ex machina</em> in questo modo. Se poi non avete familiarità
|
||
|
con il funzionamento del widget Scale dal punto di vista di chi scrive
|
||
|
un'applicazione, potrebbe essere una buona idea guardare indietro prima
|
||
|
di continuare.
|
||
|
|
||
|
<sect2> Le basi
|
||
|
|
||
|
<p>
|
||
|
Una parte del nostro widget potrebbe essere simile
|
||
|
al widget Tictactoe. In primo luogo, abbiamo il file header:
|
||
|
|
||
|
<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;
|
||
|
|
||
|
/* Politica di update (GTK_UPDATE_[CONTINUOUS/DELAYED/DISCONTINUOUS]) */
|
||
|
guint policy : 2;
|
||
|
|
||
|
/* Bottone correntemente premuto o 0 altrimenti */
|
||
|
guint8 button;
|
||
|
|
||
|
/* Dimensione della componente Dial. */
|
||
|
gint radius;
|
||
|
gint pointer_width;
|
||
|
|
||
|
/* ID del timer di update, o 0 altrimenti */
|
||
|
guint32 timer;
|
||
|
|
||
|
/* Angolo corrente. */
|
||
|
gfloat angle;
|
||
|
|
||
|
/* Vecchi valori dell'aggiustamento così sappiamo quando
|
||
|
* qualcosa cambia */
|
||
|
gfloat old_value;
|
||
|
gfloat old_lower;
|
||
|
gfloat old_upper;
|
||
|
|
||
|
/* L'oggetto adjustament che memorizza i dati per questo 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>
|
||
|
|
||
|
Essendoci più cose da fare con questo widget, rispetto al precedente,
|
||
|
abbiamo più cambi nella struttura dati, ma le altre cose sono
|
||
|
abbastamza simili.
|
||
|
|
||
|
<p>
|
||
|
|
||
|
Dopo aver incluso i file di header e aver dichiarato alcune costanti,
|
||
|
dobbiamo fornire alcune funzioni circa il widget e la sua
|
||
|
inizializzazione.
|
||
|
|
||
|
<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
|
||
|
|
||
|
/* Dichiarazioni di funzioni successive */
|
||
|
|
||
|
[ omesse per salvare spazio ]
|
||
|
|
||
|
/* variabili locali. */
|
||
|
|
||
|
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,
|
||
|
(GtkArgFunc) 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>
|
||
|
|
||
|
Notate che questa funzione <tt/init()/ fa meno rispetto all'analoga del
|
||
|
widget Tictactoe, essendo questo un widget non composto, e la
|
||
|
funzione <tt/new()/ fa di più, essendoci un argomento. Inoltre,
|
||
|
notate che quando memorizziamo un puntatore all'oggetto Adjustment,
|
||
|
incrementiamo il conteggio dei suoi riferimenti(e corrispondentemente
|
||
|
lo decrementato quando non lo usiamo più) così che GTK può tener traccia di
|
||
|
quando è possibile distruggerlo senza causare guai.
|
||
|
|
||
|
<p>
|
||
|
Inoltre, ci sono alcune funzioni per manipolare le opzioni del 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>
|
||
|
Abbiamo ora raggiunto alcuni nuovi tipi di funzione. In primo luogo,
|
||
|
abbiamo una funzione che crea la finestra di X. Noterete che viene
|
||
|
passata alla funzione <tt/gdk_window_new()/ una maschera che
|
||
|
specifica quali campi della struttura GdkWindowAttr non sono vuoti
|
||
|
(ai rimanenti campi può essere dato il valore predefinito). Anche
|
||
|
il modo con cui la maschera degli eventi del widget creata non è
|
||
|
complicato. Chiameremo <tt/gtk_widget_get_events()/ per sapere la
|
||
|
maschera degli eventi che l'utente ha specificato per questo widget
|
||
|
(con <tt/gtk_widget_set_events()/) e aggiungeremo gli eventi che ci possono
|
||
|
interessare.
|
||
|
|
||
|
<p>
|
||
|
Dopo aver creato la finestra, settiamo lo stile e lo sfondo,
|
||
|
e creiamo un puntatore al widget nel campo dei dati utente (user data)
|
||
|
del GdkWindow. Quest'ultimo passo permette a GTK di mandare gli
|
||
|
eventi della finestra al widget corretto.
|
||
|
|
||
|
<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> Negoziazione della dimensione
|
||
|
|
||
|
<p>
|
||
|
Prima di visualizzare per la prima volta la finestra, e se il
|
||
|
layout della finestra cambia, GTK chiede ad ogni widget, incluso nella
|
||
|
finestra, la propria dimensione. Questa richiesta è fatta dalla
|
||
|
funzione <tt/gtk_dial_size_request()/. Non essendo il nostro widget
|
||
|
un contenitore, e non avendo dei veri limiti per la propria
|
||
|
dimensione, restituiamo semplicemnte un valore ragionevole.
|
||
|
|
||
|
<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>
|
||
|
Dopo che tutti i widget hanno restituito una dimensione ideale, viene
|
||
|
calcolata la disposizione della finestra e ad ogni widget figlio è
|
||
|
notificata la propria dimensione attuale <!--ndMichel : che può essere diversa
|
||
|
da quella restitutita con la funzione sopra -->. Usualmente, questo sarà
|
||
|
almeno quanto richiesto, ma occasionalmente può essere più piccolo.
|
||
|
La notifica della dimensione viene fatta dalla funzione
|
||
|
<tt/gtk_dial_size_allocate()/. Notate che questa funzione è utilizzata
|
||
|
anche quando la finestra X del widget è spostata o modificata come
|
||
|
dimensione.
|
||
|
|
||
|
<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>
|
||
|
Come menzionato sopra, tutto il lavoro di questo widget viene fatto nella
|
||
|
gestione dell'evento ``expose''. Non c'è molto da notare su questo eccetto
|
||
|
l'uso della funzione <tt/gtk_draw_polygon/ per disegnare il
|
||
|
puntatore con un'ombreggiatura a tre dimensioni in accordo con il colore
|
||
|
memorizzato nello stile del wiget.
|
||
|
|
||
|
<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;
|
||
|
|
||
|
/* Draw ticks */
|
||
|
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
/* Draw pointer */
|
||
|
|
||
|
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> Gestore degli eventi
|
||
|
|
||
|
<p>
|
||
|
|
||
|
Il resto del codice del widget manipola vari tipi di eventi, e non
|
||
|
è differente da quello che può essere trovato in molte applicazione
|
||
|
GTK. Due tipi di eventi possono verificarsi: l'utente può
|
||
|
clickare sul widget con il mouse e trascinare per muovere il puntatore,
|
||
|
o il valore dell'oggetto Adjustmente può cambiare a causa di alcune
|
||
|
circostanze esterne.
|
||
|
|
||
|
<p>
|
||
|
Quando l'utente clicka sul widget, noi vediamo se la pressione
|
||
|
era veramente vicina al puntatore, e se così, memorizziamo il bottone
|
||
|
premuto dall'utente con il campo <tt/button/ della struttura del
|
||
|
widget, e prendiamo tutti gli eventi del mouse con una chiamata alla
|
||
|
funzione <tt/gtk_grab_add()/. Successivi movimenti del mouse causano il
|
||
|
ricalcolo dei valori di controllo (fatto dalla funzione
|
||
|
<tt/gtk_dial_update_mouse/). Dipendentemente dalla politica che abbiamo
|
||
|
stabilito, gli eventi ``value_changed'' possono essere generati
|
||
|
istantaneamente (<tt/GTK_UPDATE_CONTINUOUS/), dopo un certo tempo aggiunto
|
||
|
con la funzione <tt/gtk_timeout_add()/ (<tt/GTK_UPDATE_DELAYED/), o
|
||
|
solamente quando il bottone del mouse e' rilasciato
|
||
|
(<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);
|
||
|
|
||
|
/* Determina se il bottone premuto era dentro la regione del puntatore:
|
||
|
lo facciamo calcolando la distanza parallela e
|
||
|
perpendicolare dal punto dove il bottone del mouse e' stato premuto
|
||
|
alla linea passante per il puntatore. */
|
||
|
|
||
|
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>
|
||
|
Cambiamenti esterni all'Adjustment sono comunicati al nostro widget
|
||
|
dai segnali ``changed'' e ``value_changed''. Il gestore per
|
||
|
queste funzioni chiama <tt/gtk_dial_update()/ per validare gli
|
||
|
argomenti, calcolare il nuovo angolo del puntatore e ridisegnare il
|
||
|
widget (chiamando <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> Possibili Miglioramenti
|
||
|
|
||
|
<p>
|
||
|
|
||
|
Il widget Dial, da come l'abbiamo costruito, è lungo circa 670 linee
|
||
|
di codice C. Anche se questo potrebbe sembrare un po' troppo, abbiamo
|
||
|
realmente fatto un bel po' con quel tanto di codice, specialmente
|
||
|
considerando che molta della lunghezza è costituita da file header e
|
||
|
commmenti. Comunque ci sono alcuni miglioramenti che potrebbero essere
|
||
|
fatti a questo widget:
|
||
|
|
||
|
<itemize>
|
||
|
<item> Se tu provate questo widget, troverete che ci sono alcuni lampeggiamenti
|
||
|
quando il puntatore viene trascinato in giro. Questo
|
||
|
perchè l'intero widget è cancellato ogni volta che il
|
||
|
puntatore viene mosso, prima di essere ridisegnato. Spesso, il modo migliore
|
||
|
per gestire questo tipo di problema è il disegnare il tutto su una
|
||
|
pixmap non visibile, poi copiare il risultato finale sullo schermo
|
||
|
in una passata sola (il widget ProgressBar viene disegnato in questo
|
||
|
modo).
|
||
|
|
||
|
<item> L'utente potrebbe essere abilitato ad usare le frecce su e giu per
|
||
|
incrementare e diminuire il valore.
|
||
|
|
||
|
<item> Potrebbe essere carino se il widget avesse i bottoni per
|
||
|
incrementare e decrementare il valore di step. Anche se potrebbe essere
|
||
|
possibile usare dei widget Bottone incorporati per questo, possiamo anche
|
||
|
far sì che il bottone sia auto-ripentente quando premuto, come le frecce
|
||
|
in una barra di scorrimento. Molto del codice per implementare questo tipo di
|
||
|
comportamento può essere trovato nel widget GtkRange.
|
||
|
|
||
|
<item> il widget Dial potrebbe essere fatto/creato dentro un widget
|
||
|
contenitore con un singolo widget figlio posizionato all'inizio tra i
|
||
|
2 bottoni menzionati prima. L'utente potrebbe poi aggiungere o una etichetta
|
||
|
o un widget ``entry'' per mostrare il valore corrente del dial.
|
||
|
|
||
|
</itemize>
|
||
|
|
||
|
<sect1> Impararne di più
|
||
|
|
||
|
<p>
|
||
|
Fin qui abbiamo esposto solo una piccola parte di tutto quello che serve
|
||
|
per creare un widget. Se volete davvero scrivere un vostro widget, la
|
||
|
miglior risorsa di esempi è lo stesso codice sorgente GTK. Chiedete a voi
|
||
|
stessi alcune cose su come deve essere il widget che volete scrivere: è
|
||
|
un widget contenitore? dovrà avere una propria finestra? è una modifica di
|
||
|
un widget precedente? Trovate poi un widget simile e iniziate a fargli
|
||
|
delle modifiche.
|
||
|
Buone Fortuna.
|
||
|
|
||
|
|
||
|
<sect>Scribble, Un semplice esempio di Programma di Disegno
|
||
|
|
||
|
<sect1> Panoramica
|
||
|
|
||
|
<p>
|
||
|
In questa sezione, creeremo un semplice programma di disegno. Durante
|
||
|
questo processo, esamineremo come gestire gli eventi generati dal mouse,
|
||
|
come disegnare all'interno di una finestra e come disegnare in modo migliore
|
||
|
usando una pixmap di supporto. Dopo averlo creato, lo amplieremo aggiungendo
|
||
|
il supporto per i dispositivi XInput, per esempio le tavolette grafiche.
|
||
|
Il GTK fornisce delle routine di supporto grazie alle quali risulta piuttosto
|
||
|
semplice ottenere informazioni estese, come la pressione o l'inclinazione.
|
||
|
|
||
|
<sect1> Gestione degli Eventi
|
||
|
|
||
|
<p>
|
||
|
I segnali di GTK che abbiamo discusso finora si riferivano ad azioni di
|
||
|
alto livello, ad esempio la selezione di un elemento di un menù. Però, a volte
|
||
|
è utile sapere qualcosa su cose che si svolgono a livello più basso livello,
|
||
|
come possono essere il movimento del mouse o la pressione di un tasto.
|
||
|
Ci sono segnali di GTK anche per questi <em>eventi</em> di basso livello.
|
||
|
I gestori di questo tipo di segnali hanno un parametro caratteristico in più,
|
||
|
che è il puntatore ad una struttura che contiene informazioni riguardo
|
||
|
all'evento. Per esempio, ai gestori di eventi che riguardano dei movimenti,
|
||
|
si passa un puntatore ad una struttura GdkEventMotion, che è fatta (in parte)
|
||
|
così:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
struct _GdkEventMotion
|
||
|
{
|
||
|
GdkEventType type;
|
||
|
GdkWindow *window;
|
||
|
guint32 time;
|
||
|
gdouble x;
|
||
|
gdouble y;
|
||
|
...
|
||
|
guint state;
|
||
|
...
|
||
|
};
|
||
|
</verb></tscreen>
|
||
|
|
||
|
<tt/type/ avrà il valore del tipo di evento, in questo caso
|
||
|
<tt/GDK_MOTION_NOTIFY/, <tt/window/ rappresenta la finestra in cui l'evento
|
||
|
si è verificato. <tt/x/ e <tt/y/ forniscono le coordinate dell'evento e
|
||
|
<tt/state/ specifica lo stato dei modificatori nel momento in cui l'evento
|
||
|
si è verificato (cioè, specifica quali tasti modificatori e tasti del mouse
|
||
|
erano premuti in quel momento). E' un OR bit per bit dei seguenti valori:
|
||
|
|
||
|
<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>
|
||
|
Come succede per gli altri segnali, per determinare cosa deve accadere in
|
||
|
corrispondenza di un evento, si chiama <tt>gtk_signal_connect()</tt>. Ma
|
||
|
è anche necessario far sì che GTK sappia di quali eventi vogliamo essere
|
||
|
informati. A questo fine, chiamiamo la funzione:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void gtk_widget_set_events (GtkWidget *widget, gint events);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Il secondo campo specifica gli eventi che ci interessano. Si tratta dell'OR
|
||
|
bit per bit delle costanti che identificano i diversi tipi di eventi. La lista
|
||
|
dei tipi di eventi è la seguente:
|
||
|
|
||
|
<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>
|
||
|
|
||
|
Per chiamare <tt/gtk_widget_set_events()/, si devono fare alcune osservazioni
|
||
|
sottili. In primo luogo, la si deve chiamare prima che sia stata creata la
|
||
|
finestra X per il widget GTK. In pratica, ciò significa che la si deve
|
||
|
chiamare subito dopo aver creato il widget. In secondo luogo, il widget
|
||
|
deve avere una finestra X associata. Molti widget, per ragioni di
|
||
|
efficienza, non hanno una propria finetra, e vengono mostrati nella
|
||
|
finestra madre. Questi widget sono:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
GtkAlignment
|
||
|
GtkArrow
|
||
|
GtkBin
|
||
|
GtkBox
|
||
|
GtkImage
|
||
|
GtkItem
|
||
|
GtkLabel
|
||
|
GtkPaned
|
||
|
GtkPixmap
|
||
|
GtkScrolledWindow
|
||
|
GtkSeparator
|
||
|
GtkTable
|
||
|
GtkViewport
|
||
|
GtkAspectFrame
|
||
|
GtkFrame
|
||
|
GtkVPaned
|
||
|
GtkHPaned
|
||
|
GtkVBox
|
||
|
GtkHBox
|
||
|
GtkVSeparator
|
||
|
GtkHSeparator
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Per catturare degli eventi per questo tipo di widget, si deve fare uso
|
||
|
del widget EventBox. Si veda a questo proposito la sezione su
|
||
|
<ref id="sec_The_EventBox_Widget" name="The EventBox Widget">.
|
||
|
|
||
|
<p>
|
||
|
Per il nostro programma di disegno, vogliamo sapere quando il pulsante del
|
||
|
mouse è premuto e quando viene mosso, quindi specificheremo
|
||
|
<tt/GDK_POINTER_MOTION_MASK/ e <tt/GDK_BUTTON_PRESS_MASK/. Vogliamo anche
|
||
|
essere informati su quando è necessario ridisegnare la nostra finestra,
|
||
|
quindi specifichiamo <tt/GDK_EXPOSURE_MASK/. Anche se vogliamo essere
|
||
|
avvertiti con un evento ``Configure'' se la dimensione della nostra finestra
|
||
|
cambia, non è necessario specificare il flag <tt/GDK_STRUCTURE_MASK/, dal
|
||
|
momento che questo viene specificato automaticamente per tutte le finestre.
|
||
|
|
||
|
<p>
|
||
|
Risulta, conunque, che specificando semplicemente <tt/GDK_POINTER_MOTION_MASK/
|
||
|
si crea un problema. Ciò infatti fa sì che il server aggiunga nella coda un
|
||
|
un nuovo evento di movimento ogni volta che l'utente muovoe il mouse. Immaginate
|
||
|
che ci vogliano 0.1 secondi per gestire uno di questi eventi, e che il server
|
||
|
X metta in coda un nuovo evento ogni 0.05 secondi. Rimarremo ben presto indietro
|
||
|
rispetto al disegno dell'utente. Se l'utente disegna per 5 secondi, ci metteremmo
|
||
|
altri 5 secondi prima di finire dopo che l'utente ha rilasciato il pulsante del
|
||
|
mouse! Vorremmo quindi che venga notificato un solo evento di movimento per
|
||
|
ogni evento che processiamo. Il modo per farlo è di specificare
|
||
|
<tt/GDK_POINTER_MOTION_HINT_MASK/.
|
||
|
|
||
|
<p>
|
||
|
Quando specifichiamo <tt/GDK_POINTER_MOTION_HINT_MASK/, il server ci notifica
|
||
|
un evento di movimento la prima volta che il puntatore si muove dopo essere
|
||
|
entrato nella nostra finestra, oppure dopo ogni rilascio di un pulsante del
|
||
|
mouse. Gli altri eventi di movimento verranno soppressi finché non richiediamo
|
||
|
esplicitamente la posizione del puntatore con la funzione:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
GdkWindow* gdk_window_get_pointer (GdkWindow *window,
|
||
|
gint *x,
|
||
|
gint *y,
|
||
|
GdkModifierType *mask);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
(c'è anche un'altra funzione, <tt>gtk_widget_get_pointer()</tt>, che ha
|
||
|
un'interfaccia più semplice, ma che non risulta molto utile dal momento
|
||
|
che restituisce solo la posizione del puntatore, senza dettagli sullo
|
||
|
sato dei pulsanti.)
|
||
|
|
||
|
<p>
|
||
|
Quindi, il codice per assegnare gli eventi per la nostra finestra, avrà l'aspetto:
|
||
|
|
||
|
<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>
|
||
|
|
||
|
Teniamo per dopo i gestori di ``expose_event'' e ``configure_event''. Quelli di
|
||
|
``motion_notify_event'' e ``button_press_event'' sono piuttosto semplici:
|
||
|
|
||
|
<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> Il widget Area di Disegno (DrawingArea) e il procedimento per Disegnare
|
||
|
|
||
|
<p>
|
||
|
Vediamo ora il procedimento per disegnare sullo schermo. Il
|
||
|
widget da usare è l'Area di Disegno (DrawingArea). Essenzialmente si
|
||
|
tratta di una finestra X e nient'altro. E' una tela bianca su cui possimo
|
||
|
disegnare tutto quello che vogliamo. Per crearne una usiamo la chiamata:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
GtkWidget* gtk_drawing_area_new (void);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Per specificare una dimensione predefinita, si puo fare:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void gtk_drawing_area_size (GtkDrawingArea *darea,
|
||
|
gint width,
|
||
|
gint height);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Come è vero per tutti i widget, si può modificare questa dimensione
|
||
|
predefinita, tramite la chamata a <tt>gtk_widget_set_usize()</tt>, e
|
||
|
questa a sua volta può essere modificata dall'utente ridimensionando
|
||
|
manualmente la finestra che contiene l'area di disegno.
|
||
|
|
||
|
<p>
|
||
|
Si deve notare che nel momento in cui creiamo un widget DrawingArea, siamo
|
||
|
<em>completamente</em> responsabili di disegnarne il contenuto. Se ad
|
||
|
esempio la nostra finestra viene prima nascosta e poi dinuovo portata in
|
||
|
primo piano, otteniamo un evento di ``esposizione'' e doppiamo ridisegnare
|
||
|
ciò che era stato precedente nascosto.
|
||
|
|
||
|
<p>
|
||
|
Dover ricordare tutto quello che era disegnato sulla finestra in modo da
|
||
|
poterlo ridisegnare successivamente, può essere, come minimo, noioso.
|
||
|
In più, può essere spiacevole dal punto di vista visivo, se delle porzioni
|
||
|
dello schermo vengono prima cancellate e poi ridisegnate passo per passo.
|
||
|
La soluzione per questo problema è di usare una <em>pixmap di supporto</em>.
|
||
|
Invece di disegnare direttamente sullo schermo, disegnamo su un'iimagine
|
||
|
conservata nella memoria del server ma che non viene mostrata; quindi, quando
|
||
|
l'immagine cambia o ne vengono mostrate nuove porzioni, copiamo sullo schermo
|
||
|
le parti corrispondenti.
|
||
|
|
||
|
<p>
|
||
|
Per creare una ppixmap fuori dallo schermo, usiamo la funzione:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
GdkPixmap* gdk_pixmap_new (GdkWindow *window,
|
||
|
gint width,
|
||
|
gint height,
|
||
|
gint depth);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Il parametro <tt>window</tt>specifica una finestra GDK dalla quale questa
|
||
|
pixmap prende alcune delle sue proprietà. <tt>width</tt> e <tt>height</tt>
|
||
|
specificano le dimensioni della pixmap. <tt>depth</tt> specifica la
|
||
|
<em>profondità di colore</em>, cioè il numero di bit per ogni pixel, per
|
||
|
la nuova pixmap. Se alla profondità è assegnato il valore <tt>-1</tt>, questa
|
||
|
verrà posta identica a quella di <tt>window</tt>.
|
||
|
|
||
|
<p>
|
||
|
Creiamo la pixmap all'interno del gestore di ``configure_event''. Questo evento
|
||
|
è generato ogni volta che la finestra cambia di dimensione, compreso il
|
||
|
momento in cui viene creata per la prima volta.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
/* Pixmap di supporto per l'area di disegno */
|
||
|
static GdkPixmap *pixmap = NULL;
|
||
|
|
||
|
/* Creare una pixmap della dimensione appropriata */
|
||
|
static gint
|
||
|
configure_event (GtkWidget *widget, GdkEventConfigure *event)
|
||
|
{
|
||
|
if (pixmap)
|
||
|
{
|
||
|
gdk_pixmap_destroy(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 chiamata a <tt>gdk_draw_rectangle()</tt> inizialmente rende bianca l'intera
|
||
|
pixmap. Fra un momento ne riparleremo.
|
||
|
|
||
|
<p>
|
||
|
Il gestore dell'evento ``esposizione'', copia quindi la porzione appropriata
|
||
|
della pixmap sullo schermo (determiniamo qual è l'area da ridisegnare usando
|
||
|
il campo event->area dell'evento di esposizione):
|
||
|
|
||
|
<tscreen><verb>
|
||
|
/* Ridisegna sullo schermo a partire dalla pixmap di supporto */
|
||
|
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>
|
||
|
|
||
|
Abbiamo quindi visto come tenete aggiornato lo schermo con la nostra
|
||
|
pixmap, ma come facciamo per disegnare delle cose interessanti sulla
|
||
|
pixmap? Ci sono un bel po' di funzioni nella libreria GDK di GTK che
|
||
|
servono per disegnare su superfici <em>disegnabili</em>. Una superficie
|
||
|
disegnabile è semplicemente qualcosa su cui si può disegnare un'immagine.
|
||
|
Può essere una finestra, una pixmap o una bitmap (un'immagine in bianco e
|
||
|
nero). Abbiamo già visto sopra due di chiamate,
|
||
|
<tt>gdk_draw_rectangle()</tt> and <tt>gdk_draw_pixmap()</tt>. La lista
|
||
|
completa è la seguente:
|
||
|
|
||
|
<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>
|
||
|
|
||
|
Per ulteriori dettagli su queste funzioni, vedete la documentazione di
|
||
|
riferimento nei file header <tt><gdk/gdk.h></tt>.
|
||
|
Tutte queste funzioni hanno i medesimi primi due argomenti. Il primo
|
||
|
è la superficie disegnabili su cui disegnare, il secondo è un
|
||
|
<em>contesto grafico</em> (GC).
|
||
|
|
||
|
<p>
|
||
|
Un contesto grafico incapsula delle informazioni riguardo a cose come
|
||
|
il colore di sfondo e di primo piano e lo spessore della linea.
|
||
|
GDK ha un ampio insieme di funzioni per crare e modificare contesti grafici,
|
||
|
ma per tenere le cose semplici useremo solo dei contesti grafici predefiniti.
|
||
|
Ogni widget ha uno stile associato (che può essere modificato agendo su un
|
||
|
file gtkrc). Questo, fra le altre cose, contiene un certo numero di contesti
|
||
|
grafici. Alcuni esempi di come accedere a questi contesti grafici sono
|
||
|
i seguenti:
|
||
|
|
||
|
<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>
|
||
|
|
||
|
I campi <tt>fg_gc</tt>, <tt>bg_gc</tt>, <tt>dark_gc</tt>, e
|
||
|
<tt>light_gc</tt> sono indicizzati tramite un parametri di tipo
|
||
|
<tt>GtkStateType</tt>, che può assumere i valori:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
GTK_STATE_NORMAL,
|
||
|
GTK_STATE_ACTIVE,
|
||
|
GTK_STATE_PRELIGHT,
|
||
|
GTK_STATE_SELECTED,
|
||
|
GTK_STATE_INSENSITIVE
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Per esempio, per <tt/GTK_STATE_SELECTED/ il colore di sfondo predefinito
|
||
|
è blu scuro e quello di primo piano bianco.
|
||
|
|
||
|
<p>
|
||
|
La nostra funzione <tt>draw_brush()</tt>, che efettivamente disegna sullo
|
||
|
schermo, diventa quindi:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
/* Disegna un rettangolo sullo schermo */
|
||
|
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>
|
||
|
|
||
|
Dopo aver disegnato il rettangolo sulla pixmap, chiamiamo la funzione:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void gtk_widget_draw (GtkWidget *widget,
|
||
|
GdkRectangle *area);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
che notifica a X che l'area data dal parametro <tt>area</tt> deve essere
|
||
|
aggiornata. X poi genererà un evento di esposizione (che può essere combinato
|
||
|
con le aree passate da diverse chiamate a <tt>gtk_widget_draw()</tt>) che
|
||
|
farà sì che il nostro gestore dell'evento di esposizione, copi le porzioni
|
||
|
rilevanti sullo schermo.
|
||
|
|
||
|
<p>
|
||
|
Abbiamo a questo punto creato tutto il programma di disegno, tranne che
|
||
|
per qualche dettaglio irrilevante come la creazione della finestra principale.
|
||
|
Il codice sorgente completo è reperibile dove avete ottenuto questo tutorial.
|
||
|
|
||
|
<sect1> Aggiungere il supporto per XInput
|
||
|
|
||
|
<p>
|
||
|
Al giorno d'oggi è possibile acquistare dei dispositivi abbastanza a buon
|
||
|
mercato, come tavolette grafice, che permettono di disegnare con una
|
||
|
espressività artistica molto semplificata rispetto ad un mouse.
|
||
|
Il modo più semplice per usare questi dispositivi è di sostituirli
|
||
|
semplicemente al mouse, ma in questo modo si perdono molti dei loro
|
||
|
vantaggi, come:
|
||
|
|
||
|
<itemize>
|
||
|
<item> Sensibilità alla pressione
|
||
|
<item> Sensibilità all'inclinazione
|
||
|
<item> Posizionamento infra-pixel
|
||
|
<item> Ingressi multipli (per esempio, uno stilo che contiene sia una ``matita''
|
||
|
sia una ``gomma'')
|
||
|
</itemize>
|
||
|
|
||
|
Per ulteriori informazioni sulle estensioni XInput, vedere l'<url
|
||
|
url="http://www.msc.cornell.edu/~otaylor/xinput/XInput-HOWTO.html"
|
||
|
name="XInput-HOWTO">.
|
||
|
|
||
|
<p>
|
||
|
Se esaminiamo, per esempio, la definizione completa della struttura
|
||
|
GdkEventMotion, possiamo vedere che contiene dei campi per il supporto
|
||
|
delle informazioni estese dai dispositivi.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
struct _GdkEventMotion
|
||
|
{
|
||
|
GdkEventType type;
|
||
|
GdkWindow *window;
|
||
|
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/ fornisce la pressione sotto forma di un numero decimale
|
||
|
compreso fra 0 e 1. <tt/xtilt/ e <tt/ytilt/ possono assumere valori
|
||
|
compresi fra -1 e 1, corrispondenti al grado di inclinazione in ciascuna
|
||
|
direzione. <tt/source/ e <tt/deviceid/ specificano il dispositivo per il
|
||
|
quale si è verificato l'evento in due modi distinti. <tt/source/ da alcune
|
||
|
semplici informazioni sul tipo di dispositivo, e può assumere i valori:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
GDK_SOURCE_MOUSE
|
||
|
GDK_SOURCE_PEN
|
||
|
GDK_SOURCE_ERASER
|
||
|
GDK_SOURCE_CURSOR
|
||
|
</verb></tscreen>
|
||
|
|
||
|
<tt/deviceid/ specifica invece un identificativo numerico univoco per il
|
||
|
dispositivo. Questo può essere a sua volta utilizzato per avere ulteriori
|
||
|
informazioni sul dispositivo tramite la chiamata a <tt/gdk_input_list_devices()/
|
||
|
(vedi sotto). Il valore speciale <tt/GDK_CORE_POINTER/ viene usato per identificare
|
||
|
il dispositivo di puntamento principale (di solito il mouse).
|
||
|
|
||
|
<sect2> Abilitare le informazioni estese
|
||
|
|
||
|
<p>
|
||
|
Per far sì che GTK sappia che ci interessano le informazioni estese dai
|
||
|
dispositivi, basta aggiungere un'unica linea al nostro programma:
|
||
|
|
||
|
<tscreen><verb>
|
||
|
gtk_widget_set_extension_events (drawing_area, GDK_EXTENSION_EVENTS_CURSOR);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Dando il valore <tt/GDK_EXTENSION_EVENTS_CURSOR/, diciamo che ci interessano
|
||
|
gli eventi relativi alle estensioni, ma solo se non dobbiamo disegnare da noi
|
||
|
il nostro cursore. Si veda più sotto alla sezione <ref
|
||
|
id="sec_Further_Sophistications" name="Ulteriori Sofisticazioni"> per ulteriori
|
||
|
informazioni sul modo si disegnare i cursori. Potremmo anche dare i valori
|
||
|
<tt/GDK_EXTENSION_EVENTS_ALL/ se vogliamo disegnare il nostro cursore o
|
||
|
<tt/GDK_EXTENSION_EVENTS_NONE/ se vogliamo tornare alle condizioni predefinite.
|
||
|
|
||
|
<p>
|
||
|
Comunque, non finisce tutto qui. Non ci sono estensioni abilitate per difetto.
|
||
|
Abbiamo bisogno di un meccanismo per permettere agli utenti l'abilitazione e
|
||
|
la configurazione delle estensioni dei loro dispositivi, GTK fornisce il
|
||
|
widget InputDialog per automatizzare questo processo. La seguente procedura
|
||
|
mostra come gestire un widget InputDialog. Crea la finestra di dialogo nel
|
||
|
caso non sia presente, mentre la porta in primo piano in 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>
|
||
|
|
||
|
(Notate come gestiamo questo dialogo. Con la connessione del segnale
|
||
|
``destroy'' ci assicuriamo di non tenerci in giro il puntatore al dialogo
|
||
|
dopo che lo abbiamo distrutto, cosa che potrebbe portare ad un errore di
|
||
|
segmentazione.)
|
||
|
|
||
|
<p>
|
||
|
L'InputDialog ha due pulsanti, ``Close'' e ``Save'', i quali non hanno alcuna
|
||
|
azione predefinita assegnata ad essi. Nella funzione precedente, abbiamo
|
||
|
fatto in modo che ``Close'' nasconda la finestra di dialogo, e abbiamo nascosto
|
||
|
il pulsante ``Save'' dal momento che in questo programma non implementiamo il
|
||
|
salvataggio delle opzioni di XInput.
|
||
|
|
||
|
<sect2> Usare le informazioni estese
|
||
|
|
||
|
<p>
|
||
|
Una volta abilitato il dipositivo, possiamo usare le informazioni estese
|
||
|
che si trovano nei corrispondenti campi delle strutture che descrivono gli
|
||
|
eventi. A dire il vero, l'utilizzo di questi campi è sempre sicuro, perché
|
||
|
sono tutti posti per difetto a valori ragionevoli ancje quando la gestione
|
||
|
degli eventi estesi non è abilitata.
|
||
|
|
||
|
<p>
|
||
|
Un cambiamento che dobbiamo fare è di chiamare <tt/gdk_input_window_get_pointer()/
|
||
|
invece di <tt/gdk_window_get_pointer/. Ciò si rende necessario perché
|
||
|
<tt/gdk_window_get_pointer/ non restituisce le informazioni esetese.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
void gdk_input_window_get_pointer (GdkWindow *window,
|
||
|
guint32 deviceid,
|
||
|
gdouble *x,
|
||
|
gdouble *y,
|
||
|
gdouble *pressure,
|
||
|
gdouble *xtilt,
|
||
|
gdouble *ytilt,
|
||
|
GdkModifierType *mask);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
Quando chiamiamo questa funzione, dobbiamo specificare l'identificativo
|
||
|
del dispositivo e la finestra. Normalmente questo identificativo lo si
|
||
|
ottiene dal campo <tt/deviceid/ della struttura dell'evento.
|
||
|
Questa funzione restituirà valori ragionevoli nel caso che la gestione
|
||
|
degli eventi estesi non sia attivata (in questo caso, <tt/event->deviceid/
|
||
|
avrà il valore <tt/GDK_CORE_POINTER/).
|
||
|
|
||
|
Quindi, la struttura di base dei gestori degli eventi relativi alla
|
||
|
pressione di bottoni e ai movomenti non cambia molto - abbiamo solo
|
||
|
bisogno di aggiungere il codice necessario per tenere conto delle
|
||
|
informazioni estese.
|
||
|
|
||
|
<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>
|
||
|
|
||
|
Avremo anche bisogno di fare qualcosa con queste nuove informazioni. La
|
||
|
nostra nuova funzione <tt/draw_brush/ disegna con un colore diverso per
|
||
|
ogni <tt/event->source/ e cambia la dimensione della linea in funzione
|
||
|
della pressione.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
/* Disegna un rettangolo sullo schermo, con la dimensione dipendente
|
||
|
dalla pressione e il colore dipendente dal tipo di dispositivo */
|
||
|
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> Trovare ulteriori informazioni su di un dispositivo
|
||
|
|
||
|
<p>
|
||
|
Come esempio del modo di trovare altre informazioni su di un dispositivo,
|
||
|
il nostro programma stamperà il nome di ogni dispositivo che genera un
|
||
|
evento di pressione di un pulsante. Per avere il nome di un dispositivo,
|
||
|
chiamiamo la funzione
|
||
|
|
||
|
<tscreen><verb>
|
||
|
GList *gdk_input_list_devices (void);
|
||
|
</verb></tscreen>
|
||
|
|
||
|
che restituisce una GList (un tipo di lista collegata che si trova nella
|
||
|
libreria glib) di strutture di tipo GdkDeviceInfo. La definizione di
|
||
|
GdkDeviceInfo è la seguente:
|
||
|
|
||
|
<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>
|
||
|
|
||
|
La maggior parte di questi campi rappresentano informazioni di configurazione
|
||
|
che potete ignorare a meno che non implementiate il salvataggio della
|
||
|
configurazione di un XInput. Quelle che ci interessano sono <tt/name/, che
|
||
|
è semplicemente il nome che X assegna al dispositivo, e <tt/has_cursor/. Anche
|
||
|
<tt/has_cursor/ non è informazione di configurazione, e indica, nel caso
|
||
|
abbia valore ``falso'', che dobbiamo disegnare da soli il nostro cursore. Ma
|
||
|
dal momento che abbiamo specificato <tt/GDK_EXTENSION_EVENTS_CURSOR/,
|
||
|
possiamo anche non preoccuparcene.
|
||
|
|
||
|
<p>
|
||
|
|
||
|
La nostra funzione <tt/print_button_press()/ scorre semplicemente la lista
|
||
|
che è stata restituita finché non trova il valore corretto, e poi stampa
|
||
|
il nome del dispositivo.
|
||
|
|
||
|
<tscreen><verb>
|
||
|
static void
|
||
|
print_button_press (guint32 deviceid)
|
||
|
{
|
||
|
GList *tmp_list;
|
||
|
|
||
|
/* gdk_input_list_devices restituisce una lista interna, così poi
|
||
|
non dobbiamo liberarla */
|
||
|
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>
|
||
|
Questo completa i cambiamenti necessari per usare gli XInput nel nostro
|
||
|
programma. Come per la prima versione, i sorgenti completi sono prelevabili
|
||
|
da dove avete prelevato questo tutorial.
|
||
|
|
||
|
<sect2> Ulteriori sofisticazioni <label id="sec_Further_Sophistications">
|
||
|
|
||
|
<p>
|
||
|
Anche se ora il nostro programma supporta XInput pittosto bene, gli mancano
|
||
|
alcune caratteristiche che probabilmente vorremmo mettere in una applicazione
|
||
|
completa. In primo luogo, probabilmente all'utente non farà piacere dover
|
||
|
configurare i propri dispositivi ogni volta che lanciano il programma, per
|
||
|
cui dovremmo dare la possibilità di salvare la configurazione dei dispositivi.
|
||
|
Ciò può essere fatto scorrendo la lista restituita da <tt/gdk_input_list_devices()/
|
||
|
e scrivendo la configurazione su di un file.
|
||
|
|
||
|
<p>
|
||
|
Per tornare allo stato salvato la prossima volta che il programma viene
|
||
|
eseguito, GDK mette a disposizione delle funzioni per cambiare la configurazione
|
||
|
dei dispositivi:
|
||
|
|
||
|
<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 restituita da <tt/gdk_input_list_devices()/ non dovrebbe
|
||
|
essere modificata direttamente.) Un esempio di come fare può essere
|
||
|
trovato nel programma di disegno gsumi (disponibile da <htmlurl
|
||
|
url="http://www.msc.cornell.edu/~otaylor/gsumi/"
|
||
|
name="http://www.msc.cornell.edu/~otaylor/gsumi/">). Sarebbe bello
|
||
|
avere alla fine un modo standard di recuperare le informazioni per tutte
|
||
|
le applicazioni. Questo probabilmente appartiene ad un livello un po'
|
||
|
più elevato ripetto a GTK, forse alla libreria GNOME.
|
||
|
|
||
|
<p>
|
||
|
Un'altra notevole omissione a cui abbiamo accennato precedentemente è il
|
||
|
fatto di non disegnare il cursore direttamente. Piattaforme diverse da
|
||
|
XFree86 non permettono in questo momento di usare contemporaneamente un
|
||
|
dispositivo sia come puntatore principale sia direttamente da una
|
||
|
applicazione. Vedere <url url="http://www.msc.cornell.edu/~otaylor/xinput/XInput-HOWTO.html"
|
||
|
name="XInput-HOWTO"> per ulteriori informazioni. Ciò significa che le
|
||
|
applicazioni che vogliono rivolgersi al pubblico più ampio dovranno prevedere
|
||
|
di disegnare esse stesse il proprio cursore.
|
||
|
|
||
|
<p>
|
||
|
Un'applicazione che voglia disegnare il proprio cursore dovrà fare due cose:
|
||
|
determinare se il dispositivo corrente necessita che venga disegnato un
|
||
|
cursore, e determinare se il dispositivo corrente è in prossimità. (Se il
|
||
|
dispositivo è una tavoletta grafica, un tocco di finezza è fare sparire
|
||
|
il puntatore quando lo stilo viene sollevato dalla tavoletta. Quando c'è
|
||
|
contatto fra lo stilo e la tavoletta, si dice che il dispositivo è ``in
|
||
|
prossimità".) La prima cosa viene fatta scorrendo la lista dei dispositivi,
|
||
|
come abbiamo fatto per trovare il nome del dispositivo. La seconda cosa
|
||
|
viene ottenuta selezionando gli eventi ``proximity_out''. Un esempio di
|
||
|
disegno del proprio cursore si trova nel programma 'testinput' incluso nella
|
||
|
distribuzione di GTK.
|
||
|
|
||
|
<sect>Consigli per scrivere Applicazioni GTK
|
||
|
|
||
|
<p>
|
||
|
|
||
|
Questa sezione è semplicemente una raccolta di saggezza, una
|
||
|
guida di stile e un aiuto per creare buone applicazioni GTK. E' totalmente
|
||
|
inutile per ora perché è solamente un appunto.
|
||
|
|
||
|
Usa autoconf e automake! Sono tuoi amici :) Ho intenzione di fare una
|
||
|
piccola introduzione su di loro qui.
|
||
|
|
||
|
<sect>Contributi
|
||
|
<p>
|
||
|
|
||
|
Questo documento, come molti altri grandi software fuori di qui, è stato
|
||
|
creato da volontari. Se sai tutto quello che c'è da sapere su GTK e non
|
||
|
lo hai trovato qui allora considera la possibilità di contribuire a questo
|
||
|
documento.
|
||
|
|
||
|
<p>
|
||
|
Se decidi di contribuire, per favore trasmettimi il tuo testo a
|
||
|
<tt><htmlurl url="mailto:slow@intergate.bc.ca"
|
||
|
name="slow@intergate.bc.ca"></tt>. Inoltre, Si consapevole che l'intero
|
||
|
documento è ``free'', e ogni tua aggiunta sarà considerata allo stesso modo.
|
||
|
Per questo motivo le persone possono usare porzioni dei tuoi esempi nei loro
|
||
|
programmi, copie di questo documento possono essere distribuite all'infinito,
|
||
|
ecc...
|
||
|
|
||
|
<p>
|
||
|
|
||
|
Grazie.
|
||
|
|
||
|
|
||
|
<sect>Credits
|
||
|
<p>
|
||
|
Voglio qui ringraziare le persone che seguono, per il loro contributo
|
||
|
alla stesura di questo testo.
|
||
|
|
||
|
<itemize>
|
||
|
<item>Bawer Dagdeviren, <tt><htmlurl url="mailto:chamele0n@geocities.com"
|
||
|
name="chamele0n@geocities.com"></tt> per il tutorial sui menù.
|
||
|
|
||
|
<item>Raph Levien, <tt><htmlurl url="mailto:raph@acm.org"
|
||
|
name="raph@acm.org"></tt>
|
||
|
per il "hello world" alla GTK, l'immpacchettamento del widget, e in generale
|
||
|
per tutta la sua saggezza.
|
||
|
Lui ha anche donato una casa per questo tutorial.
|
||
|
|
||
|
<item>Peter Mattis, <tt><htmlurl url="mailto:petm@xcf.berkeley.edu"
|
||
|
name="petm@xcf.berkeley.edu"></tt> Per il più semplice programma GTK e l'abilità
|
||
|
di farlo. :)
|
||
|
|
||
|
<item>Werner Koch <tt><htmlurl url="mailto:werner.koch@guug.de"
|
||
|
name="werner.koch@guug.de"></tt> per la conversione da testo semplice a SGML
|
||
|
e la gerarchia delle classi di widget.
|
||
|
|
||
|
<item>Mark Crichton <tt><htmlurl url="mailto:crichton@expert.cc.purdue.edu"
|
||
|
name="crichton@expert.cc.purdue.edu"></tt> per il codice della "MenuFactory"
|
||
|
e per la parte sull'impacchettamento nelle tabelle del tutorial.
|
||
|
|
||
|
<item>Owen Taylor <tt><htmlurl url="mailto:owt1@cornell.edu"
|
||
|
name="mailto:owt1@cornell.edu"></tt> per la sezione del widget EventBox
|
||
|
(e il patch alla distribuzione). Lui è anche responsabile per il codice
|
||
|
e il tutorial delle selezioni, come per la sezione sulla scrittura di un
|
||
|
proprio widget, e l'applicazione d'esempio. Grazie di tutto Owen.
|
||
|
|
||
|
<item>Mark VanderBoom <tt><htmlurl url="mailto:mvboom42@calvin.edu"
|
||
|
name="mailto:mailto:mvboom42@calvin.edu"></tt> per il suo meraviglioso lavoro
|
||
|
sul Notebook, Progres Bar, Dialogs e File selection. Grazie molto Mark. Sei
|
||
|
stato di grande aiuto.
|
||
|
|
||
|
<item>Tim Janik <tt><htmlurl url="mailto:timj@psynet.net"
|
||
|
name="mailto:timj@psynet.net"></tt> per il suo grande lavoro sul widget List.
|
||
|
Grazie Tim :)
|
||
|
|
||
|
<item> Michael K. Johnson <tt><htmlurl url="mailto:johnsonm@redhat.com"
|
||
|
name="johnsonm@redhat.com"> </tt> per le informazioni e il codice dei menu
|
||
|
a comparsa.
|
||
|
|
||
|
</itemize>
|
||
|
<p>
|
||
|
E a tutti voi che avete fatto commenti e avete aiutato a raffinare questo documento.
|
||
|
<p>
|
||
|
|
||
|
Thanks.
|
||
|
|
||
|
<sect> Copying
|
||
|
<p>
|
||
|
This tutorial is Copyright (c) 1997 Ian Main
|
||
|
|
||
|
La traduzione italiana è sotto Copyright (c) 1997-1998 di Michel Morelli,
|
||
|
Daniele Canazza e Antonio Schifano.
|
||
|
|
||
|
<p>
|
||
|
|
||
|
This program is free software; you can redistribute it and/or
|
||
|
modify it under the terms of the GNU General Public License
|
||
|
as published by the Free Software Foundation; either version 2
|
||
|
of the License, or (at your option) any later version.
|
||
|
<p>
|
||
|
This program 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 General Public License for more details.
|
||
|
<p>
|
||
|
You should have received a copy of the GNU General Public License
|
||
|
along with this program; if not, write to the Free Software
|
||
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||
|
|
||
|
</article>
|
||
|
|