]> git.lyx.org Git - lyx.git/blob - src/frontends/xforms/FormColorpicker.C
If I ever see another licence blurb again, it'll be too soon...
[lyx.git] / src / frontends / xforms / FormColorpicker.C
1 /**
2  * \file FormColorpicker.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Angus Leeming
7  *
8  * Full author contact details are available in file CREDITS.
9  */
10
11 #include <config.h>
12
13 #include "FormColorpicker.h"
14 #include "forms/form_colorpicker.h"
15
16 #include "xforms_resize.h"
17 #include "Tooltips.h"
18 #include "Color.h"
19
20 #include "gettext.h"
21 #include "lyxrc.h"
22
23 #include "support/LAssert.h"
24 #include "support/filetools.h" //  LibFileSearch
25 #include "support/tostr.h"
26
27 #include "lyx_forms.h"
28
29 #include "Lsstream.h"
30 #include <iomanip>
31
32
33 namespace {
34
35 enum GuiColors {
36         GUI_COLOR_CHOICE   = FL_FREE_COL13,
37         GUI_COLOR_HUE_DIAL = FL_FREE_COL14,
38 };
39
40
41 string const fillLabel(RGBColor const & rgb)
42 {
43         return tostr(rgb.r) + ", " + tostr(rgb.g) + ", " + tostr(rgb.b);
44 }
45
46
47 string const fillLabel(HSVColor const & hsv)
48 {
49         int const h = int(hsv.h);
50         int const s = int(100.0 * hsv.s);
51         int const v = int(100.0 * hsv.v);
52
53         return tostr(h) + ", " + tostr(s) + ", " + tostr(v);
54 }
55
56 } // namespace anon
57
58
59 extern "C" {
60
61 // Callback function invoked by xforms when the dialog is closed by the
62 // window manager.
63 static int C_WMHideCB(FL_FORM * form, void *);
64
65 } // extern "C"
66
67
68 FormColorpicker::FormColorpicker()
69         : minw_(0), minh_(0),
70           title_(_("Select Color")),
71           finished_(true),
72           icon_pixmap_(0), icon_mask_(0),
73           tooltips_(new Tooltips)
74 {}
75
76
77 FormColorpicker::~FormColorpicker()
78 {
79         if (icon_pixmap_)
80                 XFreePixmap(fl_get_display(), icon_pixmap_);
81 }
82
83
84 RGBColor const & FormColorpicker::requestColor(RGBColor const & color)
85 {
86         input_color_ = color;
87         color_ = color;
88
89         show();
90
91         fl_deactivate_all_forms();
92         fl_activate_form(form());
93
94         finished_ = false;
95         while (!finished_)
96                 fl_check_forms();
97
98         fl_activate_all_forms();
99         return color_;
100 }
101
102
103 void FormColorpicker::show()
104 {
105         if (!form()) {
106                 build();
107                 prepare_to_show();
108         }
109
110         // make sure the form is up to date.
111         fl_freeze_form(form());
112         update();
113         fl_unfreeze_form(form());
114
115         if (form()->visible) {
116                 fl_raise_form(form());
117                 /* This XMapWindow() will hopefully ensure that
118                  * iconified dialogs are de-iconified. Mad props
119                  * out to those crazy Xlib guys for forgetting a
120                  * XDeiconifyWindow(). At least WindowMaker, when
121                  * being notified of the redirected MapRequest will
122                  * specifically de-iconify. From source, fvwm2 seems
123                  * to do the same.
124                  */
125                 XMapWindow(fl_get_display(), form()->window);
126         } else {
127                 // calls to fl_set_form_minsize/maxsize apply only to the next
128                 // fl_show_form(), so this comes first.
129                 fl_set_form_minsize(form(), minw_, minh_);
130
131                 string const maximize_title = "LyX: " + title_;
132                 int const iconify_policy =
133                         lyxrc.dialogs_iconify_with_main ? FL_TRANSIENT : 0;
134
135                 fl_show_form(form(),
136                              FL_PLACE_MOUSE | FL_FREE_SIZE,
137                              iconify_policy,
138                              maximize_title.c_str());
139         }
140 }
141
142
143 void FormColorpicker::hide() const
144 {
145         // xforms sometimes tries to process a hint-type MotionNotify, and
146         // use XQueryPointer, without verifying if the window still exists.
147         // So we try to clear out motion events in the queue before the
148         // DestroyNotify
149         XSync(fl_get_display(), false);
150
151         if (form() && form()->visible)
152                 fl_hide_form(form());
153 }
154
155
156 void FormColorpicker::build()
157 {
158         dialog_.reset(build_colorpicker(this));
159         rgb_.reset(build_colorpicker_rgb(this));
160         hsv_.reset(build_colorpicker_hsv(this));
161
162         fl_set_object_color(dialog_->button_color,
163                             GUI_COLOR_CHOICE, GUI_COLOR_CHOICE);
164
165         fl_set_object_color(hsv_->dial_hue, GUI_COLOR_HUE_DIAL, FL_BLACK);
166         fl_set_dial_bounds(hsv_->dial_hue, 0.0, 360.0);
167         fl_set_dial_step(hsv_->dial_hue, 1.0);
168         fl_set_dial_return(hsv_->dial_hue, FL_RETURN_CHANGED);
169
170         fl_set_slider_bounds(hsv_->slider_saturation, 0.0, 1.0);
171         fl_set_slider_step(hsv_->slider_saturation, 0.01);
172         fl_set_slider_return(hsv_->slider_saturation, FL_RETURN_CHANGED);
173
174         fl_set_slider_bounds(hsv_->slider_value, 0.0, 1.0);
175         fl_set_slider_step(hsv_->slider_value, 0.01);
176         fl_set_slider_return(hsv_->slider_value, FL_RETURN_CHANGED);
177
178         fl_set_slider_bounds(rgb_->slider_red, 0.0, 255.0);
179         fl_set_slider_step(rgb_->slider_red, 1.0);
180         fl_set_slider_return(rgb_->slider_red, FL_RETURN_CHANGED);
181
182         fl_set_slider_bounds(rgb_->slider_green, 0.0, 255.0);
183         fl_set_slider_step(rgb_->slider_green, 1.0);
184         fl_set_slider_return(rgb_->slider_green, FL_RETURN_CHANGED);
185
186         fl_set_slider_bounds(rgb_->slider_blue, 0.0, 255.0);
187         fl_set_slider_step(rgb_->slider_blue, 1.0);
188         fl_set_slider_return(rgb_->slider_blue, FL_RETURN_CHANGED);
189
190         // Stack tabs
191         fl_addto_tabfolder(dialog_->tabfolder,_("RGB").c_str(), rgb_->form);
192         fl_addto_tabfolder(dialog_->tabfolder,_("HSV").c_str(), hsv_->form);
193 }
194
195
196 void FormColorpicker::update() const
197 {
198         fl_mapcolor(GUI_COLOR_CHOICE, color_.r, color_.g, color_.b);
199
200         FL_FORM * folder = fl_get_active_folder(dialog_->tabfolder);
201
202         if (!folder)
203                 folder = rgb_->form;
204
205         if (folder == rgb_->form) {
206                 fl_set_slider_value(rgb_->slider_red,   color_.r);
207                 fl_set_slider_value(rgb_->slider_green, color_.g);
208                 fl_set_slider_value(rgb_->slider_blue,  color_.b);
209
210                 fl_set_object_label(dialog_->text_color_values,
211                                     fillLabel(color_).c_str());
212
213         } else if (folder == hsv_->form) {
214                 HSVColor hsv = HSVColor(color_);
215                 hsv.h = std::max(hsv.h, 0.0);
216
217                 fl_set_dial_value(hsv_->dial_hue, hsv.h);
218                 fl_set_slider_value(hsv_->slider_saturation, hsv.s);
219                 fl_set_slider_value(hsv_->slider_value, hsv.v);
220
221                 fl_set_object_label(dialog_->text_color_values,
222                                     fillLabel(hsv).c_str());
223
224                 RGBColor col = HSVColor(hsv.h, 1.0, 1.0);
225                 col.r = std::max(col.r, 0u);
226                 fl_mapcolor(GUI_COLOR_HUE_DIAL, col.r, col.g, col.b);
227                 fl_redraw_object(hsv_->dial_hue);
228         }
229 }
230
231
232 void FormColorpicker::input(FL_OBJECT * ob, long)
233 {
234         if (ob == dialog_->tabfolder) {
235                 update();
236
237         } else if (ob == hsv_->dial_hue ||
238                    ob == hsv_->slider_saturation ||
239                    ob == hsv_->slider_value) {
240                 InputHSV();
241
242         } else if (ob == rgb_->slider_red ||
243                    ob == rgb_->slider_green ||
244                    ob == rgb_->slider_blue) {
245                 InputRGB();
246
247         } else if (ob == dialog_->button_ok) {
248                 hide();
249                 finished_ = true;
250
251         } else if (ob == dialog_->button_close || ob == 0) {
252                 color_ = input_color_;
253                 hide();
254                 finished_ = true;
255         }
256 }
257
258
259 FL_FORM * FormColorpicker::form() const
260 {
261         return dialog_.get() ? dialog_->form : 0;
262 }
263
264
265 Tooltips & FormColorpicker::tooltips() const
266 {
267         return *tooltips_;
268 }
269
270
271 void FormColorpicker::prepare_to_show()
272 {
273         double const scale = get_scale_to_fit(form());
274         if (scale > 1.001)
275                 scale_form_horizontally(form(), scale);
276
277         // work around dumb xforms sizing bug
278         minw_ = form()->w;
279         minh_ = form()->h;
280
281         fl_set_form_atclose(form(), C_WMHideCB, 0);
282
283         // set the title for the minimized form
284         if (!lyxrc.dialogs_iconify_with_main)
285                 fl_winicontitle(form()->window, title_.c_str());
286
287         //  assign an icon to the form
288         string const iconname =
289                 lyx::support::LibFileSearch("images", "lyx", "xpm");
290
291         if (!iconname.empty()) {
292                 unsigned int w, h;
293                 icon_pixmap_ = fl_read_pixmapfile(fl_root,
294                                                   iconname.c_str(),
295                                                   &w,
296                                                   &h,
297                                                   &icon_mask_,
298                                                   0, 0, 0);
299                 fl_set_form_icon(form(), icon_pixmap_, icon_mask_);
300         }
301 }
302
303
304 void FormColorpicker::InputRGB()
305 {
306         int const red   = int(fl_get_slider_value(rgb_->slider_red));
307         int const green = int(fl_get_slider_value(rgb_->slider_green));
308         int const blue  = int(fl_get_slider_value(rgb_->slider_blue));
309
310         color_ = RGBColor(red, green, blue);
311
312         fl_freeze_form(dialog_->form);
313
314         fl_set_object_label(dialog_->text_color_values,
315                             fillLabel(color_).c_str());
316
317         fl_mapcolor(GUI_COLOR_CHOICE, color_.r, color_.g, color_.b);
318         fl_redraw_object(dialog_->button_color);
319
320         fl_unfreeze_form(dialog_->form);
321 }
322
323
324 void FormColorpicker::InputHSV()
325 {
326         double const hue = fl_get_dial_value(hsv_->dial_hue);
327         double const sat = fl_get_slider_value(hsv_->slider_saturation);
328         double const val = fl_get_slider_value(hsv_->slider_value);
329
330         HSVColor hsv = HSVColor(hue, sat, val);
331         color_ = hsv;
332
333         fl_freeze_form(dialog_->form);
334
335         fl_set_object_label(dialog_->text_color_values, fillLabel(hsv).c_str());
336
337         fl_mapcolor(GUI_COLOR_CHOICE, color_.r, color_.g, color_.b);
338         fl_redraw_object(dialog_->button_color);
339
340         RGBColor col = HSVColor(hue, 1.0, 1.0);
341         col.r = std::max(col.r, 0u);
342         fl_mapcolor(GUI_COLOR_HUE_DIAL, col.r, col.g, col.b);
343         fl_redraw_object(hsv_->dial_hue);
344
345         fl_unfreeze_form(dialog_->form);
346 }
347
348
349 extern "C" {
350
351 void C_FormColorpickerInputCB(FL_OBJECT * ob, long d)
352 {
353         lyx::support::Assert(ob && ob->form && ob->form->u_vdata);
354         FormColorpicker * ptr =
355                 static_cast<FormColorpicker *>(ob->form->u_vdata);
356         ptr->input(ob, d);
357 }
358
359
360 static int C_WMHideCB(FL_FORM * form, void *)
361 {
362         // Close the dialog cleanly, even if the WM is used to do so.
363         lyx::support::Assert(form && form->u_vdata);
364         FormColorpicker * ptr = static_cast<FormColorpicker *>(form->u_vdata);
365         ptr->input(0, 0);
366         return FL_CANCEL;
367 }
368
369 } // extern "C"