Sophie

Sophie

distrib > Mandriva > 2010.2 > x86_64 > by-pkgid > 49809f4e7890398acdc595e561edfff0 > files > 217

lib64gtk+-devel-1.2.10-51mdv2010.1.x86_64.rpm

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<HTML>
<HEAD>
 <META NAME="GENERATOR" CONTENT="SGML-Tools 1.0.9">
 <TITLE>GTK Tutorial: Scrivere un proprio Widget</TITLE>
 <LINK HREF="gtk_tut_it-20.html" REL=next>
 <LINK HREF="gtk_tut_it-18.html" REL=previous>
 <LINK HREF="gtk_tut_it.html#toc19" REL=contents>
</HEAD>
<BODY BGCOLOR="#FFFFFF">
<A HREF="gtk_tut_it-20.html">Avanti</A>
<A HREF="gtk_tut_it-18.html">Indietro</A>
<A HREF="gtk_tut_it.html#toc19">Indice</A>
<HR NOSHADE>
<H2><A NAME="s19">19. Scrivere un proprio Widget</A></H2>

<H2><A NAME="ss19.1">19.1 Panoramica</A>
</H2>

<P>Anche se la distribuzione GTK contiene molto tipi di widget che possono
coprire molte necessit&agrave; basilari, pu&ograve; essere necessario costruirsi
un proprio widget. GTK usa molto l'ereditariet&agrave; tra i vari
widget e, di solito, vi &egrave; un widget che si avvicina a quello che ti
servirebbe, ed &egrave; 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&agrave; creato. Questo eviter&agrave; un duplicazione
di lavoro e far&agrave; s&igrave; che i widget non-GTK puri siano minimi, cos&igrave; 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&igrave; che le altre persone ne possano
beneficiare. Il miglioro modo dove farlo &egrave; la  <CODE>gtk-list</CODE>.
<P>I sorgenti completi per i widget di esempio possono essere presi dallo stesso
sito da cui avete scaricato questo tutorial, oppure da:
<P>
<A HREF="http://www.msc.cornell.edu/~otaylor/gtk-gimp/tutorial">http://www.msc.cornell.edu/~otaylor/gtk-gimp/tutorial</A><P>
<P>
<H2><A NAME="ss19.2">19.2 L'anatomia di un widget</A>
</H2>

<P>Per creare un nuovo widget &egrave; importante aver capito come gli ogetti 
di GTK lavorano. Questa sezione &egrave; solo una breve spiegazione. Guarda la
documentazione di riferimento per maggiori dettagli.
<P>
<P>I widget GTK sono implementati in un modo orientato agli oggetti,
anche se usando il C standard. Questo aumenta notevolmente la portabilit&agrave;
e la stabilit&agrave;, 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) &egrave; 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&agrave;
il primo campo della struttura di una classe deve essere una copia della
struttura della classe genitore. La dichiarazione della struttura della 
classe GtkButton &egrave;:
<P>
<BLOCKQUOTE><CODE>
<PRE>
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);
};
</PRE>
</CODE></BLOCKQUOTE>
<P>
<P>Quando un bottone viene trattato come un contenitore (ad esempio quando viene 
ridimensionato) si pu&ograve; fare il cast della struttura della sua classe con la 
GtkContainerClass, e usare i campi rilevanti per gestire i segnali.
<P>
<P>C'&egrave; 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:
<P>
<BLOCKQUOTE><CODE>
<PRE>
struct _GtkButton
{
  GtkContainer container;

  GtkWidget *child;

  guint in_button : 1;
  guint button_down : 1;
};
</PRE>
</CODE></BLOCKQUOTE>
<P>
<P>Si noti che, similmente alla struttura della classe, il primo campo
&egrave; la struttura dell'oggetto della classe madre, cos&igrave; che, se necessario, si pu&ograve; fare il
cast di questa struttura con quella dell'oggetto della classe madre.
<P>
<H2><A NAME="ss19.3">19.3 Creare un Widget composto</A>
</H2>

<H3>Introduzione</H3>

<P>Un tipo di widget a cui potreste essere interessati &egrave; un widget che
&egrave; 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>
<P>Il widget di esempio che creeremo in questo capitolo &egrave; 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.
<P>
<H3>Scegliere la classe madre</H3>

