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