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