<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 &egrave; la classe
Dialog. Visto che i nostri bottoni sono inseriti in una tabella, &egrave; 
naturale pensare che la nostra classe madre possa essere la GtkTable.
Sfortunatamente, cos&igrave; non &egrave;. La creazione di un widget &egrave; diviso
tra 2 funzioni : la funzione <CODE>WIDGETNAME_new()</CODE> che viene invocata
dall'utente, e la funzione  <CODE>WIDGETNAME_init()</CODE> che ha il compito
principale di inizializzare il widget che &egrave; indipendente dai valori
passati alla funzione <CODE>_new()</CODE>. 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&agrave;. A meno che non vogliamo duplicare molte delle 
fuinzionalit&agrave; della <CODE>gtk_table_new()</CODE> 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.
<P>
<H3>Il File Header</H3>

<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:
<P>
<BLOCKQUOTE><CODE>
<PRE>
#ifndef __TICTACTOE_H__
#define __TICTACTOE_H__
.
.
.
#endif /* __TICTACTOE_H__ */
</PRE>
</CODE></BLOCKQUOTE>
<P>E per far felici i programmi in C++ che includono il nostro file header, in:
<P>
<BLOCKQUOTE><CODE>
<PRE>
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
.
.
.
#ifdef __cplusplus
}
#endif /* __cplusplus */
</PRE>
</CODE></BLOCKQUOTE>
<P>Insieme alle funzioni e alle strutture, dichiariamo tre macro 
standard nel nostro file header, <CODE>TICTACTOE(obj)</CODE>,
<CODE>TICTACTOE_CLASS(klass)</CODE>, e <CODE>IS_TICTACTOE(obj)</CODE>, 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 &egrave; un widget Tictactoe.
<P>
<P>Qui vi &egrave; il file header completo:
<P>
<BLOCKQUOTE><CODE>
<PRE>
/* tictactoe.h */

#ifndef __TICTACTOE_H__
#define __TICTACTOE_H__

#include &lt;gdk/gdk.h>
#include &lt;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__ */
</PRE>
</CODE></BLOCKQUOTE>
<P>
<H3>La funzione <CODE>_get_type()</CODE></H3>

<P>Continuiamo ora con l'implementazione del nostro widget. Una funzione
basilare di ogni widget &egrave; la funzione <CODE>WIDGETNAME_get_type()</CODE>.
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.
<P>
<BLOCKQUOTE><CODE>
<PRE>
guint
tictactoe_get_type ()
{
  static guint ttt_type = 0;

  if (!ttt_type)
    {
      GtkTypeInfo ttt_info =
      {
        "Tictactoe",
        sizeof (Tictactoe),
        sizeof (TictactoeClass),
        (GtkClassInitFunc) tictactoe_class_init,
        (GtkObjectInitFunc) tictactoe_init,
        (GtkArgSetFunc) NULL,
        (GtkArgGetFunc) NULL
      };

      ttt_type = gtk_type_unique (gtk_vbox_get_type (), &amp;ttt_info);
    }

  return ttt_type;
}
</PRE>
</CODE></BLOCKQUOTE>
<P>
<P>La struttura GtkTypeInfo ha la seguente definizione:
<P>
<BLOCKQUOTE><CODE>
<PRE>
struct _GtkTypeInfo
{
  gchar *type_name;
  guint object_size;
  guint class_size;
  GtkClassInitFunc class_init_func;
  GtkObjectInitFunc object_init_func;
  GtkArgSetFunc arg_set_func;
  GtkArgGetFunc arg_get_func;
};
</PRE>
</CODE></BLOCKQUOTE>
<P>
<P>I campi di questa struttura sono abbastanza auto-esplicativi.
Ignoreremo, per ora, i campi  <CODE>arg_set_func</CODE> e <CODE>arg_get_func</CODE>:
hanno un ruolo importante, ma ancora largamente non
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.
<P>
<H3>La funzione <CODE>_class_init()</CODE> </H3>

<P>La funzione <CODE>WIDGETNAME_class_init()</CODE> inizialiazza i campi della
struttura della classe del widget, e setta ogni segnale della classe.
Per il nostro widget Tictactoe ha il seguente aspetto:
<P>
<BLOCKQUOTE><CODE>
<PRE>

