source: trunk/third/gtk/docs/html/gtk_tut_it-19.html @ 14482

Revision 14482, 47.1 KB checked in by ghudson, 25 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r14481, which included commits to RCS files with non-trunk default branches.
Line 
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
2<HTML>
3<HEAD>
4 <META NAME="GENERATOR" CONTENT="SGML-Tools 1.0.9">
5 <TITLE>GTK Tutorial: Scrivere un proprio Widget</TITLE>
6 <LINK HREF="gtk_tut_it-20.html" REL=next>
7 <LINK HREF="gtk_tut_it-18.html" REL=previous>
8 <LINK HREF="gtk_tut_it.html#toc19" REL=contents>
9</HEAD>
10<BODY BGCOLOR="#FFFFFF">
11<A HREF="gtk_tut_it-20.html">Avanti</A>
12<A HREF="gtk_tut_it-18.html">Indietro</A>
13<A HREF="gtk_tut_it.html#toc19">Indice</A>
14<HR NOSHADE>
15<H2><A NAME="s19">19. Scrivere un proprio Widget</A></H2>
16
17<H2><A NAME="ss19.1">19.1 Panoramica</A>
18</H2>
19
20<P>Anche se la distribuzione GTK contiene molto tipi di widget che possono
21coprire molte necessit&agrave; basilari, pu&ograve; essere necessario costruirsi
22un proprio widget. GTK usa molto l'ereditariet&agrave; tra i vari
23widget e, di solito, vi &egrave; un widget che si avvicina a quello che ti
24servirebbe, ed &egrave; spesso possibile creare un nuovo widget con poche linee
25di codice. Ma prima di iniziare il lavoro su un nuovo widget, vediamo
26se qualcuno non lo ha gi&agrave; creato. Questo eviter&agrave; un duplicazione
27di lavoro e far&agrave; s&igrave; che i widget non-GTK puri siano minimi, cos&igrave; da
28aiutare sia chi crea il codice che chi l'interfaccia per applicazioni GTK
29molto grosse. D'altra parte, quando hai finito di scrivere un widget,
30annuncialo a tutto il mondo cos&igrave; che le altre persone ne possano
31beneficiare. Il miglioro modo dove farlo &egrave; la  <CODE>gtk-list</CODE>.
32<P>I sorgenti completi per i widget di esempio possono essere presi dallo stesso
33sito da cui avete scaricato questo tutorial, oppure da:
34<P>
35<A HREF="http://www.msc.cornell.edu/~otaylor/gtk-gimp/tutorial">http://www.msc.cornell.edu/~otaylor/gtk-gimp/tutorial</A><P>
36<P>
37<H2><A NAME="ss19.2">19.2 L'anatomia di un widget</A>
38</H2>
39
40<P>Per creare un nuovo widget &egrave; importante aver capito come gli ogetti
41di GTK lavorano. Questa sezione &egrave; solo una breve spiegazione. Guarda la
42documentazione di riferimento per maggiori dettagli.
43<P>
44<P>I widget GTK sono implementati in un modo orientato agli oggetti,
45anche se usando il C standard. Questo aumenta notevolmente la portabilit&agrave;
46e la stabilit&agrave;, specialmente per le correnti generazioni di compilatori C++;
47comunque questo significa che chi scrive un widget deve fare attenzione
48ad alcuni dettagli di implementazione. L'informazione comune a tutte le
49istanze di una classe di widget (ad esempio: a tutti i bottoni) &egrave; memorizzata
50<EM>class structure</EM>. C'e' solamente una copia di questo in cui
51sono memorizzate le informazioni riguardanti i segnali della classe
52(assomiglia ad una funzione virtuale in C). Per supportare l'ereditariet&agrave;
53il primo campo della struttura di una classe deve essere una copia della
54struttura della classe genitore. La dichiarazione della struttura della
55classe GtkButton &egrave;:
56<P>
57<BLOCKQUOTE><CODE>
58<PRE>
59struct _GtkButtonClass
60{
61  GtkContainerClass parent_class;
62
63  void (* pressed)  (GtkButton *button);
64  void (* released) (GtkButton *button);
65  void (* clicked)  (GtkButton *button);
66  void (* enter)    (GtkButton *button);
67  void (* leave)    (GtkButton *button);
68};
69</PRE>
70</CODE></BLOCKQUOTE>
71<P>
72<P>Quando un bottone viene trattato come un contenitore (ad esempio quando viene
73ridimensionato) si pu&ograve; fare il cast della struttura della sua classe con la
74GtkContainerClass, e usare i campi rilevanti per gestire i segnali.
75<P>
76<P>C'&egrave; anche una struttura per ogni widget che viene creata
77ad ogni istanza. Questa struttura ha campi per
78memorizzare le informazioni che sono differenti per ogni volta che il widget
79viene istanziato. Chiameremo questa struttura la <EM> struttura
80oggetto</EM>. Per la classe Bottone, questa ha l'aspetto:
81<P>
82<BLOCKQUOTE><CODE>
83<PRE>
84struct _GtkButton
85{
86  GtkContainer container;
87
88  GtkWidget *child;
89
90  guint in_button : 1;
91  guint button_down : 1;
92};
93</PRE>
94</CODE></BLOCKQUOTE>
95<P>
96<P>Si noti che, similmente alla struttura della classe, il primo campo
97&egrave; la struttura dell'oggetto della classe madre, cos&igrave; che, se necessario, si pu&ograve; fare il
98cast di questa struttura con quella dell'oggetto della classe madre.
99<P>
100<H2><A NAME="ss19.3">19.3 Creare un Widget composto</A>
101</H2>
102
103<H3>Introduzione</H3>
104
105<P>Un tipo di widget a cui potreste essere interessati &egrave; un widget che
106&egrave; semplicemnte un aggregato di altri widget GTK. Questo tipo di
107widget non fa nulla che non possa essere fatto creando un nuovo
108widget, ma fornisce un modo conveniente per inscatolare elementi
109dell'interfaccia utente per poi riutilizzarli.
110I widget FileSelection e ColorSelection della ditribuzione standard
111sono esempi di questo tipo di widget.
112<P>
113<P>Il widget di esempio che creeremo in questo capitolo &egrave; il
114Tictactoe, un vettore 3x3 di bottoni a commutazione il quale emette
115un segnale quando tutti e 3 i bottoni di una riga, colonna o di una
116diagonale sono premuti.
117<P>
118<H3>Scegliere la classe madre</H3>
119
120<P>La classe madre per un widget composto e' tipicamente la classe
121contenitrice che racchiude tutti gli elementi del widget composto.
122Per esempio, la classe madre del widget FileSelection &egrave; la classe
123Dialog. Visto che i nostri bottoni sono inseriti in una tabella, &egrave;
124naturale pensare che la nostra classe madre possa essere la GtkTable.
125Sfortunatamente, cos&igrave; non &egrave;. La creazione di un widget &egrave; diviso
126tra 2 funzioni : la funzione <CODE>WIDGETNAME_new()</CODE> che viene invocata
127dall'utente, e la funzione  <CODE>WIDGETNAME_init()</CODE> che ha il compito
128principale di inizializzare il widget che &egrave; indipendente dai valori
129passati alla funzione <CODE>_new()</CODE>. Widget figli o discendenti  possono
130chiamare, solamente, la funzione del loro widget genitore.
131Ma questa divisione del lavoro non funziona bene per la tabella, la
132quale, quando creata, necessita di conoscere il numero di righe e
133colonne che la comporr&agrave;. A meno che non vogliamo duplicare molte delle
134fuinzionalit&agrave; della <CODE>gtk_table_new()</CODE> nel nostro widget
135Tictactoe, faremmo meglio a evitare di derivarlo dalla GtkTable. Per questa
136ragione lo deriviamo invece da GtkVBox, e uniamo la nostra tabella
137dentro il VBox.
138<P>
139<H3>Il File Header</H3>
140
141<P>Ogni classe di widget ha un file header il quale dichiara l'oggetto e la
142struttura della classe del widget, comprese le funzioni pubbliche.
143Per prevenire duplicati di definizioni, noi includiamo l'intero file header fra:
144<P>
145<BLOCKQUOTE><CODE>
146<PRE>
147#ifndef __TICTACTOE_H__
148#define __TICTACTOE_H__
149.
150.
151.
152#endif /* __TICTACTOE_H__ */
153</PRE>
154</CODE></BLOCKQUOTE>
155<P>E per far felici i programmi in C++ che includono il nostro file header, in:
156<P>
157<BLOCKQUOTE><CODE>
158<PRE>
159#ifdef __cplusplus
160extern "C" {
161#endif /* __cplusplus */
162.
163.
164.
165#ifdef __cplusplus
166}
167#endif /* __cplusplus */
168</PRE>
169</CODE></BLOCKQUOTE>
170<P>Insieme alle funzioni e alle strutture, dichiariamo tre macro
171standard nel nostro file header, <CODE>TICTACTOE(obj)</CODE>,
172<CODE>TICTACTOE_CLASS(klass)</CODE>, e <CODE>IS_TICTACTOE(obj)</CODE>, i quali rispettivamente
173fanno il cast di un puntatore ad un puntatore ad un ogetto od ad una struttura
174di classe, e guarda se un oggetto &egrave; un widget Tictactoe.
175<P>
176<P>Qui vi &egrave; il file header completo:
177<P>
178<BLOCKQUOTE><CODE>
179<PRE>
180/* tictactoe.h */
181
182#ifndef __TICTACTOE_H__
183#define __TICTACTOE_H__
184
185#include &lt;gdk/gdk.h>
186#include &lt;gtk/gtkvbox.h>
187
188#ifdef __cplusplus
189extern "C" {
190#endif /* __cplusplus */
191
192#define TICTACTOE(obj)          GTK_CHECK_CAST (obj, tictactoe_get_type (), Tictactoe)
193#define TICTACTOE_CLASS(klass)  GTK_CHECK_CLASS_CAST (klass, tictactoe_get_type (), TictactoeClass)
194#define IS_TICTACTOE(obj)       GTK_CHECK_TYPE (obj, tictactoe_get_type ())
195
196
197typedef struct _Tictactoe       Tictactoe;
198typedef struct _TictactoeClass  TictactoeClass;
199
200struct _Tictactoe
201{
202  GtkVBox vbox;
203 
204  GtkWidget *buttons[3][3];
205};
206
207struct _TictactoeClass
208{
209  GtkVBoxClass parent_class;
210
211  void (* tictactoe) (Tictactoe *ttt);
212};
213
214guint          tictactoe_get_type        (void);
215GtkWidget*     tictactoe_new             (void);
216void           tictactoe_clear           (Tictactoe *ttt);
217
218#ifdef __cplusplus
219}
220#endif /* __cplusplus */
221
222#endif /* __TICTACTOE_H__ */
223</PRE>
224</CODE></BLOCKQUOTE>
225<P>
226<H3>La funzione <CODE>_get_type()</CODE></H3>
227
228<P>Continuiamo ora con l'implementazione del nostro widget. Una funzione
229basilare di ogni widget &egrave; la funzione <CODE>WIDGETNAME_get_type()</CODE>.
230Questa funzione, quando chiamata la prima volta, comunica a GTK la classe
231del widget, e ottiene un identificativo univoco per la classe del
232widget. Chiamate successive restituiscono semplicemente l'identificativo.
233<P>
234<BLOCKQUOTE><CODE>
235<PRE>
236guint
237tictactoe_get_type ()
238{
239  static guint ttt_type = 0;
240
241  if (!ttt_type)
242    {
243      GtkTypeInfo ttt_info =
244      {
245        "Tictactoe",
246        sizeof (Tictactoe),
247        sizeof (TictactoeClass),
248        (GtkClassInitFunc) tictactoe_class_init,
249        (GtkObjectInitFunc) tictactoe_init,
250        (GtkArgSetFunc) NULL,
251        (GtkArgGetFunc) NULL
252      };
253
254      ttt_type = gtk_type_unique (gtk_vbox_get_type (), &amp;ttt_info);
255    }
256
257  return ttt_type;
258}
259</PRE>
260</CODE></BLOCKQUOTE>
261<P>
262<P>La struttura GtkTypeInfo ha la seguente definizione:
263<P>
264<BLOCKQUOTE><CODE>
265<PRE>
266struct _GtkTypeInfo
267{
268  gchar *type_name;
269  guint object_size;
270  guint class_size;
271  GtkClassInitFunc class_init_func;
272  GtkObjectInitFunc object_init_func;
273  GtkArgSetFunc arg_set_func;
274  GtkArgGetFunc arg_get_func;
275};
276</PRE>
277</CODE></BLOCKQUOTE>
278<P>
279<P>I campi di questa struttura sono abbastanza auto-esplicativi.
280Ignoreremo, per ora, i campi  <CODE>arg_set_func</CODE> e <CODE>arg_get_func</CODE>:
281hanno un ruolo importante, ma ancora largamente non
282implementato, nel permettere ai linguaggi interpretati
283di settare convenientemente le opzioni del widget.
284Una volta che il GTK ha completato correttamente una copia di questa
285struttura, sa come creare un oggetto di un particolare widget.
286<P>
287<H3>La funzione <CODE>_class_init()</CODE> </H3>
288
289<P>La funzione <CODE>WIDGETNAME_class_init()</CODE> inizialiazza i campi della
290struttura della classe del widget, e setta ogni segnale della classe.
291Per il nostro widget Tictactoe ha il seguente aspetto:
292<P>
293<BLOCKQUOTE><CODE>
294<PRE>
295
296enum {
297  TICTACTOE_SIGNAL,
298  LAST_SIGNAL
299};
300
301static gint tictactoe_signals[LAST_SIGNAL] = { 0 };
302
303static void
304tictactoe_class_init (TictactoeClass *class)
305{
306  GtkObjectClass *object_class;
307
308  object_class = (GtkObjectClass*) class;
309 
310  tictactoe_signals[TICTACTOE_SIGNAL] = gtk_signal_new ("tictactoe",
311                                         GTK_RUN_FIRST,
312                                         object_class->type,
313                                         GTK_SIGNAL_OFFSET (TictactoeClass, tictactoe),
314                                         gtk_signal_default_marshaller, GTK_TYPE_NONE, 0);
315
316
317  gtk_object_class_add_signals (object_class, tictactoe_signals, LAST_SIGNAL);
318
319  class->tictactoe = NULL;
320}
321</PRE>
322</CODE></BLOCKQUOTE>
323<P>
324<P>Il nostro  widget ha semplicemente il segnale ``tictactoe'' che &egrave;
325invocato quando una riga, colonna o diagonale &egrave; completamente premuta.
326Non tutti i widget composti necessitano di segnali, quindi se stai
327leggendo questo per la prima volta, puoi anche saltare alla prossima sezione,
328dal momento che a questo punto le cose diventano un po' complicate.
329<P>La funzione:
330<BLOCKQUOTE><CODE>
331<PRE>
332gint   gtk_signal_new (const gchar         *name,
333                       GtkSignalRunType    run_type,
334                       GtkType             object_type,
335                       gint                function_offset,
336                       GtkSignalMarshaller marshaller,
337                       GtkType             return_val,
338                       guint               nparams,
339                       ...);
340</PRE>
341</CODE></BLOCKQUOTE>
342<P>crea un nuovo segnale. I parametri sono:
343<P>
344<UL>
345<LI> <CODE>name</CODE>: Il nome del segnale.</LI>
346<LI> <CODE>run_type</CODE>: Se il segstore predefinito viene eseguito prima o dopo
347di quello dell'utente. Di norma questo sar&agrave; <CODE>GTK_RUN_FIRST</CODE>, o <CODE>GTK_RUN_LAST</CODE>,
348anche se ci sono altre possibilit&agrave;.</LI>
349<LI> <CODE>object_type</CODE>: l'identificativo dell'oggetto a cui questo segnale si
350riferisce. Esso sar&agrave; anche applicato agli oggetti discendenti.</LI>
351<LI> <CODE>function_offset</CODE>: L'offset nella struttura della classe di un
352puntatore al gestore predefinito.</LI>
353<LI> <CODE>marshaller</CODE>: una funzione che &egrave; usata per invocare il gestore
354del segnale. Per gestori di segnali che non hanno argomenti oltre
355all'oggetto che emette il segnale e i dati dell'utente, possiamo usare
356la funzione predefinita <CODE>gtk_signal_default_marshaller</CODE></LI>
357<LI> <CODE>return_val</CODE>: Il tipo del valore di ritorno.</LI>
358<LI> <CODE>nparams</CODE>: Il numero di parametri del gestore di segnali (oltre
359ai due predefiniti menzionati sopra)</LI>
360<LI> <CODE>...</CODE>: i tipi dei parametri</LI>
361</UL>
362<P>Quando si specificano i tipi, si usa l'enumerazione <CODE>GtkType</CODE>:
363<P>
364<BLOCKQUOTE><CODE>
365<PRE>
366typedef enum
367{
368  GTK_TYPE_INVALID,
369  GTK_TYPE_NONE,
370  GTK_TYPE_CHAR,
371  GTK_TYPE_BOOL,
372  GTK_TYPE_INT,
373  GTK_TYPE_UINT,
374  GTK_TYPE_LONG,
375  GTK_TYPE_ULONG,
376  GTK_TYPE_FLOAT,
377  GTK_TYPE_DOUBLE,
378  GTK_TYPE_STRING,
379  GTK_TYPE_ENUM,
380  GTK_TYPE_FLAGS,
381  GTK_TYPE_BOXED,
382  GTK_TYPE_FOREIGN,
383  GTK_TYPE_CALLBACK,
384  GTK_TYPE_ARGS,
385
386  GTK_TYPE_POINTER,
387
388  /* sarebbe bello poter togliere alla fine i prossimi due */
389  GTK_TYPE_SIGNAL,
390  GTK_TYPE_C_CALLBACK,
391
392  GTK_TYPE_OBJECT
393
394} GtkFundamentalType;
395</PRE>
396</CODE></BLOCKQUOTE>
397<P>
398<P><CODE>gtk_signal_new()</CODE> restituisce un identificatore unico intero per il segnale,
399che memorizziamo nel vettore  <CODE>tictactoe_signals</CODE>, che
400indicizzeremo usando una enumerazione. (Convenzionalmente, gli elementi dell'enumerazione
401sono i nomi dei segnali, in maiuscolo,
402ma qui ci potrebbe essere un conflitto con la macro <CODE>TICTACTOE()</CODE>,
403quindi l'abbiamo chiamato  <CODE>TICTACTOE_SIGNAL</CODE>
404<P>Dopo aver creato un nostro segnale, abbiamo bisogno di dire a GTK
405di associare il nostro segnale alla classe Tictactoe. Lo facciamo
406invocando <CODE>gtk_object_class_add_signals()</CODE>. Settiamo quindi a NULL
407il puntatore che punta al gestore predefinito per il segnale
408``tictactoe'' a NULL, indicando che non ci sono azioni predefinite.
409<P>
410<H3>La funzione <CODE>_init()</CODE></H3>
411
412<P>
413<P>Ogni classe di Widget necessita anche di una funzione per inizializzare
414la struttura dell'oggetto. Usualmente questa funzione ha il ruolo abbastanza
415limitato di assegnare ai campi della struttura i valori predefiniti.
416Per widget composti, comunque, questa funzione crea, anche,
417i widget componenti del widget composto.
418<P>
419<BLOCKQUOTE><CODE>
420<PRE>
421
422static void
423tictactoe_init (Tictactoe *ttt)
424{
425  GtkWidget *table;
426  gint i,j;
427 
428  table = gtk_table_new (3, 3, TRUE);
429  gtk_container_add (GTK_CONTAINER(ttt), table);
430  gtk_widget_show (table);
431
432  for (i=0;i&lt;3; i++)
433    for (j=0;j&lt;3; j++)
434      {
435        ttt->buttons[i][j] = gtk_toggle_button_new ();
436        gtk_table_attach_defaults (GTK_TABLE(table), ttt->buttons[i][j],
437                                   i, i+1, j, j+1);
438        gtk_signal_connect (GTK_OBJECT (ttt->buttons[i][j]), "toggled",
439                            GTK_SIGNAL_FUNC (tictactoe_toggle), ttt);
440        gtk_widget_set_usize (ttt->buttons[i][j], 20, 20);
441        gtk_widget_show (ttt->buttons[i][j]);
442      }
443}
444</PRE>
445</CODE></BLOCKQUOTE>
446<P>
447<H3>E il resto...</H3>
448
449<P>
450<P>C'&egrave; un'altra funzione che ogni widget (eccetto i Widget di base come
451GtkBin che non possono essere instanziati) deve avere : la funzione
452che l'utente invoca per creare un oggetto di quel tipo. Questa &egrave;
453convenzionalmente chiamata <CODE>WIDGETNAME_new()</CODE>. In alcuni widget,
454non nel caso del nostro Tictactoe, questa funzione richiede degli
455argomenti, e fa alcune operazioni basandosi su di essi. Le altre
456due funzioni sono specifiche del widget Tictactoe.
457<P>
458<P><CODE>tictactoe_clear()</CODE> &egrave; una funzione pubblica che resetta tutti i
459bottoni, nel widget, allo stato iniziale (non premuto). Notate l'uso
460di <CODE>gtk_signal_handler_block_by_data()</CODE> per impedire che il nostro
461gestore dei segnali venga attivato quando non ce n'&egrave; bisogno.
462<P>
463<P><CODE>tictactoe_toggle()</CODE> &egrave; il gestore del segnale che viene invocato
464quando l'utente preme il bottone. Esso guarda se vi &egrave;
465qualche combinazione vincente che coinvolge i bottoni premuti, e nel
466caso ci fosse, emette il segnale ``tictactoe''.
467<P>
468<BLOCKQUOTE><CODE>
469<PRE>
470 
471GtkWidget*
472tictactoe_new ()
473{
474  return GTK_WIDGET ( gtk_type_new (tictactoe_get_type ()));
475}
476
477void           
478tictactoe_clear (Tictactoe *ttt)
479{
480  int i,j;
481
482  for (i=0;i&lt;3;i++)
483    for (j=0;j&lt;3;j++)
484      {
485        gtk_signal_handler_block_by_data (GTK_OBJECT(ttt->buttons[i][j]), ttt);
486        gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (ttt->buttons[i][j]),
487                                     FALSE);
488        gtk_signal_handler_unblock_by_data (GTK_OBJECT(ttt->buttons[i][j]), ttt);
489      }
490}
491
492static void
493tictactoe_toggle (GtkWidget *widget, Tictactoe *ttt)
494{
495  int i,k;
496
497  static int rwins[8][3] = { { 0, 0, 0 }, { 1, 1, 1 }, { 2, 2, 2 },
498                             { 0, 1, 2 }, { 0, 1, 2 }, { 0, 1, 2 },
499                             { 0, 1, 2 }, { 0, 1, 2 } };
500  static int cwins[8][3] = { { 0, 1, 2 }, { 0, 1, 2 }, { 0, 1, 2 },
501                             { 0, 0, 0 }, { 1, 1, 1 }, { 2, 2, 2 },
502                             { 0, 1, 2 }, { 2, 1, 0 } };
503
504  int success, found;
505
506  for (k=0; k&lt;8; k++)
507    {
508      success = TRUE;
509      found = FALSE;
510
511      for (i=0;i&lt;3;i++)
512        {
513          success = success &amp;&amp;
514            GTK_TOGGLE_BUTTON(ttt->buttons[rwins[k][i]][cwins[k][i]])->active;
515          found = found ||
516            ttt->buttons[rwins[k][i]][cwins[k][i]] == widget;
517        }
518     
519      if (success &amp;&amp; found)
520        {
521          gtk_signal_emit (GTK_OBJECT (ttt),
522                           tictactoe_signals[TICTACTOE_SIGNAL]);
523          break;
524        }
525    }
526}
527</PRE>
528</CODE></BLOCKQUOTE>
529<P>
530<P>
531<P>E finalmente un programma di esempio che usa il nostro widget
532Tictactoe:
533<P>
534<BLOCKQUOTE><CODE>
535<PRE>
536#include &lt;gtk/gtk.h>
537#include "tictactoe.h"
538
539/* Invocato quando una riga, colonna o diagonale e' completata. */
540void
541win (GtkWidget *widget, gpointer data)
542{
543  g_print ("Yay!\n");
544  tictactoe_clear (TICTACTOE (widget));
545}
546
547int
548main (int argc, char *argv[])
549{
550  GtkWidget *window;
551  GtkWidget *ttt;
552 
553  gtk_init (&amp;argc, &amp;argv);
554
555  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
556 
557  gtk_window_set_title (GTK_WINDOW (window), "Aspect Frame");
558 
559  gtk_signal_connect (GTK_OBJECT (window), "destroy",
560                      GTK_SIGNAL_FUNC (gtk_exit), NULL);
561 
562  gtk_container_border_width (GTK_CONTAINER (window), 10);
563
564  /* Crea un nuovo widget Tictactoe. */
565  ttt = tictactoe_new ();
566  gtk_container_add (GTK_CONTAINER (window), ttt);
567  gtk_widget_show (ttt);
568
569  /* E gli aggancia il segnale "tictactoe" */
570  gtk_signal_connect (GTK_OBJECT (ttt), "tictactoe",
571                      GTK_SIGNAL_FUNC (win), NULL);
572
573  gtk_widget_show (window);
574 
575  gtk_main ();
576 
577  return 0;
578}
579</PRE>
580</CODE></BLOCKQUOTE>
581<P>
582<H2><A NAME="ss19.4">19.4 Creare un widget a partire da zero</A>
583</H2>
584
585<H3>Introduzione</H3>
586
587<P>
588<P>In questa sezione impareremo meglio come i widget si mostrano sullo schermo
589e interagiscono con gli eventi. Come esempio, creeremo
590un widget di quadrante analogico con un puntatore che l'utente
591pu&ograve; trascinare per assegnare il valore.
592<P>
593<H3>Mostrare un widget sullo schermo</H3>
594
595<P>Ci sono alcuni passi che sono necessari nella visualizzazione sullo
596schermo. Dopo che il widget &egrave; stato creato con una chiamata a
597<CODE>WIDGETNAME_new()</CODE>, sono necessarie alcune altre funzioni:
598<P>
599<UL>
600<LI> <CODE>WIDGETNAME_realize()</CODE> &egrave; responsabile della creazione di
601una finestra X per il widget se ne ha una.</LI>
602<LI> <CODE>WIDGETNAME_map()</CODE> &egrave; invocata dopo che l'utente ha
603chiamato <CODE>gtk_widget_show()</CODE>. E' responsabile di vedere se il
604widget &egrave; attualmente disegnato sullo schermo (<EM>mappato</EM>). Per
605una classe contenitore, essa deve anche creare chiamate alle
606funzioni  <CODE>map()</CODE>> per ogni widget figlio.</LI>
607<LI> <CODE>WIDGETNAME_draw()</CODE> &egrave; invocata quando
608<CODE>gtk_widget_draw()</CODE> viene chiamata per il widget o per uno dei suoi
609predecessori. Esso fa s&igrave; che l'attuale chiamata alla
610funzione di disegno del widget disegni il widget sullo schermo.
611Per la classe contenitore, questa funzione deve eseguire le
612chiamate alla funzioni <CODE>gtk_widget_draw()</CODE> di ogni suo widget
613figlio.</LI>
614<LI> <CODE>WIDGETNAME_expose()</CODE> &egrave; un gestore per l'evento di esposizione
615per il widget. Esso crea le chiamate necessarie alle funzioni di disegno
616per disegnare la porzione che si &egrave; resa visibile. Per le classi
617contenitore, questa funzione deve generare gli eventi di ``expose'' per
618tutti i widget figli che non hanno una propria finestra (se essi hanno
619una loro finestra, sar&agrave; X che generer&agrave; i necessari eventi di expose).</LI>
620</UL>
621<P>
622<P>Potete notare che le ultime due funzioni sono molto simili, ognuna &egrave;
623responsabile per il disegno del widget sullo schermo. Infatti molti
624tipi di widget non sanno relamente la differenza tra le due.
625La funzione di predefinita <CODE>draw()</CODE> nella classe widget, semplicemente
626genera un sintetico evento di ``expose'' per l'area da ridisegnare.
627Comunque, alcuni tipi di widget possono risparmiare tempo distinguendo
628le due funzioni. Per esempio, se un widget ha piu' finestre X, allora
629visto che l'evento ``expose'' identifica solo la finestra esposta,
630esso pu&ograve; ridisegnare solo la finestra interessata, cosa che non &egrave;
631possibile per chiamate a <CODE>draw()</CODE>.
632<P>
633<P>I widget contenitori, anche se essi non farebbero differenze,
634non possono semplicemente usare la funzione <CODE>draw()</CODE> perch&egrave; per i
635loro widget figli la differenza potrebbere essere importante. Comunque,
636sarebbe uno spreco duplicare il codice di disegno nelle due
637funzioni. La convenzione &egrave; che questi widget abbiano una funzione
638chiamata <CODE>WIDGETNAME_paint()</CODE> che disegna il widget, che &egrave; poi
639chiamata dalle funzioni <CODE>draw()</CODE> e <CODE>expose()</CODE>
640<P>
641<P>Nell'approccio del nostro esempio, visto che il widget, ha
642una sola finestra, possiamo utilizzare il modo piu' semplice
643ed usare la funzione predefinita <CODE>draw()</CODE> e implementare
644solamente la funzione <CODE>expose()</CODE>.
645<P>
646<H3>Le origini del widget Dial</H3>
647
648<P>Come tutti gli animali terresti sono semplicemente varianti del primo
649amfibio, i widget Gtk tendono ad essere varianti di altri widget, precedentemente
650scritti. Cos&igrave;, anche se questa sezione &egrave; intitolata ``Creare
651un widget a partire da zero", il nostro widget inizia in realt&agrave; con il codice
652sorgente del widget Range. Questo &egrave; stato preso come punto d'inizio
653perche' sarebbe carino se il nostro widget avesse la
654stessa interfaccia del widget Scale il quale &egrave; semplicemente una
655specializzazione del widget Range. Cos&igrave;, sebbene  il codice sorgente e'
656presentato sotto in forma definitiva, non si deve pensare che sia stato
657scritto <EM>deus ex machina</EM> in questo modo. Se poi non avete familiarit&agrave;
658con il funzionamento del widget Scale dal punto di vista di chi scrive
659un'applicazione, potrebbe essere una buona idea guardare indietro prima
660di continuare.
661<P>
662<H3>Le basi</H3>
663
664<P>Una parte del nostro widget potrebbe essere simile
665al widget Tictactoe. In primo luogo, abbiamo il file header:
666<P>
667<BLOCKQUOTE><CODE>
668<PRE>
669/* GTK - The GIMP Toolkit
670 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
671 *
672 * This library is free software; you can redistribute it and/or
673 * modify it under the terms of the GNU Library General Public
674 * License as published by the Free Software Foundation; either
675 * version 2 of the License, or (at your option) any later version.
676 *
677 * This library is distributed in the hope that it will be useful,
678 * but WITHOUT ANY WARRANTY; without even the implied warranty of
679 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
680 * Library General Public License for more details.
681 *
682 * You should have received a copy of the GNU Library General Public
683 * License along with this library; if not, write to the Free
684 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
685 */
686
687#ifndef __GTK_DIAL_H__
688#define __GTK_DIAL_H__
689
690#include &lt;gdk/gdk.h>
691#include &lt;gtk/gtkadjustment.h>
692#include &lt;gtk/gtkwidget.h>
693
694
695#ifdef __cplusplus
696extern "C" {
697#endif /* __cplusplus */
698
699
700#define GTK_DIAL(obj)          GTK_CHECK_CAST (obj, gtk_dial_get_type (), GtkDial)
701#define GTK_DIAL_CLASS(klass)  GTK_CHECK_CLASS_CAST (klass, gtk_dial_get_type (), GtkDialClass)
702#define GTK_IS_DIAL(obj)       GTK_CHECK_TYPE (obj, gtk_dial_get_type ())
703
704
705typedef struct _GtkDial        GtkDial;
706typedef struct _GtkDialClass   GtkDialClass;
707
708struct _GtkDial
709{
710  GtkWidget widget;
711
712  /* Politica di update (GTK_UPDATE_[CONTINUOUS/DELAYED/DISCONTINUOUS]) */
713  guint policy : 2;
714
715  /* Bottone correntemente premuto o 0 altrimenti */
716  guint8 button;
717
718  /* Dimensione della componente Dial. */
719  gint radius;
720  gint pointer_width;
721
722  /* ID del timer di update, o 0 altrimenti */
723  guint32 timer;
724
725  /* Angolo corrente. */
726  gfloat angle;
727
728  /* Vecchi valori dell'aggiustamento cos&igrave; sappiamo quando
729   * qualcosa cambia */
730  gfloat old_value;
731  gfloat old_lower;
732  gfloat old_upper;
733
734  /* L'oggetto adjustament che memorizza i dati per questo dial */
735  GtkAdjustment *adjustment;
736};
737
738struct _GtkDialClass
739{
740  GtkWidgetClass parent_class;
741};
742
743
744GtkWidget*     gtk_dial_new                    (GtkAdjustment *adjustment);
745guint          gtk_dial_get_type               (void);
746GtkAdjustment* gtk_dial_get_adjustment         (GtkDial      *dial);
747void           gtk_dial_set_update_policy      (GtkDial      *dial,
748                                                GtkUpdateType  policy);
749
750void           gtk_dial_set_adjustment         (GtkDial      *dial,
751                                                GtkAdjustment *adjustment);
752#ifdef __cplusplus
753}
754#endif /* __cplusplus */
755
756
757#endif /* __GTK_DIAL_H__ */
758</PRE>
759</CODE></BLOCKQUOTE>
760<P>Essendoci pi&ugrave; cose da fare con questo widget, rispetto al precedente,
761abbiamo pi&ugrave; cambi nella struttura dati, ma le altre cose sono
762abbastamza simili.
763<P>Dopo aver incluso i file di header e aver dichiarato alcune costanti,
764dobbiamo fornire alcune funzioni circa il widget e la sua
765inizializzazione.
766<P>
767<BLOCKQUOTE><CODE>
768<PRE>
769#include &lt;math.h>
770#include &lt;stdio.h>
771#include &lt;gtk/gtkmain.h>
772#include &lt;gtk/gtksignal.h>
773
774#include "gtkdial.h"
775
776#define SCROLL_DELAY_LENGTH  300
777#define DIAL_DEFAULT_SIZE 100
778
779/* Dichiarazioni di funzioni successive */
780
781[ omesse per salvare spazio ]
782
783/* variabili locali. */
784
785static GtkWidgetClass *parent_class = NULL;
786
787guint
788gtk_dial_get_type ()
789{
790  static guint dial_type = 0;
791
792  if (!dial_type)
793    {
794      GtkTypeInfo dial_info =
795      {
796        "GtkDial",
797        sizeof (GtkDial),
798        sizeof (GtkDialClass),
799        (GtkClassInitFunc) gtk_dial_class_init,
800        (GtkObjectInitFunc) gtk_dial_init,
801        (GtkArgSetFunc) NULL,
802        (GtkArgGetFunc) NULL,
803      };
804
805      dial_type = gtk_type_unique (gtk_widget_get_type (), &amp;dial_info);
806    }
807
808  return dial_type;
809}
810
811static void
812gtk_dial_class_init (GtkDialClass *class)
813{
814  GtkObjectClass *object_class;
815  GtkWidgetClass *widget_class;
816
817  object_class = (GtkObjectClass*) class;
818  widget_class = (GtkWidgetClass*) class;
819
820  parent_class = gtk_type_class (gtk_widget_get_type ());
821
822  object_class->destroy = gtk_dial_destroy;
823
824  widget_class->realize = gtk_dial_realize;
825  widget_class->expose_event = gtk_dial_expose;
826  widget_class->size_request = gtk_dial_size_request;
827  widget_class->size_allocate = gtk_dial_size_allocate;
828  widget_class->button_press_event = gtk_dial_button_press;
829  widget_class->button_release_event = gtk_dial_button_release;
830  widget_class->motion_notify_event = gtk_dial_motion_notify;
831}
832
833static void
834gtk_dial_init (GtkDial *dial)
835{
836  dial->button = 0;
837  dial->policy = GTK_UPDATE_CONTINUOUS;
838  dial->timer = 0;
839  dial->radius = 0;
840  dial->pointer_width = 0;
841  dial->angle = 0.0;
842  dial->old_value = 0.0;
843  dial->old_lower = 0.0;
844  dial->old_upper = 0.0;
845  dial->adjustment = NULL;
846}
847
848GtkWidget*
849gtk_dial_new (GtkAdjustment *adjustment)
850{
851  GtkDial *dial;
852
853  dial = gtk_type_new (gtk_dial_get_type ());
854
855  if (!adjustment)
856    adjustment = (GtkAdjustment*) gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
857
858  gtk_dial_set_adjustment (dial, adjustment);
859
860  return GTK_WIDGET (dial);
861}
862
863static void
864gtk_dial_destroy (GtkObject *object)
865{
866  GtkDial *dial;
867
868  g_return_if_fail (object != NULL);
869  g_return_if_fail (GTK_IS_DIAL (object));
870
871  dial = GTK_DIAL (object);
872
873  if (dial->adjustment)
874    gtk_object_unref (GTK_OBJECT (dial->adjustment));
875
876  if (GTK_OBJECT_CLASS (parent_class)->destroy)
877    (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
878}
879</PRE>
880</CODE></BLOCKQUOTE>
881<P>Notate che questa funzione <CODE>init()</CODE> fa meno rispetto all'analoga del
882widget Tictactoe, essendo questo un widget non composto, e la
883funzione <CODE>new()</CODE> fa di pi&ugrave;, essendoci un argomento. Inoltre,
884notate che quando memorizziamo un puntatore all'oggetto Adjustment,
885incrementiamo il conteggio dei suoi riferimenti(e corrispondentemente
886lo decrementato quando non lo usiamo pi&ugrave;) cos&igrave; che GTK pu&ograve; tener traccia di
887quando &egrave; possibile distruggerlo senza causare guai.
888<P>
889<P>Inoltre, ci sono alcune funzioni per manipolare le opzioni del widget:
890<P>
891<BLOCKQUOTE><CODE>
892<PRE>
893GtkAdjustment*
894gtk_dial_get_adjustment (GtkDial *dial)
895{
896  g_return_val_if_fail (dial != NULL, NULL);
897  g_return_val_if_fail (GTK_IS_DIAL (dial), NULL);
898
899  return dial->adjustment;
900}
901
902void
903gtk_dial_set_update_policy (GtkDial      *dial,
904                             GtkUpdateType  policy)
905{
906  g_return_if_fail (dial != NULL);
907  g_return_if_fail (GTK_IS_DIAL (dial));
908
909  dial->policy = policy;
910}
911
912void
913gtk_dial_set_adjustment (GtkDial      *dial,
914                          GtkAdjustment *adjustment)
915{
916  g_return_if_fail (dial != NULL);
917  g_return_if_fail (GTK_IS_DIAL (dial));
918
919  if (dial->adjustment)
920    {
921      gtk_signal_disconnect_by_data (GTK_OBJECT (dial->adjustment), (gpointer) dial);
922      gtk_object_unref (GTK_OBJECT (dial->adjustment));
923    }
924
925  dial->adjustment = adjustment;
926  gtk_object_ref (GTK_OBJECT (dial->adjustment));
927
928  gtk_signal_connect (GTK_OBJECT (adjustment), "changed",
929                      (GtkSignalFunc) gtk_dial_adjustment_changed,
930                      (gpointer) dial);
931  gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed",
932                      (GtkSignalFunc) gtk_dial_adjustment_value_changed,
933                      (gpointer) dial);
934
935  dial->old_value = adjustment->value;
936  dial->old_lower = adjustment->lower;
937  dial->old_upper = adjustment->upper;
938
939  gtk_dial_update (dial);
940}
941</PRE>
942</CODE></BLOCKQUOTE>
943<P>
944<H3><CODE>gtk_dial_realize()</CODE></H3>
945
946<P>Abbiamo ora raggiunto alcuni nuovi tipi di funzione. In primo luogo,
947abbiamo una funzione che crea la finestra di X. Noterete che viene
948passata alla funzione <CODE>gdk_window_new()</CODE> una maschera che
949specifica quali campi della struttura GdkWindowAttr non sono vuoti
950(ai rimanenti campi pu&ograve; essere dato il valore predefinito). Anche
951il modo con cui la maschera degli eventi del widget  creata non &egrave;
952complicato. Chiameremo <CODE>gtk_widget_get_events()</CODE> per sapere la
953maschera degli eventi che l'utente ha specificato per questo widget
954(con <CODE>gtk_widget_set_events()</CODE>) e aggiungeremo gli eventi che ci possono
955interessare.
956<P>
957<P>Dopo aver creato la finestra, settiamo lo stile e lo sfondo,
958e creiamo un puntatore al widget nel campo dei dati utente (user data)
959del GdkWindow. Quest'ultimo passo permette a GTK di mandare gli
960eventi della finestra al widget corretto.
961<P>
962<BLOCKQUOTE><CODE>
963<PRE>
964static void
965gtk_dial_realize (GtkWidget *widget)
966{
967  GtkDial *dial;
968  GdkWindowAttr attributes;
969  gint attributes_mask;
970
971  g_return_if_fail (widget != NULL);
972  g_return_if_fail (GTK_IS_DIAL (widget));
973
974  GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
975  dial = GTK_DIAL (widget);
976
977  attributes.x = widget->allocation.x;
978  attributes.y = widget->allocation.y;
979  attributes.width = widget->allocation.width;
980  attributes.height = widget->allocation.height;
981  attributes.wclass = GDK_INPUT_OUTPUT;
982  attributes.window_type = GDK_WINDOW_CHILD;
983  attributes.event_mask = gtk_widget_get_events (widget) |
984    GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK |
985    GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK |
986    GDK_POINTER_MOTION_HINT_MASK;
987  attributes.visual = gtk_widget_get_visual (widget);
988  attributes.colormap = gtk_widget_get_colormap (widget);
989
990  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
991  widget->window = gdk_window_new (widget->parent->window, &amp;attributes, attributes_mask);
992
993  widget->style = gtk_style_attach (widget->style, widget->window);
994
995  gdk_window_set_user_data (widget->window, widget);
996
997  gtk_style_set_background (widget->style, widget->window, GTK_STATE_ACTIVE);
998}
999</PRE>
1000</CODE></BLOCKQUOTE>
1001<P>
1002<H3>Negoziazione della dimensione</H3>
1003
1004<P>Prima di visualizzare per la prima volta la finestra, e  se il
1005layout della finestra cambia, GTK chiede ad ogni widget, incluso nella
1006finestra, la propria dimensione. Questa richiesta &egrave; fatta dalla
1007funzione  <CODE>gtk_dial_size_request()</CODE>. Non essendo il nostro widget
1008un contenitore, e non avendo dei veri limiti per la propria
1009dimensione, restituiamo semplicemnte un valore ragionevole.
1010<P>
1011<BLOCKQUOTE><CODE>
1012<PRE>
1013static void
1014gtk_dial_size_request (GtkWidget      *widget,
1015                       GtkRequisition *requisition)
1016{
1017  requisition->width = DIAL_DEFAULT_SIZE;
1018  requisition->height = DIAL_DEFAULT_SIZE;
1019}
1020</PRE>
1021</CODE></BLOCKQUOTE>
1022<P>
1023<P>Dopo che tutti i widget hanno restituito una dimensione ideale, viene
1024calcolata la disposizione della finestra  e ad ogni widget figlio &egrave;
1025notificata la propria dimensione attuale . Usualmente, questo sar&agrave;
1026almeno quanto richiesto, ma occasionalmente pu&ograve; essere pi&ugrave; piccolo.
1027La notifica della dimensione  viene fatta dalla funzione
1028<CODE>gtk_dial_size_allocate()</CODE>. Notate che questa funzione &egrave; utilizzata
1029anche quando la finestra X del widget &egrave; spostata o modificata come
1030dimensione.
1031<P>
1032<BLOCKQUOTE><CODE>
1033<PRE>
1034static void
1035gtk_dial_size_allocate (GtkWidget     *widget,
1036                        GtkAllocation *allocation)
1037{
1038  GtkDial *dial;
1039
1040  g_return_if_fail (widget != NULL);
1041  g_return_if_fail (GTK_IS_DIAL (widget));
1042  g_return_if_fail (allocation != NULL);
1043
1044  widget->allocation = *allocation;
1045  if (GTK_WIDGET_REALIZED (widget))
1046    {
1047      dial = GTK_DIAL (widget);
1048
1049      gdk_window_move_resize (widget->window,
1050                              allocation->x, allocation->y,
1051                              allocation->width, allocation->height);
1052
1053      dial->radius = MAX(allocation->width,allocation->height) * 0.45;
1054      dial->pointer_width = dial->radius / 5;
1055    }
1056}
1057</PRE>
1058</CODE></BLOCKQUOTE>
1059.
1060<P>
1061<H3><CODE>gtk_dial_expose()</CODE></H3>
1062
1063<P>Come menzionato sopra, tutto il lavoro di questo widget viene fatto nella
1064gestione dell'evento ``expose''. Non c'&egrave; molto da notare su questo eccetto
1065l'uso della funzione <CODE>gtk_draw_polygon</CODE> per disegnare il
1066puntatore con un'ombreggiatura a tre dimensioni in accordo con il colore
1067memorizzato nello stile del wiget.
1068<P>
1069<BLOCKQUOTE><CODE>
1070<PRE>
1071static gint
1072gtk_dial_expose (GtkWidget      *widget,
1073                 GdkEventExpose *event)
1074{
1075  GtkDial *dial;
1076  GdkPoint points[3];
1077  gdouble s,c;
1078  gdouble theta;
1079  gint xc, yc;
1080  gint tick_length;
1081  gint i;
1082
1083  g_return_val_if_fail (widget != NULL, FALSE);
1084  g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
1085  g_return_val_if_fail (event != NULL, FALSE);
1086
1087  if (event->count > 0)
1088    return FALSE;
1089 
1090  dial = GTK_DIAL (widget);
1091
1092  gdk_window_clear_area (widget->window,
1093                         0, 0,
1094                         widget->allocation.width,
1095                         widget->allocation.height);
1096
1097  xc = widget->allocation.width/2;
1098  yc = widget->allocation.height/2;
1099
1100  /* Draw ticks */
1101
1102  for (i=0; i&lt;25; i++)
1103    {
1104      theta = (i*M_PI/18. - M_PI/6.);
1105      s = sin(theta);
1106      c = cos(theta);
1107
1108      tick_length = (i%6 == 0) ? dial->pointer_width : dial->pointer_width/2;
1109     
1110      gdk_draw_line (widget->window,
1111                     widget->style->fg_gc[widget->state],
1112                     xc + c*(dial->radius - tick_length),
1113                     yc - s*(dial->radius - tick_length),
1114                     xc + c*dial->radius,
1115                     yc - s*dial->radius);
1116    }
1117
1118  /* Draw pointer */
1119
1120  s = sin(dial->angle);
1121  c = cos(dial->angle);
1122
1123
1124  points[0].x = xc + s*dial->pointer_width/2;
1125  points[0].y = yc + c*dial->pointer_width/2;
1126  points[1].x = xc + c*dial->radius;
1127  points[1].y = yc - s*dial->radius;
1128  points[2].x = xc - s*dial->pointer_width/2;
1129  points[2].y = yc - c*dial->pointer_width/2;
1130
1131  gtk_draw_polygon (widget->style,
1132                    widget->window,
1133                    GTK_STATE_NORMAL,
1134                    GTK_SHADOW_OUT,
1135                    points, 3,
1136                    TRUE);
1137 
1138  return FALSE;
1139}
1140</PRE>
1141</CODE></BLOCKQUOTE>
1142<P>
1143<H3>Gestione degli eventi</H3>
1144
1145<P>
1146<P>Il resto del codice del widget manipola vari tipi di eventi, e non
1147&egrave; differente da quello che pu&ograve; essere trovato in molte applicazione
1148GTK. Due tipi di eventi possono verificarsi: l'utente pu&ograve;
1149clickare sul widget con il mouse e trascinare per muovere il puntatore,
1150o il valore dell'oggetto Adjustmente pu&ograve; cambiare a causa di alcune
1151circostanze esterne.
1152<P>
1153<P>Quando l'utente clicka sul widget, noi vediamo se la pressione
1154era veramente vicina al puntatore, e se cos&igrave;, memorizziamo il bottone
1155premuto dall'utente con il campo <CODE>button</CODE> della struttura del
1156widget, e prendiamo tutti gli eventi del mouse con una chiamata alla
1157funzione <CODE>gtk_grab_add()</CODE>. Successivi movimenti del mouse causano il
1158ricalcolo dei valori di controllo (fatto dalla funzione
1159<CODE>gtk_dial_update_mouse</CODE>). Dipendentemente dalla politica che abbiamo
1160stabilito, gli eventi ``value_changed'' possono essere  generati
1161istantaneamente (<CODE>GTK_UPDATE_CONTINUOUS</CODE>), dopo un certo tempo aggiunto
1162con la funzione <CODE>gtk_timeout_add()</CODE> (<CODE>GTK_UPDATE_DELAYED</CODE>), o
1163solamente quando il bottone del mouse e' rilasciato
1164(<CODE>GTK_UPDATE_DISCONTINUOUS</CODE>).
1165<P>
1166<BLOCKQUOTE><CODE>
1167<PRE>
1168static gint
1169gtk_dial_button_press (GtkWidget      *widget,
1170                       GdkEventButton *event)
1171{
1172  GtkDial *dial;
1173  gint dx, dy;
1174  double s, c;
1175  double d_parallel;
1176  double d_perpendicular;
1177
1178  g_return_val_if_fail (widget != NULL, FALSE);
1179  g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
1180  g_return_val_if_fail (event != NULL, FALSE);
1181
1182  dial = GTK_DIAL (widget);
1183
1184  /* Determina se il bottone premuto era dentro la regione del puntatore:
1185     lo facciamo calcolando la distanza parallela e
1186     perpendicolare dal punto dove il bottone del mouse e' stato premuto
1187     alla linea passante per il puntatore. */
1188 
1189  dx = event->x - widget->allocation.width / 2;
1190  dy = widget->allocation.height / 2 - event->y;
1191 
1192  s = sin(dial->angle);
1193  c = cos(dial->angle);
1194 
1195  d_parallel = s*dy + c*dx;
1196  d_perpendicular = fabs(s*dx - c*dy);
1197 
1198  if (!dial->button &amp;&amp;
1199      (d_perpendicular &lt; dial->pointer_width/2) &amp;&amp;
1200      (d_parallel > - dial->pointer_width))
1201    {
1202      gtk_grab_add (widget);
1203
1204      dial->button = event->button;
1205
1206      gtk_dial_update_mouse (dial, event->x, event->y);
1207    }
1208
1209  return FALSE;
1210}
1211
1212static gint
1213gtk_dial_button_release (GtkWidget      *widget,
1214                          GdkEventButton *event)
1215{
1216  GtkDial *dial;
1217
1218  g_return_val_if_fail (widget != NULL, FALSE);
1219  g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
1220  g_return_val_if_fail (event != NULL, FALSE);
1221
1222  dial = GTK_DIAL (widget);
1223
1224  if (dial->button == event->button)
1225    {
1226      gtk_grab_remove (widget);
1227
1228      dial->button = 0;
1229
1230      if (dial->policy == GTK_UPDATE_DELAYED)
1231        gtk_timeout_remove (dial->timer);
1232     
1233      if ((dial->policy != GTK_UPDATE_CONTINUOUS) &amp;&amp;
1234          (dial->old_value != dial->adjustment->value))
1235        gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
1236    }
1237
1238  return FALSE;
1239}
1240
1241static gint
1242gtk_dial_motion_notify (GtkWidget      *widget,
1243                         GdkEventMotion *event)
1244{
1245  GtkDial *dial;
1246  GdkModifierType mods;
1247  gint x, y, mask;
1248
1249  g_return_val_if_fail (widget != NULL, FALSE);
1250  g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
1251  g_return_val_if_fail (event != NULL, FALSE);
1252
1253  dial = GTK_DIAL (widget);
1254
1255  if (dial->button != 0)
1256    {
1257      x = event->x;
1258      y = event->y;
1259
1260      if (event->is_hint || (event->window != widget->window))
1261        gdk_window_get_pointer (widget->window, &amp;x, &amp;y, &amp;mods);
1262
1263      switch (dial->button)
1264        {
1265        case 1:
1266          mask = GDK_BUTTON1_MASK;
1267          break;
1268        case 2:
1269          mask = GDK_BUTTON2_MASK;
1270          break;
1271        case 3:
1272          mask = GDK_BUTTON3_MASK;
1273          break;
1274        default:
1275          mask = 0;
1276          break;
1277        }
1278
1279      if (mods &amp; mask)
1280        gtk_dial_update_mouse (dial, x,y);
1281    }
1282
1283  return FALSE;
1284}
1285
1286static gint
1287gtk_dial_timer (GtkDial *dial)
1288{
1289  g_return_val_if_fail (dial != NULL, FALSE);
1290  g_return_val_if_fail (GTK_IS_DIAL (dial), FALSE);
1291
1292  if (dial->policy == GTK_UPDATE_DELAYED)
1293    gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
1294
1295  return FALSE;
1296}
1297
1298static void
1299gtk_dial_update_mouse (GtkDial *dial, gint x, gint y)
1300{
1301  gint xc, yc;
1302  gfloat old_value;
1303
1304  g_return_if_fail (dial != NULL);
1305  g_return_if_fail (GTK_IS_DIAL (dial));
1306
1307  xc = GTK_WIDGET(dial)->allocation.width / 2;
1308  yc = GTK_WIDGET(dial)->allocation.height / 2;
1309
1310  old_value = dial->adjustment->value;
1311  dial->angle = atan2(yc-y, x-xc);
1312
1313  if (dial->angle &lt; -M_PI/2.)
1314    dial->angle += 2*M_PI;
1315
1316  if (dial->angle &lt; -M_PI/6)
1317    dial->angle = -M_PI/6;
1318
1319  if (dial->angle > 7.*M_PI/6.)
1320    dial->angle = 7.*M_PI/6.;
1321
1322  dial->adjustment->value = dial->adjustment->lower + (7.*M_PI/6 - dial->angle) *
1323    (dial->adjustment->upper - dial->adjustment->lower) / (4.*M_PI/3.);
1324
1325  if (dial->adjustment->value != old_value)
1326    {
1327      if (dial->policy == GTK_UPDATE_CONTINUOUS)
1328        {
1329          gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
1330        }
1331      else
1332        {
1333          gtk_widget_draw (GTK_WIDGET(dial), NULL);
1334
1335          if (dial->policy == GTK_UPDATE_DELAYED)
1336            {
1337              if (dial->timer)
1338                gtk_timeout_remove (dial->timer);
1339
1340              dial->timer = gtk_timeout_add (SCROLL_DELAY_LENGTH,
1341                                             (GtkFunction) gtk_dial_timer,
1342                                             (gpointer) dial);
1343            }
1344        }
1345    }
1346}
1347</PRE>
1348</CODE></BLOCKQUOTE>
1349<P>
1350<P>Cambiamenti esterni all'Adjustment sono comunicati al nostro widget
1351dai segnali ``changed'' e ``value_changed''. Il gestore per
1352queste funzioni chiama  <CODE>gtk_dial_update()</CODE> per validare gli
1353argomenti, calcolare il nuovo angolo del puntatore e ridisegnare il
1354widget (chiamando <CODE>gtk_widget_draw()</CODE>).
1355<P>
1356<BLOCKQUOTE><CODE>
1357<PRE>
1358static void
1359gtk_dial_update (GtkDial *dial)
1360{
1361  gfloat new_value;
1362 
1363  g_return_if_fail (dial != NULL);
1364  g_return_if_fail (GTK_IS_DIAL (dial));
1365
1366  new_value = dial->adjustment->value;
1367 
1368  if (new_value &lt; dial->adjustment->lower)
1369    new_value = dial->adjustment->lower;
1370
1371  if (new_value > dial->adjustment->upper)
1372    new_value = dial->adjustment->upper;
1373
1374  if (new_value != dial->adjustment->value)
1375    {
1376      dial->adjustment->value = new_value;
1377      gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
1378    }
1379
1380  dial->angle = 7.*M_PI/6. - (new_value - dial->adjustment->lower) * 4.*M_PI/3. /
1381    (dial->adjustment->upper - dial->adjustment->lower);
1382
1383  gtk_widget_draw (GTK_WIDGET(dial), NULL);
1384}
1385
1386static void
1387gtk_dial_adjustment_changed (GtkAdjustment *adjustment,
1388                              gpointer       data)
1389{
1390  GtkDial *dial;
1391
1392  g_return_if_fail (adjustment != NULL);
1393  g_return_if_fail (data != NULL);
1394
1395  dial = GTK_DIAL (data);
1396
1397  if ((dial->old_value != adjustment->value) ||
1398      (dial->old_lower != adjustment->lower) ||
1399      (dial->old_upper != adjustment->upper))
1400    {
1401      gtk_dial_update (dial);
1402
1403      dial->old_value = adjustment->value;
1404      dial->old_lower = adjustment->lower;
1405      dial->old_upper = adjustment->upper;
1406    }
1407}
1408
1409static void
1410gtk_dial_adjustment_value_changed (GtkAdjustment *adjustment,
1411                                    gpointer       data)
1412{
1413  GtkDial *dial;
1414
1415  g_return_if_fail (adjustment != NULL);
1416  g_return_if_fail (data != NULL);
1417
1418  dial = GTK_DIAL (data);
1419
1420  if (dial->old_value != adjustment->value)
1421    {
1422      gtk_dial_update (dial);
1423
1424      dial->old_value = adjustment->value;
1425    }
1426}
1427</PRE>
1428</CODE></BLOCKQUOTE>
1429<P>
1430<H3>Possibili Miglioramenti</H3>
1431
1432<P>
1433<P>Il widget Dial, da come l'abbiamo costruito, &egrave; lungo circa 670 linee
1434di codice C. Anche se questo potrebbe sembrare un po' troppo, abbiamo
1435realmente fatto un bel po' con quel tanto di codice, specialmente
1436considerando che molta della lunghezza &egrave; costituita da file header e
1437commmenti. Comunque ci sono alcuni miglioramenti che potrebbero essere
1438fatti a questo widget:
1439<P>
1440<UL>
1441<LI> Se tu provate questo widget, troverete che ci sono alcuni lampeggiamenti
1442quando il puntatore viene trascinato in giro. Questo
1443perch&egrave; l'intero widget &egrave; cancellato ogni volta che il
1444puntatore viene mosso, prima di essere ridisegnato. Spesso, il modo migliore
1445per gestire questo tipo di problema &egrave; il disegnare il tutto su una
1446pixmap non visibile, poi copiare il risultato finale sullo schermo
1447in una passata sola (il widget ProgressBar viene disegnato in questo
1448modo).
1449</LI>
1450<LI> L'utente potrebbe essere abilitato ad usare le frecce su e giu per
1451incrementare e diminuire il valore.
1452</LI>
1453<LI> Potrebbe essere carino se il widget avesse i bottoni per
1454incrementare e decrementare il valore di step. Anche se potrebbe essere
1455possibile usare dei widget Bottone incorporati per questo, possiamo anche
1456far s&igrave; che il bottone sia auto-ripentente quando premuto, come le frecce
1457in una barra di scorrimento. Molto del codice per implementare questo tipo di
1458comportamento pu&ograve; essere trovato nel widget GtkRange.
1459</LI>
1460<LI> il widget Dial potrebbe essere fatto/creato dentro un widget
1461contenitore con un singolo widget figlio posizionato all'inizio tra i
14622 bottoni menzionati prima. L'utente potrebbe poi aggiungere o una etichetta
1463o un widget ``entry'' per mostrare il valore corrente del dial.
1464</LI>
1465</UL>
1466<P>
1467<H2><A NAME="ss19.5">19.5 Impararne di pi&ugrave;</A>
1468</H2>
1469
1470<P> 
1471Fin qui abbiamo esposto solo una piccola parte di tutto quello che serve
1472per creare un widget. Se volete davvero  scrivere un vostro widget, la
1473miglior risorsa di esempi &egrave; lo stesso codice sorgente GTK. Chiedete a voi
1474stessi alcune cose su come deve essere il widget che volete scrivere: &egrave;
1475un widget contenitore? dovr&agrave; avere una propria finestra? &egrave; una modifica di
1476un widget precedente? Trovate poi un widget simile e iniziate a fargli
1477delle modifiche.
1478Buone Fortuna.
1479<P>
1480<HR NOSHADE>
1481<A HREF="gtk_tut_it-20.html">Avanti</A>
1482<A HREF="gtk_tut_it-18.html">Indietro</A>
1483<A HREF="gtk_tut_it.html#toc19">Indice</A>
1484</BODY>
1485</HTML>
Note: See TracBrowser for help on using the repository browser.