1 //
2 // "$Id$"
3 //
4 // Color chooser for the Fast Light Tool Kit (FLTK).
5 //
6 // Copyright 1998-2010 by Bill Spitzak and others.
7 //
8 // This library is free software. Distribution and use rights are outlined in
9 // the file "COPYING" which should have been included with this file.  If this
10 // file is missing or damaged, see the license at:
11 //
12 //     http://www.fltk.org/COPYING.php
13 //
14 // Please report all bugs and problems on the following page:
15 //
16 //     http://www.fltk.org/str.php
17 //
18 
19 #include <FL/Fl.H>
20 #include <FL/Fl_Color_Chooser.H>
21 #include <FL/fl_draw.H>
22 #include <FL/math.h>
23 #include <stdio.h>
24 
25 // Besides being a useful object on it's own, the Fl_Color_Chooser was
26 // an attempt to make a complex composite object that could be easily
27 // imbedded into a user interface.  If you wish to make complex objects
28 // of your own, be sure to read this code.
29 
30 // The function fl_color_chooser() creates a window containing a color
31 // chooser and a few buttons and current-color indicators.  It is an
32 // easier interface for simple programs that just need a color.
33 
34 // The "hue box" can be a circle or rectilinear.
35 // You get a circle by defining this:
36 #define CIRCLE 1
37 // And the "hue box" can auto-update when the value changes
38 // you get this by defining this:
39 #define UPDATE_HUE_BOX 1
40 
41 /**
42   This \e static method converts HSV colors to RGB colorspace.
43   \param[in] H, S, V color components
44   \param[out] R, G, B color components
45  */
hsv2rgb(double H,double S,double V,double & R,double & G,double & B)46 void Fl_Color_Chooser::hsv2rgb(
47 	double H, double S, double V, double& R, double& G, double& B) {
48   if (S < 5.0e-6) {
49     R = G = B = V;
50   } else {
51     int i = (int)H;
52     double f = H - (float)i;
53     double p1 = V*(1.0-S);
54     double p2 = V*(1.0-S*f);
55     double p3 = V*(1.0-S*(1.0-f));
56     switch (i) {
57     case 0: R = V;   G = p3;  B = p1;  break;
58     case 1: R = p2;  G = V;   B = p1;  break;
59     case 2: R = p1;  G = V;   B = p3;  break;
60     case 3: R = p1;  G = p2;  B = V;   break;
61     case 4: R = p3;  G = p1;  B = V;   break;
62     case 5: R = V;   G = p1;  B = p2;  break;
63     }
64   }
65 }
66 
67 /**
68   This \e static method converts RGB colors to HSV colorspace.
69   \param[in] R, G, B color components
70   \param[out] H, S, V color components
71  */
rgb2hsv(double R,double G,double B,double & H,double & S,double & V)72 void Fl_Color_Chooser::rgb2hsv(
73 	double R, double G, double B, double& H, double& S, double& V) {
74   double maxv = R > G ? R : G; if (B > maxv) maxv = B;
75   V = maxv;
76   if (maxv>0) {
77     double minv = R < G ? R : G; if (B < minv) minv = B;
78     S = 1.0 - double(minv)/maxv;
79     if (maxv > minv) {
80       if (maxv == R) {H = (G-B)/double(maxv-minv); if (H<0) H += 6.0;}
81       else if (maxv == G) H = 2.0+(B-R)/double(maxv-minv);
82       else H = 4.0+(R-G)/double(maxv-minv);
83     }
84   }
85 }
86 
87 /** Fl_Color_Chooser modes */
88 enum {
89   M_RGB,	/**< mode() of Fl_Color_Chooser showing RGB values */
90   M_BYTE,	/**< mode() of Fl_Color_Chooser showing byte values */
91   M_HEX,	/**< mode() of Fl_Color_Chooser showing hex values */
92   M_HSV		/**< mode() of Fl_Color_Chooser showing HSV values */
93 };
94 static const Fl_Menu_Item mode_menu[] = {
95   {"rgb"},
96   {"byte"},
97   {"hex"},
98   {"hsv"},
99   {0}
100 };
101 
102 #ifndef FL_DOXYGEN
format(char * buf)103 int Flcc_Value_Input::format(char* buf) {
104   Fl_Color_Chooser* c = (Fl_Color_Chooser*)parent();
105   if (c->mode() == M_HEX) return sprintf(buf,"0x%02X", int(value()));
106   else return Fl_Valuator::format(buf);
107 }
108 #endif // !FL_DOXYGEN
109 
set_valuators()110 void Fl_Color_Chooser::set_valuators() {
111   switch (mode()) {
112   case M_RGB:
113     rvalue.range(0,1); rvalue.step(1,1000); rvalue.value(r_);
114     gvalue.range(0,1); gvalue.step(1,1000); gvalue.value(g_);
115     bvalue.range(0,1); bvalue.step(1,1000); bvalue.value(b_);
116     break;
117   case M_BYTE: /* FALLTHROUGH */
118   case M_HEX:
119     rvalue.range(0,255); rvalue.step(1); rvalue.value(int(255*r_+.5));
120     gvalue.range(0,255); gvalue.step(1); gvalue.value(int(255*g_+.5));
121     bvalue.range(0,255); bvalue.step(1); bvalue.value(int(255*b_+.5));
122     break;
123   case M_HSV:
124     rvalue.range(0,6); rvalue.step(1,1000); rvalue.value(hue_);
125     gvalue.range(0,1); gvalue.step(1,1000); gvalue.value(saturation_);
126     bvalue.range(0,1); bvalue.step(1,1000); bvalue.value(value_);
127     break;
128   }
129 }
130 
131 /**
132   Sets the current rgb color values.
133   Does not do the callback. Does not clamp (but out of range values will
134   produce psychedelic effects in the hue selector).
135   \param[in] R, G, B color components.
136   \return 1 if a new rgb value was set, 0 if the rgb value was the previous one.
137  */
rgb(double R,double G,double B)138 int Fl_Color_Chooser::rgb(double R, double G, double B) {
139   if (R == r_ && G == g_ && B == b_) return 0;
140   r_ = R; g_ = G; b_ = B;
141   double ph = hue_;
142   double ps = saturation_;
143   double pv = value_;
144   rgb2hsv(R,G,B,hue_,saturation_,value_);
145   set_valuators();
146   set_changed();
147   if (value_ != pv) {
148 #ifdef UPDATE_HUE_BOX
149     huebox.damage(FL_DAMAGE_SCROLL);
150 #endif
151     valuebox.damage(FL_DAMAGE_EXPOSE);}
152   if (hue_ != ph || saturation_ != ps) {
153     huebox.damage(FL_DAMAGE_EXPOSE);
154     valuebox.damage(FL_DAMAGE_SCROLL);
155   }
156   return 1;
157 }
158 
159 /**
160   Set the hsv values.
161   The passed values are clamped (or for hue, modulus 6 is used) to get
162   legal values. Does not do the callback.
163   \param[in] H, S, V color components.
164   \return 1 if a new hsv value was set, 0 if the hsv value was the previous one.
165 */
hsv(double H,double S,double V)166 int Fl_Color_Chooser::hsv(double H, double S, double V) {
167   H = fmod(H,6.0); if (H < 0.0) H += 6.0;
168   if (S < 0.0) S = 0.0; else if (S > 1.0) S = 1.0;
169   if (V < 0.0) V = 0.0; else if (V > 1.0) V = 1.0;
170   if (H == hue_ && S == saturation_ && V == value_) return 0;
171   double ph = hue_;
172   double ps = saturation_;
173   double pv = value_;
174   hue_ = H; saturation_ = S; value_ = V;
175   if (value_ != pv) {
176 #ifdef UPDATE_HUE_BOX
177     huebox.damage(FL_DAMAGE_SCROLL);
178 #endif
179     valuebox.damage(FL_DAMAGE_EXPOSE);}
180   if (hue_ != ph || saturation_ != ps) {
181     huebox.damage(FL_DAMAGE_EXPOSE);
182     valuebox.damage(FL_DAMAGE_SCROLL);
183   }
184   hsv2rgb(H,S,V,r_,g_,b_);
185   set_valuators();
186   set_changed();
187   return 1;
188 }
189 
190 ////////////////////////////////////////////////////////////////
191 
tohs(double x,double y,double & h,double & s)192 static void tohs(double x, double y, double& h, double& s) {
193 #ifdef CIRCLE
194   x = 2*x-1;
195   y = 1-2*y;
196   s = sqrt(x*x+y*y); if (s > 1.0) s = 1.0;
197   h = (3.0/M_PI)*atan2(y,x);
198   if (h<0) h += 6.0;
199 #else
200   h = fmod(6.0*x,6.0); if (h < 0.0) h += 6.0;
201   s = 1.0-y; if (s < 0.0) s = 0.0; else if (s > 1.0) s = 1.0;
202 #endif
203 }
204 
205 #ifndef FL_DOXYGEN
handle(int e)206 int Flcc_HueBox::handle(int e) {
207   static double ih, is;
208   Fl_Color_Chooser* c = (Fl_Color_Chooser*)parent();
209   switch (e) {
210   case FL_PUSH:
211     if (Fl::visible_focus()) {
212       Fl::focus(this);
213       redraw();
214     }
215     ih = c->hue();
216     is = c->saturation();
217   case FL_DRAG: {
218     double Xf, Yf, H, S;
219     Xf = (Fl::event_x()-x()-Fl::box_dx(box()))/double(w()-Fl::box_dw(box()));
220     Yf = (Fl::event_y()-y()-Fl::box_dy(box()))/double(h()-Fl::box_dh(box()));
221     tohs(Xf, Yf, H, S);
222     if (fabs(H-ih) < 3*6.0/w()) H = ih;
223     if (fabs(S-is) < 3*1.0/h()) S = is;
224     if (Fl::event_state(FL_CTRL)) H = ih;
225     if (c->hsv(H, S, c->value())) c->do_callback();
226     } return 1;
227   case FL_FOCUS : /* FALLTHROUGH */
228   case FL_UNFOCUS :
229     if (Fl::visible_focus()) {
230       redraw();
231       return 1;
232     }
233     else return 1;
234   case FL_KEYBOARD :
235     return handle_key(Fl::event_key());
236   default:
237     return 0;
238   }
239 }
240 #endif // !FL_DOXYGEN
241 
generate_image(void * vv,int X,int Y,int W,uchar * buf)242 static void generate_image(void* vv, int X, int Y, int W, uchar* buf) {
243   Flcc_HueBox* v = (Flcc_HueBox*)vv;
244   int iw = v->w()-Fl::box_dw(v->box());
245   double Yf = double(Y)/(v->h()-Fl::box_dh(v->box()));
246 #ifdef UPDATE_HUE_BOX
247   const double V = ((Fl_Color_Chooser*)(v->parent()))->value();
248 #else
249   const double V = 1.0;
250 #endif
251   for (int x = X; x < X+W; x++) {
252     double Xf = double(x)/iw;
253     double H,S; tohs(Xf,Yf,H,S);
254     double r=0, g=0, b=0;
255     Fl_Color_Chooser::hsv2rgb(H,S,V,r,g,b);
256     *buf++ = uchar(255*r+.5);
257     *buf++ = uchar(255*g+.5);
258     *buf++ = uchar(255*b+.5);
259   }
260 }
261 
262 #ifndef FL_DOXYGEN
handle_key(int key)263 int Flcc_HueBox::handle_key(int key) {
264   int w1 = w()-Fl::box_dw(box())-6;
265   int h1 = h()-Fl::box_dh(box())-6;
266   Fl_Color_Chooser* c = (Fl_Color_Chooser*)parent();
267 
268 #ifdef CIRCLE
269   int X = int(.5*(cos(c->hue()*(M_PI/3.0))*c->saturation()+1) * w1);
270   int Y = int(.5*(1-sin(c->hue()*(M_PI/3.0))*c->saturation()) * h1);
271 #else
272   int X = int(c->hue()/6.0*w1);
273   int Y = int((1-c->saturation())*h1);
274 #endif
275 
276   switch (key) {
277     case FL_Up :
278       Y -= 3;
279       break;
280     case FL_Down :
281       Y += 3;
282       break;
283     case FL_Left :
284       X -= 3;
285       break;
286     case FL_Right :
287       X += 3;
288       break;
289     default :
290       return 0;
291   }
292 
293   double Xf, Yf, H, S;
294   Xf = (double)X/(double)w1;
295   Yf = (double)Y/(double)h1;
296   tohs(Xf, Yf, H, S);
297   if (c->hsv(H, S, c->value())) c->do_callback();
298 
299   return 1;
300 }
301 #endif // !FL_DOXYGEN
302 
303 #ifndef FL_DOXYGEN
draw()304 void Flcc_HueBox::draw() {
305   if (damage()&FL_DAMAGE_ALL) draw_box();
306   int x1 = x()+Fl::box_dx(box());
307   int yy1 = y()+Fl::box_dy(box());
308   int w1 = w()-Fl::box_dw(box());
309   int h1 = h()-Fl::box_dh(box());
310   if (damage() == FL_DAMAGE_EXPOSE) fl_push_clip(x1+px,yy1+py,6,6);
311   fl_draw_image(generate_image, this, x1, yy1, w1, h1);
312   if (damage() == FL_DAMAGE_EXPOSE) fl_pop_clip();
313   Fl_Color_Chooser* c = (Fl_Color_Chooser*)parent();
314 #ifdef CIRCLE
315   int X = int(.5*(cos(c->hue()*(M_PI/3.0))*c->saturation()+1) * (w1-6));
316   int Y = int(.5*(1-sin(c->hue()*(M_PI/3.0))*c->saturation()) * (h1-6));
317 #else
318   int X = int(c->hue()/6.0*(w1-6));
319   int Y = int((1-c->saturation())*(h1-6));
320 #endif
321   if (X < 0) X = 0; else if (X > w1-6) X = w1-6;
322   if (Y < 0) Y = 0; else if (Y > h1-6) Y = h1-6;
323   //  fl_color(c->value()>.75 ? FL_BLACK : FL_WHITE);
324   draw_box(FL_UP_BOX,x1+X,yy1+Y,6,6,Fl::focus() == this ? FL_FOREGROUND_COLOR : FL_GRAY);
325   px = X; py = Y;
326 }
327 #endif // !FL_DOXYGEN
328 
329 ////////////////////////////////////////////////////////////////
330 
331 #ifndef FL_DOXYGEN
handle(int e)332 int Flcc_ValueBox::handle(int e) {
333   static double iv;
334   Fl_Color_Chooser* c = (Fl_Color_Chooser*)parent();
335   switch (e) {
336   case FL_PUSH:
337     if (Fl::visible_focus()) {
338       Fl::focus(this);
339       redraw();
340     }
341     iv = c->value();
342   case FL_DRAG: {
343     double Yf;
344     Yf = 1-(Fl::event_y()-y()-Fl::box_dy(box()))/double(h()-Fl::box_dh(box()));
345     if (fabs(Yf-iv)<(3*1.0/h())) Yf = iv;
346     if (c->hsv(c->hue(),c->saturation(),Yf)) c->do_callback();
347     } return 1;
348   case FL_FOCUS : /* FALLTHROUGH */
349   case FL_UNFOCUS :
350     if (Fl::visible_focus()) {
351       redraw();
352       return 1;
353     }
354     else return 1;
355   case FL_KEYBOARD :
356     return handle_key(Fl::event_key());
357   default:
358     return 0;
359   }
360 }
361 #endif // !FL_DOXYGEN
362 
363 static double tr, tg, tb;
generate_vimage(void * vv,int X,int Y,int W,uchar * buf)364 static void generate_vimage(void* vv, int X, int Y, int W, uchar* buf) {
365   Flcc_ValueBox* v = (Flcc_ValueBox*)vv;
366   double Yf = 255*(1.0-double(Y)/(v->h()-Fl::box_dh(v->box())));
367   uchar r = uchar(tr*Yf+.5);
368   uchar g = uchar(tg*Yf+.5);
369   uchar b = uchar(tb*Yf+.5);
370   for (int x = X; x < X+W; x++) {
371     *buf++ = r; *buf++ = g; *buf++ = b;
372   }
373 }
374 
375 #ifndef FL_DOXYGEN
draw()376 void Flcc_ValueBox::draw() {
377   if (damage()&FL_DAMAGE_ALL) draw_box();
378   Fl_Color_Chooser* c = (Fl_Color_Chooser*)parent();
379   c->hsv2rgb(c->hue(),c->saturation(),1.0,tr,tg,tb);
380   int x1 = x()+Fl::box_dx(box());
381   int yy1 = y()+Fl::box_dy(box());
382   int w1 = w()-Fl::box_dw(box());
383   int h1 = h()-Fl::box_dh(box());
384   if (damage() == FL_DAMAGE_EXPOSE) fl_push_clip(x1,yy1+py,w1,6);
385   fl_draw_image(generate_vimage, this, x1, yy1, w1, h1);
386   if (damage() == FL_DAMAGE_EXPOSE) fl_pop_clip();
387   int Y = int((1-c->value()) * (h1-6));
388   if (Y < 0) Y = 0; else if (Y > h1-6) Y = h1-6;
389   draw_box(FL_UP_BOX,x1,yy1+Y,w1,6,Fl::focus() == this ? FL_FOREGROUND_COLOR : FL_GRAY);
390   py = Y;
391 }
392 #endif // !FL_DOXYGEN
393 
394 #ifndef FL_DOXYGEN
handle_key(int key)395 int Flcc_ValueBox::handle_key(int key) {
396   int h1 = h()-Fl::box_dh(box())-6;
397   Fl_Color_Chooser* c = (Fl_Color_Chooser*)parent();
398 
399   int Y = int((1-c->value()) * h1);
400   if (Y < 0) Y = 0; else if (Y > h1) Y = h1;
401 
402   switch (key) {
403     case FL_Up :
404       Y -= 3;
405       break;
406     case FL_Down :
407       Y += 3;
408       break;
409     default :
410       return 0;
411   }
412 
413   double Yf;
414   Yf = 1-((double)Y/(double)h1);
415   if (c->hsv(c->hue(),c->saturation(),Yf)) c->do_callback();
416 
417   return 1;
418 }
419 #endif // !FL_DOXYGEN
420 
421 ////////////////////////////////////////////////////////////////
422 
rgb_cb(Fl_Widget * o,void *)423 void Fl_Color_Chooser::rgb_cb(Fl_Widget* o, void*) {
424   Fl_Color_Chooser* c = (Fl_Color_Chooser*)(o->parent());
425   double R = c->rvalue.value();
426   double G = c->gvalue.value();
427   double B = c->bvalue.value();
428   if (c->mode() == M_HSV) {
429     if (c->hsv(R,G,B)) c->do_callback();
430     return;
431   }
432   if (c->mode() != M_RGB) {
433     R = R/255;
434     G = G/255;
435     B = B/255;
436   }
437   if (c->rgb(R,G,B)) c->do_callback();
438 }
439 
mode_cb(Fl_Widget * o,void *)440 void Fl_Color_Chooser::mode_cb(Fl_Widget* o, void*) {
441   Fl_Color_Chooser* c = (Fl_Color_Chooser*)(o->parent());
442   // force them to redraw even if value is the same:
443   c->rvalue.value(-1);
444   c->gvalue.value(-1);
445   c->bvalue.value(-1);
446   c->set_valuators();
447 }
448 
mode(int newMode)449 void Fl_Color_Chooser::mode(int newMode)
450 {
451   choice.value(newMode);
452   choice.do_callback();
453 }
454 
455 
456 ////////////////////////////////////////////////////////////////
457 
458 /**
459   Creates a new Fl_Color_Chooser widget using the given position, size, and
460   label string.
461   The recommended dimensions are 200x95. The color is initialized to black.
462   \param[in] X, Y, W, H position and size of the widget
463   \param[in] L widget label, default is no label
464  */
Fl_Color_Chooser(int X,int Y,int W,int H,const char * L)465 Fl_Color_Chooser::Fl_Color_Chooser(int X, int Y, int W, int H, const char* L)
466   : Fl_Group(0,0,195,115,L),
467     huebox(0,0,115,115),
468     valuebox(115,0,20,115),
469     choice(140,0,55,25),
470     rvalue(140,30,55,25),
471     gvalue(140,60,55,25),
472     bvalue(140,90,55,25),
473     resize_box(0,0,115,115)
474 {
475   end();
476   resizable(resize_box);
477   resize(X,Y,W,H);
478   r_ = g_ = b_ = 0;
479   hue_ = 0.0;
480   saturation_ = 0.0;
481   value_ = 0.0;
482   huebox.box(FL_DOWN_FRAME);
483   valuebox.box(FL_DOWN_FRAME);
484   choice.menu(mode_menu);
485   set_valuators();
486   rvalue.callback(rgb_cb);
487   gvalue.callback(rgb_cb);
488   bvalue.callback(rgb_cb);
489   choice.callback(mode_cb);
490   choice.box(FL_THIN_UP_BOX);
491   choice.textfont(FL_HELVETICA_BOLD_ITALIC);
492 }
493 
494 ////////////////////////////////////////////////////////////////
495 // fl_color_chooser():
496 
497 #include <FL/Fl_Window.H>
498 #include <FL/Fl_Box.H>
499 #include <FL/Fl_Return_Button.H>
500 
501 class ColorChip : public Fl_Widget {
502   void draw();
503 public:
504   uchar r,g,b;
ColorChip(int X,int Y,int W,int H)505   ColorChip(int X, int Y, int W, int H) : Fl_Widget(X,Y,W,H) {
506     box(FL_ENGRAVED_FRAME);}
507 };
508 
draw()509 void ColorChip::draw() {
510   if (damage()&FL_DAMAGE_ALL) draw_box();
511   fl_rectf(x()+Fl::box_dx(box()),
512 	   y()+Fl::box_dy(box()),
513 	   w()-Fl::box_dw(box()),
514 	   h()-Fl::box_dh(box()),r,g,b);
515 }
516 
chooser_cb(Fl_Widget * o,void * vv)517 static void chooser_cb(Fl_Widget* o, void* vv) {
518   Fl_Color_Chooser* c = (Fl_Color_Chooser*)o;
519   ColorChip* v = (ColorChip*)vv;
520   v->r = uchar(255*c->r()+.5);
521   v->g = uchar(255*c->g()+.5);
522   v->b = uchar(255*c->b()+.5);
523   v->damage(FL_DAMAGE_EXPOSE);
524 }
525 
526 extern const char* fl_ok;
527 extern const char* fl_cancel;
528 
529 // fl_color_chooser's callback for ok_button (below)
530 //  [in] o is a pointer to okay_button (below)
531 //  [in] p is a pointer to an int to receive the return value (1)
532 // closes the fl_color_chooser window
cc_ok_cb(Fl_Widget * o,void * p)533 static void cc_ok_cb (Fl_Widget *o, void *p) {
534   *((int *)p) = 1; // set return value
535   o->window()->hide();
536 }
537 
538 // fl_color_chooser's callback for cancel_button and window close
539 //  [in] o is a pointer to cancel_button (below) _or_ the dialog window
540 //  [in] p is a pointer to an int to receive the return value (0)
541 // closes the fl_color_chooser window
cc_cancel_cb(Fl_Widget * o,void * p)542 static void cc_cancel_cb (Fl_Widget *o, void *p) {
543   *((int *)p) = 0; // set return value
544   if (o->window()) // cancel button
545     o->window()->hide();
546   else // window close
547     o->hide();
548 }
549 
550 /** \addtogroup  group_comdlg
551     @{ */
552 /**
553   \brief Pops up a window to let the user pick an arbitrary RGB color.
554   \note \#include <FL/Fl_Color_Chooser.H>
555   \image html fl_color_chooser.jpg
556   \image latex  fl_color_chooser.jpg "fl_color_chooser" width=8cm
557   \param[in] name Title label for the window
558   \param[in,out] r, g, b Color components in the range 0.0 to 1.0.
559   \param[in] cmode Optional mode for color chooser. See mode(int). Default -1 if none (rgb mode).
560   \retval 1 if user confirms the selection
561   \retval 0 if user cancels the dialog
562   \relates Fl_Color_Chooser
563  */
fl_color_chooser(const char * name,double & r,double & g,double & b,int cmode)564 int fl_color_chooser(const char* name, double& r, double& g, double& b, int cmode) {
565   int ret = 0;
566   Fl_Window window(215,200,name);
567   window.callback(cc_cancel_cb,&ret);
568   Fl_Color_Chooser chooser(10, 10, 195, 115);
569   ColorChip ok_color(10, 130, 95, 25);
570   Fl_Return_Button ok_button(10, 165, 95, 25, fl_ok);
571   ok_button.callback(cc_ok_cb,&ret);
572   ColorChip cancel_color(110, 130, 95, 25);
573   cancel_color.r = uchar(255*r+.5); ok_color.r = cancel_color.r;
574   ok_color.g = cancel_color.g = uchar(255*g+.5);
575   ok_color.b = cancel_color.b = uchar(255*b+.5);
576   Fl_Button cancel_button(110, 165, 95, 25, fl_cancel);
577   cancel_button.callback(cc_cancel_cb,&ret);
578   window.resizable(chooser);
579   chooser.rgb(r,g,b);
580   chooser.callback(chooser_cb, &ok_color);
581   if (cmode!=-1) chooser.mode(cmode);
582   window.end();
583   window.set_modal();
584   window.hotspot(window);
585   window.show();
586   while (window.shown()) Fl::wait();
587   if (ret) { // ok_button or Enter
588     r = chooser.r();
589     g = chooser.g();
590     b = chooser.b();
591   }
592   return ret;
593 }
594 
595 /**
596   \brief Pops up a window to let the user pick an arbitrary RGB color.
597   \note \#include <FL/Fl_Color_Chooser.H>
598   \image html fl_color_chooser.jpg
599   \image latex  fl_color_chooser.jpg "fl_color_chooser" width=8cm
600   \param[in] name Title label for the window
601   \param[in,out] r, g, b Color components in the range 0 to 255.
602   \param[in] cmode Optional mode for color chooser. See mode(int). Default -1 if none (rgb mode).
603   \retval 1 if user confirms the selection
604   \retval 0 if user cancels the dialog
605   \relates Fl_Color_Chooser
606  */
fl_color_chooser(const char * name,uchar & r,uchar & g,uchar & b,int cmode)607 int fl_color_chooser(const char* name, uchar& r, uchar& g, uchar& b, int cmode) {
608   double dr = r/255.0;
609   double dg = g/255.0;
610   double db = b/255.0;
611   if (fl_color_chooser(name,dr,dg,db,cmode)) {
612     r = uchar(255*dr+.5);
613     g = uchar(255*dg+.5);
614     b = uchar(255*db+.5);
615     return 1;
616   }
617   return 0;
618 }
619 
620 /** @} */
621 //
622 // End of "$Id$".
623 //
624