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