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 |
---|
21 | coprire molte necessità basilari, può essere necessario costruirsi |
---|
22 | un proprio widget. GTK usa molto l'ereditarietà tra i vari |
---|
23 | widget e, di solito, vi è un widget che si avvicina a quello che ti |
---|
24 | servirebbe, ed è spesso possibile creare un nuovo widget con poche linee |
---|
25 | di codice. Ma prima di iniziare il lavoro su un nuovo widget, vediamo |
---|
26 | se qualcuno non lo ha già creato. Questo eviterà un duplicazione |
---|
27 | di lavoro e farà sì che i widget non-GTK puri siano minimi, così da |
---|
28 | aiutare sia chi crea il codice che chi l'interfaccia per applicazioni GTK |
---|
29 | molto grosse. D'altra parte, quando hai finito di scrivere un widget, |
---|
30 | annuncialo a tutto il mondo così che le altre persone ne possano |
---|
31 | beneficiare. Il miglioro modo dove farlo è la <CODE>gtk-list</CODE>. |
---|
32 | <P>I sorgenti completi per i widget di esempio possono essere presi dallo stesso |
---|
33 | sito 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 è importante aver capito come gli ogetti |
---|
41 | di GTK lavorano. Questa sezione è solo una breve spiegazione. Guarda la |
---|
42 | documentazione di riferimento per maggiori dettagli. |
---|
43 | <P> |
---|
44 | <P>I widget GTK sono implementati in un modo orientato agli oggetti, |
---|
45 | anche se usando il C standard. Questo aumenta notevolmente la portabilità |
---|
46 | e la stabilità, specialmente per le correnti generazioni di compilatori C++; |
---|
47 | comunque questo significa che chi scrive un widget deve fare attenzione |
---|
48 | ad alcuni dettagli di implementazione. L'informazione comune a tutte le |
---|
49 | istanze di una classe di widget (ad esempio: a tutti i bottoni) è memorizzata |
---|
50 | <EM>class structure</EM>. C'e' solamente una copia di questo in cui |
---|
51 | sono memorizzate le informazioni riguardanti i segnali della classe |
---|
52 | (assomiglia ad una funzione virtuale in C). Per supportare l'ereditarietà |
---|
53 | il primo campo della struttura di una classe deve essere una copia della |
---|
54 | struttura della classe genitore. La dichiarazione della struttura della |
---|
55 | classe GtkButton è: |
---|
56 | <P> |
---|
57 | <BLOCKQUOTE><CODE> |
---|
58 | <PRE> |
---|
59 | struct _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 |
---|
73 | ridimensionato) si può fare il cast della struttura della sua classe con la |
---|
74 | GtkContainerClass, e usare i campi rilevanti per gestire i segnali. |
---|
75 | <P> |
---|
76 | <P>C'è anche una struttura per ogni widget che viene creata |
---|
77 | ad ogni istanza. Questa struttura ha campi per |
---|
78 | memorizzare le informazioni che sono differenti per ogni volta che il widget |
---|
79 | viene istanziato. Chiameremo questa struttura la <EM> struttura |
---|
80 | oggetto</EM>. Per la classe Bottone, questa ha l'aspetto: |
---|
81 | <P> |
---|
82 | <BLOCKQUOTE><CODE> |
---|
83 | <PRE> |
---|
84 | struct _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 | è la struttura dell'oggetto della classe madre, così che, se necessario, si può fare il |
---|
98 | cast 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 è un widget che |
---|
106 | è semplicemnte un aggregato di altri widget GTK. Questo tipo di |
---|
107 | widget non fa nulla che non possa essere fatto creando un nuovo |
---|
108 | widget, ma fornisce un modo conveniente per inscatolare elementi |
---|
109 | dell'interfaccia utente per poi riutilizzarli. |
---|
110 | I widget FileSelection e ColorSelection della ditribuzione standard |
---|
111 | sono esempi di questo tipo di widget. |
---|
112 | <P> |
---|
113 | <P>Il widget di esempio che creeremo in questo capitolo è il |
---|
114 | Tictactoe, un vettore 3x3 di bottoni a commutazione il quale emette |
---|
115 | un segnale quando tutti e 3 i bottoni di una riga, colonna o di una |
---|
116 | diagonale 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 |
---|
121 | contenitrice che racchiude tutti gli elementi del widget composto. |
---|
122 | Per esempio, la classe madre del widget FileSelection è la classe |
---|
123 | Dialog. Visto che i nostri bottoni sono inseriti in una tabella, è |
---|
124 | naturale pensare che la nostra classe madre possa essere la GtkTable. |
---|
125 | Sfortunatamente, così non è. La creazione di un widget è diviso |
---|
126 | tra 2 funzioni : la funzione <CODE>WIDGETNAME_new()</CODE> che viene invocata |
---|
127 | dall'utente, e la funzione <CODE>WIDGETNAME_init()</CODE> che ha il compito |
---|
128 | principale di inizializzare il widget che è indipendente dai valori |
---|
129 | passati alla funzione <CODE>_new()</CODE>. Widget figli o discendenti possono |
---|
130 | chiamare, solamente, la funzione del loro widget genitore. |
---|
131 | Ma questa divisione del lavoro non funziona bene per la tabella, la |
---|
132 | quale, quando creata, necessita di conoscere il numero di righe e |
---|
133 | colonne che la comporrà. A meno che non vogliamo duplicare molte delle |
---|
134 | fuinzionalità della <CODE>gtk_table_new()</CODE> nel nostro widget |
---|
135 | Tictactoe, faremmo meglio a evitare di derivarlo dalla GtkTable. Per questa |
---|
136 | ragione lo deriviamo invece da GtkVBox, e uniamo la nostra tabella |
---|
137 | dentro 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 |
---|
142 | struttura della classe del widget, comprese le funzioni pubbliche. |
---|
143 | Per 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 |
---|
160 | extern "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 |
---|
171 | standard nel nostro file header, <CODE>TICTACTOE(obj)</CODE>, |
---|
172 | <CODE>TICTACTOE_CLASS(klass)</CODE>, e <CODE>IS_TICTACTOE(obj)</CODE>, i quali rispettivamente |
---|
173 | fanno il cast di un puntatore ad un puntatore ad un ogetto od ad una struttura |
---|
174 | di classe, e guarda se un oggetto è un widget Tictactoe. |
---|
175 | <P> |
---|
176 | <P>Qui vi è 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 <gdk/gdk.h> |
---|
186 | #include <gtk/gtkvbox.h> |
---|
187 | |
---|
188 | #ifdef __cplusplus |
---|
189 | extern "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 | |
---|
197 | typedef struct _Tictactoe Tictactoe; |
---|
198 | typedef struct _TictactoeClass TictactoeClass; |
---|
199 | |
---|
200 | struct _Tictactoe |
---|
201 | { |
---|
202 | GtkVBox vbox; |
---|
203 | |
---|
204 | GtkWidget *buttons[3][3]; |
---|
205 | }; |
---|
206 | |
---|
207 | struct _TictactoeClass |
---|
208 | { |
---|
209 | GtkVBoxClass parent_class; |
---|
210 | |
---|
211 | void (* tictactoe) (Tictactoe *ttt); |
---|
212 | }; |
---|
213 | |
---|
214 | guint tictactoe_get_type (void); |
---|
215 | GtkWidget* tictactoe_new (void); |
---|
216 | void 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 |
---|
229 | basilare di ogni widget è la funzione <CODE>WIDGETNAME_get_type()</CODE>. |
---|
230 | Questa funzione, quando chiamata la prima volta, comunica a GTK la classe |
---|
231 | del widget, e ottiene un identificativo univoco per la classe del |
---|
232 | widget. Chiamate successive restituiscono semplicemente l'identificativo. |
---|
233 | <P> |
---|
234 | <BLOCKQUOTE><CODE> |
---|
235 | <PRE> |
---|
236 | guint |
---|
237 | tictactoe_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 (), &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> |
---|
266 | struct _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. |
---|
280 | Ignoreremo, per ora, i campi <CODE>arg_set_func</CODE> e <CODE>arg_get_func</CODE>: |
---|
281 | hanno un ruolo importante, ma ancora largamente non |
---|
282 | implementato, nel permettere ai linguaggi interpretati |
---|
283 | di settare convenientemente le opzioni del widget. |
---|
284 | Una volta che il GTK ha completato correttamente una copia di questa |
---|
285 | struttura, 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 |
---|
290 | struttura della classe del widget, e setta ogni segnale della classe. |
---|
291 | Per il nostro widget Tictactoe ha il seguente aspetto: |
---|
292 | <P> |
---|
293 | <BLOCKQUOTE><CODE> |
---|
294 | <PRE> |
---|
295 | |
---|
296 | enum { |
---|
297 | TICTACTOE_SIGNAL, |
---|
298 | LAST_SIGNAL |
---|
299 | }; |
---|
300 | |
---|
301 | static gint tictactoe_signals[LAST_SIGNAL] = { 0 }; |
---|
302 | |
---|
303 | static void |
---|
304 | tictactoe_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 è |
---|
325 | invocato quando una riga, colonna o diagonale è completamente premuta. |
---|
326 | Non tutti i widget composti necessitano di segnali, quindi se stai |
---|
327 | leggendo questo per la prima volta, puoi anche saltare alla prossima sezione, |
---|
328 | dal momento che a questo punto le cose diventano un po' complicate. |
---|
329 | <P>La funzione: |
---|
330 | <BLOCKQUOTE><CODE> |
---|
331 | <PRE> |
---|
332 | gint 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 |
---|
347 | di quello dell'utente. Di norma questo sarà <CODE>GTK_RUN_FIRST</CODE>, o <CODE>GTK_RUN_LAST</CODE>, |
---|
348 | anche se ci sono altre possibilità.</LI> |
---|
349 | <LI> <CODE>object_type</CODE>: l'identificativo dell'oggetto a cui questo segnale si |
---|
350 | riferisce. Esso sarà anche applicato agli oggetti discendenti.</LI> |
---|
351 | <LI> <CODE>function_offset</CODE>: L'offset nella struttura della classe di un |
---|
352 | puntatore al gestore predefinito.</LI> |
---|
353 | <LI> <CODE>marshaller</CODE>: una funzione che è usata per invocare il gestore |
---|
354 | del segnale. Per gestori di segnali che non hanno argomenti oltre |
---|
355 | all'oggetto che emette il segnale e i dati dell'utente, possiamo usare |
---|
356 | la 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 |
---|
359 | ai 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> |
---|
366 | typedef 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, |
---|
399 | che memorizziamo nel vettore <CODE>tictactoe_signals</CODE>, che |
---|
400 | indicizzeremo usando una enumerazione. (Convenzionalmente, gli elementi dell'enumerazione |
---|
401 | sono i nomi dei segnali, in maiuscolo, |
---|
402 | ma qui ci potrebbe essere un conflitto con la macro <CODE>TICTACTOE()</CODE>, |
---|
403 | quindi l'abbiamo chiamato <CODE>TICTACTOE_SIGNAL</CODE> |
---|
404 | <P>Dopo aver creato un nostro segnale, abbiamo bisogno di dire a GTK |
---|
405 | di associare il nostro segnale alla classe Tictactoe. Lo facciamo |
---|
406 | invocando <CODE>gtk_object_class_add_signals()</CODE>. Settiamo quindi a NULL |
---|
407 | il 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 |
---|
414 | la struttura dell'oggetto. Usualmente questa funzione ha il ruolo abbastanza |
---|
415 | limitato di assegnare ai campi della struttura i valori predefiniti. |
---|
416 | Per widget composti, comunque, questa funzione crea, anche, |
---|
417 | i widget componenti del widget composto. |
---|
418 | <P> |
---|
419 | <BLOCKQUOTE><CODE> |
---|
420 | <PRE> |
---|
421 | |
---|
422 | static void |
---|
423 | tictactoe_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<3; i++) |
---|
433 | for (j=0;j<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'è un'altra funzione che ogni widget (eccetto i Widget di base come |
---|
451 | GtkBin che non possono essere instanziati) deve avere : la funzione |
---|
452 | che l'utente invoca per creare un oggetto di quel tipo. Questa è |
---|
453 | convenzionalmente chiamata <CODE>WIDGETNAME_new()</CODE>. In alcuni widget, |
---|
454 | non nel caso del nostro Tictactoe, questa funzione richiede degli |
---|
455 | argomenti, e fa alcune operazioni basandosi su di essi. Le altre |
---|
456 | due funzioni sono specifiche del widget Tictactoe. |
---|
457 | <P> |
---|
458 | <P><CODE>tictactoe_clear()</CODE> è una funzione pubblica che resetta tutti i |
---|
459 | bottoni, nel widget, allo stato iniziale (non premuto). Notate l'uso |
---|
460 | di <CODE>gtk_signal_handler_block_by_data()</CODE> per impedire che il nostro |
---|
461 | gestore dei segnali venga attivato quando non ce n'è bisogno. |
---|
462 | <P> |
---|
463 | <P><CODE>tictactoe_toggle()</CODE> è il gestore del segnale che viene invocato |
---|
464 | quando l'utente preme il bottone. Esso guarda se vi è |
---|
465 | qualche combinazione vincente che coinvolge i bottoni premuti, e nel |
---|
466 | caso ci fosse, emette il segnale ``tictactoe''. |
---|
467 | <P> |
---|
468 | <BLOCKQUOTE><CODE> |
---|
469 | <PRE> |
---|
470 | |
---|
471 | GtkWidget* |
---|
472 | tictactoe_new () |
---|
473 | { |
---|
474 | return GTK_WIDGET ( gtk_type_new (tictactoe_get_type ())); |
---|
475 | } |
---|
476 | |
---|
477 | void |
---|
478 | tictactoe_clear (Tictactoe *ttt) |
---|
479 | { |
---|
480 | int i,j; |
---|
481 | |
---|
482 | for (i=0;i<3;i++) |
---|
483 | for (j=0;j<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 | |
---|
492 | static void |
---|
493 | tictactoe_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<8; k++) |
---|
507 | { |
---|
508 | success = TRUE; |
---|
509 | found = FALSE; |
---|
510 | |
---|
511 | for (i=0;i<3;i++) |
---|
512 | { |
---|
513 | success = success && |
---|
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 && 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 |
---|
532 | Tictactoe: |
---|
533 | <P> |
---|
534 | <BLOCKQUOTE><CODE> |
---|
535 | <PRE> |
---|
536 | #include <gtk/gtk.h> |
---|
537 | #include "tictactoe.h" |
---|
538 | |
---|
539 | /* Invocato quando una riga, colonna o diagonale e' completata. */ |
---|
540 | void |
---|
541 | win (GtkWidget *widget, gpointer data) |
---|
542 | { |
---|
543 | g_print ("Yay!\n"); |
---|
544 | tictactoe_clear (TICTACTOE (widget)); |
---|
545 | } |
---|
546 | |
---|
547 | int |
---|
548 | main (int argc, char *argv[]) |
---|
549 | { |
---|
550 | GtkWidget *window; |
---|
551 | GtkWidget *ttt; |
---|
552 | |
---|
553 | gtk_init (&argc, &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 |
---|
589 | e interagiscono con gli eventi. Come esempio, creeremo |
---|
590 | un widget di quadrante analogico con un puntatore che l'utente |
---|
591 | può 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 |
---|
596 | schermo. Dopo che il widget è 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> è responsabile della creazione di |
---|
601 | una finestra X per il widget se ne ha una.</LI> |
---|
602 | <LI> <CODE>WIDGETNAME_map()</CODE> è invocata dopo che l'utente ha |
---|
603 | chiamato <CODE>gtk_widget_show()</CODE>. E' responsabile di vedere se il |
---|
604 | widget è attualmente disegnato sullo schermo (<EM>mappato</EM>). Per |
---|
605 | una classe contenitore, essa deve anche creare chiamate alle |
---|
606 | funzioni <CODE>map()</CODE>> per ogni widget figlio.</LI> |
---|
607 | <LI> <CODE>WIDGETNAME_draw()</CODE> è invocata quando |
---|
608 | <CODE>gtk_widget_draw()</CODE> viene chiamata per il widget o per uno dei suoi |
---|
609 | predecessori. Esso fa sì che l'attuale chiamata alla |
---|
610 | funzione di disegno del widget disegni il widget sullo schermo. |
---|
611 | Per la classe contenitore, questa funzione deve eseguire le |
---|
612 | chiamate alla funzioni <CODE>gtk_widget_draw()</CODE> di ogni suo widget |
---|
613 | figlio.</LI> |
---|
614 | <LI> <CODE>WIDGETNAME_expose()</CODE> è un gestore per l'evento di esposizione |
---|
615 | per il widget. Esso crea le chiamate necessarie alle funzioni di disegno |
---|
616 | per disegnare la porzione che si è resa visibile. Per le classi |
---|
617 | contenitore, questa funzione deve generare gli eventi di ``expose'' per |
---|
618 | tutti i widget figli che non hanno una propria finestra (se essi hanno |
---|
619 | una loro finestra, sarà X che genererà i necessari eventi di expose).</LI> |
---|
620 | </UL> |
---|
621 | <P> |
---|
622 | <P>Potete notare che le ultime due funzioni sono molto simili, ognuna è |
---|
623 | responsabile per il disegno del widget sullo schermo. Infatti molti |
---|
624 | tipi di widget non sanno relamente la differenza tra le due. |
---|
625 | La funzione di predefinita <CODE>draw()</CODE> nella classe widget, semplicemente |
---|
626 | genera un sintetico evento di ``expose'' per l'area da ridisegnare. |
---|
627 | Comunque, alcuni tipi di widget possono risparmiare tempo distinguendo |
---|
628 | le due funzioni. Per esempio, se un widget ha piu' finestre X, allora |
---|
629 | visto che l'evento ``expose'' identifica solo la finestra esposta, |
---|
630 | esso può ridisegnare solo la finestra interessata, cosa che non è |
---|
631 | possibile per chiamate a <CODE>draw()</CODE>. |
---|
632 | <P> |
---|
633 | <P>I widget contenitori, anche se essi non farebbero differenze, |
---|
634 | non possono semplicemente usare la funzione <CODE>draw()</CODE> perchè per i |
---|
635 | loro widget figli la differenza potrebbere essere importante. Comunque, |
---|
636 | sarebbe uno spreco duplicare il codice di disegno nelle due |
---|
637 | funzioni. La convenzione è che questi widget abbiano una funzione |
---|
638 | chiamata <CODE>WIDGETNAME_paint()</CODE> che disegna il widget, che è poi |
---|
639 | chiamata dalle funzioni <CODE>draw()</CODE> e <CODE>expose()</CODE> |
---|
640 | <P> |
---|
641 | <P>Nell'approccio del nostro esempio, visto che il widget, ha |
---|
642 | una sola finestra, possiamo utilizzare il modo piu' semplice |
---|
643 | ed usare la funzione predefinita <CODE>draw()</CODE> e implementare |
---|
644 | solamente 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 |
---|
649 | amfibio, i widget Gtk tendono ad essere varianti di altri widget, precedentemente |
---|
650 | scritti. Così, anche se questa sezione è intitolata ``Creare |
---|
651 | un widget a partire da zero", il nostro widget inizia in realtà con il codice |
---|
652 | sorgente del widget Range. Questo è stato preso come punto d'inizio |
---|
653 | perche' sarebbe carino se il nostro widget avesse la |
---|
654 | stessa interfaccia del widget Scale il quale è semplicemente una |
---|
655 | specializzazione del widget Range. Così, sebbene il codice sorgente e' |
---|
656 | presentato sotto in forma definitiva, non si deve pensare che sia stato |
---|
657 | scritto <EM>deus ex machina</EM> in questo modo. Se poi non avete familiarità |
---|
658 | con il funzionamento del widget Scale dal punto di vista di chi scrive |
---|
659 | un'applicazione, potrebbe essere una buona idea guardare indietro prima |
---|
660 | di continuare. |
---|
661 | <P> |
---|
662 | <H3>Le basi</H3> |
---|
663 | |
---|
664 | <P>Una parte del nostro widget potrebbe essere simile |
---|
665 | al 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 <gdk/gdk.h> |
---|
691 | #include <gtk/gtkadjustment.h> |
---|
692 | #include <gtk/gtkwidget.h> |
---|
693 | |
---|
694 | |
---|
695 | #ifdef __cplusplus |
---|
696 | extern "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 | |
---|
705 | typedef struct _GtkDial GtkDial; |
---|
706 | typedef struct _GtkDialClass GtkDialClass; |
---|
707 | |
---|
708 | struct _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ì 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 | |
---|
738 | struct _GtkDialClass |
---|
739 | { |
---|
740 | GtkWidgetClass parent_class; |
---|
741 | }; |
---|
742 | |
---|
743 | |
---|
744 | GtkWidget* gtk_dial_new (GtkAdjustment *adjustment); |
---|
745 | guint gtk_dial_get_type (void); |
---|
746 | GtkAdjustment* gtk_dial_get_adjustment (GtkDial *dial); |
---|
747 | void gtk_dial_set_update_policy (GtkDial *dial, |
---|
748 | GtkUpdateType policy); |
---|
749 | |
---|
750 | void 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ù cose da fare con questo widget, rispetto al precedente, |
---|
761 | abbiamo più cambi nella struttura dati, ma le altre cose sono |
---|
762 | abbastamza simili. |
---|
763 | <P>Dopo aver incluso i file di header e aver dichiarato alcune costanti, |
---|
764 | dobbiamo fornire alcune funzioni circa il widget e la sua |
---|
765 | inizializzazione. |
---|
766 | <P> |
---|
767 | <BLOCKQUOTE><CODE> |
---|
768 | <PRE> |
---|
769 | #include <math.h> |
---|
770 | #include <stdio.h> |
---|
771 | #include <gtk/gtkmain.h> |
---|
772 | #include <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 | |
---|
785 | static GtkWidgetClass *parent_class = NULL; |
---|
786 | |
---|
787 | guint |
---|
788 | gtk_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 (), &dial_info); |
---|
806 | } |
---|
807 | |
---|
808 | return dial_type; |
---|
809 | } |
---|
810 | |
---|
811 | static void |
---|
812 | gtk_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 | |
---|
833 | static void |
---|
834 | gtk_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 | |
---|
848 | GtkWidget* |
---|
849 | gtk_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 | |
---|
863 | static void |
---|
864 | gtk_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 |
---|
882 | widget Tictactoe, essendo questo un widget non composto, e la |
---|
883 | funzione <CODE>new()</CODE> fa di più, essendoci un argomento. Inoltre, |
---|
884 | notate che quando memorizziamo un puntatore all'oggetto Adjustment, |
---|
885 | incrementiamo il conteggio dei suoi riferimenti(e corrispondentemente |
---|
886 | lo decrementato quando non lo usiamo più) così che GTK può tener traccia di |
---|
887 | quando è 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> |
---|
893 | GtkAdjustment* |
---|
894 | gtk_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 | |
---|
902 | void |
---|
903 | gtk_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 | |
---|
912 | void |
---|
913 | gtk_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, |
---|
947 | abbiamo una funzione che crea la finestra di X. Noterete che viene |
---|
948 | passata alla funzione <CODE>gdk_window_new()</CODE> una maschera che |
---|
949 | specifica quali campi della struttura GdkWindowAttr non sono vuoti |
---|
950 | (ai rimanenti campi può essere dato il valore predefinito). Anche |
---|
951 | il modo con cui la maschera degli eventi del widget creata non è |
---|
952 | complicato. Chiameremo <CODE>gtk_widget_get_events()</CODE> per sapere la |
---|
953 | maschera 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 |
---|
955 | interessare. |
---|
956 | <P> |
---|
957 | <P>Dopo aver creato la finestra, settiamo lo stile e lo sfondo, |
---|
958 | e creiamo un puntatore al widget nel campo dei dati utente (user data) |
---|
959 | del GdkWindow. Quest'ultimo passo permette a GTK di mandare gli |
---|
960 | eventi della finestra al widget corretto. |
---|
961 | <P> |
---|
962 | <BLOCKQUOTE><CODE> |
---|
963 | <PRE> |
---|
964 | static void |
---|
965 | gtk_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, &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 |
---|
1005 | layout della finestra cambia, GTK chiede ad ogni widget, incluso nella |
---|
1006 | finestra, la propria dimensione. Questa richiesta è fatta dalla |
---|
1007 | funzione <CODE>gtk_dial_size_request()</CODE>. Non essendo il nostro widget |
---|
1008 | un contenitore, e non avendo dei veri limiti per la propria |
---|
1009 | dimensione, restituiamo semplicemnte un valore ragionevole. |
---|
1010 | <P> |
---|
1011 | <BLOCKQUOTE><CODE> |
---|
1012 | <PRE> |
---|
1013 | static void |
---|
1014 | gtk_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 |
---|
1024 | calcolata la disposizione della finestra e ad ogni widget figlio è |
---|
1025 | notificata la propria dimensione attuale . Usualmente, questo sarà |
---|
1026 | almeno quanto richiesto, ma occasionalmente può essere più piccolo. |
---|
1027 | La notifica della dimensione viene fatta dalla funzione |
---|
1028 | <CODE>gtk_dial_size_allocate()</CODE>. Notate che questa funzione è utilizzata |
---|
1029 | anche quando la finestra X del widget è spostata o modificata come |
---|
1030 | dimensione. |
---|
1031 | <P> |
---|
1032 | <BLOCKQUOTE><CODE> |
---|
1033 | <PRE> |
---|
1034 | static void |
---|
1035 | gtk_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 |
---|
1064 | gestione dell'evento ``expose''. Non c'è molto da notare su questo eccetto |
---|
1065 | l'uso della funzione <CODE>gtk_draw_polygon</CODE> per disegnare il |
---|
1066 | puntatore con un'ombreggiatura a tre dimensioni in accordo con il colore |
---|
1067 | memorizzato nello stile del wiget. |
---|
1068 | <P> |
---|
1069 | <BLOCKQUOTE><CODE> |
---|
1070 | <PRE> |
---|
1071 | static gint |
---|
1072 | gtk_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<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 | è differente da quello che può essere trovato in molte applicazione |
---|
1148 | GTK. Due tipi di eventi possono verificarsi: l'utente può |
---|
1149 | clickare sul widget con il mouse e trascinare per muovere il puntatore, |
---|
1150 | o il valore dell'oggetto Adjustmente può cambiare a causa di alcune |
---|
1151 | circostanze esterne. |
---|
1152 | <P> |
---|
1153 | <P>Quando l'utente clicka sul widget, noi vediamo se la pressione |
---|
1154 | era veramente vicina al puntatore, e se così, memorizziamo il bottone |
---|
1155 | premuto dall'utente con il campo <CODE>button</CODE> della struttura del |
---|
1156 | widget, e prendiamo tutti gli eventi del mouse con una chiamata alla |
---|
1157 | funzione <CODE>gtk_grab_add()</CODE>. Successivi movimenti del mouse causano il |
---|
1158 | ricalcolo dei valori di controllo (fatto dalla funzione |
---|
1159 | <CODE>gtk_dial_update_mouse</CODE>). Dipendentemente dalla politica che abbiamo |
---|
1160 | stabilito, gli eventi ``value_changed'' possono essere generati |
---|
1161 | istantaneamente (<CODE>GTK_UPDATE_CONTINUOUS</CODE>), dopo un certo tempo aggiunto |
---|
1162 | con la funzione <CODE>gtk_timeout_add()</CODE> (<CODE>GTK_UPDATE_DELAYED</CODE>), o |
---|
1163 | solamente quando il bottone del mouse e' rilasciato |
---|
1164 | (<CODE>GTK_UPDATE_DISCONTINUOUS</CODE>). |
---|
1165 | <P> |
---|
1166 | <BLOCKQUOTE><CODE> |
---|
1167 | <PRE> |
---|
1168 | static gint |
---|
1169 | gtk_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 && |
---|
1199 | (d_perpendicular < dial->pointer_width/2) && |
---|
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 | |
---|
1212 | static gint |
---|
1213 | gtk_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) && |
---|
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 | |
---|
1241 | static gint |
---|
1242 | gtk_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, &x, &y, &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 & mask) |
---|
1280 | gtk_dial_update_mouse (dial, x,y); |
---|
1281 | } |
---|
1282 | |
---|
1283 | return FALSE; |
---|
1284 | } |
---|
1285 | |
---|
1286 | static gint |
---|
1287 | gtk_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 | |
---|
1298 | static void |
---|
1299 | gtk_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 < -M_PI/2.) |
---|
1314 | dial->angle += 2*M_PI; |
---|
1315 | |
---|
1316 | if (dial->angle < -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 |
---|
1351 | dai segnali ``changed'' e ``value_changed''. Il gestore per |
---|
1352 | queste funzioni chiama <CODE>gtk_dial_update()</CODE> per validare gli |
---|
1353 | argomenti, calcolare il nuovo angolo del puntatore e ridisegnare il |
---|
1354 | widget (chiamando <CODE>gtk_widget_draw()</CODE>). |
---|
1355 | <P> |
---|
1356 | <BLOCKQUOTE><CODE> |
---|
1357 | <PRE> |
---|
1358 | static void |
---|
1359 | gtk_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 < 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 | |
---|
1386 | static void |
---|
1387 | gtk_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 | |
---|
1409 | static void |
---|
1410 | gtk_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, è lungo circa 670 linee |
---|
1434 | di codice C. Anche se questo potrebbe sembrare un po' troppo, abbiamo |
---|
1435 | realmente fatto un bel po' con quel tanto di codice, specialmente |
---|
1436 | considerando che molta della lunghezza è costituita da file header e |
---|
1437 | commmenti. Comunque ci sono alcuni miglioramenti che potrebbero essere |
---|
1438 | fatti a questo widget: |
---|
1439 | <P> |
---|
1440 | <UL> |
---|
1441 | <LI> Se tu provate questo widget, troverete che ci sono alcuni lampeggiamenti |
---|
1442 | quando il puntatore viene trascinato in giro. Questo |
---|
1443 | perchè l'intero widget è cancellato ogni volta che il |
---|
1444 | puntatore viene mosso, prima di essere ridisegnato. Spesso, il modo migliore |
---|
1445 | per gestire questo tipo di problema è il disegnare il tutto su una |
---|
1446 | pixmap non visibile, poi copiare il risultato finale sullo schermo |
---|
1447 | in una passata sola (il widget ProgressBar viene disegnato in questo |
---|
1448 | modo). |
---|
1449 | </LI> |
---|
1450 | <LI> L'utente potrebbe essere abilitato ad usare le frecce su e giu per |
---|
1451 | incrementare e diminuire il valore. |
---|
1452 | </LI> |
---|
1453 | <LI> Potrebbe essere carino se il widget avesse i bottoni per |
---|
1454 | incrementare e decrementare il valore di step. Anche se potrebbe essere |
---|
1455 | possibile usare dei widget Bottone incorporati per questo, possiamo anche |
---|
1456 | far sì che il bottone sia auto-ripentente quando premuto, come le frecce |
---|
1457 | in una barra di scorrimento. Molto del codice per implementare questo tipo di |
---|
1458 | comportamento può essere trovato nel widget GtkRange. |
---|
1459 | </LI> |
---|
1460 | <LI> il widget Dial potrebbe essere fatto/creato dentro un widget |
---|
1461 | contenitore con un singolo widget figlio posizionato all'inizio tra i |
---|
1462 | 2 bottoni menzionati prima. L'utente potrebbe poi aggiungere o una etichetta |
---|
1463 | o 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ù</A> |
---|
1468 | </H2> |
---|
1469 | |
---|
1470 | <P> |
---|
1471 | Fin qui abbiamo esposto solo una piccola parte di tutto quello che serve |
---|
1472 | per creare un widget. Se volete davvero scrivere un vostro widget, la |
---|
1473 | miglior risorsa di esempi è lo stesso codice sorgente GTK. Chiedete a voi |
---|
1474 | stessi alcune cose su come deve essere il widget che volete scrivere: è |
---|
1475 | un widget contenitore? dovrà avere una propria finestra? è una modifica di |
---|
1476 | un widget precedente? Trovate poi un widget simile e iniziate a fargli |
---|
1477 | delle modifiche. |
---|
1478 | Buone 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> |
---|