]> Pileus Git - ~andy/gtk/blob - examples/gtkdial/gtkdial.c
Fix to dial test allocation. Make wheelbarrow not flash window
[~andy/gtk] / examples / gtkdial / gtkdial.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the Free
16  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18 #include <math.h>
19 #include <stdio.h>
20 #include <gtk/gtkmain.h>
21 #include <gtk/gtksignal.h>
22
23 #include "gtkdial.h"
24
25 #define SCROLL_DELAY_LENGTH  300
26 #define DIAL_DEFAULT_SIZE 100
27
28 /* Forward declararations */
29
30 static void gtk_dial_class_init               (GtkDialClass    *klass);
31 static void gtk_dial_init                     (GtkDial         *dial);
32 static void gtk_dial_destroy                  (GtkObject        *object);
33 static void gtk_dial_realize                  (GtkWidget        *widget);
34 static void gtk_dial_size_request             (GtkWidget      *widget,
35                                                GtkRequisition *requisition);
36 static void gtk_dial_size_allocate            (GtkWidget     *widget,
37                                                GtkAllocation *allocation);
38 static gint gtk_dial_expose                   (GtkWidget        *widget,
39                                                 GdkEventExpose   *event);
40 static gint gtk_dial_button_press             (GtkWidget        *widget,
41                                                 GdkEventButton   *event);
42 static gint gtk_dial_button_release           (GtkWidget        *widget,
43                                                 GdkEventButton   *event);
44 static gint gtk_dial_motion_notify            (GtkWidget        *widget,
45                                                 GdkEventMotion   *event);
46 static gint gtk_dial_timer                    (GtkDial         *dial);
47
48 static void gtk_dial_update_mouse             (GtkDial *dial, gint x, gint y);
49 static void gtk_dial_update                   (GtkDial *dial);
50 static void gtk_dial_adjustment_changed       (GtkAdjustment    *adjustment,
51                                                 gpointer          data);
52 static void gtk_dial_adjustment_value_changed (GtkAdjustment    *adjustment,
53                                                 gpointer          data);
54
55 /* Local data */
56
57 static GtkWidgetClass *parent_class = NULL;
58
59 guint
60 gtk_dial_get_type ()
61 {
62   static guint dial_type = 0;
63
64   if (!dial_type)
65     {
66       GtkTypeInfo dial_info =
67       {
68         "GtkDial",
69         sizeof (GtkDial),
70         sizeof (GtkDialClass),
71         (GtkClassInitFunc) gtk_dial_class_init,
72         (GtkObjectInitFunc) gtk_dial_init,
73         (GtkArgSetFunc) NULL,
74         (GtkArgGetFunc) NULL,
75       };
76
77       dial_type = gtk_type_unique (gtk_widget_get_type (), &dial_info);
78     }
79
80   return dial_type;
81 }
82
83 static void
84 gtk_dial_class_init (GtkDialClass *class)
85 {
86   GtkObjectClass *object_class;
87   GtkWidgetClass *widget_class;
88
89   object_class = (GtkObjectClass*) class;
90   widget_class = (GtkWidgetClass*) class;
91
92   parent_class = gtk_type_class (gtk_widget_get_type ());
93
94   object_class->destroy = gtk_dial_destroy;
95
96   widget_class->realize = gtk_dial_realize;
97   widget_class->expose_event = gtk_dial_expose;
98   widget_class->size_request = gtk_dial_size_request;
99   widget_class->size_allocate = gtk_dial_size_allocate;
100   widget_class->button_press_event = gtk_dial_button_press;
101   widget_class->button_release_event = gtk_dial_button_release;
102   widget_class->motion_notify_event = gtk_dial_motion_notify;
103 }
104
105 static void
106 gtk_dial_init (GtkDial *dial)
107 {
108   dial->button = 0;
109   dial->policy = GTK_UPDATE_CONTINUOUS;
110   dial->timer = 0;
111   dial->radius = 0;
112   dial->pointer_width = 0;
113   dial->angle = 0.0;
114   dial->old_value = 0.0;
115   dial->old_lower = 0.0;
116   dial->old_upper = 0.0;
117   dial->adjustment = NULL;
118 }
119
120 GtkWidget*
121 gtk_dial_new (GtkAdjustment *adjustment)
122 {
123   GtkDial *dial;
124
125   dial = gtk_type_new (gtk_dial_get_type ());
126
127   if (!adjustment)
128     adjustment = (GtkAdjustment*) gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
129
130   gtk_dial_set_adjustment (dial, adjustment);
131
132   return GTK_WIDGET (dial);
133 }
134
135 static void
136 gtk_dial_destroy (GtkObject *object)
137 {
138   GtkDial *dial;
139
140   g_return_if_fail (object != NULL);
141   g_return_if_fail (GTK_IS_DIAL (object));
142
143   dial = GTK_DIAL (object);
144
145   if (dial->adjustment)
146     gtk_object_unref (GTK_OBJECT (dial->adjustment));
147
148   if (GTK_OBJECT_CLASS (parent_class)->destroy)
149     (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
150 }
151
152 GtkAdjustment*
153 gtk_dial_get_adjustment (GtkDial *dial)
154 {
155   g_return_val_if_fail (dial != NULL, NULL);
156   g_return_val_if_fail (GTK_IS_DIAL (dial), NULL);
157
158   return dial->adjustment;
159 }
160
161 void
162 gtk_dial_set_update_policy (GtkDial      *dial,
163                              GtkUpdateType  policy)
164 {
165   g_return_if_fail (dial != NULL);
166   g_return_if_fail (GTK_IS_DIAL (dial));
167
168   dial->policy = policy;
169 }
170
171 void
172 gtk_dial_set_adjustment (GtkDial      *dial,
173                           GtkAdjustment *adjustment)
174 {
175   g_return_if_fail (dial != NULL);
176   g_return_if_fail (GTK_IS_DIAL (dial));
177
178   if (dial->adjustment)
179     {
180       gtk_signal_disconnect_by_data (GTK_OBJECT (dial->adjustment), (gpointer) dial);
181       gtk_object_unref (GTK_OBJECT (dial->adjustment));
182     }
183
184   dial->adjustment = adjustment;
185   gtk_object_ref (GTK_OBJECT (dial->adjustment));
186
187   gtk_signal_connect (GTK_OBJECT (adjustment), "changed",
188                       (GtkSignalFunc) gtk_dial_adjustment_changed,
189                       (gpointer) dial);
190   gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed",
191                       (GtkSignalFunc) gtk_dial_adjustment_value_changed,
192                       (gpointer) dial);
193
194   dial->old_value = adjustment->value;
195   dial->old_lower = adjustment->lower;
196   dial->old_upper = adjustment->upper;
197
198   gtk_dial_update (dial);
199 }
200
201 static void
202 gtk_dial_realize (GtkWidget *widget)
203 {
204   GtkDial *dial;
205   GdkWindowAttr attributes;
206   gint attributes_mask;
207
208   g_return_if_fail (widget != NULL);
209   g_return_if_fail (GTK_IS_DIAL (widget));
210
211   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
212   dial = GTK_DIAL (widget);
213
214   attributes.x = widget->allocation.x;
215   attributes.y = widget->allocation.y;
216   attributes.width = widget->allocation.width;
217   attributes.height = widget->allocation.height;
218   attributes.wclass = GDK_INPUT_OUTPUT;
219   attributes.window_type = GDK_WINDOW_CHILD;
220   attributes.event_mask = gtk_widget_get_events (widget) | 
221     GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | 
222     GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK |
223     GDK_POINTER_MOTION_HINT_MASK;
224   attributes.visual = gtk_widget_get_visual (widget);
225   attributes.colormap = gtk_widget_get_colormap (widget);
226
227   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
228   widget->window = gdk_window_new (widget->parent->window, &attributes, attributes_mask);
229
230   widget->style = gtk_style_attach (widget->style, widget->window);
231
232   gdk_window_set_user_data (widget->window, widget);
233
234   gtk_style_set_background (widget->style, widget->window, GTK_STATE_ACTIVE);
235 }
236
237 static void 
238 gtk_dial_size_request (GtkWidget      *widget,
239                        GtkRequisition *requisition)
240 {
241   requisition->width = DIAL_DEFAULT_SIZE;
242   requisition->height = DIAL_DEFAULT_SIZE;
243 }
244
245 static void
246 gtk_dial_size_allocate (GtkWidget     *widget,
247                         GtkAllocation *allocation)
248 {
249   GtkDial *dial;
250
251   g_return_if_fail (widget != NULL);
252   g_return_if_fail (GTK_IS_DIAL (widget));
253   g_return_if_fail (allocation != NULL);
254
255   widget->allocation = *allocation;
256   dial = GTK_DIAL (widget);
257
258   if (GTK_WIDGET_REALIZED (widget))
259     {
260
261       gdk_window_move_resize (widget->window,
262                               allocation->x, allocation->y,
263                               allocation->width, allocation->height);
264
265     }
266   dial->radius = MIN(allocation->width,allocation->height) * 0.45;
267   dial->pointer_width = dial->radius / 5;
268 }
269
270 static gint
271 gtk_dial_expose (GtkWidget      *widget,
272                  GdkEventExpose *event)
273 {
274   GtkDial *dial;
275   GdkPoint points[3];
276   gdouble s,c;
277   gdouble theta;
278   gint xc, yc;
279   gint tick_length;
280   gint i;
281
282   g_return_val_if_fail (widget != NULL, FALSE);
283   g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
284   g_return_val_if_fail (event != NULL, FALSE);
285
286   if (event->count > 0)
287     return FALSE;
288   
289   dial = GTK_DIAL (widget);
290
291   gdk_window_clear_area (widget->window,
292                          0, 0,
293                          widget->allocation.width,
294                          widget->allocation.height);
295
296   xc = widget->allocation.width/2;
297   yc = widget->allocation.height/2;
298
299   /* Draw ticks */
300
301   for (i=0; i<25; i++)
302     {
303       theta = (i*M_PI/18. - M_PI/6.);
304       s = sin(theta);
305       c = cos(theta);
306
307       tick_length = (i%6 == 0) ? dial->pointer_width : dial->pointer_width/2;
308       
309       gdk_draw_line (widget->window,
310                      widget->style->fg_gc[widget->state],
311                      xc + c*(dial->radius - tick_length),
312                      yc - s*(dial->radius - tick_length),
313                      xc + c*dial->radius,
314                      yc - s*dial->radius);
315     }
316
317   /* Draw pointer */
318
319   s = sin(dial->angle);
320   c = cos(dial->angle);
321
322
323   points[0].x = xc + s*dial->pointer_width/2;
324   points[0].y = yc + c*dial->pointer_width/2;
325   points[1].x = xc + c*dial->radius;
326   points[1].y = yc - s*dial->radius;
327   points[2].x = xc - s*dial->pointer_width/2;
328   points[2].y = yc - c*dial->pointer_width/2;
329
330   gtk_draw_polygon (widget->style,
331                     widget->window,
332                     GTK_STATE_NORMAL,
333                     GTK_SHADOW_OUT,
334                     points, 3,
335                     TRUE);
336   
337   return FALSE;
338 }
339
340 static gint
341 gtk_dial_button_press (GtkWidget      *widget,
342                        GdkEventButton *event)
343 {
344   GtkDial *dial;
345   gint dx, dy;
346   double s, c;
347   double d_parallel;
348   double d_perpendicular;
349
350   g_return_val_if_fail (widget != NULL, FALSE);
351   g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
352   g_return_val_if_fail (event != NULL, FALSE);
353
354   dial = GTK_DIAL (widget);
355
356   /* Determine if button press was within pointer region - we 
357      do this by computing the parallel and perpendicular distance of
358      the point where the mouse was pressed from the line passing through
359      the pointer */
360   
361   dx = event->x - widget->allocation.width / 2;
362   dy = widget->allocation.height / 2 - event->y;
363   
364   s = sin(dial->angle);
365   c = cos(dial->angle);
366   
367   d_parallel = s*dy + c*dx;
368   d_perpendicular = fabs(s*dx - c*dy);
369   
370   if (!dial->button &&
371       (d_perpendicular < dial->pointer_width/2) &&
372       (d_parallel > - dial->pointer_width))
373     {
374       gtk_grab_add (widget);
375
376       dial->button = event->button;
377
378       gtk_dial_update_mouse (dial, event->x, event->y);
379     }
380
381   return FALSE;
382 }
383
384 static gint
385 gtk_dial_button_release (GtkWidget      *widget,
386                           GdkEventButton *event)
387 {
388   GtkDial *dial;
389
390   g_return_val_if_fail (widget != NULL, FALSE);
391   g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
392   g_return_val_if_fail (event != NULL, FALSE);
393
394   dial = GTK_DIAL (widget);
395
396   if (dial->button == event->button)
397     {
398       gtk_grab_remove (widget);
399
400       dial->button = 0;
401
402       if (dial->policy == GTK_UPDATE_DELAYED)
403         gtk_timeout_remove (dial->timer);
404       
405       if ((dial->policy != GTK_UPDATE_CONTINUOUS) &&
406           (dial->old_value != dial->adjustment->value))
407         gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
408     }
409
410   return FALSE;
411 }
412
413 static gint
414 gtk_dial_motion_notify (GtkWidget      *widget,
415                          GdkEventMotion *event)
416 {
417   GtkDial *dial;
418   GdkModifierType mods;
419   gint x, y, mask;
420
421   g_return_val_if_fail (widget != NULL, FALSE);
422   g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE);
423   g_return_val_if_fail (event != NULL, FALSE);
424
425   dial = GTK_DIAL (widget);
426
427   if (dial->button != 0)
428     {
429       x = event->x;
430       y = event->y;
431
432       if (event->is_hint || (event->window != widget->window))
433         gdk_window_get_pointer (widget->window, &x, &y, &mods);
434
435       switch (dial->button)
436         {
437         case 1:
438           mask = GDK_BUTTON1_MASK;
439           break;
440         case 2:
441           mask = GDK_BUTTON2_MASK;
442           break;
443         case 3:
444           mask = GDK_BUTTON3_MASK;
445           break;
446         default:
447           mask = 0;
448           break;
449         }
450
451       if (mods & mask)
452         gtk_dial_update_mouse (dial, x,y);
453     }
454
455   return FALSE;
456 }
457
458 static gint
459 gtk_dial_timer (GtkDial *dial)
460 {
461   g_return_val_if_fail (dial != NULL, FALSE);
462   g_return_val_if_fail (GTK_IS_DIAL (dial), FALSE);
463
464   if (dial->policy == GTK_UPDATE_DELAYED)
465     gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
466
467   return FALSE;
468 }
469
470 static void
471 gtk_dial_update_mouse (GtkDial *dial, gint x, gint y)
472 {
473   gint xc, yc;
474   gfloat old_value;
475
476   g_return_if_fail (dial != NULL);
477   g_return_if_fail (GTK_IS_DIAL (dial));
478
479   xc = GTK_WIDGET(dial)->allocation.width / 2;
480   yc = GTK_WIDGET(dial)->allocation.height / 2;
481
482   old_value = dial->adjustment->value;
483   dial->angle = atan2(yc-y, x-xc);
484
485   if (dial->angle < -M_PI/2.)
486     dial->angle += 2*M_PI;
487
488   if (dial->angle < -M_PI/6)
489     dial->angle = -M_PI/6;
490
491   if (dial->angle > 7.*M_PI/6.)
492     dial->angle = 7.*M_PI/6.;
493
494   dial->adjustment->value = dial->adjustment->lower + (7.*M_PI/6 - dial->angle) *
495     (dial->adjustment->upper - dial->adjustment->lower) / (4.*M_PI/3.);
496
497   if (dial->adjustment->value != old_value)
498     {
499       if (dial->policy == GTK_UPDATE_CONTINUOUS)
500         {
501           gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
502         }
503       else
504         {
505           gtk_widget_draw (GTK_WIDGET(dial), NULL);
506
507           if (dial->policy == GTK_UPDATE_DELAYED)
508             {
509               if (dial->timer)
510                 gtk_timeout_remove (dial->timer);
511
512               dial->timer = gtk_timeout_add (SCROLL_DELAY_LENGTH,
513                                              (GtkFunction) gtk_dial_timer,
514                                              (gpointer) dial);
515             }
516         }
517     }
518 }
519
520 static void
521 gtk_dial_update (GtkDial *dial)
522 {
523   gfloat new_value;
524   
525   g_return_if_fail (dial != NULL);
526   g_return_if_fail (GTK_IS_DIAL (dial));
527
528   new_value = dial->adjustment->value;
529   
530   if (new_value < dial->adjustment->lower)
531     new_value = dial->adjustment->lower;
532
533   if (new_value > dial->adjustment->upper)
534     new_value = dial->adjustment->upper;
535
536   if (new_value != dial->adjustment->value)
537     {
538       dial->adjustment->value = new_value;
539       gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
540     }
541
542   dial->angle = 7.*M_PI/6. - (new_value - dial->adjustment->lower) * 4.*M_PI/3. /
543     (dial->adjustment->upper - dial->adjustment->lower);
544
545   gtk_widget_draw (GTK_WIDGET(dial), NULL);
546 }
547
548 static void
549 gtk_dial_adjustment_changed (GtkAdjustment *adjustment,
550                               gpointer       data)
551 {
552   GtkDial *dial;
553
554   g_return_if_fail (adjustment != NULL);
555   g_return_if_fail (data != NULL);
556
557   dial = GTK_DIAL (data);
558
559   if ((dial->old_value != adjustment->value) ||
560       (dial->old_lower != adjustment->lower) ||
561       (dial->old_upper != adjustment->upper))
562     {
563       gtk_dial_update (dial);
564
565       dial->old_value = adjustment->value;
566       dial->old_lower = adjustment->lower;
567       dial->old_upper = adjustment->upper;
568     }
569 }
570
571 static void
572 gtk_dial_adjustment_value_changed (GtkAdjustment *adjustment,
573                                     gpointer       data)
574 {
575   GtkDial *dial;
576
577   g_return_if_fail (adjustment != NULL);
578   g_return_if_fail (data != NULL);
579
580   dial = GTK_DIAL (data);
581
582   if (dial->old_value != adjustment->value)
583     {
584       gtk_dial_update (dial);
585
586       dial->old_value = adjustment->value;
587     }
588 }
589
590