]> Pileus Git - ~andy/gtk/blob - gtk/gtkhsv.c
7b72f9511e66ee2bb4b8b87d4b3a15cd9b2b446c
[~andy/gtk] / gtk / gtkhsv.c
1 /* HSV color selector for GTK+
2  *
3  * Copyright (C) 1999 The Free Software Foundation
4  *
5  * Authors: Simon Budig <Simon.Budig@unix-ag.org> (original code)
6  *          Federico Mena-Quintero <federico@gimp.org> (cleanup for GTK+)
7  *          Jonathan Blandford <jrb@redhat.com> (cleanup for GTK+)
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the
21  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22  * Boston, MA 02111-1307, USA.
23  */
24
25 #include <math.h>
26 #include <string.h>
27 #include "gtkhsv.h"
28 #include "gdk/gdkkeysyms.h"
29 #include "gtkbindings.h"
30 #include "gtkcontainer.h"
31 #include "gtkmarshalers.h"
32
33 /*
34  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
35  * file for a list of people on the GTK+ Team.  See the ChangeLog
36  * files for a list of changes.  These files are distributed with
37  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
38  */
39
40 /* Default width/height */
41 #define DEFAULT_SIZE 100
42
43 /* Default ring width */
44 #define DEFAULT_RING_WIDTH 10
45
46
47 /* Dragging modes */
48 typedef enum {
49   DRAG_NONE,
50   DRAG_H,
51   DRAG_SV
52 } DragMode;
53
54 /* Private part of the GtkHSV structure */
55 typedef struct {
56   /* Color value */
57   double h;
58   double s;
59   double v;
60   
61   /* Size and ring width */
62   int size;
63   int ring_width;
64   
65   /* Window for capturing events */
66   GdkWindow *window;
67   
68   /* GC for drawing */
69   GdkGC *gc;
70   
71   /* Dragging mode */
72   DragMode mode;
73
74   guint focus_on_ring : 1;
75   
76 } HSVPrivate;
77
78 \f
79
80 /* Signal IDs */
81
82 enum {
83   CHANGED,
84   MOVE,
85   LAST_SIGNAL
86 };
87
88 static void     gtk_hsv_class_init     (GtkHSVClass      *class);
89 static void     gtk_hsv_init           (GtkHSV           *hsv);
90 static void     gtk_hsv_destroy        (GtkObject        *object);
91 static void     gtk_hsv_map            (GtkWidget        *widget);
92 static void     gtk_hsv_unmap          (GtkWidget        *widget);
93 static void     gtk_hsv_realize        (GtkWidget        *widget);
94 static void     gtk_hsv_unrealize      (GtkWidget        *widget);
95 static void     gtk_hsv_size_request   (GtkWidget        *widget,
96                                         GtkRequisition   *requisition);
97 static void     gtk_hsv_size_allocate  (GtkWidget        *widget,
98                                         GtkAllocation    *allocation);
99 static gint     gtk_hsv_button_press   (GtkWidget        *widget,
100                                         GdkEventButton   *event);
101 static gint     gtk_hsv_button_release (GtkWidget        *widget,
102                                         GdkEventButton   *event);
103 static gint     gtk_hsv_motion         (GtkWidget        *widget,
104                                         GdkEventMotion   *event);
105 static gint     gtk_hsv_expose         (GtkWidget        *widget,
106                                         GdkEventExpose   *event);
107 static gboolean gtk_hsv_focus          (GtkWidget        *widget,
108                                         GtkDirectionType  direction);
109 static void     gtk_hsv_move           (GtkHSV           *hsv,
110                                         GtkDirectionType  dir);
111 static GdkGC *  gtk_hsv_get_focus_gc   (GtkHSV           *hsv,
112                                         gint             *line_width);
113
114 static guint hsv_signals[LAST_SIGNAL];
115 static GtkWidgetClass *parent_class;
116
117
118 /**
119  * gtk_hsv_get_type:
120  * @void:
121  *
122  * Registers the &GtkHSV class if necessary, and returns the type ID associated
123  * to it.
124  *
125  * Return value: The type ID of the &GtkHSV class.
126  **/
127 GType
128 gtk_hsv_get_type (void)
129 {
130   static GType hsv_type = 0;
131   
132   if (!hsv_type) {
133     static const GTypeInfo hsv_info = {
134       sizeof (GtkHSVClass),
135       NULL,             /* base_init */
136       NULL,             /* base_finalize */
137       (GClassInitFunc) gtk_hsv_class_init,
138       NULL,             /* class_finalize */
139       NULL,             /* class_data */
140       sizeof (GtkHSV),
141       0,                /* n_preallocs */
142       (GInstanceInitFunc) gtk_hsv_init,
143     };
144     
145     hsv_type = g_type_register_static (GTK_TYPE_WIDGET, "GtkHSV",
146                                        &hsv_info, 0);
147   }
148   
149   return hsv_type;
150 }
151
152 /* Class initialization function for the HSV color selector */
153 static void
154 gtk_hsv_class_init (GtkHSVClass *class)
155 {
156   GtkObjectClass *object_class;
157   GtkWidgetClass *widget_class;
158   GtkHSVClass    *hsv_class;
159   GtkBindingSet  *binding_set;
160   
161   object_class = (GtkObjectClass *) class;
162   widget_class = (GtkWidgetClass *) class;
163   hsv_class = GTK_HSV_CLASS (class);
164   
165   parent_class = g_type_class_peek_parent (class);
166   
167   object_class->destroy = gtk_hsv_destroy;
168   
169   widget_class->map = gtk_hsv_map;
170   widget_class->unmap = gtk_hsv_unmap;                                      
171   widget_class->realize = gtk_hsv_realize;
172   widget_class->unrealize = gtk_hsv_unrealize;
173   widget_class->size_request = gtk_hsv_size_request;
174   widget_class->size_allocate = gtk_hsv_size_allocate;
175   widget_class->button_press_event = gtk_hsv_button_press;
176   widget_class->button_release_event = gtk_hsv_button_release;
177   widget_class->motion_notify_event = gtk_hsv_motion;
178   widget_class->expose_event = gtk_hsv_expose;
179   widget_class->focus = gtk_hsv_focus;
180   
181   hsv_class->move = gtk_hsv_move;
182   
183   hsv_signals[CHANGED] =
184     g_signal_new ("changed",
185                   G_OBJECT_CLASS_TYPE (object_class),
186                   G_SIGNAL_RUN_FIRST,
187                   G_STRUCT_OFFSET (GtkHSVClass, changed),
188                   NULL, NULL,
189                   _gtk_marshal_VOID__VOID,
190                   G_TYPE_NONE, 0);
191
192   hsv_signals[MOVE] =
193     g_signal_new ("move",
194                   G_OBJECT_CLASS_TYPE (object_class),
195                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
196                   G_STRUCT_OFFSET (GtkHSVClass, move),
197                   NULL, NULL,
198                   _gtk_marshal_VOID__ENUM,
199                   G_TYPE_NONE, 1,
200                   GTK_TYPE_DIRECTION_TYPE);
201
202   binding_set = gtk_binding_set_by_class (class);
203
204   gtk_binding_entry_add_signal (binding_set, GDK_Up, 0,
205                                 "move", 1,
206                                 G_TYPE_ENUM, GTK_DIR_UP);
207   gtk_binding_entry_add_signal (binding_set, GDK_KP_Up, 0,
208                                 "move", 1,
209                                 G_TYPE_ENUM, GTK_DIR_UP);
210   
211   gtk_binding_entry_add_signal (binding_set, GDK_Down, 0,
212                                 "move", 1,
213                                 G_TYPE_ENUM, GTK_DIR_DOWN);
214   gtk_binding_entry_add_signal (binding_set, GDK_KP_Down, 0,
215                                 "move", 1,
216                                 G_TYPE_ENUM, GTK_DIR_DOWN);
217
218   
219   gtk_binding_entry_add_signal (binding_set, GDK_Right, 0,
220                                 "move", 1,
221                                 G_TYPE_ENUM, GTK_DIR_RIGHT);
222   gtk_binding_entry_add_signal (binding_set, GDK_KP_Right, 0,
223                                 "move", 1,
224                                 G_TYPE_ENUM, GTK_DIR_RIGHT);
225   
226   gtk_binding_entry_add_signal (binding_set, GDK_Left, 0,
227                                 "move", 1,
228                                 G_TYPE_ENUM, GTK_DIR_LEFT);
229   gtk_binding_entry_add_signal (binding_set, GDK_KP_Left, 0,
230                                 "move", 1,
231                                 G_TYPE_ENUM, GTK_DIR_LEFT);
232 }
233
234 /* Object initialization function for the HSV color selector */
235 static void
236 gtk_hsv_init (GtkHSV *hsv)
237 {
238   HSVPrivate *priv;
239   
240   priv = g_new0 (HSVPrivate, 1);
241   hsv->priv = priv;
242   
243   GTK_WIDGET_SET_FLAGS (hsv, GTK_NO_WINDOW);
244   GTK_WIDGET_SET_FLAGS (hsv, GTK_CAN_FOCUS);
245   
246   priv->h = 0.0;
247   priv->s = 0.0;
248   priv->v = 0.0;
249   
250   priv->size = DEFAULT_SIZE;
251   priv->ring_width = DEFAULT_RING_WIDTH;
252 }
253
254 /* Destroy handler for the HSV color selector */
255 static void
256 gtk_hsv_destroy (GtkObject *object)
257 {
258   GtkHSV *hsv;
259   
260   g_return_if_fail (GTK_IS_HSV (object));
261   
262   hsv = GTK_HSV (object);
263
264   if (hsv->priv)
265     {
266       g_free (hsv->priv);
267       hsv->priv = NULL;
268     }
269
270   GTK_OBJECT_CLASS (parent_class)->destroy (object);
271 }
272
273 /* Default signal handlers */
274
275     
276 /* Map handler for the HSV color selector */
277
278 static void
279 gtk_hsv_map (GtkWidget *widget)
280 {
281   GtkHSV *hsv;
282   HSVPrivate *priv;
283
284   hsv = GTK_HSV (widget);
285   priv = hsv->priv;
286
287   GTK_WIDGET_CLASS (parent_class)->map (widget);
288
289   gdk_window_show (priv->window);
290 }
291
292 /* Unmap handler for the HSV color selector */
293
294 static void
295 gtk_hsv_unmap (GtkWidget *widget)
296 {
297   GtkHSV *hsv;
298   HSVPrivate *priv;
299
300   hsv = GTK_HSV (widget);
301   priv = hsv->priv;
302
303   gdk_window_hide (priv->window);
304
305   GTK_WIDGET_CLASS (parent_class)->unmap (widget);
306 }                                                                           
307                                       
308 /* Realize handler for the HSV color selector */
309 static void
310 gtk_hsv_realize (GtkWidget *widget)
311 {
312   GtkHSV *hsv;
313   HSVPrivate *priv;
314   GdkWindowAttr attr;
315   int attr_mask;
316   GdkWindow *parent_window;
317   
318   hsv = GTK_HSV (widget);
319   priv = hsv->priv;
320   
321   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
322   
323   /* Create window */
324   
325   attr.window_type = GDK_WINDOW_CHILD;
326   attr.x = widget->allocation.x;
327   attr.y = widget->allocation.y;
328   attr.width = widget->allocation.width;
329   attr.height = widget->allocation.height;
330   attr.wclass = GDK_INPUT_ONLY;
331   attr.event_mask = gtk_widget_get_events (widget);
332   attr.event_mask |= (GDK_KEY_PRESS_MASK
333                       | GDK_BUTTON_PRESS_MASK
334                       | GDK_BUTTON_RELEASE_MASK
335                       | GDK_POINTER_MOTION_MASK
336                       | GDK_ENTER_NOTIFY_MASK
337                       | GDK_LEAVE_NOTIFY_MASK);
338   
339   attr_mask = GDK_WA_X | GDK_WA_Y;
340   
341   parent_window = gtk_widget_get_parent_window (widget);
342   
343   widget->window = parent_window;
344   g_object_ref (widget->window);
345   
346   priv->window = gdk_window_new (parent_window, &attr, attr_mask);
347   gdk_window_set_user_data (priv->window, hsv);
348   
349   widget->style = gtk_style_attach (widget->style, widget->window);
350   
351   /* Create GC */
352   
353   priv->gc = gdk_gc_new (parent_window);
354 }
355
356 /* Unrealize handler for the HSV color selector */
357 static void
358 gtk_hsv_unrealize (GtkWidget *widget)
359 {
360   GtkHSV *hsv;
361   HSVPrivate *priv;
362   
363   hsv = GTK_HSV (widget);
364   priv = hsv->priv;
365   
366   gdk_window_set_user_data (priv->window, NULL);
367   gdk_window_destroy (priv->window);
368   priv->window = NULL;
369   
370   g_object_unref (priv->gc);
371   priv->gc = NULL;
372   
373   if (GTK_WIDGET_CLASS (parent_class)->unrealize)
374     GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
375 }
376
377 /* Size_request handler for the HSV color selector */
378 static void
379 gtk_hsv_size_request (GtkWidget      *widget,
380                       GtkRequisition *requisition)
381 {
382   GtkHSV *hsv;
383   HSVPrivate *priv;
384   
385   hsv = GTK_HSV (widget);
386   priv = hsv->priv;
387   
388   requisition->width = priv->size;
389   requisition->height = priv->size;
390 }
391
392 /* Size_allocate handler for the HSV color selector */
393 static void
394 gtk_hsv_size_allocate (GtkWidget     *widget,
395                        GtkAllocation *allocation)
396 {
397   GtkHSV *hsv;
398   HSVPrivate *priv;
399   
400   hsv = GTK_HSV (widget);
401   priv = hsv->priv;
402   
403   widget->allocation = *allocation;
404   
405   if (GTK_WIDGET_REALIZED (widget))
406     gdk_window_move_resize (priv->window,
407                             allocation->x,
408                             allocation->y,
409                             allocation->width,
410                             allocation->height);
411 }
412
413
414 /* Utility functions */
415
416 #define INTENSITY(r, g, b) ((r) * 0.30 + (g) * 0.59 + (b) * 0.11)
417
418 /* Converts from HSV to RGB */
419 static void
420 hsv_to_rgb (gdouble *h,
421             gdouble *s,
422             gdouble *v)
423 {
424   gdouble hue, saturation, value;
425   gdouble f, p, q, t;
426   
427   if (*s == 0.0)
428     {
429       *h = *v;
430       *s = *v;
431       *v = *v; /* heh */
432     }
433   else
434     {
435       hue = *h * 6.0;
436       saturation = *s;
437       value = *v;
438       
439       if (hue == 6.0)
440         hue = 0.0;
441       
442       f = hue - (int) hue;
443       p = value * (1.0 - saturation);
444       q = value * (1.0 - saturation * f);
445       t = value * (1.0 - saturation * (1.0 - f));
446       
447       switch ((int) hue)
448         {
449         case 0:
450           *h = value;
451           *s = t;
452           *v = p;
453           break;
454           
455         case 1:
456           *h = q;
457           *s = value;
458           *v = p;
459           break;
460           
461         case 2:
462           *h = p;
463           *s = value;
464           *v = t;
465           break;
466           
467         case 3:
468           *h = p;
469           *s = q;
470           *v = value;
471           break;
472           
473         case 4:
474           *h = t;
475           *s = p;
476           *v = value;
477           break;
478           
479         case 5:
480           *h = value;
481           *s = p;
482           *v = q;
483           break;
484           
485         default:
486           g_assert_not_reached ();
487         }
488     }
489 }
490
491 /* Converts from RGB to HSV */
492 static void
493 rgb_to_hsv (gdouble *r,
494             gdouble *g,
495             gdouble *b)
496 {
497   gdouble red, green, blue;
498   gdouble h, s, v;
499   gdouble min, max;
500   gdouble delta;
501   
502   red = *r;
503   green = *g;
504   blue = *b;
505   
506   h = 0.0;
507   
508   if (red > green)
509     {
510       if (red > blue)
511         max = red;
512       else
513         max = blue;
514       
515       if (green < blue)
516         min = green;
517       else
518         min = blue;
519     }
520   else
521     {
522       if (green > blue)
523         max = green;
524       else
525         max = blue;
526       
527       if (red < blue)
528         min = red;
529       else
530         min = blue;
531     }
532   
533   v = max;
534   
535   if (max != 0.0)
536     s = (max - min) / max;
537   else
538     s = 0.0;
539   
540   if (s == 0.0)
541     h = 0.0;
542   else
543     {
544       delta = max - min;
545       
546       if (red == max)
547         h = (green - blue) / delta;
548       else if (green == max)
549         h = 2 + (blue - red) / delta;
550       else if (blue == max)
551         h = 4 + (red - green) / delta;
552       
553       h /= 6.0;
554       
555       if (h < 0.0)
556         h += 1.0;
557       else if (h > 1.0)
558         h -= 1.0;
559     }
560   
561   *r = h;
562   *g = s;
563   *b = v;
564 }
565
566 /* Computes the vertices of the saturation/value triangle */
567 static void
568 compute_triangle (GtkHSV *hsv,
569                   gint   *hx,
570                   gint   *hy,
571                   gint   *sx,
572                   gint   *sy,
573                   gint   *vx,
574                   gint   *vy)
575 {
576   HSVPrivate *priv;
577   gdouble center;
578   gdouble inner, outer;
579   gdouble angle;
580   
581   priv = hsv->priv;
582   
583   center = priv->size / 2.0;
584   outer = priv->size / 2.0;
585   inner = outer - priv->ring_width;
586   angle = priv->h * 2.0 * G_PI;
587   
588   *hx = floor (center + cos (angle) * inner + 0.5);
589   *hy = floor (center - sin (angle) * inner + 0.5);
590   *sx = floor (center + cos (angle + 2.0 * G_PI / 3.0) * inner + 0.5);
591   *sy = floor (center - sin (angle + 2.0 * G_PI / 3.0) * inner + 0.5);
592   *vx = floor (center + cos (angle + 4.0 * G_PI / 3.0) * inner + 0.5);
593   *vy = floor (center - sin (angle + 4.0 * G_PI / 3.0) * inner + 0.5);
594 }
595
596 /* Computes whether a point is inside the hue ring */
597 static gboolean
598 is_in_ring (GtkHSV *hsv,
599             gdouble x,
600             gdouble y)
601 {
602   HSVPrivate *priv;
603   gdouble dx, dy, dist;
604   gdouble center, inner, outer;
605   
606   priv = hsv->priv;
607   
608   center = priv->size / 2.0;
609   outer = priv->size / 2.0;
610   inner = outer - priv->ring_width;
611   
612   dx = x - center;
613   dy = center - y;
614   dist = dx * dx + dy * dy;
615   
616   return (dist >= inner * inner && dist <= outer * outer);
617 }
618
619 /* Computes a saturation/value pair based on the mouse coordinates */
620 static void
621 compute_sv (GtkHSV  *hsv,
622             gdouble  x,
623             gdouble  y,
624             gdouble *s,
625             gdouble *v)
626 {
627   HSVPrivate *priv;
628   int ihx, ihy, isx, isy, ivx, ivy;
629   double hx, hy, sx, sy, vx, vy;
630   double center;
631   
632   priv = hsv->priv;
633   
634   compute_triangle (hsv, &ihx, &ihy, &isx, &isy, &ivx, &ivy);
635   center = priv->size / 2.0;
636   hx = ihx - center;
637   hy = center - ihy;
638   sx = isx - center;
639   sy = center - isy;
640   vx = ivx - center;
641   vy = center - ivy;
642   x -= center;
643   y = center - y;
644   
645   if (vx * (x - sx) + vy * (y - sy) < 0.0)
646     {
647       *s = 1.0;
648       *v = (((x - sx) * (hx - sx) + (y - sy) * (hy-sy))
649             / ((hx - sx) * (hx - sx) + (hy - sy) * (hy - sy)));
650       
651       if (*v < 0.0)
652         *v = 0.0;
653       else if (*v > 1.0)
654         *v = 1.0;
655     }
656   else if (hx * (x - sx) + hy * (y - sy) < 0.0)
657     {
658       *s = 0.0;
659       *v = (((x - sx) * (vx - sx) + (y - sy) * (vy - sy))
660             / ((vx - sx) * (vx - sx) + (vy - sy) * (vy - sy)));
661       
662       if (*v < 0.0)
663         *v = 0.0;
664       else if (*v > 1.0)
665         *v = 1.0;
666     }
667   else if (sx * (x - hx) + sy * (y - hy) < 0.0)
668     {
669       *v = 1.0;
670       *s = (((x - vx) * (hx - vx) + (y - vy) * (hy - vy)) /
671             ((hx - vx) * (hx - vx) + (hy - vy) * (hy - vy)));
672       
673       if (*s < 0.0)
674         *s = 0.0;
675       else if (*s > 1.0)
676         *s = 1.0;
677     }
678   else
679     {
680       *v = (((x - sx) * (hy - vy) - (y - sy) * (hx - vx))
681             / ((vx - sx) * (hy - vy) - (vy - sy) * (hx - vx)));
682       
683       if (*v<= 0.0)
684         {
685           *v = 0.0;
686           *s = 0.0;
687         }
688       else
689         {
690           if (*v > 1.0)
691             *v = 1.0;
692
693           if (fabs (hy - vy) < fabs (hx - vx))
694             *s = (x - sx - *v * (vx - sx)) / (*v * (hx - vx));
695           else
696             *s = (y - sy - *v * (vy - sy)) / (*v * (hy - vy));
697             
698           if (*s < 0.0)
699             *s = 0.0;
700           else if (*s > 1.0)
701             *s = 1.0;
702         }
703     }
704 }
705
706 /* Computes whether a point is inside the saturation/value triangle */
707 static gboolean
708 is_in_triangle (GtkHSV *hsv,
709                 gdouble x,
710                 gdouble y)
711 {
712   int hx, hy, sx, sy, vx, vy;
713   double det, s, v;
714   
715   compute_triangle (hsv, &hx, &hy, &sx, &sy, &vx, &vy);
716   
717   det = (vx - sx) * (hy - sy) - (vy - sy) * (hx - sx);
718   
719   s = ((x - sx) * (hy - sy) - (y - sy) * (hx - sx)) / det;
720   v = ((vx - sx) * (y - sy) - (vy - sy) * (x - sx)) / det;
721   
722   return (s >= 0.0 && v >= 0.0 && s + v <= 1.0);
723 }
724
725 /* Computes a value based on the mouse coordinates */
726 static double
727 compute_v (GtkHSV *hsv,
728            gdouble x,
729            gdouble y)
730 {
731   HSVPrivate *priv;
732   double center;
733   double dx, dy;
734   double angle;
735   
736   priv = hsv->priv;
737   
738   center = priv->size / 2.0;
739   dx = x - center;
740   dy = center - y;
741   
742   angle = atan2 (dy, dx);
743   if (angle < 0.0)
744     angle += 2.0 * G_PI;
745   
746   return angle / (2.0 * G_PI);
747 }
748
749 /* Event handlers */
750
751 static void
752 set_cross_grab (GtkHSV *hsv,
753                 guint32 time)
754 {
755   HSVPrivate *priv;
756   GdkCursor *cursor;
757   
758   priv = hsv->priv;
759   
760   cursor = gdk_cursor_new_for_display (gtk_widget_get_display (GTK_WIDGET (hsv)),
761                                        GDK_CROSSHAIR);
762   gdk_pointer_grab (priv->window, FALSE,
763                     (GDK_POINTER_MOTION_MASK
764                      | GDK_POINTER_MOTION_HINT_MASK
765                      | GDK_BUTTON_RELEASE_MASK),
766                     NULL,
767                     cursor,
768                     time);
769   gdk_cursor_unref (cursor);
770 }
771
772 /* Button_press_event handler for the HSV color selector */
773 static gint
774 gtk_hsv_button_press (GtkWidget      *widget,
775                       GdkEventButton *event)
776 {
777   GtkHSV *hsv;
778   HSVPrivate *priv;
779   double x, y;
780   
781   hsv = GTK_HSV (widget);
782   priv = hsv->priv;
783   
784   if (priv->mode != DRAG_NONE || event->button != 1)
785     return FALSE;
786   
787   x = event->x;
788   y = event->y;
789   
790   if (is_in_ring (hsv, x, y))
791     {
792       priv->mode = DRAG_H;
793       set_cross_grab (hsv, event->time);
794       
795       gtk_hsv_set_color (hsv,
796                          compute_v (hsv, x, y),
797                          priv->s,
798                          priv->v);
799
800       gtk_widget_grab_focus (widget);
801       priv->focus_on_ring = TRUE;
802       
803       return TRUE;
804     }
805   
806   if (is_in_triangle (hsv, x, y))
807     {
808       gdouble s, v;
809       
810       priv->mode = DRAG_SV;
811       set_cross_grab (hsv, event->time);
812       
813       compute_sv (hsv, x, y, &s, &v);
814       gtk_hsv_set_color (hsv, priv->h, s, v);
815
816       gtk_widget_grab_focus (widget);
817       priv->focus_on_ring = FALSE;
818       
819       return TRUE;
820     }
821   
822   return FALSE;
823 }
824
825 /* Button_release_event handler for the HSV color selector */
826 static gint
827 gtk_hsv_button_release (GtkWidget      *widget,
828                         GdkEventButton *event)
829 {
830   GtkHSV *hsv;
831   HSVPrivate *priv;
832   DragMode mode;
833   gdouble x, y;
834   
835   hsv = GTK_HSV (widget);
836   priv = hsv->priv;
837   
838   if (priv->mode == DRAG_NONE || event->button != 1)
839     return FALSE;
840   
841   /* Set the drag mode to DRAG_NONE so that signal handlers for "catched"
842    * can see that this is the final color state.
843    */
844   
845   mode = priv->mode;
846   priv->mode = DRAG_NONE;
847   
848   x = event->x;
849   y = event->y;
850   
851   if (mode == DRAG_H)
852     gtk_hsv_set_color (hsv, compute_v (hsv, x, y), priv->s, priv->v);
853   else if (mode == DRAG_SV) {
854     double s, v;
855     
856     compute_sv (hsv, x, y, &s, &v);
857     gtk_hsv_set_color (hsv, priv->h, s, v);
858   } else
859     g_assert_not_reached ();
860   
861   gdk_display_pointer_ungrab (gdk_drawable_get_display (event->window),
862                               event->time);
863   return TRUE;
864 }
865
866 /* Motion_notify_event handler for the HSV color selector */
867 static gint
868 gtk_hsv_motion (GtkWidget      *widget,
869                 GdkEventMotion *event)
870 {
871   GtkHSV *hsv;
872   HSVPrivate *priv;
873   double x, y;
874   gint ix, iy;
875   GdkModifierType mods;
876   
877   hsv = GTK_HSV (widget);
878   priv = hsv->priv;
879   
880   if (priv->mode == DRAG_NONE)
881     return FALSE;
882   
883   if (event->is_hint)
884     {
885       gdk_window_get_pointer (priv->window, &ix, &iy, &mods);
886       x = ix;
887       y = iy;
888     }
889   else
890     {
891       x = event->x;
892       y = event->y;
893     }
894   
895   if (priv->mode == DRAG_H)
896     {
897       gtk_hsv_set_color (hsv, compute_v (hsv, x, y), priv->s, priv->v);
898       return TRUE;
899     }
900   else if (priv->mode == DRAG_SV)
901     {
902       double s, v;
903       
904       compute_sv (hsv, x, y, &s, &v);
905       gtk_hsv_set_color (hsv, priv->h, s, v);
906       return TRUE;
907     }
908   
909   g_assert_not_reached ();
910   return FALSE;
911 }
912
913
914 /* Redrawing */
915
916 /* Paints the hue ring */
917 static void
918 paint_ring (GtkHSV      *hsv,
919             GdkDrawable *drawable,
920             gint         x,
921             gint         y,
922             gint         width,
923             gint         height)
924 {
925   HSVPrivate *priv;
926   int xx, yy;
927   gdouble dx, dy, dist;
928   gdouble center;
929   gdouble inner, outer;
930   guchar *buf, *p;
931   gdouble angle;
932   gdouble hue;
933   gdouble r, g, b;
934   GdkBitmap *mask;
935   GdkGC *gc;
936   GdkColor color;
937   
938   priv = hsv->priv;
939   
940   center = priv->size / 2.0;
941   
942   outer = priv->size / 2.0;
943   inner = outer - priv->ring_width;
944   
945   /* Paint the ring */
946   
947   buf = g_new (guchar, width * height * 3);
948   
949   for (yy = 0; yy < height; yy++)
950     {
951       p = buf + yy * width * 3;
952       
953       dy = -(yy + y - center);
954       
955       for (xx = 0; xx < width; xx++)
956         {
957           dx = xx + x - center;
958           
959           dist = dx * dx + dy * dy;
960           if (dist < ((inner-1) * (inner-1)) || dist > ((outer+1) * (outer+1)))
961             {
962               *p++ = 0;
963               *p++ = 0;
964               *p++ = 0;
965               continue;
966             }
967           
968           angle = atan2 (dy, dx);
969           if (angle < 0.0)
970             angle += 2.0 * G_PI;
971           
972           hue = angle / (2.0 * G_PI);
973           
974           r = hue;
975           g = 1.0;
976           b = 1.0;
977           hsv_to_rgb (&r, &g, &b);
978           
979           *p++ = floor (r * 255 + 0.5);
980           *p++ = floor (g * 255 + 0.5);
981           *p++ = floor (b * 255 + 0.5);
982         }
983     }
984   
985   /* Create clipping mask */
986   
987   mask = gdk_pixmap_new (GTK_WIDGET (hsv)->window, width, height, 1);
988
989   gc = gdk_gc_new (mask);
990   
991   color.pixel = 0;
992   gdk_gc_set_foreground (gc, &color);
993   gdk_draw_rectangle (mask, gc, TRUE,
994                       0, 0, width, height);
995   
996   
997   color.pixel = 1;
998   gdk_gc_set_foreground (gc, &color);
999   gdk_draw_arc (mask, gc, TRUE,
1000                 -x, -y,
1001                 priv->size - 1, priv->size - 1,
1002                 0, 360 * 64);
1003   
1004   color.pixel = 0;
1005   gdk_gc_set_foreground (gc, &color);
1006   gdk_draw_arc (mask, gc, TRUE,
1007                 -x + priv->ring_width - 1, -y + priv->ring_width - 1,
1008                 priv->size - 2 * priv->ring_width + 1, priv->size - 2 * priv->ring_width + 1,
1009                 0, 360 * 64);
1010   
1011   g_object_unref (gc);
1012   
1013   gdk_gc_set_clip_mask (priv->gc, mask);
1014   gdk_gc_set_clip_origin (priv->gc, 0, 0);
1015   
1016   /* Draw ring */
1017   
1018   gdk_draw_rgb_image_dithalign (drawable, priv->gc, 0, 0, width, height,
1019                                 GDK_RGB_DITHER_MAX,
1020                                 buf,
1021                                 width * 3,
1022                                 x, y);
1023   
1024   /* Draw value marker */
1025   
1026   r = priv->h;
1027   g = 1.0;
1028   b = 1.0;
1029   hsv_to_rgb (&r, &g, &b);
1030   
1031   if (INTENSITY (r, g, b) > 0.5)
1032     gdk_rgb_gc_set_foreground (priv->gc, 0x000000);
1033   else
1034     gdk_rgb_gc_set_foreground (priv->gc, 0xffffff);
1035   
1036   gdk_draw_line (drawable, priv->gc,
1037                  -x + center, -y + center,
1038                  -x + center + cos (priv->h * 2.0 * G_PI) * center,
1039                  -y + center - sin (priv->h * 2.0 * G_PI) * center);
1040   
1041   gdk_gc_set_clip_mask (priv->gc, NULL);
1042   g_object_unref (mask);
1043   
1044   g_free (buf);
1045   
1046   /* Draw ring outline */
1047
1048   if (GTK_WIDGET_HAS_FOCUS (hsv) &&
1049       priv->focus_on_ring)
1050     {
1051       gint focus_width;
1052       gint focus_halfwidth;
1053       GdkGC *gc = gtk_hsv_get_focus_gc (hsv, &focus_width);
1054       focus_halfwidth = (focus_width + 1) / 2;
1055       
1056       gdk_draw_arc (drawable, gc, FALSE,
1057                     -x + focus_width/2, -y + focus_width/2,
1058                     priv->size - focus_width, priv->size - focus_width,
1059                     0, 360 * 64);
1060       gdk_draw_arc (drawable, gc, FALSE,
1061                     -x + priv->ring_width - focus_halfwidth, -y + priv->ring_width - focus_halfwidth,
1062                     priv->size - 2 * priv->ring_width + focus_width,
1063                     priv->size - 2 * priv->ring_width + focus_width,
1064                     0, 360 * 64);
1065       
1066       g_object_unref (gc);
1067     }
1068 }
1069
1070 /* Converts an HSV triplet to an integer RGB triplet */
1071 static void
1072 get_color (gdouble h,
1073            gdouble s,
1074            gdouble v,
1075            gint   *r,
1076            gint   *g,
1077            gint   *b)
1078 {
1079   hsv_to_rgb (&h, &s, &v);
1080   
1081   *r = floor (h * 255 + 0.5);
1082   *g = floor (s * 255 + 0.5);
1083   *b = floor (v * 255 + 0.5);
1084 }
1085
1086 #define SWAP(a, b, t) ((t) = (a), (a) = (b), (b) = (t))
1087
1088 #define LERP(a, b, v1, v2, i) (((v2) - (v1) != 0)                                       \
1089                                ? ((a) + ((b) - (a)) * ((i) - (v1)) / ((v2) - (v1)))     \
1090                                : (a))
1091
1092 /* Paints the HSV triangle */
1093 static void
1094 paint_triangle (GtkHSV      *hsv,
1095                 GdkDrawable *drawable,
1096                 gint         x,
1097                 gint         y,
1098                 gint         width,
1099                 gint         height)
1100 {
1101   HSVPrivate *priv;
1102   gint hx, hy, sx, sy, vx, vy; /* HSV vertices */
1103   gint x1, y1, r1, g1, b1; /* First vertex in scanline order */
1104   gint x2, y2, r2, g2, b2; /* Second vertex */
1105   gint x3, y3, r3, g3, b3; /* Third vertex */
1106   gint t;
1107   guchar *buf, *p;
1108   gint xl, xr, rl, rr, gl, gr, bl, br; /* Scanline data */
1109   gint xx, yy;
1110   GdkBitmap *mask;
1111   GdkGC *gc;
1112   GdkColor color;
1113   GdkPoint points[3];
1114   gdouble r, g, b;
1115   
1116   priv = hsv->priv;
1117   
1118   /* Compute triangle's vertices */
1119   
1120   compute_triangle (hsv, &hx, &hy, &sx, &sy, &vx, &vy);
1121   
1122   x1 = hx;
1123   y1 = hy;
1124   get_color (priv->h, 1.0, 1.0, &r1, &g1, &b1);
1125   
1126   x2 = sx;
1127   y2 = sy;
1128   get_color (priv->h, 1.0, 0.0, &r2, &g2, &b2);
1129   
1130   x3 = vx;
1131   y3 = vy;
1132   get_color (priv->h, 0.0, 1.0, &r3, &g3, &b3);
1133   
1134   if (y2 > y3)
1135     {
1136       SWAP (x2, x3, t);
1137       SWAP (y2, y3, t);
1138       SWAP (r2, r3, t);
1139       SWAP (g2, g3, t);
1140       SWAP (b2, b3, t);
1141     }
1142   
1143   if (y1 > y3)
1144     {
1145       SWAP (x1, x3, t);
1146       SWAP (y1, y3, t);
1147       SWAP (r1, r3, t);
1148       SWAP (g1, g3, t);
1149       SWAP (b1, b3, t);
1150     }
1151   
1152   if (y1 > y2)
1153     {
1154       SWAP (x1, x2, t);
1155       SWAP (y1, y2, t);
1156       SWAP (r1, r2, t);
1157       SWAP (g1, g2, t);
1158       SWAP (b1, b2, t);
1159     }
1160   
1161   /* Shade the triangle */
1162   
1163   buf = g_new (guchar, width * height * 3);
1164   
1165   for (yy = 0; yy < height; yy++)
1166     {
1167       p = buf + yy * width * 3;
1168       
1169       if (yy + y < y1 || yy + y > y3)
1170         for (xx = 0; xx < width; xx++)
1171           {
1172             *p++ = 0;
1173             *p++ = 0;
1174             *p++ = 0;
1175           }
1176       else {
1177         if (yy + y < y2)
1178           {
1179             xl = LERP (x1, x2, y1, y2, yy + y);
1180             
1181             rl = LERP (r1, r2, y1, y2, yy + y);
1182             gl = LERP (g1, g2, y1, y2, yy + y);
1183             bl = LERP (b1, b2, y1, y2, yy + y);
1184           }
1185         else
1186           {
1187             xl = LERP (x2, x3, y2, y3, yy + y);
1188             
1189             rl = LERP (r2, r3, y2, y3, yy + y);
1190             gl = LERP (g2, g3, y2, y3, yy + y);
1191             bl = LERP (b2, b3, y2, y3, yy + y);
1192           }
1193         
1194         xr = LERP (x1, x3, y1, y3, yy + y);
1195         
1196         rr = LERP (r1, r3, y1, y3, yy + y);
1197         gr = LERP (g1, g3, y1, y3, yy + y);
1198         br = LERP (b1, b3, y1, y3, yy + y);
1199         
1200         if (xl > xr)
1201           {
1202             SWAP (xl, xr, t);
1203             SWAP (rl, rr, t);
1204             SWAP (gl, gr, t);
1205             SWAP (bl, br, t);
1206           }
1207         
1208         for (xx = 0; xx < width; xx++)
1209           {
1210             if (xx + x < xl || xx + x > xr)
1211               {
1212                 *p++ = 0;
1213                 *p++ = 0;
1214                 *p++ = 0;
1215               }
1216             else
1217               {
1218                 *p++ = LERP (rl, rr, xl, xr, xx + x);
1219                 *p++ = LERP (gl, gr, xl, xr, xx + x);
1220                 *p++ = LERP (bl, br, xl, xr, xx + x);
1221               }
1222           }
1223       }
1224     }
1225   
1226   /* Create clipping mask */
1227   
1228   mask = gdk_pixmap_new (GTK_WIDGET (hsv)->window, width, height, 1);
1229
1230   gc = gdk_gc_new (mask);
1231   
1232   color.pixel = 0;
1233   gdk_gc_set_foreground (gc, &color);
1234   gdk_draw_rectangle (mask, gc, TRUE,
1235                       0, 0, width, height);
1236   
1237   color.pixel = 1;
1238   gdk_gc_set_foreground (gc, &color);
1239   
1240   points[0].x = x1 - x;
1241   points[0].y = y1 - y;
1242   points[1].x = x2 - x;
1243   points[1].y = y2 - y;
1244   points[2].x = x3 - x;
1245   points[2].y = y3 - y;
1246   gdk_draw_polygon (mask, gc, TRUE, points, 3);
1247   
1248   g_object_unref (gc);
1249   
1250   gdk_gc_set_clip_mask (priv->gc, mask);
1251   gdk_gc_set_clip_origin (priv->gc, 0, 0);
1252   
1253   /* Draw triangle */
1254   
1255   gdk_draw_rgb_image_dithalign (drawable, priv->gc, 0, 0, width, height,
1256                                 GDK_RGB_DITHER_MAX,
1257                                 buf,
1258                                 width * 3,
1259                                 x, y);
1260   
1261   gdk_gc_set_clip_mask (priv->gc, NULL);
1262   g_object_unref (mask);
1263   
1264   g_free (buf);
1265   
1266   /* Draw triangle focus outline */
1267
1268   if (GTK_WIDGET_HAS_FOCUS (hsv) &&
1269       !priv->focus_on_ring)
1270     {
1271       gint focus_width = 1;         
1272       GdkGC *gc = gtk_hsv_get_focus_gc (hsv, &focus_width);
1273   
1274       gdk_draw_polygon (drawable, gc, FALSE, points, 3);
1275       g_object_unref (gc);
1276     }
1277   
1278   /* Draw value marker */
1279   
1280   xx = floor (sx + (vx - sx) * priv->v + (hx - vx) * priv->s * priv->v + 0.5) - x;
1281   yy = floor (sy + (vy - sy) * priv->v + (hy - vy) * priv->s * priv->v + 0.5) - y;
1282   
1283   r = priv->h;
1284   g = priv->s;
1285   b = priv->v;
1286   hsv_to_rgb (&r, &g, &b);
1287   
1288   if (INTENSITY (r, g, b) > 0.5)
1289     gdk_rgb_gc_set_foreground (priv->gc, 0x000000);
1290   else
1291     gdk_rgb_gc_set_foreground (priv->gc, 0xffffff);
1292
1293 #define OUTER_RADIUS 4
1294 #define INNER_RADIUS 3 
1295   
1296   gdk_draw_arc (drawable, priv->gc, FALSE,
1297                 xx - OUTER_RADIUS, yy - OUTER_RADIUS,
1298                 OUTER_RADIUS * 2, OUTER_RADIUS * 2,
1299                 0, 360 * 64);
1300   gdk_draw_arc (drawable, priv->gc, FALSE,
1301                 xx - INNER_RADIUS, yy - INNER_RADIUS,
1302                 INNER_RADIUS * 2, INNER_RADIUS * 2,
1303                 0, 360 * 64);
1304 }
1305
1306 /* Paints the contents of the HSV color selector */
1307 static void
1308 paint (GtkHSV      *hsv,
1309        GdkDrawable *drawable,
1310        gint         x,
1311        gint         y,
1312        gint         width,
1313        gint         height)
1314 {
1315   paint_ring (hsv, drawable, x, y, width, height);
1316   paint_triangle (hsv, drawable, x, y, width, height);
1317 }
1318
1319 /* Expose_event handler for the HSV color selector */
1320 static gint
1321 gtk_hsv_expose (GtkWidget      *widget,
1322                 GdkEventExpose *event)
1323 {
1324   GtkHSV *hsv;
1325   HSVPrivate *priv;
1326   GdkRectangle rect, dest;
1327   GdkPixmap *pixmap;
1328   
1329   hsv = GTK_HSV (widget);
1330   priv = hsv->priv;
1331   
1332   if (!(GTK_WIDGET_DRAWABLE (widget) && event->window == widget->window))
1333     return FALSE;
1334   
1335   rect.x = widget->allocation.x;
1336   rect.y = widget->allocation.y;
1337   rect.width = widget->allocation.width;
1338   rect.height = widget->allocation.height;
1339   
1340   if (!gdk_rectangle_intersect (&event->area, &rect, &dest))
1341     return FALSE;
1342   
1343   pixmap = gdk_pixmap_new (widget->window, dest.width, dest.height,
1344                            gtk_widget_get_visual (widget)->depth);
1345   
1346   rect = dest;
1347   rect.x = 0;
1348   rect.y = 0;
1349   
1350   gdk_draw_rectangle (pixmap,
1351                       widget->style->bg_gc[GTK_WIDGET_STATE (widget)],
1352                       TRUE,
1353                       0, 0, dest.width, dest.height);
1354   paint (hsv, pixmap,
1355          dest.x - widget->allocation.x, dest.y - widget->allocation.y,
1356          dest.width, dest.height);
1357   
1358   gdk_draw_drawable (widget->window,
1359                      priv->gc,
1360                      pixmap,
1361                      0, 0,
1362                      dest.x,
1363                      dest.y,
1364                      event->area.width, event->area.height);
1365   
1366   g_object_unref (pixmap);
1367   
1368   return FALSE;
1369 }
1370
1371 static gboolean
1372 gtk_hsv_focus (GtkWidget       *widget,
1373                GtkDirectionType dir)
1374 {
1375   GtkHSV *hsv;
1376   HSVPrivate *priv;
1377
1378   hsv = GTK_HSV (widget);
1379   priv = hsv->priv;
1380
1381   if (!GTK_WIDGET_HAS_FOCUS (hsv))
1382     {
1383       if (dir == GTK_DIR_TAB_BACKWARD)
1384         priv->focus_on_ring = FALSE;
1385       else
1386         priv->focus_on_ring = TRUE;
1387
1388       gtk_widget_grab_focus (GTK_WIDGET (hsv));
1389       return TRUE;
1390     }
1391   
1392   switch (dir)
1393     {
1394     case GTK_DIR_UP:
1395       if (priv->focus_on_ring)
1396         return FALSE;
1397       else
1398         priv->focus_on_ring = TRUE;
1399       break;
1400
1401     case GTK_DIR_DOWN:
1402       if (priv->focus_on_ring)
1403         priv->focus_on_ring = FALSE;
1404       else
1405         return FALSE;
1406       break;
1407
1408     case GTK_DIR_LEFT:
1409     case GTK_DIR_TAB_BACKWARD:
1410       if (priv->focus_on_ring)
1411         return FALSE;
1412       else
1413         priv->focus_on_ring = TRUE;
1414       break;
1415
1416     case GTK_DIR_RIGHT:
1417     case GTK_DIR_TAB_FORWARD:
1418       if (priv->focus_on_ring)
1419         priv->focus_on_ring = FALSE;
1420       else
1421         return FALSE;
1422       break;
1423     }
1424
1425   gtk_widget_queue_draw (GTK_WIDGET (hsv));
1426
1427   return TRUE;
1428 }
1429
1430 /**
1431  * gtk_hsv_new:
1432  * @void:
1433  *
1434  * Creates a new HSV color selector.
1435  *
1436  * Return value: A newly-created HSV color selector.
1437  **/
1438 GtkWidget*
1439 gtk_hsv_new (void)
1440 {
1441   return g_object_new (GTK_TYPE_HSV, NULL);
1442 }
1443
1444 /**
1445  * gtk_hsv_set_color:
1446  * @hsv: An HSV color selector.
1447  * @h: Hue.
1448  * @s: Saturation.
1449  * @v: Value.
1450  *
1451  * Sets the current color in an HSV color selector.  Color component values must
1452  * be in the [0.0, 1.0] range.
1453  **/
1454 void
1455 gtk_hsv_set_color (GtkHSV *hsv,
1456                    gdouble h,
1457                    gdouble s,
1458                    gdouble v)
1459 {
1460   HSVPrivate *priv;
1461   
1462   g_return_if_fail (hsv != NULL);
1463   g_return_if_fail (GTK_IS_HSV (hsv));
1464   g_return_if_fail (h >= 0.0 && h <= 1.0);
1465   g_return_if_fail (s >= 0.0 && s <= 1.0);
1466   g_return_if_fail (v >= 0.0 && v <= 1.0);
1467   
1468   priv = hsv->priv;
1469   
1470   priv->h = h;
1471   priv->s = s;
1472   priv->v = v;
1473   
1474   g_signal_emit (hsv, hsv_signals[CHANGED], 0);
1475   
1476   gtk_widget_queue_draw (GTK_WIDGET (hsv));
1477 }
1478
1479 /**
1480  * gtk_hsv_get_color:
1481  * @hsv: An HSV color selector.
1482  * @h: Return value for the hue.
1483  * @s: Return value for the saturation.
1484  * @v: Return value for the value.
1485  *
1486  * Queries the current color in an HSV color selector.  Returned values will be
1487  * in the [0.0, 1.0] range.
1488  **/
1489 void
1490 gtk_hsv_get_color (GtkHSV *hsv, double *h, double *s, double *v)
1491 {
1492   HSVPrivate *priv;
1493   
1494   g_return_if_fail (GTK_IS_HSV (hsv));
1495   
1496   priv = hsv->priv;
1497   
1498   if (h)
1499     *h = priv->h;
1500   
1501   if (s)
1502     *s = priv->s;
1503   
1504   if (v)
1505     *v = priv->v;
1506 }
1507
1508 /**
1509  * gtk_hsv_set_metrics:
1510  * @hsv: An HSV color selector.
1511  * @size: Diameter for the hue ring.
1512  * @ring_width: Width of the hue ring.
1513  *
1514  * Sets the size and ring width of an HSV color selector.
1515  **/
1516 void
1517 gtk_hsv_set_metrics (GtkHSV *hsv,
1518                      gint    size,
1519                      gint    ring_width)
1520 {
1521   HSVPrivate *priv;
1522   int same_size;
1523   
1524   g_return_if_fail (GTK_IS_HSV (hsv));
1525   g_return_if_fail (size > 0);
1526   g_return_if_fail (ring_width > 0);
1527   g_return_if_fail (2 * ring_width + 1 <= size);
1528   
1529   priv = hsv->priv;
1530   
1531   same_size = (priv->size == size);
1532   
1533   priv->size = size;
1534   priv->ring_width = ring_width;
1535   
1536   if (same_size)
1537     gtk_widget_queue_draw (GTK_WIDGET (hsv));
1538   else
1539     gtk_widget_queue_resize (GTK_WIDGET (hsv));
1540 }
1541
1542 /**
1543  * gtk_hsv_get_metrics:
1544  * @hsv: An HSV color selector.
1545  * @size: Return value for the diameter of the hue ring.
1546  * @ring_width: Return value for the width of the hue ring.
1547  *
1548  * Queries the size and ring width of an HSV color selector.
1549  **/
1550 void
1551 gtk_hsv_get_metrics (GtkHSV *hsv,
1552                      gint   *size,
1553                      gint   *ring_width)
1554 {
1555   HSVPrivate *priv;
1556   
1557   g_return_if_fail (GTK_IS_HSV (hsv));
1558   
1559   priv = hsv->priv;
1560   
1561   if (size)
1562     *size = priv->size;
1563   
1564   if (ring_width)
1565     *ring_width = priv->ring_width;
1566 }
1567
1568 /**
1569  * gtk_hsv_is_adjusting:
1570  * @hsv:
1571  *
1572  * An HSV color selector can be said to be adjusting if multiple rapid changes
1573  * are being made to its value, for example, when the user is adjusting the
1574  * value with the mouse.  This function queries whether the HSV color selector
1575  * is being adjusted or not.
1576  *
1577  * Return value: TRUE if clients can ignore changes to the color value, since
1578  * they may be transitory, or FALSE if they should consider the color value
1579  * status to be final.
1580  **/
1581 gboolean
1582 gtk_hsv_is_adjusting (GtkHSV *hsv)
1583 {
1584   HSVPrivate *priv;
1585   
1586   g_return_val_if_fail (GTK_IS_HSV (hsv), FALSE);
1587   
1588   priv = hsv->priv;
1589
1590   return priv->mode != DRAG_NONE;
1591 }
1592
1593 /**
1594  * gtk_hsv_to_rgb:
1595  * @h: Hue.
1596  * @s: Saturation.
1597  * @v: Value.
1598  * @r: Return value for the red component.
1599  * @g: Return value for the green component.
1600  * @b: Return value for the blue component.
1601  * 
1602  * Converts a color from HSV space to RGB.  Input values must be in the
1603  * [0.0, 1.0] range; output values will be in the same range.
1604  **/
1605 void
1606 gtk_hsv_to_rgb (gdouble  h,
1607                 gdouble  s,
1608                 gdouble  v,
1609                 gdouble *r,
1610                 gdouble *g,
1611                 gdouble *b)
1612 {
1613   g_return_if_fail (h >= 0.0 && h <= 1.0);
1614   g_return_if_fail (s >= 0.0 && s <= 1.0);
1615   g_return_if_fail (v >= 0.0 && v <= 1.0);
1616   
1617   hsv_to_rgb (&h, &s, &v);
1618   
1619   if (r)
1620     *r = h;
1621   
1622   if (g)
1623     *g = s;
1624   
1625   if (b)
1626     *b = v;
1627 }
1628
1629 /**
1630  * gtk_hsv_to_rgb:
1631  * @r: Red.
1632  * @g: Green.
1633  * @b: Blue.
1634  * @h: Return value for the hue component.
1635  * @s: Return value for the saturation component.
1636  * @v: Return value for the value component.
1637  * 
1638  * Converts a color from RGB space to HSV.  Input values must be in the
1639  * [0.0, 1.0] range; output values will be in the same range.
1640  **/
1641 void
1642 gtk_rgb_to_hsv (gdouble  r,
1643                 gdouble  g,
1644                 gdouble  b,
1645                 gdouble *h,
1646                 gdouble *s,
1647                 gdouble *v)
1648 {
1649   g_return_if_fail (r >= 0.0 && r <= 1.0);
1650   g_return_if_fail (g >= 0.0 && g <= 1.0);
1651   g_return_if_fail (b >= 0.0 && b <= 1.0);
1652   
1653   rgb_to_hsv (&r, &g, &b);
1654   
1655   if (h)
1656     *h = r;
1657   
1658   if (s)
1659     *s = g;
1660   
1661   if (v)
1662     *v = b;
1663 }
1664
1665 static void
1666 gtk_hsv_move (GtkHSV          *hsv,
1667               GtkDirectionType dir)
1668 {
1669   HSVPrivate *priv;
1670   gdouble hue, sat, val;
1671   gint hx, hy, sx, sy, vx, vy; /* HSV vertices */
1672   gint x, y; /* position in triangle */
1673   
1674   priv = hsv->priv;
1675
1676   hue = priv->h;
1677   sat = priv->s;
1678   val = priv->v;
1679
1680   compute_triangle (hsv, &hx, &hy, &sx, &sy, &vx, &vy);
1681
1682   x = floor (sx + (vx - sx) * priv->v + (hx - vx) * priv->s * priv->v + 0.5);
1683   y = floor (sy + (vy - sy) * priv->v + (hy - vy) * priv->s * priv->v + 0.5);
1684
1685 #define HUE_DELTA 0.002
1686   switch (dir)
1687     {
1688     case GTK_DIR_UP:
1689       if (priv->focus_on_ring)
1690         hue += HUE_DELTA;
1691       else
1692         {
1693           y -= 1;
1694           compute_sv (hsv, x, y, &sat, &val);
1695         }
1696       break;
1697
1698     case GTK_DIR_DOWN:
1699       if (priv->focus_on_ring)
1700         hue -= HUE_DELTA;
1701       else
1702         {
1703           y += 1;
1704           compute_sv (hsv, x, y, &sat, &val);
1705         }
1706       break;
1707
1708     case GTK_DIR_LEFT:
1709       if (priv->focus_on_ring)
1710         hue += HUE_DELTA;
1711       else
1712         {
1713           x -= 1;
1714           compute_sv (hsv, x, y, &sat, &val);
1715         }
1716       break;
1717
1718     case GTK_DIR_RIGHT:
1719       if (priv->focus_on_ring)
1720         hue -= HUE_DELTA
1721           ;
1722       else
1723         {
1724           x += 1;
1725           compute_sv (hsv, x, y, &sat, &val);
1726         }
1727       break;
1728
1729     default:
1730       /* we don't care about the tab directions */
1731       break;
1732     }
1733
1734   /* Wrap */
1735   if (hue < 0.0)
1736     hue = 1.0;
1737   else if (hue > 1.0)
1738     hue = 0.0;
1739   
1740   gtk_hsv_set_color (hsv, hue, sat, val);
1741 }
1742
1743 static GdkGC *
1744 gtk_hsv_get_focus_gc (GtkHSV *hsv,
1745                       gint   *line_width)
1746 {
1747   GdkGC *focus_gc;
1748   GtkWidget *widget = GTK_WIDGET (hsv);
1749   gint8 *dash_list;
1750   
1751   focus_gc = gdk_gc_new (widget->window);
1752   gdk_gc_copy (focus_gc, widget->style->fg_gc[GTK_WIDGET_STATE (widget)]);
1753
1754   gtk_widget_style_get (widget,
1755                         "focus-line-width", line_width,
1756                         "focus-line-pattern", (gchar *)&dash_list,
1757                         NULL);
1758   
1759   gdk_gc_set_line_attributes (focus_gc, *line_width,
1760                               dash_list[0] ? GDK_LINE_ON_OFF_DASH : GDK_LINE_SOLID,
1761                               GDK_CAP_BUTT, GDK_JOIN_MITER);
1762   if (dash_list[0])
1763     gdk_gc_set_dashes (focus_gc, 0, dash_list, strlen (dash_list));
1764
1765   g_free (dash_list);
1766   
1767   return focus_gc;
1768 }