2 // "$Id: Fl_Color_Chooser.cxx 7981 2010-12-08 23:53:04Z greg.ercolano $"
4 // Color chooser for the Fast Light Tool Kit (FLTK).
6 // Copyright 1998-2010 by Bill Spitzak and others.
8 // This library is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU Library General Public
10 // License as published by the Free Software Foundation; either
11 // version 2 of the License, or (at your option) any later version.
13 // This library is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 // Library General Public License for more details.
18 // You should have received a copy of the GNU Library General Public
19 // License along with this library; if not, write to the Free Software
20 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
23 // Please report all bugs and problems on the following page:
25 // http://www.fltk.org/str.php
29 #include <FL/Fl_Color_Chooser.H>
30 #include <FL/fl_draw.H>
34 // Besides being a useful object on it's own, the Fl_Color_Chooser was
35 // an attempt to make a complex composite object that could be easily
36 // imbedded into a user interface. If you wish to make complex objects
37 // of your own, be sure to read this code.
39 // The function fl_color_chooser() creates a window containing a color
40 // chooser and a few buttons and current-color indicators. It is an
41 // easier interface for simple programs that just need a color.
43 // The "hue box" can be a circle or rectilinear.
44 // You get a circle by defining this:
46 // And the "hue box" can auto-update when the value changes
47 // you get this by defining this:
48 #define UPDATE_HUE_BOX 1
51 This \e static method converts HSV colors to RGB colorspace.
52 \param[in] H, S, V color components
53 \param[out] R, G, B color components
55 void Fl_Color_Chooser::hsv2rgb(
56 double H
, double S
, double V
, double& R
, double& G
, double& B
) {
61 double f
= H
- (float)i
;
62 double p1
= V
*(1.0-S
);
63 double p2
= V
*(1.0-S
*f
);
64 double p3
= V
*(1.0-S
*(1.0-f
));
66 case 0: R
= V
; G
= p3
; B
= p1
; break;
67 case 1: R
= p2
; G
= V
; B
= p1
; break;
68 case 2: R
= p1
; G
= V
; B
= p3
; break;
69 case 3: R
= p1
; G
= p2
; B
= V
; break;
70 case 4: R
= p3
; G
= p1
; B
= V
; break;
71 case 5: R
= V
; G
= p1
; B
= p2
; break;
77 This \e static method converts RGB colors to HSV colorspace.
78 \param[in] R, G, B color components
79 \param[out] H, S, V color components
81 void Fl_Color_Chooser::rgb2hsv(
82 double R
, double G
, double B
, double& H
, double& S
, double& V
) {
83 double maxv
= R
> G
? R
: G
; if (B
> maxv
) maxv
= B
;
86 double minv
= R
< G
? R
: G
; if (B
< minv
) minv
= B
;
87 S
= 1.0 - double(minv
)/maxv
;
89 if (maxv
== R
) {H
= (G
-B
)/double(maxv
-minv
); if (H
<0) H
+= 6.0;}
90 else if (maxv
== G
) H
= 2.0+(B
-R
)/double(maxv
-minv
);
91 else H
= 4.0+(R
-G
)/double(maxv
-minv
);
96 /** Fl_Color_Chooser modes */
98 M_RGB
, /**< mode() of Fl_Color_Chooser showing RGB values */
99 M_BYTE
, /**< mode() of Fl_Color_Chooser showing byte values */
100 M_HEX
, /**< mode() of Fl_Color_Chooser showing hex values */
101 M_HSV
/**< mode() of Fl_Color_Chooser showing HSV values */
103 static Fl_Menu_Item mode_menu
[] = {
112 int Flcc_Value_Input::format(char* buf
) {
113 Fl_Color_Chooser
* c
= (Fl_Color_Chooser
*)parent();
114 if (c
->mode() == M_HEX
) return sprintf(buf
,"0x%02X", int(value()));
115 else return Fl_Valuator::format(buf
);
117 #endif // !FL_DOXYGEN
119 void Fl_Color_Chooser::set_valuators() {
122 rvalue
.range(0,1); rvalue
.step(1,1000); rvalue
.value(r_
);
123 gvalue
.range(0,1); gvalue
.step(1,1000); gvalue
.value(g_
);
124 bvalue
.range(0,1); bvalue
.step(1,1000); bvalue
.value(b_
);
126 case M_BYTE
: /* FALLTHROUGH */
128 rvalue
.range(0,255); rvalue
.step(1); rvalue
.value(int(255*r_
+.5));
129 gvalue
.range(0,255); gvalue
.step(1); gvalue
.value(int(255*g_
+.5));
130 bvalue
.range(0,255); bvalue
.step(1); bvalue
.value(int(255*b_
+.5));
133 rvalue
.range(0,6); rvalue
.step(1,1000); rvalue
.value(hue_
);
134 gvalue
.range(0,1); gvalue
.step(1,1000); gvalue
.value(saturation_
);
135 bvalue
.range(0,1); bvalue
.step(1,1000); bvalue
.value(value_
);
141 Sets the current rgb color values.
142 Does not do the callback. Does not clamp (but out of range values will
143 produce psychedelic effects in the hue selector).
144 \param[in] R, G, B color components.
145 \return 1 if a new rgb value was set, 0 if the rgb value was the previous one.
147 int Fl_Color_Chooser::rgb(double R
, double G
, double B
) {
148 if (R
== r_
&& G
== g_
&& B
== b_
) return 0;
149 r_
= R
; g_
= G
; b_
= B
;
151 double ps
= saturation_
;
153 rgb2hsv(R
,G
,B
,hue_
,saturation_
,value_
);
157 #ifdef UPDATE_HUE_BOX
158 huebox
.damage(FL_DAMAGE_SCROLL
);
160 valuebox
.damage(FL_DAMAGE_EXPOSE
);}
161 if (hue_
!= ph
|| saturation_
!= ps
) {
162 huebox
.damage(FL_DAMAGE_EXPOSE
);
163 valuebox
.damage(FL_DAMAGE_SCROLL
);
170 The passed values are clamped (or for hue, modulus 6 is used) to get
171 legal values. Does not do the callback.
172 \param[in] H, S, V color components.
173 \return 1 if a new hsv value was set, 0 if the hsv value was the previous one.
175 int Fl_Color_Chooser::hsv(double H
, double S
, double V
) {
176 H
= fmod(H
,6.0); if (H
< 0.0) H
+= 6.0;
177 if (S
< 0.0) S
= 0.0; else if (S
> 1.0) S
= 1.0;
178 if (V
< 0.0) V
= 0.0; else if (V
> 1.0) V
= 1.0;
179 if (H
== hue_
&& S
== saturation_
&& V
== value_
) return 0;
181 double ps
= saturation_
;
183 hue_
= H
; saturation_
= S
; value_
= V
;
185 #ifdef UPDATE_HUE_BOX
186 huebox
.damage(FL_DAMAGE_SCROLL
);
188 valuebox
.damage(FL_DAMAGE_EXPOSE
);}
189 if (hue_
!= ph
|| saturation_
!= ps
) {
190 huebox
.damage(FL_DAMAGE_EXPOSE
);
191 valuebox
.damage(FL_DAMAGE_SCROLL
);
193 hsv2rgb(H
,S
,V
,r_
,g_
,b_
);
199 ////////////////////////////////////////////////////////////////
201 static void tohs(double x
, double y
, double& h
, double& s
) {
205 s
= sqrt(x
*x
+y
*y
); if (s
> 1.0) s
= 1.0;
206 h
= (3.0/M_PI
)*atan2(y
,x
);
209 h
= fmod(6.0*x
,6.0); if (h
< 0.0) h
+= 6.0;
210 s
= 1.0-y
; if (s
< 0.0) s
= 0.0; else if (s
> 1.0) s
= 1.0;
215 int Flcc_HueBox::handle(int e
) {
216 static double ih
, is
;
217 Fl_Color_Chooser
* c
= (Fl_Color_Chooser
*)parent();
220 if (Fl::visible_focus()) {
225 is
= c
->saturation();
228 Xf
= (Fl::event_x()-x()-Fl::box_dx(box()))/double(w()-Fl::box_dw(box()));
229 Yf
= (Fl::event_y()-y()-Fl::box_dy(box()))/double(h()-Fl::box_dh(box()));
231 if (fabs(H
-ih
) < 3*6.0/w()) H
= ih
;
232 if (fabs(S
-is
) < 3*1.0/h()) S
= is
;
233 if (Fl::event_state(FL_CTRL
)) H
= ih
;
234 if (c
->hsv(H
, S
, c
->value())) c
->do_callback();
236 case FL_FOCUS
: /* FALLTHROUGH */
238 if (Fl::visible_focus()) {
244 return handle_key(Fl::event_key());
249 #endif // !FL_DOXYGEN
251 static void generate_image(void* vv
, int X
, int Y
, int W
, uchar
* buf
) {
252 Flcc_HueBox
* v
= (Flcc_HueBox
*)vv
;
253 int iw
= v
->w()-Fl::box_dw(v
->box());
254 double Yf
= double(Y
)/(v
->h()-Fl::box_dh(v
->box()));
255 #ifdef UPDATE_HUE_BOX
256 const double V
= ((Fl_Color_Chooser
*)(v
->parent()))->value();
258 const double V
= 1.0;
260 for (int x
= X
; x
< X
+W
; x
++) {
261 double Xf
= double(x
)/iw
;
262 double H
,S
; tohs(Xf
,Yf
,H
,S
);
264 Fl_Color_Chooser::hsv2rgb(H
,S
,V
,r
,g
,b
);
265 *buf
++ = uchar(255*r
+.5);
266 *buf
++ = uchar(255*g
+.5);
267 *buf
++ = uchar(255*b
+.5);
272 int Flcc_HueBox::handle_key(int key
) {
273 int w1
= w()-Fl::box_dw(box())-6;
274 int h1
= h()-Fl::box_dh(box())-6;
275 Fl_Color_Chooser
* c
= (Fl_Color_Chooser
*)parent();
278 int X
= int(.5*(cos(c
->hue()*(M_PI
/3.0))*c
->saturation()+1) * w1
);
279 int Y
= int(.5*(1-sin(c
->hue()*(M_PI
/3.0))*c
->saturation()) * h1
);
281 int X
= int(c
->hue()/6.0*w1
);
282 int Y
= int((1-c
->saturation())*h1
);
303 Xf
= (double)X
/(double)w1
;
304 Yf
= (double)Y
/(double)h1
;
306 if (c
->hsv(H
, S
, c
->value())) c
->do_callback();
310 #endif // !FL_DOXYGEN
313 void Flcc_HueBox::draw() {
314 if (damage()&FL_DAMAGE_ALL
) draw_box();
315 int x1
= x()+Fl::box_dx(box());
316 int yy1
= y()+Fl::box_dy(box());
317 int w1
= w()-Fl::box_dw(box());
318 int h1
= h()-Fl::box_dh(box());
319 if (damage() == FL_DAMAGE_EXPOSE
) fl_push_clip(x1
+px
,yy1
+py
,6,6);
320 fl_draw_image(generate_image
, this, x1
, yy1
, w1
, h1
);
321 if (damage() == FL_DAMAGE_EXPOSE
) fl_pop_clip();
322 Fl_Color_Chooser
* c
= (Fl_Color_Chooser
*)parent();
324 int X
= int(.5*(cos(c
->hue()*(M_PI
/3.0))*c
->saturation()+1) * (w1
-6));
325 int Y
= int(.5*(1-sin(c
->hue()*(M_PI
/3.0))*c
->saturation()) * (h1
-6));
327 int X
= int(c
->hue()/6.0*(w1
-6));
328 int Y
= int((1-c
->saturation())*(h1
-6));
330 if (X
< 0) X
= 0; else if (X
> w1
-6) X
= w1
-6;
331 if (Y
< 0) Y
= 0; else if (Y
> h1
-6) Y
= h1
-6;
332 // fl_color(c->value()>.75 ? FL_BLACK : FL_WHITE);
333 draw_box(FL_UP_BOX
,x1
+X
,yy1
+Y
,6,6,Fl::focus() == this ? FL_FOREGROUND_COLOR
: FL_GRAY
);
336 #endif // !FL_DOXYGEN
338 ////////////////////////////////////////////////////////////////
341 int Flcc_ValueBox::handle(int e
) {
343 Fl_Color_Chooser
* c
= (Fl_Color_Chooser
*)parent();
346 if (Fl::visible_focus()) {
353 Yf
= 1-(Fl::event_y()-y()-Fl::box_dy(box()))/double(h()-Fl::box_dh(box()));
354 if (fabs(Yf
-iv
)<(3*1.0/h())) Yf
= iv
;
355 if (c
->hsv(c
->hue(),c
->saturation(),Yf
)) c
->do_callback();
357 case FL_FOCUS
: /* FALLTHROUGH */
359 if (Fl::visible_focus()) {
365 return handle_key(Fl::event_key());
370 #endif // !FL_DOXYGEN
372 static double tr
, tg
, tb
;
373 static void generate_vimage(void* vv
, int X
, int Y
, int W
, uchar
* buf
) {
374 Flcc_ValueBox
* v
= (Flcc_ValueBox
*)vv
;
375 double Yf
= 255*(1.0-double(Y
)/(v
->h()-Fl::box_dh(v
->box())));
376 uchar r
= uchar(tr
*Yf
+.5);
377 uchar g
= uchar(tg
*Yf
+.5);
378 uchar b
= uchar(tb
*Yf
+.5);
379 for (int x
= X
; x
< X
+W
; x
++) {
380 *buf
++ = r
; *buf
++ = g
; *buf
++ = b
;
385 void Flcc_ValueBox::draw() {
386 if (damage()&FL_DAMAGE_ALL
) draw_box();
387 Fl_Color_Chooser
* c
= (Fl_Color_Chooser
*)parent();
388 c
->hsv2rgb(c
->hue(),c
->saturation(),1.0,tr
,tg
,tb
);
389 int x1
= x()+Fl::box_dx(box());
390 int yy1
= y()+Fl::box_dy(box());
391 int w1
= w()-Fl::box_dw(box());
392 int h1
= h()-Fl::box_dh(box());
393 if (damage() == FL_DAMAGE_EXPOSE
) fl_push_clip(x1
,yy1
+py
,w1
,6);
394 fl_draw_image(generate_vimage
, this, x1
, yy1
, w1
, h1
);
395 if (damage() == FL_DAMAGE_EXPOSE
) fl_pop_clip();
396 int Y
= int((1-c
->value()) * (h1
-6));
397 if (Y
< 0) Y
= 0; else if (Y
> h1
-6) Y
= h1
-6;
398 draw_box(FL_UP_BOX
,x1
,yy1
+Y
,w1
,6,Fl::focus() == this ? FL_FOREGROUND_COLOR
: FL_GRAY
);
401 #endif // !FL_DOXYGEN
404 int Flcc_ValueBox::handle_key(int key
) {
405 int h1
= h()-Fl::box_dh(box())-6;
406 Fl_Color_Chooser
* c
= (Fl_Color_Chooser
*)parent();
408 int Y
= int((1-c
->value()) * h1
);
409 if (Y
< 0) Y
= 0; else if (Y
> h1
) Y
= h1
;
423 Yf
= 1-((double)Y
/(double)h1
);
424 if (c
->hsv(c
->hue(),c
->saturation(),Yf
)) c
->do_callback();
428 #endif // !FL_DOXYGEN
430 ////////////////////////////////////////////////////////////////
432 void Fl_Color_Chooser::rgb_cb(Fl_Widget
* o
, void*) {
433 Fl_Color_Chooser
* c
= (Fl_Color_Chooser
*)(o
->parent());
434 double R
= c
->rvalue
.value();
435 double G
= c
->gvalue
.value();
436 double B
= c
->bvalue
.value();
437 if (c
->mode() == M_HSV
) {
438 if (c
->hsv(R
,G
,B
)) c
->do_callback();
441 if (c
->mode() != M_RGB
) {
446 if (c
->rgb(R
,G
,B
)) c
->do_callback();
449 void Fl_Color_Chooser::mode_cb(Fl_Widget
* o
, void*) {
450 Fl_Color_Chooser
* c
= (Fl_Color_Chooser
*)(o
->parent());
451 // force them to redraw even if value is the same:
458 void Fl_Color_Chooser::mode(int newMode
)
460 choice
.value(newMode
);
461 choice
.do_callback();
465 ////////////////////////////////////////////////////////////////
468 Creates a new Fl_Color_Chooser widget using the given position, size, and
470 The recommended dimensions are 200x95. The color is initialized to black.
471 \param[in] X, Y, W, H position and size of the widget
472 \param[in] L widget label, default is no label
474 Fl_Color_Chooser::Fl_Color_Chooser(int X
, int Y
, int W
, int H
, const char* L
)
475 : Fl_Group(0,0,195,115,L
),
477 valuebox(115,0,20,115),
479 rvalue(140,30,55,25),
480 gvalue(140,60,55,25),
481 bvalue(140,90,55,25),
482 resize_box(0,0,115,115)
485 resizable(resize_box
);
491 huebox
.box(FL_DOWN_FRAME
);
492 valuebox
.box(FL_DOWN_FRAME
);
493 choice
.menu(mode_menu
);
495 rvalue
.callback(rgb_cb
);
496 gvalue
.callback(rgb_cb
);
497 bvalue
.callback(rgb_cb
);
498 choice
.callback(mode_cb
);
499 choice
.box(FL_THIN_UP_BOX
);
500 choice
.textfont(FL_HELVETICA_BOLD_ITALIC
);
503 ////////////////////////////////////////////////////////////////
504 // fl_color_chooser():
506 #include <FL/Fl_Window.H>
507 #include <FL/Fl_Box.H>
508 #include <FL/Fl_Return_Button.H>
510 class ColorChip
: public Fl_Widget
{
514 ColorChip(int X
, int Y
, int W
, int H
) : Fl_Widget(X
,Y
,W
,H
) {
515 box(FL_ENGRAVED_FRAME
);}
518 void ColorChip::draw() {
519 if (damage()&FL_DAMAGE_ALL
) draw_box();
520 fl_rectf(x()+Fl::box_dx(box()),
521 y()+Fl::box_dy(box()),
522 w()-Fl::box_dw(box()),
523 h()-Fl::box_dh(box()),r
,g
,b
);
526 static void chooser_cb(Fl_Widget
* o
, void* vv
) {
527 Fl_Color_Chooser
* c
= (Fl_Color_Chooser
*)o
;
528 ColorChip
* v
= (ColorChip
*)vv
;
529 v
->r
= uchar(255*c
->r()+.5);
530 v
->g
= uchar(255*c
->g()+.5);
531 v
->b
= uchar(255*c
->b()+.5);
532 v
->damage(FL_DAMAGE_EXPOSE
);
535 extern const char* fl_ok
;
536 extern const char* fl_cancel
;
538 // fl_color_chooser's callback for ok_button (below)
539 // [in] o is a pointer to okay_button (below)
540 // [in] p is a pointer to an int to receive the return value (1)
541 // closes the fl_color_chooser window
542 static void cc_ok_cb (Fl_Widget
*o
, void *p
) {
543 *((int *)p
) = 1; // set return value
547 // fl_color_chooser's callback for cancel_button and window close
548 // [in] o is a pointer to cancel_button (below) _or_ the dialog window
549 // [in] p is a pointer to an int to receive the return value (0)
550 // closes the fl_color_chooser window
551 static void cc_cancel_cb (Fl_Widget
*o
, void *p
) {
552 *((int *)p
) = 0; // set return value
553 if (o
->window()) // cancel button
559 /** \addtogroup group_comdlg
562 \brief Pops up a window to let the user pick an arbitrary RGB color.
563 \note \#include <FL/Fl_Color_Chooser.H>
564 \image html fl_color_chooser.jpg
565 \image latex fl_color_chooser.jpg "fl_color_chooser" width=8cm
566 \param[in] name Title label for the window
567 \param[in,out] r, g, b Color components in the range 0.0 to 1.0.
568 \param[in] cmode Optional mode for color chooser. See mode(int). Default -1 if none (rgb mode).
569 \retval 1 if user confirms the selection
570 \retval 0 if user cancels the dialog
571 \relates Fl_Color_Chooser
573 int fl_color_chooser(const char* name
, double& r
, double& g
, double& b
, int cmode
) {
575 Fl_Window
window(215,200,name
);
576 window
.callback(cc_cancel_cb
,&ret
);
577 Fl_Color_Chooser
chooser(10, 10, 195, 115);
578 ColorChip
ok_color(10, 130, 95, 25);
579 Fl_Return_Button
ok_button(10, 165, 95, 25, fl_ok
);
580 ok_button
.callback(cc_ok_cb
,&ret
);
581 ColorChip
cancel_color(110, 130, 95, 25);
582 cancel_color
.r
= uchar(255*r
+.5); ok_color
.r
= cancel_color
.r
;
583 ok_color
.g
= cancel_color
.g
= uchar(255*g
+.5);
584 ok_color
.b
= cancel_color
.b
= uchar(255*b
+.5);
585 Fl_Button
cancel_button(110, 165, 95, 25, fl_cancel
);
586 cancel_button
.callback(cc_cancel_cb
,&ret
);
587 window
.resizable(chooser
);
589 chooser
.callback(chooser_cb
, &ok_color
);
590 if (cmode
!=-1) chooser
.mode(cmode
);
593 window
.hotspot(window
);
595 while (window
.shown()) Fl::wait();
596 if (ret
) { // ok_button or Enter
605 \brief Pops up a window to let the user pick an arbitrary RGB color.
606 \note \#include <FL/Fl_Color_Chooser.H>
607 \image html fl_color_chooser.jpg
608 \image latex fl_color_chooser.jpg "fl_color_chooser" width=8cm
609 \param[in] name Title label for the window
610 \param[in,out] r, g, b Color components in the range 0 to 255.
611 \param[in] cmode Optional mode for color chooser. See mode(int). Default -1 if none (rgb mode).
612 \retval 1 if user confirms the selection
613 \retval 0 if user cancels the dialog
614 \relates Fl_Color_Chooser
616 int fl_color_chooser(const char* name
, uchar
& r
, uchar
& g
, uchar
& b
, int cmode
) {
620 if (fl_color_chooser(name
,dr
,dg
,db
,cmode
)) {
621 r
= uchar(255*dr
+.5);
622 g
= uchar(255*dg
+.5);
623 b
= uchar(255*db
+.5);
631 // End of "$Id: Fl_Color_Chooser.cxx 7981 2010-12-08 23:53:04Z greg.ercolano $".