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