enum {
  TICTACTOE_SIGNAL,
  LAST_SIGNAL
};

static gint tictactoe_signals[LAST_SIGNAL] = { 0 };

static void
tictactoe_class_init (TictactoeClass *class)
{
  GtkObjectClass *object_class;

  object_class = (GtkObjectClass*) class;
  
  tictactoe_signals[TICTACTOE_SIGNAL] = gtk_signal_new ("tictactoe",
                                         GTK_RUN_FIRST,
                                         object_class->type,
                                         GTK_SIGNAL_OFFSET (TictactoeClass, tictactoe),
                                         gtk_signal_default_marshaller, GTK_TYPE_NONE, 0);


  gtk_object_class_add_signals (object_class, tictactoe_signals, LAST_SIGNAL);

  class->tictactoe = NULL;
}
</PRE>
</CODE></BLOCKQUOTE>
<P>
<P>Il nostro  widget ha semplicemente il segnale ``tictactoe'' che &egrave;
invocato quando una riga, colonna o diagonale &egrave; 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.
<P>La funzione:
<BLOCKQUOTE><CODE>
<PRE>
gint   gtk_signal_new (const gchar         *name,
                       GtkSignalRunType    run_type,
                       GtkType             object_type,
                       gint                function_offset,
                       GtkSignalMarshaller marshaller,
                       GtkType             return_val,
                       guint               nparams,
                       ...);
</PRE>
</CODE></BLOCKQUOTE>
<P>crea un nuovo segnale. I parametri sono:
<P>
<UL>
<LI> <CODE>name</CODE>: Il nome del segnale.</LI>
<LI> <CODE>run_type</CODE>: Se il segstore predefinito viene eseguito prima o dopo
di quello dell'utente. Di norma questo sar&agrave; <CODE>GTK_RUN_FIRST</CODE>, o <CODE>GTK_RUN_LAST</CODE>,
anche se ci sono altre possibilit&agrave;.</LI>
<LI> <CODE>object_type</CODE>: l'identificativo dell'oggetto a cui questo segnale si 
riferisce. Esso sar&agrave; anche applicato agli oggetti discendenti.</LI>
<LI> <CODE>function_offset</CODE>: L'offset nella struttura della classe di un
puntatore al gestore predefinito.</LI>
<LI> <CODE>marshaller</CODE>: una funzione che &egrave; 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 <CODE>gtk_signal_default_marshaller</CODE></LI>
<LI> <CODE>return_val</CODE>: Il tipo del valore di ritorno.</LI>
<LI> <CODE>nparams</CODE>: Il numero di parametri del gestore di segnali (oltre
ai due predefiniti menzionati sopra)</LI>
<LI> <CODE>...</CODE>: i tipi dei parametri</LI>
</UL>
<P>Quando si specificano i tipi, si usa l'enumerazione <CODE>GtkType</CODE>:
<P>
<BLOCKQUOTE><CODE>
<PRE>
typedef enum
{
  GTK_TYPE_INVALID,
  GTK_TYPE_NONE,
  GTK_TYPE_CHAR,
  GTK_TYPE_BOOL,
  GTK_TYPE_INT,
  GTK_TYPE_UINT,
  GTK_TYPE_LONG,
  GTK_TYPE_ULONG,
  GTK_TYPE_FLOAT,
  GTK_TYPE_DOUBLE,
  GTK_TYPE_STRING,
  GTK_TYPE_ENUM,
  GTK_TYPE_FLAGS,
  GTK_TYPE_BOXED,
  GTK_TYPE_FOREIGN,
  GTK_TYPE_CALLBACK,
  GTK_TYPE_ARGS,

  GTK_TYPE_POINTER,

  /* sarebbe bello poter togliere alla fine i prossimi due */
  GTK_TYPE_SIGNAL,
  GTK_TYPE_C_CALLBACK,

  GTK_TYPE_OBJECT

} GtkFundamentalType;
</PRE>
</CODE></BLOCKQUOTE>
<P>
<P><CODE>gtk_signal_new()</CODE> restituisce un identificatore unico intero per il segnale, 
che memorizziamo nel vettore  <CODE>tictactoe_signals</CODE>, 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 <CODE>TICTACTOE()</CODE>, 
quindi l'abbiamo chiamato  <CODE>TICTACTOE_SIGNAL</CODE>
<P>Dopo aver creato un nostro segnale, abbiamo bisogno di dire a GTK
di associare il nostro segnale alla classe Tictactoe. Lo facciamo
invocando <CODE>gtk_object_class_add_signals()</CODE>. Settiamo quindi a NULL
il puntatore che punta al gestore predefinito per il segnale 
``tictactoe'' a NULL, indicando che non ci sono azioni predefinite.
<P>
<H3>La funzione <CODE>_init()</CODE></H3>

