gimpcolorwheel.c revision 80501083d3ebf938d267a3a476df20651637b578
/* HSV color selector for GTK+
*
* Copyright (C) 1999 The Free Software Foundation
*
* Authors: Simon Budig <Simon.Budig@unix-ag.org> (original code)
* Federico Mena-Quintero <federico@gimp.org> (cleanup for GTK+)
* Jonathan Blandford <jrb@redhat.com> (cleanup for GTK+)
* Michael Natterer <mitch@gimp.org> (ported back to GIMP)
*
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/*
* Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
* file for a list of people on the GTK+ Team. See the ChangeLog
* files for a list of changes. These files are distributed with
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
*/
/*
* This widget was adopted by Inkscape by Alex Valavanis <valavanisalex@gmail.com>
* on 2013-01-08. Last merges with GIMP code were applied using the following
* commits from the GIMP git repository at
*
* Gtk+ 2 code merge: commit 632c5 (2013-01-06)
* Gtk+ 3 code merge: commit bcfc6, gtk3-port branch (2013-01-06)
*/
#include "config.h"
#include <gdk/gdkkeysyms.h>
#include "gimpcolorwheel.h"
#include <math.h>
/* Default ring fraction */
#define DEFAULT_FRACTION 0.1
#define DEFAULT_SIZE 100
/* Default ring width */
#define DEFAULT_RING_WIDTH 10
/* Dragging modes */
typedef enum
{
} DragMode;
/* Private part of the GimpColorWheel structure */
typedef struct
{
/* Color value */
gdouble h;
gdouble s;
gdouble v;
/* ring_width is this fraction of size */
/* Size and ring width */
/* Window for capturing events */
/* Dragging mode */
enum
{
MOVE,
};
#if GTK_CHECK_VERSION(3,0,0)
#else
#endif
static void
{
#if GTK_CHECK_VERSION(3,0,0)
#else
#endif
g_signal_new ("changed",
G_TYPE_NONE, 0);
g_signal_new ("move",
G_TYPE_NONE, 1,
"move", 1,
"move", 1,
"move", 1,
"move", 1,
"move", 1,
"move", 1,
"move", 1,
"move", 1,
}
static void
{
}
static void
{
}
static void
{
}
static void
{
attr.x = allocation.x;
attr.y = allocation.y;
}
static void
{
}
#if GTK_CHECK_VERSION(3,0,0)
static void
{
"focus-line-width", &focus_width,
"focus-padding", &focus_pad,
NULL);
}
static void
{
"focus-line-width", &focus_width,
"focus-padding", &focus_pad,
NULL);
}
#else
static void
{
"focus-line-width", &focus_width,
"focus-padding", &focus_pad,
NULL);
}
#endif
static void
{
"focus-line-width", &focus_width,
"focus-padding", &focus_pad,
NULL);
if (gtk_widget_get_realized (widget))
allocation->x,
allocation->y,
allocation->height);
}
/* Utility functions */
/* Converts from HSV to RGB */
static void
hsv_to_rgb (gdouble *h,
gdouble *s,
gdouble *v)
{
gdouble f, p, q, t;
if (*s == 0.0)
{
*h = *v;
*s = *v;
*v = *v; /* heh */
}
else
{
hue = *h * 6.0;
saturation = *s;
value = *v;
if (hue == 6.0)
hue = 0.0;
switch ((int) hue)
{
case 0:
*h = value;
*s = t;
*v = p;
break;
case 1:
*h = q;
*s = value;
*v = p;
break;
case 2:
*h = p;
*s = value;
*v = t;
break;
case 3:
*h = p;
*s = q;
*v = value;
break;
case 4:
*h = t;
*s = p;
*v = value;
break;
case 5:
*h = value;
*s = p;
*v = q;
break;
default:
}
}
}
/* Computes the vertices of the saturation/value triangle */
static void
{
}
/* Computes whether a point is inside the hue ring */
static gboolean
gdouble x,
gdouble y)
{
}
/* Computes a saturation/value pair based on the mouse coordinates */
static void
gdouble x,
gdouble y,
gdouble *s,
gdouble *v)
{
x -= center_x;
y = center_y - y;
{
*s = 1.0;
if (*v < 0.0)
*v = 0.0;
else if (*v > 1.0)
*v = 1.0;
}
{
*s = 0.0;
if (*v < 0.0)
*v = 0.0;
else if (*v > 1.0)
*v = 1.0;
}
{
*v = 1.0;
if (*s < 0.0)
*s = 0.0;
else if (*s > 1.0)
*s = 1.0;
}
else
{
if (*v<= 0.0)
{
*v = 0.0;
*s = 0.0;
}
else
{
if (*v > 1.0)
*v = 1.0;
else
if (*s < 0.0)
*s = 0.0;
else if (*s > 1.0)
*s = 1.0;
}
}
}
/* Computes whether a point is inside the saturation/value triangle */
static gboolean
gdouble x,
gdouble y)
{
return (s >= 0.0 && v >= 0.0 && s + v <= 1.0);
}
/* Computes a value based on the mouse coordinates */
static double
gdouble x,
gdouble y)
{
if (angle < 0.0)
}
static void
{
cursor =
#if GTK_CHECK_VERSION(3,0,0)
#else
#endif
}
{
(void)event;
return TRUE;
}
static gboolean
{
gdouble x, y;
return FALSE;
x = event->x;
y = event->y;
if (is_in_ring (wheel, x, y))
{
priv->s,
priv->v);
return TRUE;
}
if (is_in_triangle (wheel, x, y))
{
gdouble s, v;
compute_sv (wheel, x, y, &s, &v);
return TRUE;
}
return FALSE;
}
static gboolean
{
gdouble x, y;
return FALSE;
/* Set the drag mode to DRAG_NONE so that signal handlers for "catched"
* can see that this is the final color state.
*/
x = event->x;
y = event->y;
{
}
{
gdouble s, v;
compute_sv (wheel, x, y, &s, &v);
}
else
#if GTK_CHECK_VERSION(3,0,0)
#else
#endif
return TRUE;
}
static gboolean
{
gdouble x, y;
return FALSE;
x = event->x;
y = event->y;
{
return TRUE;
}
{
gdouble s, v;
compute_sv (wheel, x, y, &s, &v);
return TRUE;
}
return FALSE;
}
/* Redrawing */
/* Paints the hue ring */
static void
{
#if GTK_CHECK_VERSION(3,0,0)
#else
#endif
gdouble r, g, b;
#if GTK_CHECK_VERSION(3,0,0)
#else
#endif
/* Create an image initialized with the ring colors */
{
{
{
*p++ = 0;
continue;
}
if (angle < 0.0)
r = hue;
g = 1.0;
b = 1.0;
hsv_to_rgb (&r, &g, &b);
}
}
/* Now draw the value marker onto the source image, so that it
* will get properly clipped at the edges of the ring
*/
r = priv->h;
g = 1.0;
b = 1.0;
hsv_to_rgb (&r, &g, &b);
if (INTENSITY (r, g, b) > 0.5)
else
/* Draw the ring using the source image */
cairo_save (cr);
cairo_new_path (cr);
0, 2 * G_PI);
cairo_stroke (cr);
cairo_restore (cr);
}
/* Converts an HSV triplet to an integer RGB triplet */
static void
gdouble s,
gdouble v,
gint *r,
gint *g,
gint *b)
{
hsv_to_rgb (&h, &s, &v);
}
#define SWAP(a, b, t) ((t) = (a), (a) = (b), (b) = (t))
: (a))
/* Number of pixels we extend out from the edges when creating
* color source to avoid artifacts
*/
#define PAD 3
/* Paints the HSV triangle */
static void
{
gint t;
gdouble r, g, b;
#if GTK_CHECK_VERSION(3,0,0)
#else
#endif
/* Compute triangle's vertices */
{
}
{
}
{
}
/* Shade the triangle */
{
{
{
}
else
{
}
{
}
*p++ = c;
{
}
*p++ = c;
}
}
/* Draw a triangle with the image as a source */
cairo_fill (cr);
/* Draw value marker */
r = priv->h;
g = priv->s;
b = priv->v;
hsv_to_rgb (&r, &g, &b);
#if GTK_CHECK_VERSION(3,0,0)
#endif
if (INTENSITY (r, g, b) > 0.5)
{
#if GTK_CHECK_VERSION(3,0,0)
#else
detail = "colorwheel_light";
#endif
}
else
{
#if GTK_CHECK_VERSION(3,0,0)
#else
detail = "colorwheel_dark";
#endif
}
#define RADIUS 4
#define FOCUS_RADIUS 6
cairo_new_path (cr);
cairo_stroke (cr);
/* Draw focus outline */
{
"focus-line-width", &focus_width,
"focus-padding", &focus_pad,
NULL);
#if GTK_CHECK_VERSION(3,0,0)
#else
#endif
}
#if GTK_CHECK_VERSION(3,0,0)
#endif
}
static gboolean
{
{
}
return FALSE;
}
#else
static gint
{
return FALSE;
cairo_clip (cr);
cairo_destroy (cr);
allocation.x,
allocation.y,
return FALSE;
}
#endif
static gboolean
{
if (!gtk_widget_has_focus (widget))
{
if (dir == GTK_DIR_TAB_BACKWARD)
else
return TRUE;
}
switch (dir)
{
case GTK_DIR_UP:
if (priv->focus_on_ring)
return FALSE;
else
break;
case GTK_DIR_DOWN:
if (priv->focus_on_ring)
else
return FALSE;
break;
case GTK_DIR_LEFT:
case GTK_DIR_TAB_BACKWARD:
if (priv->focus_on_ring)
return FALSE;
else
break;
case GTK_DIR_RIGHT:
case GTK_DIR_TAB_FORWARD:
if (priv->focus_on_ring)
else
return FALSE;
break;
}
return TRUE;
}
/**
* gimp_color_wheel_new:
*
* Creates a new HSV color selector.
*
* Return value: A newly-created HSV color selector.
*
* Since: 2.14
*/
gimp_color_wheel_new (void)
{
}
/**
* gimp_color_wheel_set_color:
* @hsv: An HSV color selector
* @h: Hue
* @s: Saturation
* @v: Value
*
* Sets the current color in an HSV color selector.
* Color component values must be in the [0.0, 1.0] range.
*
* Since: 2.14
*/
void
gdouble h,
gdouble s,
gdouble v)
{
if(h == 0.0 && s == 0.0) {
h = priv->h;
}
priv->h = h;
priv->s = s;
priv->v = v;
}
/**
* gimp_color_wheel_get_color:
* @hsv: An HSV color selector
* @h: (out): Return value for the hue
* @s: (out): Return value for the saturation
* @v: (out): Return value for the value
*
* Queries the current color in an HSV color selector.
* Returned values will be in the [0.0, 1.0] range.
*
* Since: 2.14
*/
void
gdouble *h,
gdouble *s,
gdouble *v)
{
if (h) *h = priv->h;
if (s) *s = priv->s;
if (v) *v = priv->v;
}
/**
* gimp_color_wheel_set_ring_fraction:
* @ring: A wheel color selector
* @fraction: Ring fraction
*
* Sets the ring fraction of a wheel color selector.
*
* Since: GIMP 2.10
*/
void
{
}
/**
* gimp_color_wheel_get_ring_fraction:
* @ring: A wheel color selector
*
* Returns value: The ring fraction of the wheel color selector.
*
* Since: GIMP 2.10
*/
{
return priv->ring_fraction;
}
/**
* gimp_color_wheel_is_adjusting:
* @hsv: A #GimpColorWheel
*
* An HSV color selector can be said to be adjusting if multiple rapid
* changes are being made to its value, for example, when the user is
* adjusting the value with the mouse. This function queries whether
* the HSV color selector is being adjusted or not.
*
* Return value: %TRUE if clients can ignore changes to the color value,
* since they may be transitory, or %FALSE if they should consider
* the color value status to be final.
*
* Since: 2.14
*/
{
}
static void
{
gint x, y; /* position in triangle */
#define HUE_DELTA 0.002
switch (dir)
{
case GTK_DIR_UP:
if (priv->focus_on_ring)
else
{
y -= 1;
}
break;
case GTK_DIR_DOWN:
if (priv->focus_on_ring)
else
{
y += 1;
}
break;
case GTK_DIR_LEFT:
if (priv->focus_on_ring)
else
{
x -= 1;
}
break;
case GTK_DIR_RIGHT:
if (priv->focus_on_ring)
;
else
{
x += 1;
}
break;
default:
/* we don't care about the tab directions */
break;
}
/* Wrap */
if (hue < 0.0)
hue = 1.0;
else if (hue > 1.0)
hue = 0.0;
}