80287c79aa2fe648312fc0aea5b954235c25629b
[synfig.git] /
1 /* === S Y N F I G ========================================================= */
2 /*!     \file widget_coloredit.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 **  Copyright (c) 2008 Paul Wise
11 **
12 **      This package is free software; you can redistribute it and/or
13 **      modify it under the terms of the GNU General Public License as
14 **      published by the Free Software Foundation; either version 2 of
15 **      the License, or (at your option) any later version.
16 **
17 **      This package is distributed in the hope that it will be useful,
18 **      but WITHOUT ANY WARRANTY; without even the implied warranty of
19 **      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 **      General Public License for more details.
21 **      \endlegal
22 */
23 /* ========================================================================= */
24
25 /* === H E A D E R S ======================================================= */
26
27 #ifdef USING_PCH
28 #       include "pch.h"
29 #else
30 #ifdef HAVE_CONFIG_H
31 #       include <config.h>
32 #endif
33
34 #include "widget_coloredit.h"
35 #include <cmath>
36 #include "app.h"
37 #include <gtkmm/drawingarea.h>
38 #include <pangomm/attributes.h>
39 #include <pangomm/attrlist.h>
40 #include <algorithm>
41 #include <gtkmm/notebook.h>
42 #include <gtkmm/box.h>
43
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 #define SPINBUTTON_WIDTH 100
58
59 /* === G L O B A L S ======================================================= */
60
61 /* === P R O C E D U R E S ================================================= */
62
63 /* === C L A S S E S ======================================================= */
64
65 ColorSlider::ColorSlider(const ColorSlider::Type &x):
66         type(x)
67 {
68         signal_expose_event().connect(sigc::mem_fun(*this, &ColorSlider::redraw));
69         set_size_request(-1,12);
70         add_events(Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK);
71         add_events(Gdk::BUTTON1_MOTION_MASK);
72 }
73
74 void
75 ColorSlider::set_type(Type x) { type=x; queue_draw(); }
76
77 void
78 ColorSlider::set_color(synfig::Color x) { color_=x; queue_draw(); }
79
80 void
81 ColorSlider::slider_color_TYPE_R(synfig::Color &color, float amount) { color.set_r(amount); }
82 void
83 ColorSlider::slider_color_TYPE_G(synfig::Color &color, float amount) { color.set_g(amount); }
84 void
85 ColorSlider::slider_color_TYPE_B(synfig::Color &color, float amount) { color.set_b(amount); }
86 void
87 ColorSlider::slider_color_TYPE_Y(synfig::Color &color, float amount) { color.set_y(amount); }
88 void
89 ColorSlider::slider_color_TYPE_U(synfig::Color &color, float amount) { color.set_u(amount-0.5f); }
90 void
91 ColorSlider::slider_color_TYPE_V(synfig::Color &color, float amount) { color.set_v(amount-0.5f); }
92 void
93 ColorSlider::slider_color_TYPE_HUE(synfig::Color &color, float amount) { color.set_uv_angle(Angle::rot(amount)); }
94 void
95 ColorSlider::slider_color_TYPE_SAT(synfig::Color &color, float amount) { color.set_s(amount*0.5f); }
96 void
97 ColorSlider::slider_color_TYPE_A(synfig::Color &color, float amount) { color.set_a(amount); }
98
99 void
100 ColorSlider::adjust_color(Type type, synfig::Color &color, float amount)
101 {
102         static const slider_color_func jump_table[int(TYPE_END)] =
103         {
104                 slider_color_TYPE_R,
105                 slider_color_TYPE_G,
106                 slider_color_TYPE_B,
107                 slider_color_TYPE_Y,
108                 slider_color_TYPE_U,
109                 slider_color_TYPE_V,
110                 slider_color_TYPE_HUE,
111                 slider_color_TYPE_SAT,
112                 slider_color_TYPE_A,
113         };
114         jump_table[int(type)](color,amount);
115 }
116
117 bool
118 ColorSlider::redraw(GdkEventExpose */*bleh*/)
119 {
120         Color color(color_);
121
122         static const slider_color_func jump_table[int(TYPE_END)] =
123         {
124                 slider_color_TYPE_R,
125                 slider_color_TYPE_G,
126                 slider_color_TYPE_B,
127                 slider_color_TYPE_Y,
128                 slider_color_TYPE_U,
129                 slider_color_TYPE_V,
130                 slider_color_TYPE_HUE,
131                 slider_color_TYPE_SAT,
132                 slider_color_TYPE_A,
133         };
134
135         slider_color_func color_func(jump_table[int(type)]);
136
137         float amount;
138         switch(type)
139         {
140                 case TYPE_R: amount=color.get_r(); break;
141                 case TYPE_G: amount=color.get_g(); break;
142                 case TYPE_B: amount=color.get_b(); break;
143                 case TYPE_Y: amount=color.get_y(); break;
144                 case TYPE_U: amount=color.get_u()+0.5; break;
145                 case TYPE_V: amount=color.get_v()+0.5; break;
146                 case TYPE_HUE: amount=Angle::rot(color.get_uv_angle()).get(); amount-=floor(amount); break;
147                 case TYPE_SAT: amount=color.get_s()*2.0; break;
148                 case TYPE_A: amount=color.get_a(); break;
149                 default: amount=0; break;
150         }
151         if(use_colorspace_gamma() && (type<TYPE_U))
152                 amount=gamma_in(amount);
153
154         const int height(get_height());
155         const int width(get_width());
156
157         Gdk::Rectangle ca(0,0,width,height);
158
159         Glib::RefPtr<Gdk::GC> gc(Gdk::GC::create(get_window()));
160         const Color bg1(0.75, 0.75, 0.75);
161         const Color bg2(0.5, 0.5, 0.5);
162         int i;
163         for(i=width-1;i>=0;i--)
164         {
165                 color_func(color,
166                                    (use_colorspace_gamma() && type<TYPE_U)
167                                    ? gamma_out(float(i)/float(width))
168                                    :              (float(i)/float(width)));
169                 const Color c1(Color::blend(color,bg1,1.0).clamped());
170                 const Color c2(Color::blend(color,bg2,1.0).clamped());
171                 assert(c1.is_valid());
172                 assert(c2.is_valid());
173
174                 if((i*2/height)&1)
175                 {
176                         gc->set_rgb_fg_color(colorconv_synfig2gdk(c1));
177                         get_window()->draw_rectangle(gc, true, ca.get_x()+i, ca.get_y(), 1, height/2);
178
179                         gc->set_rgb_fg_color(colorconv_synfig2gdk(c2));
180                         get_window()->draw_rectangle(gc, true, ca.get_x()+i, ca.get_y()+height/2, 1, height/2);
181                 }
182                 else
183                 {
184                         gc->set_rgb_fg_color(colorconv_synfig2gdk(c2));
185                         get_window()->draw_rectangle(gc, true, ca.get_x()+i, ca.get_y(), 1, height/2);
186
187                         gc->set_rgb_fg_color(colorconv_synfig2gdk(c1));
188                         get_window()->draw_rectangle(gc, true, ca.get_x()+i, ca.get_y()+height/2, 1, height/2);
189                 }
190         }
191
192         get_style()->paint_arrow(
193                 get_window(),
194                 Gtk::STATE_SELECTED,
195                 Gtk::SHADOW_OUT,
196                 ca,
197                 *this,
198                 " ",
199                 Gtk::ARROW_UP,
200                 1,
201                 int(amount*width)-height/2,
202                 0,
203                 height,
204                 height
205         );
206
207         gc->set_rgb_fg_color(Gdk::Color("#ffffff"));
208         get_window()->draw_rectangle(gc, false, ca.get_x()+1, ca.get_y()+1, width-3, height-3);
209         gc->set_rgb_fg_color(Gdk::Color("#000000"));
210         get_window()->draw_rectangle(gc, false, ca.get_x(), ca.get_y(), width-1, height-1);
211         return true;
212 }
213
214 bool
215 ColorSlider::on_event(GdkEvent *event)
216 {
217         const int width(get_width());
218         float x = 0;
219         if( GDK_SCROLL == event->type ){
220                 Color color(color_);
221                 float amount;
222                 switch(type)
223                 {
224                         case TYPE_R: amount=color.get_r(); break;
225                         case TYPE_G: amount=color.get_g(); break;
226                         case TYPE_B: amount=color.get_b(); break;
227                         case TYPE_Y: amount=color.get_y(); break;
228                         case TYPE_U: amount=color.get_u()+0.5; break;
229                         case TYPE_V: amount=color.get_v()+0.5; break;
230                         case TYPE_HUE: amount=Angle::rot(color.get_uv_angle()).get(); amount-=floor(amount); break;
231                         case TYPE_SAT: amount=color.get_s()*2.0; break;
232                         case TYPE_A: amount=color.get_a(); break;
233                         default: amount=0; break;
234                 }
235                 if(use_colorspace_gamma() && (type<TYPE_U))
236                         amount=gamma_in(amount);
237                 x = amount*width;
238                 switch(event->scroll.direction){
239                         case GDK_SCROLL_UP:
240                         case GDK_SCROLL_RIGHT:
241                                 x+=1.0;
242                                 break;
243                         case GDK_SCROLL_DOWN:
244                         case GDK_SCROLL_LEFT:
245                                 x-=1.0;
246                                 break;
247                 }
248         } else {
249                 x = float(event->button.x);
250         }
251         
252         float pos(x/width);
253         if(pos<0 || x<=0)pos=0;
254         if(pos>1)pos=1;
255
256         if(use_colorspace_gamma() && (type<TYPE_U))
257                 pos=gamma_out(pos);
258         if(pos<0 || event->button.x<=0)pos=0;
259         if(pos>1)pos=1;
260
261         switch(event->type)
262         {
263         case GDK_SCROLL:
264                 signal_slider_moved_(type,pos);
265                 queue_draw();
266                 signal_activated_();
267                 return true;
268
269         case GDK_BUTTON_RELEASE:
270                 signal_activated_();
271                 return true;
272
273         case GDK_BUTTON_PRESS:
274         case GDK_MOTION_NOTIFY:
275 //              adjust_color(type,color_,pos);
276                 signal_slider_moved_(type,pos);
277                 queue_draw();
278                 return true;
279                 break;
280         default:
281                 break;
282         }
283         return false;
284 }
285
286 /* === M E T H O D S ======================================================= */
287
288 Widget_ColorEdit::Widget_ColorEdit():
289         R_adjustment(0,-10000000,10000000,1,10,0),
290         G_adjustment(0,-10000000,10000000,1,10,0),
291         B_adjustment(0,-10000000,10000000,1,10,0),
292         A_adjustment(0,-10000000,10000000,1,10,0)
293 {
294         notebook=manage(new Gtk::Notebook);
295
296         Gtk::Table* rgb_table(manage(new Gtk::Table()));
297         Gtk::Table* yuv_table(manage(new Gtk::Table()));
298         Gtk::Table* main_table(this);
299
300         {
301                 Gtk::VBox* rgb_box(manage(new Gtk::VBox()));
302                 Gtk::VBox* yuv_box(manage(new Gtk::VBox()));
303                 rgb_box->pack_start(*rgb_table,false,false);
304                 yuv_box->pack_start(*yuv_table,false,false);
305                 notebook->append_page(*rgb_box,_("RGB"));
306                 notebook->append_page(*yuv_box,_("YUV"));
307         }
308
309         color=Color(0,0,0,0);
310
311         set_size_request(200,-1);
312         hold_signals=true;
313
314         Gtk::Label *label;
315
316         R_adjustment.set_lower(-10000000);
317         G_adjustment.set_lower(-10000000);
318         B_adjustment.set_lower(-10000000);
319         A_adjustment.set_lower(-10000000);
320
321         clamp_=true;
322
323         Pango::AttrList attr_list;
324         Pango::AttrInt pango_size(Pango::Attribute::create_attr_size(Pango::SCALE*7));
325         pango_size.set_start_index(0);
326         pango_size.set_end_index(64);
327         attr_list.change(pango_size);
328
329         widget_color.set_size_request(-1,16);
330         attach(widget_color, 0, 2, 0, 1, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
331         attach(*notebook, 0, 2, 1, 2, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
332
333 #define SLIDER_ROW(i,n,l) \
334         slider_##n=manage(new ColorSlider(ColorSlider::TYPE_##n));      \
335         slider_##n->signal_slider_moved().connect(sigc::mem_fun(*this,&studio::Widget_ColorEdit::on_slider_moved)); \
336         /*slider_##n->signal_activated().connect(sigc::mem_fun(*this,&studio::Widget_ColorEdit::activated));*/ \
337         slider_##n->signal_activated().connect(sigc::mem_fun(*this,&studio::Widget_ColorEdit::on_value_changed)); \
338         label=manage(new class Gtk::Label(l,0.0,0.5)); \
339         label->set_use_markup(false); \
340         label->set_use_underline(false); \
341         label->set_attributes(attr_list); \
342         table->attach(*label, 0, 1, 1+2*i, 2+2*i, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);  \
343         table->attach(*slider_##n, 0, 1, 2+2*i, 3+2*i, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0)
344
345 #define ATTACH_SPIN_BUTTON(i,n) \
346         spinbutton_##n=manage(new class Gtk::SpinButton(n##_adjustment,1,0)); \
347         spinbutton_##n->set_update_policy(Gtk::UPDATE_ALWAYS); \
348         spinbutton_##n->set_size_request(SPINBUTTON_WIDTH,-1); \
349         spinbutton_##n->show(); \
350         table->attach(*spinbutton_##n, 1, 2, 1+2*i, 3+2*i, Gtk::SHRINK, Gtk::EXPAND, 2, 0)
351
352         {
353                 Gtk::Table* table(rgb_table);
354                 SLIDER_ROW(0,R,_("Red"));
355                 ATTACH_SPIN_BUTTON(0,R);
356                 SLIDER_ROW(1,G,_("Green"));
357                 ATTACH_SPIN_BUTTON(1,G);
358                 SLIDER_ROW(2,B,_("Blue"));
359                 ATTACH_SPIN_BUTTON(2,B);
360
361                 hex_color_label = manage(new Gtk::Label(_("HTML code"), 0.0, 0.5));
362                 hex_color_label->set_use_markup(false);
363                 hex_color_label->set_use_underline(false);
364                 hex_color_label->set_attributes(attr_list);
365                 table->attach(*hex_color_label, 0, 1, 7, 8, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
366
367                 hex_color = manage(new Gtk::Entry());
368                 hex_color->set_width_chars(8);
369                 hex_color->signal_activate().connect(sigc::mem_fun(*this,&studio::Widget_ColorEdit::on_hex_edited));
370                 table->attach(*hex_color, 0, 1, 8, 9, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
371         }
372         {
373                 Gtk::Table* table(yuv_table);
374                 SLIDER_ROW(0,Y,_("Luma"));
375                 SLIDER_ROW(1,HUE,_("Hue"));
376                 SLIDER_ROW(2,SAT,_("Saturation"));
377                 SLIDER_ROW(3,U,_("U"));
378                 SLIDER_ROW(4,V,_("V"));
379         }
380         {
381                 Gtk::Table* table(main_table);
382                 SLIDER_ROW(1,A,_("Alpha"));
383                 ATTACH_SPIN_BUTTON(1,A);
384         }
385
386 #undef SLIDER_ROW
387 #undef ATTACH_SPIN_BUTTON
388
389         spinbutton_R->signal_activate().connect(sigc::mem_fun(*spinbutton_G,&Gtk::SpinButton::grab_focus));
390         spinbutton_G->signal_activate().connect(sigc::mem_fun(*spinbutton_B,&Gtk::SpinButton::grab_focus));
391         spinbutton_B->signal_activate().connect(sigc::mem_fun(*spinbutton_A,&Gtk::SpinButton::grab_focus));
392         spinbutton_A->signal_activate().connect(sigc::mem_fun(*spinbutton_R,&Gtk::SpinButton::grab_focus));
393
394         R_adjustment.signal_value_changed().connect(sigc::mem_fun(*this,&studio::Widget_ColorEdit::on_value_changed));
395         G_adjustment.signal_value_changed().connect(sigc::mem_fun(*this,&studio::Widget_ColorEdit::on_value_changed));
396         B_adjustment.signal_value_changed().connect(sigc::mem_fun(*this,&studio::Widget_ColorEdit::on_value_changed));
397         A_adjustment.signal_value_changed().connect(sigc::mem_fun(*this,&studio::Widget_ColorEdit::on_value_changed));
398
399         show_all_children();
400
401         set_digits(1);
402         set_value(color);
403
404         hold_signals=false;
405 }
406
407 Widget_ColorEdit::~Widget_ColorEdit()
408 {
409 }
410
411 void
412 Widget_ColorEdit::on_slider_moved(ColorSlider::Type type, float amount)
413 {
414         Color color(get_value_raw());
415
416         assert(color.is_valid());
417         ColorSlider::adjust_color(type,color,amount);
418         assert(color.is_valid());
419
420         // If a non-primary colorslider is adjusted,
421         // we want to make sure that we clamp
422         if(type>ColorSlider::TYPE_B && (color.get_r()<0 ||color.get_g()<0 ||color.get_b()<0))
423                 clamp_=true;
424
425         /*
426         if(type==ColorSlider::TYPE_R && color.get_r()<0)clamp_=false;
427         if(type==ColorSlider::TYPE_G && color.get_g()<0)clamp_=false;
428         if(type==ColorSlider::TYPE_B && color.get_b()<0)clamp_=false;
429         */
430         clamp_=false;
431
432         set_value(color);
433         assert(color.is_valid());
434 }
435
436 void
437 Widget_ColorEdit::on_hex_edited()
438 {
439         Color color(get_value_raw());
440         String s = hex_color->get_text();
441         color.set_hex(s);
442         set_value(color);
443         signal_value_changed_();
444 }
445
446 void
447 Widget_ColorEdit::on_value_changed()
448 {
449         if(hold_signals)
450                 return;
451
452         const Color color(get_value_raw());
453         assert(color.is_valid());
454         slider_R->set_color(color);
455         slider_G->set_color(color);
456         slider_B->set_color(color);
457         slider_Y->set_color(color);
458         slider_U->set_color(color);
459         slider_V->set_color(color);
460         slider_HUE->set_color(color);
461         slider_SAT->set_color(color);
462         slider_A->set_color(color);
463         hex_color->set_text(color.get_hex());
464         widget_color.set_value(color);
465
466         activate();
467         signal_value_changed_();
468 }
469
470 void
471 Widget_ColorEdit::set_has_frame(bool x)
472 {
473         spinbutton_R->set_has_frame(x);
474         spinbutton_G->set_has_frame(x);
475         spinbutton_B->set_has_frame(x);
476         spinbutton_A->set_has_frame(x);
477         spinbutton_R->set_size_request(SPINBUTTON_WIDTH,-1);
478         spinbutton_G->set_size_request(SPINBUTTON_WIDTH,-1);
479         spinbutton_B->set_size_request(SPINBUTTON_WIDTH,-1);
480         spinbutton_A->set_size_request(SPINBUTTON_WIDTH,-1);
481 }
482
483 void
484 Widget_ColorEdit::set_digits(int x)
485 {
486         spinbutton_R->set_digits(x);
487         spinbutton_G->set_digits(x);
488         spinbutton_B->set_digits(x);
489         spinbutton_A->set_digits(x);
490         spinbutton_R->set_size_request(SPINBUTTON_WIDTH,-1);
491         spinbutton_G->set_size_request(SPINBUTTON_WIDTH,-1);
492         spinbutton_B->set_size_request(SPINBUTTON_WIDTH,-1);
493         spinbutton_A->set_size_request(SPINBUTTON_WIDTH,-1);
494 }
495
496 void
497 Widget_ColorEdit::set_value(const synfig::Color &data)
498 {
499         assert(data.is_valid());
500         hold_signals=true;
501         clamp_=false;
502
503         color=data;
504
505         if(use_colorspace_gamma())
506         {
507                 R_adjustment.set_value(gamma_in(color.get_r())*100);
508                 G_adjustment.set_value(gamma_in(color.get_g())*100);
509                 B_adjustment.set_value(gamma_in(color.get_b())*100);
510         }
511         else
512         {
513                 R_adjustment.set_value(color.get_r()*100);
514                 G_adjustment.set_value(color.get_g()*100);
515                 B_adjustment.set_value(color.get_b()*100);
516         }
517         A_adjustment.set_value(color.get_a()*100);
518
519         slider_R->set_color(color);
520         slider_G->set_color(color);
521         slider_B->set_color(color);
522         slider_Y->set_color(color);
523         slider_U->set_color(color);
524         slider_V->set_color(color);
525         slider_HUE->set_color(color);
526         slider_SAT->set_color(color);
527         slider_A->set_color(color);
528         hex_color->set_text(color.get_hex());
529         widget_color.set_value(color);
530
531         hold_signals=false;
532 }
533
534 synfig::Color
535 Widget_ColorEdit::get_value_raw()
536 {
537         Color color;
538         if(use_colorspace_gamma())
539         {
540                 color.set_r(gamma_out(R_adjustment.get_value()/100.0f));
541                 color.set_g(gamma_out(G_adjustment.get_value()/100.0f));
542                 color.set_b(gamma_out(B_adjustment.get_value()/100.0f));
543         }
544         else
545         {
546                 color.set_r(R_adjustment.get_value()/100);
547                 color.set_g(G_adjustment.get_value()/100);
548                 color.set_b(B_adjustment.get_value()/100);
549         }
550         color.set_a(A_adjustment.get_value()/100);
551         assert(color.is_valid());
552
553         return color;
554 }
555
556 const synfig::Color &
557 Widget_ColorEdit::get_value()
558 {
559         if(use_colorspace_gamma())
560         {
561                 color.set_r(gamma_out(R_adjustment.get_value()/100.0f));
562                 color.set_g(gamma_out(G_adjustment.get_value()/100.0f));
563                 color.set_b(gamma_out(B_adjustment.get_value()/100.0f));
564                 assert(color.is_valid());
565         }
566         else
567         {
568                 color.set_r(R_adjustment.get_value()/100);
569                 color.set_g(G_adjustment.get_value()/100);
570                 color.set_b(B_adjustment.get_value()/100);
571                 assert(color.is_valid());
572         }
573         color.set_a(A_adjustment.get_value()/100);
574         assert(color.is_valid());
575
576         if(notebook->get_current_page()!=0)
577                 color=color.clamped();
578
579         /*{
580                 // Clamp out negative values
581                 color.set_r(std::max(0.0f,(float)color.get_r()));
582                 color.set_g(std::max(0.0f,(float)color.get_g()));
583                 color.set_b(std::max(0.0f,(float)color.get_b()));
584         }*/
585
586         return color;
587 }