<P>
<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.
<P>
<BLOCKQUOTE><CODE>
<PRE>

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&lt;3; i++)
    for (j=0;j&lt;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]);
      }
}
</PRE>
</CODE></BLOCKQUOTE>
<P>
<H3>E il resto...</H3>

<P>
<P>C'&egrave; 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 &egrave; 
convenzionalmente chiamata <CODE>WIDGETNAME_new()</CODE>. 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>
<P><CODE>tictactoe_clear()</CODE> &egrave; una funzione pubblica che resetta tutti i 
bottoni, nel widget, allo stato iniziale (non premuto). Notate l'uso 
di <CODE>gtk_signal_handler_block_by_data()</CODE> per impedire che il nostro
gestore dei segnali venga attivato quando non ce n'&egrave; bisogno.
<P>
<P><CODE>tictactoe_toggle()</CODE> &egrave; il gestore del segnale che viene invocato 
quando l'utente preme il bottone. Esso guarda se vi &egrave;
qualche combinazione vincente che coinvolge i bottoni premuti, e nel
caso ci fosse, emette il segnale ``tictactoe''.
<P>
<BLOCKQUOTE><CODE>
<PRE>
  
GtkWidget*
tictactoe_new ()
{
  return GTK_WIDGET ( gtk_type_new (tictactoe_get_type ()));
}

void           
tictactoe_clear (Tictactoe *ttt)
{
  int i,j;

  for (i=0;i&lt;3;i++)
    for (j=0;j&lt;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&lt;8; k++)
    {
      success = TRUE;
      found = FALSE;

      for (i=0;i&lt;3;i++)
        {
          success = success &amp;&amp; 
            GTK_TOGGLE_BUTTON(ttt->buttons[rwins[k][i]][cwins[k][i]])->active;
          found = found ||
            ttt->buttons[rwins[k][i]][cwins[k][i]] == widget;
        }
      
      if (success &amp;&amp; found)
        {
          gtk_signal_emit (GTK_OBJECT (ttt), 
                           tictactoe_signals[TICTACTOE_SIGNAL]);
          break;
        }
    }
}
</PRE>
</CODE></BLOCKQUOTE>
<P>
<P>
<P>E finalmente un programma di esempio che usa il nostro widget
Tictactoe:
<P>
<BLOCKQUOTE><CODE>
<PRE>
#include &lt;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 (&amp;argc, &amp;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;
}
</PRE>
</CODE></BLOCKQUOTE>
<P>
<H2><A NAME="ss19.4">19.4 Creare un widget a partire da zero</A>
</H2>

<H3>Introduzione</H3>

<P>
<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&ograve; trascinare per assegnare il valore.
<P>
<H3>Mostrare un widget sullo schermo</H3>

