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