Fix most of the warnings from doxygen for synfig-studio sources.
[synfig.git] / synfig-studio / trunk / src / gtkmm / dialog_setup.cpp
1 /* === S Y N F I G ========================================================= */
2 /*!     \file dialog_setup.cpp
3 **      \brief Template File
4 **
5 **      $Id$
6 **
7 **      \legal
8 **      Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
9 **      Copyright (c) 2007 Chris Moore
10 **
11 **      This package is free software; you can redistribute it and/or
12 **      modify it under the terms of the GNU General Public License as
13 **      published by the Free Software Foundation; either version 2 of
14 **      the License, or (at your option) any later version.
15 **
16 **      This package is distributed in the hope that it will be useful,
17 **      but WITHOUT ANY WARRANTY; without even the implied warranty of
18 **      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 **      General Public License for more details.
20 **      \endlegal
21 */
22 /* ========================================================================= */
23
24 /* === H E A D E R S ======================================================= */
25
26 #ifdef USING_PCH
27 #       include "pch.h"
28 #else
29 #ifdef HAVE_CONFIG_H
30 #       include <config.h>
31 #endif
32
33 #include "dialog_setup.h"
34 #include "app.h"
35 #include <gtkmm/scale.h>
36 #include <gtkmm/table.h>
37 #include <gtkmm/frame.h>
38 #include <gtkmm/notebook.h>
39 #include <gtkmm/spinbutton.h>
40 #include "widget_enum.h"
41
42 #include <ETL/stringf>
43 #include <ETL/misc>
44 #include "general.h"
45
46 #endif
47
48 /* === U S I N G =========================================================== */
49
50 using namespace std;
51 using namespace etl;
52 using namespace synfig;
53 using namespace studio;
54
55 /* === M A C R O S ========================================================= */
56
57 /* === G L O B A L S ======================================================= */
58
59 /* === P R O C E D U R E S ================================================= */
60
61 /* === M E T H O D S ======================================================= */
62
63 Dialog_Setup::Dialog_Setup():
64         Dialog(_("Synfig Studio Setup"),false,true),
65         adj_gamma_r(2.2,0.1,3.0,0.025,0.025,0.025),
66         adj_gamma_g(2.2,0.1,3.0,0.025,0.025,0.025),
67         adj_gamma_b(2.2,0.1,3.0,0.025,0.025,0.025),
68         adj_recent_files(15,1,50,1,1,1),
69         adj_undo_depth(100,10,5000,1,1,1),
70         toggle_use_colorspace_gamma(_("Visually Linear Color Selection")),
71         toggle_single_threaded(_("Use Only a Single Thread"))
72 {
73         // Setup the buttons
74
75         Gtk::Button *ok_button(manage(new class Gtk::Button(Gtk::StockID("gtk-ok"))));
76         ok_button->show();
77         add_action_widget(*ok_button,2);
78         ok_button->signal_clicked().connect(sigc::mem_fun(*this, &Dialog_Setup::on_ok_pressed));
79
80         Gtk::Button *apply_button(manage(new class Gtk::Button(Gtk::StockID("gtk-apply"))));
81         apply_button->show();
82         add_action_widget(*apply_button,1);
83         apply_button->signal_clicked().connect(sigc::mem_fun(*this, &Dialog_Setup::on_apply_pressed));
84
85         Gtk::Button *cancel_button(manage(new class Gtk::Button(Gtk::StockID("gtk-close"))));
86         cancel_button->show();
87         add_action_widget(*cancel_button,0);
88         cancel_button->signal_clicked().connect(sigc::mem_fun(*this, &Dialog_Setup::hide));
89
90
91         // Notebook
92         Gtk::Notebook *notebook=manage(new class Gtk::Notebook());
93         get_vbox()->pack_start(*notebook);
94
95
96         // Gamma
97         Gtk::Table *gamma_table=manage(new Gtk::Table(2,2,false));
98         notebook->append_page(*gamma_table,_("Gamma"));
99         //gamma_frame->add(*gamma_table);
100
101         gamma_table->attach(gamma_pattern, 0, 2, 0, 1, Gtk::EXPAND, Gtk::SHRINK|Gtk::FILL, 0, 0);
102
103         Gtk::HScale* scale_gamma_r(manage(new Gtk::HScale(adj_gamma_r)));
104         gamma_table->attach(*manage(new Gtk::Label(_("Red"))), 0, 1, 1, 2, Gtk::SHRINK|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0);
105         gamma_table->attach(*scale_gamma_r, 1, 2, 1, 2, Gtk::EXPAND|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0);
106         adj_gamma_r.signal_value_changed().connect(sigc::mem_fun(*this,&studio::Dialog_Setup::on_gamma_r_change));
107
108         Gtk::HScale* scale_gamma_g(manage(new Gtk::HScale(adj_gamma_g)));
109         gamma_table->attach(*manage(new Gtk::Label(_("Green"))), 0, 1, 2, 3, Gtk::SHRINK|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0);
110         gamma_table->attach(*scale_gamma_g, 1, 2, 2, 3, Gtk::EXPAND|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0);
111         adj_gamma_g.signal_value_changed().connect(sigc::mem_fun(*this,&studio::Dialog_Setup::on_gamma_g_change));
112
113         Gtk::HScale* scale_gamma_b(manage(new Gtk::HScale(adj_gamma_b)));
114         gamma_table->attach(*manage(new Gtk::Label(_("Blue"))), 0, 1, 3, 4, Gtk::SHRINK|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0);
115         gamma_table->attach(*scale_gamma_b, 1, 2, 3, 4, Gtk::EXPAND|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0);
116         adj_gamma_b.signal_value_changed().connect(sigc::mem_fun(*this,&studio::Dialog_Setup::on_gamma_b_change));
117
118         gamma_table->attach(*manage(new Gtk::Label(_("Black Level"))), 0, 1, 4, 5, Gtk::SHRINK|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0);
119         gamma_table->attach(black_level_selector, 1, 2, 4, 5, Gtk::EXPAND|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0);
120         black_level_selector.signal_value_changed().connect(sigc::mem_fun(*this,&studio::Dialog_Setup::on_black_level_change));
121
122         //gamma_table->attach(*manage(new Gtk::Label(_("Red-Blue Level"))), 0, 1, 5, 6, Gtk::SHRINK|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0);
123         //gamma_table->attach(red_blue_level_selector, 1, 2, 5, 6, Gtk::EXPAND|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0);
124         //red_blue_level_selector.signal_value_changed().connect(sigc::mem_fun(*this,&studio::Dialog_Setup::on_red_blue_level_change));
125
126
127         // Misc
128         Gtk::Table *misc_table=manage(new Gtk::Table(2,2,false));
129         notebook->append_page(*misc_table,_("Misc."));
130
131         // Misc - Timestamp
132         timestamp_menu=manage(new class Gtk::Menu());
133         misc_table->attach(*manage(new Gtk::Label(_("Timestamp"))), 0, 1, 0, 1, Gtk::SHRINK|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0);
134         misc_table->attach(timestamp_optionmenu, 1, 2, 0, 1, Gtk::EXPAND|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0);
135
136 #define ADD_TIMESTAMP(desc,x)   \
137         timestamp_menu->items().push_back(      \
138                 Gtk::Menu_Helpers::MenuElem(    \
139                         desc,   \
140                         sigc::bind(     \
141                                 sigc::mem_fun(  \
142                                         *this,  \
143                                         &studio::Dialog_Setup::set_time_format  \
144                                 ),      \
145                                 x       \
146                         )       \
147                 )       \
148         );
149         ADD_TIMESTAMP("HH:MM:SS.FF",Time::FORMAT_VIDEO);
150         ADD_TIMESTAMP("(HHh MMm SSs) FFf",Time::FORMAT_NORMAL);
151         ADD_TIMESTAMP("(HHhMMmSSs)FFf",Time::FORMAT_NORMAL|Time::FORMAT_NOSPACES);
152         ADD_TIMESTAMP("HHh MMm SSs FFf",Time::FORMAT_NORMAL|Time::FORMAT_FULL);
153         ADD_TIMESTAMP("HHhMMmSSsFFf",Time::FORMAT_NORMAL|Time::FORMAT_NOSPACES|Time::FORMAT_FULL);
154
155         timestamp_optionmenu.set_menu(*timestamp_menu);
156
157 #undef ADD_TIMESTAMP
158
159         {
160                 ParamDesc param_desc;
161                 param_desc
162                         .set_hint("enum")
163                         .add_enum_value(Distance::SYSTEM_UNITS,"u",_("Units"))
164                         .add_enum_value(Distance::SYSTEM_PIXELS,"px",_("Pixels"))
165                         .add_enum_value(Distance::SYSTEM_POINTS,"pt",_("Points"))
166                         .add_enum_value(Distance::SYSTEM_INCHES,"in",_("Inches"))
167                         .add_enum_value(Distance::SYSTEM_METERS,"m",_("Meters"))
168                         .add_enum_value(Distance::SYSTEM_CENTIMETERS,"cm",_("Centimeters"))
169                         .add_enum_value(Distance::SYSTEM_MILLIMETERS,"mm",_("Millimeters"));
170
171                 widget_enum=manage(new Widget_Enum());
172                 widget_enum->set_param_desc(param_desc);
173
174                 misc_table->attach(*manage(new Gtk::Label(_("Unit System"))), 0, 1, 3, 4, Gtk::SHRINK|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0);
175                 misc_table->attach(*widget_enum, 1, 2, 3, 4, Gtk::EXPAND|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0);
176         }
177
178         // Misc - recent files
179         Gtk::SpinButton* recent_files_spinbutton(manage(new Gtk::SpinButton(adj_recent_files,1,0)));
180         misc_table->attach(*manage(new Gtk::Label(_("Recent Files"))), 0, 1, 1, 2, Gtk::SHRINK|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0);
181         misc_table->attach(*recent_files_spinbutton, 1, 2, 1, 2, Gtk::EXPAND|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0);
182
183         // Misc - use_colorspace_gamma
184         misc_table->attach(toggle_use_colorspace_gamma, 0, 2, 2, 3, Gtk::EXPAND|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0);
185
186         // Misc - single_threaded
187         misc_table->attach(toggle_single_threaded, 0, 2, 4, 5, Gtk::EXPAND|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0);
188
189         show_all_children();
190 }
191
192 Dialog_Setup::~Dialog_Setup()
193 {
194 }
195
196 void
197 Dialog_Setup::on_ok_pressed()
198 {
199     on_apply_pressed();
200         hide();
201 }
202
203 void
204 Dialog_Setup::on_apply_pressed()
205 {
206         App::gamma.set_all(1.0/adj_gamma_r.get_value(),1.0/adj_gamma_g.get_value(),1.0/adj_gamma_b.get_value(),black_level_selector.get_value(),red_blue_level_selector.get_value());
207
208         App::set_max_recent_files((int)adj_recent_files.get_value());
209
210         // Set the time format
211         App::set_time_format(get_time_format());
212
213         // Set the use_colorspace_gamma flag
214         App::use_colorspace_gamma=toggle_use_colorspace_gamma.get_active();
215
216         // Set the single_threaded flag
217         App::single_threaded=toggle_single_threaded.get_active();
218
219         App::distance_system=Distance::System(widget_enum->get_value());
220
221         App::save_settings();
222 }
223
224 void
225 Dialog_Setup::on_gamma_r_change()
226 {
227         gamma_pattern.set_gamma_r(1.0/adj_gamma_r.get_value());
228         gamma_pattern.refresh();
229         gamma_pattern.queue_draw();
230 }
231
232 void
233 Dialog_Setup::on_gamma_g_change()
234 {
235         gamma_pattern.set_gamma_g(1.0/adj_gamma_g.get_value());
236         gamma_pattern.refresh();
237         gamma_pattern.queue_draw();
238 }
239
240 void
241 Dialog_Setup::on_gamma_b_change()
242 {
243         gamma_pattern.set_gamma_b(1.0/adj_gamma_b.get_value());
244         gamma_pattern.refresh();
245         gamma_pattern.queue_draw();
246 }
247
248 void
249 Dialog_Setup::on_black_level_change()
250 {
251         gamma_pattern.set_black_level(black_level_selector.get_value());
252         gamma_pattern.refresh();
253         gamma_pattern.queue_draw();
254 }
255
256 void
257 Dialog_Setup::on_red_blue_level_change()
258 {
259         gamma_pattern.set_red_blue_level(red_blue_level_selector.get_value());
260         gamma_pattern.refresh();
261         gamma_pattern.queue_draw();
262 }
263
264
265 void
266 Dialog_Setup::refresh()
267 {
268         // Refresh the temporary gamma; do this before adjusting the sliders,
269         // or variables will be used before their initialization.
270         gamma_pattern.set_gamma_r(App::gamma.get_gamma_r());
271         gamma_pattern.set_gamma_g(App::gamma.get_gamma_g());
272         gamma_pattern.set_gamma_b(App::gamma.get_gamma_b());
273         gamma_pattern.set_black_level(App::gamma.get_black_level());
274         gamma_pattern.set_red_blue_level(App::gamma.get_red_blue_level());
275
276         adj_gamma_r.set_value(1.0/App::gamma.get_gamma_r());
277         adj_gamma_g.set_value(1.0/App::gamma.get_gamma_g());
278         adj_gamma_b.set_value(1.0/App::gamma.get_gamma_b());
279         black_level_selector.set_value(App::gamma.get_black_level());
280         red_blue_level_selector.set_value(App::gamma.get_red_blue_level());
281
282         gamma_pattern.refresh();
283
284         adj_recent_files.set_value(App::get_max_recent_files());
285
286         // Refresh the time format
287         set_time_format(App::get_time_format());
288
289         widget_enum->set_value(App::distance_system);
290
291         // Refresh the status of the use_colorspace_gamma flag
292         toggle_use_colorspace_gamma.set_active(App::use_colorspace_gamma);
293
294         // Refresh the status of the single_threaded flag
295         toggle_single_threaded.set_active(App::single_threaded);
296 }
297
298 GammaPattern::GammaPattern():
299         tile_w(80),
300         tile_h(80)
301 {
302         set_size_request(tile_w*4,tile_h*3);
303         signal_expose_event().connect(sigc::mem_fun(*this, &studio::GammaPattern::redraw));
304 }
305
306 GammaPattern::~GammaPattern()
307 {
308 }
309
310 void
311 GammaPattern::refresh()
312 {
313         black[0].set_rgb_p(
314                 r_F32_to_F32(0.0),
315                 g_F32_to_F32(0.0),
316                 b_F32_to_F32(0.0)
317         );
318         white[0].set_rgb_p(
319                 r_F32_to_F32(1.0),
320                 g_F32_to_F32(1.0),
321                 b_F32_to_F32(1.0)
322         );
323         gray50[0].set_rgb_p(
324                 r_F32_to_F32(0.5),
325                 g_F32_to_F32(0.5),
326                 b_F32_to_F32(0.5)
327         );
328         gray25[0].set_rgb_p(
329                 r_F32_to_F32(0.25),
330                 g_F32_to_F32(0.25),
331                 b_F32_to_F32(0.25)
332         );
333
334         // Reds
335         black[1].set_rgb(black[0].get_red(),0,0);
336         gray25[1].set_rgb(gray25[0].get_red(),0,0);
337         gray50[1].set_rgb(gray50[0].get_red(),0,0);
338         white[1].set_rgb(white[0].get_red(),0,0);
339
340         // Greens
341         black[2].set_rgb(0,black[0].get_green(),0);
342         gray25[2].set_rgb(0,gray25[0].get_green(),0);
343         gray50[2].set_rgb(0,gray50[0].get_green(),0);
344         white[2].set_rgb(0,white[0].get_green(),0);
345
346         // blues
347         black[3].set_rgb(0,0,black[0].get_blue());
348         gray25[3].set_rgb(0,0,gray25[0].get_blue());
349         gray50[3].set_rgb(0,0,gray50[0].get_blue());
350         white[3].set_rgb(0,0,white[0].get_blue());
351 }
352
353 bool
354 GammaPattern::redraw(GdkEventExpose */*bleh*/)
355 {
356         static const char hlines[] = { 3, 0 };
357
358         Glib::RefPtr<Gdk::GC> gc(Gdk::GC::create(get_window()));
359
360         int i;
361         Gdk::Color trueblack("#000000");
362
363         // 50% Pattern
364         for(i=0;i<4;i++)
365         {
366                 gc->set_rgb_fg_color(black[i]);
367                 get_window()->draw_rectangle(gc, true, i*tile_w, 0, tile_w, tile_h);
368
369                 gc->set_stipple(Gdk::Bitmap::create(hlines,2,2));
370                 gc->set_fill(Gdk::STIPPLED);
371                 gc->set_rgb_fg_color(white[i]);
372                 get_window()->draw_rectangle(gc, true, i*tile_w, 0, tile_w, tile_h);
373
374                 gc->set_fill(Gdk::SOLID);
375                 gc->set_rgb_fg_color(gray50[i]);
376
377                 get_window()->draw_rectangle(gc, true, i*tile_w+tile_w/4, tile_h/4, tile_w-tile_w/2, tile_h-tile_h/2);
378         }
379
380         // 25% Pattern
381         for(i=0;i<4;i++)
382         {
383                 gc->set_rgb_fg_color(black[i]);
384                 get_window()->draw_rectangle(gc, true, i*tile_w, tile_h, tile_w, tile_h);
385
386                 gc->set_stipple(Gdk::Bitmap::create(hlines,2,2));
387                 gc->set_fill(Gdk::STIPPLED);
388                 gc->set_rgb_fg_color(gray50[i]);
389                 get_window()->draw_rectangle(gc, true, i*tile_w, tile_h, tile_w, tile_h);
390
391                 gc->set_fill(Gdk::SOLID);
392                 gc->set_rgb_fg_color(gray25[i]);
393
394                 get_window()->draw_rectangle(gc, true, i*tile_w+tile_w/4, tile_h+tile_h/4, tile_w-tile_w/2, tile_h-tile_h/2);
395         }
396
397         // Black-level Pattern
398         gc->set_rgb_fg_color(trueblack);
399         get_window()->draw_rectangle(gc, true, 0, tile_h*2, tile_w*4, tile_h);
400         gc->set_fill(Gdk::SOLID);
401         for(i=0;i<4;i++)
402         {
403                 gc->set_rgb_fg_color(black[i]);
404
405                 get_window()->draw_rectangle(gc, true, i*tile_w+tile_w/4, tile_h*2+tile_h/4, tile_w-tile_w/2, tile_h-tile_h/2);
406         }
407
408         return true;
409 }
410
411
412 BlackLevelSelector::BlackLevelSelector()
413 {
414         set_size_request(-1,24);
415         signal_expose_event().connect(sigc::mem_fun(*this, &studio::BlackLevelSelector::redraw));
416
417         add_events(Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK);
418         add_events(Gdk::BUTTON1_MOTION_MASK);
419         add_events(Gdk::BUTTON1_MOTION_MASK);
420 }
421
422 BlackLevelSelector::~BlackLevelSelector()
423 {
424 }
425
426 bool
427 BlackLevelSelector::redraw(GdkEventExpose */*bleh*/)
428 {
429         const int w(get_width()),h(get_height());
430
431         Gdk::Color color;
432
433         Glib::RefPtr<Gdk::GC> gc(Gdk::GC::create(get_window()));
434
435         int i;
436
437         // Draw the gradient
438         for(i=0;i<w;i++)
439         {
440                 color.set_rgb(i*65536/w,i*65536/w,i*65536/w);
441
442                 gc->set_rgb_fg_color(color);
443                 get_window()->draw_rectangle(gc, true, i, 0, 1, h);
444         }
445
446         // Draw a frame
447         gc->set_rgb_fg_color(Gdk::Color("#000000"));
448         get_window()->draw_rectangle(gc, false, 0, 0, w-1, h-1);
449
450         // Draw the position of the current value
451         i=(int)(level*w+0.5);
452         gc->set_rgb_fg_color(Gdk::Color("#ff0000"));
453         get_window()->draw_rectangle(gc, true, i, 1, 1, h-1);
454
455         // Print out the value
456         Glib::RefPtr<Pango::Layout> layout(Pango::Layout::create(get_pango_context()));
457         layout->set_text(etl::strprintf("%0.01f%%",level*100.0f));
458         layout->set_alignment(Pango::ALIGN_CENTER);
459         gc->set_rgb_fg_color(Gdk::Color("#a00000"));
460         get_window()->draw_layout(gc, w/2, 4, layout);
461
462         return true;
463 }
464
465
466
467 bool
468 BlackLevelSelector::on_event(GdkEvent *event)
469 {
470         int x(round_to_int(event->button.x));
471         //int y(round_to_int(event->button.y));
472
473     switch(event->type)
474     {
475         case GDK_MOTION_NOTIFY:
476                 level=(float)x/(float)get_width();
477                 if(level<0.0f)level=0.0f;
478                 if(level>1.0f)level=1.0f;
479                 signal_value_changed_();
480                 queue_draw();
481                 return true;
482                 break;
483         case GDK_BUTTON_PRESS:
484         case GDK_BUTTON_RELEASE:
485                 if(event->button.button==1)
486                 {
487                         level=(float)x/(float)get_width();
488                         if(level<0.0f)level=0.0f;
489                         if(level>1.0f)level=1.0f;
490                         signal_value_changed_();
491                         queue_draw();
492                         return true;
493                 }
494                 break;
495         default:
496                 break;
497         }
498
499         return false;
500 }
501
502
503 void
504 Dialog_Setup::set_time_format(synfig::Time::Format x)
505 {
506         time_format=x;
507         if(x<=Time::FORMAT_VIDEO)
508                 timestamp_optionmenu.set_history(0);
509         else
510         {
511                 if(x==(Time::FORMAT_NOSPACES|Time::FORMAT_FULL))
512                         timestamp_optionmenu.set_history(4);
513                 else if(x==(Time::FORMAT_FULL))
514                         timestamp_optionmenu.set_history(3);
515                 else if(x==(Time::FORMAT_NOSPACES))
516                         timestamp_optionmenu.set_history(2);
517                 else
518                         timestamp_optionmenu.set_history(1);
519         }
520 }
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537 RedBlueLevelSelector::RedBlueLevelSelector()
538 {
539         set_size_request(-1,24);
540         signal_expose_event().connect(sigc::mem_fun(*this, &studio::RedBlueLevelSelector::redraw));
541
542         add_events(Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK);
543         add_events(Gdk::BUTTON1_MOTION_MASK);
544         add_events(Gdk::BUTTON1_MOTION_MASK);
545 }
546
547 RedBlueLevelSelector::~RedBlueLevelSelector()
548 {
549 }
550
551 bool
552 RedBlueLevelSelector::redraw(GdkEventExpose */*bleh*/)
553 {
554         const int w(get_width()),h(get_height());
555
556         Gdk::Color color;
557
558         Glib::RefPtr<Gdk::GC> gc(Gdk::GC::create(get_window()));
559
560         int i;
561
562         // Draw the gradient
563         for(i=0;i<w;i++)
564         {
565                 float red_blue(((float(i)/float(w)+0.5f)-1.0f)/2.0f+1.0f);
566                 float blue_red(2.0f-(red_blue));
567                 if(red_blue>1.0f)red_blue=1.0f;
568                 if(blue_red>1.0f)blue_red=1.0f;
569
570                 color.set_rgb(
571                         round_to_int(min(red_blue,1.0f)*65535),
572                         round_to_int(sqrt(min(red_blue,blue_red))*65535),
573                         round_to_int(min(blue_red,1.0f)*65535)
574                 );
575
576                 gc->set_rgb_fg_color(color);
577                 get_window()->draw_rectangle(gc, true, i, 0, 1, h);
578         }
579
580         // Draw a frame
581         gc->set_rgb_fg_color(Gdk::Color("#000000"));
582         get_window()->draw_rectangle(gc, false, 0, 0, w-1, h-1);
583
584         // Draw the position of the current value
585         i=(int)(((level-1.0f)*2.0f+1.0f-0.5f)*w+0.5);
586         gc->set_rgb_fg_color(Gdk::Color("#00ff00"));
587         get_window()->draw_rectangle(gc, true, i, 1, 1, h-1);
588
589         // Print out the value
590         Glib::RefPtr<Pango::Layout> layout(Pango::Layout::create(get_pango_context()));
591         layout->set_text(etl::strprintf("%0.02f",level));
592         layout->set_alignment(Pango::ALIGN_CENTER);
593         gc->set_rgb_fg_color(Gdk::Color("#a00000"));
594         get_window()->draw_layout(gc, w/2, 4, layout);
595
596         return true;
597 }
598
599
600
601 bool
602 RedBlueLevelSelector::on_event(GdkEvent *event)
603 {
604         int x(round_to_int(event->button.x));
605         //int y(round_to_int(event->button.y));
606
607     switch(event->type)
608     {
609         case GDK_MOTION_NOTIFY:
610                 level=(((float)(x)/(float)get_width()+0.5)-1.0f)/2.0f+1.0f;
611                 if(level<0.5f)level=0.5f;
612                 if(level>1.5f)level=1.5f;
613                 signal_value_changed_();
614                 queue_draw();
615                 return true;
616                 break;
617         case GDK_BUTTON_PRESS:
618         case GDK_BUTTON_RELEASE:
619                 if(event->button.button==1)
620                 {
621                         level=(((float)(x)/(float)get_width()+0.5)-1.0f)/2.0f+1.0f;
622                         if(level<0.5f)level=0.5f;
623                         if(level>1.5f)level=1.5f;
624                         signal_value_changed_();
625                         queue_draw();
626                         return true;
627                 }
628                 break;
629         default:
630                 break;
631         }
632
633         return false;
634 }