<P>Ci sono alcuni passi che sono necessari nella visualizzazione sullo
schermo. Dopo che il widget &egrave; stato creato con una chiamata a 
<CODE>WIDGETNAME_new()</CODE>, sono necessarie alcune altre funzioni:
<P>
<UL>
<LI> <CODE>WIDGETNAME_realize()</CODE> &egrave; responsabile della creazione di 
una finestra X per il widget se ne ha una.</LI>
<LI> <CODE>WIDGETNAME_map()</CODE> &egrave; invocata dopo che l'utente ha 
chiamato <CODE>gtk_widget_show()</CODE>. E' responsabile di vedere se il
widget &egrave; attualmente disegnato sullo schermo (<EM>mappato</EM>). Per 
una classe contenitore, essa deve anche creare chiamate alle 
funzioni  <CODE>map()</CODE>> per ogni widget figlio.</LI>
<LI> <CODE>WIDGETNAME_draw()</CODE> &egrave; invocata quando 
<CODE>gtk_widget_draw()</CODE> viene chiamata per il widget o per uno dei suoi
predecessori. Esso fa s&igrave; 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 <CODE>gtk_widget_draw()</CODE> di ogni suo widget
figlio.</LI>
<LI> <CODE>WIDGETNAME_expose()</CODE> &egrave; 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 &egrave; 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&agrave; X che generer&agrave; i necessari eventi di expose).</LI>
</UL>
<P>
<P>Potete notare che le ultime due funzioni sono molto simili, ognuna &egrave;
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 <CODE>draw()</CODE> 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&ograve; ridisegnare solo la finestra interessata, cosa che non &egrave; 
possibile per chiamate a <CODE>draw()</CODE>.
<P>
<P>I widget contenitori, anche se essi non farebbero differenze,
non possono semplicemente usare la funzione <CODE>draw()</CODE> perch&egrave; per i 
loro widget figli la differenza potrebbere essere importante. Comunque, 
sarebbe uno spreco duplicare il codice di disegno nelle due
funzioni. La convenzione &egrave; che questi widget abbiano una funzione
chiamata <CODE>WIDGETNAME_paint()</CODE> che disegna il widget, che &egrave; poi
chiamata dalle funzioni <CODE>draw()</CODE> e <CODE>expose()</CODE>
<P>
<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 <CODE>draw()</CODE> e implementare
solamente la funzione <CODE>expose()</CODE>.
<P>
<H3>Le origini del widget Dial</H3>

<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&igrave;, anche se questa sezione &egrave; intitolata ``Creare
un widget a partire da zero", il nostro widget inizia in realt&agrave; con il codice 
sorgente del widget Range. Questo &egrave; stato preso come punto d'inizio
perche' sarebbe carino se il nostro widget avesse la
stessa interfaccia del widget Scale il quale &egrave; semplicemente una
specializzazione del widget Range. Cos&igrave;, 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&agrave;
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.
<P>
<H3>Le basi</H3>

<P>Una parte del nostro widget potrebbe essere simile
al widget Tictactoe. In primo luogo, abbiamo il file header:
<P>
<BLOCKQUOTE><CODE>
<PRE>
/* 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 &lt;gdk/gdk.h>
#include &lt;gtk/gtkadjustment.h>
#include &lt;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&igrave; 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__ */
</PRE>
</CODE></BLOCKQUOTE>
<P>Essendoci pi&ugrave; cose da fare con questo widget, rispetto al precedente,
abbiamo pi&ugrave; 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.
<P>
<BLOCKQUOTE><CODE>
<PRE>
#include &lt;math.h>
#include &lt;stdio.h>
#include &lt;gtk/gtkmain.h>
#include &lt;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,
        (GtkArgSetFunc) NULL,
        (GtkArgGetFunc) NULL,
      };

      dial_type = gtk_type_unique (gtk_widget_get_type (), &amp;dial_info);
    }

  return dial_type;
}

static void
gtk_dial_class_init (GtkDialClass *class)
{
  GtkObjectClass *object_class;
  GtkWidgetClass *widget_class;

  object_class = (GtkObjectClass*) class;
  widget_class = (GtkWidgetClass*) class;

  parent_class = gtk_type_class (gtk_widget_get_type ());

  object_class->destroy = gtk_dial_destroy;

  widget_class->realize = gtk_dial_realize;
  widget_class->expose_event = gtk_dial_expose;
  widget_class->size_request = gtk_dial_size_request;
  widget_class->size_allocate = gtk_dial_size_allocate;
  widget_class->button_press_event = gtk_dial_button_press;
  widget_class->button_release_event = gtk_dial_button_release;
  widget_class->motion_notify_event = gtk_dial_motion_notify;
}

static void
gtk_dial_init (GtkDial *dial)
{
  dial->button = 0;
  dial->policy = GTK_UPDATE_CONTINUOUS;
  dial->timer = 0;
  dial->radius = 0;
  dial->pointer_width = 0;
  dial->angle = 0.0;
  dial->old_value = 0.0;
  dial->old_lower = 0.0;
  dial->old_upper = 0.0;
  dial->adjustment = NULL;
}

