Fix a crash when running single-threaded and dragging the time slider.
[synfig.git] / synfig-studio / trunk / src / gtkmm / widget_curves.cpp
1 /* === S Y N F I G ========================================================= */
2 /*!     \file widget_curves.cpp
3 **      \brief Template File
4 **
5 **      $Id$
6 **
7 **      \legal
8 **      Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
9 **
10 **      This package is free software; you can redistribute it and/or
11 **      modify it under the terms of the GNU General Public License as
12 **      published by the Free Software Foundation; either version 2 of
13 **      the License, or (at your option) any later version.
14 **
15 **      This package is distributed in the hope that it will be useful,
16 **      but WITHOUT ANY WARRANTY; without even the implied warranty of
17 **      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 **      General Public License for more details.
19 **      \endlegal
20 */
21 /* ========================================================================= */
22
23 /* === H E A D E R S ======================================================= */
24
25 #ifdef USING_PCH
26 #       include "pch.h"
27 #else
28 #ifdef HAVE_CONFIG_H
29 #       include <config.h>
30 #endif
31
32 #include "widget_curves.h"
33 #include <cmath>
34 #include "app.h"
35 #include <gtkmm/drawingarea.h>
36 #include <map>
37 #include <vector>
38 #include <ETL/misc>
39 #include <sigc++/object.h>
40
41 #endif
42
43 /* === U S I N G =========================================================== */
44
45 using namespace std;
46 using namespace etl;
47 using namespace synfig;
48 using namespace studio;
49
50 /* === M A C R O S ========================================================= */
51
52 /* === G L O B A L S ======================================================= */
53
54 /* === P R O C E D U R E S ================================================= */
55
56 /*
57 void
58 studio::render_color_to_window(const Glib::RefPtr<Gdk::Drawable>& window,const Gdk::Rectangle& ca,const synfig::Color &color)
59 {
60         const int height(ca.get_height());
61         const int width(ca.get_width());
62
63         const int square_size(height/2);
64
65         Glib::RefPtr<Gdk::GC> gc(Gdk::GC::create(window));
66
67         if(color.get_alpha()!=1.0)
68         {
69                 // In this case we need to render the alpha squares
70
71                 const Color bg1(Color::blend(color,Color(0.75, 0.75, 0.75),1.0).clamped());
72                 const Color bg2(Color::blend(color,Color(0.5, 0.5, 0.5),1.0).clamped());
73
74                 Gdk::Color gdk_c1(colorconv_synfig2gdk(bg1));
75                 Gdk::Color gdk_c2(colorconv_synfig2gdk(bg2));
76
77                 bool toggle(false);
78                 for(int i=0;i<width;i+=square_size)
79                 {
80                         const int square_width(min(square_size,width-i));
81
82                         if(toggle)
83                         {
84                                 gc->set_rgb_fg_color(gdk_c1);
85                                 window->draw_rectangle(gc, true, ca.get_x()+i, ca.get_y(), square_width, square_size);
86
87                                 gc->set_rgb_fg_color(gdk_c2);
88                                 window->draw_rectangle(gc, true, ca.get_x()+i, ca.get_y()+square_size, square_width, square_size);
89                                 toggle=false;
90                         }
91                         else
92                         {
93                                 gc->set_rgb_fg_color(gdk_c2);
94                                 window->draw_rectangle(gc, true, ca.get_x()+i, ca.get_y(), square_width, square_size);
95
96                                 gc->set_rgb_fg_color(gdk_c1);
97                                 window->draw_rectangle(gc, true, ca.get_x()+i, ca.get_y()+square_size, square_width, square_size);
98                                 toggle=true;
99                         }
100                 }
101         }
102         else
103         {
104                 // In this case we have a solid color to use
105                 Gdk::Color gdk_c1(colorconv_synfig2gdk(color));
106
107                 gc->set_rgb_fg_color(gdk_c1);
108                 window->draw_rectangle(gc, true, ca.get_x(), ca.get_y(), width-1, height-1);
109         }
110         gc->set_rgb_fg_color(Gdk::Color("#ffffff"));
111         window->draw_rectangle(gc, false, ca.get_x()+1, ca.get_y()+1, width-3, height-3);
112         gc->set_rgb_fg_color(Gdk::Color("#000000"));
113         window->draw_rectangle(gc, false, ca.get_x(), ca.get_y(), width-1, height-1);
114 }
115 */
116
117 /* === C L A S S E S ======================================================= */
118
119
120
121 struct studio::Widget_Curves::Channel
122 {
123         synfig::String name;
124         Gdk::Color color;
125         std::map<synfig::Real,synfig::Real> values;
126 };
127
128 struct studio::Widget_Curves::CurveStruct : sigc::trackable
129 {
130         synfigapp::ValueDesc value_desc;
131         std::vector<Channel> channels;
132
133         CurveStruct(const synfigapp::ValueDesc& x):
134                 value_desc(x)
135         {
136                 switch(value_desc.get_value_type())
137                 {
138                         case ValueBase::TYPE_REAL:
139                                 channels.push_back(Channel());
140                                 channels.back().name="real";
141                                 channels.back().color=Gdk::Color("#007f7f");
142                                 break;
143                         case ValueBase::TYPE_TIME:
144                                 channels.push_back(Channel());
145                                 channels.back().name="time";
146                                 channels.back().color=Gdk::Color("#7f7f00");
147                                 break;
148                         case ValueBase::TYPE_INTEGER:
149                                 channels.push_back(Channel());
150                                 channels.back().name="int";
151                                 channels.back().color=Gdk::Color("#7f0000");
152                                 break;
153                         case ValueBase::TYPE_BOOL:
154                                 channels.push_back(Channel());
155                                 channels.back().name="bool";
156                                 channels.back().color=Gdk::Color("#ff7f00");
157                                 break;
158                         case ValueBase::TYPE_ANGLE:
159                                 channels.push_back(Channel());
160                                 channels.back().name="theta";
161                                 channels.back().color=Gdk::Color("#004f4f");
162                                 break;
163                         case ValueBase::TYPE_COLOR:
164                                 channels.push_back(Channel());
165                                 channels.back().name="red";
166                                 channels.back().color=Gdk::Color("#7f0000");
167                                 channels.push_back(Channel());
168                                 channels.back().name="green";
169                                 channels.back().color=Gdk::Color("#007f00");
170                                 channels.push_back(Channel());
171                                 channels.back().name="blue";
172                                 channels.back().color=Gdk::Color("#00007f");
173                                 channels.push_back(Channel());
174                                 channels.back().name="alpha";
175                                 channels.back().color=Gdk::Color("#000000");
176                                 break;
177                         case ValueBase::TYPE_VECTOR:
178                                 channels.push_back(Channel());
179                                 channels.back().name="x";
180                                 channels.back().color=Gdk::Color("#7f007f");
181                                 channels.push_back(Channel());
182                                 channels.back().name="y";
183                                 channels.back().color=Gdk::Color("#007f7f");
184                                 break;
185                         case ValueBase::TYPE_BLINEPOINT:
186                                 channels.push_back(Channel());
187                                 channels.back().name="v.x";
188                                 channels.back().color=Gdk::Color("#ff7f00");
189                                 channels.push_back(Channel());
190                                 channels.back().name="v.y";
191                                 channels.back().color=Gdk::Color("#7f3f00");
192
193                                 channels.push_back(Channel());
194                                 channels.back().name="width";
195                                 channels.back().color=Gdk::Color("#000000");
196
197                                 channels.push_back(Channel());
198                                 channels.back().name="origin";
199                                 channels.back().color=Gdk::Color("#ffffff");
200
201                                 channels.push_back(Channel());
202                                 channels.back().name="tsplit";
203                                 channels.back().color=Gdk::Color("#ff00ff");
204
205                                 channels.push_back(Channel());
206                                 channels.back().name="t1.x";
207                                 channels.back().color=Gdk::Color("#ff0000");
208                                 channels.push_back(Channel());
209                                 channels.back().name="t1.y";
210                                 channels.back().color=Gdk::Color("#7f0000");
211
212                                 channels.push_back(Channel());
213                                 channels.back().name="t2.x";
214                                 channels.back().color=Gdk::Color("#ffff00");
215                                 channels.push_back(Channel());
216                                 channels.back().name="t2.y";
217                                 channels.back().color=Gdk::Color("#7f7f00");
218                                 break;
219                         default:
220                                 throw synfig::Exception::BadType("Bad type for curves");
221                 }
222         }
223
224         void clear_all_values()
225         {
226                 DEBUGPOINT();
227                 std::vector<Channel>::iterator iter;
228                 for(iter=channels.begin();iter!=channels.end();++iter)
229                         iter->values.clear();
230         }
231
232         synfig::Real get_value(int chan, synfig::Real time, synfig::Real tolerance)
233         {
234                 std::map<synfig::Real,synfig::Real>::iterator iter;
235
236                 // First check to see if we have a value
237                 // that is "close enough" to the time
238                 // we are looking for
239                 iter=channels[chan].values.lower_bound(time);
240                 if(iter!=channels[chan].values.end() && iter->first-time<=tolerance)
241                         return -iter->second;
242
243                 // Since that didn't work, we now need
244                 // to go ahead and figure out what the
245                 // actual value is at that time.
246                 ValueBase value(value_desc.get_value(time));
247                 switch(value.get_type())
248                 {
249                         case ValueBase::TYPE_REAL:
250                                 channels[0].values[time]=value.get(Real());
251                                 break;
252                         case ValueBase::TYPE_TIME:
253                                 channels[0].values[time]=value.get(Time());
254                                 break;
255                         case ValueBase::TYPE_INTEGER:
256                                 channels[0].values[time]=value.get(int());
257                                 break;
258                         case ValueBase::TYPE_BOOL:
259                                 channels[0].values[time]=value.get(bool());
260                                 break;
261                         case ValueBase::TYPE_ANGLE:
262                                 channels[0].values[time]=Angle::rad(value.get(Angle())).get();
263                                 break;
264                         case ValueBase::TYPE_COLOR:
265                                 channels[0].values[time]=value.get(Color()).get_r();
266                                 channels[1].values[time]=value.get(Color()).get_g();
267                                 channels[2].values[time]=value.get(Color()).get_b();
268                                 channels[3].values[time]=value.get(Color()).get_a();
269                                 break;
270                         case ValueBase::TYPE_VECTOR:
271                                 channels[0].values[time]=value.get(Vector())[0];
272                                 channels[1].values[time]=value.get(Vector())[1];
273                                 break;
274                         case ValueBase::TYPE_BLINEPOINT:
275                                 channels[0].values[time]=value.get(BLinePoint()).get_vertex()[0];
276                                 channels[1].values[time]=value.get(BLinePoint()).get_vertex()[1];
277                                 channels[2].values[time]=value.get(BLinePoint()).get_width();
278                                 channels[3].values[time]=value.get(BLinePoint()).get_origin();
279                                 channels[4].values[time]=value.get(BLinePoint()).get_split_tangent_flag();
280                                 channels[5].values[time]=value.get(BLinePoint()).get_tangent1()[0];
281                                 channels[6].values[time]=value.get(BLinePoint()).get_tangent1()[1];
282                                 channels[7].values[time]=value.get(BLinePoint()).get_tangent2()[0];
283                                 channels[8].values[time]=value.get(BLinePoint()).get_tangent2()[1];
284                                 break;
285                         default:
286                                 return 0;
287                 }
288
289                 return -channels[chan].values[time];
290         }
291 };
292
293 /* === M E T H O D S ======================================================= */
294
295 Widget_Curves::Widget_Curves():
296         range_adjustment_(new Gtk::Adjustment(-1,-2,2,0.1,0.1,2))
297 {
298         set_size_request(64,64);
299
300         range_adjustment_->signal_changed().connect(
301                 sigc::mem_fun(
302                         *this,
303                         &Widget_Curves::queue_draw
304                 )
305         );
306         range_adjustment_->signal_value_changed().connect(
307                 sigc::mem_fun(
308                         *this,
309                         &Widget_Curves::queue_draw
310                 )
311         );
312         //set_vadjustment(*range_adjustment_);
313
314         signal_expose_event().connect(sigc::mem_fun(*this, &studio::Widget_Curves::redraw));
315         add_events(Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK);
316
317 }
318
319 Widget_Curves::~Widget_Curves()
320 {
321 }
322
323 void
324 Widget_Curves::set_time_adjustment(Gtk::Adjustment&x)
325 {
326         time_adjustment_=&x;
327         time_adjustment_->signal_changed().connect(
328                 sigc::mem_fun(
329                         *this,
330                         &Widget_Curves::queue_draw
331                 )
332         );
333         time_adjustment_->signal_value_changed().connect(
334                 sigc::mem_fun(
335                         *this,
336                         &Widget_Curves::queue_draw
337                 )
338         );
339         //set_hadjustment(*time_adjustment_);
340 }
341
342 void
343 Widget_Curves::clear()
344 {
345         curve_list_.clear();
346 }
347
348 void
349 Widget_Curves::refresh()
350 {
351         std::list<CurveStruct>::iterator curve_iter;
352         for(curve_iter=curve_list_.begin();curve_iter!=curve_list_.end();++curve_iter)
353         {
354                 curve_iter->clear_all_values();
355         }
356         queue_draw();
357 }
358
359 void
360 Widget_Curves::set_value_descs(std::list<synfigapp::ValueDesc> value_descs)
361 {
362         curve_list_.clear();
363
364         std::list<synfigapp::ValueDesc>::iterator iter;
365         for(iter=value_descs.begin();iter!=value_descs.end();++iter)
366         {
367                 try {
368                         curve_list_.push_back(*iter);
369                         if(iter->is_value_node())
370                         {
371                                 DEBUGPOINT();
372                                 iter->get_value_node()->signal_changed().connect(
373                                         sigc::mem_fun(
374                                                 *this,
375                                                 &studio::Widget_Curves::refresh
376                                         )
377                                 );
378                         }
379                         if(iter->parent_is_value_node())
380                         {
381                                 DEBUGPOINT();
382                                 iter->get_parent_value_node()->signal_changed().connect(
383                                         sigc::mem_fun(
384                                                 *this,
385                                                 &studio::Widget_Curves::refresh
386                                         )
387                                 );
388                         }
389                         if(iter->parent_is_layer_param())
390                         {
391                                 DEBUGPOINT();
392                                 iter->get_layer()->signal_changed().connect(
393                                         sigc::mem_fun(
394                                                 *this,
395                                                 &studio::Widget_Curves::refresh
396                                         )
397                                 );
398                         }
399                 }catch(synfig::Exception::BadType)
400                 {
401                         continue;
402                 }
403         }
404         queue_draw();
405 }
406
407 bool
408 Widget_Curves::on_event(GdkEvent *event)
409 {
410         switch(event->type)
411         {
412         case GDK_SCROLL:
413                 switch(event->scroll.direction)
414                 {
415                         case GDK_SCROLL_UP:
416                                 range_adjustment_->set_page_size(range_adjustment_->get_page_size()/1.25);
417                                 range_adjustment_->changed();
418                                 break;
419                         case GDK_SCROLL_DOWN:
420                                 range_adjustment_->set_page_size(range_adjustment_->get_page_size()*1.25);
421                                 range_adjustment_->changed();
422                                 break;
423                         default:
424                                 break;
425                 }
426                 break;
427         default:
428                 return Gtk::DrawingArea::on_event(event);
429                 break;
430         }
431
432         return true;
433
434 /*      switch(event->type)
435         {
436         case GDK_BUTTON_PRESS:
437                 if(event->button.button==1)
438                 {
439                         signal_activate_();
440                         return true;
441                 }
442                 if(event->button.button==3)
443                 {
444                         signal_secondary_();
445                         return true;
446                 }
447                 break;
448
449         default:
450                 break;
451         }
452         return false;
453 */
454 }
455
456 bool
457 Widget_Curves::redraw(GdkEventExpose */*bleh*/)
458 {
459         const int h(get_height());
460         const int w(get_width());
461         get_window()->clear();
462
463         if(!time_adjustment_ || !range_adjustment_ || !h || !w)
464                 return false;
465
466         Glib::RefPtr<Gdk::GC> gc(Gdk::GC::create(get_window()));
467
468         const Real t_begin(time_adjustment_->get_lower());
469         const Real t_end(time_adjustment_->get_upper());
470         const Real dt((t_end-t_begin)/w);
471
472         const Real r_bottom(range_adjustment_->get_value());
473         const Real r_top(r_bottom+range_adjustment_->get_page_size());
474         const Real dr((r_top-r_bottom)/h);
475         Real r_max(-100000000);
476         Real r_min(100000000);
477
478         std::list<CurveStruct>::iterator curve_iter;
479
480         vector<Gdk::Point> points[10];
481
482         gc->set_function(Gdk::COPY);
483         gc->set_line_attributes(1,Gdk::LINE_SOLID,Gdk::CAP_BUTT,Gdk::JOIN_MITER);
484
485         // Draw zero mark
486         gc->set_rgb_fg_color(Gdk::Color("#4f4f4f"));
487         get_window()->draw_rectangle(gc, false, 0, round_to_int((0-r_bottom)/dr), w, 0);
488
489         // Draw current time
490         gc->set_rgb_fg_color(Gdk::Color("#00007f"));
491         get_window()->draw_rectangle(gc, false, round_to_int((time_adjustment_->get_value()-t_begin)/dt), 0, 0, h);
492
493         for(curve_iter=curve_list_.begin();curve_iter!=curve_list_.end();++curve_iter)
494         {
495                 Real t;
496                 int i;
497                 int channels(curve_iter->channels.size());
498                 for(i=0;i<channels;i++)
499                         points[i].clear();
500
501                 for(i=0,t=t_begin;i<w;i++,t+=dt)
502                 {
503                         for(int chan=0;chan<channels;chan++)
504                         {
505                                 Real x(curve_iter->get_value(chan,t,dt));
506                                 r_max=max(r_max,x);
507                                 r_min=min(r_min,x);
508                                 points[chan].push_back(
509                                         Gdk::Point(
510                                                 i,
511                                                 round_to_int(
512                                                         (
513                                                                 x-r_bottom
514                                                         )/dr
515                                                 )
516                                         )
517                                 );
518                         }
519                 }
520
521                 for(int chan=0;chan<channels;chan++)
522                 {
523                         gc->set_rgb_fg_color(curve_iter->channels[chan].color);
524
525                         // Draw the curve
526                         get_window()->draw_lines(gc, Glib::ArrayHandle<Gdk::Point>(points[chan]));
527
528                         Glib::RefPtr<Pango::Layout> layout(Pango::Layout::create(get_pango_context()));
529
530                         layout->set_text(curve_iter->channels[chan].name);
531                         get_window()->draw_layout(gc, 1, points[chan][0].get_y()+1, layout);
532                 }
533         }
534
535         if(!curve_list_.empty())
536         {
537                 range_adjustment_->set_upper(r_max+range_adjustment_->get_page_size()/2);
538                 range_adjustment_->set_lower(r_min-range_adjustment_->get_page_size()/2);
539         }
540         get_window()->get_update_area();
541
542         return true;
543 }