1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
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.
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.
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.
20 #include <gtk/gtkmain.h>
21 #include <gtk/gtksignal.h>
25 #define SCROLL_DELAY_LENGTH 300
26 #define DIAL_DEFAULT_SIZE 100
28 /* Forward declararations */
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);
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,
52 static void gtk_dial_adjustment_value_changed (GtkAdjustment *adjustment,
57 static GtkWidgetClass *parent_class = NULL;
62 static guint dial_type = 0;
66 GtkTypeInfo dial_info =
70 sizeof (GtkDialClass),
71 (GtkClassInitFunc) gtk_dial_class_init,
72 (GtkObjectInitFunc) gtk_dial_init,
77 dial_type = gtk_type_unique (gtk_widget_get_type (), &dial_info);
84 gtk_dial_class_init (GtkDialClass *class)
86 GtkObjectClass *object_class;
87 GtkWidgetClass *widget_class;
89 object_class = (GtkObjectClass*) class;
90 widget_class = (GtkWidgetClass*) class;
92 parent_class = gtk_type_class (gtk_widget_get_type ());
94 object_class->destroy = gtk_dial_destroy;
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;
106 gtk_dial_init (GtkDial *dial)
109 dial->policy = GTK_UPDATE_CONTINUOUS;
112 dial->pointer_width = 0;
114 dial->old_value = 0.0;
115 dial->old_lower = 0.0;
116 dial->old_upper = 0.0;
117 dial->adjustment = NULL;
121 gtk_dial_new (GtkAdjustment *adjustment)
125 dial = gtk_type_new (gtk_dial_get_type ());
128 adjustment = (GtkAdjustment*) gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
130 gtk_dial_set_adjustment (dial, adjustment);
132 return GTK_WIDGET (dial);
136 gtk_dial_destroy (GtkObject *object)
140 g_return_if_fail (object != NULL);
141 g_return_if_fail (GTK_IS_DIAL (object));
143 dial = GTK_DIAL (object);
145 if (dial->adjustment)
146 gtk_object_unref (GTK_OBJECT (dial->adjustment));
148 if (GTK_OBJECT_CLASS (parent_class)->destroy)
149 (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
153 gtk_dial_get_adjustment (GtkDial *dial)
155 g_return_val_if_fail (dial != NULL, NULL);
156 g_return_val_if_fail (GTK_IS_DIAL (dial), NULL);
158 return dial->adjustment;
162 gtk_dial_set_update_policy (GtkDial *dial,
163 GtkUpdateType policy)
165 g_return_if_fail (dial != NULL);
166 g_return_if_fail (GTK_IS_DIAL (dial));
168 dial->policy = policy;
172 gtk_dial_set_adjustment (GtkDial *dial,
173 GtkAdjustment *adjustment)
175 g_return_if_fail (dial != NULL);
176 g_return_if_fail (GTK_IS_DIAL (dial));
178 if (dial->adjustment)
180 gtk_signal_disconnect_by_data (GTK_OBJECT (dial->adjustment), (gpointer) dial);
181 gtk_object_unref (GTK_OBJECT (dial->adjustment));
184 dial->adjustment = adjustment;
185 gtk_object_ref (GTK_OBJECT (dial->adjustment));
187 gtk_signal_connect (GTK_OBJECT (adjustment), "changed",
188 (GtkSignalFunc) gtk_dial_adjustment_changed,
190 gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed",
191 (GtkSignalFunc) gtk_dial_adjustment_value_changed,
194 dial->old_value = adjustment->value;
195 dial->old_lower = adjustment->lower;
196 dial->old_upper = adjustment->upper;
198 gtk_dial_update (dial);
202 gtk_dial_realize (GtkWidget *widget)
205 GdkWindowAttr attributes;
206 gint attributes_mask;
208 g_return_if_fail (widget != NULL);
209 g_return_if_fail (GTK_IS_DIAL (widget));
211 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
212 dial = GTK_DIAL (widget);
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);
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);
230 widget->style = gtk_style_attach (widget->style, widget->window);
232 gdk_window_set_user_data (widget->window, widget);
234 gtk_style_set_background (widget->style, widget->window, GTK_STATE_ACTIVE);
238 gtk_dial_size_request (GtkWidget *widget,
239 GtkRequisition *requisition)
241 requisition->width = DIAL_DEFAULT_SIZE;
242 requisition->height = DIAL_DEFAULT_SIZE;
246 gtk_dial_size_allocate (GtkWidget *widget,
247 GtkAllocation *allocation)
251 g_return_if_fail (widget != NULL);
252 g_return_if_fail (GTK_IS_DIAL (widget));
253 g_return_if_fail (allocation != NULL);
255 widget->allocation = *allocation;
256 dial = GTK_DIAL (widget);
258 if (GTK_WIDGET_REALIZED (widget))
261 gdk_window_move_resize (widget->window,
262 allocation->x, allocation->y,
263 allocation->width, allocation->height);
266 dial->radius = MIN(allocation->width,allocation->height) * 0.45;
267 dial->pointer_width = dial->radius / 5;
271 gtk_dial_expose (GtkWidget *widget,
272 GdkEventExpose *event)
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);
286 if (event->count > 0)
289 dial = GTK_DIAL (widget);
291 gdk_window_clear_area (widget->window,
293 widget->allocation.width,
294 widget->allocation.height);
296 xc = widget->allocation.width/2;
297 yc = widget->allocation.height/2;
303 theta = (i*M_PI/18. - M_PI/6.);
307 tick_length = (i%6 == 0) ? dial->pointer_width : dial->pointer_width/2;
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),
314 yc - s*dial->radius);
319 s = sin(dial->angle);
320 c = cos(dial->angle);
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;
330 gtk_draw_polygon (widget->style,
341 gtk_dial_button_press (GtkWidget *widget,
342 GdkEventButton *event)
348 double d_perpendicular;
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);
354 dial = GTK_DIAL (widget);
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
361 dx = event->x - widget->allocation.width / 2;
362 dy = widget->allocation.height / 2 - event->y;
364 s = sin(dial->angle);
365 c = cos(dial->angle);
367 d_parallel = s*dy + c*dx;
368 d_perpendicular = fabs(s*dx - c*dy);
371 (d_perpendicular < dial->pointer_width/2) &&
372 (d_parallel > - dial->pointer_width))
374 gtk_grab_add (widget);
376 dial->button = event->button;
378 gtk_dial_update_mouse (dial, event->x, event->y);
385 gtk_dial_button_release (GtkWidget *widget,
386 GdkEventButton *event)
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);
394 dial = GTK_DIAL (widget);
396 if (dial->button == event->button)
398 gtk_grab_remove (widget);
402 if (dial->policy == GTK_UPDATE_DELAYED)
403 gtk_timeout_remove (dial->timer);
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");
414 gtk_dial_motion_notify (GtkWidget *widget,
415 GdkEventMotion *event)
418 GdkModifierType mods;
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);
425 dial = GTK_DIAL (widget);
427 if (dial->button != 0)
432 if (event->is_hint || (event->window != widget->window))
433 gdk_window_get_pointer (widget->window, &x, &y, &mods);
435 switch (dial->button)
438 mask = GDK_BUTTON1_MASK;
441 mask = GDK_BUTTON2_MASK;
444 mask = GDK_BUTTON3_MASK;
452 gtk_dial_update_mouse (dial, x,y);
459 gtk_dial_timer (GtkDial *dial)
461 g_return_val_if_fail (dial != NULL, FALSE);
462 g_return_val_if_fail (GTK_IS_DIAL (dial), FALSE);
464 if (dial->policy == GTK_UPDATE_DELAYED)
465 gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
471 gtk_dial_update_mouse (GtkDial *dial, gint x, gint y)
476 g_return_if_fail (dial != NULL);
477 g_return_if_fail (GTK_IS_DIAL (dial));
479 xc = GTK_WIDGET(dial)->allocation.width / 2;
480 yc = GTK_WIDGET(dial)->allocation.height / 2;
482 old_value = dial->adjustment->value;
483 dial->angle = atan2(yc-y, x-xc);
485 if (dial->angle < -M_PI/2.)
486 dial->angle += 2*M_PI;
488 if (dial->angle < -M_PI/6)
489 dial->angle = -M_PI/6;
491 if (dial->angle > 7.*M_PI/6.)
492 dial->angle = 7.*M_PI/6.;
494 dial->adjustment->value = dial->adjustment->lower + (7.*M_PI/6 - dial->angle) *
495 (dial->adjustment->upper - dial->adjustment->lower) / (4.*M_PI/3.);
497 if (dial->adjustment->value != old_value)
499 if (dial->policy == GTK_UPDATE_CONTINUOUS)
501 gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
505 gtk_widget_draw (GTK_WIDGET(dial), NULL);
507 if (dial->policy == GTK_UPDATE_DELAYED)
510 gtk_timeout_remove (dial->timer);
512 dial->timer = gtk_timeout_add (SCROLL_DELAY_LENGTH,
513 (GtkFunction) gtk_dial_timer,
521 gtk_dial_update (GtkDial *dial)
525 g_return_if_fail (dial != NULL);
526 g_return_if_fail (GTK_IS_DIAL (dial));
528 new_value = dial->adjustment->value;
530 if (new_value < dial->adjustment->lower)
531 new_value = dial->adjustment->lower;
533 if (new_value > dial->adjustment->upper)
534 new_value = dial->adjustment->upper;
536 if (new_value != dial->adjustment->value)
538 dial->adjustment->value = new_value;
539 gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");
542 dial->angle = 7.*M_PI/6. - (new_value - dial->adjustment->lower) * 4.*M_PI/3. /
543 (dial->adjustment->upper - dial->adjustment->lower);
545 gtk_widget_draw (GTK_WIDGET(dial), NULL);
549 gtk_dial_adjustment_changed (GtkAdjustment *adjustment,
554 g_return_if_fail (adjustment != NULL);
555 g_return_if_fail (data != NULL);
557 dial = GTK_DIAL (data);
559 if ((dial->old_value != adjustment->value) ||
560 (dial->old_lower != adjustment->lower) ||
561 (dial->old_upper != adjustment->upper))
563 gtk_dial_update (dial);
565 dial->old_value = adjustment->value;
566 dial->old_lower = adjustment->lower;
567 dial->old_upper = adjustment->upper;
572 gtk_dial_adjustment_value_changed (GtkAdjustment *adjustment,
577 g_return_if_fail (adjustment != NULL);
578 g_return_if_fail (data != NULL);
580 dial = GTK_DIAL (data);
582 if (dial->old_value != adjustment->value)
584 gtk_dial_update (dial);
586 dial->old_value = adjustment->value;