GtkWidget*
gtk_dial_new (GtkAdjustment *adjustment)
{
  GtkDial *dial;

  dial = gtk_type_new (gtk_dial_get_type ());

  if (!adjustment)
    adjustment = (GtkAdjustment*) gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);

  gtk_dial_set_adjustment (dial, adjustment);

  return GTK_WIDGET (dial);
}

static void
gtk_dial_destroy (GtkObject *object)
{
  GtkDial *dial;

  g_return_if_fail (object != NULL);
  g_return_if_fail (GTK_IS_DIAL (object));

  dial = GTK_DIAL (object);

  if (dial->adjustment)
    gtk_object_unref (GTK_OBJECT (dial->adjustment));

  if (GTK_OBJECT_CLASS (parent_class)->destroy)
    (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
}
</PRE>
</CODE></BLOCKQUOTE>
<P>Notate che questa funzione <CODE>init()</CODE> fa meno rispetto all'analoga del
widget Tictactoe, essendo questo un widget non composto, e la 
funzione <CODE>new()</CODE> fa di pi&ugrave;, 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&ugrave;) cos&igrave; che GTK pu&ograve; tener traccia di 
quando &egrave; possibile distruggerlo senza causare guai.
<P>
<P>Inoltre, ci sono alcune funzioni per manipolare le opzioni del widget:
<P>
<BLOCKQUOTE><CODE>
<PRE>
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);
}
</PRE>
</CODE></BLOCKQUOTE>
<P>
<H3><CODE>gtk_dial_realize()</CODE></H3>

<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 <CODE>gdk_window_new()</CODE> una maschera che 
specifica quali campi della struttura GdkWindowAttr non sono vuoti 
(ai rimanenti campi pu&ograve; essere dato il valore predefinito). Anche 
il modo con cui la maschera degli eventi del widget  creata non &egrave;
complicato. Chiameremo <CODE>gtk_widget_get_events()</CODE> per sapere la 
maschera degli eventi che l'utente ha specificato per questo widget
(con <CODE>gtk_widget_set_events()</CODE>) e aggiungeremo gli eventi che ci possono 
interessare.
<P>
<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.
<P>
<BLOCKQUOTE><CODE>
<PRE>
static void
gtk_dial_realize (GtkWidget *widget)
{
  GtkDial *dial;
  GdkWindowAttr attributes;
  gint attributes_mask;

  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_DIAL (widget));

  GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
  dial = GTK_DIAL (widget);

  attributes.x = widget->allocation.x;
  attributes.y = widget->allocation.y;
  attributes.width = widget->allocation.width;
  attributes.height = widget->allocation.height;
  attributes.wclass = GDK_INPUT_OUTPUT;
  attributes.window_type = GDK_WINDOW_CHILD;
  attributes.event_mask = gtk_widget_get_events (widget) | 
    GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | 
    GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK |
    GDK_POINTER_MOTION_HINT_MASK;
  attributes.visual = gtk_widget_get_visual (widget);
  attributes.colormap = gtk_widget_get_colormap (widget);

  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
  widget->window = gdk_window_new (widget->parent->window, &amp;attributes, attributes_mask);

  widget->style = gtk_style_attach (widget->style, widget->window);

  gdk_window_set_user_data (widget->window, widget);

  gtk_style_set_background (widget->style, widget->window, GTK_STATE_ACTIVE);
}
</PRE>
</CODE></BLOCKQUOTE>
<P>
<H3>Negoziazione della dimensione</H3>

