diff options
author | PST 1998 Shawn T. Amundson <amundson@gimp.org> | 1998-03-12 18:23:11 +0000 |
---|---|---|
committer | Shawn Amundson <amundson@src.gnome.org> | 1998-03-12 18:23:11 +0000 |
commit | affaf4f9d265522c71f1a9ba4b359094c00dc18a (patch) | |
tree | 2ed9182335ff959014320e46f7b1e1b76d27c39a /docs/gtk_tut_it.sgml | |
parent | 4f3495f955fb0798c18325c4d061891019e6b4f5 (diff) | |
download | gdk-pixbuf-affaf4f9d265522c71f1a9ba4b359094c00dc18a.tar.gz |
I just remembered this commit failed before because
of problems with the cvs server... connection timed out.
Wed Mar 11 14:36:48 PST 1998 Shawn T. Amundson <amundson@gimp.org>
* gtk/docs/: added tutorial, changed some files around to
make more sense.
Diffstat (limited to 'docs/gtk_tut_it.sgml')
-rw-r--r-- | docs/gtk_tut_it.sgml | 8340 |
1 files changed, 8340 insertions, 0 deletions
diff --git a/docs/gtk_tut_it.sgml b/docs/gtk_tut_it.sgml new file mode 100644 index 000000000..9ed24eb86 --- /dev/null +++ b/docs/gtk_tut_it.sgml @@ -0,0 +1,8340 @@ + +<!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> + |