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