<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 &egrave; fatta dalla
funzione  <CODE>gtk_dial_size_request()</CODE>. Non essendo il nostro widget 
un contenitore, e non avendo dei veri limiti per la propria
dimensione, restituiamo semplicemnte un valore ragionevole.
<P>
<BLOCKQUOTE><CODE>
<PRE>
static void 
gtk_dial_size_request (GtkWidget      *widget,
                       GtkRequisition *requisition)
{
  requisition->width = DIAL_DEFAULT_SIZE;
  requisition->height = DIAL_DEFAULT_SIZE;
}
</PRE>
</CODE></BLOCKQUOTE>
<P>
<P>Dopo che tutti i widget hanno restituito una dimensione ideale, viene 
calcolata la disposizione della finestra  e ad ogni widget figlio &egrave;
notificata la propria dimensione attuale . Usualmente, questo sar&agrave; 
almeno quanto richiesto, ma occasionalmente pu&ograve; essere pi&ugrave; piccolo. 
La notifica della dimensione  viene fatta dalla funzione
<CODE>gtk_dial_size_allocate()</CODE>. Notate che questa funzione &egrave; utilizzata
anche quando la finestra X del widget &egrave; spostata o modificata come 
dimensione.
<P>
<BLOCKQUOTE><CODE>
<PRE>
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;
    }
}
</PRE>
</CODE></BLOCKQUOTE>
.
<P>
<H3><CODE>gtk_dial_expose()</CODE></H3>

<P>Come menzionato sopra, tutto il lavoro di questo widget viene fatto nella
gestione dell'evento ``expose''. Non c'&egrave; molto da notare su questo eccetto
l'uso della funzione <CODE>gtk_draw_polygon</CODE> per disegnare il 
puntatore con un'ombreggiatura a tre dimensioni in accordo con il colore
memorizzato nello stile del wiget.
<P>
<BLOCKQUOTE><CODE>
<PRE>
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&lt;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;
}
</PRE>
</CODE></BLOCKQUOTE>
<P>
<H3>Gestione degli eventi</H3>

<P>
<P>Il resto del codice del widget manipola vari tipi di eventi, e non
&egrave; differente da quello che pu&ograve; essere trovato in molte applicazione
GTK. Due tipi di eventi possono verificarsi: l'utente pu&ograve; 
clickare sul widget con il mouse e trascinare per muovere il puntatore, 
o il valore dell'oggetto Adjustmente pu&ograve; cambiare a causa di alcune
circostanze esterne.
<P>
<P>Quando l'utente clicka sul widget, noi vediamo se la pressione
era veramente vicina al puntatore, e se cos&igrave;, memorizziamo il bottone
premuto dall'utente con il campo <CODE>button</CODE> della struttura del
widget, e prendiamo tutti gli eventi del mouse con una chiamata alla
funzione <CODE>gtk_grab_add()</CODE>. Successivi movimenti del mouse causano il 
ricalcolo dei valori di controllo (fatto dalla funzione 
<CODE>gtk_dial_update_mouse</CODE>). Dipendentemente dalla politica che abbiamo
stabilito, gli eventi ``value_changed'' possono essere  generati
istantaneamente (<CODE>GTK_UPDATE_CONTINUOUS</CODE>), dopo un certo tempo aggiunto
con la funzione <CODE>gtk_timeout_add()</CODE> (<CODE>GTK_UPDATE_DELAYED</CODE>), o
solamente quando il bottone del mouse e' rilasciato 
(<CODE>GTK_UPDATE_DISCONTINUOUS</CODE>).
<P>
<BLOCKQUOTE><CODE>
<PRE>
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 &amp;&amp;
      (d_perpendicular &lt; dial->pointer_width/2) &amp;&amp;
      (d_parallel > - dial->pointer_width))
    {
      gtk_grab_add (widget);

      dial->button = event->button;

      gtk_dial_update_mouse (dial, event->x, event->y);
    }

  return FALSE;
}

static gint
gtk_dial_button_release (GtkWidget      *widget,
                          GdkEventButton *event)
{
  GtkDial *dial;

  g_return_val_if_fail (widget != NULL, FALSE);
  g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
  g_return_val_if_fail (event != NULL, FALSE);

  dial = GTK_DIAL (widget);

  if (dial->button == event->button)
    {
      gtk_grab_remove (widget);

      dial->button = 0;

      if (dial->policy == GTK_UPDATE_DELAYED)
        gtk_timeout_remove (dial->timer);
      
      if ((dial->policy != GTK_UPDATE_CONTINUOUS) &amp;&amp;
          (dial->old_value != dial->adjustment->value))
        gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
    }

  return FALSE;
}

