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