static gint
gtk_dial_motion_notify (GtkWidget      *widget,
                         GdkEventMotion *event)
{
  GtkDial *dial;
  GdkModifierType mods;
  gint x, y, mask;

  g_return_val_if_fail (widget != NULL, FALSE);
  g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
  g_return_val_if_fail (event != NULL, FALSE);

  dial = GTK_DIAL (widget);

  if (dial->button != 0)
    {
      x = event->x;
      y = event->y;

      if (event->is_hint || (event->window != widget->window))
        gdk_window_get_pointer (widget->window, &amp;x, &amp;y, &amp;mods);

      switch (dial->button)
        {
        case 1:
          mask = GDK_BUTTON1_MASK;
          break;
        case 2:
          mask = GDK_BUTTON2_MASK;
          break;
        case 3:
          mask = GDK_BUTTON3_MASK;
          break;
        default:
          mask = 0;
          break;
        }

      if (mods &amp; mask)
        gtk_dial_update_mouse (dial, x,y);
    }

  return FALSE;
}

static gint
gtk_dial_timer (GtkDial *dial)
{
  g_return_val_if_fail (dial != NULL, FALSE);
  g_return_val_if_fail (GTK_IS_DIAL (dial), FALSE);

  if (dial->policy == GTK_UPDATE_DELAYED)
    gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");

  return FALSE;
}

static void
gtk_dial_update_mouse (GtkDial *dial, gint x, gint y)
{
  gint xc, yc;
  gfloat old_value;

  g_return_if_fail (dial != NULL);
  g_return_if_fail (GTK_IS_DIAL (dial));

  xc = GTK_WIDGET(dial)->allocation.width / 2;
  yc = GTK_WIDGET(dial)->allocation.height / 2;

  old_value = dial->adjustment->value;
  dial->angle = atan2(yc-y, x-xc);

  if (dial->angle &lt; -M_PI/2.)
    dial->angle += 2*M_PI;

  if (dial->angle &lt; -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);
            }
        }
    }
}
</PRE>
</CODE></BLOCKQUOTE>
<P>
<P>Cambiamenti esterni all'Adjustment sono comunicati al nostro widget
dai segnali ``changed'' e ``value_changed''. Il gestore per 
queste funzioni chiama  <CODE>gtk_dial_update()</CODE> per validare gli
argomenti, calcolare il nuovo angolo del puntatore e ridisegnare il
widget (chiamando <CODE>gtk_widget_draw()</CODE>).
<P>
<BLOCKQUOTE><CODE>
<PRE>
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 &lt; 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;
    }
}
</PRE>
</CODE></BLOCKQUOTE>
<P>
<H3>Possibili Miglioramenti</H3>

<P>
<P>Il widget Dial, da come l'abbiamo costruito, &egrave; 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 &egrave; costituita da file header e
commmenti. Comunque ci sono alcuni miglioramenti che potrebbero essere
fatti a questo widget:
<P>
<UL>
<LI> Se tu provate questo widget, troverete che ci sono alcuni lampeggiamenti
quando il puntatore viene trascinato in giro. Questo 
perch&egrave; l'intero widget &egrave; cancellato ogni volta che il 
puntatore viene mosso, prima di essere ridisegnato. Spesso, il modo migliore
per gestire questo tipo di problema &egrave; 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).
</LI>
<LI> L'utente potrebbe essere abilitato ad usare le frecce su e giu per
incrementare e diminuire il valore.
</LI>
<LI> 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&igrave; 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&ograve; essere trovato nel widget GtkRange.
</LI>
<LI> 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.
</LI>
</UL>
<P>
<H2><A NAME="ss19.5">19.5 Impararne di pi&ugrave;</A>
</H2>

<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 &egrave; lo stesso codice sorgente GTK. Chiedete a voi
stessi alcune cose su come deve essere il widget che volete scrivere: &egrave;
un widget contenitore? dovr&agrave; avere una propria finestra? &egrave; una modifica di 
un widget precedente? Trovate poi un widget simile e iniziate a fargli 
delle modifiche.
Buone Fortuna.
<P>
<HR NOSHADE>
<A HREF="gtk_tut_it-20.html">Avanti</A>
<A HREF="gtk_tut_it-18.html">Indietro</A>
<A HREF="gtk_tut_it.html#toc19">Indice</A>
</BODY>
</HTML>